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