vanunits 1.5.0

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