I've been trying to use a facade column but I notice that the form
helpers don't seem to respect the facade.
In the following example I'm trying to story the prices as Integer
values but present them to the user as decimals.
class Product < ActiveRecord::Base
def price=(new_price)
write_attribute(:price, Float(new_price)*100)
end
def price
read_attribute(:price)/100.00
end
end
and for the most part that works great, I can call product.price and
get
9.99, I call product.price=9.99 and it gets stored in the DB as 999.
However, when I use the form helpers in the views....
<%= text_field 'product', 'price' %></p>
I get a text field with the value 999. I assume this is because the
form helpers are calling for the attributes in a more direct fashion.
Either with read_attribute or directly accessing @attributes.
My question is: is this a bug with the form helpers (or is there a
reason they use lower level code), have I made a mistake in my code, or
have I just approached the problem in the wrong way? I think probably
one of the latter two.
I've been trying to use a facade column but I notice that the form
helpers don't seem to respect the facade.
In the following example I'm trying to story the prices as Integer
values but present them to the user as decimals.
class Product < ActiveRecord::Base
def price=(new_price)
write_attribute(:price, Float(new_price)*100)
end
def price
read_attribute(:price)/100.00
end
end
My question is: is this a bug with the form helpers (or is there a
reason they use lower level code), have I made a mistake in my code, or
have I just approached the problem in the wrong way? I think probably
one of the latter two.
Many thanks for your time.
Gavin.
I have never used form helper with a method that overwrites an attrbute
like that, but I know that form helpers DO work with abstract methods.
So you can give your column a name like price_as_int. This way the
form helper will not get confused.
then:
class Product < ActiveRecord::Base
def price=(new_price)
self.price_as_int = Float(new_price) * 100
end
def price
price_as_int / 100.00
end
end
This also may be better practice since it serves as a reminder that the
price column's value is not valid without some conversion. Much like
the acts_as_authenticated plugin uses a column called
"crypted_password" rather than just "password" to remind you that the
data it stores is not the actual password.
The trouble is, and I'm quoting from "Ruby on Rails" 2nd edition page
391
class ProductData < ActiveRecord::Base
CUBITS_TO_INCHES = 18
def length
read_attribute("length") * CUBITS_TO_INCHES
end
def length=(inches)
write_attribute("length", Float(inches) / CUBITS_TO_INCHES)
end
end
...the example from the book describes a situation where the field is
overwritten with an accessor method with the same name. Maybe it's
more an issue for the authors than the framework but if the ability to
use the facade cleanly isn't carried through all the layers of MVC then
perhaps it's worth mentioning.
In the meantime I'll probably adopt your practice. A quick test shows
that non-attribute methods are respected.
file: form_helper.rb from the stable svn branch
line: 337
------------------------
def value_before_type_cast
unless object.nil?
object.respond_to?(@method_name + "_before_type_cast") ?
object.send(@method_name + "_before_type_cast") :
object.send(@method_name)
end
end
------------------------
If the object responds to <attribute>_before_type_cast then that
function gets called in preference to the facade. This means that, as
Alex mentioned above, one way round the problem is to use an alterative
name. I'm not sure why this should be the case though. Is there a
reason that the _before_type_cast should be used rather than just
stringing the attribute?
Cheers,
Gavin.
Interesting...
I also want to know why this is like this. Is this the same on edge as
well?