gmailのsmtpサーバー使うのが多分いちばん楽に用意できると思うのでgmailのsmtpサーバー使って送信する例を書きます。
trait GmailConfig extends SkinnyMailerConfig {
override def mailSmtpHost = "smtp.gmail.com"
/*
アカウント設定
*/
override def mailUser = "" // あなたのgmailアカウント名
override def mailPassword = "password" // あなたのgmailパスワード
override def mailDefaultFrom = "[email protected]" // デフォルトの送信元。書かなくてもOKですが、[email protected]が送信アドレスになるはずです。
}
SkinnyMailerConfigはデフォルトではsmtpsを使用して送信するせていになっているため、gmailサーバーならこれでOKなはずです
SkinnyMailerとSkinnyMailerConfigのmixinします。
class MyMailer extends SkinnyMailer with GmailConfig {
def send_email = mail(to = "送信先@gmail.com",
subject = "タイトル",
text = "コンテンツ")
.deliver
}
deliverメソッドを呼び出すとメールが送信されます。
mailメソッドは多分いちばんよく使われるテキストメール用に
def mail(to: String, from: String = config.mailDefaultFrom, subject: String = "", text: String = "") = getDefaultMessage
.from(new InternetAddress(from))
.to(to)
.subject(subject, mailDefaultCharset)
.text(text, mailDefaultCharset)
と定義されているだけなので、下記のように書いてもOKです。
class MyMailer extends SkinnyMailer with GmailConfig {
def send_email = getDefaultMessage
.to("送信先@gmail.com")
.from("送信元@gmail.com")
.subject("タイトル")
.text("コンテンツ")
.deliver
}
class MyMailer extends SkinnyMailer with GmailConfig {
def send_email = mail(to = "送信先@gmail.com", subject = "タイトル", text = "コンテンツ")
.attachment("ファイル名", "ファイルパス")
.attachment("ファイル名", "ファイルパス")
.deliver
}
これももちろん、下記のように書いてもOKです。
class MyMailer extends SkinnyMailer with GmailConfig {
def send_email = getDefaultMessage
.to("送信先@gmail.com")
.from("送信元@gmail.com")
.subject("タイトル")
.text("コンテンツ")
.attachment("ファイル名", "ファイルパス")
.attachment("ファイル名", "ファイルパス")
.deliver
}
sspでも使えばいいんじゃないかな!
ssp使えないならStringInterporationとか使えばいいんじゃないかな!
テスト対象クラスにSkinnyMockMailerをmixinします。
object TestMailer extends MyMailer extends SkinnyMockMailer
テストクラスの方にSkinnyMessageHelperをmixinします。
class FooSpec extends FlatSpec
with ShouldMatchers with SkinnyMessageHelper
一通しか送信しないような場合は、SkinnyMailTestSupportを利用すると便利です。
package skinny.test
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
import skinny.mail._
import javax.mail.MessagingException
class SkinnyMailTestSupportSpec extends FlatSpec
with ShouldMatchers with SkinnyMessageHelper
with SkinnyMailTestSupport {
behavior of "SkinnyMailTestSupportSpec"
override val singleMailTo = "[email protected]"
object MyMailer extends SkinnyMailer
with SkinnyMailerConfig
with SkinnyMockMailer {
def message = {
mail(to = singleMailTo,
subject = "test subject 日本語",
text="body 日本語").deliver
}
}
it should "singleMail basic" in {
singleMail()(MyMailer.message) { msg =>
msg.getSubject should be("test subject 日本語")
msg.getMailText should be("body 日本語")
// 添付ファイルを取りたいなら
// msg.getMailAttachmentFiles
} should be(true)
}
}
下記が書き方の定型文です。
singleMail(/* 送信されるメールアドレス。デフォルトはsingleMailToなのでto = singleMailToと書いた場合はいらない */)(/* ここで送信するメソッドを与える */) { msg =>
// ここでアサーション処理
} should be(true) // 1通のメールが送信されてきたときのみtrueを返す
これが定型文です。
SkinnyMailerはjavax.mailをラップして作っただけなので、mock_javamailを使えばできます。
it should "be available" in {
Mailbox.clearAll()
TestMailer.message.isRight should be(true)
TestMailer.message.isRight should be(true)
val inbox = Mailbox.get("[email protected]")
// 一通目
inbox.get(0).getSubject should be("subject")
inbox.get(0).getMailText should be("body")
// 二通目
inbox.get(1).getSubject should be("subject")
inbox.get(1).getMailText should be("body")
}
ちなみにConfigを毎回mixinするのはたるいので mail.confを用意して
mail {
default {
debug=true
mimeVersion="1.0"
charset="UTF-8"
contentType="text/plain"
from="[email protected]"
smtp {
host="smtp.skinny.org"
port=587
connectionTimeout=6000
timeout=6000
auth=false
starttls {
enable:true
}
}
transportProtocol="smtp"
user=""
password=""
}
}
こんな設定を書いておいて
class FooMailer extends SkinnyDefaultMailer
とするだけで終わりです。
でも、この設定ファイルは非常に微妙で、手抜き感に溢れてます。あとでやります。
TODO:
- deliverをEitherで返すのは微妙?
- 設定ファイルの設計…
- 設定ファイルがproductionやdevelopで分けれないって…
- リファクタリング
- CastExceptionを潰す
- あと何をやればいいかわからなくなってきた
https://github.com/BlackPrincess/skinny-framework/tree/mail
ありがとうございます。なかなか迷いますよねぇ。
例外でいいんじゃないかなって思います。Skinny 全体としても強い理由がなければ Either より例外を優先してます。Scala on Rails ですし。
SkinnyDefaultMailer 的な感じがいいですね。デフォルトは mail.conf ではなく application.conf から渡すようにしたいです。実装は共通化できてないんですけど ORM の dbmigration が env も含めて対応していて、同じようにやれればと思います。
必要ですかね。なくてもいいんじゃないかと思いました。
ファイルパス以外のバリエーションもありそうですね。InputStream とか読み込んだ byte array を渡せるとか。私もまだ見通せてないですが。
やるなら Scalate をデフォルトでサポートするかなという感じですね。text("xxx") の代わりに render("/mailer/hoge", attributes) とか渡したら WEB-INF/views/mailer/hoge.txt.ssp やら hoge.html.ssp やらを使うとかそんなのかな。text or html の設定をどう渡すかってのもあるけど。
ActionMailer のテストは割と簡単に出来ていいなぁと思うので、こだわりたいですね。SkinnyMailerTest みたいな trait を継承したら MailBox は隠蔽される、とかはあってもよいのかも。
getDefaultMessage って public API としては違う名前にしたいですね。何も指定せずに mail を呼び出せればいいんじゃないかと(config の default が設定済みの Message が返る)。利用側に message とか mail とか似たような複数のキーワードを意識させたくない気がします。
getXXX は基本的には避けたいですね。あと Java 側の getXXX を頑張ってラップして get なしにしたいですね。test の mock のやつもラップしないといけないけど。
で、今後、どうしましょうか?とりあえず気が済むところまでやってあとは私がいじってもいいということであれば区切りの良いところで pull request いただければ、無条件に merge して引き継ぎますが。。。その際は mailer というサブプロジェクトにしていただければと思います。