auom 0.0.3

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.
Files changed (54) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +13 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.devtools +52 -0
  6. data/Guardfile +8 -0
  7. data/LICENSE +20 -0
  8. data/README.md +92 -0
  9. data/Rakefile +5 -0
  10. data/TODO +3 -0
  11. data/auom.gemspec +21 -0
  12. data/config/flay.yml +3 -0
  13. data/config/flog.yml +2 -0
  14. data/config/heckle.yml +9 -0
  15. data/config/roodi.yml +20 -0
  16. data/config/site.reek +95 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/auom.rb +287 -0
  19. data/lib/auom/algebra.rb +129 -0
  20. data/lib/auom/equalization.rb +90 -0
  21. data/lib/auom/inspection.rb +103 -0
  22. data/lib/auom/version.rb +3 -0
  23. data/spec/rcov.opts +7 -0
  24. data/spec/shared/command_method_behavior.rb +7 -0
  25. data/spec/shared/each_method_behaviour.rb +15 -0
  26. data/spec/shared/hash_method_behavior.rb +17 -0
  27. data/spec/shared/idempotent_method_behavior.rb +7 -0
  28. data/spec/shared/incompatible_operation_behavior.rb +5 -0
  29. data/spec/shared/invertible_method_behaviour.rb +9 -0
  30. data/spec/shared/operation_behavior.rb +12 -0
  31. data/spec/shared/sunits_shared.rb +6 -0
  32. data/spec/spec.opts +3 -0
  33. data/spec/spec_helper.rb +13 -0
  34. data/spec/unit/auom/add_spec.rb +0 -0
  35. data/spec/unit/auom/algebra/add_spec.rb +59 -0
  36. data/spec/unit/auom/algebra/divide_spec.rb +74 -0
  37. data/spec/unit/auom/algebra/multiply_spec.rb +74 -0
  38. data/spec/unit/auom/algebra/substract_spec.rb +59 -0
  39. data/spec/unit/auom/equalization/eql_spec.rb +40 -0
  40. data/spec/unit/auom/equalization/equal_value_spec.rb +57 -0
  41. data/spec/unit/auom/equalization/hash_spec.rb +13 -0
  42. data/spec/unit/auom/inspection/class_methods/prettify_unit_part_spec.rb +27 -0
  43. data/spec/unit/auom/inspection/inspect_spec.rb +41 -0
  44. data/spec/unit/auom/unit/class_methods/convert_spec.rb +35 -0
  45. data/spec/unit/auom/unit/class_methods/lookup_spec.rb +21 -0
  46. data/spec/unit/auom/unit/class_methods/new_spec.rb +156 -0
  47. data/spec/unit/auom/unit/class_methods/try_convert_spec.rb +31 -0
  48. data/spec/unit/auom/unit/class_methods/units_spec.rb +11 -0
  49. data/spec/unit/auom/unit/denominators_spec.rb +16 -0
  50. data/spec/unit/auom/unit/numerators_spec.rb +16 -0
  51. data/spec/unit/auom/unit/scalar_spec.rb +16 -0
  52. data/spec/unit/auom/unit/unit_spec.rb +16 -0
  53. data/spec/unit/auom/unit/unitless_spec.rb +18 -0
  54. metadata +164 -0
@@ -0,0 +1,3 @@
1
+ /Gemfile.lock
2
+ /tmp
3
+ /coverage
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ script: 'bundle exec rake spec'
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ # - jruby-18mode Disabled for core dump on travis http://travis-ci.org/#!/mbj/veritas-elasticsearch-adapter/jobs/1769741
8
+ # - jruby-19mode Disabled for core dump on travis http://travis-ci.org/#!/mbj/veritas-elasticsearch-adapter/jobs/1768119
9
+ - rbx-18mode
10
+ - rbx-19mode
11
+ notifications:
12
+ email:
13
+ - mbj@seonic.net
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'devtools', :git => 'https://github.com/mbj/devtools'
9
+ end
10
+ eval File.read('Gemfile.devtools')
@@ -0,0 +1,52 @@
1
+ group :development do
2
+ gem 'rake', '~> 0.9.2'
3
+ gem 'rspec', '~> 1.3.2'
4
+ gem 'yard', '~> 0.8.3'
5
+ end
6
+
7
+ group :guard do
8
+ gem 'guard', '~> 1.1.1'
9
+ gem 'guard-bundler', '~> 0.1.3'
10
+ gem 'guard-rspec', '~> 0.7.3'
11
+ gem 'rb-inotify', :git => 'https://github.com/mbj/rb-inotify'
12
+ end
13
+
14
+ group :benchmarks do
15
+ gem 'rbench', '~> 0.2.3'
16
+ end
17
+
18
+ platform :jruby do
19
+ group :jruby do
20
+ gem 'jruby-openssl', '~> 0.7.4'
21
+ end
22
+ end
23
+
24
+ group :metrics do
25
+ gem 'flay', '~> 1.4.2'
26
+ gem 'flog', '~> 2.5.1'
27
+ gem 'reek', '~> 1.2.8', :git => 'https://github.com/dkubb/reek.git'
28
+ gem 'roodi', '~> 2.1.0'
29
+ gem 'yardstick', '~> 0.7.0'
30
+ gem 'simplecov'
31
+
32
+ platforms :ruby_18, :ruby_19 do
33
+ # this indirectly depends on ffi which does not build on ruby-head
34
+ gem 'yard-spellcheck', '~> 0.1.5'
35
+ end
36
+
37
+ platforms :mri_18 do
38
+ gem 'arrayfields', '~> 4.7.4' # for metric_fu
39
+ gem 'fattr', '~> 2.2.0' # for metric_fu
40
+ gem 'heckle', '~> 1.4.3'
41
+ gem 'json', '~> 1.7.3' # for metric_fu rake task
42
+ gem 'map', '~> 6.0.1' # for metric_fu
43
+ gem 'metric_fu', '~> 2.1.1'
44
+ gem 'mspec', '~> 1.5.17'
45
+ gem 'rcov', '~> 1.0.0'
46
+ gem 'ruby2ruby', '= 1.2.2' # for heckle
47
+ end
48
+
49
+ platforms :rbx do
50
+ gem 'pelusa', '~> 0.2.1'
51
+ end
52
+ end
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 1 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Markus Schirp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,92 @@
1
+ AUOM Algebra (for) Units of Measuerment
2
+ =======================================
3
+
4
+ [![Build Status](https://secure.travis-ci.org/mbj/auom.png?branch=master)](http://travis-ci.org/mbj/auom)
5
+ [![Dependency Status](https://gemnasium.com/mbj/auom.png)](https://gemnasium.com/mbj/auom)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mbj/auom)
7
+
8
+ This is another unit system for ruby.
9
+ It was created since I was not confident about the existing once.
10
+
11
+ Features:
12
+
13
+ * No unit conversions
14
+ * No core patches (Especially does not require mathn!)
15
+ * Dependency free.
16
+ * Functional implementation style.
17
+ * No magic coercion from number like strings to numbers.
18
+ * Will never loose precision (Uses rational as scalar internally)
19
+ * Allows namespacing of unit systems via subclass.
20
+ * Well tested (100% heckle coverage)
21
+
22
+ The default set of predefined units is miminal as this library should be used in an application
23
+ specific subclass. Override: ```AUOR::Unit.units```
24
+
25
+ Installation
26
+ ------------
27
+
28
+ In your **Gemfile**
29
+
30
+ ``` ruby
31
+ gem 'auom', :git => 'https://github.com/mbj/auom'
32
+ ```
33
+
34
+ Examples
35
+ --------
36
+
37
+ ``` ruby
38
+
39
+ # Create a unit
40
+ require 'auom'
41
+
42
+ include AUOM
43
+
44
+ u = Unit.new(1, :meter) # <AUOM::Unit @scalar=1 meter>
45
+ u * 100 # <AUOM::Unit @scalar=100 meter>
46
+ u / Unit.new(10,:meter) # <AUOM::Unit @scalar=0.1>
47
+ u * Unit.new(10,:meter) # <AUOM::Unit @scalar=10 meter^2>
48
+ u * Unit.new(1, :euro) # <AUOM::Unit @scalar=1 euro*meter>
49
+ u - Unit.new(1, :meter) # <AUOM::Unit @scalar=0 meter>
50
+ u + Unit.new(1, :meter) # <AUOM::Unit @scalar=2 meter>
51
+ u + Unit.new(1, :euro) # raises error about incompatible units
52
+ ```
53
+
54
+ Credits
55
+ -------
56
+
57
+ Room for your name!
58
+
59
+ Contributing
60
+ -------------
61
+
62
+ * Fork the project.
63
+ * Make your feature addition or bug fix.
64
+ * Add tests for it. This is important so I don't break it in a
65
+ future version unintentionally.
66
+ * Commit, do not mess with Rakefile or version
67
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
68
+ * Send me a pull request. Bonus points for topic branches.
69
+
70
+ License
71
+ -------
72
+
73
+ Copyright (c) 2012 Markus Schirp
74
+
75
+ Permission is hereby granted, free of charge, to any person obtaining
76
+ a copy of this software and associated documentation files (the
77
+ "Software"), to deal in the Software without restriction, including
78
+ without limitation the rights to use, copy, modify, merge, publish,
79
+ distribute, sublicense, and/or sell copies of the Software, and to
80
+ permit persons to whom the Software is furnished to do so, subject to
81
+ the following conditions:
82
+
83
+ The above copyright notice and this permission notice shall be
84
+ included in all copies or substantial portions of the Software.
85
+
86
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
88
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
89
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
90
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
91
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
92
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ require 'devtools'
4
+
5
+ Devtools.init
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Remove more custom typecasting logic? Can we coerce to SUnit::Unit?
2
+ * Use code metrics
3
+ * Make buildin units configurable
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/auom/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'auom'
6
+ s.version = AUOM::VERSION.dup
7
+
8
+ s.authors = ['Markus Schirp']
9
+ s.email = 'mbj@seonic.net'
10
+ s.summary = 'Algebra (with) Units Of Measurement'
11
+ s.homepage = 'http://github.com/mbj/auom'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.require_paths = %w(lib)
16
+ s.extra_rdoc_files = %w(TODO LICENSE)
17
+
18
+ s.rubygems_version = '1.8.10'
19
+
20
+ s.add_runtime_dependency('backports', '~> 2.6.3')
21
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ threshold: 12.0
3
+ total_score: 68.0
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 24.6
@@ -0,0 +1,9 @@
1
+ ---
2
+ library: auom
3
+ namespace: AUOM
4
+ aliases:
5
+ AUOM::Algebra:
6
+ '-': substract
7
+ '+': add
8
+ '*': multiply
9
+ '/': divide
@@ -0,0 +1,20 @@
1
+ ---
2
+ AbcMetricMethodCheck:
3
+ score: 25.1
4
+ AssignmentInConditionalCheck: { }
5
+ CaseMissingElseCheck: { }
6
+ ClassLineCountCheck: { line_count: 274 }
7
+ ClassNameCheck:
8
+ pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/
9
+ ClassVariableCheck: { }
10
+ CyclomaticComplexityBlockCheck: { complexity: 3 }
11
+ CyclomaticComplexityMethodCheck: { complexity: 4 }
12
+ EmptyRescueBodyCheck: { }
13
+ ForLoopCheck: { }
14
+ MethodLineCountCheck: { line_count: 14 }
15
+ MethodNameCheck:
16
+ pattern: !ruby/regexp /\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|<<|[+*&|-])\z/
17
+ ModuleLineCountCheck: { line_count: 277 }
18
+ ModuleNameCheck:
19
+ pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/
20
+ ParameterNumberCheck: { parameter_count: 3 }
@@ -0,0 +1,95 @@
1
+ UncommunicativeParameterName:
2
+ accept: []
3
+ exclude: []
4
+ enabled: true
5
+ reject:
6
+ - !ruby/regexp /^.$/
7
+ - !ruby/regexp /[0-9]$/
8
+ - !ruby/regexp /[A-Z]/
9
+ LargeClass:
10
+ max_methods: 14
11
+ enabled: true
12
+ exclude: []
13
+ max_instance_variables: 4
14
+ UncommunicativeMethodName:
15
+ accept: []
16
+ exclude: []
17
+ enabled: true
18
+ reject:
19
+ - !ruby/regexp /^[a-z]$/
20
+ - !ruby/regexp /[0-9]$/
21
+ - !ruby/regexp /[A-Z]/
22
+ LongParameterList:
23
+ max_params: 2
24
+ exclude:
25
+ - "AUOM::Unit#initialize" # 3 params
26
+ - "AUOM::Unit#self.new" # 3 params
27
+ - "AUOM::Unit#self.resolve" # 3 params
28
+ enabled: true
29
+ overrides: {}
30
+ FeatureEnvy:
31
+ exclude: []
32
+ enabled: true
33
+ ClassVariable:
34
+ exclude: []
35
+ enabled: true
36
+ BooleanParameter:
37
+ exclude: []
38
+ enabled: true
39
+ IrresponsibleModule:
40
+ exclude: []
41
+ enabled: true
42
+ UncommunicativeModuleName:
43
+ accept: []
44
+ exclude: []
45
+ enabled: true
46
+ reject:
47
+ - !ruby/regexp /^.$/
48
+ - !ruby/regexp /[0-9]$/
49
+ NestedIterators:
50
+ ignore_iterators: []
51
+ exclude:
52
+ - AUOM::Unit#initialize # 2 levels
53
+ enabled: true
54
+ max_allowed_nesting: 1
55
+ LongMethod:
56
+ max_statements: 5
57
+ exclude:
58
+ - "AUOM::Inspection#pretty_unit" # 7 statements
59
+ enabled: true
60
+ Duplication:
61
+ allow_calls: []
62
+ exclude:
63
+ - "AUOM::ClassMethods#new" # invalid detection
64
+ enabled: true
65
+ max_calls: 1
66
+ UtilityFunction:
67
+ max_helper_calls: 0
68
+ exclude: []
69
+ enabled: true
70
+ Attribute:
71
+ exclude: []
72
+ enabled: false
73
+ UncommunicativeVariableName:
74
+ accept: []
75
+ exclude: []
76
+ enabled: true
77
+ reject:
78
+ - !ruby/regexp /^.$/
79
+ - !ruby/regexp /[0-9]$/
80
+ - !ruby/regexp /[A-Z]/
81
+ SimulatedPolymorphism:
82
+ enabled: true
83
+ max_ifs: 1
84
+ DataClump:
85
+ exclude: []
86
+ enabled: true
87
+ max_copies: 0
88
+ min_clump_size: 2
89
+ ControlCouple:
90
+ exclude: []
91
+ enabled: true
92
+ LongYieldList:
93
+ max_params: 0
94
+ exclude: []
95
+ enabled: true
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 100
@@ -0,0 +1,287 @@
1
+ require 'rational'
2
+ require 'backports'
3
+
4
+ require 'auom/algebra'
5
+ require 'auom/equalization'
6
+ require 'auom/inspection'
7
+
8
+ # Library namespace
9
+ module AUOM
10
+ # A scalar with units
11
+ class Unit
12
+ include Algebra
13
+ include Equalization
14
+ include Inspection
15
+
16
+ # Return scalar
17
+ #
18
+ # @example
19
+ #
20
+ # include AUOM
21
+ # m = Unit.new(1, :meter)
22
+ # m.scalar # => Rational(1, 1)
23
+ #
24
+ # @return [Rational]
25
+ #
26
+ # @api public
27
+ #
28
+ attr_reader :scalar
29
+
30
+ # Return numerators
31
+ #
32
+ # @example
33
+ #
34
+ # include AUOM
35
+ # m = Unit.new(1, :meter)
36
+ # m.numerators # => [:meter]
37
+ #
38
+ # @return [Rational]
39
+ #
40
+ # @api public
41
+ #
42
+ attr_reader :numerators
43
+
44
+ # Return denominators
45
+ #
46
+ # @example
47
+ #
48
+ # include AUOM
49
+ # m = Unit.new(1, :meter)
50
+ # m.denoninators # => []
51
+ #
52
+ # @return [Rational]
53
+ #
54
+ # @api public
55
+ #
56
+ attr_reader :denominators
57
+
58
+ # Return unit descriptor
59
+ #
60
+ # @return [Array]
61
+ #
62
+ # @api public
63
+ #
64
+ # @example
65
+ #
66
+ # u = Unit.new(1, [:meter, :meter], :euro)
67
+ # u.unit # => [[:meter, :meter], [:euro]]
68
+ #
69
+ attr_reader :unit
70
+
71
+ # These constants can easily be changed
72
+ # by an application specific subclass that overrides
73
+ # AUOM::Unit.units with an own hash!
74
+ UNITS = {
75
+ :item => [1, :item],
76
+ :liter => [1, :liter],
77
+ :pack => [1, :pack],
78
+ :can => [1, :can],
79
+ :kilogramm => [1, :kilogramm],
80
+ :euro => [1, :euro],
81
+ :meter => [1, :meter],
82
+ :kilometer => [1000, :meter]
83
+ }.freeze
84
+
85
+ # Return buildin units symbols
86
+ #
87
+ # @return [Hash]
88
+ #
89
+ # @api private
90
+ #
91
+ def self.units
92
+ UNITS
93
+ end
94
+
95
+ # Check for unitless unit
96
+ #
97
+ # @return [true]
98
+ # return true if unit is unitless
99
+ #
100
+ # @return [false]
101
+ # return false if unit is NOT unitless
102
+ #
103
+ # @example
104
+ #
105
+ # Unit.new(1).unitless? # => true
106
+ # Unit.new(1, :meter).unitless ? # => false
107
+ #
108
+ # @api public
109
+ #
110
+ def unitless?
111
+ numerators.empty? and denominators.empty?
112
+ end
113
+
114
+ # Instancitate a new unit
115
+ #
116
+ # @param [Rational] scalar
117
+ # @param [Enumerable] numerators
118
+ # @param [Enumerable] denominators
119
+ #
120
+ # @return [Unit]
121
+ #
122
+ # @example
123
+ #
124
+ # # A unitless unit
125
+ # u = Unit.new(1)
126
+ # u.unitless? # => true
127
+ # u.scalar # => Rational(1, 1)
128
+ #
129
+ # # A unitless unit from string
130
+ # u = Unit.new('1.5')
131
+ # u.unitless? # => true
132
+ # u.scalar # => Rational(3, 2)
133
+ #
134
+ # # A simple unit
135
+ # u = Unit.new(1, :meter)
136
+ # u.unitless? # => false
137
+ # u.numerators # => [:meter]
138
+ # u.scalar # => Rational(1, 1)
139
+ #
140
+ # # A complex unit
141
+ # u = Unit.new(Rational(1, 3), :euro, :meter)
142
+ # u.fractions? # => true
143
+ # u.sclar # => Rational(1, 3)
144
+ # u.inspect # => <AUOM::Unit @scalar=~0.3333 euro/meter>
145
+ # u.unit # => [[:euro], [:meter]]
146
+ #
147
+ # @api public
148
+ #
149
+ def self.new(scalar, numerators=[], denominators=[])
150
+ scalar = rational(scalar)
151
+
152
+ scalar, numerators = resolve([*numerators], scalar, :*)
153
+ scalar, denominators = resolve([*denominators], scalar, :/)
154
+
155
+ # sorting on #to_s as Symbol#<=> is not present on 1.8.7
156
+ super(scalar, *[numerators, denominators].map { |base| base.sort_by(&:to_s) }).freeze
157
+ end
158
+
159
+ private
160
+
161
+ # Initialize unit
162
+ #
163
+ # @param [Rational] scalar
164
+ # @param [Enumerable] numerators
165
+ # @param [Enumerable] denominators
166
+ #
167
+ # @api private
168
+ #
169
+ def initialize(scalar, numerators, denominators)
170
+ @scalar = scalar
171
+
172
+ [numerators, denominators].permutation do |left, right|
173
+ left.delete_if { |item| right.delete_at(right.index(item) || right.length) }
174
+ end
175
+
176
+ @numerators = numerators.freeze
177
+ @denominators = denominators.freeze
178
+
179
+ @unit = [@numerators, @denominators].freeze
180
+ @scalar.freeze
181
+ end
182
+
183
+
184
+ # Return converted operand or raise error
185
+ #
186
+ # @param [Object] operand
187
+ #
188
+ # @return [Unit]
189
+ #
190
+ # @raise [ArgumentError]
191
+ # raises argument error in case operand cannot be converted
192
+ #
193
+ # @api private
194
+ #
195
+ def self.convert(operand)
196
+ converted = try_convert(operand)
197
+ unless converted
198
+ raise ArgumentError, "Cannot convert #{operand.inspect} to #{self}"
199
+ end
200
+ converted
201
+ end
202
+
203
+ # Return converted operand or nil
204
+ #
205
+ # @param [Object] operand
206
+ #
207
+ # @return [Unit]
208
+ # return unit in case operand can be converted
209
+ #
210
+ # @return [nil]
211
+ # return nil in case operand can NOT be converted
212
+ #
213
+ # @api private
214
+ #
215
+ def self.try_convert(operand)
216
+ case operand
217
+ when self
218
+ operand
219
+ when Fixnum, Rational
220
+ self.new(operand)
221
+ else
222
+ nil
223
+ end
224
+ end
225
+
226
+ # Return rational converted from value
227
+ #
228
+ # @param [Object] value
229
+ #
230
+ # @return [Rationa]
231
+ #
232
+ # @raise [ArgumentError]
233
+ # raises argument error when cannot be converted to a rational
234
+ #
235
+ # @api private
236
+ #
237
+ def self.rational(value)
238
+ case value
239
+ when Rational
240
+ value
241
+ when Fixnum
242
+ Rational(value, 1)
243
+ else
244
+ raise ArgumentError, "#{value.inspect} cannot be converted to rational"
245
+ end
246
+ end
247
+
248
+ private_class_method :rational
249
+
250
+ # Resolve unit component
251
+ #
252
+ # @param [Enumerable] components
253
+ # @param [Symbol] operation
254
+ #
255
+ # @return [Array]
256
+ #
257
+ # @api private
258
+ #
259
+ def self.resolve(components, scalar, operation)
260
+ resolved = components.map do |component|
261
+ scale, component = lookup(component)
262
+ scalar = scalar.send(operation, scale)
263
+ component
264
+ end
265
+ [scalar, resolved]
266
+ end
267
+
268
+ private_class_method :resolve
269
+
270
+ # Return unit information
271
+ #
272
+ # @param [Symbol] value
273
+ # the unit to search for
274
+ #
275
+ # @return [Array]
276
+ #
277
+ # @api private
278
+ #
279
+ def self.lookup(value)
280
+ units.fetch(value) do
281
+ raise ArgumentError, "Unknown unit #{value.inspect}"
282
+ end
283
+ end
284
+
285
+ private_class_method :lookup
286
+ end
287
+ end