After searching arround I found a quite smart solution to me for binding
form fields to model in an active way. I use aggregations with single
value objects such as
class Percentage
attr_accessor :value
def initialize(value)
@value = (value.is_a? String) ? value.tr(’ %’, ‘’).to_f : value
end
def to_s
return unless value
sprintf(’%.2f %%’, value)
end
end
This is an object that takes either float or string (with possible %
character in it) and viewed as string looks like “1.23 %”. So I can
easily create an intelligent “shadow” attribute for interaction:
composed_of :i_discount_rate, :class_name => 'Percentage',
:mapping => %w(discount_rate value)
Using i_discount_rate in form it makes value of discount_rate shown in
text field pretty with % sign. But we cannot pass it to the model
because in mass-assignment everything is just string from request.
So I used reflection on aggregation and as attributes are about to be
processed, they are “objectized”, so attributes[:i_discount_rate] will
gain a real Percentage object:
def attributes=(attributes)
attributes.each do |param, value|
aggregation = self.class.reflect_on_aggregation(param.to_sym)
if aggregation
attributes[param] = aggregation.klass.new(value)
end
end
super(attributes)
end
Now an user can enter “2%”, “2.0 %”, "2.1 % ", etc. and everything is
acceptable. We can now create value object for money so field “54,20
EUR” is perfectly acceptable. And there can be objects for entering date
like “+3 days” and so on.
The mechanizm for “i_attributes” can be enhanced similiary as Date is
expanded into form fields. So for example we can have aggregation
composed_of :i_amount, :class_name => 'Amount',
:map => [%w(a_count count), %w(a_units units)]
And then helper used like
amount_field 'order_item', 'i_amount'
which renders to one text field and one select box such as:
<input type="text" name="order_item[i_amount(count)]"/>
<select name="order_item[i_amount(units)]">
<option value="l">liters</option>
<option value="t">tons</option>
<option value="pc">pieces</option>
</select>
That’s it.
–
Kamil