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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +14 -12
- data/lib/unitwise.rb +2 -6
- data/lib/unitwise/base.rb +1 -1
- data/lib/unitwise/expression.rb +1 -1
- data/lib/unitwise/expression/composer.rb +9 -7
- data/lib/unitwise/expression/decomposer.rb +6 -3
- data/lib/unitwise/expression/matcher.rb +5 -3
- data/lib/unitwise/expression/parser.rb +10 -5
- data/lib/unitwise/expression/transformer.rb +14 -7
- data/lib/unitwise/ext/numeric.rb +17 -3
- data/lib/unitwise/measurement.rb +9 -4
- data/lib/unitwise/scale.rb +16 -1
- data/lib/unitwise/search.rb +9 -7
- data/lib/unitwise/unit.rb +59 -1
- data/lib/unitwise/version.rb +1 -1
- data/test/support/scale_tests.rb +5 -0
- data/test/unitwise/ext/numeric_test.rb +4 -1
- data/test/unitwise/measurement_test.rb +16 -2
- data/test/unitwise_test.rb +5 -2
- metadata +32 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddf45867c28ef57163f7703f4b80c48dff1e4120
|
4
|
+
data.tar.gz: 53c4b64c1fa5a86553f49d4ee2ae6072eb46ca70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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')
|
71
|
+
Unitwise(100, 'pound') # => #<Unitwise::Measurement value=100 unit=pound>
|
69
72
|
```
|
70
73
|
|
71
|
-
Unitwise doesn't mess with the
|
72
|
-
|
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
|
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
|
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
|
189
|
+
Unitwise(1, '[ft_i]') == Unitwise(1, '[ft_us]') # => false
|
188
190
|
|
189
|
-
3
|
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
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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(
|
57
|
-
|
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
data/lib/unitwise/expression.rb
CHANGED
@@ -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{|
|
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{|
|
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 |
|
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
|
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
|
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) >>
|
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(')') >>
|
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 >>
|
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)) { |
|
9
|
-
rule(:atom_code => simple(:c)) { |
|
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),
|
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)
|
23
|
-
|
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
|
data/lib/unitwise/ext/numeric.rb
CHANGED
@@ -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
|
-
# @
|
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
|
-
|
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
|
data/lib/unitwise/measurement.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/unitwise/scale.rb
CHANGED
@@ -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
|
data/lib/unitwise/search.rb
CHANGED
@@ -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.
|
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.
|
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
|
data/lib/unitwise/version.rb
CHANGED
data/test/support/scale_tests.rb
CHANGED
@@ -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(
|
210
|
-
meter.to_s.
|
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")
|
data/test/unitwise_test.rb
CHANGED
@@ -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
|
9
|
-
Unitwise(
|
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.
|
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-
|
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.
|
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:
|