Forum: Ruby on Rails Saving values to the db as metric but displaying/editing in imperial.

Posted by Frank Mattia (frankjmattia)
on 2012-10-30 00:58
(Received via mailing list)
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.

Thanks,
- FJM
Posted by Scott Ribe (Guest)
on 2012-10-30 01:24
(Received via mailing list)
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.
>


--
Scott Ribe
scott_ribe@elevated-dev.com
http://www.elevated-dev.com/
(303) 722-0567 voice
Posted by Frank Mattia (frankjmattia)
on 2012-11-01 20:50
(Received via mailing list)
Thanks Scott,

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"?

Thanks for your advice,
- FJM
Posted by Frank Mattia (frankjmattia)
on 2012-11-02 00:53
(Received via mailing list)
In case anyone ever wants to do something similar and runs into the same
follies I'll leave my solution here:

Model

> class Thing < ActiveRecord::Base
>   attr_accessible :diameter_in_units



  validates :diameter_in_units, numericality: true
>
>   before_save :update_diameter_in_units, if: :diameter_changed?


>   def diameter_in_units
>     @diameter_in_units || Unit.to_current(read_attribute(:diameter))
>   end


>   def diameter_in_units=(value)
>     diameter_will_change!
>     @diameter_in_units = value
>   end
>
> private
>   def update_diameter_in_units
>     write_attribute(:diameter, Unit.to_db(diameter_in_units))
>   end
> end
Posted by Scott Ribe (Guest)
on 2012-11-02 03:34
(Received via mailing list)
On Nov 1, 2012, at 5:52 PM, Frank Mattia wrote:

> In case anyone ever wants to do something similar and runs into the same follies 
I'll leave my solution here:

I never saw your second question; but I like your solution and will use 
it ;-)

--
Scott Ribe
scott_ribe@elevated-dev.com
http://www.elevated-dev.com/
(303) 722-0567 voice
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.