2013-10-01 6 views
0

Я хотел бы сделать это, не загружая вложения, а затем повторно прикрепляя к новому электронному письму.
Это то, что я пробовал:пересылка электронной почты с вложениями

$emailslist.each do |e| 
    Mail.deliver do 
    from fromstr 
    to "[email protected]" 
    subject "[Events] #{subjectstr}" 

    if e.attachments.length>0 
     e.attachments.each do |a| 
     add_file a 
    end 
    end 
end 
end 

#error in 'e.attachments.each'=>undefined method `attachments' for 
#<TypeError: can't convert nil into String> 

EDIT Я использую этот код в течение нескольких месяцев, и она работала отлично.

Новый материал, который я представил сейчас, - это код выше.

В любом случае, я вставляю весь код по запросу.

require 'mail' 

$subscribers=[] 

File.new("C:/Users/j.de_miguel/Desktop/mailman.forma/subscribers2.txt",'r').each do |line| 
    line=line.sub("\n","") 
    $subscribers.push(line) if line =~ /@/ 
end 

puts $subscribers 

$errorfile=File.new("C:/Users/j.de_miguel/Desktop/mailman.forma/error_log2.txt",'a+') 
$errorfile.write("#{Time.now}\n") 
$errorfile.flush 

def deleteSubjectRecursion(subjstr) 

    if subjstr =~ /(.\[FORMA 2013\])+/ 
    subjstr.gsub!(/.\[FORMA 2013\]/,"") 
    end 

    if subjstr =~ /((?i)Re:){2,}/ 
    subjstr.gsub!(/((?i)Re:){2,}/,"Re: ") 
    end 

    return subjstr 
end 

def UserIsRegistered(mailaddr) 

    registered = false 
    $subscribers.each{|s| registered = true if mailaddr==s} 
    if registered == false 
    $errorfile.write("#{Time.now} : user #{mailaddr} attempted to mailman\n") 
    $errorfile.flush 
    end 

    return registered 

end 


Mail.defaults do 
    retriever_method :imap, { :address => "imap.1and1.es", 
          :port  => 143, 
          :user_name => "[email protected]", 
          :password => "xxxxxxxx", 
          :enable_ssl => false } 

    delivery_method :smtp, { :address    => "smtp.1and1.es", 
          :port     => 587, 
          :domain    => '1and1.es', 
          :user_name   => '[email protected]', 
          :password    => 'xxxxxxxxxxxx', 
          :authentication  => 'plain', 
          :enable_starttls_auto => true } 
end 

#$emailslist=Mail.find(keys: ['NOT','SEEN']) 
$emailslist=[Mail.last] 

$emailslist.each do |e| 

    eplain_part = e.text_part ? e.text_part.body.decoded : nil 
    ehtml_part = e.html_part ? e.html_part.body.decoded : nil 

    type=e.charset 
    type_plain=eplain_part ? e.text_part.charset.to_s : nil 
    type_html=ehtml_part ? e.html_part.charset.to_s : nil 

    bodystr= type ? e.body.decoded.to_s.force_encoding(type) : nil 

    type=type ? type.to_s : type_plain 
    puts type.inspect 

    subjectstr=e.subject.to_s.encode(type) 
    fromstr=e.from.first.to_s.encode(type) 
    puts fromstr 

    bodystr_plain=eplain_part ? eplain_part.force_encoding(type_plain) : nil 
    bodystr_html=ehtml_part ? ehtml_part.force_encoding(type_html) : nil 

    $subscribers.each do |tostr| 

    puts tostr.inspect 

    if (not subjectstr =~ /^\[FORMA 2013\]/ ) && (UserIsRegistered(fromstr) == true) 
     subjectstr=deleteSubjectRecursion(subjectstr) 

     begin 
     Mail.deliver do 

      from fromstr 
      to  "[email protected]" 
      bcc tostr 
      subject "[FORMA 2013] #{subjectstr}" 

      if ehtml_part != nil 
      html_part do 
       content_type("text/html; charset=# {type_html}") 
       #content_transfer_encoding("7bit") 
       body "# {bodystr_html}\[email protected] para darte de baja escribe \"baja\" a [email protected]" 
      end 
      end 

      if eplain_part != nil 
      text_part do 
       content_type("text/plain; charset=# {type_plain}") 
       #content_transfer_encoding("7bit") 
       body "#{bodystr_plain}\[email protected] para darte de baja escribe \"baja\" a [email protected]" 
      end 
      end 

      if eplain_part == nil && ehtml_part == nil 
      body "#{bodystr}\[email protected] para darte de baja escribe \"baja\" a [email protected]" 
      charset=type 
      end 
      #puts e.attachments.inspect 
      if e.attachments.length>0 
      e.attachments.each do |a| 
       add_file a.encoded 
      end 
      end 



     end 
     puts "1 email sent" 
     rescue => e 
     puts "error: #{e}" 
     $errorfile.write("#{Time.now}\nerror sending to #{tostr}: #{e},\nemail subject: #{subjectstr}\n\n") 
     $errorfile.flush() 
     end 
    end 
    end 
end 

$errorfile.close() 
+0

Причина, по которой это не сработало, состоит в том, что 'e' не имеет значения, это' nil'. Так почему же 'e' не имеет значения? Ваш '$ emailslist' пуст? Или он содержит значения «nil»? Вы бы не показали нам достаточно, чтобы разумно ответить на ваш вопрос. –

+0

'e' не равен нулю, иначе он не будет передавать инструкцию if. Это действительно отлично работает без привязанностей. – fartagaintuxedo

+0

если я печатаю 'e.attachments.inspect', он фактически показывает прикрепления. – fartagaintuxedo

ответ

2

Это не проверено, и на самом деле это не попытка найти или исправить ошибку. Это показать, как ваш код должен посмотреть, написанный более идиоматическим кодом Ruby. И, как результат, это может решить проблему, которую вы видите. Если нет, по крайней мере, вы будете иметь лучшее представление о том, как вы должны писать код:

require 'mail' 
  • Определить некоторые константы для текстовых строк, которые получают повторно. Сделайте это наверху, поэтому вам не нужно искать код, чтобы изменить ситуацию в нескольких местах, что делает вероятным, что вы пропустите один из них.

    PATH_TO_FILES = "C:/Users/j.de_miguel/Desktop/mailman.forma" 
    BODY_BOILERPLATE_FORMAT = "%s\[email protected] para darte de baja escribe \"baja\" a [email protected]" 
    
  • Группируйте свои методы в верхней части файла после констант.

  • Мы открыта с использованием 'a', а не 'a+'. Нам не нужно читать/писать, нам нужно писать только.
  • Это открывает и закрывает файл по мере необходимости.
  • Закрытие файла автоматически делает флеш.
  • Если вы часто вызываете метод журнала, тогда есть лучшие способы сделать это, но это не тяжелый сценарий.
  • Я использую File.join для создания имени файла на основе пути. File.join знает о разделителях пути и делает правильную вещь автоматически.
  • String.% позволяет легко создавать стандартный выходной формат.

    def log(text) 
    
        File.open(File.join(PATH_TO_FILES, "error_log2.txt"), 'a') do |log_file| 
        log_file.puts "%s : %s" % [Time.now, text] 
        end 
    
    end 
    
  • Имена методов в Ruby - это snake_case, а не CamelCase.

  • Нет причин иметь несколько gsub! и не требуется условное тестирование. Если подстрока, которую вы хотите очистить, существует в строке gsub, она сделает это, в противном случае она будет двигаться дальше. Цепочка методов gsub уменьшает код до одной строки.
  • gsub может/должен быть, вероятно, sub, если вы не знаете, что в строке может быть несколько ударов.
  • return является избыточным, поэтому мы не используем его, если мы явно не возвращаем значение, чтобы оставить блок преждевременно.

    def delete_subject_recursion(subjstr) 
    
        subjstr.gsub(/.\[FORMA 2013\]/,"").gsub(/((?i)Re:){2,}/, "Re: ") 
    
    end 
    
  • Поскольку registered должен быть логическим значением, используйте any?, чтобы сделать тест. Если найдены совпадения, any? выдает и возвращает true.

    def user_is_registered(mailaddr) 
    
        registered = subscribers.any?{ |s| mailaddr == s } 
        log("user #{ mailaddr } attempted to mailman") unless registered 
    
        registered 
    
    end 
    
  • Использование foreach перебрать строк файла.

    subscribers = [] 
    File.foreach(File.join(PATH_TO_FILES, "subscribers2.txt")) do |line| 
        subscribers << line.chomp if line['@'] 
    end 
    
    puts subscribers 
    
    log('') 
    
    Mail.defaults do 
    
        retriever_method(
        :imap, 
        { 
         :address => "imap.1and1.es", 
         :port  => 143, 
         :user_name => "[email protected]", 
         :password => "xxxxxxxx", 
         :enable_ssl => false 
        } 
    ) 
    
        delivery_method(
        :smtp, 
        { 
         :address    => "smtp.1and1.es", 
         :port     => 587, 
         :domain    => '1and1.es', 
         :user_name   => '[email protected]', 
         :password    => 'xxxxxxxxxxxx', 
         :authentication  => 'plain', 
         :enable_starttls_auto => true 
        } 
    ) 
    
    end 
    
    #emailslist=Mail.find(keys: ['NOT','SEEN']) 
    emailslist = [Mail.last] 
    
    emailslist.each do |e| 
    
  • Это использование трехкомпонентных заявлений здесь, вероятно, не желательно, но я оставил его.

  • Форматирование в столбцы облегчает чтение.
  • Организуйте свои назначения и использует, чтобы они не были разбросаны по всему файлу.

    eplain_part = e.text_part ? e.text_part.body.decoded : nil 
        type_plain = eplain_part ? e.text_part.charset.to_s : nil 
        ehtml_part = e.html_part ? e.html_part.body.decoded : nil 
        type_html = ehtml_part ? e.html_part.charset.to_s : nil 
    
        e_charset = e.charset 
        body_str = e_charset ? e.body.decoded.to_s.force_encoding(e_charset) : nil 
        e_charset = e_charset ? e_charset.to_s : type_plain 
        puts e_charset.inspect 
    
        subjectstr = e.subject.to_s.encode(e_charset) 
        fromstr = e.from.first.to_s.encode(e_charset) 
        puts fromstr 
    
        bodystr_plain = eplain_part ? eplain_part.force_encoding(type_plain) : nil 
        bodystr_html = ehtml_part ? ehtml_part.force_encoding(type_html) : nil 
    
        subscribers.each do |subscriber| 
    
        puts subscriber.inspect 
    
        if !subjectstr[/^\[FORMA 2013\]/] && user_is_registered(fromstr) 
    
         subjectstr = delete_subject_recursion(subjectstr) 
    
         begin 
    
         Mail.deliver do 
    
          from fromstr 
          to  "[email protected]" 
          bcc subscriber 
          subject "[FORMA 2013] #{ subjectstr }" 
    
          if ehtml_part 
          html_part do 
           content_type("text/html; charset=#{ type_html }") 
           #content_transfer_encoding("7bit") 
           body BODY_BOILERPLATE_FORMAT % bodystr_html 
          end 
          end 
    
          if eplain_part 
          text_part do 
           content_type("text/plain; charset=#{ type_plain }") 
           #content_transfer_encoding("7bit") 
           body BODY_BOILERPLATE_FORMAT % bodystr_plain 
          end 
          end 
    
          if !eplain_part && !ehtml_part 
          body BODY_BOILERPLATE_FORMAT % body_str 
          charset = e_charset 
          end 
    
          #puts e.attachments.inspect 
          e.attachments.each { |a| add_file a.encoded } if e.attachments.length > 0 
         end 
    
         puts "1 email sent" 
    
         rescue => e 
    
         puts "error: #{ e }" 
         log("error sending to #{ subscriber }: #{ e },\nemail subject: #{ subjectstr }") 
    
         end 
        end 
        end 
    end 
    

if e.attachments.length>0 
    e.attachments.each do |a| 
    add_file a 
    end 
end 

Это может быть переработан в простой, одной линии с использованием задней условной if тест:

e.attachments.each { |a| add_file a.encoded } if e.attachments.length > 0 

Использование одной линии, как это нормально, когда вы делаете что-то простое. Не используйте их для более сложного кода, потому что вы будете наводить визуальный шум, что затрудняет понимание и чтение вашего кода.

Но давайте посмотрим, что делает на самом деле код. e.attachments в этом контексте, кажется, возвращает массив или какую-то перечислимую коллекцию, иначе each не будет работать. length расскажет нам, сколько элементов существует в «массиве» (или что-то еще), которое возвращается attachments.

Если length равен нулю, то мы не хотим ничего делать, так что мы могли бы сказать:

e.attachments.each { |a| add_file a.encoded } unless e.attachments.empty? 

(Предполагая, что attachments реализует empty? метод.)

Это отчасти излишним, хотя тоже , Если e.attachments уже пуст, что сделает each? Он проверил бы, должен ли attachments получить массив, содержащий любые элементы, и если он пуст, он полностью пропустит свой блок, эффективно действуя так же, как и начальное условие if. SOOOOOO, мы можем использовать это вместо того, чтобы:

e.attachments.each { |a| add_file a.encoded } 

Рубин Стиль руководства:

Второй основан на первом.

+0

Вау, спасибо за урок рубина! на самом деле, очень ценится - id никогда не получит шанс узнать этот материал иначе, так как im всегда кодирует только + компьютерный справочник:)/i будет проверять код ur и посмотреть, как это происходит. – fartagaintuxedo

+1

Нет проблем. На каждом языке есть свои руководства по стилю. Я добавил ссылки на два для Ruby, один из которых основан на другом. Всегда приятно читать их пару раз, а затем периодически перечитывать их, когда вы учитесь. Гиды - это просто «проводники», а не законы. Используйте то, что имеет наибольший смысл, основанный на удобочитаемости и ремонтопригодности, потому что это то, что помогает нам понять, что мы написали год спустя. Я также добавил некоторые рефакторинг вашего кода «добавить вложения». По-прежнему нет гарантии, что код работает, потому что у меня нет системы, настроенной для ее проверки, но это снова идиоматический Ruby. –

0

The Tin Mans отвечает в основном работает. Я изменяю, как добавлены вложения, поскольку его версия не работала для меня.

e.attachments.each { |a| attachments[a.filename] = a.decoded } if e.attachments.length > 0 
Смежные вопросы