unitwise 0.10.0 → 1.0.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: b218dca039e22556a558a1ec6e20887a9d1776fe
4
- data.tar.gz: a0015ccf978b027d0adba04e331055dfddcaa069
3
+ metadata.gz: ddf45867c28ef57163f7703f4b80c48dff1e4120
4
+ data.tar.gz: 53c4b64c1fa5a86553f49d4ee2ae6072eb46ca70
5
5
  SHA512:
6
- metadata.gz: 49d3df5c02e5c6cc71df3d82c39cd586b18482c08e1cf14b5091bf2f047a4f73156f91b4b623ddd2997fa5b7bdc8b9fa7d341fb547112ec25258c7003b1cbd92
7
- data.tar.gz: 06e22d56046f7d5cbaac2f69c68e416f4b652865fa0dd46d3c52d9e66ffbecb7b708899054f1945785f7d6ba5121e24e2e4a51f87affa627c49319fdb8640b3c
6
+ metadata.gz: 8de1d241930ba24d479d4b54f4ede6e72383a3b1254231b8471ea215035e9b3567ea8a834dcf023865b2038b56772234d291bd95b846a7c627e730cf3d1ef331
7
+ data.tar.gz: 798ee2b96ab5123dc5494a286e5bae69143e9b51e4039c03906792fe51320540c162eeddf832969f1a3ca43b34d02c1a61ee4f37b5f21db6ae68a87a96ee8c6f
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Unitwise Changelog
2
+
3
+ All notable changes to Unitwise will be documented in this file, starting at
4
+ version 1.0.0.
5
+
6
+ Unitwise uses semantic versioning.
7
+
8
+ ## 1.0.0 - 2014-08-25
9
+
10
+ ### Added
11
+ - Uniwise() now accepts a Unitwise::Measurement as the first argument.
12
+ - Unitwise::Measurement now supports #round.
13
+
14
+ ### Fixed
15
+ - Respect Rationals when inspecting/printing a Unitwise::Measurement.
16
+ - Dynamically created methods from unitwise/ext now work with #respond_to?
17
+ and #methods appropriately.
18
+
19
+ ### Deprecated
20
+ - Unitwise() and Unitwise::Measurement.new() now requires two non-optional
21
+ arguments (value and unit).
22
+ - Unitwise::Measurement no longer has an implicit Integer conversion.
data/README.md CHANGED
@@ -30,6 +30,9 @@ speed = ((2.0 * acceleration * distance) ** 0.5).convert_to("mile/hour")
30
30
  # => #<Unitwise::Measurement value=180.0 unit=mile/hour>
31
31
  ```
32
32
 
33
+ [RubyTapas](http://rubytapas.com) subscribers can also view a screencast:
34
+ [225-Unitwise](https://rubytapas.dpdcart.com/subscriber/post?id=563)
35
+
33
36
  ## Rationale
34
37
 
35
38
  Unitwise is based on the [Unified Code for Units of Measure(UCUM)](http://unitsofmeasure.org/),
@@ -65,12 +68,11 @@ Measurements can be instantiated with `Unitwise()`.
65
68
  require 'unitwise'
66
69
 
67
70
  Unitwise(2.3, 'kilogram') # => #<Unitwise::Measurement value=2.3 unit=kilogram>
68
- Unitwise('pound') # => #<Unitwise::Measurement value=1 unit=pound>
71
+ Unitwise(100, 'pound') # => #<Unitwise::Measurement value=100 unit=pound>
69
72
  ```
70
73
 
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.
74
+ Unitwise doesn't mess with the core library by default. However, you can
75
+ optionally require the core extensions for some handy helpers.
74
76
 
75
77
  ```ruby
76
78
  require 'unitwise/ext'
@@ -103,7 +105,7 @@ It also has the ability to compare measurements with the same or different units
103
105
 
104
106
  ```ruby
105
107
  12.inch == 1.foot # => true
106
- 1.meter > 1.yard # => true
108
+ 1.meter > 1.yard # => true
107
109
  ```
108
110
 
109
111
  Again, you have to compare compatible units. For example, comparing two
@@ -114,7 +116,7 @@ temperatures will work, comparing a mass to a length would fail.
114
116
  You can use shorthand for SI units.
115
117
 
116
118
  ```ruby
117
- 1000.m == 1.km # => true
119
+ 1000.m == 1.km # => true
118
120
  1.ml == 0.001.l # => true
119
121
  ```
120
122
 
@@ -184,9 +186,9 @@ Just for example, you can see here that there are actually a few versions of inc
184
186
  and foot:
185
187
 
186
188
  ```ruby
187
- 1.convert_to('[ft_i]') == 1.convert_to('[ft_us]') # => false
189
+ Unitwise(1, '[ft_i]') == Unitwise(1, '[ft_us]') # => false
188
190
 
189
- 3.convert_to('[in_br]') == 3.convert_to('[in_i]') # => false
191
+ Unitwise(3, '[in_br]') == Unitwise(3, '[in_i]') # => false
190
192
  ```
191
193
 
192
194
  ### Available Units
@@ -228,10 +230,10 @@ desigation you used. However, if you want to print it with another designation,
228
230
  that's also possible:
229
231
 
230
232
  ```ruby
231
- temperature_change = Unitwise(10, "Cel/h")
232
- temperature_change.to_s # => "10 Cel/h"
233
- temperature_change.to_s(:names) # => "10 degree Celsius/hour"
234
- temperature_change.to_s(:symbol) # => "10 °C/h"
233
+ temperature = Unitwise(10, "Cel")
234
+ temperature.to_s # => "10 Cel"
235
+ temperature.to_s(:names) # => "10 degree Celsius"
236
+ temperature.to_s(:symbol) # => "10 °C"
235
237
  ```
236
238
 
237
239
  There is on caveat here. You must use the same designation for each atom in a
data/lib/unitwise.rb CHANGED
@@ -53,10 +53,6 @@ end
53
53
  # Unitwise(20, 'mile') # => #<Unitwise::Measurement 20 mile>
54
54
  # Unitwise('km') # => #<Unitwise::Measurement 1 km>
55
55
  # @api public
56
- def Unitwise(first, last=nil)
57
- if last
58
- Unitwise::Measurement.new(first, last)
59
- else
60
- Unitwise::Measurement.new(1, first)
61
- end
56
+ def Unitwise(*args)
57
+ Unitwise::Measurement.new(*args)
62
58
  end
data/lib/unitwise/base.rb CHANGED
@@ -51,7 +51,7 @@ module Unitwise
51
51
  # @return [String]
52
52
  # @api public
53
53
  def to_s(mode = :primary_code)
54
- res = self.send(mode)
54
+ res = send(mode)
55
55
  res.respond_to?(:each) ? res.first.to_s : res.to_s
56
56
  end
57
57
  end
@@ -15,7 +15,7 @@ module Unitwise
15
15
  # @example
16
16
  # Unitwise::Expression.compose(terms) # => "m2/s2"
17
17
  # @api public
18
- def compose(terms, method=:primary_code)
18
+ def compose(terms, method = :primary_code)
19
19
  Composer.new(terms, method).expression
20
20
  end
21
21
 
@@ -1,5 +1,7 @@
1
1
  module Unitwise
2
2
  module Expression
3
+ # Composer creates string expressions for arrays of terms, following
4
+ # UCUM's conventions.
3
5
  class Composer
4
6
  attr_reader :terms, :mode
5
7
  def initialize(terms, mode)
@@ -17,15 +19,15 @@ module Unitwise
17
19
  end
18
20
 
19
21
  def numerator
20
- @numerator ||= set.select{|k,v| v > 0}.map do |k,v|
21
- "#{k[:f] if k[:f] != 1}#{k[:p]}#{k[:a]}#{v if v != 1}"
22
- end.select{|t| !t.empty?}.join('.')
22
+ @numerator ||= set.select{ |_, v| v > 0 }.map do |k, v|
23
+ "#{ k[:f] if k[:f] != 1 }#{ k[:p] }#{ k[:a] }#{ v if v != 1 }"
24
+ end.select { |t| !t.empty? }.join('.')
23
25
  end
24
26
 
25
27
  def denominator
26
- @denominator ||= set.select{|k,v| v < 0}.map do |k,v|
27
- "#{k[:f] if k[:f] != 1}#{k[:p]}#{k[:a]}#{-v if v != -1}"
28
- end.select{|t| !t.empty?}.join('.')
28
+ @denominator ||= set.select{ |_, v| v < 0 }.map do |k, v|
29
+ "#{ k[:f] if k[:f] != 1 }#{ k[:p] }#{ k[:a] }#{ -v if v != -1 }"
30
+ end.select { |t| !t.empty? }.join('.')
29
31
  end
30
32
 
31
33
  def expression
@@ -36,4 +38,4 @@ module Unitwise
36
38
  end
37
39
  end
38
40
  end
39
- end
41
+ end
@@ -1,5 +1,8 @@
1
1
  module Unitwise
2
2
  module Expression
3
+ # The decomposer is used to turn string expressions into collections of
4
+ # terms. It is responsible for executing the parsing and transformation
5
+ # of a string, as well as caching the results.
3
6
  class Decomposer
4
7
 
5
8
  MODES = [:primary_code, :secondary_code, :names, :slugs, :symbol]
@@ -20,7 +23,7 @@ module Unitwise
20
23
  end
21
24
 
22
25
  def parse
23
- @parse ||= PARSERS.reduce(nil) do |null,(mode, parser)|
26
+ @parse ||= PARSERS.reduce(nil) do |_, (mode, parser)|
24
27
  parsed = parser.parse(expression) rescue next
25
28
  @mode = mode
26
29
  break parsed
@@ -38,7 +41,7 @@ module Unitwise
38
41
  Array(transform)
39
42
  end
40
43
  end
41
-
44
+
42
45
  end
43
46
  end
44
- end
47
+ end
@@ -1,5 +1,7 @@
1
1
  module Unitwise
2
2
  module Expression
3
+ # Matcher is responsible for building up Parslet alternatives of atoms and
4
+ # prefixes to be used by Unitwise::Expression::Parser.
3
5
  class Matcher
4
6
  class << self
5
7
  def atom(mode)
@@ -17,19 +19,19 @@ module Unitwise
17
19
 
18
20
  attr_reader :collection, :mode
19
21
 
20
- def initialize(collection, mode=:primary_code)
22
+ def initialize(collection, mode = :primary_code)
21
23
  @collection = collection
22
24
  @mode = mode
23
25
  end
24
26
 
25
27
  def strings
26
- @stings ||= collection.map(&mode).flatten.compact.sort do |x,y|
28
+ @stings ||= collection.map(&mode).flatten.compact.sort do |x, y|
27
29
  y.length <=> x.length
28
30
  end
29
31
  end
30
32
 
31
33
  def matchers
32
- @matchers ||= strings.map {|s| Parslet::Atoms::Str.new(s) }
34
+ @matchers ||= strings.map { |s| Parslet::Atoms::Str.new(s) }
33
35
  end
34
36
 
35
37
  def alternative
@@ -1,8 +1,10 @@
1
1
  module Unitwise
2
2
  module Expression
3
+ # Parses a string expression into a hash tree representing the
4
+ # expression's terms, prefixes, and atoms.
3
5
  class Parser < Parslet::Parser
4
6
  attr_reader :key
5
- def initialize(key=:primary_code)
7
+ def initialize(key = :primary_code)
6
8
  @key = key
7
9
  end
8
10
 
@@ -37,17 +39,20 @@ module Unitwise
37
39
  rule (:operator) { (str('.') | str('/')).as(:operator) }
38
40
 
39
41
  rule (:term) do
40
- ((factor >> simpleton | simpleton | factor) >> exponent.maybe >> annotation.maybe).as(:term)
42
+ ((factor >> simpleton | simpleton | factor) >>
43
+ exponent.maybe >> annotation.maybe).as(:term)
41
44
  end
42
45
 
43
46
  rule (:group) do
44
- (factor.maybe >> str('(') >> expression.as(:nested) >> str(')') >> exponent.maybe).as(:group)
47
+ (factor.maybe >> str('(') >> expression.as(:nested) >> str(')') >>
48
+ exponent.maybe).as(:group)
45
49
  end
46
50
 
47
51
  rule (:expression) do
48
- ((group | term).as(:left)).maybe >> (operator >> expression.as(:right)).maybe
52
+ ((group | term).as(:left)).maybe >>
53
+ (operator >> expression.as(:right)).maybe
49
54
  end
50
55
 
51
56
  end
52
57
  end
53
- end
58
+ end
@@ -1,30 +1,37 @@
1
1
  module Unitwise
2
2
  module Expression
3
+ # Transformer is responsible for turning a Unitwise::Expression::Parser
4
+ # hash result into a collection of Unitwise::Terms.
3
5
  class Transformer < Parslet::Transform
4
6
 
5
7
  rule(:integer => simple(:i)) { i.to_i }
6
8
  rule(:fixnum => simple(:f)) { f.to_f }
7
9
 
8
- rule(:prefix_code => simple(:c)) { |ctx| Prefix.find(ctx[:c], ctx[:mode]) }
9
- rule(:atom_code => simple(:c)) { |ctx| Atom.find(ctx[:c], ctx[:mode]) }
10
+ rule(:prefix_code => simple(:c)) { |x| Prefix.find(x[:c], x[:mode]) }
11
+ rule(:atom_code => simple(:c)) { |x| Atom.find(x[:c], x[:mode]) }
10
12
  rule(:term => subtree(:h)) { Term.new(h) }
11
13
 
12
14
  rule(:operator => simple(:o), :right => simple(:r)) do
13
15
  o == '/' ? r ** -1 : r
14
16
  end
15
17
 
16
- rule(:left => simple(:l), :operator => simple(:o), :right => simple(:r)) do
18
+ rule(:left => simple(:l), :operator => simple(:o),
19
+ :right => simple(:r)) do
17
20
  o == '/' ? l / r : l * r
18
21
  end
19
22
 
20
23
  rule(:left => simple(:l)) { l }
21
24
 
22
- rule(:group => { :factor => simple(:f) , :nested => simple(:n), :exponent => simple(:e) }) do
23
- ( n ** e ) * f
25
+ rule(:group => { :factor => simple(:f),
26
+ :nested => simple(:n), :exponent => simple(:e) }) do
27
+ (n ** e) * f
28
+ end
29
+
30
+ rule(:group => { :nested => simple(:n) , :exponent => simple(:e) }) do
31
+ n ** e
24
32
  end
25
- rule(:group => { :nested => simple(:n) , :exponent => simple(:e)}) { n ** e }
26
33
 
27
34
  rule(:group => { :nested => simple(:n) }) { n }
28
35
  end
29
36
  end
30
- end
37
+ end
@@ -1,3 +1,6 @@
1
+ # Unitwise extends Numeric to add these dyanmic method conveniences: `1.meter`,
2
+ # `26.2.to_mile`, and `4.convert_to("Joule")`. These overrides are optional due
3
+ # to their controversial nature. `require 'unitwise/ext'` to enable them.
1
4
  class Numeric
2
5
  # Converts numeric to a measurement
3
6
  # @param unit [Unitwise::Unit, String] The unit to use in the measurement
@@ -10,7 +13,7 @@ class Numeric
10
13
  end
11
14
 
12
15
  # Converts numeric to a measurement by the method name
13
- # @exmple
16
+ # @example
14
17
  # 26.2.mile # => #<Unitwise::Measurement 26.2 mile>
15
18
  # 100.to_foot # => #<Unitwise::Measurement 100 foot>
16
19
  # @api semipublic
@@ -18,7 +21,9 @@ class Numeric
18
21
  if args.empty? && !block_given?
19
22
  unit = (match = /\Ato_(\w+)\Z/.match(meth.to_s)) ? match[1] : meth
20
23
  begin
21
- convert_to(unit)
24
+ res = convert_to(unit)
25
+ Numeric.define_unit_conversion_methods_for(unit)
26
+ res
22
27
  rescue Unitwise::ExpressionError
23
28
  super(meth, *args, &block)
24
29
  end
@@ -26,4 +31,13 @@ class Numeric
26
31
  super(meth, *args, &block)
27
32
  end
28
33
  end
29
- end
34
+
35
+ def self.define_unit_conversion_methods_for(name)
36
+ [name.to_sym, "to_#{ name }".to_sym].each do |meth|
37
+ next if method_defined?(meth)
38
+ define_method meth do
39
+ convert_to(name)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -85,6 +85,14 @@ module Unitwise
85
85
  end
86
86
  end
87
87
 
88
+ # Round the measurement value. Delegates to the value's class.
89
+ # @return [Integer, Float]
90
+ # @api public
91
+ def round(digits = nil)
92
+ rounded_value = digits ? value.round(digits) : value.round
93
+ self.class.new(rounded_value, unit)
94
+ end
95
+
88
96
  # Coerce a numeric to a a measurement for mathematical operations
89
97
  # @param other [Numeric]
90
98
  # @example
@@ -107,7 +115,6 @@ module Unitwise
107
115
  def to_i
108
116
  Integer(value)
109
117
  end
110
- alias_method :to_int, :to_i
111
118
 
112
119
  # Convert a measurement to a Float.
113
120
  # @example
@@ -151,9 +158,7 @@ module Unitwise
151
158
 
152
159
  # Set the value for the measurement.
153
160
  # @api private
154
- def value=(value)
155
- @value = value
156
- end
161
+ attr_writer :value
157
162
 
158
163
  # Determine value of the unit after conversion to another unit
159
164
  # @api private
@@ -6,6 +6,16 @@ module Unitwise
6
6
  liner :value, :unit
7
7
  include Unitwise::Compatible
8
8
 
9
+ def initialize(value, unit)
10
+ self.value = if value.is_a? self.class
11
+ value.convert_to(unit).value
12
+ else
13
+ value
14
+ end
15
+ self.unit = unit
16
+ freeze
17
+ end
18
+
9
19
  # Set the unit vector.
10
20
  # @param value [String, Unitwise::Unit]
11
21
  # @api public
@@ -79,12 +89,17 @@ module Unitwise
79
89
  end
80
90
  memoize :depth
81
91
 
92
+ # Attempts to coerce the value to the simplest Numeric that fully expresses
93
+ # it's value. For instance a value of 1.0 would return 1, a value of
94
+ # #<BigDecimal:7f9558d559b8,'0.45E1',18(18)> would return 4.5.
95
+ # @return [Numeric]
96
+ # @api public
82
97
  def simplified_value
83
98
  if value.is_a?(Integer)
84
99
  value
85
100
  elsif (i = Integer(value)) == value
86
101
  i
87
- elsif value.is_a?(Float)
102
+ elsif value.is_a?(Float) || value.is_a?(Rational)
88
103
  value
89
104
  elsif (f = Float(value)) == value
90
105
  f
@@ -1,10 +1,13 @@
1
1
  module Unitwise
2
+ # The search module provides a simple search mechanism around known basic
3
+ # units. The full list of avaliable units infinite, so this search creates
4
+ # a small subset of atoms and prefixes to help users find what they are
5
+ # looking for. Thus, there is a multitude of valid units that may be
6
+ # constructed that this module will not be aware of.
2
7
  module Search
3
8
  class << self
4
9
  # An abbreviated list of possible units. These are known combinations
5
- # of atoms and prefixes. Since units can be combined to create more
6
- # complex units (and thus an infinite number), a full list can't be
7
- # provided.
10
+ # of atoms and prefixes.
8
11
  # @return [Array] A list of known units
9
12
  # @api public
10
13
  def all
@@ -12,14 +15,13 @@ module Unitwise
12
15
  units = []
13
16
  Atom.all.each do |a|
14
17
  units << build(a)
15
- Unitwise::Prefix.all.each { |p| units << build(a,p) } if a.metric?
18
+ Unitwise::Prefix.all.each { |p| units << build(a, p) } if a.metric?
16
19
  end
17
20
  units
18
21
  end
19
22
  end
20
23
 
21
- # Search the list of known units for a match. Note that this cannot
22
- # find all possible units, only simple combinations of atoms and prefixes.
24
+ # Search the list of known units for a match.
23
25
  # @param term [String, Regexp] The term to search for
24
26
  # @return [Array] A list of matching units.
25
27
  # @api public
@@ -36,7 +38,7 @@ module Unitwise
36
38
  # @param prefix [Unitwise::Prefix, nil]
37
39
  # @return [Unitwise::Unit]
38
40
  # @api private
39
- def build(atom, prefix=nil)
41
+ def build(atom, prefix = nil)
40
42
  Unit.new([Term.new(:atom => atom, :prefix => prefix)])
41
43
  end
42
44
  end
data/lib/unitwise/unit.rb CHANGED
@@ -9,6 +9,7 @@ module Unitwise
9
9
  # Create a new unit. You can send an expression or a collection of terms
10
10
  # @param input [String, Unit, [Term]] A string expression, a unit, or a
11
11
  # collection of tems.
12
+ # @api public
12
13
  def initialize(input)
13
14
  case input
14
15
  when Compatible
@@ -20,6 +21,9 @@ module Unitwise
20
21
  end
21
22
  end
22
23
 
24
+ # The collection of terms used by this unit.
25
+ # @return [Array]
26
+ # @api public
23
27
  def terms
24
28
  unless frozen?
25
29
  unless @terms
@@ -32,42 +36,74 @@ module Unitwise
32
36
  @terms
33
37
  end
34
38
 
39
+ # Build a string representation of this unit by it's terms.
40
+ # @param mode [Symbol] The mode to use to stringify the atoms
41
+ # (:primary_code, :names, :secondary_code).
42
+ # @return [String]
43
+ # @api public
35
44
  def expression(mode = mode)
36
45
  Expression.compose(terms, mode)
37
46
  end
38
47
 
48
+
49
+ # The collection of atoms that compose this unit. Essentially delegated to
50
+ # terms.
51
+ # @return [Array]
52
+ # @api public
39
53
  def atoms
40
54
  terms.map(&:atom)
41
55
  end
42
56
  memoize :atoms
43
57
 
58
+ # Is this unit special (meaning on a non-linear scale)?
59
+ # @return [true, false]
60
+ # @api public
44
61
  def special?
45
62
  terms.count == 1 && terms.all?(&:special?)
46
63
  end
47
64
  memoize :special?
48
65
 
66
+ # A number representing this unit's distance from it's deepest terminal atom.
67
+ # @return [Integer]
68
+ # @api public
49
69
  def depth
50
70
  terms.map(&:depth).max + 1
51
71
  end
52
72
  memoize :depth
53
73
 
74
+ # A collection of the deepest terms, or essential composition of the unit.
75
+ # @return [Array]
76
+ # @api public
54
77
  def root_terms
55
78
  terms.map(&:root_terms).flatten
56
79
  end
57
80
  memoize :root_terms
58
81
 
82
+ # Get a scalar value for this unit.
83
+ # @param magnitude [Numeric] An optional magnitude on this unit's scale.
84
+ # @return [Numeric] A scalar value on a linear scale
85
+ # @api public
59
86
  def scalar(magnitude = 1.0)
60
87
  terms.reduce(1.0) do |prod, term|
61
88
  prod * term.scalar(magnitude)
62
89
  end
63
90
  end
64
91
 
92
+ # Get a magnitude for this unit based on a linear scale value.
93
+ # Should only be used by units with special atoms in it's hierarchy.
94
+ # @param scalar [Numeric] A linear scalar value
95
+ # @return [Numeric] The equivalent magnitude on this scale
96
+ # @api public
65
97
  def magnitude(scalar = scalar)
66
98
  terms.reduce(1.0) do |prod, term|
67
99
  prod * term.magnitude(scalar)
68
100
  end
69
101
  end
70
102
 
103
+ # Multiply this unit by another unit, term, or number.
104
+ # @param other [Unitwise::Unit, Unitwise::Term, Numeric]
105
+ # @return [Unitwise::Unit]
106
+ # @api public
71
107
  def *(other)
72
108
  if other.respond_to?(:terms)
73
109
  self.class.new(terms + other.terms)
@@ -80,16 +116,26 @@ module Unitwise
80
116
  end
81
117
  end
82
118
 
119
+ # Divide this unit by another unit,term, or number.
120
+ # @param other [Unitwise::Unit, Unitwise::Term, Numeric]
121
+ # @return [Unitwise::Unit]
122
+ # @api public
83
123
  def /(other)
84
124
  if other.respond_to?(:terms)
85
125
  self.class.new(terms + other.terms.map { |t| t ** -1 })
86
126
  elsif other.respond_to?(:atom)
87
127
  self.class.new(terms << other ** -1)
128
+ elsif other.is_a?(Numeric)
129
+ self.class.new(terms.map { |t| t / other })
88
130
  else
89
131
  fail TypeError, "Can't divide #{self} by #{other}."
90
132
  end
91
133
  end
92
134
 
135
+ # Raise this unit to a numeric power.
136
+ # @param other [Numeric]
137
+ # @return [Unitwise::Unit]
138
+ # @api public
93
139
  def **(other)
94
140
  if other.is_a?(Numeric)
95
141
  self.class.new(terms.map { |t| t ** other })
@@ -98,17 +144,29 @@ module Unitwise
98
144
  end
99
145
  end
100
146
 
147
+ # A string representation of this unit.
148
+ # @param mode [:symbol] The mode used to represent the unit
149
+ # (:primary_code, :names, :secondary_code)
150
+ # @return [String]
151
+ # @api public
101
152
  def to_s(mode = mode)
102
153
  expression(mode || self.mode)
103
154
  end
104
155
 
156
+ # A collection of the possible string representations of this unit.
157
+ # Primarily used by Unitwise::Search.
158
+ # @return [Array]
159
+ # @api public
105
160
  def aliases
106
- [:names, :primary_code, :secondary_code, :symbol].map do |mode|
161
+ [:names, :primary_code, :secondary_code, :symbol].map do |mode|
107
162
  to_s(mode)
108
163
  end.uniq
109
164
  end
110
165
  memoize :aliases
111
166
 
167
+ # The default mode to use for inspecting and printing.
168
+ # @return [Symbol]
169
+ # @api semipublic
112
170
  def mode
113
171
  terms
114
172
  @mode || :primary_code
@@ -1,3 +1,3 @@
1
1
  module Unitwise
2
- VERSION = '0.10.0'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -97,6 +97,11 @@ module ScaleTests
97
97
  result.must_equal 4
98
98
  result.must_be_kind_of(Integer)
99
99
  end
100
+ it "should convert from a BigDecimal" do
101
+ result = described_class.new(BigDecimal("4.5"), "volt").simplified_value
102
+ result.must_equal 4.5
103
+ result.must_be_kind_of(Float)
104
+ end
100
105
  end
101
106
  describe "when the value is equivalent to a Float" do
102
107
  it "should convert from a BigDecimal" do
@@ -1,7 +1,6 @@
1
1
  require 'test_helper'
2
2
  require 'unitwise/ext'
3
3
  describe Numeric do
4
-
5
4
  describe "#convert" do
6
5
  it "must work for Integer" do
7
6
  measurement = 22.convert_to("kg")
@@ -30,11 +29,15 @@ describe Numeric do
30
29
  mm = 2.5.mm
31
30
  mm.must_be_instance_of(Unitwise::Measurement)
32
31
  mm.value.must_equal 2.5
32
+ 1.0.respond_to?(:mm).must_equal true
33
+ 3.respond_to?(:to_mm).must_equal true
33
34
  end
34
35
  it "must match 'to_mm'" do
35
36
  mm = 2.5.to_mm
36
37
  mm.must_be_instance_of(Unitwise::Measurement)
37
38
  mm.value.must_equal 2.5
39
+ 4.0.methods.grep(/mm/).count.must_equal 2
40
+ 1.methods.grep(/mm/).count.must_equal 2
38
41
  end
39
42
 
40
43
  it "must not match 'foo'" do
@@ -184,6 +184,20 @@ describe Unitwise::Measurement do
184
184
 
185
185
  end
186
186
 
187
+ describe "#round" do
188
+ it "must round Floats to Integers" do
189
+ result = Unitwise::Measurement.new(98.6, "[degF]").round
190
+ result.value.must_equal(99)
191
+ result.value.must_be_kind_of(Integer)
192
+ end
193
+ it "must round Floats to Floats" do
194
+ if RUBY_VERSION > '1.8.7'
195
+ result = Unitwise::Measurement.new(17.625, "J").round(2)
196
+ result.value.must_equal(17.63)
197
+ result.value.must_be_kind_of(Float)
198
+ end
199
+ end
200
+ end
187
201
  describe "#to_f" do
188
202
  it "must convert to a float" do
189
203
  f.to_f.must_be_kind_of(Float)
@@ -206,8 +220,8 @@ describe Unitwise::Measurement do
206
220
  it "should include the simplified value and use the mode it was created with" do
207
221
  foot = described_class.new(7.00, "foot")
208
222
  foot.to_s.must_equal "7 foot"
209
- meter = described_class.new(Rational(22,7), "m")
210
- meter.to_s.must_match(/3\.142\d+ m/)
223
+ meter = described_class.new(BigDecimal("3.142"), "m")
224
+ meter.to_s.must_equal("3.142 m")
211
225
  end
212
226
  it "should accept a mode and print that mode string" do
213
227
  temp = described_class.new(25, "degree Celsius")
@@ -5,8 +5,11 @@ describe Unitwise do
5
5
  it "should accept a number and string" do
6
6
  Unitwise(2, 'm/s').must_be_instance_of Unitwise::Measurement
7
7
  end
8
- it "should accept a lonely string" do
9
- Unitwise('kg').must_be_instance_of Unitwise::Measurement
8
+ it "should accept a measurement and a string" do
9
+ mass = Unitwise(1, "lb")
10
+ new_mass = Unitwise(mass, "kg")
11
+ new_mass.value.must_equal(0.45359237)
12
+ new_mass.unit.to_s.must_equal("kg")
10
13
  end
11
14
  end
12
15
 
metadata CHANGED
@@ -1,167 +1,167 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unitwise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 1.0.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-07-17 00:00:00.000000000 Z
11
+ date: 2014-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liner
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
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
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
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
40
  version: '0.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: memoizable
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0.4'
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
54
  version: '0.4'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bigdecimal
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.2'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.2'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: parslet
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '1.5'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.5'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: nokogiri
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ~>
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '1.5'
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
96
  version: '1.5'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: coveralls
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ~>
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0.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
110
  version: '0.6'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: pry
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ~>
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0.9'
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
124
  version: '0.9'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: minitest
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ~>
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
131
  version: '5.0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ~>
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '5.0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rake
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ~>
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
145
  version: '10.0'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ~>
150
+ - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '10.0'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: nori
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - ~>
157
+ - - "~>"
158
158
  - !ruby/object:Gem::Version
159
159
  version: '2.3'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - ~>
164
+ - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: '2.3'
167
167
  description: Physical quantity and units of measure conversion and math library
@@ -171,9 +171,10 @@ executables: []
171
171
  extensions: []
172
172
  extra_rdoc_files: []
173
173
  files:
174
- - .gitignore
175
- - .ruby-version
176
- - .travis.yml
174
+ - ".gitignore"
175
+ - ".ruby-version"
176
+ - ".travis.yml"
177
+ - CHANGELOG.md
177
178
  - Gemfile
178
179
  - LICENSE.txt
179
180
  - README.md
@@ -238,17 +239,17 @@ require_paths:
238
239
  - lib
239
240
  required_ruby_version: !ruby/object:Gem::Requirement
240
241
  requirements:
241
- - - '>='
242
+ - - ">="
242
243
  - !ruby/object:Gem::Version
243
244
  version: '0'
244
245
  required_rubygems_version: !ruby/object:Gem::Requirement
245
246
  requirements:
246
- - - '>='
247
+ - - ">="
247
248
  - !ruby/object:Gem::Version
248
249
  version: '0'
249
250
  requirements: []
250
251
  rubyforge_project:
251
- rubygems_version: 2.2.2
252
+ rubygems_version: 2.2.0
252
253
  signing_key:
253
254
  specification_version: 4
254
255
  summary: Convert between and perform mathematical operations on physical quantities
@@ -270,3 +271,4 @@ test_files:
270
271
  - test/unitwise/term_test.rb
271
272
  - test/unitwise/unit_test.rb
272
273
  - test/unitwise_test.rb
274
+ has_rdoc: