Mongoidを使ってアプリケーションを作っていて、RSpecでテストした時にFactoryGirlでバリデーションエラーが無くならないという事がありました。
一時的にtestの環境ではヴァリデーションをスキップするという方法でしのいでいたのですが、実はspec_helperにDBのコレクションをbefore(:each)のタイミングでドロップするという設定が必要だという事でした。これを実行してみると、問題無くバリデーションエラーがとれました。mongoid-rspecのドキュメンテーションにもこんな事は書いていなかったと思うし、ドキュメントの読み方が悪いのでしょうか…。
http://adventuresincoding.com/2012/05/how-to-configure-cucumber-and-rspec-to-work-with-mongoid-30
database_clearnerのgemを導入したタイミングのどこかで自動でテストの時にデータベースのコレクションを削除してくれるスクリプトも導入されるのかと思ったらそんなことは無い様ですね…。
Showing posts with label RoR. Show all posts
Showing posts with label RoR. Show all posts
Saturday, 15 December 2012
Friday, 14 December 2012
Ruby on Rails AntiPatterns (3) このシリーズはこれで終わりです

AntiPattern: 何故かデータベースに保存されていない…
これは最近暫く悩んだ問題です。MongoDBをMongoidを使っていたのですが、モデルのバリデーションのところでvalidates_uniqueness_of :product_number, allow_nil: true
としていたのですが、モデルのインスタンスを新規保存・更新するとエラーも出ないのに、何故かDBのデータが更新されません???。暫く迷っているうちにふとindexの時に
index( {model_number: 1}, {unique: true} )
と書いた事に思い当たりました。DBがロールバックした事実をmongoidの方でユーザに報せる機構が内容です。ActiveRecordでもDBのレベルでの一意性のチェックなどはしないのが良いプラクティスだそうです。SQLデータベースでもこの辺りの実装は異なるらしいので互換性の実装が難しいのでしょうね。それとももっと他の所に問題があるのでしょうか。もっとDBの実装を理解しないと問題の本質が分からないです(残念ですが)。MongoDBでも例えばクラウドサービスのMongoHQなどでは若干仕様を変えている可能性はありますね…。
AntiPattern: アセットをアプリケーションサーバのファイルシステムに保存する
例えばユーザがアップロードした画像や音楽などのファイルをアプリケーションサーバのファイルシステムに保存すると、サーバ移行やデプロイの際に無駄に作業が増えてしまうことが多い様です。クラウドのファイルストレージサービスが推奨されています。現時点で代表的なサービスはS3ですね。
AntiPattern: テストされていないrake tasks
rakeのタスクはプロジェクトルートでrake spec raketasksなどとしてテストを走らせる事ができないため、テストでカバーされない事が多い様です。これはモデルにコードを記述して、
class Post < ActiveRecord::Base
def send_summary_report_to_admin
### Here comes actual codes...
end
end
rake taskには
namespace :send_summary_report_to_admin
task search: :environment do
Post.send_summary_report_to_admin
end
end
と記述すると、他のモデルと同じようにrake spec:models でテストできます。
ちなみにAntiPatternではないですが、他の人が書いたライブラリを利用する際は下の事を参照にすると良いとの事です。
T: Test テストでカバーされているか?
A: Activity ユーザに使われているか?
M: Maturity 実際にある程度の期間動いているか?
テストでカバーされていないライブラリにはテストを書くという貢献の仕方もOSSの世界なのであり得るそうですが、人が作ったコードにテストを書くという行為はちょっと勇気が要りますね。メンテナンスの責任も勿論大きくなりますし…。実際ある文献によるとプログラミングの50%はメンテナンスに割かれているそうです。
テストはできるだけ自分で書いた全ての行をカバーするべきである、それができない時は最低限ブロックのレベルでテストを書くべきだという基準を耳にします。責任のあるコードを書くのは大変ですね。例としてmongoidのコードを見てみると、実際のコードとテストコードの割合は約2:1。大体これぐらいが良いプラクティスの目安なのでしょうか…?
ちなみにちなみにネットワークのレスポンスはFakeWebというgemで、ファイル読み書きはFileUtils::NoWriteでモック・スタッブできるそうなので、ここにもテストを書かない口実は無さそうです…。
AntiPattern: レスポンスが200OKなのにbodyはエラーですと言っている…
これはHTTP APIを提供しない限り許されるのかもしれませんが、レスポンスコート200を返しながら.jsonのレスポンスは{
"error": {
"message": できません
}
}
などとなっているのはAPIのコンシューマとしては不便です。レスポンスコードはW3のサイトに記載されています。ざくっと見たらコードの数はそんなに多くないので、対応するのは難しくなさそうです。
HTTPステータスコードはコントローラで下のように返します。
respond_to |format|
format.json {
render json: @post.errors, status: :unprocessable_entity
}
end
RailsでのHTTPレスポンスコードとシンボルのマッピングは下のサイトにあります。
http://www.codyfauser.com/2008/7/4/rails-http-status-code-to-symbol-mapping
AntiPattern: respond_to do |format| x 3 x methods.count
コントローラのメソッド毎にそれぞれhtmlとxmlとjsonでのレスポンスの挙動が書かれていています。Rails3で導入されたresponderを使えば例えばindexはdef index
@posts = Post.all
respond_to do |format|
format.html
format.xml { render xml: @posts}
end
end
を
def index
@posts = Post.all
respond_with(@posts)
end
と書けるそうです。
何故scaffoldでもこちらの方法でコードを生成しないのかは少し不思議です。確かにこのメソッドを使うとコントローラのコードを見ただけではxmlでサービスを提供しているのか、jsonのレスポンスも生成されるのか分からないですね。これは必要に応じて使い分けでしょうか。ウェブアプリケーションの中で統一してしまって、全てのレスポンスはxmlとjsonで返すというような指針を決めてしまえばresponderを使った方が無駄が無いかもしれないですね。
Thursday, 13 December 2012
Ruby on Rails AntiPatterns (2)

AntiPattern: RESTfulでないアプリケーション
状況が許す限りscaffoldの状態に近いルーティングに保つのが良いプラクティスの様です。デフォルトのCRUDにコントローラのメソッドを追加する際にも例えばconfig/routes.rbの中で
resources :orders do
collection do
post :shipping
post :billing
end
end
とすると下のようなパスが作成されます。
/orders/new
/orders/shipping
/orders/billing
/orders/1
これを書き直して
resources :orders do
members do
post :shipping
post :billing
end
end
とすると下のようにもっとRESTfulな下のようなパスをつくってくれます。
/orders/new
/orders/1/shipping
/orders/1/billing
私も以前に作ったショッピングカートのルートをリファクタリングしないと駄目です…。
AntiPattern: 何故か500ページが表示される
ユーザがボタンをクリックして何故か知らないけど500ページが表示されたり、トップページにリダイレクトされてエラーメッセージも表示されないなどのサイトが多いらしいです(耳が痛い…)。ユーザとアプリケーションの信頼を確保するために正確なエラー表示が必要です。サーバサイドのエラーは管理者に報告して、ユーザサイドのエラー(例えばメールアドレスが違うなど)はユーザにその旨を伝えると同時に、New Relicなどのログシステムを使って管理者に随時報告するのが筋な様です。
エラーをキャッチする際にもメソッドにエラーの際はnilを返すようにして do_this rescue nilとするのは無責任なので、メソッドの中で的確なエラーをキャッチして報告する仕組みにする必要があります。
例えばメールを送信する場合はまず
config.action_mailer.raise_delivery_errors = true
にします。
Net::HTTPでは下のようにエラーが下のように定義されている様なので、
SMTP_SERVER_ERRORS = [TimeoutError,
IOError,
Net::SMTPUnknownError,
Net::SMTPServerBusy,
Net::SMTPAuthenticationError]
SMTP_CLIENT_ERRORS = [Net::SMTPFatalError,
Net::SMTPSyntaxError]
管理者のログシステムとしてHoptoadを使用した場合、コントローラでは
と書く事ができるらしいです。
なんでもかんでもbegin/rescueしたら良い訳でも無く、例えばメールアドレスの誤入力など例外ではなく、通常の行動として予想できる内容はActiveRecordのバリデーション機能などを使用してレスポンスを返すのが的確な挙動です。
AntiPattern: 長いレスポンス待ち
問題の一つは、外部サービスを使っている場合(例えば外部サーバのSMTPを使ってメールを送信する場合)サーバからのレスポンスが遅い場合があります。デフォルトではNet::HTTPのタイムアウトは30秒に設定されているそうですが、これを3秒に設定するという対処方法があります。もう一つの問題としては、大きなプロセスの仕事があります。この場合、対処方法の一つとしてはプロセスをキューに押し込んでしまい、バックグラウンドで仕事をしてもらい順々にこなしてもらうというものです。ライブラリとしてはdelayed_jobというのとRescueというgemがあるそうです。delayed_jobはSQLを、RescueはRedisを使ってキューを管理するそうです。
ちなみにeventmachineなどconcurrencyライブラリもありますが、大きな仕事の場合は同時にさばく意味が少ないので、外部APIから情報を引っ張ってくるなど待ち時間が発生して処理に時間がかかる仕事と、プロセスの処理自体に計算の時間がかかるなどの仕事はそれぞれ異なるストラテジーでさばく必要がある様ですね。
Wednesday, 12 December 2012
Ruby on Rails AntiPatterns (1)

AntiPattern: Fat Models
"Fat Model, Skinny Controller"という言葉は聞きますが、too fat modelはそもそもコードが読めなくなるし、Railsのパターン以前にOOPやもっと足下の理念から外れてしまいます。クラスを分ける、モジュールを使う、callbackを使うなどORM自体で搭載されている機能は使用するという解決策を。
AntiPattern: ながーいSQLチェーン
例えば人気のある記事トップ10を引っ張って来る時にコントローラの中で、@posts = Post.where(status: "published", order: "number_visited DESC").limit(10)
とするよりも
モデルの中で
class Post
scope :tops, where(status: "published", order: "number_visited DESC").limit(10)
end
とすれば
@posts = Post.tops
だけですみます。これはテストを書く際にも
before(:each)
published_posts = create(:posts)
Post.stub(:where){published_posts}
tops_posts = published_posts.take(10)
published_posts.stub(:limit).with(10) {top_posts}
end
などというスタッブ・モックチェーンを書かなくても済みます。テストを書く際にメソッドチェーンはしんどい…。"Don't ask codes but tell what to do."というような言葉もありましたが、やはりTDDのプラクティスをうまく実行すればしんどいコードを書かなくても済みそうですね。
AntiPattern: 入り組んだユーザ権限処理(authorization)
authorizationの実装を下の様にするケースが多いそうです。こうやっていくとだんだんとスパゲッティーになっていくので、こういう書き方は一切辞めて下のように is_guest? is_manager? などと役割役割の名前でauthorizationを実装します。また、役割もデータベースに保存しないでコンスタントとしてハードコードしてしまいます。
多くの場合、役割をデータベーすに保存するのはメリットが少ないそうです。
Monday, 10 December 2012
ActiveRecord #count, #length, #size
データベースのクエリを使ってできる事はRubyで書かないのが一つのパフォーマンスを向上するキーになるのは当然ですが、勿論それにはデータベースのクエリやORMのコマンドを知らないといけないですね。
limit(num)などは明らかにORMっぽいコマンドなので分かりやすいのですが、最近になってようやくORM使用の際の#count, #length, #sizeの相違についての記事を読みました。結論でActiveRecordでは#countはSQLでCOUNTを出します。#lengthはSQLでは処理されないので、一度配列としてデータを取ってきてRubyとして配列の長さを数えます。#sizeについては、既に配列としてデータを取ってきているオブジェクトに対しては配列の長さを返し、まだクエリのProcで残っているものに対してはCOUNTを発効してくれる便利な関数です。
こういう事を知らないでいつの間にか無駄なデータ転送を強いるコマンドを闇雲に走らせている可能性は冷や汗ですね…。
ちなみに私が最近メインで使っているmongoidでも#countはMongoDBのcountを発効してくれ様です。
limit(num)などは明らかにORMっぽいコマンドなので分かりやすいのですが、最近になってようやくORM使用の際の#count, #length, #sizeの相違についての記事を読みました。結論でActiveRecordでは#countはSQLでCOUNTを出します。#lengthはSQLでは処理されないので、一度配列としてデータを取ってきてRubyとして配列の長さを数えます。#sizeについては、既に配列としてデータを取ってきているオブジェクトに対しては配列の長さを返し、まだクエリのProcで残っているものに対してはCOUNTを発効してくれる便利な関数です。
こういう事を知らないでいつの間にか無駄なデータ転送を強いるコマンドを闇雲に走らせている可能性は冷や汗ですね…。
ちなみに私が最近メインで使っているmongoidでも#countはMongoDBのcountを発効してくれ様です。
Saturday, 8 December 2012
Ruby on Rails 人の失敗から学ぶ
私の経験としては失敗からは学ぶ事がとても多い様に感じます。自分で失敗すると印象が強く残るからでしょうか?でも人の失敗を観察する機会でも学びが多い様に感じます。これは自分の失敗と人の失敗を重ね合わせるというプロセスを勝手に頭の中でしているのでしょうか?
Saleh, T & Pytel, C著 "Rails AntiPattern"という本を読みました。興味深く、おもしろく、苦い内容でした。
デザインパターンの書籍は見る機会が多いのですが、AntiPatternというのは面白く感じました。成功例を並べられても正直面白くないですが、人の失敗を例に挙げて解説してくれるのはとても興味深く面白く感じました。
少しずつ学んだ事を記事に書いてはいきたいと思います。
Saleh, T & Pytel, C著 "Rails AntiPattern"という本を読みました。興味深く、おもしろく、苦い内容でした。
デザインパターンの書籍は見る機会が多いのですが、AntiPatternというのは面白く感じました。成功例を並べられても正直面白くないですが、人の失敗を例に挙げて解説してくれるのはとても興味深く面白く感じました。
少しずつ学んだ事を記事に書いてはいきたいと思います。
Friday, 7 December 2012
Ruby Array#each_sliceメソッド
配列を4つごとに区切った後で更にその4つを.eachで繰り返したいということは多くあるのではないでしょうか?
RoRでの実装はeach_sliceを使ってこうしました(slimを使ったviewのテンプレートです)。
- @products.each_slice(4) do |products|
ul
- products.each do |product|
li= product.name
これで
ul
li
li
li
li
ul
li
li
li
li
ul
li
li
と続くHTMLを作れます。
RoRでの実装はeach_sliceを使ってこうしました(slimを使ったviewのテンプレートです)。
- @products.each_slice(4) do |products|
ul
- products.each do |product|
li= product.name
これで
ul
li
li
li
li
ul
li
li
li
li
ul
li
li
と続くHTMLを作れます。
Monday, 3 December 2012
viewでcontent_forを使うか、ヘルパーメソッドを使うか?
最近 Rails の viewで
<% content_for :title do "Title" end %>
として
<%= yield :title %>
とすれば yieldされた場所にcontents_for の内容が返ってくる事を知りました。テンプレートでcontents_for で定義した内容をlayoutの例えば<title>とか<meta name="description" contents="">などに使えそうです。
ヘルパーを使うのとどちらが良いのかと考えた時、ロジックとしてはコントローラから渡されたコンテンツをviewの中でcontents_forとしたほうが奇麗だと思います。
しかしコードが重複しがちな所があるので、やはり使い分けが必要でしょうか。取りあえずはできるだけcontents_for / yieldで書く方向性で。
<% content_for :title do "Title" end %>
として
<%= yield :title %>
とすれば yieldされた場所にcontents_for の内容が返ってくる事を知りました。テンプレートでcontents_for で定義した内容をlayoutの例えば<title>とか<meta name="description" contents="">などに使えそうです。
ヘルパーを使うのとどちらが良いのかと考えた時、ロジックとしてはコントローラから渡されたコンテンツをviewの中でcontents_forとしたほうが奇麗だと思います。
しかしコードが重複しがちな所があるので、やはり使い分けが必要でしょうか。取りあえずはできるだけcontents_for / yieldで書く方向性で。
Friday, 30 November 2012
Herokuのスタックに独自のシェルコマンドを導入する
Herokuではgemでパックされていないシェルコマンドは使えないと思っていました。
が、最近buildpackでgit push でスタックを作成する時に
が、最近buildpackでgit push でスタックを作成する時に
$ heroku create myapp --buildpack https://github.com/heroku/heroku-buildpack-ruby
としてカスタムのbuildpackを指定する事により、カスタムのシェルコマンドを導入する事ができます。あとはRubyの中で `command`です。buildpackについては下に記事があります。
シェルで使い慣れているあれやこれやのコマンドをHerokuにデプロイしたアプリケーションでもつかえるようになります。ただここまで来ると自分でクラウドアプリケーションサーバであるHerokuではなくVPSなど自分でサーバの環境から構築するのとどちらが良いのか微妙なラインになってきますね…。
Wednesday, 28 November 2012
Caching implementation on RoR
今作っているRuby on Railsでアプリケーションをキャシングをするのに、調べ物をあれこれと。
アプリケーションはHeroku上で動かすのですが、Herokuのドキュメント上にはアプリケーションが再起動する際にはコンパイル後に保存されたディスク上のデータは消えるので、ディスクキャッシュは使えないそうです。なのでHerokuのおすすめとしてはmemcachedを使う事。
ということで、今回はHerokuのプラグインであるMemcacheを使ってキャッシュしてみました。キャッシュの方法は下の3つ。
- ページキャッシュ --> アプリケーションのレイアウトを流用した404や500ページを使用したいのでカスタムでページを作ってページキャッシュ。データも大きくないし、消えてしまっても特に問題無いのでディスクキャッシュで。これは単純に
caches_page :action_name
と書くだけです。
- アクションキャッシュ --> sitemap.xmlをデータベースからデータを引っ張ってきて作成しているので、これをアクションキャッシュで保存。データベースが大きくなる予定なので、botが来る度にxmlを生成しているとパフォーアンスの低下に繋がる可能性が高いです。検索botたちが訪問する頻度はそんなに高くないだろうと思うので、3日で賞味期限が切れる様に設定。多分これで良いと思います…。これも同じくコントローラの最初で
caches_action :action_name, expires_in: 3.days
と記述するだけ。
- 外部API --> ショッピングモールが提供するAPIから商品の情報を引っ張ってきているのですが、このレスポンスがかなり時間がかかっている様です(20個ぐらいの商品情報を引っ張って来るのに2秒近く)。これは取りあえずAPIのデータのみをmemcachedに保存。まずは賞味期限8時間ぐらいで動かしてみます。コントローラ上でmemcacheを使用して幾つかのviewでもキャッシュしたデータを利用できる様にします。製品ごとに販売できる商品を製品のキャッシュデータをデータベースの中のidとupdated_atのタイムスタンプ、それに'buyable'の文字列を付与したインデックスで管理します。
@buyables = Rails.cache.fetch([@product, 'buyables'], expires_in: 8.hours) { @product.buyables }
アプリケーションはHeroku上で動かすのですが、Herokuのドキュメント上にはアプリケーションが再起動する際にはコンパイル後に保存されたディスク上のデータは消えるので、ディスクキャッシュは使えないそうです。なのでHerokuのおすすめとしてはmemcachedを使う事。
ということで、今回はHerokuのプラグインであるMemcacheを使ってキャッシュしてみました。キャッシュの方法は下の3つ。
- ページキャッシュ --> アプリケーションのレイアウトを流用した404や500ページを使用したいのでカスタムでページを作ってページキャッシュ。データも大きくないし、消えてしまっても特に問題無いのでディスクキャッシュで。これは単純に
caches_page :action_name
と書くだけです。
- アクションキャッシュ --> sitemap.xmlをデータベースからデータを引っ張ってきて作成しているので、これをアクションキャッシュで保存。データベースが大きくなる予定なので、botが来る度にxmlを生成しているとパフォーアンスの低下に繋がる可能性が高いです。検索botたちが訪問する頻度はそんなに高くないだろうと思うので、3日で賞味期限が切れる様に設定。多分これで良いと思います…。これも同じくコントローラの最初で
caches_action :action_name, expires_in: 3.days
と記述するだけ。
- 外部API --> ショッピングモールが提供するAPIから商品の情報を引っ張ってきているのですが、このレスポンスがかなり時間がかかっている様です(20個ぐらいの商品情報を引っ張って来るのに2秒近く)。これは取りあえずAPIのデータのみをmemcachedに保存。まずは賞味期限8時間ぐらいで動かしてみます。コントローラ上でmemcacheを使用して幾つかのviewでもキャッシュしたデータを利用できる様にします。製品ごとに販売できる商品を製品のキャッシュデータをデータベースの中のidとupdated_atのタイムスタンプ、それに'buyable'の文字列を付与したインデックスで管理します。
@buyables = Rails.cache.fetch([@product, 'buyables'], expires_in: 8.hours) { @product.buyables }
Monday, 29 October 2012
SinatraとRSpecで開発(ちょっとしんどい)
ここ2日程SinatraとRSpecを使って、1ページウェブサイトの制作をしていました。
Sinatraはとてもシンプルなのが好きで使っているのですが、少しやる事が複雑になると一つずつrequireを追加していく必要があります。フォームを設置する、外部APIを使用する、テストを書くという程度の制作になればSinatraではなくてRailsにしたほうがかなり時間を抑えて開発できるのではないでしょうか。小さな規模のサイトなのにかなり時間がかかりました。
例えばURLのパースにはURIを使い、外部APIを使うにはNet:HTTPを使い、メールの送信にはPonyかMailなどのgemを使用、viewのヘルパーもデフォルトでは便利なものが多くないので、Sinatra_moreのgemを引っ張ってくるという具合です。RSpecのテストをする際にもデフォルトではassignsやrender_templateなどの何気なく使用しているマッチャーも無く、カスタマイズが必要でした。メールの送信もあまりマニュアルが整備されていないgemを使う必要があり、コードを読みにいったりする必要がありました。
今回はいかにRoRが皆に支持されていろんな便利なツールを実装しているか知る良い機会になりました。それはそれでありがたい経験です。
Sinatraはとてもシンプルなのが好きで使っているのですが、少しやる事が複雑になると一つずつrequireを追加していく必要があります。フォームを設置する、外部APIを使用する、テストを書くという程度の制作になればSinatraではなくてRailsにしたほうがかなり時間を抑えて開発できるのではないでしょうか。小さな規模のサイトなのにかなり時間がかかりました。
例えばURLのパースにはURIを使い、外部APIを使うにはNet:HTTPを使い、メールの送信にはPonyかMailなどのgemを使用、viewのヘルパーもデフォルトでは便利なものが多くないので、Sinatra_moreのgemを引っ張ってくるという具合です。RSpecのテストをする際にもデフォルトではassignsやrender_templateなどの何気なく使用しているマッチャーも無く、カスタマイズが必要でした。メールの送信もあまりマニュアルが整備されていないgemを使う必要があり、コードを読みにいったりする必要がありました。
今回はいかにRoRが皆に支持されていろんな便利なツールを実装しているか知る良い機会になりました。それはそれでありがたい経験です。
Tuesday, 18 September 2012
Google Map API を使ってフロント開発
久しぶりにRailsを使ってフロント開発です。Google Maps APIを使用した地図上に会社の位置を表示する内容です。
コントローラのコードは1行だけで
class MyController < ApplicationController
def map
@companies = Company.all
end
end
モデルも今回は大して触りません。
あとはひたすらテンプレートとJavaScriptの編集作業です。
Google Maps APIはバージョン3になってかなりできることの範囲が広がったようで、特にRails用のプラグインなど使用しなくても大きな幅でカスタマイズが可能です。ただコマーシャルユースの場合はライセンスが必要で、社内利用のサイトだとサービスに問い合わせると、USD$11,000 for 250,000 page views per yearという返事が返ってきました。安くはありませんね。
Google Mapsは独自の google.maps.event.addListener などのメソッドを提供していて、その挙動と一つづつ試しながらなので、一進一退のゆっくり作業です。ちょっと面倒…。jQueryと組み合わせてゆっくりゆっくり開発です。
マーカーのDOMを取得できないという質問がウェブにあり、これは大変そうだなと思ったのですが、
marker = new google.maps.Marker({
position: m_position,
id: "8iu3afluh"
});
google.maps.event.addListener(marker, "click", function() {
alert(this.id);
});
などとすれば設定したマーカーの id: 8iu3afluh が返って来るので、とにかくこれで問題無さそうです。
コントローラのコードは1行だけで
class MyController < ApplicationController
def map
@companies = Company.all
end
end
モデルも今回は大して触りません。
あとはひたすらテンプレートとJavaScriptの編集作業です。
Google Maps APIはバージョン3になってかなりできることの範囲が広がったようで、特にRails用のプラグインなど使用しなくても大きな幅でカスタマイズが可能です。ただコマーシャルユースの場合はライセンスが必要で、社内利用のサイトだとサービスに問い合わせると、USD$11,000 for 250,000 page views per yearという返事が返ってきました。安くはありませんね。
Google Mapsは独自の google.maps.event.addListener などのメソッドを提供していて、その挙動と一つづつ試しながらなので、一進一退のゆっくり作業です。ちょっと面倒…。jQueryと組み合わせてゆっくりゆっくり開発です。
マーカーのDOMを取得できないという質問がウェブにあり、これは大変そうだなと思ったのですが、
marker = new google.maps.Marker({
position: m_position,
id: "8iu3afluh"
});
google.maps.event.addListener(marker, "click", function() {
alert(this.id);
});
などとすれば設定したマーカーの id: 8iu3afluh が返って来るので、とにかくこれで問題無さそうです。
Friday, 14 September 2012
BDDとTDDはどう違うのか?
以前BDDとTDDはどう違うのか?という質問をされてうまく答えられませんでした。
現在私がBDDで好きだなと思い、TDDと異なると思うのは、BDDはとにかくまずプログラムに何をさせたいかを伝えることです。
暫くRoRとBDDで実践をしていて、決済のAPIを導入することがありました。最初はAPIの仕組みを調べてAPI指定のコマンドを直接コントローラに記述する形で、例えば
gateway = | PaypalApi::Billing.gateway
setup_purchase = gateway.setup_purchase
if setup_purchase.success?
redirect_to gateway.redirect_url_for(setup_purchase.token)
else
redirect_to somewhere-else, notice: "transaction failure"
end
などとしていたのですが、これだとテスト用APIのモックやスタッブを書くのがとても面倒。BDDではこのアプローチをやめて、とにかくAPIの仕様など気にしなくてプログラムに何をさせたいか書く事から始めるとすっきりします。例えばモデルのインスタンスを@orderにしている場合、
if @order.setup_purchase
redirect_to gateway_url
else
redirect_to somewhere-else, notice: "transaction failure"
end
としてモデルの中で実際にsetup_purchaseのコードを書きます。こうするとコードもテストコードも整理しやすくなります。
ただ実際はコントローラからtokenなどのpostリクエストの部分をモデルに渡す必要があるところで後でメソッドに引数を追加する必要など私の場合は出てきましたが。
現在私がBDDで好きだなと思い、TDDと異なると思うのは、BDDはとにかくまずプログラムに何をさせたいかを伝えることです。
暫くRoRとBDDで実践をしていて、決済のAPIを導入することがありました。最初はAPIの仕組みを調べてAPI指定のコマンドを直接コントローラに記述する形で、例えば
gateway = | PaypalApi::Billing.gateway
setup_purchase = gateway.setup_purchase
if setup_purchase.success?
redirect_to gateway.redirect_url_for(setup_purchase.token)
else
redirect_to somewhere-else, notice: "transaction failure"
end
などとしていたのですが、これだとテスト用APIのモックやスタッブを書くのがとても面倒。BDDではこのアプローチをやめて、とにかくAPIの仕様など気にしなくてプログラムに何をさせたいか書く事から始めるとすっきりします。例えばモデルのインスタンスを@orderにしている場合、
if @order.setup_purchase
redirect_to gateway_url
else
redirect_to somewhere-else, notice: "transaction failure"
end
としてモデルの中で実際にsetup_purchaseのコードを書きます。こうするとコードもテストコードも整理しやすくなります。
ただ実際はコントローラからtokenなどのpostリクエストの部分をモデルに渡す必要があるところで後でメソッドに引数を追加する必要など私の場合は出てきましたが。
Wednesday, 5 September 2012
ロリポップがRubyウェブアプリケーションクラウドサービスを始めたみたいです
先ほどロリポップからDMが入り、Rubyウェブアプリケーションクラウドサービスの宣伝が届きました。
http://sqale.jp/
Heroku はAWSのサーバを使っているけど日本のサーバは使えなかったので、ダウンロードのスピードがネックでした。その点ロリポップは日本のサーバを使っているのでしょうか?スピードが改善できてあと、ツールもしっかりしているか、試してみる価値はあるかなという気がします。
http://sqale.jp/
Heroku はAWSのサーバを使っているけど日本のサーバは使えなかったので、ダウンロードのスピードがネックでした。その点ロリポップは日本のサーバを使っているのでしょうか?スピードが改善できてあと、ツールもしっかりしているか、試してみる価値はあるかなという気がします。
Monday, 23 July 2012
Ruby は殆ど知らなくてもRoRは組めるのですが
RoRはRubyで書かれたフレームワークですが、フレームワークのパーツ(scaffoldやgemなど)を集めて組み立てればRuby自体は基本的なことだけ知っていればあり程度動くウェブアプリケーションは作ることができます。セキュリティーの面でも怖ければ、セッション管理などはライブラリに任せて触らないようにすれば難は逃れられるでしょう。
良いフレームワークは矯正ギブスのようなもので、良い習慣を身につけるのに使えるという話を以前PHPerに聞きました。この意味でRoRはとても良いと思います。最近は、小さなウェブサービスの構築だとRoRを使わずにSinatraで作れるし、実はデータベースが必要なアプリケーションも単純にgemでActiveRecordやMongoIDのようなORマッパーさえ拾ってくればRoRはなくても組めるということにも気がつきます。
もっと自由にRubyを使えるようになりたいと思い、以前に買ったYugui著「はじめてのRuby」を引っ張り出してきたのですが、このコンテクストでは使えないようです。この本はRubyを学び始める人に有益なように譲りたいですね…。Rubyとソフトウェアのデザインパターンを一緒に習得できる書籍を見つけます。
良いフレームワークは矯正ギブスのようなもので、良い習慣を身につけるのに使えるという話を以前PHPerに聞きました。この意味でRoRはとても良いと思います。最近は、小さなウェブサービスの構築だとRoRを使わずにSinatraで作れるし、実はデータベースが必要なアプリケーションも単純にgemでActiveRecordやMongoIDのようなORマッパーさえ拾ってくればRoRはなくても組めるということにも気がつきます。
もっと自由にRubyを使えるようになりたいと思い、以前に買ったYugui著「はじめてのRuby」を引っ張り出してきたのですが、このコンテクストでは使えないようです。この本はRubyを学び始める人に有益なように譲りたいですね…。Rubyとソフトウェアのデザインパターンを一緒に習得できる書籍を見つけます。
Tuesday, 26 June 2012
ROR scaffoldで作られる newとcreateアクションの関係
RoRのscaffoldを用いると、index, new, createを含む一連のアクションが指定したモデルに対して自動で作成されます。自動で作成されるため、これまで理解をせずに使用していたnewとcreateアクションに関して調べる良い機会ができたので、今まで恥ずかしながら”謎”に包まれていた scaffold の挙動を例えばpostというモデルに関して作成した場合に関して覚書します。
scaffoldはRESTfulなURLを用いて巧みにnewやcreate、indexなどにユーザのリクエストをさばいていることが見えます。こんなことを考えた人はすごいですね。createアクションでpost.saveが失敗したときにnewのテンプレートを表示して、更にformのパーシャルに既にエラーメッセージ表示のコードも書いておくというのにも感心します。
最近 TDD (Test Driven Development) を採用する方向で仕事をしています。プログラムを組む前に必要とされる結果を特定するという作業は、ステップごとに明確な知識が必要とさるため、下調べが必要になります。現時点でこれはとても有益なのではないかと感じています。
RoRのTDD環境は現時点でできるだけインダストリースタンダードに従いたいため、RSpecを基本として組んでいます。リクエストテストはCapybara, メールのテストは Email_Spec, mock とstubオブジェクトはfactory_girlという具合です。Cucumber やSporkも導入検討が必要ですね。
scaffoldはRESTfulなURLを用いて巧みにnewやcreate、indexなどにユーザのリクエストをさばいていることが見えます。こんなことを考えた人はすごいですね。createアクションでpost.saveが失敗したときにnewのテンプレートを表示して、更にformのパーシャルに既にエラーメッセージ表示のコードも書いておくというのにも感心します。
最近 TDD (Test Driven Development) を採用する方向で仕事をしています。プログラムを組む前に必要とされる結果を特定するという作業は、ステップごとに明確な知識が必要とさるため、下調べが必要になります。現時点でこれはとても有益なのではないかと感じています。
RoRのTDD環境は現時点でできるだけインダストリースタンダードに従いたいため、RSpecを基本として組んでいます。リクエストテストはCapybara, メールのテストは Email_Spec, mock とstubオブジェクトはfactory_girlという具合です。Cucumber やSporkも導入検討が必要ですね。
Wednesday, 20 June 2012
RoRとmongoidとRSpecでTDDことはじめ
RoRとmongoidとRSpecでテストを動かそうとしたところ、下のようなエラーメッセージが出ました。
undefined method `fixture_path=' for # (NoMethodError)
これに関してはすでに解決している人がいたので、ググって解決です。
Thursday, 7 June 2012
Rails インターナショナライゼーション・ローカライゼーションでの注意点?
Rails プロジェクトのインターナショナライゼーション・ローカライゼーションをしていて、マニュアル
http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-url-params
にそって作業をしているのにurl_forのヘルパーがうまく動きません。メニューから貼っているコントローラ・アクションへのURLリクエストに en や jp などのパラメータがくっついてくれなくてしばらくうろうろしていました。
で、やっと気がついたのはメニューのリンクをhref="/posts"の様にハードコーディングしてしまっていました。これはhref="<%= posts_path %>"のように書き直すことで難なく解決。
私はこういう些細なミスが多くて困ります。そもそも、もっとちゃんとRoRの規約に従ってコーディングをするべきなんでしょうね。
http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-url-params
にそって作業をしているのにurl_forのヘルパーがうまく動きません。メニューから貼っているコントローラ・アクションへのURLリクエストに en や jp などのパラメータがくっついてくれなくてしばらくうろうろしていました。
で、やっと気がついたのはメニューのリンクをhref="/posts"の様にハードコーディングしてしまっていました。これはhref="<%= posts_path %>"のように書き直すことで難なく解決。
私はこういう些細なミスが多くて困ります。そもそも、もっとちゃんとRoRの規約に従ってコーディングをするべきなんでしょうね。
Wednesday, 23 May 2012
日本円でActiveMerchantの金額の指定は*100が必要
Ruby on Rails と ActiveMerchantでショッピングカートを作りました。
https://www.sasayamavegbox.me/
ActiveMerchant本家のサイトやhttp://www.codyfauser.com/2008/1/17/paypal-express-payments-with-activemerchantを参考にして特に問題無く実装はできましたが、日本円に関わる記事はテストまでで、本番環境の実装例はネット上に情報が見つかりませんでした。
上記のサイトでは
としているのですが、この2行目の"5000"が購入者に支払いを請求する金額です。ここを日本円の場合は5000*100にしないと¥50で請求になります。ので、5000*10にすると¥5,000の請求にできます。
https://www.sasayamavegbox.me/
ActiveMerchant本家のサイトやhttp://www.codyfauser.com/2008/1/17/paypal-express-payments-with-activemerchantを参考にして特に問題無く実装はできましたが、日本円に関わる記事はテストまでで、本番環境の実装例はネット上に情報が見つかりませんでした。
上記のサイトでは
def checkout setup_response = gateway.setup_purchase(5000, :ip => request.remote_ip, :return_url => url_for(:action => 'confirm', :only_path => false), :cancel_return_url => url_for(:action => 'index', :only_path => false) ) redirect_to gateway.redirect_url_for(setup_response.token) end
としているのですが、この2行目の"5000"が購入者に支払いを請求する金額です。ここを日本円の場合は5000*100にしないと¥50で請求になります。ので、5000*10にすると¥5,000の請求にできます。
Monday, 21 May 2012
RoR new と create アクションをちゃんと繋ぐ
newとcreateに限った話ではないのですが、今回はこの二つが繋がらずにかなりおばかな時間を費やしてしまったので忘備録です。
Railsのscaffoldデフォルトでは、パラメータをメソッドに渡す際に、例えば postというモデルを使用している時、newアクションで
def new
@post = Post.new
end
としてフォームなどを表示させて、ユーザがフォームに入力をしてsubmitを押すとcreateアクションにpostでフォームのデータを渡します。そこでcreateアクションがデータベースに保存するなどの処理をします。
def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
Do something here...
end
end
この params([:post]) で newからcreateアクションにフォームのデータを渡しているのですが、私はここをparams[:id]としてしまい、newフォームのデータがcreateアクションに渡らなかったので初歩的なことでずいぶん時間を費やしてしまいました…。他の人はこんなミスはしないのでしょうか…。
Subscribe to:
Posts (Atom)