diff --git a/README.md b/README.md index b4da26e9..3755e959 100644 --- a/README.md +++ b/README.md @@ -581,6 +581,28 @@ class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput end ``` +If needed, you can namespace your custom inputs in a module and tell **SimpleForm** to look for +their definition in this module. This can avoid conflicts with other form libraries (like Formtastic) that look up +the global context to find inputs definition too. + +```ruby +# app/inputs/custom_inputs/numeric_input +module CustomInputs + class NumericInput < SimpleForm::Inputs::NumericInput + def input_html_classes + super.push('no-spinner') + end + end +end +``` + +And in the **SimpleForm** initializer : + +```ruby +# config/simple_form.rb +config.custom_inputs_namespaces << "CustomInputs" +``` + ## Custom form builder You can create a custom form builder that uses **Simple Form**. diff --git a/lib/generators/simple_form/templates/config/initializers/simple_form.rb b/lib/generators/simple_form/templates/config/initializers/simple_form.rb index bf7be1cc..2427d4a3 100644 --- a/lib/generators/simple_form/templates/config/initializers/simple_form.rb +++ b/lib/generators/simple_form/templates/config/initializers/simple_form.rb @@ -131,6 +131,10 @@ SimpleForm.setup do |config| # type as key and the wrapper that will be used for all inputs with specified type. # config.wrapper_mappings = { string: :prepend } + # Namespaces where SimpleForm should look for custom input classes that + # override default inputs. + # config.custom_inputs_namespaces << "CustomInputs" + # Default priority for time_zone inputs. # config.time_zone_priority = nil diff --git a/lib/simple_form.rb b/lib/simple_form.rb index 02b01fa7..76b9b74a 100644 --- a/lib/simple_form.rb +++ b/lib/simple_form.rb @@ -130,6 +130,14 @@ See https://github.com/plataformatec/simple_form/pull/997 for more information. mattr_accessor :wrapper_mappings @@wrapper_mappings = nil + # Namespaces where SimpleForm should look for custom input classes that override + # default inputs. Namespaces are given as string to allow lazy loading inputs + # e.g. config.custom_inputs_namespaces << "CustomInputs" + # will try to find CustomInputs::NumericInput when an :integer + # field is called + mattr_accessor :custom_inputs_namespaces + @@custom_inputs_namespaces = [] + # Default priority for time_zone inputs. mattr_accessor :time_zone_priority @@time_zone_priority = nil diff --git a/lib/simple_form/form_builder.rb b/lib/simple_form/form_builder.rb index 5d06aa4d..e9df4f49 100644 --- a/lib/simple_form/form_builder.rb +++ b/lib/simple_form/form_builder.rb @@ -567,6 +567,7 @@ module SimpleForm else camelized = "#{input_type.to_s.camelize}Input" attempt_mapping(camelized, Object) || attempt_mapping(camelized, self.class) || + attempt_mapping_with_custom_namespace(camelized) || raise("No input found for #{input_type}") end end @@ -604,7 +605,9 @@ module SimpleForm def mapping_override(klass) name = klass.name if name =~ /^SimpleForm::Inputs/ - attempt_mapping name.split("::").last, Object + input_name = name.split("::").last + attempt_mapping(input_name, Object) || + attempt_mapping_with_custom_namespace(input_name) end end @@ -617,5 +620,17 @@ module SimpleForm raise if e.message !~ /#{mapping}$/ end end + + def attempt_mapping_with_custom_namespace(input_name) + SimpleForm.custom_inputs_namespaces.each do |namespace| + camelized = [namespace, input_name].join("::") + + if (mapping = attempt_mapping(camelized, Object)) + return mapping + end + end + + nil + end end end diff --git a/test/inputs/discovery_test.rb b/test/inputs/discovery_test.rb index dc97d984..1fcebefb 100644 --- a/test/inputs/discovery_test.rb +++ b/test/inputs/discovery_test.rb @@ -14,6 +14,8 @@ class DiscoveryTest < ActionView::TestCase Object.send :remove_const, :CustomizedInput Object.send :remove_const, :DeprecatedInput Object.send :remove_const, :CollectionSelectInput + CustomInputs.send :remove_const, :PasswordInput + CustomInputs.send :remove_const, :NumericInput end end end @@ -61,6 +63,24 @@ class DiscoveryTest < ActionView::TestCase end end + test 'builder discovers new inputs from configured namespaces if not cached' do + discovery do + swap SimpleForm, custom_inputs_namespaces: ['CustomInputs'] do + with_form_for @user, :password, as: :password + assert_select 'form input#user_password.password-custom-input' + end + end + end + + test 'builder discovers new inputs from top level namespace before the ones from configured namespaces' do + discovery do + swap SimpleForm, custom_inputs_namespaces: ['CustomInputs'] do + with_form_for @user, :age + assert_no_select 'form input#user_age.numeric-custom-input' + end + end + end + test 'new inputs can override the input_html_options' do discovery do with_form_for @user, :active, as: :select diff --git a/test/support/discovery_inputs.rb b/test/support/discovery_inputs.rb index eebf4db8..c627f6f4 100644 --- a/test/support/discovery_inputs.rb +++ b/test/support/discovery_inputs.rb @@ -35,3 +35,17 @@ class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput super.push('chosen') end end + +module CustomInputs + class PasswordInput < SimpleForm::Inputs::PasswordInput + def input_html_classes + super.push('password-custom-input') + end + end + + class NumericInput < SimpleForm::Inputs::PasswordInput + def input_html_classes + super.push('numeric-custom-input') + end + end +end \ No newline at end of file