2013-10-01 241 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> 

編輯 我一直在使用這個代碼個月,它工作得很好。

我現在介紹的新東西就是上面的代碼。

反正我粘貼整個代碼的要求。

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是空的嗎?或者,它是否包含「無」值?你沒有向我們展示足以合理回答你的問題。 –

+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從來沒有機會知道這種東西,否則,因爲我總是編碼獨自+沒有計算機科學背景:)/我會測試你的代碼,看看它是如何去的。 – fartagaintuxedo

+1

沒問題。每種語言都有其風格指南。我爲Ruby添加了兩個鏈接,一個基於另一個。多讀幾遍總是好的,然後在你學習的時候定期重讀。指南就是這樣,「指南」,而不是法律。根據可讀性和可維護性使用最有意義的內容,因爲這些內容有助於我們理解一年後寫入的內容。我還添加了一些「添加附件」代碼的重構。仍然不能保證代碼的正常工作,因爲我沒有設置系統來測試它,但它又是更習慣Ruby。 –

0

Tin Mans的答案大多是作品。由於他的版本不適合我,我改變了附件的添加方式。

e.attachments.each { |a| attachments[a.filename] = a.decoded } if e.attachments.length > 0