vanunits 1.5.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.
Files changed (56) hide show
  1. data/HISTORY +16 -0
  2. data/HOWITWORKS.rdoc +72 -0
  3. data/LICENSE +23 -0
  4. data/README.rdoc +68 -0
  5. data/lib/van/units.rb +116 -0
  6. data/lib/van/units/base.rb +991 -0
  7. data/lib/van/units/constants.rb +2 -0
  8. data/lib/van/units/constants/cgs.rb +122 -0
  9. data/lib/van/units/constants/math.rb +3 -0
  10. data/lib/van/units/constants/math/cgs.rb +125 -0
  11. data/lib/van/units/constants/math/mks.rb +126 -0
  12. data/lib/van/units/constants/math/natural.rb +33 -0
  13. data/lib/van/units/constants/mks.rb +122 -0
  14. data/lib/van/units/currency.rb +160 -0
  15. data/lib/van/units/data/binary/base.rb +4 -0
  16. data/lib/van/units/data/cex.rb +5 -0
  17. data/lib/van/units/data/currency-default.rb +5 -0
  18. data/lib/van/units/data/currency-standard.rb +2 -0
  19. data/lib/van/units/data/currency/base.rb +89 -0
  20. data/lib/van/units/data/iec.rb +5 -0
  21. data/lib/van/units/data/iec_binary/base.rb +6 -0
  22. data/lib/van/units/data/si.rb +8 -0
  23. data/lib/van/units/data/si/base.rb +11 -0
  24. data/lib/van/units/data/si/constants.rb +88 -0
  25. data/lib/van/units/data/si/derived.rb +33 -0
  26. data/lib/van/units/data/si/extra.rb +35 -0
  27. data/lib/van/units/data/si/misc.rb +10 -0
  28. data/lib/van/units/data/uk.rb +10 -0
  29. data/lib/van/units/data/uk/base.rb +25 -0
  30. data/lib/van/units/data/units-default.rb +12 -0
  31. data/lib/van/units/data/units-standard.rb +5 -0
  32. data/lib/van/units/data/us.rb +10 -0
  33. data/lib/van/units/data/us/base.rb +47 -0
  34. data/lib/van/units/data/xmethods.rb +5 -0
  35. data/lib/van/units/data/xmethods/cached.rb +84 -0
  36. data/lib/van/units/data/xmethods/mapping.rb +87 -0
  37. data/lib/van/units/loaders.rb +100 -0
  38. data/lib/van/units/units.rb +111 -0
  39. data/lib/van/units_currency.rb +12 -0
  40. data/meta/author +1 -0
  41. data/meta/collection +1 -0
  42. data/meta/contact +1 -0
  43. data/meta/created +1 -0
  44. data/meta/description +5 -0
  45. data/meta/homepage +1 -0
  46. data/meta/name +1 -0
  47. data/meta/summary +1 -0
  48. data/meta/title +1 -0
  49. data/meta/version +1 -0
  50. data/qed/conversion.rdoc +14 -0
  51. data/qed/equality.rdoc +150 -0
  52. data/qed/operations.rdoc +74 -0
  53. data/test/test_constants.rb +12 -0
  54. data/test/test_currency.rb +26 -0
  55. data/test/test_units.rb +205 -0
  56. metadata +123 -0
data/HISTORY ADDED
@@ -0,0 +1,16 @@
1
+ = Release History
2
+
3
+ == 1.0.0 / 2010-02=22
4
+
5
+ VanUnits is the units system orginally underpinning Stick.
6
+ Stick has implemented a new expirmental unit system, so prior
7
+ system is being spun-off as VanUnits.
8
+
9
+ The components include are the base unit system, the currency
10
+ syste (which needs a need online backend to function), and
11
+ a set of scientific constants utilizing the unit system.
12
+
13
+ Changes:
14
+
15
+ * Happy Brithday
16
+
@@ -0,0 +1,72 @@
1
+ = How Units.rb Works
2
+
3
+ == Basics
4
+
5
+ There are two types of units: base units, which are not expressed in
6
+ function of other units, and derived units which are expressed as a
7
+ function of other units. The base units are represented by the
8
+ Van::Units::BaseUnit class. Derived units are implemented by
9
+ Van::Units::Unit. Derived units are represented as the product of
10
+ powers of other units that can be base units or derived units. These
11
+ powers are kept in a Hash. BaseUnit is never exposed to the user; if
12
+ you need a base unit, is represented as a Unit that's the product of a
13
+ single BaseUnit to the power 1. To be able to work with units, it's
14
+ often necessary to normalize them in some way, i.e., express them in
15
+ function of base units only. This is done by the Unit#simplify method.
16
+ This normalization is not performed automatically; rather we have
17
+ chosen to have the user of units.rb initiate any conversion so he or
18
+ she can have more control over rounding errors and such. Units can be
19
+ multiplied, divided and exponentiated.
20
+
21
+ Units by itself are not very interesting unless they are combined with
22
+ some numeric value. This is what the Van::Units::Value class does.
23
+ It holds a Unit, and a value which can be integers, float,
24
+ BigDecimals, complex numbers, etc. Values can be multiplied, divided,
25
+ and added and subtracted if the units are compatible. This is checked
26
+ by normalizing the units and then transforming the Value with the
27
+ larger unit to the smaller unit. For instance, adding inches and feet
28
+ will result in inches because an inch is the smaller unit. Value
29
+ supports most other numeric operators.
30
+
31
+ == Converters
32
+
33
+ units.rb has the notion of converters. This notion has been introduced
34
+ because sometimes units have the same name but differ in value
35
+ depending on location (e.g., a hundredweight in the UK and the US),
36
+ they can have the same symbol though they are different, or in the
37
+ case of currency you might want to use different services with
38
+ possibly slightly different exchange rates. A unit belongs
39
+ unambiguously to a single converter. Units from different converters
40
+ can be used together, so it is possible to use US and UK
41
+ hundredweights in the same expression. What happens here is that a
42
+ unit is not only determined by a name but also by a converter. That's
43
+ all there is to it really.
44
+
45
+ There's always the notion of a current converter. When constructing a
46
+ unit, the current converter is used by default unless specified
47
+ otherwise. This current converter can be changed with
48
+ Van::Units.with_unit_converter which takes a block. The current
49
+ converter is stored in a thread-local variable for the duration of the
50
+ block and is taken from there. This gives the user means to specify
51
+ what unit systems to use in a more granular way. Converter can include
52
+ other converter. This allows a converter to extend another converter
53
+ and override some of the names. This is actually what
54
+ Van::Units.with_unit_converter does: internally it creates an
55
+ anonymous converter that includes both the previous "current
56
+ converter" and the new one which gets stacked on top and thus takes
57
+ precedence.
58
+
59
+ == Syntactic Sugar
60
+
61
+ Units are specified using symbols like :mile and :in, but units.rb
62
+ offers a lot of syntactic sugar to make it easier to use. After
63
+ including Van::Units, you can use mile and in directly, or even do
64
+ 1.in and 2.miles. This is implemented through method_missing and
65
+ const_missing (for units that start with a capital letter).
66
+
67
+ == Loading Config Files
68
+
69
+ units.rb uses a DSL to specify converters and units. These DSLs are
70
+ conveniently used in the config files to preload a large number of
71
+ units.
72
+
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Peter Vanbroekhoven & Thomas Sawyer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+
@@ -0,0 +1,68 @@
1
+ = VanUnits
2
+
3
+ * home: http://rubyworks.github.com/vanunits
4
+
5
+ == Description
6
+
7
+ VanUnits is a sophisticated SI units system. It also includes
8
+ currency units and a large set of scientific constants.
9
+
10
+ == Usage
11
+
12
+ Here are some examples of using the units system.
13
+
14
+ reqiure 'van/units'
15
+
16
+ include Van::Units
17
+
18
+ 1.bit/s + 8.bytes/s
19
+ (1.bit/s).to(byte/s)
20
+ 1.mile.to(feet)
21
+ 1.acre.to(yd**2)
22
+ 1.acre.to(sq_yd)
23
+ 1.gallon.to(self.L)
24
+ 1.lb.to(kg)
25
+ 1.m.s.to(m.s)
26
+ 1.sq_mi.to(km**2)
27
+ 1.mile.to(km)
28
+
29
+ Van::Units is namespace for all unit related classes. Mixing this in has
30
+ the additional effect of making Units.with_unit_converter available
31
+ without the <code>Units.</code> prefix, as well as the shortcuts for
32
+ creating Units (see Van::Units#method_missing).
33
+
34
+
35
+ === Constants
36
+
37
+ Also included are a large assortment of real world contants. These come in two
38
+ varieties, typeless and typed via units.rb. (PLEASE NOTE: The typed variety is not
39
+ yet complete).
40
+
41
+ Constants are also provided in both mks (m kg s) and in cgs (cm g s) format.
42
+
43
+ require 'van/units/constants/mks'
44
+ require 'van/units/constants/cgs'
45
+
46
+ include Van::Units::Constants
47
+
48
+ MKS::SPEED_OF_LIGHT #=> 2.99792458e8 m/s
49
+ CGS::SPEED_OF_LIGHT #=> 2.99792458e10 cm/s
50
+
51
+ Big thanks to Daniel Carrera and Brian Gough for their original work on Math::Constants
52
+ from which these numbers derive.
53
+
54
+
55
+ == Authors/Contributors
56
+
57
+ * Peter Vanbroekhoven
58
+ * Thomas Sawyer
59
+ * Daniel Carrera
60
+ * Brian Gough
61
+
62
+
63
+ == License
64
+
65
+ Copyright 2006, 2007 Peter Vanbroekhoven, Thomas Sawyer
66
+
67
+ Stick is distributed under the terms of the MIT license.
68
+
@@ -0,0 +1,116 @@
1
+ require 'van/units/units'
2
+
3
+ module Van
4
+ module Units
5
+
6
+ # Load conversion units.
7
+ class Converter
8
+ require("units-standard")
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+
15
+ # Checkrun
16
+
17
+ =begin check
18
+
19
+ class A
20
+
21
+ include Van::Units
22
+
23
+ def test
24
+ puts 1.bit/s + 8.bytes/s
25
+
26
+ puts((1.bit/s).to(byte/s))
27
+
28
+ puts 1.mile.to(feet)
29
+
30
+ puts 1.acre.to(yd**2)
31
+
32
+ puts 1.acre.to(sq_yd)
33
+
34
+ puts 1.gallon.to(L)
35
+
36
+ puts 1.lb.to(kg)
37
+
38
+ puts 1.m.s.to(m.s)
39
+
40
+ puts 1.sq_mi.to(km**2)
41
+
42
+ puts 1.mile.to(km)
43
+
44
+ #puts 1.usd.to(twd)
45
+
46
+ with_unit_converter(:uk) {
47
+ puts 1.cwt.to(lb)
48
+ }
49
+
50
+ with_unit_converter(:us) {
51
+ puts 1.cwt.to(lb)
52
+ }
53
+
54
+ puts 1.cwt(:uk).to(lb(:uk))
55
+ puts 1.cwt(:us).to(lb(:us))
56
+
57
+ puts Converter.current.lb
58
+
59
+ p Converter.registered_converters
60
+
61
+ #begin
62
+ # puts 1.try.to(usd)
63
+ #rescue TypeError
64
+ # p $!
65
+ #end
66
+
67
+ #puts 1.usd(:cex).to(twd(:cex))
68
+
69
+ puts 1.cwt(:uk).to(cwt(:us))
70
+ puts 1.cwt(:us).to(cwt(:uk))
71
+
72
+ with_unit_converter(:uk) {
73
+ puts 1.cwt(:uk).to(cwt(:us))
74
+ puts 1.cwt(:us).to(cwt(:uk))
75
+ }
76
+
77
+ p (1.m <=> 1.L)
78
+ p (1.m <=> 1.cm)
79
+
80
+ p((1.MB / s).to(kB / s))
81
+
82
+ with_unit_converter(:binary_iec_base) {
83
+ p((1.MB / s).to(kB / s))
84
+ }
85
+
86
+ p "m / s".to_unit
87
+ p "1 m / s".to_value
88
+
89
+ p "1 m / cm L".to_value.simplify
90
+
91
+ p "1 m / cm".to_value.to_f
92
+
93
+ p 1.m.to("cm")
94
+
95
+ p 1.m + "5cm"
96
+
97
+ p 1.m + 5.cm
98
+
99
+ p 5.cm + 1.m
100
+
101
+ p cm * m
102
+
103
+ p cm * "m"
104
+
105
+ p "-5mm".to_value
106
+
107
+ p "-5mm".to_value.abs
108
+
109
+ p ("5.0mm".to_value / 1).infinite?
110
+ end
111
+ end
112
+
113
+ A.new.test
114
+
115
+ =end
116
+
@@ -0,0 +1,991 @@
1
+ # Title:
2
+ #
3
+ # Units
4
+ #
5
+ # Summary:
6
+ #
7
+ # SI Units system, integrated into Ruby's method call system.
8
+ #
9
+ # Copyright:
10
+ #
11
+ # Copyright (c) 2005 Peter Vanbroekhoven, Thomas Sawyer
12
+ #
13
+ # License:
14
+ #
15
+ # Ruby License
16
+ #
17
+ # This module is free software. You may use, modify, and/or redistribute this
18
+ # software under the same terms as Ruby.
19
+ #
20
+ # This program is distributed in the hope that it will be useful, but WITHOUT
21
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
+ # FOR A PARTICULAR PURPOSE.
23
+ #
24
+ # Created:
25
+ #
26
+ # 2005.08.01
27
+ #
28
+ # Authors:
29
+ #
30
+ # - Peter Vanbroekhoven
31
+ # - Thomas Sawyer
32
+
33
+ require 'rbconfig'
34
+ require 'van/units/loaders'
35
+
36
+ class Exception
37
+
38
+ def clean_backtrace(regex)
39
+ regex = /^#{::Regexp.escape(__FILE__)}:\d+:in `#{::Regexp.escape(regex)}'$/ if regex.is_a? ::String
40
+ set_backtrace(backtrace.reject { |a| regex =~ a })
41
+ self
42
+ end
43
+
44
+ def self.with_clean_backtrace(regex)
45
+ begin
46
+ yield
47
+ rescue ::Exception
48
+ $!.clean_backtrace(regex).clean_backtrace("with_clean_backtrace") if not $DEBUG
49
+ raise
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ module Van
56
+
57
+ # The namespace for all unit related classes. Mixing this in has the additional effect
58
+ # of making Units.with_unit_converter available without the <code>Units.</code> prefix,
59
+ # as well as the shortcuts for creating Units (see Units#method_missing).
60
+ #
61
+ #--
62
+ # http://en.wikipedia.org/wiki/Celsius_scale
63
+ #
64
+ # degrees_Celsius vs Celsius_degrees
65
+ # Kelvin
66
+ #
67
+ # Kelvin Celsius Fahrenheit Rankine Delisle Newton Réaumur Rømer
68
+ #
69
+ # K C F R D N R R
70
+ #
71
+ # http://en.wikipedia.org/wiki/Conversion_of_units
72
+ #++
73
+
74
+ module Units
75
+
76
+ def method_missing(m, *args, &blk)
77
+ if args.length == 1
78
+ args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter
79
+ return Units::Unit.new({m => 1}, args[0]) if args[0] && args[0].registered?(m)
80
+ elsif (Units::Converter.current.registered?(m) rescue false)
81
+ raise ::ArgumentError, "Wrong number of arguments" if args.length != 0
82
+ return Units::Unit.new({m => 1}, Units::Converter.current)
83
+ end
84
+ ::Exception.with_clean_backtrace("method_missing") {
85
+ super
86
+ }
87
+ end
88
+
89
+ def const_missing(c)
90
+ if (Units::Converter.current.registered?(c) rescue false)
91
+ return Units::Unit.new({c => 1}, Units::Converter.current)
92
+ else
93
+ ::Exception.with_clean_backtrace("const_missing") {
94
+ super
95
+ }
96
+ end
97
+ end
98
+
99
+ def self.append_features(m)
100
+ m.send(:extend, Units)
101
+ super
102
+ end
103
+
104
+ # Executes the block with the current Converter changed to
105
+ # the given Converter. This allows to temporarily change the
106
+ # Converter used by default. A Converter object can be given,
107
+ # or the name of a registered Converter.
108
+ #
109
+ # Example:
110
+ #
111
+ # with_unit_converter(:uk) {
112
+ # puts 1.cwt.to(lb) # => 112.0 lb
113
+ # }
114
+ #
115
+ # with_unit_converter(:us) {
116
+ # puts 1.cwt.to(lb) # => 100.0 lb
117
+ # }
118
+ #
119
+ # See also Converter.current.
120
+ def with_unit_converter(name, &blk) # :yields:
121
+ Units::Converter.with_converter(name, &blk)
122
+ end
123
+
124
+ module_function :with_unit_converter
125
+
126
+ class Regexps
127
+
128
+ NUMBER_REGEXP = /(\-?\d+((?:\.\d+)?(?:[eE][-+]?\d+)?))/
129
+ SINGLE_UNIT_REGEXP = /([a-zA-Z_]+)(?::([a-zA-Z_]+))?(?:\s*\*\*\s*([+-]?\d+))?/
130
+ SINGLE_UNIT_NOC_REGEXP = /[a-zA-Z_]+(?::[a-zA-Z_]+)?(?:\s*\*\*\s*[+-]?\d+)?/ # Violates DRY principle
131
+ MULTIPLE_UNIT_REGEXP = /#{SINGLE_UNIT_NOC_REGEXP}(?:(?:\s+|\s*\*\s*)#{SINGLE_UNIT_NOC_REGEXP})*/
132
+ TOTAL_UNIT_REGEXP = /^\s*(?:1|(#{MULTIPLE_UNIT_REGEXP}))\s*(?:\/\s*(#{MULTIPLE_UNIT_REGEXP})\s*)?$/
133
+ TOTAL_UNIT_NOC_REGEXP = /\s*(?:1|(?:#{MULTIPLE_UNIT_REGEXP}))\s*(?:\/\s*(?:#{MULTIPLE_UNIT_REGEXP})\s*)?/ # Violates DRY principle
134
+ VALUE_REGEXP = /^\s*#{NUMBER_REGEXP}\s*\*?\s*(#{TOTAL_UNIT_NOC_REGEXP})$/
135
+
136
+ end
137
+
138
+ class BaseUnit
139
+
140
+ attr_reader :name, :converter
141
+
142
+ def initialize(name, converter = Units::Converter.current)
143
+ name = name.to_sym
144
+ raise ::ArgumentError, "unit #{name.to_s.dump} not registered with #{converter}" if not converter.registered? name
145
+ @name = name
146
+ @converter = converter
147
+ end
148
+
149
+ def conversion
150
+ @converter.send(:conversions, @name)
151
+ end
152
+
153
+ def ==(other)
154
+ other.is_a?(Units::BaseUnit) && other.name == @name && other.converter == @converter
155
+ end
156
+
157
+ alias eql? ==
158
+
159
+ def hash
160
+ @name.hash ^ @converter.hash
161
+ end
162
+
163
+ def to_s
164
+ if Units::Converter.current.includes?(converter)
165
+ @name.to_s
166
+ else
167
+ "#{@converter}:#{@name}"
168
+ end
169
+ end
170
+
171
+ alias inspect to_s
172
+
173
+ end
174
+
175
+ # This class represents a Unit. A Unit uses a given Converter with
176
+ # a number of registered units in which it can be expressed.
177
+ # A Unit is the product of the powers of other units. In principle, these
178
+ # need not be integer powers, but this may cause problems with rounding.
179
+ # The following code for example returns +false+:
180
+ # Unit.new(:m => 0.1) * Unit.new(:m => 0.2) == Unit.new(:m => 0.3)
181
+ #
182
+ # Units can be multiplied, divided, and raised to a given power.
183
+ # As an extra, 1 can be divided by a Unit.
184
+ #
185
+ # Examples:
186
+ #
187
+ # Unit.new(:mi => 1, :s => -1) ** 2 # => mi**2/s**2
188
+ # Unit.new(:mi => 1, :s => -1) * Unit.new(:s => 1, :usd => -1) # => mi/usd
189
+ # Unit.new(:mi => 1, :s => -1, Converter.converter(:uk)) *
190
+ # Unit.new(:s => 1, :usd => -1, Converter.converter(:us)) # => TypeError
191
+ # 1 / Unit.new(:mi => 1, :s => -1) # => s/mi
192
+ class Unit
193
+
194
+ attr_reader :units
195
+
196
+ # Creates a new (composite) Unit.
197
+ # It is passed a hash of the form <code>{:unit => exponent}</code>, and the
198
+ # Converter to use.
199
+ #
200
+ # Examples:
201
+ #
202
+ # Unit.new(:m => 1, :s => -1, Converter.converter(:uk)) # => m/s
203
+ # Unit.new(:mi => 1, :s => -2) # => mi/s**2
204
+ #
205
+ # See also Converter, Converter.converter
206
+ def initialize(units = {}, converter = nil)
207
+ conv = proc { converter ||= Units::Converter.current }
208
+ if units.is_a? ::String
209
+ @units = decode_string(units, conv)
210
+ else
211
+ @units = {}
212
+ units.each_pair do |k, v|
213
+ k = conv[].base_unit(k.to_sym) if not k.is_a? Units::BaseUnit
214
+ @units[k] = v
215
+ end
216
+ end
217
+ normalize_units
218
+ end
219
+
220
+ # Raises to the given power.
221
+ def **(p)
222
+ result = {}
223
+ @units.each_pair do |u, e|
224
+ result[u] = e * p
225
+ end
226
+ Units::Unit.new(result)
227
+ end
228
+
229
+ # Multiplies with the given Unit.
230
+ def *(other)
231
+ do_op(:*, :+, other)
232
+ end
233
+
234
+ # Divides by the given Unit.
235
+ def /(other)
236
+ do_op(:/, :-, other)
237
+ end
238
+
239
+ # Returns +true+ iff this Unit has all exponents equal to 0.
240
+ def unitless?
241
+ @units.empty?
242
+ end
243
+
244
+ def coerce(other) # :nodoc:
245
+ return [Units::Unit.new({}), self] if other == 1
246
+ Units::Converter.coerce_units(self, other)
247
+ end
248
+
249
+ def simplify
250
+ Units::Converter.simplify_unit(self)
251
+ end
252
+
253
+ # Returns +true+ iff the two units are equals, <i>i.e.,</i> iff they have
254
+ # the same exponent for all units, and they both use the same Converter.
255
+ def ==(other)
256
+ other.is_a?(Units::Unit) && other.units == units
257
+ end
258
+
259
+ alias eql? ==
260
+
261
+ def hash
262
+ @units.to_a.map { |e| e.hash }.hash
263
+ end
264
+
265
+ # Returns +true+ iff this Unit is compatible with the given
266
+ # Unit. This is less strict than equality because for example hours are
267
+ # compatible with seconds ("we can add them"), but hours are not seconds.
268
+ def compatible_with?(other)
269
+ conv1, conv2 = Units::Converter.coerce_units(self, other)
270
+ conv1.units == conv2.units
271
+ end
272
+
273
+ # Returns a human readable string representation.
274
+ def to_s
275
+ numerator = ""
276
+ denominator = ""
277
+ @units.each_pair do |u, e|
278
+ e_abs = e > 0 ? e : -e # This works with Complex too
279
+ (e > 0 ? numerator : denominator) << " #{u.to_s}#{"**#{e_abs}" if e_abs != 1}"
280
+ end
281
+ "#{numerator.lstrip}#{"/" + denominator.lstrip if not denominator.empty?}"
282
+ end
283
+
284
+ # Returns +self+.
285
+ def to_unit(converter = nil)
286
+ self
287
+ end
288
+
289
+ alias inspect to_s
290
+
291
+ def method_missing(m, *args, &blk)
292
+ if args.length == 1
293
+ args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter
294
+ return self * Units::Unit.new({m => 1}, args[0]) if args[0] && args[0].registered?(m)
295
+ elsif (Units::Converter.current.registered?(m) rescue false)
296
+ raise ::ArgumentError, "Wrong number of arguments" if args.length != 0
297
+ return self * Units::Unit.new({m => 1}, Units::Converter.current)
298
+ end
299
+ ::Exception.with_clean_backtrace("method_missing") {
300
+ super
301
+ }
302
+ end
303
+
304
+ private
305
+
306
+ def decode_string(s, converter)
307
+ if Units::Regexps::TOTAL_UNIT_REGEXP =~ s
308
+ numerator, denominator = $1, $2
309
+ units = {}
310
+ decode_multiplicative_string(numerator, 1, converter, units) if numerator
311
+ decode_multiplicative_string(denominator, -1, converter, units) if denominator
312
+ units
313
+ elsif /^\s*$/ =~ s
314
+ {}
315
+ else
316
+ raise ::ArgumentError, "Illegal unit string #{s.dump}"
317
+ end
318
+ end
319
+
320
+ def decode_multiplicative_string(s, multiplier, converter, result)
321
+ s.scan(Units::Regexps::SINGLE_UNIT_REGEXP) do |conv, unit, exp|
322
+ if unit
323
+ conv = Units::Converter.converter(conv)
324
+ else
325
+ conv, unit = converter[], conv
326
+ end
327
+ exp ||= '1'
328
+ unit = conv.base_unit(unit)
329
+ result[unit] = (result[unit] || 0) + Integer(exp) * multiplier
330
+ end
331
+ end
332
+
333
+ def normalize_units
334
+ @units.keys.each do |unit|
335
+ @units.delete(unit) if @units[unit] == 0
336
+ end
337
+ end
338
+
339
+ def do_op(op, dual_op, other)
340
+ other = other.to_unit
341
+ raise TypeError, "cannot convert to Unit" unless Units::Unit === other
342
+ result = @units.dup
343
+ other.units.each_pair do |u, e|
344
+ result[u] = 0 if not result[u]
345
+ result[u] = result[u].send(dual_op, e)
346
+ end
347
+ Units::Unit.new(result)
348
+ end
349
+
350
+ end
351
+
352
+ # This class represents a Value with a numeric value and a Unit.
353
+ # The numeric value can be any Numeric, though it is not recommended
354
+ # to use Values.
355
+ #
356
+ # A Value can be added to, subtracted from and multiplied with another value,
357
+ # though only when both Values are using the same Converter. While multiplication
358
+ # is always possible, adding or subtracting values with incompatible units
359
+ # results in a TypeError. When two units are compatible but not the same,
360
+ # the Value with the larger of the units is converted to the smaller of the units.
361
+ # For example adding 100 seconds and 1 minute, the latter is converted to 60 seconds
362
+ # because a second is smaller than a minute. The result is 160 seconds.
363
+ class Value < Numeric
364
+
365
+ # The numeric value of this Value.
366
+ attr_reader :value
367
+ # The Unit of this value.
368
+ attr_reader :unit
369
+
370
+ include Comparable
371
+
372
+ class << self
373
+
374
+ alias old_new new
375
+
376
+ private :old_new
377
+
378
+ # Creates a new Value with the given numeric value and the given unit.
379
+ # Simply returns the given value if the given unit is unitless,
380
+ # <i>i.e.,</i> when <code>unit.unitless?</code> is true.
381
+ def new(value, *args)
382
+ res = new!(value, *args)
383
+ return res.value if res.unit.unitless?
384
+ res
385
+ end
386
+
387
+ def new!(value, *args)
388
+ if ::String === value
389
+ str = *args
390
+ converter = case args.length
391
+ when 0
392
+ when 1
393
+ conv = args[0]
394
+ else
395
+ raise ArgumentError, "wrong number of arguments"
396
+ end
397
+ value, unit = decode_string(value, converter)
398
+ else
399
+ if args.length == 1
400
+ unit = args[0]
401
+ else
402
+ raise ArgumentError, "wrong number of arguments"
403
+ end
404
+ end
405
+ unit = Unit.new(unit) unless unit.is_a?(Unit)
406
+ old_new(value, unit)
407
+ end
408
+
409
+ end
410
+
411
+ def initialize(value, unit) # :nodoc:
412
+ @value, @unit = value, unit
413
+ end
414
+
415
+ %w{ * / }.each do |op|
416
+ eval %{
417
+ def #{op}(other)
418
+ Units::Value.new(*do_multiplicative_op(:#{op}, other))
419
+ end
420
+ }
421
+ end
422
+
423
+ %w{ + - modulo remainder % }.each do |op|
424
+ eval %{
425
+ def #{op}(other)
426
+ Units::Value.new(*do_additive_op(:#{op}, other))
427
+ end
428
+ }
429
+ end
430
+
431
+ def divmod(other)
432
+ (q, r), unit = *do_additive_op(:divmod, other)
433
+ [q, Units::Value.new(r, unit)]
434
+ end
435
+
436
+ def div(other)
437
+ do_additive_op(:div, other)[0]
438
+ end
439
+
440
+ def -@ # :nodoc:
441
+ Value.new(-@value, @unit)
442
+ end
443
+
444
+ def +@ # :nodoc:
445
+ self
446
+ end
447
+
448
+ def **(other) # :nodoc:
449
+ Units::Value.new(@value ** other, @unit ** other)
450
+ end
451
+
452
+ def <=>(other) # :nodoc:
453
+ if other == 0
454
+ @value <=> 0
455
+ else
456
+ (self - other).value <=> 0
457
+ end
458
+ rescue TypeError
459
+ nil
460
+ end
461
+
462
+ alias eql? ==
463
+
464
+ def hash
465
+ @value.hash ^ @unit.hash
466
+ end
467
+
468
+ %w{ abs ceil floor next round succ truncate }.each do |op|
469
+ eval %{
470
+ def #{op}
471
+ Units::Value.new(@value.#{op}, @unit)
472
+ end
473
+ }
474
+ end
475
+
476
+ %w{ finite? infinite? integer? nan? nonzero? zero? }.each do |op|
477
+ eval %{
478
+ def #{op}
479
+ @value.#{op}
480
+ end
481
+ }
482
+ end
483
+
484
+ # Converts this Value to the given Unit.
485
+ # This only works if the Converters used by this Value's Unit
486
+ # and the given Unit are the same. It obviously fails if the
487
+ # Units are not compatible (can't add apples and oranges).
488
+ def to(to_unit, converter = nil)
489
+ raise ArgumentError, "Wrong number of arguments" if converter && !(::String === to_unit)
490
+ to_unit = to_unit.to_unit(converter)
491
+ raise TypeError, "cannot convert to Unit" unless Units::Unit === to_unit
492
+ conv1, conv2 = unit.coerce(to_unit)
493
+ raise TypeError, "incompatible units for operation" if conv1.units != conv2.units
494
+ mult = conv1.multiplier / conv2.multiplier
495
+ Units::Value.new(value * mult, to_unit)
496
+ end
497
+
498
+ def coerce(other) # :nodoc:
499
+ if ::Numeric === other
500
+ [Units::Value.new!(other, Units::Unit.new), self]
501
+ else
502
+ super
503
+ end
504
+ end
505
+
506
+ # Returns a human readable string representation.
507
+ def to_s
508
+ "#{@value} #{@unit}"
509
+ end
510
+
511
+ # Returns a float if this Value is unitless, and raises an
512
+ # exception otherwise.
513
+ def to_f
514
+ val = simplify
515
+ if Units::Value === val
516
+ raise TypeError, "Cannot convert to float"
517
+ else
518
+ val.to_f
519
+ end
520
+ end
521
+
522
+ # Returns an int if this Value is unitless, and raises an
523
+ # exception otherwise.
524
+ def to_i
525
+ val = simplify
526
+ if Units::Value === val
527
+ raise TypeError, "Cannot convert to integer"
528
+ else
529
+ val.to_i
530
+ end
531
+ end
532
+
533
+ # Returns an int if this Value is unitless, and raises an
534
+ # exception otherwise.
535
+ def to_int
536
+ val = simplify
537
+ if Units::Value === val
538
+ raise TypeError, "Cannot convert to integer"
539
+ else
540
+ val.to_int
541
+ end
542
+ end
543
+
544
+ # Forces simplification of the Unit part of this Value. Returns
545
+ # a new Value or a Float.
546
+ def simplify
547
+ mul, new_unit = *@unit.simplify
548
+ if new_unit.unitless?
549
+ @value * mul
550
+ else
551
+ Units::Value.new(@value * mul, new_unit)
552
+ end
553
+ end
554
+
555
+ # Returns +self+.
556
+ def to_value(converter = nil)
557
+ self
558
+ end
559
+
560
+ alias inspect to_s
561
+
562
+ def method_missing(m, *args, &blk)
563
+ if args.length == 1
564
+ args[0] = (Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a? Units::Converter
565
+ return self * Units::Value.new(1, Units::Unit.new({m => 1}, args[0])) if args[0] && args[0].registered?(m)
566
+ elsif (Units::Converter.current.registered?(m) rescue false)
567
+ raise ::ArgumentError, "Wrong number of arguments" if args.length != 0
568
+ return self * Units::Value.new(1, Units::Unit.new({m => 1}, Units::Converter.current))
569
+ end
570
+ ::Exception.with_clean_backtrace("method_missing") {
571
+ super
572
+ }
573
+ end
574
+
575
+ private
576
+
577
+ def self.decode_string(s, converter)
578
+ if m = Units::Regexps::VALUE_REGEXP.match(s)
579
+ value = m[2].empty? ? Integer(m[1]) : Float(m[1])
580
+ unit = Units::Unit.new(m[3], converter)
581
+ [value, unit]
582
+ else
583
+ raise ::ArgumentError, "Illegal value string #{s.dump}"
584
+ end
585
+ end
586
+
587
+ def do_additive_op(op, other)
588
+ other = other.to_value
589
+ raise TypeError, "cannot convert to Value" unless Units::Value === other
590
+ if other.is_a? Units::Value
591
+ conv1, conv2 = unit.coerce(other.unit)
592
+ raise TypeError, "incompatible units for #{op}" if conv1.units != conv2.units
593
+ mult = conv2.multiplier / conv1.multiplier
594
+ if mult > 1
595
+ [value.send(op, other.value * mult), unit]
596
+ else
597
+ mult = conv1.multiplier / conv2.multiplier
598
+ [(value * mult).send(op, other.value), other.unit]
599
+ end
600
+ else
601
+ raise TypeError, "incompatible operands for #{op}"
602
+ end
603
+ end
604
+
605
+ def do_multiplicative_op(op, other)
606
+ case other
607
+ when Units::Value
608
+ [value.send(op, other.value), unit.send(op, other.unit)]
609
+ when Units::Unit
610
+ [value, unit.send(op, other)]
611
+ when ::Numeric
612
+ [value.send(op, other), unit]
613
+ else
614
+ # TODO: How to make this work for Units as well?
615
+ # Problem : how check whether to_value failed without
616
+ # masking all exceptions?
617
+ other = other.to_value
618
+ raise TypeError, "cannot convert to Value" unless Units::Value === other
619
+ do_multiplicative_op(op, other)
620
+ end
621
+ end
622
+
623
+ end
624
+
625
+ # This class handles all conversions between units.
626
+ #
627
+ # There are two kinds of units; those that are not expressed as a function
628
+ # of other units --called base units-- and those that are expressed
629
+ # as a function of other units --called derived units. The latter kind is
630
+ # registered specifying how it depends on other units, while the former
631
+ # kind is not.
632
+ #
633
+ # This class also registers a list of Converters that are generally useable.
634
+ # The default Converter which is used when none is specified, can be retrieved
635
+ # with Converter.current. Converters can be registered with Converter.register.
636
+ #
637
+ # Converters can be loaded from YAML. This allows Converters to be specified in
638
+ # configuration files.
639
+ class Converter
640
+
641
+ Conversion = Struct.new(:units, :multiplier) # :nodoc:
642
+
643
+ class Conversion # :nodoc:
644
+
645
+ def **(p)
646
+ Conversion.new(units ** p, multiplier ** p)
647
+ end
648
+
649
+ def *(m)
650
+ Conversion.new(units * m.units, multiplier * m.multiplier)
651
+ end
652
+
653
+ def /(m)
654
+ Conversion.new(units / m.units, multiplier / m.multiplier)
655
+ end
656
+
657
+ end
658
+
659
+ ShiftedConversion = Struct.new(:delta_type, :offset)
660
+
661
+ # Returns the name of this Converter, or <tt>nil</tt> if the
662
+ # Converter is not registered.
663
+ attr_accessor :name
664
+ private :name=
665
+
666
+ # Creates a new Converter. If a block is given,
667
+ # it is executed in the newly created Converter's context.
668
+ def initialize(name)
669
+ @conversions = {}
670
+ @included = []
671
+ @name = name
672
+ end
673
+
674
+ # Included the given converter in the receiver, unless it
675
+ # was already included.
676
+ def include(conv)
677
+ conv = Units::Converter.converter(conv) if not conv.is_a?(Units::Converter)
678
+ raise "Circular include" if conv.includes?(self)
679
+ @included << conv if not includes? conv
680
+ self
681
+ end
682
+
683
+ # Returns whether the given converter was included in the
684
+ # receiver.
685
+ def includes?(conv)
686
+ conv = Units::Converter.converter(conv) if not conv.is_a?(Units::Converter)
687
+ return true if conv == self
688
+ @included.each do |c|
689
+ return true if conv == c || c.includes?(conv)
690
+ end
691
+ false
692
+ end
693
+
694
+ # Returns the list of all included converters. This list may
695
+ # contain duplicates in some cases.
696
+ def included_converters(result = [])
697
+ result << self
698
+ @included.reverse_each { |c| c.included_converters(result) }
699
+ result
700
+ end
701
+
702
+ # Checks whether the unit with the given name is registered.
703
+ # The name can be a symbol or a string.
704
+ def registered?(unit)
705
+ unit = unit.to_sym
706
+ return self if registered_here?(unit)
707
+ @included.reverse_each do |c|
708
+ if res = c.registered?(unit)
709
+ return res
710
+ end
711
+ end
712
+ nil
713
+ end
714
+
715
+ # Returns the base unit with this name
716
+ def base_unit(name)
717
+ if conv = registered?(name)
718
+ return Units::BaseUnit.new(name, conv)
719
+ end
720
+ raise "unit #{name.to_s.dump} not registered with #{self}"
721
+ end
722
+
723
+ # Returns the list of registered unit names as symbols.
724
+ def registered_units
725
+ @conversions.keys
726
+ end
727
+
728
+ #
729
+ def method_missing(m, *args, &blk)
730
+ if registered?(m)
731
+ raise ::ArgumentError, "Wrong number of arguments" if args.length != 0
732
+ return Units::Unit.new({m => 1}, self)
733
+ end
734
+ ::Exception.with_clean_backtrace("method_missing") {
735
+ super
736
+ }
737
+ end
738
+
739
+ # Returns a human readable string representation of this Converter.
740
+ def to_s
741
+ (@name.to_s if @name) || "#<Converter:#{object_id.to_s(16)}>"
742
+ end
743
+
744
+ alias inspect to_s
745
+
746
+ private
747
+
748
+ # Registers a new Unit with the given name. The +data+ parameter
749
+ # is a Hash with some extra parameters (can be Strings or Symbols):
750
+ # +alias+:: Specifies possible aliases for the Unit.
751
+ # +abbrev+:: Specifies possible abbreviations or symbols for the Unit.
752
+ # The differences with aliases is that prefixes work differently;
753
+ # see +register_si_unit+, +register_binary_unit+ and
754
+ # +register_binary_iec_unit+.
755
+ # +equals+:: If present, specifies how the Unit depends on other units.
756
+ # The value for this key can either be a Hash with +unit+ mapping to
757
+ # a Unit and +multiplier+ mapping to a numeric multiplier, or
758
+ # a String containing the multiplier, followed by a space, followed by
759
+ # a representation of the Unit as returned by Unit#to_s.
760
+ #
761
+ # Examples:
762
+ #
763
+ # converter.register_unit(:pint, :alias => :pints, :abbrev => [:pt, :pts]))
764
+ # converter.register_unit(:quart, 'alias' => :quarts, :abbrev => ['qt', :qts], :equals => '2.0 pt'))
765
+ # converter.register_unit(:gallon, :alias => :gallons, :abbrev => :gal, 'equals' => {:unit => Unit.new('qt' => 1, converter), 'multiplier' => 4.0))
766
+ #
767
+ # Note that Symbols and Strings are generally exchangeable within this
768
+ # library (internally they are converted to Symbols). The number one reason
769
+ # for this is that String look better in YAML.
770
+ #
771
+ # See also +register_si_unit+, +register_binary_unit+, +register_binary_iec_unit+,
772
+ # +register_length_unit+, and +register_currency+ in currency.rb.
773
+ def register_unit(unit, abbrevs=[], aliases=[], &conversion)
774
+ unit = unit.to_sym
775
+ abbrevs = [abbrevs].flatten.map{ |a| a.to_sym }
776
+ aliases = [aliases].flatten.map{ |a| a.to_sym }
777
+ #aliases = ["#{unit}s".to_sym] if aliases.empty? # TRANS: hmm... not here?
778
+ #unit, aliases, abbrevs = extract_data(name, data, :to_sym)
779
+ #conversion = data[:equals]
780
+ # TRANS: this can be imporved now that conversion is a block?
781
+ conversion = conversion.call if conversion
782
+ conversion = decode_conversion(conversion) if conversion
783
+ conversion = self.class.convert_conversion(conversion[:unit].units, conversion[:multiplier]) if conversion
784
+ register_unit_internal(unit, conversion)
785
+ conversion = self.class.convert_conversion({base_unit(unit) => 1}, 1) if not conversion
786
+ (aliases + abbrevs).each do |u|
787
+ register_unit_internal(u, conversion)
788
+ end
789
+ end
790
+
791
+ def register_unit_internal(unit, conversion)
792
+ raise "unit #{unit.to_s.dump} already registered with #{self}" if registered_here? unit
793
+ @conversions[unit] = conversion || :none
794
+ end
795
+
796
+ def register_prefixed_unit(unit, prefixes, abbrevs=[], aliases=[], &conversion)
797
+ unit = unit.to_s
798
+ abbrevs = [abbrevs].flatten.map{ |a| a.to_s }
799
+ aliases = [aliases].flatten.map{ |a| a.to_s }
800
+ aliases = ["#{unit}s"] if aliases.empty?
801
+ #aliases, abbrevs = extract_data(unit, data, :to_s)
802
+ register_unit(unit, abbrevs, aliases, &conversion)
803
+ unit_sym = unit.to_sym
804
+ prefixes.each_pair do |pre,info|
805
+ abbrev = info[:abbrev]
806
+ multiplier = info[:multiplier] || 1
807
+ power = info[:power] || 1
808
+ register_unit(pre + unit) do
809
+ {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier}
810
+ end
811
+ aliases.each do |a|
812
+ register_unit(pre + a) do
813
+ {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier}
814
+ end
815
+ end
816
+ abbrevs.each do |a|
817
+ register_unit(abbrev + a) do
818
+ {:unit => Units::Unit.new({unit_sym => power}, self), :multiplier => multiplier}
819
+ end
820
+ end
821
+ end
822
+ end
823
+
824
+ def decode_conversion(data)
825
+ if not data.is_a? ::String
826
+ return {:unit => data[:unit] || data['unit'],
827
+ :multiplier => data[:multiplier] || data['multiplier']}
828
+ end
829
+ if /^\s*1\s*\// =~ data
830
+ {:unit => Units::Unit.new(data, self)}
831
+ elsif m = /^\s*#{Units::Regexps::NUMBER_REGEXP}(?:\s+(\S.*)$|\s*$)/.match(data)
832
+ unit = m[3] ? Units::Unit.new(m[3], self) : Units::Unit.new({}, self)
833
+ if m[1]
834
+ multiplier = m[2].empty? ? Integer(m[1]) : Float(m[1])
835
+ {:unit => unit, :multiplier => multiplier}
836
+ else
837
+ {:unit => unit}
838
+ end
839
+ else
840
+ {:unit => Units::Unit.new(data, self)}
841
+ end
842
+ end
843
+
844
+ # Checks whether the unit with the given name is registered.
845
+ # The name can be a symbol or a string.
846
+ def registered_here?(unit)
847
+ unit = unit.to_sym
848
+ conversions(unit) != nil
849
+ end
850
+
851
+ def conversions(unit)
852
+ @conversions[unit] #|| (unit == :'--base-currency--' ? :none : nil)
853
+ end
854
+
855
+ class << self
856
+
857
+ THREAD_REFERENCE = 'Units::converter'.to_sym
858
+
859
+ private :new
860
+
861
+ # Returns the current Converter in the current Thread.
862
+ # The default converter is the one returned by <code>converter(:default)</code>.
863
+ # See also Units#with_converter and Converter.converter.
864
+ def current
865
+ Thread.current[THREAD_REFERENCE] ||= converter(:default)
866
+ end
867
+
868
+ def with_converter(conv) # :nodoc:
869
+ conv = converter(conv) if not conv.is_a? Units::Converter
870
+ raise ::ArgumentError, "Converter expected" if not conv.is_a? Units::Converter
871
+ begin
872
+ old_conv = Thread.current[THREAD_REFERENCE]
873
+ if old_conv
874
+ new_conv = Converter.send(:new, nil)
875
+ new_conv.include(old_conv)
876
+ new_conv.include(conv)
877
+ else
878
+ new_conv = conv
879
+ end
880
+ Thread.current[THREAD_REFERENCE] = new_conv
881
+ yield
882
+ ensure
883
+ Thread.current[THREAD_REFERENCE] = old_conv
884
+ end
885
+ end
886
+
887
+ # Returns the converter with the given name.
888
+ # This name can be a Symbol or a String.
889
+ def converter(name, &blk)
890
+ if blk
891
+ (converters[name.to_sym] ||= new(name.to_sym)).instance_eval(&blk)
892
+ else
893
+ converters[name.to_sym] or raise ::ArgumentError, "No converter #{name.to_s.dump} found"
894
+ end
895
+ end
896
+
897
+ # # Registers the given Converter under the given name.
898
+ # # This name can be a Symbol or a String. A Converter
899
+ # # can be registered under one name only, and only one
900
+ # # Converter can be registered under a given name.
901
+ # def register(name, converter)
902
+ # name = name.to_sym
903
+ # return if converters[name] == converter
904
+ # raise ArgumentError, "This converter is already registered under anoher name" if converter.name
905
+ # raise ArgumentError, "A converter with this name already exists" if converters[name]
906
+ # converters[name] = converter
907
+ # converter.name ||= name
908
+ # end
909
+
910
+ # Returns the list of names of registered converters.
911
+ def registered_converters
912
+ converters.keys
913
+ end
914
+
915
+ def coerce_units(unit1, unit2) # :nodoc:
916
+ [convert_conversion(unit1.units), convert_conversion(unit2.units)]
917
+ end
918
+
919
+ def simplify_unit(unit) # :nodoc:
920
+ conv = convert_conversion(unit.units)
921
+ [conv[:multiplier], conv[:units]]
922
+ end
923
+
924
+ def convert_conversion(units, multiplier = nil)
925
+ multiplier ||= 1
926
+ base_units = {}
927
+ other_units = {}
928
+ units.each_pair do |u, e|
929
+ (u.conversion != :none ? other_units : base_units)[u] = e
930
+ end
931
+ result = Conversion.new(Units::Unit.new(base_units, self), multiplier)
932
+ other_units.each_pair do |u, e|
933
+ result *= (u.conversion ** e)
934
+ end
935
+ result
936
+ end
937
+
938
+ def converters
939
+ @converters ||= {}
940
+ end
941
+
942
+ end
943
+
944
+ end
945
+
946
+ # Contains some configuration related constants.
947
+ # TODO: Use plugin manager to find units.
948
+ module Config
949
+ # The directory in which the data files are searched for
950
+ #DATADIR = 'data/van/units' #DATADIR = File.join(::Config::CONFIG['DATADIR'], 'van', 'units')
951
+ #CONFIGDIR = 'lib/van/data' #DATADIR = File.join(::Config::CONFIG['DATADIR'], 'van', 'units')
952
+ SYSTEMDIR = File.dirname(__FILE__)
953
+ CONFIGDIR = File.join(SYSTEMDIR, 'data')
954
+ end
955
+
956
+ end
957
+ end
958
+
959
+ class Numeric
960
+ #
961
+ def method_missing(m, *args, &blk)
962
+ if args.length == 1
963
+ args[0] = (Van::Units::Converter.converter(args[0]) rescue nil) if not args[0].is_a?(Van::Units::Converter)
964
+ return Van::Units::Value.new(self, Van::Units::Unit.new({m => 1}, args[0])) if args[0] && args[0].registered?(m)
965
+ elsif Van::Units::Converter.current.registered?(m)
966
+ raise ::ArgumentError, "Wrong number of arguments" if args.length != 0
967
+ return Van::Units::Value.new(self, Van::Units::Unit.new({m => 1}, Van::Units::Converter.current))
968
+ end
969
+ ::Exception.with_clean_backtrace("method_missing") {
970
+ super
971
+ }
972
+ end
973
+
974
+ #
975
+ def to_value(unit)
976
+ Van::Units::Value.new(self, unit)
977
+ end
978
+ end
979
+
980
+
981
+ class String
982
+ #
983
+ def to_unit(converter = nil)
984
+ Van::Units::Unit.new(self, converter)
985
+ end
986
+
987
+ #
988
+ def to_value(converter = nil)
989
+ Van::Units::Value.new(self, converter)
990
+ end
991
+ end