今更だけど、
<%= form_for @article do |f| %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
こんな感じのフォームビルダーの挙動を換えたい、とか
<%= form_for @article do |f| %>
<%= f.my_field :title %>
<%= f.submit %>
<% end %>
独自のメソッド(この例ではmy_field)を使いたい、とか、そんなときには、 ActionView::Helpers::FormBuilder のサブクラスを作ればよい。
参考
http://guides.rubyonrails.org/form_helpers.html#customizing-form-builders:title=Customizing Form Builders - Ruby on Rails Guides
http://d.hatena.ne.jp/zariganitosh/20080117/1200554948:title=ラベル付きエラー表示付きフォームの雛形が欲しい! - ザリガニが見ていた...。
##基本
class ApplicationFormBuilder < ActionView::Helpers::FormBuilder
def my_field(method, options={})
# 任意の処理
# ここではあらかじめ作成していた同名のヘルパーを使う例
@template.my_field(@object_name, method, objectify_options(options))
end
end
:builderオプションで指定
<%= form_for @article, builder: ApplicationFormBuilder do |f| %>
<%= f.my_field :title %>
<%= f.submit %>
<% end %>
##テスト
よくわかんないけど、僕はこんな感じ。
require 'spec_helper'
describe ApplicationFormBuilder do
let(:object){ mock_model(Article) }
let(:helper){ double(ActionView::Helpers::FormHelper).as_null_object }
let(:builder){ ApplicationFormBuilder.new(:article, object, helper, {}, nil) }
describe '#my_field' do
it 'my_fieldヘルパーを呼び出すこと' do
helper.should_receive(:my_field).with(:article, :name, object: object)
helper.my_field :name
end
# その他オプションを渡したときの挙動とかイロイロ
end
end
##独自FormBuilderを頻繁に使うとき
###独自のビューヘルパ application_form_for を定義する
:builderオプションを省略してコーディング効率と見通しをよくする
def application_form_for(*args, &proc)
args << {} unless args.last.is_a?(Hash)
options = args.last
if args.first.is_a?(Symbol)
options.merge!(as: args.shift)
end
options.merge!({builder: ApplicationFormBuilder})
form_for(*args, &proc)
end
と適当なところで定義しておき
<%= application_form_for @article do |f| %>
<%= f.my_field :title %>
<%= f.submit %>
<% end %>
###いっそデフォルトにしてしまうとか
form_for で独自FormBuilderを使ってしまえばよくね?
config.action_view.default_form_builder = ApplicationFormBuilder
らしいんだけど、initializers内ではうまくいかなかったのでググったらこんなかんじでやってる人がいた。
# http://calvinconaway.com/2011/11/08/how-to-set-a-default-form-builder-in-rails-3-1-while-letting-it-be-autoloaded/
module ActionView
module Helpers
module FormHelper
def form_for_with_application(record, options = {}, &proc)
options[:builder] ||= ApplicationFormBuilder
form_for_without_application(record, options, &proc)
end
def fields_for_with_application(record_name, record_object = nil, options = {}, &block)
options[:builder] ||= ApplicationFormBuilder
fields_for_without_application(record_name, record_object, options, &block)
end
alias_method_chain :form_for, :application
alias_method_chain :fields_for, :application
end
end
end
でもまぁ、ちょっときもいかなぁ。
##独自FormBuilderで render f したときに _form.html.erb が使われるようにしたい
<%= form_for @article, builder: ApplicationFormBuilder do |f| %>
<%= render f %>
<%= f.submit %>
<% end %>
としたときには、「_application_form.html.erb」が選択される。
多分、FormBuilder ごとに部分テンプレートを切り替えられるようにするためだと思うんだけど、 ApplicationFormBuilder をデフォルトとして使うようにした場合には、元々のデフォルトと同じ「_form.html.erb」が選択された方が都合が良い。
ソースを読んでみると、._to_partial_path というメソッドが使われているようなので、これをオーバーライドしてやることにした。
class ApplicationFormBuilder < ActionView::Helpers::FormBuilder
def self._to_partial_path
return @_to_partial_path if defined?(@_to_partial_path)
@_to_partial_path = self == ApplicationFormBuilder ? 'form' : super
end
# 省略
end
でもやっぱりきもいなぁ、あんまりこういうのはしない方が良いと思うけど、どうしてもやるなら。