LoginSignup
42
38

More than 1 year has passed since last update.

独自FormBuilderを使うメモ

Last updated at Posted at 2013-04-05

今更だけど、

<%= 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=ラベル付きエラー表示付きフォームの雛形が欲しい! - ザリガニが見ていた...。

基本

app/helpers/application_form_builder.rb
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 %>

テスト

よくわかんないけど、僕はこんな感じ。

spec/bulders/application_form_builder_spec.rb
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内ではうまくいかなかったのでググったらこんなかんじでやってる人がいた。

config/initializers/default_form_builder.rb
# 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

でもやっぱりきもいなぁ、あんまりこういうのはしない方が良いと思うけど、どうしてもやるなら。

42
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
38