lib/active_admin/form_builder.rb in yousty-activeadmin-1.0.4.pre vs lib/active_admin/form_builder.rb in yousty-activeadmin-1.0.5.pre
- old
+ new
@@ -1,147 +1,136 @@
-# Note for posterity:
-#
-# Here we have two core customizations on top of Formtastic. First, this allows
-# you to build forms in the AA DSL without dealing with the HTML return value of
-# individual form methods (hence the +form_buffers+ object). Second, this provides
-# an intuitive way to build has_many associated records in the same form.
-#
+# Provides an intuitive way to build has_many associated records in the same form.
+module Formtastic
+ module Inputs
+ module Base
+ def input_wrapping(&block)
+ html = super
+ template.concat(html) if template.output_buffer && template.assigns['has_many_block']
+ html
+ end
+ end
+ end
+end
+
module ActiveAdmin
class FormBuilder < ::Formtastic::FormBuilder
+ self.input_namespaces = [::Object, ::ActiveAdmin::Inputs, ::Formtastic::Inputs]
- attr_reader :form_buffers
+ # TODO: remove both class finders after formtastic 4 (where it will be default)
+ self.input_class_finder = ::Formtastic::InputClassFinder
+ self.action_class_finder = ::Formtastic::ActionClassFinder
- def initialize(*args)
- @form_buffers = ["".html_safe]
- super
- end
-
- def inputs(*args, &block)
- @use_form_buffer = block_given?
- form_buffers.last << with_new_form_buffer{ super }
- end
-
- # If this `input` call is inside a `inputs` block, add the content
- # to the form buffer. Else, return it directly.
- def input(method, *args)
- content = with_new_form_buffer{ super }
- @use_form_buffer ? form_buffers.last << content : content
- end
-
- def cancel_link(url = {:action => "index"}, html_options = {}, li_attrs = {})
+ def cancel_link(url = {action: "index"}, html_options = {}, li_attrs = {})
li_attrs[:class] ||= "cancel"
li_content = template.link_to I18n.t('active_admin.cancel'), url, html_options
- form_buffers.last << template.content_tag(:li, li_content, li_attrs)
+ template.content_tag(:li, li_content, li_attrs)
end
- def actions(*args, &block)
- form_buffers.last << with_new_form_buffer do
- block_given? ? super : super{ commit_action_with_cancel_link }
- end
- end
+ attr_accessor :already_in_an_inputs_block
- def action(*args)
- form_buffers.last << with_new_form_buffer{ super }
+ def assoc_heading(assoc)
+ object.class.reflect_on_association(assoc).klass.model_name.
+ human(count: ::ActiveAdmin::Helpers::I18n::PLURAL_MANY_COUNT)
end
- def commit_action_with_cancel_link
- action(:submit)
- cancel_link
- end
-
def has_many(assoc, options = {}, &block)
- options = {for: assoc, new_record: true}.merge options
- options[:class] ||= ""
- options[:class] << "inputs has_many_fields"
+ # remove options that should not render as attributes
+ custom_settings = :new_record, :allow_destroy, :heading, :sortable, :sortable_start
+ builder_options = {new_record: true}.merge! options.slice *custom_settings
+ options = {for: assoc }.merge! options.except *custom_settings
+ options[:class] = [options[:class], "inputs has_many_fields"].compact.join(' ')
+ sortable_column = builder_options[:sortable]
+ sortable_start = builder_options.fetch(:sortable_start, 0)
- # Add Delete Links
- form_block = proc do |has_many_form|
- index = parent_child_index options[:parent] if options[:parent]
- contents = block.call has_many_form, index
+ if sortable_column
+ options[:for] = [assoc, sorted_children(assoc, sortable_column)]
+ end
- if has_many_form.object.new_record?
- contents << template.content_tag(:li) do
- template.link_to I18n.t('active_admin.has_many_remove'), "#", class: 'button has_many_remove'
- end
- elsif options[:allow_destroy]
- has_many_form.input :_destroy, as: :boolean, wrapper_html: {class: 'has_many_delete'},
- label: I18n.t('active_admin.has_many_delete')
+ html = "".html_safe
+ unless builder_options.key?(:heading) && !builder_options[:heading]
+ html << template.content_tag(:h3) do
+ builder_options[:heading] || assoc_heading(assoc)
end
- contents
end
- form_buffers.last << with_new_form_buffer do
- template.content_tag :div, class: "has_many #{assoc}" do
- unless options.key?(:heading) && !options[:heading]
- form_buffers.last << template.content_tag(:h3) do
- options[:heading] || object.class.reflect_on_association(assoc).klass.model_name.human(count: 1.1)
- end
- end
+ html << template.capture do
+ contents = "".html_safe
+ form_block = proc do |has_many_form|
+ index = parent_child_index options[:parent] if options[:parent]
+ block.call has_many_form, index
+ template.concat has_many_actions(has_many_form, builder_options, "".html_safe)
+ end
+
+ template.assign('has_many_block'=> true)
+ contents = without_wrapper { inputs(options, &form_block) }
- inputs options, &form_block
-
- form_buffers.last << js_for_has_many(assoc, form_block, template, options[:new_record]) if options[:new_record]
+ if builder_options[:new_record]
+ contents << js_for_has_many(assoc, form_block, template, builder_options[:new_record], options[:class])
+ else
+ contents
end
end
- end
- def semantic_errors(*args)
- form_buffers.last << with_new_form_buffer{ super }
+ tag = @already_in_an_inputs_block ? :li : :div
+ html = template.content_tag(tag, html, class: "has_many_container #{assoc}", 'data-sortable' => sortable_column, 'data-sortable-start' => sortable_start)
+ template.concat(html) if template.output_buffer
+ html
end
protected
- def active_admin_input_class_name(as)
- "ActiveAdmin::Inputs::#{as.to_s.camelize}Input"
- end
+ def has_many_actions(has_many_form, builder_options, contents)
+ if has_many_form.object.new_record?
+ contents << template.content_tag(:li) do
+ template.link_to I18n.t('active_admin.has_many_remove'), "#", class: 'button has_many_remove'
+ end
+ elsif builder_options[:allow_destroy]
+ has_many_form.input(:_destroy, as: :boolean,
+ wrapper_html: {class: 'has_many_delete'},
+ label: I18n.t('active_admin.has_many_delete'))
+ end
- def input_class(as)
- @input_classes_cache ||= {}
- @input_classes_cache[as] ||= begin
- begin
- custom_input_class_name(as).constantize
- rescue NameError
- begin
- active_admin_input_class_name(as).constantize
- rescue NameError
- standard_input_class_name(as).constantize
- end
+ if builder_options[:sortable]
+ has_many_form.input builder_options[:sortable], as: :hidden
+
+ contents << template.content_tag(:li, class: 'handle') do
+ Iconic.icon :move_vertical
end
- rescue NameError
- raise Formtastic::UnknownInputError, "Unable to find input class for #{as}"
end
+
+ contents
end
- # This method calls the block it's passed (in our case, the `f.inputs` block)
- # and wraps the resulting HTML in a fieldset. If your block doesn't have a
- # valid return value but it was otherwise built correctly, we instead use
- # the most recent part of the Active Admin form buffer.
- def field_set_and_list_wrapping(*args, &block)
- block_given? ? super{
- (val = yield).is_a?(String) ? val : form_buffers.last
- } : super
+ def sorted_children(assoc, column)
+ object.public_send(assoc).sort_by do |o|
+ attribute = o.public_send column
+ [attribute.nil? ? Float::INFINITY : attribute, o.id || Float::INFINITY]
+ end
end
private
- def with_new_form_buffer
- form_buffers << ''.html_safe
- return_value = (yield || '').html_safe
- form_buffers.pop
- return_value
+ def without_wrapper
+ is_being_wrapped = @already_in_an_inputs_block
+ @already_in_an_inputs_block = false
+
+ html = yield
+
+ @already_in_an_inputs_block = is_being_wrapped
+ html
end
# Capture the ADD JS
- def js_for_has_many(assoc, form_block, template, new_record)
+ def js_for_has_many(assoc, form_block, template, new_record, class_string)
assoc_reflection = object.class.reflect_on_association assoc
assoc_name = assoc_reflection.klass.model_name
- placeholder = "NEW_#{assoc_name.to_s.upcase.split(' ').join('_')}_RECORD"
+ placeholder = "NEW_#{assoc_name.to_s.underscore.upcase.gsub(/\//, '_')}_RECORD"
opts = {
- :for => [assoc, assoc_reflection.klass.new],
- :class => "inputs has_many_fields",
- :for_options => { child_index: placeholder }
+ for: [assoc, assoc_reflection.klass.new],
+ class: class_string,
+ for_options: { child_index: placeholder }
}
- html = with_new_form_buffer{ inputs_for_nested_attributes opts, &form_block }
+ html = template.capture{ inputs_for_nested_attributes opts, &form_block }
text = new_record.is_a?(String) ? new_record : I18n.t('active_admin.has_many_new', model: assoc_name.human)
template.link_to text, '#', class: "button has_many_add", data: {
html: CGI.escapeHTML(html).html_safe, placeholder: placeholder
}