unitwise 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ebcd2779e6a54ca23cc3cd644f3720bd9de8846d
4
- data.tar.gz: 4bc1072fe92630814f05e9402e5cc5ac8983ce94
3
+ metadata.gz: 2cb7a8a61e366794106f2f13a5b18bce3ca3be61
4
+ data.tar.gz: 6c430a9fb789f8aafb7911ba6832056bd47af100
5
5
  SHA512:
6
- metadata.gz: 5de61cb9d586961307d03e8c3db35ba3d851d6cc8dcdfd8944c2c6d4b6e9e6833abe82c0fe498d1924bd3a23927c980a7c0fa252ba5e931d848afd10fa278672
7
- data.tar.gz: 9b01d1fc8f63294e72bd20060f411c8a54d5ffffe7cb872b504728aa0ea8e4c189f2df696a50bb392b291c1582629c852e7dcbeab2634429b61f3883f4f95f46
6
+ metadata.gz: c683c3397398221d6d80903b7cbb5b393868f132c9a73e67579f0c8f18f03817ce39b7956b6afdd9f8823876777579d07bd3e3e5e9f298fa9fcdd304fb228fa0
7
+ data.tar.gz: ad2a38986b46e8e5ad356dc92ff6076e11d4c1fb2bba01000f40fd89a4b9f36902e95a497dc6666f8fbb6f4a7f40f6f2664eaaba619b75f7f56bad9783f47ae4
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Unitwise
1
+ # [Unitwise](//github.com/joshwlewis/unitwise)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/unitwise.png)](http://badge.fury.io/rb/unitwise)
4
4
  [![Build Status](https://travis-ci.org/joshwlewis/unitwise.png)](https://travis-ci.org/joshwlewis/unitwise)
@@ -7,33 +7,76 @@
7
7
  [![Code Climate](https://codeclimate.com/github/joshwlewis/unitwise.png)](https://codeclimate.com/github/joshwlewis/unitwise)
8
8
 
9
9
 
10
- Unitwise is a library for performing mathematical operations and conversions on all units defined by the [Unified Code for Units of Measure(UCUM)](http://unitsofmeasure.org/).
10
+ Unitwise is a Ruby library for unit measurement conversion and math.
11
11
 
12
- Unitwise supports a vast number of units. At the time of writing, it supports 95 metric units, 199 non-metric units, and 24 unit prefixes. That's approximately 2,500 basic units, but these can also be combined through multiplication and/or division to create an infinite number of possibilities.
12
+ For an over the top example, consider a car (2800 lb) completing the quarter
13
+ mile in 10 seconds (with uniform acceleration).
14
+
15
+ ```ruby
16
+ distance = 0.25.mile # => #<Unitwise::Measurement value=0.25 unit=mile>
17
+ time = 10.second # => #<Unitwise::Measurement value=10 unit=second>
18
+ mass = 2800.pound # => #<Unitwise::Measurement value=2800 unit=pound>
19
+
20
+ acceleration = 2 * distance / time ** 2
21
+ # => #<Unitwise::Measurement value=0.005 unit=[mi_us]/s2>
22
+
23
+ force = (mass * acceleration).to_lbf
24
+ # => #<Unitwise::Measurement value=2297.5084316991147 unit=lbf>
25
+
26
+ power = (force * distance / time).to_horsepower
27
+ # => #<Unitwise::Measurement value=551.4031264140402 unit=horsepower>
28
+
29
+ speed = ((2 * acceleration * distance) ** 0.5).convert_to("mile/hour")
30
+ # => #<Unitwise::Measurement value=180.0 unit=mile/hour>
31
+ ```
32
+
33
+ ## Rationale
34
+
35
+ Unitwise is based on the [Unified Code for Units of Measure(UCUM)](http://unitsofmeasure.org/),
36
+ which aims to maintain a cross-platform list of units and their conversions.
37
+ This gives Unitwise a few key advantages:
38
+
39
+ - An enormous list of units. At the time of writing, there are 96 metric units,
40
+ 211 non-metric units, and 24 unit prefixes. Whatever unit/units you need, they
41
+ are here.
42
+
43
+ - An accurate and up to date set of units. The units, prefixes, and conversions
44
+ are maintained by UCUM, and are imported into this library with a rake task.
45
+
46
+ One of the objectives of Unitwise was that it should comprehend any combination
47
+ of units. For instance it needed to understand that a unit of
48
+ 'kilogram.(meter/second)2' was equivalent to 'kilogram.meter.(meter/second2)'.
49
+ This resulted in two unique features:
50
+
51
+ - An expression grammer built with a PEG parser. This makes expression
52
+ parsing more efficient and allows nestable parentheses. For example, this is possible: '(kilogram.(meter/second)2)2'
53
+
54
+ - Smart compatibility detection. Each unit is reduced down to its most elementary
55
+ atoms to determine compatibility with another unit. For example, it knows that
56
+ 'meter/second2' should be considered compatible with 'kilogram.foot.minute-2/pound'.
13
57
 
14
58
  ## Usage
15
59
 
16
60
  ### Initialization:
17
61
 
18
- Instantiate measurements with `Unitwise()`
62
+ Measurements can be instantiated with `Unitwise()`.
19
63
 
20
64
  ```ruby
21
65
  require 'unitwise'
22
66
 
23
- Unitwise(2.3, 'kilogram') # => <Unitwise::Measurement 2.3 kilogram>
24
- Unitwise('pound') # => <Unitwise::Measurement 1 pound>
67
+ Unitwise(2.3, 'kilogram') # => #<Unitwise::Measurement value=2.3 unit=kilogram>
68
+ Unitwise('pound') # => #<Unitwise::Measurement value=1 unit=pound>
25
69
  ```
26
70
 
27
- or require the core extensions for some syntactic sugar.
71
+ Unitwise doesn't mess with the Ruby core library by default. So if you want
72
+ the syntactic sugar shown throughout this document, you'll need to require the
73
+ core extensions.
28
74
 
29
75
  ```ruby
30
76
  require 'unitwise/ext'
31
77
 
32
- 1.convert('liter')
33
- # => <Unitwise::Measurement 1 liter>
34
-
35
- 4.teaspoon
36
- # => <Unitwise::Measurement 4 teaspoon>
78
+ 1.convert_to('liter') # => #<Unitwise::Measurement value=1 unit=liter>
79
+ 4.teaspoon # => #<Unitwise::Measurement value=4 unit=teaspoon>
37
80
  ```
38
81
 
39
82
  ### Conversion
@@ -42,22 +85,17 @@ Unitwise is able to convert any unit within the UCUM spec to any other
42
85
  compatible unit.
43
86
 
44
87
  ```ruby
45
- distance = Unitwise(5, 'kilometer')
46
- # => <Unitwise::Measurement 5 kilometer>
47
-
48
- distance.convert('mile')
49
- # => <Unitwise::Measurement 3.106849747474748 mile>
88
+ 5.kilometer.convert_to('mile')
89
+ # => #<Unitwise::Measurement value=3.106849747474748 unit=mile>
50
90
  ```
51
91
 
52
- The prettier version of `convert(unit)` is just calling the unit as a method
53
- name:
92
+ The prettier version of `convert_to(unit)` is appending the unit code, name, etc.
93
+ to a `to_` message.
94
+ name.
54
95
 
55
96
  ```ruby
56
- distance = 26.2.mile
57
- # => <Unitwise::Measurement 26.2 mile>
58
-
59
- distance.kilometer
60
- # => <Unitwise::Measurement 42.164897129794255 kilometer>
97
+ Unitwise(26.2, 'mile').to_kilometer
98
+ # => #<Unitwise::Measurement value=42.164897129794255 unit=kilometer>
61
99
  ```
62
100
 
63
101
  ### Comparison
@@ -66,11 +104,10 @@ It also has the ability to compare measurements with the same or different units
66
104
 
67
105
  ```ruby
68
106
  12.inch == 1.foot # => true
69
-
70
107
  1.meter > 1.yard # => true
71
108
  ```
72
109
 
73
- Again, you have to compare compatible units. For example, comparing two
110
+ Again, you have to compare compatible units. For example, comparing two
74
111
  temperatures will work, comparing a mass to a length would fail.
75
112
 
76
113
  ### SI abbreviations
@@ -78,8 +115,8 @@ temperatures will work, comparing a mass to a length would fail.
78
115
  You can use shorthand for SI units.
79
116
 
80
117
  ```ruby
81
- 1.m # => <Unitwise::Measurement 1 meter>
82
- 1.ml #=> <Unitwise::Measurement 1 milliliter>
118
+ 1000.m == 1.km # => true
119
+ 1.ml == 0.001.l # => true
83
120
  ```
84
121
 
85
122
  ### Complex Units
@@ -89,17 +126,17 @@ them -- they can still be converted, compared, or operated on.
89
126
 
90
127
  ```ruby
91
128
  speed = Unitwise(60, 'mile/hour')
92
- # => <Unitwise::Measurement 60 mile/hour>
129
+ # => #<Unitwise::Measurement value=60 unit=mile/hour>
93
130
 
94
- speed.convert('m/s')
95
- # => <Unitwise::Measurement 26.822453644907288 m/s>
131
+ speed.convert_to('m/s')
132
+ # => #<Unitwise::Measurement value=26.822453644907288 unit=m/s>
96
133
  ```
97
134
 
98
135
  Exponents and parenthesis are supported as well.
99
136
 
100
137
  ```ruby
101
- Unitwise(1000, 'kg.s-1.(m/s)2').watt
102
- # => <Unitwise::Measurement 1000 watt>
138
+ Unitwise(1000, 'kg.s-1.(m/s)2').to_kilowatt
139
+ # => #<Unitwise::Measurement value=1.0 unit=kilowatt>
103
140
  ```
104
141
 
105
142
  ### Math
@@ -108,39 +145,30 @@ You can add or subtract compatible measurements.
108
145
 
109
146
  ```ruby
110
147
  2.meter + 3.inch - 1.yard
111
- # => <Unitwise::Measurement 1.1618 meter>
148
+ # => #<Unitwise::Measurement value=1.1618 unit=meter>
112
149
  ```
113
150
 
114
151
  You can multiply or divide measurements and numbers.
115
152
 
116
153
  ```ruby
117
154
  110.volt * 2
118
- # => <Unitwise::Measurement 220 volt>
155
+ # => #<Unitwise::Measurement value=220 unit=volt>
119
156
  ```
120
157
 
121
- You can multiply or divide measurements with measurements. Here is a fun example
122
- from Physics 101
158
+ You can multiply or divide measurements with measurements.
123
159
 
124
160
  ```ruby
125
- m = 20.kg # => <Unitwise::Measurement 20 kg>
126
-
127
- a = 10.m / 1.s2 # => <Unitwise::Measurement 10 m/s2>
161
+ 20.milligram / 1.liter
162
+ # => #<Unitwise::Measurement value=20 unit=mg/l>
128
163
 
129
- f = m * a # => <Unitwise::Measurement 50 kg.m/s2>
130
-
131
- f.newton # => <Unitwise::Measurement 50 newton>
132
164
  ```
133
165
 
134
- ### Unit Compatibility
135
-
136
- Unitwise is fairly intelligent about unit compatibility. It boils each unit down
137
- to it's basic composition to determine if they are compatible. For instance,
138
- energy (say a Joule, which can be expressed as kg*m2/s2) would have the
139
- components mass<sup>1</sup>, length<sup>2</sup>, and
140
- time<sup>-2</sup>. Any unit that could be reduced to this same composition
141
- would be considered compatible.
166
+ Exponentiation is also supported.
142
167
 
143
- I've extracted this datatype into it's own gem ([SignedMultiset](//github.com/joshwlewis/signed_multiset)) if you find this construct interesting.
168
+ ```ruby
169
+ (10.cm ** 3).to_liter
170
+ # => #<Unitwise::Measurement value=1.0 unit=liter>
171
+ ```
144
172
 
145
173
  ### Unit Names and Atom Codes
146
174
 
@@ -157,30 +185,28 @@ Just as an example, you can see here that there are actually a few versions of i
157
185
  and foot:
158
186
 
159
187
  ```ruby
160
- 1.convert('[ft_i]') == 1.convert('[ft_us]') # => false
188
+ 1.convert_to('[ft_i]') == 1.convert_to('[ft_us]') # => false
161
189
 
162
- 3.convert('[in_br]') == 3.convert('[in_i]') # => false
190
+ 3.convert_to('[in_br]') == 3.convert_to('[in_i]') # => false
163
191
  ```
164
192
 
165
193
  ### Available Units
166
194
 
167
- If you are looking for a particular unit (or 'atom' in the UCUM spec), chances
168
- are that it is in here. There is a rudimentary search function for both
169
- `Unitwise::Atom` and `Unitwise::Prefix`.
195
+ If you are looking for a particular unit, you can search with a string or
196
+ Regexp.
170
197
 
171
198
  ```ruby
172
- Unitwise::Atom.search('fathom')
173
- # => [ ... ]
174
- Unitwise::Prefix.search('milli')
199
+ Unitwise.search('fathom')
175
200
  # => [ ... ]
201
+
176
202
  ```
177
203
 
178
- You can also get the official list from the UCUM website in XML format at
179
- [unitsofmeasure.org/ucum-essence.xml](http://unitsofmeasure.org/ucum-essence.xml)
180
- or a YAML version within this repo
204
+ You can also get the official list from the UCUM website in XML format at
205
+ [unitsofmeasure.org/ucum-essence.xml](http://unitsofmeasure.org/ucum-essence.xml)
206
+ or a YAML version within this repo
181
207
  [github.com/joshwlewis/unitwise/tree/master/data](//github.com/joshwlewis/unitwise/tree/master/data).
182
208
 
183
- ### Supported Ruby Versions
209
+ ## Supported Ruby Versions
184
210
 
185
211
  This library aims to support and is tested against the following Ruby
186
212
  implementations:
data/lib/unitwise/atom.rb CHANGED
@@ -41,26 +41,29 @@ module Unitwise
41
41
  # prefixes.
42
42
  # @return [true, false]
43
43
  # @api public
44
- def metric?
45
- base? ? true : !!metric
44
+ def metric
45
+ base? ? true : !!@metric
46
46
  end
47
+ alias_method :metric?, :metric
47
48
 
48
49
  # Determine if a unit is special. Special atoms are not defined on a
49
50
  # traditional ratio scale.
50
51
  # @return [true, false]
51
52
  # @api public
52
- def special?
53
- !!special
53
+ def special
54
+ !!@special
54
55
  end
56
+ alias_method :special?, :special
55
57
 
56
58
  # Determine if a unit is arbitrary. Arbitrary atoms are not of any specific
57
59
  # dimension and have no general meaning, therefore cannot be compared with
58
60
  # any other unit.
59
61
  # @return [true, false]
60
62
  # @api public
61
- def arbitrary?
62
- !!arbitrary
63
+ def arbitrary
64
+ !!@arbitrary
63
65
  end
66
+ alias_method :arbitrary?, :arbitrary
64
67
 
65
68
  # Determine how far away a unit is from a base unit.
66
69
  # @return [Integer]
data/lib/unitwise/base.rb CHANGED
@@ -7,11 +7,7 @@ module Unitwise
7
7
  @all ||= data.map{|d| self.new d }
8
8
  end
9
9
 
10
- def self.find(term)
11
- all.find { |i| i.search_strings.any? { |string| string == term } }
12
- end
13
-
14
- def self.find_by(method, string)
10
+ def self.find(string, method=:primary_code)
15
11
  self.all.find do |i|
16
12
  key = i.send(method)
17
13
  if key.respond_to?(:each)
@@ -22,12 +18,6 @@ module Unitwise
22
18
  end
23
19
  end
24
20
 
25
- def self.search(term)
26
- self.all.select do |i|
27
- i.search_strings.any? { |string| string =~ /#{term}/i }
28
- end
29
- end
30
-
31
21
  def names=(names)
32
22
  @names = Array(names)
33
23
  end
@@ -38,10 +28,6 @@ module Unitwise
38
28
  end
39
29
  end
40
30
 
41
- def search_strings
42
- [primary_code, secondary_code, names, slugs, symbol].flatten.compact
43
- end
44
-
45
31
  def to_s
46
32
  primary_code
47
33
  end
@@ -0,0 +1,76 @@
1
+ module Unitwise
2
+ # A compound is a combination of an atom and a prefix.
3
+ # This class is isolated from the rest of the code base and primarily exists
4
+ # for the convenience of listing and searching possible combinations
5
+ # of atoms and prefixes.
6
+ class Compound < Liner.new(:atom, :prefix)
7
+ # List all possible compounds
8
+ # @return [Array]
9
+ # @api public
10
+ def self.all
11
+ @all || build
12
+ end
13
+
14
+ # Builds up the list of possible compounds. Only required if you are
15
+ # defining your own atoms/prefixes.
16
+ # @return [Array]
17
+ # @api public
18
+ def self.build
19
+ compounds = Atom.all.map { |a| new(a) }
20
+ Atom.all.select(&:metric).each do |a|
21
+ Prefix.all.each do |p|
22
+ compounds << new(a, p)
23
+ end
24
+ end
25
+ @all = compounds
26
+ end
27
+
28
+ # Search for compounds with a search term
29
+ # @param [String, Regexp] What to search for
30
+ # @return [Array]
31
+ # @api public
32
+ def self.search(term)
33
+ all.select do |compound|
34
+ compound.search_strings.any? { |string| Regexp.new(term).match(string) }
35
+ end
36
+ end
37
+
38
+ [:primary_code, :secondary_code, :symbol].each do |attr|
39
+ define_method attr do
40
+ instance_variable_get("@#{attr}") ||
41
+ instance_variable_set("@#{attr}",
42
+ prefix ? "#{prefix.send attr}#{atom.send attr}" : atom.send(attr))
43
+ end
44
+ end
45
+
46
+ [:names, :slugs].each do |attr|
47
+ define_method attr do
48
+ instance_variable_get("@#{attr}") ||
49
+ instance_variable_set("@#{attr}",
50
+ if prefix
51
+ prefix.send(attr).zip(atom.send(attr)).map{ |set| set.join('') }
52
+ else
53
+ atom.send(attr)
54
+ end)
55
+ end
56
+ end
57
+
58
+ def atom=(value)
59
+ value.is_a?(Atom) ? super(value) : super(Atom.find value)
60
+ end
61
+
62
+ def prefix=(value)
63
+ value.is_a?(Prefix) ? super(value) : super(Prefix.find value)
64
+ end
65
+
66
+ def search_strings
67
+ @search_strings ||= [primary_code, secondary_code, symbol, names, slugs].flatten.uniq
68
+ end
69
+
70
+ def attribute_string
71
+ [:atom, :prefix, :primary_code, :secondary_code, :symbol, :names, :slugs].map do |attr|
72
+ "#{attr}='#{send attr}'"
73
+ end.join(', ')
74
+ end
75
+ end
76
+ end
@@ -5,8 +5,8 @@ module Unitwise
5
5
  rule(integer: simple(:i)) { i.to_i }
6
6
  rule(fixnum: simple(:f)) { f.to_f }
7
7
 
8
- rule(prefix_code: simple(:c)) { |ctx| Prefix.find_by(ctx[:key], ctx[:c]) }
9
- rule(atom_code: simple(:c)) { |ctx| Atom.find_by(ctx[:key], ctx[:c]) }
8
+ rule(prefix_code: simple(:c)) { |ctx| Prefix.find(ctx[:c], ctx[:key]) }
9
+ rule(atom_code: simple(:c)) { |ctx| Atom.find(ctx[:c], ctx[:key]) }
10
10
  rule(term: subtree(:h)) { Term.new(h) }
11
11
 
12
12
  rule(operator: simple(:o), right: simple(:r)) do
@@ -1,11 +1,29 @@
1
1
  class Numeric
2
- def convert(unit)
2
+ # Converts numeric to a measurement
3
+ # @param unit [Unitwise::Unit, String] The unit to use in the measurement
4
+ # @return [Unitwise::Measurement]
5
+ # @example
6
+ # 26.2.convert_to('mile') # => #<Unitwise::Measurement 1 mile>
7
+ # @api public
8
+ def convert_to(unit)
3
9
  Unitwise::Measurement.new(self, unit)
4
10
  end
5
11
 
12
+ # Converts numeric to a measurement by the method name
13
+ # @exmple
14
+ # 26.2.mile # => #<Unitwise::Measurement 26.2 mile>
15
+ # 100.to_foot # => #<Unitwise::Measurement 100 foot>
16
+ # @api semipublic
6
17
  def method_missing(meth, *args, &block)
7
- convert(meth)
8
- rescue Unitwise::ExpressionError
9
- super(meth, *args)
18
+ if args.empty? && !block_given?
19
+ unit = (match = /\Ato_(\w+)\Z/.match(meth)) ? match[1] : meth
20
+ begin
21
+ convert_to(unit)
22
+ rescue Unitwise::ExpressionError
23
+ super(meth, *args, &block)
24
+ end
25
+ else
26
+ super(meth, *args, &block)
27
+ end
10
28
  end
11
29
  end
@@ -23,10 +23,10 @@ module Unitwise
23
23
  # @param other_unit [String, Measurement::Unit] Either a string expression
24
24
  # or a Measurement::Unit
25
25
  # @example
26
- # measurement1.convert('foot')
27
- # measurement2.convert('kilogram')
26
+ # measurement1.convert_to('foot')
27
+ # measurement2.convert_to('kilogram')
28
28
  # @api public
29
- def convert(other_unit)
29
+ def convert_to(other_unit)
30
30
  other_unit = Unit.new(other_unit)
31
31
  if similar_to?(other_unit)
32
32
  new(converted_value(other_unit), other_unit)
@@ -126,13 +126,17 @@ module Unitwise
126
126
  Rational(value)
127
127
  end
128
128
 
129
- # Will attempt to convert to a unit by the method name.
129
+ # Will attempt to convert to a unit by method name.
130
130
  # @example
131
- # measurement.foot # => <Unitwise::Measurement 4 foot>
131
+ # measurement.to_foot # => <Unitwise::Measurement 4 foot>
132
132
  # @api semipublic
133
133
  def method_missing(meth, *args, &block)
134
- if Unitwise::Expression.decompose(meth)
135
- self.convert(meth)
134
+ if args.empty? && !block_given? && (match = /\Ato_(\w+)\Z/.match(meth))
135
+ begin
136
+ convert_to(match[1])
137
+ rescue ExpressionError
138
+ super(meth, *args, &block)
139
+ end
136
140
  else
137
141
  super(meth, *args, &block)
138
142
  end
@@ -168,7 +172,7 @@ module Unitwise
168
172
  # @api private
169
173
  def combine(operator, other)
170
174
  if similar_to?(other)
171
- new(value.send(operator, other.convert(unit).value), unit)
175
+ new(value.send(operator, other.convert_to(unit).value), unit)
172
176
  end
173
177
  end
174
178
 
@@ -179,7 +183,7 @@ module Unitwise
179
183
  new(value.send(operator, other), unit)
180
184
  elsif other.respond_to?(:composition)
181
185
  if similar_to?(other)
182
- converted = other.convert(unit)
186
+ converted = other.convert_to(unit)
183
187
  new(value.send(operator, converted.value), unit.send(operator, converted.unit))
184
188
  else
185
189
  new(value.send(operator, other.value), unit.send(operator, other.unit))
@@ -14,13 +14,6 @@ module Unitwise
14
14
  @unit.is_a?(Unit) ? @unit : Unit.new(@unit)
15
15
  end
16
16
 
17
- # Duplicate this instance
18
- # @return [Unitwise::Unit]
19
- # @api public
20
- def dup
21
- self.class.new(value, unit)
22
- end
23
-
24
17
  # List the atoms associated with this scale's unit.
25
18
  # @return [Array]
26
19
  # @api public
@@ -28,7 +21,7 @@ module Unitwise
28
21
  unit.atoms
29
22
  end
30
23
 
31
- # List the atoms associated with this scale's unit.
24
+ # List the terms associated with this scale's unit.
32
25
  # @return [Array]
33
26
  # @api public
34
27
  def terms
@@ -74,18 +67,14 @@ module Unitwise
74
67
  unit.depth + 1
75
68
  end
76
69
 
77
- # Is this the deepest level scale in the scale chain?
78
- # @return [true, false]
79
- # @api public
80
- def terminal?
81
- depth <= 3
82
- end
83
-
84
70
  # Convert to a simple string representing the scale.
85
71
  # @api public
86
72
  def to_s
87
73
  "#{value} #{unit}"
88
74
  end
89
75
 
76
+ def inspect
77
+ "#<#{self.class} value=#{value} unit=#{unit}>"
78
+ end
90
79
  end
91
80
  end
data/lib/unitwise/unit.rb CHANGED
@@ -34,28 +34,16 @@ module Unitwise
34
34
  terms.first.functional(x, forward)
35
35
  end
36
36
 
37
- def dup
38
- self.class.new(expression)
39
- end
40
-
41
37
  def depth
42
38
  terms.map(&:depth).max + 1
43
39
  end
44
40
 
45
- def terminal?
46
- depth <= 3
47
- end
48
-
49
41
  def root_terms
50
42
  terms.flat_map(&:root_terms)
51
43
  end
52
44
 
53
45
  def scalar
54
- if terms.empty?
55
- 1
56
- else
57
- terms.map(&:scalar).inject(&:*)
58
- end
46
+ terms.map(&:scalar).inject(&:*)
59
47
  end
60
48
 
61
49
  def *(other)
@@ -1,3 +1,3 @@
1
1
  module Unitwise
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/unitwise.rb CHANGED
@@ -11,11 +11,22 @@ require 'unitwise/atom'
11
11
  require 'unitwise/prefix'
12
12
  require 'unitwise/term'
13
13
  require 'unitwise/unit'
14
+ require 'unitwise/compound'
14
15
  require 'unitwise/errors'
15
16
 
16
- # Unitwise is a library for performing mathematical operations and conversions
17
+ # Unitwise is a library for performing mathematical operations and conversions
17
18
  # on all units defined by the [Unified Code for Units of Measure(UCUM).
18
19
  module Unitwise
20
+
21
+ # Search for available compounds. This is just a helper method for
22
+ # convenience
23
+ # @param term [String, Regexp]
24
+ # @return [Array]
25
+ # @api public
26
+ def self.search(term)
27
+ Compound.search(term)
28
+ end
29
+
19
30
  # The system path for the installed gem
20
31
  # @api private
21
32
  def self.path
@@ -44,4 +55,3 @@ def Unitwise(first, last=nil)
44
55
  Unitwise::Measurement.new(1, first)
45
56
  end
46
57
  end
47
-
@@ -36,16 +36,6 @@ module ScaleTests
36
36
  end
37
37
  end
38
38
 
39
- describe "#dup" do
40
- it "must return a new instance" do
41
- subject.must_respond_to(:dup)
42
- subject.dup.must_be_instance_of(described_class)
43
- subject.dup.value.must_equal subject.value
44
- subject.dup.unit.to_s.must_equal subject.unit.to_s
45
- subject.dup.object_id.wont_equal subject.dup.object_id
46
- end
47
- end
48
-
49
39
  describe "#terms" do
50
40
  it "must return an array of terms" do
51
41
  subject.terms.must_be_kind_of(Enumerable)
@@ -90,12 +80,6 @@ module ScaleTests
90
80
  end
91
81
  end
92
82
 
93
- describe "#terminal?" do
94
- it "must return true for the last of kind in the chain" do
95
- subject.terminal?.must_equal false
96
- k.terminal?.must_equal true
97
- end
98
- end
99
83
  end
100
84
  end
101
85
  end
@@ -23,14 +23,6 @@ describe Unitwise::Atom do
23
23
  end
24
24
  end
25
25
 
26
- describe "::search" do
27
- it "must search for atoms" do
28
- result = subject.search("inch")
29
- result.count.must_equal 10
30
- result.must_include subject.find("inch")
31
- end
32
- end
33
-
34
26
  let(:second) { Unitwise::Atom.find("s") }
35
27
  let(:yard) { Unitwise::Atom.find("[yd_i]")}
36
28
  let(:pi) { Unitwise::Atom.find("[pi]")}
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ describe Unitwise::Compound do
4
+ describe :class_methods do
5
+ describe :all do
6
+ subject { Unitwise::Compound.all }
7
+ it { subject.must_be_kind_of Enumerable }
8
+ it { subject.first.must_be_instance_of Unitwise::Compound }
9
+ end
10
+ describe :search do
11
+ it "should return a list of units" do
12
+ search = Unitwise::Compound.search('foo')
13
+ search.must_be_kind_of Enumerable
14
+ search.first.must_be_instance_of Unitwise::Compound
15
+ end
16
+ end
17
+ end
18
+
19
+ describe :instance_methods do
20
+ let(:prefixed) { Unitwise::Compound.new('m','k') }
21
+ let(:unprefixed) { Unitwise::Compound.new("[in_i]") }
22
+ it "should have an atom" do
23
+ [prefixed, unprefixed].each { |ex| ex.atom.must_be_instance_of Unitwise::Atom }
24
+ end
25
+ it "should have a prefix when appropriate" do
26
+ prefixed.prefix.must_be_instance_of Unitwise::Prefix
27
+ unprefixed.prefix.must_equal nil
28
+ end
29
+ it "should concatenate common methods" do
30
+ prefixed.primary_code.must_equal 'km'
31
+ unprefixed.secondary_code.must_equal '[IN_I]'
32
+ prefixed.names.must_equal ['kilometer']
33
+ unprefixed.slugs.must_equal ['inch']
34
+ end
35
+ it "should have an array of strings for searching" do
36
+ prefixed.search_strings.must_be_kind_of(Array)
37
+ end
38
+ it "should look ok for inspection" do
39
+ prefixed.inspect.must_include 'km'
40
+ unprefixed.inspect.must_include '[IN_I]'
41
+ end
42
+ end
43
+ end
@@ -4,42 +4,47 @@ describe Numeric do
4
4
 
5
5
  describe "#convert" do
6
6
  it "must work for Integer" do
7
- measurement = 22.convert("kg")
7
+ measurement = 22.convert_to("kg")
8
8
  measurement.must_be_instance_of(Unitwise::Measurement)
9
9
  measurement.value.must_equal 22
10
10
  end
11
11
  it "must work for Fixnum" do
12
- measurement = 24.25.convert("[ft_i]")
12
+ measurement = 24.25.convert_to("[ft_i]")
13
13
  measurement.must_be_instance_of(Unitwise::Measurement)
14
14
  measurement.value.must_equal 24.25
15
15
  end
16
16
  it "must work for Float" do
17
- measurement = (22.0/7).convert("[mi_i]")
17
+ measurement = (22.0/7).convert_to("[mi_i]")
18
18
  measurement.must_be_instance_of(Unitwise::Measurement)
19
19
  measurement.value.must_equal 3.142857142857143
20
20
  end
21
21
  it "must work for Rational" do
22
- measurement = Rational(22/7).convert("N/m2")
22
+ measurement = Rational(22/7).convert_to("N/m2")
23
23
  measurement.must_be_instance_of(Unitwise::Measurement)
24
24
  measurement.value.must_equal Rational(22/7)
25
25
  end
26
26
  end
27
27
 
28
28
  describe "#method_missing" do
29
- it "must match mm" do
29
+ it "must match 'mm'" do
30
30
  mm = 2.5.mm
31
31
  mm.must_be_instance_of(Unitwise::Measurement)
32
32
  mm.value.must_equal 2.5
33
33
  end
34
- it "must match foot" do
35
- ft = 4.foot
36
- ft.must_be_instance_of(Unitwise::Measurement)
37
- ft.value.must_equal 4
34
+ it "must match 'to_mm'" do
35
+ mm = 2.5.to_mm
36
+ mm.must_be_instance_of(Unitwise::Measurement)
37
+ mm.value.must_equal 2.5
38
38
  end
39
+
39
40
  it "must not match 'foo'" do
40
41
  ->{ 1.foo }.must_raise NoMethodError
41
42
  end
42
43
 
44
+ it "must not match 'to_foo'" do
45
+ ->{ 1.to_foo }.must_raise NoMethodError
46
+ end
47
+
43
48
  end
44
49
 
45
50
 
@@ -11,27 +11,27 @@ describe Unitwise::Measurement do
11
11
  end
12
12
  end
13
13
 
14
- describe "#convert" do
14
+ describe "#convert_to" do
15
15
  it "must convert to a similar unit code" do
16
- mph.convert('km/h').value.must_equal 96.56063999999999
16
+ mph.convert_to('km/h').value.must_equal 96.56063999999999
17
17
  end
18
18
  it "must raise an error if the units aren't similar" do
19
- ->{ mph.convert('N') }.must_raise Unitwise::ConversionError
19
+ ->{ mph.convert_to('N') }.must_raise Unitwise::ConversionError
20
20
  end
21
21
  it "must convert special units to their base units" do
22
- cel.convert('K').value.must_equal 295.15
22
+ cel.convert_to('K').value.must_equal 295.15
23
23
  end
24
24
  it "must convert base units to special units" do
25
- k.convert('Cel').value.must_equal 100
25
+ k.convert_to('Cel').value.must_equal 100
26
26
  end
27
27
  it "must convert special units to special units" do
28
- f.convert('Cel').value.must_equal 37
28
+ f.convert_to('Cel').value.must_equal 37
29
29
  end
30
30
  it "must convert special units to non-special units" do
31
- cel.convert("[degR]").value.must_equal 531.27
31
+ cel.convert_to("[degR]").value.must_equal 531.27
32
32
  end
33
33
  it "must convert derived units to special units" do
34
- r.convert("Cel").value.round.must_equal 0
34
+ r.convert_to("Cel").value.round.must_equal 0
35
35
  end
36
36
  end
37
37
 
@@ -127,14 +127,14 @@ describe Unitwise::Measurement do
127
127
 
128
128
  describe "#method_missing" do
129
129
  let(:meter) { Unitwise::Measurement.new(1, 'm')}
130
- it "must convert 'mm'" do
131
- convert = meter.mm
130
+ it "must convert 'to_mm'" do
131
+ convert = meter.to_mm
132
132
  convert.must_be_instance_of Unitwise::Measurement
133
133
  convert.value.must_equal 1000
134
134
  end
135
135
 
136
- it "must convert 'foot'" do
137
- convert = meter.foot
136
+ it "must convert 'to_foot'" do
137
+ convert = meter.to_foot
138
138
  convert.must_be_instance_of Unitwise::Measurement
139
139
  convert.value.must_equal 3.280839895013123
140
140
  end
@@ -143,6 +143,10 @@ describe Unitwise::Measurement do
143
143
  ->{ meter.foo }.must_raise NoMethodError
144
144
  end
145
145
 
146
+ it "must not convert 'to_foo'" do
147
+ ->{ meter.to_foo }.must_raise NoMethodError
148
+ end
149
+
146
150
  end
147
151
 
148
152
  describe "#to_f" do
@@ -22,12 +22,4 @@ describe Unitwise::Prefix do
22
22
  end
23
23
  end
24
24
 
25
- describe "#search" do
26
- it "should search for a prefix" do
27
- result = subject.search("mi")
28
- result.count.must_equal 3
29
- result.must_include subject.find("milli")
30
- end
31
- end
32
-
33
25
  end
@@ -9,7 +9,15 @@ describe Unitwise do
9
9
  Unitwise('kg').must_be_instance_of Unitwise::Measurement
10
10
  end
11
11
  end
12
+
13
+ describe 'search' do
14
+ it "must return results" do
15
+ Unitwise.search('foo').must_be_instance_of(Array)
16
+ end
17
+ end
18
+
12
19
  it "should have a path" do
13
20
  Unitwise.path.must_match(/unitwise$/)
14
21
  end
15
- end
22
+
23
+ end
data/unitwise.gemspec CHANGED
@@ -17,13 +17,13 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^test/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency "liner", "~> 0.2.1"
21
- gem.add_dependency "signed_multiset", "~> 0.2.0"
22
- gem.add_dependency "parslet", "~> 1.5.0"
20
+ gem.add_dependency "liner", "~> 0.2"
21
+ gem.add_dependency "signed_multiset", "~> 0.2"
22
+ gem.add_dependency "parslet", "~> 1.5"
23
23
 
24
- gem.add_development_dependency "minitest"
25
- gem.add_development_dependency "rake"
26
- gem.add_development_dependency "nori"
27
- gem.add_development_dependency "nokogiri"
28
- gem.add_development_dependency "coveralls"
24
+ gem.add_development_dependency "minitest", ">= 5.0"
25
+ gem.add_development_dependency "rake", ">= 10.0"
26
+ gem.add_development_dependency "nori", "~> 2.3"
27
+ gem.add_development_dependency "nokogiri", "~> 1.6"
28
+ gem.add_development_dependency "coveralls", "~> 0.6"
29
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unitwise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lewis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-01 00:00:00.000000000 Z
11
+ date: 2014-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liner
@@ -16,112 +16,112 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.1
19
+ version: '0.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.1
26
+ version: '0.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: signed_multiset
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.0
33
+ version: '0.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.2.0
40
+ version: '0.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: parslet
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.5.0
47
+ version: '1.5'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.5.0
54
+ version: '1.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '5.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '5.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '10.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '10.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: nori
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '2.3'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '2.3'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: nokogiri
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '1.6'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '1.6'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: coveralls
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '0.6'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '0.6'
125
125
  description: Ruby implementation of the Unified Code for Units of Measure (UCUM)
126
126
  email:
127
127
  - josh.w.lewis@gmail.com
@@ -143,6 +143,7 @@ files:
143
143
  - lib/unitwise/atom.rb
144
144
  - lib/unitwise/base.rb
145
145
  - lib/unitwise/composable.rb
146
+ - lib/unitwise/compound.rb
146
147
  - lib/unitwise/errors.rb
147
148
  - lib/unitwise/expression.rb
148
149
  - lib/unitwise/expression/composer.rb
@@ -172,6 +173,7 @@ files:
172
173
  - test/test_helper.rb
173
174
  - test/unitwise/atom_test.rb
174
175
  - test/unitwise/base_test.rb
176
+ - test/unitwise/compound_test.rb
175
177
  - test/unitwise/expression/decomposer_test.rb
176
178
  - test/unitwise/expression/matcher_test.rb
177
179
  - test/unitwise/expression/parser_test.rb
@@ -214,6 +216,7 @@ test_files:
214
216
  - test/test_helper.rb
215
217
  - test/unitwise/atom_test.rb
216
218
  - test/unitwise/base_test.rb
219
+ - test/unitwise/compound_test.rb
217
220
  - test/unitwise/expression/decomposer_test.rb
218
221
  - test/unitwise/expression/matcher_test.rb
219
222
  - test/unitwise/expression/parser_test.rb
@@ -225,3 +228,4 @@ test_files:
225
228
  - test/unitwise/term_test.rb
226
229
  - test/unitwise/unit_test.rb
227
230
  - test/unitwise_test.rb
231
+ has_rdoc: