unitwise 1.0.1 → 1.0.2
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 +7 -0
- data/lib/unitwise/expression.rb +2 -20
- data/lib/unitwise/expression/decomposer.rb +24 -3
- data/lib/unitwise/expression/matcher.rb +10 -6
- data/lib/unitwise/ext/numeric.rb +7 -5
- data/lib/unitwise/scale.rb +1 -1
- data/lib/unitwise/term.rb +19 -14
- data/lib/unitwise/unit.rb +29 -24
- data/lib/unitwise/version.rb +1 -1
- data/test/unitwise/term_test.rb +6 -0
- data/test/unitwise/unit_test.rb +1 -1
- metadata +2 -3
- data/simple_bench.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 410fb733b657a34122237793d03bf634fa0676b2
|
4
|
+
data.tar.gz: 57ab5cfe90d3bfb9dea058ea3b85904b3bb451dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0171c1aef8b9daf3e07a89959bbde2ee947854c51b6f7be0c1d569e140b0e9f24e95ec9a455babcb489d105c1656928cb207180b935fc02b329c330c9e3a41b8
|
7
|
+
data.tar.gz: 389f2c83e8505e1e7e6df4b8a9aeb6ae79869e2693dd3bb9d9476667ed440823f32faa63403c78a234a7fbdb83fd46d7e48072bed52c1946615bd408ef13bf6d
|
data/CHANGELOG.md
CHANGED
data/lib/unitwise/expression.rb
CHANGED
@@ -19,8 +19,7 @@ module Unitwise
|
|
19
19
|
Composer.new(terms, method).expression
|
20
20
|
end
|
21
21
|
|
22
|
-
# Convert a string representation of a unit
|
23
|
-
# an array of terms
|
22
|
+
# Convert a string representation of a unit into an array of terms
|
24
23
|
# @param expression [String] The string you wish to convert
|
25
24
|
# @return [Array]
|
26
25
|
# @example
|
@@ -28,26 +27,9 @@ module Unitwise
|
|
28
27
|
# # => [<Unitwise::Term m2>, <Unitwise::Term s-2>]
|
29
28
|
# @api public
|
30
29
|
def decompose(expression)
|
31
|
-
expression
|
32
|
-
if decomposed.key?(expression)
|
33
|
-
decomposed[expression]
|
34
|
-
else
|
35
|
-
decomposed[expression] = begin
|
36
|
-
Decomposer.new(expression).terms
|
37
|
-
rescue ExpressionError
|
38
|
-
nil
|
39
|
-
end
|
40
|
-
end
|
30
|
+
Decomposer.parse(expression)
|
41
31
|
end
|
42
32
|
|
43
|
-
private
|
44
|
-
|
45
|
-
# A cache of decomposed strings.
|
46
|
-
# @return [Hash]
|
47
|
-
# @api private
|
48
|
-
def decomposed
|
49
|
-
@decomposed ||= {}
|
50
|
-
end
|
51
33
|
end
|
52
34
|
end
|
53
35
|
end
|
@@ -13,17 +13,38 @@ module Unitwise
|
|
13
13
|
|
14
14
|
TRANSFORMER = Transformer.new
|
15
15
|
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Parse an expression to an array of terms and cache the results
|
19
|
+
def parse(expression)
|
20
|
+
expression = expression.to_s
|
21
|
+
if cache.key?(expression)
|
22
|
+
cache[expression]
|
23
|
+
elsif decomposer = new(expression)
|
24
|
+
cache[expression] = decomposer
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# A simple cache to prevent re-decomposing the same units
|
31
|
+
# api private
|
32
|
+
def cache
|
33
|
+
@cache ||= {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
16
37
|
attr_reader :expression, :mode
|
17
38
|
|
18
39
|
def initialize(expression)
|
19
40
|
@expression = expression.to_s
|
20
41
|
if terms.nil? || terms.empty?
|
21
|
-
fail
|
42
|
+
fail(ExpressionError, "Could not evaluate '#{ expression }'.")
|
22
43
|
end
|
23
44
|
end
|
24
45
|
|
25
46
|
def parse
|
26
|
-
|
47
|
+
PARSERS.reduce(nil) do |_, (mode, parser)|
|
27
48
|
parsed = parser.parse(expression) rescue next
|
28
49
|
@mode = mode
|
29
50
|
break parsed
|
@@ -41,7 +62,7 @@ module Unitwise
|
|
41
62
|
Array(transform)
|
42
63
|
end
|
43
64
|
end
|
44
|
-
|
65
|
+
|
45
66
|
end
|
46
67
|
end
|
47
68
|
end
|
@@ -5,15 +5,19 @@ module Unitwise
|
|
5
5
|
class Matcher
|
6
6
|
class << self
|
7
7
|
def atom(mode)
|
8
|
-
|
8
|
+
@atom ||= {}
|
9
|
+
@atom[mode] ||= new(Atom.all, mode).alternative
|
9
10
|
end
|
10
11
|
|
11
12
|
def metric_atom(mode)
|
12
|
-
|
13
|
+
@metric_atom ||= {}
|
14
|
+
@metric_atom[mode] ||=
|
15
|
+
new(Atom.all.select(&:metric?), mode).alternative
|
13
16
|
end
|
14
17
|
|
15
18
|
def prefix(mode)
|
16
|
-
|
19
|
+
@prefix ||= {}
|
20
|
+
@prefix[mode] ||= new(Prefix.all, mode).alternative
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
@@ -25,17 +29,17 @@ module Unitwise
|
|
25
29
|
end
|
26
30
|
|
27
31
|
def strings
|
28
|
-
|
32
|
+
collection.map(&mode).flatten.compact.sort do |x, y|
|
29
33
|
y.length <=> x.length
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
37
|
def matchers
|
34
|
-
|
38
|
+
strings.map { |s| Parslet::Atoms::Str.new(s) }
|
35
39
|
end
|
36
40
|
|
37
41
|
def alternative
|
38
|
-
|
42
|
+
Parslet::Atoms::Alternative.new(*matchers)
|
39
43
|
end
|
40
44
|
|
41
45
|
end
|
data/lib/unitwise/ext/numeric.rb
CHANGED
@@ -20,13 +20,15 @@ class Numeric
|
|
20
20
|
def method_missing(meth, *args, &block)
|
21
21
|
if args.empty? && !block_given?
|
22
22
|
unit = (match = /\Ato_(\w+)\Z/.match(meth.to_s)) ? match[1] : meth
|
23
|
-
begin
|
24
|
-
|
25
|
-
Numeric.define_unit_conversion_methods_for(unit)
|
26
|
-
res
|
23
|
+
converted = begin
|
24
|
+
convert_to(unit)
|
27
25
|
rescue Unitwise::ExpressionError
|
28
|
-
|
26
|
+
nil
|
29
27
|
end
|
28
|
+
end
|
29
|
+
if converted
|
30
|
+
Numeric.define_unit_conversion_methods_for(unit)
|
31
|
+
converted
|
30
32
|
else
|
31
33
|
super(meth, *args, &block)
|
32
34
|
end
|
data/lib/unitwise/scale.rb
CHANGED
data/lib/unitwise/term.rb
CHANGED
@@ -89,28 +89,19 @@ module Unitwise
|
|
89
89
|
# params other [Unit, Term, Numeric]
|
90
90
|
# @return [Term]
|
91
91
|
def *(other)
|
92
|
-
|
93
|
-
|
94
|
-
elsif other.respond_to?(:atom)
|
95
|
-
Unit.new([self, other])
|
96
|
-
elsif other.is_a?(Numeric)
|
97
|
-
self.class.new(to_hash.merge(:factor => factor * other))
|
98
|
-
end
|
92
|
+
operate('*', other) ||
|
93
|
+
fail(TypeError, "Can't multiply #{ self } by #{ other }.")
|
99
94
|
end
|
100
95
|
|
101
96
|
# Term division. Divide by a Unit, another Term, or a Numeric.
|
102
97
|
# params other [Unit, Term, Numeric]
|
103
98
|
# @return [Term]
|
104
99
|
def /(other)
|
105
|
-
|
106
|
-
|
107
|
-
elsif other.respond_to?(:atom)
|
108
|
-
Unit.new([self, other ** -1])
|
109
|
-
elsif other.is_a?(Numeric)
|
110
|
-
self.class.new(to_hash.merge(:factor => factor / other))
|
111
|
-
end
|
100
|
+
operate('/', other) ||
|
101
|
+
fail(TypeError, "Can't divide #{ self } by #{ other }.")
|
112
102
|
end
|
113
103
|
|
104
|
+
|
114
105
|
# Term exponentiation. Raise a term to a numeric power.
|
115
106
|
# params other [Numeric]
|
116
107
|
# @return [Term]
|
@@ -133,5 +124,19 @@ module Unitwise
|
|
133
124
|
def calculate(value)
|
134
125
|
(factor * (prefix ? prefix.scalar : 1) * value) ** exponent
|
135
126
|
end
|
127
|
+
|
128
|
+
# Multiply or divide a term
|
129
|
+
# @api private
|
130
|
+
def operate(operator, other)
|
131
|
+
exp = operator == '/' ? -1 : 1
|
132
|
+
if other.respond_to?(:terms)
|
133
|
+
Unit.new(other.terms.map { |t| t ** exp } << self)
|
134
|
+
elsif other.respond_to?(:atom)
|
135
|
+
Unit.new([self, other ** exp])
|
136
|
+
elsif other.is_a?(Numeric)
|
137
|
+
self.class.new(to_hash.merge(:factor => factor.send(operator, other)))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
136
141
|
end
|
137
142
|
end
|
data/lib/unitwise/unit.rb
CHANGED
@@ -27,7 +27,7 @@ module Unitwise
|
|
27
27
|
def terms
|
28
28
|
unless frozen?
|
29
29
|
unless @terms
|
30
|
-
decomposer = Expression
|
30
|
+
decomposer = Expression.decompose(@expression)
|
31
31
|
@mode = decomposer.mode
|
32
32
|
@terms = decomposer.terms
|
33
33
|
end
|
@@ -41,11 +41,14 @@ module Unitwise
|
|
41
41
|
# (:primary_code, :names, :secondary_code).
|
42
42
|
# @return [String]
|
43
43
|
# @api public
|
44
|
-
def expression(mode
|
45
|
-
|
44
|
+
def expression(mode=nil)
|
45
|
+
if @expression && (mode.nil? || mode == self.mode)
|
46
|
+
@expression
|
47
|
+
else
|
48
|
+
Expression.compose(terms, mode || self.mode)
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
|
-
|
49
52
|
# The collection of atoms that compose this unit. Essentially delegated to
|
50
53
|
# terms.
|
51
54
|
# @return [Array]
|
@@ -105,15 +108,8 @@ module Unitwise
|
|
105
108
|
# @return [Unitwise::Unit]
|
106
109
|
# @api public
|
107
110
|
def *(other)
|
108
|
-
|
109
|
-
|
110
|
-
elsif other.respond_to?(:atom)
|
111
|
-
self.class.new(terms << other)
|
112
|
-
elsif other.is_a?(Numeric)
|
113
|
-
self.class.new(terms.map { |t| t * other })
|
114
|
-
else
|
115
|
-
fail TypeError, "Can't multiply #{self} by #{other}."
|
116
|
-
end
|
111
|
+
operate('*', other) ||
|
112
|
+
fail(TypeError, "Can't multiply #{ self } by #{ other }.")
|
117
113
|
end
|
118
114
|
|
119
115
|
# Divide this unit by another unit,term, or number.
|
@@ -121,17 +117,11 @@ module Unitwise
|
|
121
117
|
# @return [Unitwise::Unit]
|
122
118
|
# @api public
|
123
119
|
def /(other)
|
124
|
-
|
125
|
-
|
126
|
-
elsif other.respond_to?(:atom)
|
127
|
-
self.class.new(terms << other ** -1)
|
128
|
-
elsif other.is_a?(Numeric)
|
129
|
-
self.class.new(terms.map { |t| t / other })
|
130
|
-
else
|
131
|
-
fail TypeError, "Can't divide #{self} by #{other}."
|
132
|
-
end
|
120
|
+
operate('/', other) ||
|
121
|
+
fail(TypeError, "Can't divide #{ self } by #{ other }.")
|
133
122
|
end
|
134
123
|
|
124
|
+
|
135
125
|
# Raise this unit to a numeric power.
|
136
126
|
# @param other [Numeric]
|
137
127
|
# @return [Unitwise::Unit]
|
@@ -149,8 +139,8 @@ module Unitwise
|
|
149
139
|
# (:primary_code, :names, :secondary_code)
|
150
140
|
# @return [String]
|
151
141
|
# @api public
|
152
|
-
def to_s(mode =
|
153
|
-
expression(mode
|
142
|
+
def to_s(mode = nil)
|
143
|
+
expression(mode)
|
154
144
|
end
|
155
145
|
|
156
146
|
# A collection of the possible string representations of this unit.
|
@@ -172,5 +162,20 @@ module Unitwise
|
|
172
162
|
@mode || :primary_code
|
173
163
|
end
|
174
164
|
|
165
|
+
private
|
166
|
+
|
167
|
+
# Multiply or divide units
|
168
|
+
# @api private
|
169
|
+
def operate(operator, other)
|
170
|
+
exp = operator == '/' ? -1 : 1
|
171
|
+
if other.respond_to?(:terms)
|
172
|
+
self.class.new(terms + other.terms.map { |t| t ** exp })
|
173
|
+
elsif other.respond_to?(:atom)
|
174
|
+
self.class.new(terms << other ** exp)
|
175
|
+
elsif other.is_a?(Numeric)
|
176
|
+
self.class.new(terms.map { |t| t.send(operator, other) })
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
175
180
|
end
|
176
181
|
end
|
data/lib/unitwise/version.rb
CHANGED
data/test/unitwise/term_test.rb
CHANGED
data/test/unitwise/unit_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unitwise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
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-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liner
|
@@ -183,7 +183,6 @@ files:
|
|
183
183
|
- lib/unitwise/term.rb
|
184
184
|
- lib/unitwise/unit.rb
|
185
185
|
- lib/unitwise/version.rb
|
186
|
-
- simple_bench.rb
|
187
186
|
- test/support/scale_tests.rb
|
188
187
|
- test/test_helper.rb
|
189
188
|
- test/unitwise/atom_test.rb
|
data/simple_bench.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'unitwise'
|
2
|
-
require 'ruby-units'
|
3
|
-
require 'benchmark'
|
4
|
-
|
5
|
-
Benchmark.bm(10) do |x|
|
6
|
-
|
7
|
-
x.report('unitwise') do
|
8
|
-
100000.times do |x|
|
9
|
-
expression = "mm#{x % 20}"
|
10
|
-
Unitwise::Measurement.new(x, expression)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
x.report('ruby-units') do
|
15
|
-
100000.times do |x|
|
16
|
-
expression = "mm^#{x % 20}"
|
17
|
-
Unit(x, expression)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|