auom 0.0.3

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