formtastic 3.0.0 → 3.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -0
- data/Appraisals +4 -0
- data/CHANGELOG +12 -22
- data/DEPRECATIONS +47 -0
- data/README.textile +9 -4
- data/formtastic.gemspec +3 -3
- data/gemfiles/rails_4.2.gemfile +7 -0
- data/lib/formtastic.rb +13 -7
- data/lib/formtastic/action_class_finder.rb +18 -0
- data/lib/formtastic/deprecation.rb +42 -0
- data/lib/formtastic/form_builder.rb +12 -6
- data/lib/formtastic/helpers/action_helper.rb +43 -6
- data/lib/formtastic/helpers/form_helper.rb +2 -2
- data/lib/formtastic/helpers/input_helper.rb +71 -31
- data/lib/formtastic/html_attributes.rb +12 -1
- data/lib/formtastic/input_class_finder.rb +18 -0
- data/lib/formtastic/inputs.rb +1 -0
- data/lib/formtastic/inputs/base.rb +11 -12
- data/lib/formtastic/inputs/base/choices.rb +1 -1
- data/lib/formtastic/inputs/base/collections.rb +1 -1
- data/lib/formtastic/inputs/base/html.rb +3 -3
- data/lib/formtastic/inputs/datalist_input.rb +41 -0
- data/lib/formtastic/inputs/file_input.rb +2 -2
- data/lib/formtastic/namespaced_class_finder.rb +89 -0
- data/lib/formtastic/util.rb +1 -1
- data/lib/formtastic/version.rb +1 -1
- data/lib/generators/templates/formtastic.rb +20 -0
- data/spec/action_class_finder_spec.rb +12 -0
- data/spec/builder/custom_builder_spec.rb +2 -2
- data/spec/builder/semantic_fields_for_spec.rb +4 -4
- data/spec/helpers/action_helper_spec.rb +9 -355
- data/spec/helpers/form_helper_spec.rb +11 -1
- data/spec/helpers/input_helper_spec.rb +1 -916
- data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
- data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
- data/spec/input_class_finder_spec.rb +10 -0
- data/spec/inputs/check_boxes_input_spec.rb +2 -2
- data/spec/inputs/datalist_input_spec.rb +61 -0
- data/spec/inputs/select_input_spec.rb +1 -1
- data/spec/localizer_spec.rb +2 -2
- data/spec/namespaced_class_finder_spec.rb +79 -0
- data/spec/spec_helper.rb +17 -7
- data/spec/support/custom_macros.rb +22 -4
- data/spec/support/shared_examples.rb +1244 -0
- data/spec/support/specialized_class_finder_shared_example.rb +27 -0
- data/spec/support/test_environment.rb +1 -1
- data/spec/util_spec.rb +20 -6
- metadata +66 -15
- checksums.yaml +0 -15
| @@ -36,6 +36,9 @@ module Formtastic | |
| 36 36 | 
             
                # @see Formtastic::Helpers::InputsHelper#inputs
         | 
| 37 37 | 
             
                # @see Formtastic::Helpers::FormHelper#semantic_form_for
         | 
| 38 38 | 
             
                module InputHelper
         | 
| 39 | 
            +
                  INPUT_CLASS_DEPRECATION = 'configure Formtastic::FormBuilder.input_class_finder instead'.freeze
         | 
| 40 | 
            +
                  private_constant(:INPUT_CLASS_DEPRECATION)
         | 
| 41 | 
            +
             | 
| 39 42 | 
             
                  include Formtastic::Helpers::Reflection
         | 
| 40 43 | 
             
                  include Formtastic::Helpers::FileColumnDetection
         | 
| 41 44 |  | 
| @@ -86,26 +89,26 @@ module Formtastic | |
| 86 89 | 
             
                  #
         | 
| 87 90 | 
             
                  # Available input styles:
         | 
| 88 91 | 
             
                  #
         | 
| 89 | 
            -
                  # * `:boolean` | 
| 90 | 
            -
                  # * `:check_boxes` | 
| 91 | 
            -
                  # * `:color` | 
| 92 | 
            -
                  # * `:country` | 
| 93 | 
            -
                  # * `:datetime_select` | 
| 94 | 
            -
                  # * `:date_select` | 
| 95 | 
            -
                  # * `:email` | 
| 96 | 
            -
                  # * `:file` | 
| 97 | 
            -
                  # * `:hidden` | 
| 98 | 
            -
                  # * `:number` | 
| 99 | 
            -
                  # * `:password` | 
| 100 | 
            -
                  # * `:phone` | 
| 101 | 
            -
                  # * `:radio` | 
| 102 | 
            -
                  # * `:search` | 
| 103 | 
            -
                  # * `:select` | 
| 104 | 
            -
                  # * `:string` | 
| 105 | 
            -
                  # * `:text` | 
| 106 | 
            -
                  # * `:time_zone` | 
| 107 | 
            -
                  # * `:time_select` | 
| 108 | 
            -
                  # * `:url` | 
| 92 | 
            +
                  # * `:boolean`          (see {Inputs::BooleanInput})
         | 
| 93 | 
            +
                  # * `:check_boxes`      (see {Inputs::CheckBoxesInput})
         | 
| 94 | 
            +
                  # * `:color`            (see {Inputs::ColorInput})
         | 
| 95 | 
            +
                  # * `:country`          (see {Inputs::CountryInput})
         | 
| 96 | 
            +
                  # * `:datetime_select`  (see {Inputs::DatetimeSelectInput})
         | 
| 97 | 
            +
                  # * `:date_select`      (see {Inputs::DateSelectInput})
         | 
| 98 | 
            +
                  # * `:email`            (see {Inputs::EmailInput})
         | 
| 99 | 
            +
                  # * `:file`             (see {Inputs::FileInput})
         | 
| 100 | 
            +
                  # * `:hidden`           (see {Inputs::HiddenInput})
         | 
| 101 | 
            +
                  # * `:number`           (see {Inputs::NumberInput})
         | 
| 102 | 
            +
                  # * `:password`         (see {Inputs::PasswordInput})
         | 
| 103 | 
            +
                  # * `:phone`            (see {Inputs::PhoneInput})
         | 
| 104 | 
            +
                  # * `:radio`            (see {Inputs::RadioInput})
         | 
| 105 | 
            +
                  # * `:search`           (see {Inputs::SearchInput})
         | 
| 106 | 
            +
                  # * `:select`           (see {Inputs::SelectInput})
         | 
| 107 | 
            +
                  # * `:string`           (see {Inputs::StringInput})
         | 
| 108 | 
            +
                  # * `:text`             (see {Inputs::TextInput})
         | 
| 109 | 
            +
                  # * `:time_zone`        (see {Inputs::TimeZoneInput})
         | 
| 110 | 
            +
                  # * `:time_select`      (see {Inputs::TimeSelectInput})
         | 
| 111 | 
            +
                  # * `:url`              (see {Inputs::UrlInput})
         | 
| 109 112 | 
             
                  #
         | 
| 110 113 | 
             
                  # Calling `:as => :string` (for example) will call `#to_html` on a new instance of
         | 
| 111 114 | 
             
                  # `Formtastic::Inputs::StringInput`. Before this, Formtastic will try to instantiate a top-level
         | 
| @@ -233,7 +236,7 @@ module Formtastic | |
| 233 236 | 
             
                  # @todo Many many more examples. Some of the detail probably needs to be pushed out to the relevant methods too.
         | 
| 234 237 | 
             
                  # @todo More i18n examples.
         | 
| 235 238 | 
             
                  def input(method, options = {})
         | 
| 236 | 
            -
                    method = method.to_sym | 
| 239 | 
            +
                    method = method.to_sym
         | 
| 237 240 | 
             
                    options = options.dup # Allow options to be shared without being tainted by Formtastic
         | 
| 238 241 | 
             
                    options[:as] ||= default_input_type(method, options)
         | 
| 239 242 |  | 
| @@ -299,15 +302,21 @@ module Formtastic | |
| 299 302 |  | 
| 300 303 | 
             
                  # Get a column object for a specified attribute method - if possible.
         | 
| 301 304 | 
             
                  def column_for(method) #:nodoc:
         | 
| 302 | 
            -
                     | 
| 305 | 
            +
                    if @object.respond_to?(:column_for_attribute)
         | 
| 306 | 
            +
                      # Remove deprecation wrapper & review after Rails 5.0 ships
         | 
| 307 | 
            +
                      ActiveSupport::Deprecation.silence do
         | 
| 308 | 
            +
                        @object.column_for_attribute(method)
         | 
| 309 | 
            +
                      end
         | 
| 310 | 
            +
                    end
         | 
| 303 311 | 
             
                  end
         | 
| 304 312 |  | 
| 305 | 
            -
                  # Takes the `:as` option and attempts to return the corresponding input | 
| 306 | 
            -
                  # `:as => : | 
| 307 | 
            -
                  #  | 
| 313 | 
            +
                  # Takes the `:as` option and attempts to return the corresponding input
         | 
| 314 | 
            +
                  # class. In the case of `:as => :awesome` it will first attempt to find a
         | 
| 315 | 
            +
                  # top level `AwesomeInput` class (to allow the application to subclass
         | 
| 316 | 
            +
                  # and modify to suit), falling back to `Formtastic::Inputs::AwesomeInput`.
         | 
| 308 317 | 
             
                  #
         | 
| 309 | 
            -
                  #  | 
| 310 | 
            -
                  #  | 
| 318 | 
            +
                  # Custom input namespaces to look into can be configured via the
         | 
| 319 | 
            +
                  # .input_namespaces +FormBuilder+ configuration setting.
         | 
| 311 320 | 
             
                  #
         | 
| 312 321 | 
             
                  # @param [Symbol] as A symbol representing the type of input to render
         | 
| 313 322 | 
             
                  # @raise [Formtastic::UnknownInputError] An appropriate input class could not be found
         | 
| @@ -319,8 +328,22 @@ module Formtastic | |
| 319 328 | 
             
                  #
         | 
| 320 329 | 
             
                  # @example When a top-level class is found
         | 
| 321 330 | 
             
                  #   input_class(:string) #=> StringInput
         | 
| 322 | 
            -
                  #   input_class(:awesome) #=> AwesomeInput | 
| 331 | 
            +
                  #   input_class(:awesome) #=> AwesomeInput
         | 
| 332 | 
            +
             | 
| 333 | 
            +
                  def namespaced_input_class(as)
         | 
| 334 | 
            +
                    @input_class_finder ||= input_class_finder.new(self)
         | 
| 335 | 
            +
                    @input_class_finder.find(as)
         | 
| 336 | 
            +
                  rescue Formtastic::InputClassFinder::NotFoundError
         | 
| 337 | 
            +
                    raise Formtastic::UnknownInputError, "Unable to find input #{$!.message}"
         | 
| 338 | 
            +
                  end
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                  # @api private
         | 
| 341 | 
            +
                  # @deprecated Use {#namespaced_input_class} instead.
         | 
| 323 342 | 
             
                  def input_class(as)
         | 
| 343 | 
            +
                    return namespaced_input_class(as) if input_class_finder
         | 
| 344 | 
            +
             | 
| 345 | 
            +
                    input_class_deprecation_warning(__method__)
         | 
| 346 | 
            +
             | 
| 324 347 | 
             
                    @input_classes_cache ||= {}
         | 
| 325 348 | 
             
                    @input_classes_cache[as] ||= begin
         | 
| 326 349 | 
             
                      config = Rails.application.config
         | 
| @@ -328,7 +351,9 @@ module Formtastic | |
| 328 351 | 
             
                      use_const_defined ? input_class_with_const_defined(as) : input_class_by_trying(as)
         | 
| 329 352 | 
             
                    end
         | 
| 330 353 | 
             
                  end
         | 
| 331 | 
            -
             | 
| 354 | 
            +
             | 
| 355 | 
            +
                  # @api private
         | 
| 356 | 
            +
                  # @deprecated Use {InputClassFinder#find} instead.
         | 
| 332 357 | 
             
                  # prevent exceptions in production environment for better performance
         | 
| 333 358 | 
             
                  def input_class_with_const_defined(as)
         | 
| 334 359 | 
             
                    input_class_name = custom_input_class_name(as)
         | 
| @@ -336,12 +361,14 @@ module Formtastic | |
| 336 361 | 
             
                    if ::Object.const_defined?(input_class_name)
         | 
| 337 362 | 
             
                      input_class_name.constantize
         | 
| 338 363 | 
             
                    elsif Formtastic::Inputs.const_defined?(input_class_name)
         | 
| 339 | 
            -
                      standard_input_class_name(as).constantize | 
| 364 | 
            +
                      standard_input_class_name(as).constantize
         | 
| 340 365 | 
             
                    else
         | 
| 341 366 | 
             
                      raise Formtastic::UnknownInputError, "Unable to find input class #{input_class_name}"
         | 
| 342 367 | 
             
                    end
         | 
| 343 368 | 
             
                  end
         | 
| 344 | 
            -
             | 
| 369 | 
            +
             | 
| 370 | 
            +
                  # @api private
         | 
| 371 | 
            +
                  # @deprecated Use {InputClassFinder#find} instead.
         | 
| 345 372 | 
             
                  # use auto-loading in development environment
         | 
| 346 373 | 
             
                  def input_class_by_trying(as)
         | 
| 347 374 | 
             
                    begin
         | 
| @@ -353,16 +380,29 @@ module Formtastic | |
| 353 380 | 
             
                    raise Formtastic::UnknownInputError, "Unable to find input class for #{as}"
         | 
| 354 381 | 
             
                  end
         | 
| 355 382 |  | 
| 383 | 
            +
                  # @api private
         | 
| 384 | 
            +
                  # @deprecated Use {InputClassFinder#class_name} instead.
         | 
| 356 385 | 
             
                  # :as => :string # => StringInput
         | 
| 357 386 | 
             
                  def custom_input_class_name(as)
         | 
| 387 | 
            +
                    input_class_deprecation_warning(__method__)
         | 
| 358 388 | 
             
                    "#{as.to_s.camelize}Input"
         | 
| 359 389 | 
             
                  end
         | 
| 360 390 |  | 
| 391 | 
            +
                  # @api private
         | 
| 392 | 
            +
                  # @deprecated Use {InputClassFinder#class_name} instead.
         | 
| 361 393 | 
             
                  # :as => :string # => Formtastic::Inputs::StringInput
         | 
| 362 394 | 
             
                  def standard_input_class_name(as)
         | 
| 395 | 
            +
                    input_class_deprecation_warning(__method__)
         | 
| 363 396 | 
             
                    "Formtastic::Inputs::#{as.to_s.camelize}Input"
         | 
| 364 397 | 
             
                  end
         | 
| 365 398 |  | 
| 399 | 
            +
                  private
         | 
| 400 | 
            +
             | 
| 401 | 
            +
                  def input_class_deprecation_warning(method)
         | 
| 402 | 
            +
                    @input_class_deprecation_warned ||=
         | 
| 403 | 
            +
                        Formtastic.deprecation.deprecation_warning(method, INPUT_CLASS_DEPRECATION, caller(2))
         | 
| 404 | 
            +
                  end
         | 
| 405 | 
            +
             | 
| 366 406 | 
             
                end
         | 
| 367 407 | 
             
              end
         | 
| 368 408 | 
             
            end
         | 
| @@ -1,6 +1,17 @@ | |
| 1 1 | 
             
            module Formtastic
         | 
| 2 2 | 
             
              # @private
         | 
| 3 3 | 
             
              module HtmlAttributes
         | 
| 4 | 
            +
                # Returns a namespace passed by option or inherited from parent builders / class configuration
         | 
| 5 | 
            +
                def dom_id_namespace
         | 
| 6 | 
            +
                  namespace = options[:custom_namespace]
         | 
| 7 | 
            +
                  parent = options[:parent_builder]
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  case
         | 
| 10 | 
            +
                    when namespace then namespace
         | 
| 11 | 
            +
                    when parent && parent != self then parent.dom_id_namespace
         | 
| 12 | 
            +
                    else custom_namespace
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 4 15 |  | 
| 5 16 | 
             
                protected
         | 
| 6 17 |  | 
| @@ -18,4 +29,4 @@ module Formtastic | |
| 18 29 | 
             
                end
         | 
| 19 30 |  | 
| 20 31 | 
             
              end
         | 
| 21 | 
            -
            end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Formtastic
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # Uses the Formtastic::NamespacedClassFinder to look up input class names.
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # See Formtastic::Helpers::InputHelper#namespaced_input_class for details.
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class InputClassFinder < NamespacedClassFinder
         | 
| 8 | 
            +
                def initialize(builder)
         | 
| 9 | 
            +
                  super builder.input_namespaces
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                private
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def class_name(as)
         | 
| 15 | 
            +
                  "#{super}Input"
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/lib/formtastic/inputs.rb
    CHANGED
    
    
| @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            module Formtastic
         | 
| 2 2 | 
             
              module Inputs
         | 
| 3 3 | 
             
                module Base
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                  attr_accessor :builder, :template, :object, :object_name, :method, :options
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                  def initialize(builder, template, object, object_name, method, options)
         | 
| 8 8 | 
             
                    @builder = builder
         | 
| 9 9 | 
             
                    @template = template
         | 
| @@ -12,29 +12,29 @@ module Formtastic | |
| 12 12 | 
             
                    @method = method
         | 
| 13 13 | 
             
                    @options = options.dup
         | 
| 14 14 | 
             
                  end
         | 
| 15 | 
            -
             | 
| 15 | 
            +
             | 
| 16 16 | 
             
                  # Usefull for deprecating options.
         | 
| 17 17 | 
             
                  def warn_and_correct_option!(old_option_name, new_option_name)
         | 
| 18 18 | 
             
                    if options.key?(old_option_name)
         | 
| 19 | 
            -
                      ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version")
         | 
| 19 | 
            +
                      ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version", caller(6))
         | 
| 20 20 | 
             
                      options[new_option_name] = options.delete(old_option_name)
         | 
| 21 21 | 
             
                    end
         | 
| 22 22 | 
             
                  end
         | 
| 23 | 
            -
             | 
| 23 | 
            +
             | 
| 24 24 | 
             
                  # Usefull for deprecating options.
         | 
| 25 25 | 
             
                  def warn_deprecated_option!(old_option_name, instructions)
         | 
| 26 26 | 
             
                    if options.key?(old_option_name)
         | 
| 27 | 
            -
                      ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}` and will be removed in the next version")
         | 
| 27 | 
            +
                      ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}` and will be removed in the next version", caller(6))
         | 
| 28 28 | 
             
                    end
         | 
| 29 29 | 
             
                  end
         | 
| 30 | 
            -
             | 
| 30 | 
            +
             | 
| 31 31 | 
             
                  # Usefull for raising an error on previously supported option.
         | 
| 32 32 | 
             
                  def removed_option!(old_option_name)
         | 
| 33 33 | 
             
                    raise ArgumentError, ":#{old_option_name} is no longer available" if options.key?(old_option_name)
         | 
| 34 34 | 
             
                  end
         | 
| 35 | 
            -
             | 
| 35 | 
            +
             | 
| 36 36 | 
             
                  extend ActiveSupport::Autoload
         | 
| 37 | 
            -
             | 
| 37 | 
            +
             | 
| 38 38 | 
             
                  autoload :DatetimePickerish
         | 
| 39 39 | 
             
                  autoload :Associations
         | 
| 40 40 | 
             
                  autoload :Collections
         | 
| @@ -53,7 +53,7 @@ module Formtastic | |
| 53 53 | 
             
                  autoload :Timeish
         | 
| 54 54 | 
             
                  autoload :Validations
         | 
| 55 55 | 
             
                  autoload :Wrapping
         | 
| 56 | 
            -
             | 
| 56 | 
            +
             | 
| 57 57 | 
             
                  include Html
         | 
| 58 58 | 
             
                  include Options
         | 
| 59 59 | 
             
                  include Database
         | 
| @@ -65,8 +65,7 @@ module Formtastic | |
| 65 65 | 
             
                  include Associations
         | 
| 66 66 | 
             
                  include Labelling
         | 
| 67 67 | 
             
                  include Wrapping
         | 
| 68 | 
            -
             | 
| 68 | 
            +
             | 
| 69 69 | 
             
                end
         | 
| 70 70 | 
             
              end
         | 
| 71 71 | 
             
            end
         | 
| 72 | 
            -
              
         | 
| @@ -48,7 +48,7 @@ module Formtastic | |
| 48 48 |  | 
| 49 49 | 
             
                    def collection
         | 
| 50 50 | 
             
                      # Return if we have a plain string
         | 
| 51 | 
            -
                      return raw_collection if raw_collection. | 
| 51 | 
            +
                      return raw_collection if raw_collection.is_a?(String)
         | 
| 52 52 |  | 
| 53 53 | 
             
                      # Return if we have an Array of strings, fixnums or arrays
         | 
| 54 54 | 
             
                      return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
         | 
| @@ -28,9 +28,9 @@ module Formtastic | |
| 28 28 |  | 
| 29 29 | 
             
                    def dom_id
         | 
| 30 30 | 
             
                      [
         | 
| 31 | 
            -
                        builder. | 
| 32 | 
            -
                        sanitized_object_name, | 
| 33 | 
            -
                        dom_index, | 
| 31 | 
            +
                        builder.dom_id_namespace,
         | 
| 32 | 
            +
                        sanitized_object_name,
         | 
| 33 | 
            +
                        dom_index,
         | 
| 34 34 | 
             
                        association_primary_key || sanitized_method_name
         | 
| 35 35 | 
             
                      ].reject { |x| x.blank? }.join('_')
         | 
| 36 36 | 
             
                    end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module Formtastic
         | 
| 2 | 
            +
              module Inputs
         | 
| 3 | 
            +
                # Outputs a label and a text field, along with a datalist tag
         | 
| 4 | 
            +
                # datalist tag provides a list of options which drives a simple autocomplete
         | 
| 5 | 
            +
                # on the text field. This is a HTML5 feature, more info can be found at
         | 
| 6 | 
            +
                # {https://developer.mozilla.org/en/docs/Web/HTML/Element/datalist <datalist> at MDN}
         | 
| 7 | 
            +
                # This input accepts a :collection option which takes data in all the usual formats accepted by
         | 
| 8 | 
            +
                # {http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select options_for_select}
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @example Input is used as follows
         | 
| 11 | 
            +
                #   f.input :fav_book, :as => :datalist, :collection => Book.pluck(:name)
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                class DatalistInput
         | 
| 14 | 
            +
                  include Base
         | 
| 15 | 
            +
                  include Base::Stringish
         | 
| 16 | 
            +
                  include Base::Collections
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def to_html
         | 
| 19 | 
            +
                    @name = input_html_options[:id].gsub(/_id$/, "")
         | 
| 20 | 
            +
                    input_wrapping do
         | 
| 21 | 
            +
                      label_html <<
         | 
| 22 | 
            +
                      builder.text_field(method, input_html_options) << # standard input
         | 
| 23 | 
            +
                      data_list_html # append new datalist element
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def input_html_options
         | 
| 28 | 
            +
                    super.merge(:list => html_id_of_datalist)
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def html_id_of_datalist
         | 
| 32 | 
            +
                    "#{@name}_datalist"
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def data_list_html
         | 
| 36 | 
            +
                    html = builder.template.options_for_select(collection)
         | 
| 37 | 
            +
                    builder.template.content_tag(:datalist,html, { :id => html_id_of_datalist }, false)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -20,7 +20,7 @@ module Formtastic | |
| 20 20 | 
             
                #   <form...>
         | 
| 21 21 | 
             
                #     <fieldset>
         | 
| 22 22 | 
             
                #       <ol>
         | 
| 23 | 
            -
                #         <li class=" | 
| 23 | 
            +
                #         <li class="file">
         | 
| 24 24 | 
             
                #           <label for="user_avatar">Avatar</label>
         | 
| 25 25 | 
             
                #           <input type="file" id="user_avatar" name="user[avatar]">
         | 
| 26 26 | 
             
                #         </li>
         | 
| @@ -29,7 +29,7 @@ module Formtastic | |
| 29 29 | 
             
                #   </form>
         | 
| 30 30 | 
             
                #
         | 
| 31 31 | 
             
                # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
         | 
| 32 | 
            -
                class FileInput | 
| 32 | 
            +
                class FileInput
         | 
| 33 33 | 
             
                  include Base
         | 
| 34 34 | 
             
                  def to_html
         | 
| 35 35 | 
             
                    input_wrapping do
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            module Formtastic
         | 
| 2 | 
            +
              # This class implements class resolution in a namespace chain. It
         | 
| 3 | 
            +
              # is used both by Formtastic::Helpers::InputHelper and
         | 
| 4 | 
            +
              # Formtastic::Helpers::ActionHelper to look up action and input classes.
         | 
| 5 | 
            +
              #
         | 
| 6 | 
            +
              # ==== Example
         | 
| 7 | 
            +
              # You can implement own class finder that for example prefixes the class name or uses custom module.
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              #   class MyInputClassFinder < Formtastic::NamespacedClassFinder
         | 
| 10 | 
            +
              #     def initialize(builder)
         | 
| 11 | 
            +
              #       super [MyNamespace, Object] # first lookup in MyNamespace then the globals
         | 
| 12 | 
            +
              #     end
         | 
| 13 | 
            +
              #
         | 
| 14 | 
            +
              #     private
         | 
| 15 | 
            +
              #
         | 
| 16 | 
            +
              #     def class_name(as)
         | 
| 17 | 
            +
              #       "My#{super}Input" # for example MyStringInput
         | 
| 18 | 
            +
              #     end
         | 
| 19 | 
            +
              #   end
         | 
| 20 | 
            +
              #
         | 
| 21 | 
            +
              # And then set Formtastic::FormBuilder.input_class_finder with that class.
         | 
| 22 | 
            +
              #
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              class NamespacedClassFinder
         | 
| 25 | 
            +
                attr_reader :namespaces #:nodoc:
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # @private
         | 
| 28 | 
            +
                class NotFoundError < NameError
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def initialize(namespaces) #:nodoc:
         | 
| 32 | 
            +
                  @namespaces = namespaces.flatten
         | 
| 33 | 
            +
                  @cache = {}
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Looks up the given reference in the configured namespaces.
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                # Two finder methods are provided, one for development tries to
         | 
| 39 | 
            +
                # reference the constant directly, triggering Rails' autoloading
         | 
| 40 | 
            +
                # const_missing machinery; the second one instead for production
         | 
| 41 | 
            +
                # checks with .const_defined before referencing the constant.
         | 
| 42 | 
            +
                #
         | 
| 43 | 
            +
                def find(as)
         | 
| 44 | 
            +
                  @cache[as] ||= resolve(as)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def resolve(as)
         | 
| 48 | 
            +
                  class_name = class_name(as)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  finder(class_name) or raise NotFoundError, "class #{class_name}"
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                private
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def class_name(as)
         | 
| 56 | 
            +
                  as.to_s.camelize
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                if defined?(Rails) && ::Rails.application && ::Rails.application.config.cache_classes
         | 
| 60 | 
            +
                  def finder(class_name) # :nodoc:
         | 
| 61 | 
            +
                    find_with_const_defined(class_name)
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                else
         | 
| 64 | 
            +
                  def finder(class_name) # :nodoc:
         | 
| 65 | 
            +
                    find_by_trying(class_name)
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                # Looks up the given class name in the configured namespaces in order,
         | 
| 70 | 
            +
                # returning the first one that has the class name constant defined.
         | 
| 71 | 
            +
                def find_with_const_defined(class_name)
         | 
| 72 | 
            +
                  @namespaces.find do |namespace|
         | 
| 73 | 
            +
                    if namespace.const_defined?(class_name)
         | 
| 74 | 
            +
                      break namespace.const_get(class_name)
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                # Use auto-loading in development environment
         | 
| 80 | 
            +
                def find_by_trying(class_name)
         | 
| 81 | 
            +
                  @namespaces.find do |namespace|
         | 
| 82 | 
            +
                    begin
         | 
| 83 | 
            +
                      break namespace.const_get(class_name)
         | 
| 84 | 
            +
                    rescue NameError
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         |