From 1520353fc2fac441514651f0a441577a241820ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 27 Apr 2011 20:55:03 +0200 Subject: [PATCH] Add inputs discovery. --- Gemfile | 2 +- Gemfile.lock | 6 ++-- lib/simple_form.rb | 8 +++++ lib/simple_form/form_builder.rb | 53 +++++++++++++++++++++++++++++++-- test/test_helper.rb | 6 ++++ 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 9d1f3e7d..a0a3e40f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "http://rubygems.org" -gem "rails", "~> 3.0.0" +gem "rails", "~> 3.0.7" group :test do gem "mocha", :require => false diff --git a/Gemfile.lock b/Gemfile.lock index 6dac6d8f..d973be12 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,7 +35,7 @@ GEM abstract (>= 1.0.0) i18n (0.5.0) linecache (0.43) - mail (2.2.17) + mail (2.2.18) activesupport (>= 2.3.6) i18n (>= 0.4.0) mime-types (~> 1.16) @@ -71,12 +71,12 @@ GEM thor (0.14.6) treetop (1.4.9) polyglot (>= 0.3.1) - tzinfo (0.3.26) + tzinfo (0.3.27) PLATFORMS ruby DEPENDENCIES mocha - rails (~> 3.0.0) + rails (~> 3.0.7) ruby-debug diff --git a/lib/simple_form.rb b/lib/simple_form.rb index 5076ff09..fe3d8d32 100644 --- a/lib/simple_form.rb +++ b/lib/simple_form.rb @@ -128,6 +128,14 @@ module SimpleForm mattr_accessor :translate @@translate = true + # Automatically discover new inputs in Rails' autoload path. + mattr_accessor :inputs_discovery + @@inputs_discovery = true + + # Cache simple form inputs discovery + mattr_accessor :cache_discovery + @@cache_discovery = !Rails.env.development? + # Default way to setup SimpleForm. Run rails generate simple_form:install # to create a fresh initializer with all configuration values. def self.setup diff --git a/lib/simple_form/form_builder.rb b/lib/simple_form/form_builder.rb index b6f4989d..e5d70f35 100644 --- a/lib/simple_form/form_builder.rb +++ b/lib/simple_form/form_builder.rb @@ -13,6 +13,10 @@ module SimpleForm map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput map_type :boolean, :to => SimpleForm::Inputs::BooleanInput + def self.discovery_cache + @discovery_cache ||= {} + end + # Basic input helper, combines all components in the stack to generate # input html based on options the user define and some guesses through # database column information. By default a call to input will generate @@ -85,8 +89,7 @@ module SimpleForm if block_given? SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block).render else - klass = self.class.mappings[input_type] || self.class.const_get("#{input_type.to_s.camelize}Input") - klass.new(self, attribute_name, column, input_type, options).render + find_mapping(input_type).new(self, attribute_name, column, input_type, options).render end end alias :attribute :input @@ -314,5 +317,51 @@ module SimpleForm @object.class.reflect_on_association(association) end end + + # Attempts to find a mapping. It follows the following rules: + # + # 1) It tries to find a registered mapping, if succeeds: + # a) If a mapping is found, try to find an alternative as #{mapping}Input + # b) Or use the found mapping + # 2) If not, fallbacks to #{input_type}Input + # 3) If not, fallbacks to SimpleForm::Inputs::#{input_type} + def find_mapping(input_type) #:nodoc: + discovery_cache[input_type] ||= + if mapping = self.class.mappings[input_type] + mapping_override(mapping) || mapping + else + camelized = "#{input_type.to_s.camelize}Input" + attempt_mapping(camelized, Object) || attempt_mapping(camelized, self.class) || + raise("No input found for #{input_type}") + end + end + + # If cache_discovery is enabled, use the class level cache that persists + # between requests, otherwise use the instance one. + def discovery_cache #:nodoc: + if SimpleForm.cache_discovery + self.class.discovery_cache + else + @discovery_cache ||= {} + end + end + + def mapping_override(klass) #:nodoc: + name = klass.name + if name =~ /^SimpleForm::Inputs/ + attempt = "#{name.split("::").last}Input" + attempt_mapping attempt, Object + end + end + + def attempt_mapping(mapping, at) #:nodoc: + return if SimpleForm.inputs_discovery == false && at == Object + + begin + at.const_get(mapping) + rescue NameError => e + e.message =~ /#{mapping}$/ ? nil : raise + end + end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6c1b686a..e3d2e884 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,6 +13,12 @@ require 'action_view/template' require 'active_support/core_ext/module/deprecation' require 'action_view/test_case' +module Rails + def self.env + ActiveSupport::StringInquirer.new("test") + end +end + $:.unshift File.expand_path("../../lib", __FILE__) require 'simple_form'