Rubyコードを他人に使ってもらう
簡単なRubyコードを他人に使ってもらいたいことがしばしばあります。CSVの洗浄だったり、スクレイピングだったりするスクリプトです。そういうときに使えるTips
Rubyをインストールしてもらう
開発者の負担:低
利用者の負担:高
ワンクリックインストーラーがあるとはいえ、CSVの洗浄のためにRubyをインストールするのはちょっと。実行もコマンドプロンプトを立ち上げるのか、あるいはバッチファイルを作るのか、ですが、どちらも初心者には不安が残る方法です。ただ、状況によってはこれが一番いい選択肢のときもあります。かなりITリテラシーが高い人なら、コードを直接編集できますからね。
Rubysscript2exeを使う
https://github.com/ryanbooker/rubyscript2exe
開発者の負担:中
利用者の負担:中
Rubyインタープリタとコードをexeファイルにまとめてくれるものです。まあまあ使いやすいけど、マックとかで使えないのと、GUIがないのがネック。
jRuby & Swingを使う
開発者の負担:高
利用者の負担:低
クロスプラットフォームでつかえると言えばJava。RubyにはjRubyというすばらしいプロジェクトがありますから、それをつかえば、大した手間なくGUIアプリができます。「GO!」ボタンだけがあるGUIアプリでも、バッチのスクリプトよりは初心者にとってずっと安心できるでしょう。
ちなみに、最近はSwingの後継であるJavaFXが流行っていますが、私の環境だと動いてくれなかったので、残念ながらあきらめました。
jRuby & Swingで配布できるような実行ファイルを作るまで。
rawrのインストール
rawrというのは、jRubyをコンパイルするためのgemです。普通にインストールするとRuby 1.8系列用のものが入ってしまうので作者のサイトからインストールします。
gem install rawr --source http://gems.neurogami.com
アプリ開発
src/main.rb が編集すべきファイルです。以下の通り書いてください。
require "java" java_import javax.swing.JFrame java_import javax.swing.JButton java_import javax.swing.JOptionPane class HelloWorld < JFrame def initialize super "Example" setSize(150, 100) setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE) setLocationRelativeTo(nil) button = JButton.new("Say Hello") add(button) button.addActionListener do |e| JOptionPane.showMessageDialog(nil, "Hello World") end setVisible(true) end end HelloWorld.new
コンパイル
以下のコマンドでコンパイルすれば完成です。
rake rawr:bundle:exe rake rawr:bundle:app
先週一週間の生産量=コード一行
先週一週間の私の仕事をgit diffで:
- tol = 1e-12; + tol = 1e-16;
Matlabで書いた割と複雑なシミュレーションのコードが、ときどきあり得ない値を出すことを発見しました。大量に近似解とか局所最適化とか使っているので、疑わしいことばかりです。
バグハンティングを一週間本気で、残業して、土日返上でやって、やっと判明。最適化の収束判定条件が1e-12(=0.0000000001)では甘くて、1e-16(=0.0000000000000001)でなくてはならなかったようです。
近似の有効数字がたかだか10桁くらいかと思って油断しました。たぶん入力によっては一五桁くらいまで行くんでしょう。私のシミュレーション繊細すぎだって。
Rubyで大きなファイルを編集する
あるフォルダ以下のファイルに関して、正規表現とか使って内容を少し変更したいことがあります。
そういうときの方法についてメモ。
Dir.glob("./path-to-folder/**/*.rb") do |file| Tempfile.open('foo') do |tf| IO.foreach(file) do |line| line = line.gsub(...) tf.write line end tf.close FileUtils.copy_file tf.path, file end end
Rubyは実に美しい。
ポイント
- Dir.globを使うと、特定のフォルダ以下のファイルを検索できる。**を使うと、サブフォルダ以下を再帰的に探していく。
- IO.foreachを使うと、行ごとに編集できるから、メモリに収まらないような巨大なファイルでも編集できる。
- とりあえず一時ファイルに保存して、その後上書きすればよい。直接ファイルを編集することはできないらしい(参考)
Ruby on RailsからJavaへの引っ越しはやっぱり大変
先日の記事(デスクトップアプリ開発してみる)の続きです。とりあえずJava/GroovyのGriffonっていうフレームワークを使ってみることにしました。
インストールそのものはオフィシャルサイトに従って行ったら簡単でした。
環境変数の設定の仕方とか忘れててちょっと手間取りましたが。
http://griffon.codehaus.org/Installing+Griffon
Hello world!までは順調に進んだので、続いて開発環境を整えようとしたら、これがまた泣きそうに大変でした。
Eclipse
JavaといえばEclipseでしょう!とりあえずダウンロードしてインストールしてみたけど、よくわからない。ワークスペースとプロジェクトって何?さっき作ったHello world!アプリのフォルダを追加しようとしても、「ワークスペースがありません」「プロジェクトがありません」「プロジェクトのフォルダ構造が不正です」とか言われるし・・・。
悪戦苦闘の後、なんとかフォルダは作成できましたが、コンパイルができません。そもそもGriffonのプラグインってどうやっていれるの?環境変数の設定?どこで?
エラーメッセージをGoogle先生で調べてみても意味が分からない。あきらめました。
Netbeans
やっぱりJavaの本家本元Oracleが作ってるNetbeansだよね!とりあえず最低構成のものをダウンロードしてインストールしてみたけど、これもよくわからない。どうやってもGriffonのプラグインが入ってくれません。フルセットでダウンロードしてインストールし直してもだめ。バージョンが新しすぎるのかと思って、7.2.1の代わりに7.2.0を使ってみてもだめ。Griffonプラグインで、サポート掲示板にある最新のビルドを使ってみてもだめ。
Swing
古い。1995年作成。使いづらい。リソースすごく多い。
JavaFX
新しい。使いやすい。リソースやや少ない。これからのオフィシャル。
やっぱり技術者は最新版に飛びつくものだよね、と思って、JavaFXをダウンロードしてみようとしたものの、私のマック(Snow Leopard)では対応していないらしいです。
いつものStack overflowを参考にインストーラーをハックして、なんとかごまかしてインストール。
これで、コーディングとは関係ない長く険しい道のりを踏み越え、やっとアプリがIDEから起動し、Hello worldが出来るようになりました。さてコーディング、とおもったものの、まだテスト方法を知らないじゃないか・・・。なんと・・・。私はRailsにTDDじゃないコーディングができない体にされているので、自動テストなしでは一行たりともコードがかけません。
Railsで自動テスト環境を整えたときは丸二日くらいかかったけど、あれをまた繰り返すのか・・・。もうこの際GUIテストはあきらめれば、なんとかなるだろう。autotest4jとかになるのかな。
autotest4jはeclipseのみ?え・・・。
ということで、続きます。
Web系開発者がデスクトップアプリを開発しようとするときの10の選択肢
友達にちょっとしたデータ分析機能を頼まれました。エクセルで使えるようにして欲しい、とのことで、10年ぶりくらいにエクセルVBAを見てみたのですが、Ruby on Railsで甘やかされて育った私には、MVCもクロージャーもTDDもイテレーターもない、VBAの言語仕様と開発環境に耐えられませんでした。いっそのことだからまともなデスクトップアプリ作っちゃうか、作ったことないし、と無謀な挑戦を始めました。
どんなアプリでも、一番面倒かつユーザーの評判に直結するのはUI部分の作り込みなので、それを重視して選びました。あとは配布の容易さですね。時代はHTML5だし〜、HTML5のフレームワークを使えばウェブ技術そのまま使えるしそんなむずかしくないよね、デザインはThemeforestでいいし、とか思ったら見通し甘過ぎ。私の迷走をシェアします。
tidesdK
http://www.tidesdk.org/
Titanium Desktopがコミュニティメンテになったもので、HTML5系では最有力です。モダンで良さそうな感じ。Rubyもつかえるし、CSSとjQueryが使えるのは、UIが楽そうで素晴らしいです。ただ、私の環境だとそもそもSDKが起動しなかった。あと、実績も心配。プラットフォームの癖とその対応方法が蓄積されてなさそうです。筋はすごくいいと思うので、半年か一年後にもう一度見てみます。一年後にコミュニティがまだ元気だったら、使ってみるかも。
残念ながら他にはHTML5フレームワークはないようです。
ChroniumEnbeddedを使ったオレオレHTML5フレームワーク
http://code.google.com/p/chromiumembedded/
ないならつくればいいじゃない。Stack Overflowでお薦めされていて、それも有りかな、と思いましたが、Rubyラッパーがないことにまずやる気をそがれ、Pythonで頑張るかと気を取り直したものの、やればやるほどtidesdk(の品質が低い模造品)に近づく気がして、あほらしくなってやめました。Do not reinvent the wheel。
気を取り直して別の道を探してみましょう。
.Net系の言語
ウィンドウズだと一番の選択肢です。C#のよい評判はよく聞きます。また、Visual Studioも昔ちょっと触った限りでは、いい感じです。ただ、私の環境はMacで、彼はwindowsなのでだめ。MacやLinux用.Netのオープンソース実装Monoはありますが、いい評判を聞きません。あと、コンパイラにお金を払うのは個人的には嫌いです。そのクローズドな感じがちょっと。PC変えるとお金払わなくちゃいけないのは現実不便だし。
MacCocoa
マック専用だと一番の選択肢ですが、彼はウィンドウズだからだめ。あと、MacCocoaは使いづらい、ってgithub for macを作った人が言っていました。
Adobe Air
クロスプラットフォームでカジュアルなアプリだとこれが一番多そうですが、あまりにクローズドな感じでちょっと。IDEが高くて趣味に合わない、と言うのが致命的。そもそもAdobeは本物のアプリをAirで作っているわけ?Eat your own dogfood, men.
Ruby系GUIプラットフォーム(Shoes, Visual Rubyとか)
Rubyは大好きなので、これができたら一番なんですが、スタンドアロンにするのが面倒そうです。それなりに大きなデータなので速度も不安。あと、Ruby系のGUIプラットフォームのオフィシャルサイトは、「ルック&フィールを改善するためのツールなんだから、もうちょっとサイトそのもののルック&フィールも考えた方がよろしいのではないでしょうか。」的な感じで、ちょっと・・・。
Visual Ruby
http://visualruby.net/
Shoes
http://shoesrb.com/
ここまで調査して、「これもだめ、これもだめ」ばかりでかなり疲れました。やっぱりメジャーなところじゃないとさまざまな問題が発生することが分かり、5年前に二週間やったきりのJava大陸に進出することを決意。
jRuby系GUIフレームワーク
https://github.com/jruby/jruby/wiki/GUIFrameworks
これで、CRubyにあった配布の問題は解決します。ただ、やっぱりサイトがかっこわるいのは変わらず。完成度が低そうで、手を出すのが怖いです。ただでさえJavaは経験がないのに、フレームワークのバグと、IDEの設定ミスと、自分のバグを切り分けるのに苦労しそうです。
Griffon
http://griffon.codehaus.org/
もうRubyはすっぱり忘れて、ちゃんとJavaることにしました。Ruby on Railsを元にしたGrailsを元にした、デスクトップアプリフレームワークGriffonです。MVCとか、設定より規約とか、なんか覚えのあるフレーズがたくさんあって、安心感があります。Rubyを元にしたGroovyを使っているので、言語もそんなにJavaっぽくなくていい感じ。
参考書が出版されるくらいにはコミュニティも成熟しているようですし、とりあえずこれでいきます。つづく、かも。
Railsの簡単なデバッグ
Railsのデバッグにはいろいろな方法があります。 Ruby Debuggerを使ってもいいし、IDE内蔵デバッガを使ってもいいでしょう。原始的な方法として、p, pp , putsあたりをつかってコンソールに出力する、というのもあります。複雑なのは使いこなせれば便利だろうと思って、Rails Guideのデバッグセクション(英語)を頑張って読んでそのままやってみようとしました。
ただ、私の環境では、Ruby Debuggerを入れたらRailsが起動しなくなってしまって、あきらめました。複雑なフックがかかっているようで、目の前の問題を解いても、今後また相性問題が発生しそうだな、と感じて。
原始的なp, pp, putsはかなり強力でもありますが、多用するとどの出力がどこから来ているのか分からなくなります。このtrueは何だっけ?あと、デバッグが終わった後に消し忘れることもあります。本番ログに怪しげな数字が大量に出力されていて気づくとか笑えない。
pの使いやすさはそのままに、機能を追加した新しいコマンドを作りました。名付けてpl *1。これは、現在のファイル名、メソッド名、行数を出力してから、変数を出力する単純なメソッドです。
以下のコマンドをpl.rbと名前を付けて、config/initializersフォルダに置いてください。
require 'pp' def pl(*args) str0 = "=== " + caller.select{|l| l.match(/Rails.root/)}.first str_out = args.inject(str0){|strs, obj| strs + obj.pretty_inspect} puts str_out str_out end
使い方
pl #=> === /Users/.../app/models/user.rb:24 'true_friends' pl current_user.name, current_user.friends.all #=> === /Users/.../app/models/user.rb:line:24 'true_friends' User1 [<User...>, <User...>, ...]
*1:print line. 最も使うメソッドなので短いメソッド名にしました。
ちょっと複雑なファクトリーメソッドのテンプレート
Railsでアソシエーションとバリデーションが複雑に絡み合ったモデルを保存するときには、モデルにファクトリーメソッドを作るとよいと言われています。そういうときの要求はいくつかあります。
- バリデーションに失敗したときは、たとえアソシエーションの奥で失敗したとしても、そのことを教えてほしい。
- 500ではなくて、エラーはちゃんと綺麗に教えてほしい。
- どこかで失敗したら全部キャンセルしてほしい。
こういう中級イディオムを見つけ出すのは時間がかかりますが、とりあえずの物を書いておきます。create_with_managerメソッドがファクトリーメソッドで、バリデーションに応じて真偽値を返します。失敗したときは理由をstore.errorsに保存します。
class Store < ActiveRecord::Base has_many :manager_stores has_many :managers, :through => :manager_stores validates :managers, :presence => true def create_with_manager(manager) Store.transaction do begin manager.save! save(:validate => false) add_manager(manager) save! rescue => e errors.add(:base, e) raise ActiveRecord::Rollback end end end def add_manager(manager) (失敗したらエラーを起こす複雑なメソッド) end end
基本的にはsave!で失敗したらエラーを起こすようにする。でも、これだとバリデーションエラーは一度に一つしか発見できません。入力に複数のエラーがあったときまとめてエラーを表示したいなら、
unless manager.save errors.add(:manager, manager.errors) end ... if errors.length > 0 raise ActiveRecord:Rollback end
とかやると、失敗したときにすべてのエラーをコントローラーに返せる。
前のsave!の成功に次のsave!が依存しているようなときは、あとのエラーの解釈に困るから注意。