Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Initialization by hash #39

Open
wants to merge 1 commit into from

2 participants

@bmishkin

Thanks for the great plugin!

I am interested in using Unit(hash) syntax, so I fixed the base library and tests to properly handle several variants of the Unit(scalar: i, numerator: n, denominator: d) format. I'm not sure if what I added was precisely what was intended or not (permitting non-arrays for numerator, denominator etc) but it's working well for our purposes and passing the tests I added.

The impetus for using hash instead of string initialization, is that we didn't like the implicit conversion of whole-number floats and decimals to fixnums. Example:

1.9.2p136 :002 > f = 3.000
=> 3.0
1.9.2p136 :003 > f.class
=> Float
1.9.2p136 :005 > Unit("#{f} pounds").scalar.class
=> Fixnum
1.9.2p136 :006 > Unit(scalar: f, numerator: 'pounds').scalar.class
=> Float

This gets even nastier, once you dig into mathematical operations:
1.9.2p136 :012 > (Unit("#{f} pounds") / Unit("#{2*f} pounds")).scalar
=> (1/2)
1.9.2p136 :013 > (Unit("#{f} pounds") / Unit("#{2*f} pounds")).scalar.class
=> Rational
1.9.2p136 :014 > (Unit(scalar: f, numerator: 'pounds') / Unit(scalar: 2*f, numerator:'pounds')).scalar
=> 0.5
1.9.2p136 :016 > (Unit(scalar: f, numerator: 'pounds') / Unit(scalar: 2*f, numerator:'pounds')).scalar.class
=> Float

In our case, we are using floats and want to ensure that resultant Unit objects retain scalar class of Float.

@olbrich
Owner

Is the problem you are trying to solve here being able to control the type of the scalar, or do you really need to be able to initialize a unit with a Hash? Note that this form works:

a = Unit(1.0, "in^2", "lbs") #=> 1 in^2/lbs
a.scalar.class #=> Float

Also note that this will work too:

a = Unit("in^2/lbs") * 1.0
a.scalar.class #=> Float
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 3, 2012
  1. @bmishkin
This page is out of date. Refresh to see the latest.
Showing with 61 additions and 0 deletions.
  1. +21 −0 lib/ruby_units/unit.rb
  2. +40 −0 spec/ruby-units/unit_spec.rb
View
21 lib/ruby_units/unit.rb
@@ -162,6 +162,14 @@ def self.definition(_unit)
return @@definitions[unit]
end
+ # return the unit definition for a unit, searching aliases too
+ # throw error if not found
+ def self.definition_or_alias(_unit)
+ return if !_unit
+ self.definition(_unit) || self.definition(@@UNIT_MAP[_unit]) || raise(ArgumentError, "'#{_unit}' Unit not recognized")
+ end
+
+
# return a list of all defined units
# @return [Array]
def self.definitions
@@ -343,6 +351,19 @@ def initialize(*options)
@numerator = options[0][:numerator] || UNITY_ARRAY
@denominator = options[0][:denominator] || UNITY_ARRAY
@signature = options[0][:signature]
+
+ # numerator needs to be an array, 'pounds' => ['pounds']
+ @numerator = [@numerator] unless @numerator.is_a?(Array)
+
+ # numerator needs to be based on a definition
+ @numerator = @numerator.collect{|n| Unit.definition_or_alias(n).name}.flatten.compact
+
+ # denominator needs to be an array, 'pounds' => ['pounds']
+ @denominator = [@denominator] unless @denominator.kind_of?(Array)
+
+ # denominator needs to be based on a definition
+ @denominator = @denominator.collect{|n| Unit.definition_or_alias(n).name}.flatten.compact
+
when Array
initialize(*options[0])
return
View
40 spec/ruby-units/unit_spec.rb
@@ -508,6 +508,44 @@
its(:units) {should == "m/s"}
end
+ #hash
+ describe Unit(:scalar => 1.345, :numerator => 'oz') do
+ its(:kind) {should == :mass}
+ its(:units) {should == 'oz'}
+ its(:scalar) {should == 1.345}
+ its(:numerator) {should == ["<ounce>"]}
+ end
+
+ # hash with speed
+ describe Unit(:scalar => 62.5, :numerator => 'feet', :denominator => 's') do
+ its(:kind) {should == :speed}
+ its(:units) {should == 'ft/s'}
+ its(:scalar) {should == 62.5}
+ its(:numerator) {should == ["<foot>"]}
+ its(:denominator) {should == ["<second>"]}
+ end
+
+ # hash with array numerator
+ describe Unit(:scalar => 199.9, :numerator => ['inches', 'inch'], :denominator => 'pounds') do
+ its(:kind) {should == nil}
+ its(:units) {should == 'in^2/lbs'}
+ its(:scalar) {should == 199.9}
+ its(:numerator) {should == ["<inch>", "<inch>"] }
+ its(:denominator) {should == ["<pound>"]}
+ end
+
+ # hash - handle zero-decimal floats properly
+ describe Unit(:scalar => 97.00000, :numerator => 'kelvin') do
+ its(:kind) {should == :temperature}
+ its(:units) {should == 'degK'}
+ its(:scalar) {should == 97}
+ its(:scalar) {should be_a(Float)}
+ its(:numerator) {should == ["<kelvin>"]}
+ end
+
+
+ # tbd: test setting signature manually in a hash?
+
end
describe "Unit handles attempts to create bad units" do
@@ -553,6 +591,8 @@
specify "no undefined units" do
expect {Unit("1 mFoo")}.to raise_error(ArgumentError,"'1 mFoo' Unit not recognized")
+ expect {Unit(:scalar => 2.444, :numerator => "mFoo")}.to raise_error(ArgumentError,"'mFoo' Unit not recognized")
+ expect {Unit(:scalar => 2.444, :numerator => "m", :denominator => "mFoo")}.to raise_error(ArgumentError,"'mFoo' Unit not recognized")
end
specify "no units with powers greater than 19" do
Something went wrong with that request. Please try again.