lib/reform/form/validate.rb in reform-1.0.4 vs lib/reform/form/validate.rb in reform-1.1.0

- old
+ new

@@ -1,110 +1,93 @@ # Mechanics for writing to forms in #validate. module Reform::Form::Validate module Update + # IDEA: what if Populate was a Decorator that simply knows how to setup the Form object graph, nothing more? That would decouple + # the population from the validation (good and bad as less customizable). + + # Go through all nested forms and call form.update!(hash). def from_hash(*) nested_forms do |attr| - attr.delete(:prepare) - attr.delete(:extend) - attr.merge!( + # set parse_strategy: sync> # DISCUSS: that kills the :setter directive, which usually sucks. at least document this in :populator. :collection => attr[:collection], # TODO: Def#merge! doesn't consider :collection if it's already set in attr YET. :parse_strategy => :sync, # just use nested objects as they are. + :deserialize => lambda { |object, params, args| object.update!(params) }, ) + + # TODO: :populator now is just an alias for :instance. handle in ::property. + attr.merge!(:instance => attr[:populator]) if attr[:populator] + + attr.merge!(:instance => Populator::PopulateIfEmpty.new) if attr[:populate_if_empty] end + # FIXME: solve this with a dedicated Populate Decorator per Form. + representable_attrs.each do |attr| + attr.merge!(:parse_filter => Representable::Coercion::Coercer.new(attr[:coercion_type])) if attr[:coercion_type] + end + super end end module Populator + # This might change soon (e.g. moved into disposable). class PopulateIfEmpty - def initialize(*args) - @fields, @fragment, args = args - @index = args.first - @args = args.last - end + include Uber::Callable - def call - binding = @args.binding + def call(fields, fragment, *args) + index = args.first + options = args.last + binding = options.binding form = binding.get - parent_form = @args.user_options[:parent_form] - form_model = parent_form.model # FIXME: sort out who's responsible for sync. + parent_form = options.user_options[:parent_form] - return if binding.array? and form and form[@index] # TODO: this should be handled by the Binding. - return if !binding.array? and form + # FIXME: test those cases!!! + return form[index] if binding.array? and form and form[index] # TODO: this should be handled by the Binding. + return form if !binding.array? and form # only get here when above form is nil. + if binding[:populate_if_empty].is_a?(Proc) - model = parent_form.instance_exec(@fragment, @args, &binding[:populate_if_empty]) # call user block. + model = parent_form.instance_exec(fragment, options.user_options, &binding[:populate_if_empty]) # call user block. else model = binding[:populate_if_empty].new end form = binding[:form].new(model) # free service: wrap model with Form. this usually happens in #setup. if binding.array? - form_model.send("#{binding.getter}") << model # FIXME: i don't like this, but we have to add the model to the parent object to make associating work. i have to use #<< to stay compatible with AR's has_many API. DISCUSS: what happens when we get out-of-sync here? - @fields.send("#{binding.getter}")[@index] = form + fields.send("#{binding.getter}")[index] = form else - form_model.send("#{binding.setter}", model) # FIXME: i don't like this, but we have to add the model to the parent object to make associating work. - @fields.send("#{binding.setter}", form) # :setter is currently overwritten by :parse_strategy. + fields.send("#{binding.setter}", form) # :setter is currently overwritten by :parse_strategy. end end end # PopulateIfEmpty - - - def from_hash(params, args) - populated_attrs = [] - - nested_forms do |attr| - next unless attr[:populate_if_empty] - - attr.merge!( - # DISCUSS: it would be cool to move the lambda block to PopulateIfEmpty#call. - :populator => lambda do |fragment, *args| - PopulateIfEmpty.new(self, fragment, args).call - end - ) - end - - - nested_forms do |attr| - next unless attr[:populator] - - attr.merge!( - :parse_strategy => attr[:populator], - :representable => false - ) - populated_attrs << attr.name.to_sym - end - - super(params, {:include => populated_attrs}.merge(args)) - end end - + # 1. Populate the form object graph so that each incoming object has a representative form object. + # 2. Deserialize. This is wrong and should be done in 1. + # 3. Validate the form object graph. def validate(params) update!(params) - super() + super() # run the actual validation on self. end def update!(params) - populate!(params) deserialize!(params) end private - def populate!(params) - # populate only happens for nested forms, if you override that setter it's your fault. - mapper.new(fields).extend(Populator).from_hash(params, :parent_form => self) # TODO: remove model(form) once we found out how to synchronize the model correctly. see https://github.com/apotonick/reform/issues/86#issuecomment-43402047 - end - def deserialize!(params) # using self here will call the form's setters like title= which might be overridden. - mapper.new(self).extend(Update).from_hash(params) + # from_hash(params, parent_form: self) + mapper.new(self).extend(Update).send(deserialize_method, params, :parent_form => self) + end + + def deserialize_method + :from_hash end end