Skinny Controller, Fat Model
http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
ロジックはなるべくモデルで定義して、コントーラーはシンプルに保ちましょうというpractice(習慣)
Object.tap
オブジェクトの状態を任意の場所で確認できるため、便利。従来のデバッグ方法のように構文を大きく崩す事なく実行できるところが良い。
class Object def tap yield self self end end
使用例
blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah } ( k + 1 ) / ( ( q - t ).tap { |i| p i } / 2 ) def blah @things.map { |x| x.length }.inject( 0 ) { |a, b| a + b }.tap { |sum| p sum } end
ActiveRecord::Errorsあたりのローカライズ
このサイトを参考にしました。というより全くこの通りにやりました。
validate_関連で、エラーが出た際にいろいろ英語がでてきちゃうのでその辺りを修正。まずは、フォームのエレメント名がそのままでちゃうのを防ぐために、modelの中で、下記のようにローカライズ
class Member < ActiveRecord::Base class << self HUMANIZED_ATTRIBUTE_KEY_NAMES = { "email" => "メールアドレス", "password" => "パスワード", "nickname" => "ニックネーム", "gender" => "性別", } def human_attribute_name(attribute_key_name) HUMANIZED_ATTRIBUTE_KEY_NAMES[attribute_key_name] || super end end
それから、"email is blank."とか出てくる" is blank"とかのメッセージをローカライズ。これは個々のvalidate_...でmessagesを指定すれば良いのだけど、毎回やるのはDRYじゃないので、まとめて設定。environment.rbの中に下記のように設定
ActiveRecord::Errors.default_error_messages[:inclusion] = "が選択肢にありません" ActiveRecord::Errors.default_error_messages[:exclusion] = "が予約されています" ActiveRecord::Errors.default_error_messages[:invalid] = "が不正です" ActiveRecord::Errors.default_error_messages[:confirmation] = "が確認内容があっていません" ActiveRecord::Errors.default_error_messages[:accepted] = "が許可されていません" ActiveRecord::Errors.default_error_messages[:empty] = "が入力されていません" ActiveRecord::Errors.default_error_messages[:blank] = "が入力されていません" ActiveRecord::Errors.default_error_messages[:too_long] = "が長すぎます(最長%d文字)" ActiveRecord::Errors.default_error_messages[:too_short] = "が短すぎます(最短%d文字)" ActiveRecord::Errors.default_error_messages[:wrong_length] = "が間違った長さです(長さ%d文字)" ActiveRecord::Errors.default_error_messages[:taken] = "が既に使われています" ActiveRecord::Errors.default_error_messages[:not_a_number] = "が無効な数値です"
これでもまだ、エラーメッセージ全体を表示する<%=error_messages_for '...' %>の部分で英語がでてくるので、これの変わりになるヘルパーを作成。app/helpers/application_helper.rbの中に下記を追加
def template_error_messages_for (object_name, options = {}) options = options.symbolize_keys object = instance_variable_get("@#{object_name}") unless object.errors.empty? render :partial=>"system/error_messages_for", :locals=>{:messages=>object.errors.full_messages, :object=>object} end end
で、上で使うpartialを作成する。app/views/system/_error_messages_for.rhtmlとして、下記のように作成。mobile on railsを使っている場合はapp/views/views_mobile/system/_error_messages_for.rhtmlも作成する
<div class="errorExplanation" id="errorExplanation"> <h2><%= messages.size %>個のエラーが発生しました</h2> <p>次の項目に問題があります</p> <ul> <% for mes in messages %><li><%= mes %></li><% end %> </ul> </div>
あとはローカライズではないけど、エラーが出たときにフォームフィールドが<div class="fieldWithErrors">で囲まれてしまう問題を回避する。
ActionView::Base.field_error_proc = lambda{|tag, instance| "<span class=\"fieldWithErrors\">#{tag}</span>" }
今日のrailsはこんな感じ。しかし検索で見つけ出した対応策が2005-08-02付けとかだったらしたら、もうobsoleteだろうとか思って不安になってくる。一応コードを試すとちゃんと動くんだけど、実際にはもっとスマートなやり方があるんじゃないかとか。まぁそういうのは使っていくうちに気づくだろう。
ActiveRecord::RecordNotFound
Object.findの時にはraiseされるが、find_by_fooのように確実に結果があることを期待していない場合などにはraiseされない。今日の発見はこんなところ。
携帯用に入力モード指定ヘルパー
mobile on railsというプラグインを入れてみたけど、これはとても便利。しかし、これには携帯の入力モードを指定できるようなコードは無かったっぽいので、その辺りを自作。
まず、app/helpers/mobile_tag_builder.rbというファイルを作成して、中身を下記のように記述
class MobileTagBuilder < ActionView::Helpers::FormBuilder def text_field (field, options = {}) if options.has_key?(:mobile_input_style) if @template.controller.request.mobile_carrier == ActionController::Mobile::DoCoMo options[:istyle] = mobile_input_style_by_carrier(options[:mobile_input_style]); elsif @template.controller.request.mobile_carrier == ActionController::Mobile::AU options[:istyle] = mobile_input_style_by_carrier(options[:mobile_input_style]); elsif @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank options[:mode] = mobile_input_style_by_carrier(options[:mobile_input_style]); end options.delete(:mobile_input_style) end super(field, options) end private def mobile_input_style_by_carrier (input_style) # ひらがな if input_style == :input_style_hiragana if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :hiragana else :'1' end # カタナカ elsif input_style == :input_style_katakana if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank return :katakana else :'1' end # 半角カタナカ elsif input_style == :input_style_hankakukana if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :hankakukana else :'2' end # 英字 elsif input_style == :input_style_alphabet if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :alphabet else :'3' end # 数字 elsif input_style == :input_style_numeric if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :numeric else :'4' end # なんでも elsif input_style == :input_style_any if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :any else :'1' end # メールアドレス elsif input_style == :input_style_emailaddr if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :emailaddr else :'3' end # 電話番号 elsif input_style == :input_style_phonenumber if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :phonenumber else :'4' end # url elsif input_style == :input_style_url if @template.controller.request.mobile_carrier == ActionController::Mobile::SoftBank :url else :'3' end end end end
ソフトバンクの入力モードの指定が一番多いので、それを基準にして、ドコモとAUはマッピング。
requestオブジェクトをどうやって参照すれば良いのかちょい悩んだが、@templateインスタンス経由でアクセスできた。これは素直に本読めば普通に書いてある事だった。
とりあえず、このあとはビューで、下記のようにして呼び出し
<% form_for :signup, :url => { :action => :signup }, :builder => MobileTagBuilder do |form| %> <%= form.text_field :email, :size => 20, :maxlength => 255, :mobile_input_style => :input_style_alphabet %>
これが正しいやり方なのかどうか分からないけど、とりあえず動いているみたい。絶対ruby的な書き方じゃないだろうし…