Rubyでメールを送る

ウェブサイトの会員登録なんかでメール送信をプログラムで書く機会は結構あると思います。

基本的に他所に書かれてある物を集めただけですが紹介したいと思います。主に自分参照用に。

Mailライブラリーを使う
----------------------
[TMail][tmail]には伝統がありますが、使い方が結構難しかった記憶があるので、今のRailsで使っているという[Mail][mail]というライブラリーを使ってみます。

`gem install mail` でインストールできます。

[tmail]: http://tmail.rubyforge.org/
[mail]:  https://rubygems.org/gems/mail

### 1. メールを作る ###
メール本文のオブジェクトを作ります。

```ruby:mail.rb
require 'mail'

mail = Mail.new do
  from    'KitaitiMakoto@example.net'
  to      'KitaitiMakoto@example.net'
  subject 'Mail from Mail'
  body    'There is a body.'
end
```

### 2. メールを送る ###
SMTPの設定をした後で、```#deliver!```を呼び出します。

```ruby:mail.rb
mail.delivery_method :smtp, { address:   'smtp.example.net',
                              port:      587,
                              domain:    'example',
                              user_name: 'KitaitiMakoto',
                              password:  ($stderr.print 'password> '; gets.chomp) }
mail.deliver!
```

これを実行すると、メールが送れます。

    ruby ./mail.rb

パスワードをエコーバックしないとか、環境変数や設定ファイルで設定するとか、そういう処理くらいは入れたほうがいいですかね？

### 3. クラスメソッドを使う ###
クラスメソッドだけで送信処理をすることもできます。

```ruby:mail.rb
require 'mail'

Mail.defaults do
  delivery_method :smtp, { address:   'smtp.example.net',
                           port:      587,
                           domain:    'example',
                           user_name: 'KitaitiMakoto',
                           password:  ($stderr.print 'password> '; gets.chomp) }
end

Mail.deliver do
  from    'KitaitiMakoto@example.net'
  to      'KitaitiMakoto@example.net'
  subject 'Mail from Mail'
  body    'There is a body.'
end
```

### 4. オフラインで確認する ###
僕はオフラインで開発をすることが多いので、また、そうではなくても一々メールサーバーに接続したくないことも多いでしょう。テストの時など。
そういう時には、メールメッセージの送り先や内容だけを確認できます。

```ruby:mail.rb
puts mail.to
puts mail.body
```

変数を使ってtoやbodyを指定している場合に便利でしょう。

ActionMailerを使う
------------------
RailsではMail gemを直接触ることはなくて、実際にはActionMailerを触ることになると思います。

これは、勿論Railsを使っていなくても便利な物です。

    gem install actionmailer

でインストールします。

### 1. 環境の設定 ###
設定は```ActionMailer::Base```のクラスメソッドを通して行います。

```ruby:actionmailer.rb
require 'action_mailer'

ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
  address:   'smtp.example.net',
  port:      587,
  domain:    'example',
  user_name: 'KitaitiMakoto',
  password:  ($stderr.print 'password> '; gets.chomp)
}
```

### 2. メール送信用クラスの作成 ###
メール送信内容は```ActionMailer::Base```の子クラスを通じて組み立てます。

```ruby:actionmailer.rb
class SampleMailer < ActionMailer::Base
  def first_example(body)
    mail(
      to:      'KitaitiMakoto@example.net',
      from:    'KitaitiMakoto@example.net',
      subject: 'Mail from SampleMailer',
      body:    body.to_s
    )
  end
end
```

### 3. メールを送る ###
（```mail```メソッドを呼んでいるだけですが）メール送信処理はインスタンスメソッドとして定義していますが、呼び出す時にはクラスメソッドを使います。

```ruby:actionmailer.rb
require 'English'

if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
  SampleMailer.first_example('There is a body.').deliver
end
```

    ruby ./actionmailer.rb

でメールを送ります。

テンプレートファイルを使う
--------------------------
メール本文をプログラムにハードコードするのはあり得ないので、テンプレートを使いたいですよね（送信先等の管理は特別なことが無いので省略）。

### 1. テンプレートファイルの場所を指定 ###
ActionMailer::Baseのクラスメソッドを使って、テンプレートファイル（を探す起点）の場所を指定します。

```ruby:template.rb
require 'action_mailer'

ActionMailer::Base.prepend_view_path File.expand_path('../templates', __FILE__)
# （その他設定）
```

### 2. メール送信メソッドをテンプレート版に対応させる ###
RailsのActionControllerの```respond_to```のようにして「テンプレートを使う」ということを指定できます。

これもRailsのビューテンプレートと同じように、インスタンス変数をテンプレート側でも使えます。

```ruby:template.rb
class TemplateMailer < ActionMailer::Base
  def template_example(name)
    @name = name
    mail(
      to:      'KitaitiMakoto@example.net',
      from:    'KitaitiMakoto@example.net',
      subject: 'Mail from TemplateMailer',
    ) do |format|
      format.text
    end
  end
end
```

```ruby
format.html
```

と書くとHTMLメールを送ってくれるようです。

### 3. テンプレートファイルを作る ###
1.で指定したように、テンプレートファイルは（ここでは）```templates```ディレクトリー以下に作ります。

ディレクトリー名にはクラス名をスネークケースにした物、ファイル名にはメソッド名を使います。テキストメールには```.text.erb```を、HTMLメールには```.html.erb```を付けます。

上の例だと

templates/template_mailer/template_example.text.erb

というファイル名になります。

```erb:templates/template_mailer/template_example.text.erb
There is <%= @name %>'s body.
```

### 4. メールを送る ###
メールの送信方法は変わりません。ので、送信せずに内容だけ確認するようにする、ということに挑戦してみましょう。
こうすればテストもしやすくなります。

```ruby:template.rb
require 'English'

if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
  mail = TemplateMailer.template_example('北市真')
  puts mail.body
end
```

```deliver``` を呼ばずに ```body``` 属性を確認します。

    ruby ./template.rb

で実行できます。

```
password> 
There is 北市真's body.
```

あ、SMTP関係の設定はいりませんね。

ネストしたモジュールでのテンプレートファイル
--------------------------------------------
さて、バッチ処理でメールを送る場合など、クラスがモジュールの中にあることも多いと思います。

```ruby:nested.rb
# ActionMailerの設定色々
# ActionMailer::Base...

module Batch
  class Aggregate
    # ...
    # 色々のバッチ処理
    # ...

    def report_done
      Mailer.done.deliver
    end

    def report_error(exception)
      # 例外情報からエラーメッセージを作る
      # error_message = ...
      Mailer.error(error_message).deliver
    end

    class Mailer < ActionMailer::Base
      def done
        # 正常終了を報告するメールを送る
      end

      def error(error_message)
        # エラーを報告するメールを送る
      end
    end
  end
end
```

こういう時のテンプレートの場所は、モジュールのネスト分だけディレクトリーを掘った物になります。上の例だと、

* templates/batch/aggregate/mailer/done.text.erb
* templates/batch/aggregate/mailer/error.text.erb

の二つのファイルを使うことになります。

参考
----
### Mail ###
Mailライブラリーについてはライブラリーその物の[README][mailreadme]が充分わかり易い物です。

### ActionMailer ###
ActionMailerについては、日記「[発狂する近況][hakkyo]」の記事（「[RubyからGMailにメールを送る テキスト編][actionmailer-text]」と「[html編][actionmailer-html]」）がとても参考になりました。

テンプレートファイルを置く場所については、ActionMailerのエラーメッセージを見て理解しました。

[mailreadme]: https://github.com/mikel/mail#readme
[hakkyo]: http://bookmeter.que.jp/hakkyo/
[actionmailer-text]: http://bookmeter.que.jp/hakkyo/?date=20110830
[actionmailer-html]: http://bookmeter.que.jp/hakkyo/?date=20110905