unitwise 1.0.1 → 1.0.2

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: 7de40334f99c4238cd5e8c492b52a2e6556f80fd
4
- data.tar.gz: 52c7bce118481d12d1955d7395b06068dc400c23
3
+ metadata.gz: 410fb733b657a34122237793d03bf634fa0676b2
4
+ data.tar.gz: 57ab5cfe90d3bfb9dea058ea3b85904b3bb451dd
5
5
  SHA512:
6
- metadata.gz: 9e3e7a34913889ff32d9d7f7af7e4f3dc3ef563295f0469653f5f46f84c1977cd022a888a2a31efbe6b33f2397dfa5eac1810781b061716f6c233acd3e9a91e3
7
- data.tar.gz: 4635700db7000da9b2a2958e1f0bdd2696bc8bdf698396cf90ce66da6750d173402c9ddb04162e2375766ddc287e5f1c7cda19e9bdf650e260b2ff35826e63c3
6
+ metadata.gz: 0171c1aef8b9daf3e07a89959bbde2ee947854c51b6f7be0c1d569e140b0e9f24e95ec9a455babcb489d105c1656928cb207180b935fc02b329c330c9e3a41b8
7
+ data.tar.gz: 389f2c83e8505e1e7e6df4b8a9aeb6ae79869e2693dd3bb9d9476667ed440823f32faa63403c78a234a7fbdb83fd46d7e48072bed52c1946615bd408ef13bf6d
@@ -5,6 +5,13 @@ version 1.0.0.
5
5
 
6
6
  Unitwise uses semantic versioning.
7
7
 
8
+ ## 1.0.2 - 2014-09-14
9
+
10
+ ## Fixed
11
+
12
+ - Decomposer caching is now a little smarter. This resulted in a mild
13
+ performance increase.
14
+
8
15
  ## 1.0.1 - 2014-08-30
9
16
 
10
17
  ### Fixed
@@ -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, and turn it into a
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 = expression.to_s
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 ExpressionError, "Could not evaluate '#{@expression}'."
42
+ fail(ExpressionError, "Could not evaluate '#{ expression }'.")
22
43
  end
23
44
  end
24
45
 
25
46
  def parse
26
- @parse ||= PARSERS.reduce(nil) do |_, (mode, parser)|
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
- new(Atom.all, mode).alternative
8
+ @atom ||= {}
9
+ @atom[mode] ||= new(Atom.all, mode).alternative
9
10
  end
10
11
 
11
12
  def metric_atom(mode)
12
- new(Atom.all.select(&:metric?), mode).alternative
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
- new(Prefix.all, mode).alternative
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
- @stings ||= collection.map(&mode).flatten.compact.sort do |x, y|
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
- @matchers ||= strings.map { |s| Parslet::Atoms::Str.new(s) }
38
+ strings.map { |s| Parslet::Atoms::Str.new(s) }
35
39
  end
36
40
 
37
41
  def alternative
38
- @alternative ||= Parslet::Atoms::Alternative.new(*matchers)
42
+ Parslet::Atoms::Alternative.new(*matchers)
39
43
  end
40
44
 
41
45
  end
@@ -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
- res = convert_to(unit)
25
- Numeric.define_unit_conversion_methods_for(unit)
26
- res
23
+ converted = begin
24
+ convert_to(unit)
27
25
  rescue Unitwise::ExpressionError
28
- super(meth, *args, &block)
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
@@ -81,7 +81,7 @@ module Unitwise
81
81
  end
82
82
  memoize :root_terms
83
83
 
84
- # How far away is this instances unit from the deepest leve atom.
84
+ # How far away is this instances unit from the deepest level atom.
85
85
  # @return [Integer]
86
86
  # @api public
87
87
  def depth
@@ -89,28 +89,19 @@ module Unitwise
89
89
  # params other [Unit, Term, Numeric]
90
90
  # @return [Term]
91
91
  def *(other)
92
- if other.respond_to?(:terms)
93
- Unit.new(other.terms << self)
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
- if other.respond_to?(:terms)
106
- Unit.new(other.terms.map { |t| t ** -1 } << self)
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
@@ -27,7 +27,7 @@ module Unitwise
27
27
  def terms
28
28
  unless frozen?
29
29
  unless @terms
30
- decomposer = Expression::Decomposer.new(@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 = mode)
45
- Expression.compose(terms, mode)
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
- if other.respond_to?(:terms)
109
- self.class.new(terms + other.terms)
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
- if other.respond_to?(:terms)
125
- self.class.new(terms + other.terms.map { |t| t ** -1 })
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 = mode)
153
- expression(mode || self.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
@@ -1,3 +1,3 @@
1
1
  module Unitwise
2
- VERSION = '1.0.1'
2
+ VERSION = '1.0.2'
3
3
  end
@@ -45,5 +45,11 @@ describe Unitwise::Term do
45
45
  subject.frozen?.must_equal true
46
46
  end
47
47
  end
48
+
49
+ describe "#to_s" do
50
+ it "should return the UCUM code" do
51
+ subject.to_s.must_equal "kJ"
52
+ end
53
+ end
48
54
  end
49
55
  end
@@ -66,7 +66,7 @@ describe Unitwise::Unit do
66
66
 
67
67
  describe "#frozen?" do
68
68
  it "should be frozen" do
69
- kg.to_s
69
+ kg.scalar
70
70
  kg.frozen?.must_equal true
71
71
  end
72
72
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unitwise
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
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-08-30 00:00:00.000000000 Z
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
@@ -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