unitwise 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: