Scala, F#, Ruby によるメール送信 - メールで Evernote にノート登録
メールを使った Evernote へのノート登録を Scala, F#, Ruby で試してみました。
Evernote は、ノート登録用のメールアドレス(アカウント情報に記載あり)に以下のような Subject(件名)でメール送信するだけでノート登録ができます。(メールの本文がノートの内容となる)
Subjectの命名規則
ノート名 @ノートブック #タグ1 #タグ2 ・・・
ただし、# を含んだタグ(例 F#)の指定方法はわかりません。
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20101101/
なお、各プログラムは以下の 4つのパラメータを実行時引数として与え、標準入力の内容をメール本文にします。
- SMTPサーバー名
- 送信者(From)メールアドレス
- 宛先(To)メールアドレス
- 件名(Subject)
また、件名と標準入力の文字コードは Shift_JIS とし、メールは UTF-8 形式で送信する事にします。(件名も UTF-8)
ちなみに、Evernote へのノート登録は JIS(ISO-2022-JP)でも特に問題無いようでした。
Scala でメール送信
JavaMail を直接使うのは面倒なので、今回は Commons Email を使いました。
- Apache Commons Email 1.2
- JavaMail 1.4.3
charset に UTF-8 を設定すれば UTF-8 で送信できます。
send_mail.scala
import scala.io.Source import org.apache.commons.mail.SimpleEmail new SimpleEmail { charset = "UTF-8" hostName = args(0) setFrom(args(1)) addTo(args(2)) subject = args(3) //標準入力を文字列化して本文に設定 setMsg(Source.stdin.mkString) }.send
F# でメール送信
.NET Framework の System.Net.Mail.SmtpClient を使いました。
デフォルト UTF-8 でメール送信するので文字コードを指定する必要はありません。
send_mail.fs
open System open System.Net.Mail [<EntryPoint>] let main(args: string[]) = let smtp = new SmtpClient(args.[0]) //標準入力を文字列化 let body = Console.In.ReadToEnd() //メール送信 smtp.Send(args.[1], args.[2], args.[3], body) 0
実行例(test ノートブックにタグ "fsharp" ".NET" "開発" を付けてノート登録)
- F# 2.0.0
> fsc send_mail.fs > send_mail.exe localhost xxxx@xxx.com xxxx@m.evernote.com "テストノート by Fsharp @test #fsharp #.NET #開発" < mail.txt
Ruby でメール送信
Net::SMTP だと文字コードまわりが面倒そうだったので、ActionMailer 3.0 を単体で使ってみました。
- ActionMailer 3.0
ActionMailer::Base のサブクラスを定義するのが一般的な使い方(Rails の場合)だと思いますが、今回は ActionMailer::Base を直接使ってます。
なお、enable_starttls_auto は環境に合わせて設定すればよいと思います。
また、デフォルト UTF-8 でメール送信するのでメール送信時の文字コードを指定する必要はありませんが、Subject やメール本文に使用する文字列は事前に Shift_JIS から UTF-8 へ文字コード変換を行っておく必要があります。
send_mail.rb(Ruby1.9用)
require "rubygems" require "action_mailer" ActionMailer::Base.smtp_settings = { #SMTPサーバー設定 :address => ARGV[0], :enable_starttls_auto => false } subject = ARGV[3].encode("UTF-8", "Shift_JIS") #標準入力を文字列化 body = $stdin.readlines.join.encode("UTF-8", "Shift_JIS") ActionMailer::Base.mail(:from => ARGV[1], :to => ARGV[2], :subject => subject, :body => body).deliver
Windows 環境だと個人的に JRuby の方が使い勝手が良い気がしているので、JRuby で実行しています。(encode を使用できるようにするため --1.9 オプションを指定する必要があります)
Visual Studio を使わずに F# で Silverlight 4 のサンプル作成 - fsc.exe を用いた手動作成
Visual F# 等の助けを借りず、Silverlight 4 の簡単なサンプルを fsc.exe を使って手動作成してみました。
以前 id:fits:20080310 に Silverlight 2 のサンプルを IronPython で作った方法と似てますが、今回は AppManifest.xaml も自前で用意するので Chiron.exe も必要ありません。(ただし、zip 圧縮の手段は必要)
とりあえず、以下と Silverlight 4 の SDK をインストールしておきます。(今回の用途では Silverlight のランタイムだけあればよくて SDK は要らないかも)
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20101017/
アプリケーション作成
まず、メインアプリケーションの UI を XAML で記述します。
app/MainPage.xaml
<UserControl xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="System.Windows.Controls.UserControl" x:Name="Page"> <StackPanel Background="White"> <TextBlock Name="label1" Text="サンプルリスト" /> <ListBox Name="listBox1" Margin="10 10 0 0" HorizontalAlignment="Left" Width="300" Height="200" > <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=Id}" /> <TextBlock Text="{Binding Path=Title}" Margin="30 0 0 0" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> </UserControl>
次に、上記で作成した XAML(MainPage.xaml)を読み込んで表示するメインアプリケーションを F# で実装します。(Visual Studio で Silverlight を作成する場合の App.xaml・App.xaml.cs 等に相当)
処理内容は以下の通り。
src/ListSampleApp.fs
namespace ListSample open System open System.Windows open System.Windows.Controls //表示用データ定義(レコード型) type Data = { Id: string Title: string } //アプリケーションクラスの定義 type ListSampleApp() as this = inherit Application() //初期化処理 do let xamlUri = new Uri("MainPage.xaml", UriKind.Relative) //Startup イベント時の処理を追加 this.Startup.AddHandler(fun _ _ -> let control = new UserControl() //XAML 読み込み Application.LoadComponent(control, xamlUri) //コントロールの表示 this.RootVisual <- control //ListBox 取得 let listBox = control.FindName("listBox1") :?> ListBox //ListBox への表示用データ設定(Data のインスタンスを作成) listBox.ItemsSource <- [ {Id = "A001"; Title = "XAMLファイルを出力する方法"} {Id = "A002"; Title = "WPF レイアウト"} {Id = "B001"; Title = "F# ラムダ式の記法"} ] )
なお、ListBox の取得時にダウンキャストを使ってますが、パターンマッチを使った方が望ましいかもしれません。
ビルド
fsc.exe で src\ListSampleApp.fs をビルドして app ディレクトリに ListSample.dll ファイルを作成します。
fsc.exe には以下のようなオプションを指定する必要があります。
- "--noframework" で通常の .NET Framework が参照されるのを防止
- F# の Silverlight 用アセンブリ FSharp.Core.dll を参照指定
- 実装内容に応じて Silverlight 用の各種アセンブリを参照指定
なお。fsc.exe は .NET 2.0 用のものを使う点に注意。(例 C:\FSharp-2.0.0.0\bin\fsc.exe を使う)
ビルドの内容をバッチファイル化すると以下のような内容になります。(アセンブリの位置は環境に応じて変更が必要)
ビルド用バッチファイル例 build.bat
set FSHARP_SL_LIB=C:\FSharp-2.0.0.0\Silverlight\2.0\bin set SL_LIB=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0 fsc.exe -a --noframework --out:app\ListSample.dll -r:"%FSHARP_SL_LIB%\FSharp.Core.dll" -r:"%SL_LIB%\System.dll" -r:"%SL_LIB%\System.Windows.dll" src\ListSampleApp.fs copy %FSHARP_SL_LIB%\FSharp.Core.dll app\
なお、FSharp.Core.dll は後で XAP ファイルに含める事になるので、app ディレクトリにコピーしておきます。
C:\FSharp-2.0.0.0\bin を環境変数 PATH に設定しておき、build.bat を実行します。
build.bat 実行例
> build.bat
AppManifest.xaml 作成
AppManifest.xaml を作成します。(Visual Studio や Chiron.exe とかだと自動で作ってくれる)
ListSample.dll・FSharp.Core.dll 用の AssemblyPart の定義、EntryPointAssembly にアプリケーションのアセンブリ・EntryPointType にアプリケーションクラスをそれぞれ設定します。
app/AppManifest.xaml
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="ListSample" EntryPointType="ListSample.ListSampleApp" RuntimeVersion="4.0.50826.0"> <Deployment.Parts> <AssemblyPart x:Name="ListSample" Source="ListSample.dll" /> <AssemblyPart Source="FSharp.Core.dll" /> </Deployment.Parts> </Deployment>
XAP ファイル作成
これまでに作成したファイル(app ディレクトリ内の配置されているはず)を zip 圧縮して XAP ファイル(ListSample.xap)を作成します。(app ディレクトリを含めない点に注意)
今回は署名とかしないので、普通に zip 圧縮して必要に応じて拡張子を変更すれば OK です。
ListSample.xap ファイル内の構成例
ちなみに、IronRuby 1.1 等に含まれている Chiron.exe(silverlight\bin\Chiron.exe)を使って XAP ファイルを作成するには以下のように /x オプションを使います。
Chiron.exe での作成例
> Chiron.exe /d:app /x:ListSample.xap
動作確認
以下のような Silverlight 実行用の HTML を用意して、Web ブラウザで実行します。
index.html
<!DOCTYPE html> <html> <head> <title>ListSample</title> <link rel="stylesheet" href="default.css" type="text/css" /> <script type="text/javascript" src="Silverlight.js"></script> </head> <body> <div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ListSample.xap"/> <param name="background" value="white" /> <param name="minRuntimeVersion" value="4.0.50826.0" /> </object> </div> </body> </html>
Sinatra風にASP.NET(F# 編)
前回 id:fits:20100920 作成したサンプルを F# で実装しなおしてみた。
(ASP.NET で F# を使う設定に関しては id:fits:20100906 参照)
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20100923/
Get, Post メソッドの定義
C# では拡張メソッドを使って実装していた Get, Post メソッドを F# の型拡張で。
そして、各種インターフェースの実装を F# のオブジェクト式で実装。
CustomRouting.fs
namespace Fits.Sample.Web.Routing open System open System.Web open System.Web.Routing module CustomRouting = //HttpApplication に型拡張を適用 type HttpApplication with member this.Get(pattern: string, proc: Func<RequestContext, string>) = this.Action("GET", pattern, proc) member this.Post(pattern: string, proc: Func<RequestContext, string>) = this.Action("POST", pattern, proc) member this.Action(methodType: string, pattern: string, proc: Func<RequestContext, string>) = let r = new Route(pattern, { //IRouteHandler インターフェースの実装 new IRouteHandler with member this.GetHttpHandler(rctx: RequestContext) = { //IHttpHandler インターフェースの実装 new IHttpHandler with member this.IsReusable = true member this.ProcessRequest(hctx: HttpContext) = let res = proc.Invoke(rctx) hctx.Response.Write(res) } }) //httpMethod の制約を設定 let rv = RouteValueDictionary() rv.Add("httpMethod", new HttpMethodConstraint(methodType)) r.Constraints <- rv //ルーティングの追加 RouteTable.Routes.Add(r)
上記ファイルをビルドして、bin ディレクトリに DLL を配置しておきます。
ビルド
>fsc --target:library --out:bin\Fits.Sample.Web.dll CustomRouting.fs
なお、F# のホームディレクトリにある v4.0\bin 内の fsc.exe でビルドしなければならない点に注意。(ホームディレクトリ直下の bin\fsc.exe を使用すると System.Web.Routing が参照できない)
Global.asax の定義
前回と同等の Global.asax を F# で実装します。今回は実行時に Global.asax.fs がビルドされるように Codebehind 属性の代わりに CodeFile 属性を使いました。
Global.asax
<%@ Application CodeFile="Global.asax.fs" Inherits="Fits.Sample.Web.Global" Language="F#" %>
基本的に C# 版と同等の実装ですが、型拡張を行うために CustomRouting を「名前空間.モジュール名」で open している点と RouteData.Values から値を取得する際に ".[キー名]" としている点に注意。
Global.asax.fs
namespace Fits.Sample.Web open System open System.Web //型拡張の適用(名前空間.モジュール名) open Fits.Sample.Web.Routing.CustomRouting type Global() = inherit HttpApplication() member this.Application_Start(sender: Object, e: EventArgs) = // test/:index への POST に対する定義 //(例) "test/1" への POST で WebForm1.aspx にリダイレクト this.Post("test/{index}", fun ctx -> ctx.HttpContext.Response.Redirect("/WebForm1.aspx") null ) // /:name/:index への GET に対する定義 //(例) "test/1" への GET で "hello test - 1" という文字列を表示 this.Get("{name}/{index}", fun ctx -> let pm = ctx.RouteData.Values sprintf "hello %O - %O" pm.["name"] pm.["index"] )
動作確認
WebDev.WebServer40.exe を使って動作確認できます。
ASP.NET 開発サーバー実行バッチ例(start_webserver.bat)
WebDev.WebServer40.exe /port:8080 /path:%~d0%~p0
F# で ASP.NET
F# で ASP.NET を実装してみました。
Visual Web Developer 2010 Express では、F# 用プロジェクトは作成してくれないみたいだったので(Visual F# が要ると思う)、自前で Web.config ファイルを用意し、Visual Web Developer を使わずに実装してみる事にします。
とりあえず、以下をインストールしておきます。
- Visual Web Developer 2010 Express(直接は使わない)
- F# 2.0.0
- F# PowerPack 2.0.0
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20100906/
設定ファイル Web.confg の用意
Web.config ファイルに以下のような F# コンパイラの設定を行えば、ASP.NET で F# が使用できるようになります。
なお、FSharpAspNetCodeProvider は F# PowerPack に含まれるのでインストールしておく必要があります。
Web.config
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" /> </system.web> <system.codedom> <compilers> <compiler language="F#;f#;fs;fsharp" extension=".fs" type="Microsoft.FSharp.Compiler.CodeDom.FSharpAspNetCodeProvider, FSharp.Compiler.CodeDom, Version=2.0.0.0, Culture=neutral, PublicKeyToken=a19089b1c74d0809"/> </compilers> </system.codedom> </configuration>
トップページの実装
それでは、ASP.NET のページを作成します。
Language で F# を指定する以外は一般的な ASP.NET のページを作成する事になります。
Default.aspx
<%@ Page Language="F#" AutoEventWireup="true" CodeFile="Default.aspx.fs" Inherits="Fits.Sample.DefaultPage"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>F# Sample</title> </head> <body> <form runat="server"> <div> <asp:TextBox runat="server" id="InfoText" /> <asp:Button runat="server" id="InfoButton" text="Button" onClick="InfoButton_Click" /> </div> <div> <asp:Label runat="server" id="InfoLabel" /> </div> </form> </body> </html>
次に、F# による処理の実装を行います。
自分で配置したコントロール類は、"[
val キーワードはフィールドを初期化無しで宣言するためのもので、DefaultValue 属性でゼロ初期化(今回のケースでは null で初期化)を指定しています。
ちなみに、val キーワードを使って宣言されたフィールドは「明示的なフィールド」と呼ばれます。
Default.aspx.fs
namespace Fits.Sample open System open System.Web open System.Web.UI open System.Web.UI.WebControls type DefaultPage() = inherit Page() [<DefaultValue>] val mutable InfoText : TextBox [<DefaultValue>] val mutable InfoButton : Button [<DefaultValue>] val mutable InfoLabel : Label //ページロード時の処理 member this.Page_Load(sender : obj, e : EventArgs) = this.InfoLabel.Text <- "hello" //ボタンクリック時の処理 member this.InfoButton_Click(sender : obj, e : EventArgs) = this.InfoLabel.Text <- "入力: " + this.InfoText.Text
動作確認
ASP.NET 開発サーバー(通常は C:\Program Files\Common Files\microsoft shared\DevServer\10.0 にインストールされているはず)を使って動作確認を行います。
以下のようにコマンド実行すると、path で指定したディレクトリ内のファイルを ASP.NET で実行します。(path は絶対パスを指定する点に注意)
ASP.NET 開発サーバー実行
>WebDev.WebServer40.exe /port:8080 /path:d:\try_samples\blog\20100906\asp.net_fsharp
上記コマンドの実行後、http://localhost:8080/ に Web ブラウザで接続し、Button ボタンをクリックすると動作している事が確認できるはずです。