I have a requirement to store values in a database as unitless
decimals(12,9) which will be presumed to be metric values. For
display/editing purposes I need the user to be able to choose their
input
method – no mixing and matching on a form, just a simple
User#current_units call to determine the expected units.
For example.
item:
name: item1
diameter: 150
width: 6
When User#current_units is “mm” all of my form fields are straight
forward…
f.label “Diameter”
f.text_field :diameter
My issue is when a current_units is “in”, what’s a good method for doing
all the translation? The text_field for diameter should read “5.9055”.
If a
user altered it to read “6” then it should save back to the db as
“152.400000000”
I’ve tried everything from the ruby-units gem to composed_of (which is
practically useless with rails 4 getting rid of it). storing the units
in
the db is a no go only because I need to be able to search for “items
where
diameter > x and diameter < y”
If anyone has any theories on a good starting point I’d love to
elaborate
further on what I’ve tried and failed with.
Unless I’m missing something, you want a setter and getter for a
“virtual” attribute: say DiameterStr and DiameterStr=, which perform or
not the conversion as needed. Then you always reference f.text_field
:DiameterStr.
You could always play around with making the underlying attributes
private, or at least a diameter= method which throws an exception…
(Which slightly complicates DiameterStr=…)
On Oct 29, 2012, at 5:57 PM, Frank Mattia wrote:
If anyone has any theories on a good starting point I’d love to elaborate
further on what I’ve tried and failed with.
I’m closer but not quite there. I had originally tried virtual
attributes
but all my implementations fell short. After you suggested it, I did
some
more homework and tried again. Now I have a solution that works in every
way except validation. My virtual attributes are defined like this:
def diameter_in_units
Unit.to_current(read_attribute(:diameter))
end
def diameter_in_units=(value)
write_attribute(:diameter, Unit.to_db(value))
end
#to_current and #to_db just apply a conversion factor depending on the
users current_units setting but my issue is visible when the user
errantly
inputs something that isn’t a number, it never triggers my validations.
I’m
not 100% but I believe it is because the #to_ methods are coercing the
string input to a BigDecimal before doing any calculation and therefore
if
the user inputs a string it helpfully changes it to zero for the
calculation.
1.9.3p194 :036 > “randomstring”.to_d.to_s
=> “0.0”
How can I get around this without duplicating validation code while
still
getting the full “validation experience”?