require 'active_model' require 'active_model/form/version' require 'active_model/form/attributes' require 'active_support/inflector' module ActiveModel class Form include ActiveModel::Validations include ActiveModel::Conversion include ActiveModel extend ActiveModel::Naming def initialize(attributes=nil) self.assign_attributes(attributes) end # For the Rails route helpers def persisted? false end # Set the model name to change the field names generated by the Rails form helpers def self.model_name=(name) @_model_name = ActiveModel::Name.new(self, nil, name) end def self.attribute(name, type_name) # TODO: Make inheritance of ActiveModel::Forms do the right thing with attributes @_attributes ||= {} @_attributes[name.to_s] = self.type_from_type_name(type_name) self.send(:attr_accessor, name) end def self.attributes @_attributes ||= {} end # Returns the column object for the named attribute. def column_for_attribute(name) self.class.attributes[name.to_s] end def assign_attributes(new_attributes) return if new_attributes.blank? new_attributes = self.clean_attributes(new_attributes) new_attributes.each do |k, v| if attribute = self.class.attributes[k].presence send("#{k}=", attribute.parse(v)) end end end protected def clean_attributes(attributes) cleaned_attributes = {} attributes.stringify_keys.each do |k, v| if k.include?("(") attribute_name = k.split("(").first cleaned_attributes[attribute_name] ||= [] v = v.empty? ? nil : type_cast_attribute_value(k, v) cleaned_attributes[attribute_name][find_parameter_position(k)] ||= v else cleaned_attributes[k] = v end end cleaned_attributes end def self.type_from_type_name(type_name) classified_type_name = ActiveSupport::Inflector.classify(type_name) ActiveSupport::Inflector.safe_constantize("::ActiveModel::Form::#{classified_type_name}Attribute") end def type_cast_attribute_value(multiparameter_name, value) multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value end def find_parameter_position(multiparameter_name) multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i - 1 end end end