Override default form builder for a controller

This commit is contained in:
Kevin McPhillips 2015-04-06 22:20:57 -04:00
parent efaec3dd63
commit 2b8acdcd21
10 changed files with 129 additions and 1 deletions

@ -1,3 +1,11 @@
* Add ability to override default form builder for a controller.
class AdminController < ApplicationController
default_form_builder AdminFormBuilder
end
*Kevin McPhillips*
* For actions with no corresponding templates, render `head :no_content` * For actions with no corresponding templates, render `head :no_content`
instead of raising an error. This allows for slimmer API controller instead of raising an error. This allows for slimmer API controller
methods that simply work, without needing further instructions. methods that simply work, without needing further instructions.

@ -12,6 +12,7 @@ module ActionController
autoload :Metal autoload :Metal
autoload :Middleware autoload :Middleware
autoload :Renderer autoload :Renderer
autoload :FormBuilder
autoload_under "metal" do autoload_under "metal" do
autoload :Compatibility autoload :Compatibility

@ -221,6 +221,7 @@ def self.without_modules(*modules)
Cookies, Cookies,
Flash, Flash,
FormBuilder,
RequestForgeryProtection, RequestForgeryProtection,
ForceSSL, ForceSSL,
Streaming, Streaming,

@ -0,0 +1,48 @@
module ActionController
# Override the default form builder for all views rendered by this
# controller and any of its descendents. Accepts a sublcass of
# +ActionView::Helpers::FormBuilder+.
#
# For example, given a form builder:
#
# class AdminFormBuilder < ActionView::Helpers::FormBuilder
# def special_field(name)
# end
# end
#
# The controller specifies a form builder as its default:
#
# class AdminAreaController < ApplicationController
# default_form_builder AdminFormBuilder
# end
#
# Then in the view any form using +form_for+ will be an instance of the
# specified form builder:
#
# <%= form_for(@instance) do |builder| %>
# <%= builder.special_field(:name) %>
# <%= end %>
module FormBuilder
extend ActiveSupport::Concern
included do
class_attribute :_default_form_builder, instance_accessor: false
end
module ClassMethods
# Set the form builder to be used as the default for all forms
# in the views rendered by this controller and its subclasses.
#
# ==== Parameters
# * <tt>builder</tt> - Default form builder, an instance of +ActionView::Helpers::FormBuilder+
def default_form_builder(builder)
self._default_form_builder = builder
end
end
# Default form builder for the controller
def default_form_builder
self.class._default_form_builder
end
end
end

@ -0,0 +1,17 @@
require 'abstract_unit'
class FormBuilderController < ActionController::Base
class SpecializedFormBuilder < ActionView::Helpers::FormBuilder ; end
default_form_builder SpecializedFormBuilder
end
class ControllerFormBuilderTest < ActiveSupport::TestCase
setup do
@controller = FormBuilderController.new
end
def test_default_form_builder_assigned
assert_equal FormBuilderController::SpecializedFormBuilder, @controller.default_form_builder
end
end

@ -1,3 +1,8 @@
* Load the `default_form_builder` from the controller on initialization, which overrides
the global config if it is present.
*Kevin McPhillips*
* Accept lambda as `child_index` option in `fields_for` method. * Accept lambda as `child_index` option in `fields_for` method.
*Karol Galanciak* *Karol Galanciak*

@ -14,6 +14,7 @@ def assign_controller(controller)
if @_controller = controller if @_controller = controller
@_request = controller.request if controller.respond_to?(:request) @_request = controller.request if controller.respond_to?(:request)
@_config = controller.config.inheritable_copy if controller.respond_to?(:config) @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
@_default_form_builder = controller.default_form_builder if controller.respond_to?(:default_form_builder)
end end
end end

@ -114,6 +114,8 @@ module FormHelper
include ModelNaming include ModelNaming
include RecordIdentifier include RecordIdentifier
attr_internal :default_form_builder
# Creates a form that allows the user to create or update the attributes # Creates a form that allows the user to create or update the attributes
# of a specific model object. # of a specific model object.
# #
@ -1233,7 +1235,7 @@ def instantiate_builder(record_name, record_object, options)
end end
def default_form_builder_class def default_form_builder_class
builder = ActionView::Base.default_form_builder builder = default_form_builder || ActionView::Base.default_form_builder
builder.respond_to?(:constantize) ? builder.constantize : builder builder.respond_to?(:constantize) ? builder.constantize : builder
end end
end end

@ -0,0 +1,21 @@
require 'abstract_unit'
class ControllerHelperTest < ActionView::TestCase
tests ActionView::Helpers::ControllerHelper
class SpecializedFormBuilder < ActionView::Helpers::FormBuilder ; end
def test_assign_controller_sets_default_form_builder
@controller = OpenStruct.new(default_form_builder: SpecializedFormBuilder)
assign_controller(@controller)
assert_equal SpecializedFormBuilder, self.default_form_builder
end
def test_assign_controller_skips_default_form_builder
@controller = OpenStruct.new
assign_controller(@controller)
assert_nil self.default_form_builder
end
end

@ -3269,6 +3269,30 @@ def test_lazy_loading_default_form_builder
ActionView::Base.default_form_builder = old_default_form_builder ActionView::Base.default_form_builder = old_default_form_builder
end end
def test_form_builder_override
self.default_form_builder = LabelledFormBuilder
output_buffer = fields_for(:post, @post) do |f|
concat f.text_field(:title)
end
expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
assert_dom_equal expected, output_buffer
end
def test_lazy_loading_form_builder_override
self.default_form_builder = "FormHelperTest::LabelledFormBuilder"
output_buffer = fields_for(:post, @post) do |f|
concat f.text_field(:title)
end
expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
assert_dom_equal expected, output_buffer
end
def test_fields_for_with_labelled_builder def test_fields_for_with_labelled_builder
output_buffer = fields_for(:post, @post, builder: LabelledFormBuilder) do |f| output_buffer = fields_for(:post, @post, builder: LabelledFormBuilder) do |f|
concat f.text_field(:title) concat f.text_field(:title)