quantity 0.0.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ # Units for the most commonly used US units. If you need to
2
+ # worry about the minitae, such as the difference between a
3
+ # fluid pint and a dry pint, pleases see the documentation
4
+ # for the quantity/systems/us modules.
5
+ class Quantity::Unit
6
+ add_unit :foot, :length, 304.8, :ft, :feet
7
+ add_unit :inch, :width, 25.4, :in, :inches
8
+ add_unit :yard, :length, 914.4, :yd, :yards
9
+ add_unit :mile, :length, 1_609_344, :miles
10
+
11
+ add_unit :pound, :mass, 453592.37, :pounds, :lb, :lbs
12
+ add_unit :ounce, :mass, 28349.5231, :ounces, :oz
13
+ add_unit :ton, :mass, 907184740, :tons
14
+
15
+ add_unit :fluid_ounce, :volume, 29.57, :floz, :ozfl
16
+ add_unit :pint, :volume, 473.18, :pint, :pints
17
+ add_unit :quart, :volume, 946.35, :qt, :quarts
18
+ add_unit :gallon, :volume, 3785.41, :gallons, :gal
19
+ end
@@ -1,28 +1,320 @@
1
1
  class Quantity
2
+ # A unit of measurement.
3
+ #
4
+ # Units are a well-defined increment of a measurement domain. Units
5
+ # measure a particular Dimension, which may be base or compound.
6
+ # Examples of units are meters and degrees celius.
7
+ #
8
+ # Units are not quantities, and their associated value only defines
9
+ # their relationship to their measurement domain. For a representation
10
+ # of a given number of units, see Quantity.
11
+ #
12
+ # There is only one type of unit. Units are simply denote a range on
13
+ # their measurement dimension, which may be compound and complicated.
14
+ #
15
+ # Units are implemented in terms of a reference unit for each dimension.
16
+ # The SI milli- unit for each of the base physical dimensions is the
17
+ # reference unit for each of the so-called base quantities.
18
+ #
19
+ # Units are known by a wide variety of abbreviations and names. Each
20
+ # unit is only instantiated once, regardless of what name it is called
21
+ # by. The cannonical name is used internally. A client Quantity
22
+ # object is responsible for remembering which name a unit was originally
23
+ # called as.
24
+ #
2
25
  class Unit
3
- autoload :Length, 'quantity/unit/length'
4
- autoload :Mass, 'quantity/unit/mass'
5
- autoload :Time, 'quantity/unit/time'
6
- autoload :Current, 'quantity/unit/current'
7
- autoload :Temperature, 'quantity/unit/temperature'
8
- autoload :Luminosity, 'quantity/unit/luminosity'
9
- autoload :Substance, 'quantity/unit/substance'
26
+ include Comparable
27
+
28
+ # All known units
29
+ @@units = {}
10
30
 
31
+ # The unit for a given symbol or string description of a compound unit
32
+ # @param [Symbol String Unit] to
33
+ # @return [Unit]
34
+ def self.for(to)
35
+ to.is_a?(Unit) ? to : @@units[to]
36
+ end
37
+
38
+ # Whether or not the given symbol or string refers to an existing unit
39
+ # @param [Symbol String Unit] to
40
+ # @return [Boolean]
41
+ def self.is_unit?(to)
42
+ to.is_a?(Unit) || @@units.has_key?(to)
43
+ end
44
+
45
+ # Register a unit with the given symbols
46
+ # @param [Unit] unit
47
+ # @param [*names]
48
+ def self.add_alias(unit,*names)
49
+ unit = Unit.for(unit) unless unit.is_a? Unit
50
+ names.each do |name|
51
+ @@units[name] = unit
52
+ end
53
+ end
54
+
55
+ # Add a unit to the system
56
+ # @param [Dimension] dimension
57
+ # @param [Symbol] name
58
+ # @param [Numeric] value
59
+ # @param [[String Symbol]] *aliases
60
+ def self.add_unit(name,dimension,value,*names)
61
+ new_unit = Unit.new({ :name => name,:dimension => Quantity::Dimension.for(dimension),:value => value})
62
+ names.each do | name |
63
+ add_alias new_unit, name
64
+ end
65
+ end
66
+ class << self ; alias_method :add, :add_unit; end
67
+
68
+ # Add a number of units to the system
69
+ # @example
70
+ # length = Quantity::Dimension.for(:length)
71
+ # Quantity::Unit.add_units do
72
+ # add :meter length 1000
73
+ # add :mm :length 1
74
+ # end
75
+ def self.add_units(&block)
76
+ self.class_eval(&block)
77
+ end
78
+
79
+ ### Instance-level methods/vars
80
+ attr_reader :name, :value, :dimension, :aliases
81
+
82
+ # All the known aliases for this Unit, i.e. name + aliases
83
+ # @return [[Symbol String]]
84
+ def names
85
+ [@name] + @aliases
86
+ end
87
+
88
+ # Can this unit be converted into the target unit?
89
+ # @param [Symbol String Unit]
90
+ # @return [Boolean]
91
+ def can_convert_to?(to)
92
+ Unit.for(to).dimension == @dimension
93
+ end
94
+
95
+ # Return the unit this unit will convert to.
96
+ # It's sometimes necessary to let the unit decide, in case a conversion such as
97
+ # meters^2 -> feet is requested, for which feet^2 should be returned.
98
+ # @param [Symbol String Unit]
99
+ # @return [Unit]
100
+ def convert(to)
101
+ Unit.for(to)
102
+ end
103
+
104
+ # Return a proc that will perform conversion from this unit to the given one
105
+ # @param [Symbol String Unit]
106
+ # @return [Unit]
107
+ def convert_proc(to)
108
+ to = convert(to)
109
+ #to = Unit.for(to)
110
+ raise ArgumentError, "Unable to find unit #{to}" unless to
111
+ unless (to.dimension == self.dimension)
112
+ raise ArgumentError, "Cannot convert #{self.dimension} to #{to.dimension}"
113
+ end
114
+ if defined?(Rational) && (@value.is_a?(Fixnum)) && (to.value.is_a?(Fixnum))
115
+ lambda do | from |
116
+ from * Rational(@value, to.value)
117
+ end
118
+ elsif defined?(Rational) && (@value.is_a?(Rational)) && (to.value.is_a?(Rational))
119
+ lambda do | from |
120
+ from * @value / to.value
121
+ end
122
+ else
123
+ lambda do | from |
124
+ from * (@value / to.value.to_f)
125
+ end
126
+ end
127
+ end
128
+
129
+ # The value for a given reference value.
130
+ # @example
131
+ # Unit.add_unit :meter, :length, :1000
132
+ # Unit.for(:meter).value_for(5000) = 5
133
+ # @param [Numeric] value
134
+ # @return [Numeric]
135
+ def value_for(reference_value)
136
+ if defined?(Rational) && (reference_value.is_a?(Fixnum)) && (@value.is_a?(Fixnum))
137
+ Rational(reference_value, @value)
138
+ elsif defined?(Rational) && (reference_value.is_a?(Rational) || reference_value.is_a?(Fixnum)) && (@value.is_a?(Rational))
139
+ reference_value / @value #Rational(reference_value, @value)
140
+ else
141
+ reference_value / @value.to_f
142
+ end
143
+ end
144
+
145
+ # A string representation of this unit at the given value
146
+ # @param [Any] value
11
147
  # @return [String]
12
- attr_reader :name
148
+ def s_for(value)
149
+ "#{value} #{@name.to_s}"
150
+ end
13
151
 
14
- alias_method :to_s, :name
152
+ def inspect
153
+ "<Unit #{@name} (#{@object_id}), value #{@value}, dimension #{@dimension}>"
154
+ end
15
155
 
16
- ##
17
- # @param [String] name
18
- def initialize(name = nil)
19
- @name = name || self.class.name.split(':').last.downcase
156
+ def <=>(other)
157
+ if other.is_a?(Unit) && other.dimension == @dimension
158
+ @value <=> other.value
159
+ elsif other.is_a?(Unit)
160
+ @name <=> other.name
161
+ else
162
+ nil
163
+ end
164
+ end
165
+
166
+ # Exponentiation
167
+ # @param other [Numeric]
168
+ # @return [Unit]
169
+ def **(other)
170
+ if other.is_a?(Fixnum) && other > 0
171
+ other == 1 ? self : self * self**(other-1)
172
+ else
173
+ raise ArgumentError, "#{self} cannot be raised to #{other} power."
174
+ end
175
+ end
176
+
177
+ # Unit multiplication.
178
+ # @param [Unit] other
179
+ # @return [Unit]
180
+ def *(other)
181
+ if other.is_a?(Unit)
182
+ units = other.units || { other.dimension => other }
183
+ units.merge!(@units || { @dimension => self })
184
+ dim = @dimension * other.dimension
185
+ existing = Unit.for(Unit.string_form(dim,units).to_sym)
186
+ existing ||= Unit.new({ :dimension => dim, :units => units })
187
+ else
188
+ raise ArgumentError, "Cannot multiply #{self} with #{other}"
189
+ end
190
+ end
191
+
192
+ # Unit division.
193
+ # @param [Unit] other
194
+ # @return [Unit]
195
+ def /(other)
196
+ if other.is_a?(Unit)
197
+ units = other.units || { other.dimension => other }
198
+ units.merge!(@units || { @dimension => self })
199
+ dim = @dimension / other.dimension
200
+ existing = Unit.for(Unit.string_form(dim,units).to_sym)
201
+ existing ||= Unit.new({ :dimension => dim, :units => units })
202
+ existing
203
+ else
204
+ raise ArgumentError, "Cannot multiply #{self} with #{other}"
205
+ end
206
+ end
207
+
208
+ # Convert a portion of this compound to another unit.
209
+ # This one is tricky, because a lot of things can be happening.
210
+ # It's valid to convert m^2 to ft^2 and to feet (ft^2), but not
211
+ # really valid to convert to ft^3.
212
+ # @param [Symbol Unit] to
213
+ # @return [Unit]
214
+ def convert(target)
215
+ to = Unit.from_string_form(target)
216
+ if (to.dimension == @dimension)
217
+ to
218
+ elsif @units && @units[to.dimension]
219
+ units = @units.merge({ to.dimension => to })
220
+ unit = Unit.for(Unit.string_form(@dimension,units).to_sym)
221
+ unit ||= Unit.new({ :dimension => @dimension, :units => units })
222
+ unit
223
+ else
224
+ raise ArgumentError, "Cannot convert #{self} to #{target}"
225
+ end
226
+ end
227
+
228
+ # Parse a string representation of a unit, such as foot^2/time^2, and return
229
+ # a compound object representing it.
230
+ def self.from_string_form(to)
231
+ if Unit.for(to)
232
+ Unit.for(to)
233
+ else
234
+ dimension_string = to.to_s.dup
235
+ units = {}
236
+ to.to_s.split(/(\^|\/|\*)/).each do | name |
237
+ next if name =~ /(\^|\/|\*)/ || name =~ /^\d$/
238
+ unit = Unit.for(name.to_sym) || Unit.for(name)
239
+ dimension_string.gsub!(name,unit.dimension.name.to_s)
240
+ units[unit.dimension] = unit
241
+ end
242
+ dimension = Dimension.for(dimension_string.to_sym)
243
+ raise ArgumentError, "Couldn't create Unit for #{to}" unless dimension && units
244
+ unit = Unit.new({ :dimension => dimension, :units => units })
245
+ add_alias(unit,unit.name.to_sym)
246
+ unit
247
+ end
248
+ end
249
+
250
+
251
+ # Higher-order units have a set of units to reference each aspect of the dimension they
252
+ # measure. This is unused in basic units.
253
+ attr_reader :units
254
+
255
+ # A new compound unit. There are two modes of operation. One provides a way to add units with
256
+ # a DSL. The other provides an options hash a little better for programming. The last provides
257
+ # a way to create a unit for a given dimension--useful for reference units.
258
+ #
259
+ # @overload initialize(opts = {})
260
+ # @param opts [String Symbol] :name
261
+ # @param opts [[Unit]] :units
262
+ # @param opts [Dimension] :dimension
263
+ # @return [Unit]
264
+ def initialize(opts)
265
+ @units = opts[:units]
266
+ @dimension = opts[:dimension]
267
+ @value = opts[:value] || calculate_value
268
+ if @dimension.nil?
269
+ raise ArgumentError, "Adding invalid unit with nil dimension (#{name} - #{dimension})"
270
+ end
271
+ unless opts[:name] || !@dimension.is_base?
272
+ raise ArgumentError, "Single-order units must be uniquely named (#{name} - #{dimension})"
273
+ end
274
+ @name = opts[:name] || string_form
275
+ self.class.add_alias(self,@name.to_sym)
276
+ raise ArgumentError, "Creating new unit with no value" unless @value
277
+ end
278
+
279
+ # calculate this unit's value compared to the reference unit
280
+ def calculate_value
281
+ value = defined?(Rational) ? Rational(1) : 1.0
282
+ @dimension.numerators.each do | component |
283
+ component.power.times do
284
+ # we might have a unit for a compound dimension, such as liters for length^3.
285
+ value *= @units[Quantity::Dimension.for(component.dimension)].value
286
+ end
287
+ end
288
+ @dimension.denominators.each do | component |
289
+ component.power.times do
290
+ value /= @units[Quantity::Dimension.for(component.dimension)].value
291
+ end
292
+ end
293
+ @value = value
294
+ end
295
+
296
+ # A vaguely human-readable form for this unit
297
+ # @return [String]
298
+ def string_form
299
+ self.class.string_form(@dimension,@units)
300
+ end
301
+
302
+ # a vaguely human-readable format for a compound unit
303
+ # @param [Dimension] dimension
304
+ # @param [{}] units
305
+ # @return [String]
306
+ def self.string_form(dimension,units)
307
+ string = dimension.string_form
308
+ units.each do | dimension, unit |
309
+ string = string.gsub(dimension.name.to_s, unit.name.to_s)
310
+ end
311
+ string
20
312
  end
21
313
 
22
- ##
23
- # @return [Symbol]
24
- def to_sym
25
- name.to_sym
314
+ # Reset the world. Useful in testing.
315
+ # @private
316
+ def self.__reset!
317
+ @@units = {}
26
318
  end
27
319
  end
28
320
  end
@@ -1,7 +1,7 @@
1
1
  class Quantity
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 0
4
+ MINOR = 1
5
5
  TINY = 0
6
6
  EXTRA = nil
7
7
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quantity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Lavender
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-12-29 00:00:00 +01:00
13
+ date: 2010-01-05 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -33,7 +33,7 @@ dependencies:
33
33
  - !ruby/object:Gem::Version
34
34
  version: 0.5.2
35
35
  version:
36
- description: Units and quantities for Ruby.
36
+ description: " Quantity provides first-class quantities, units, and base quantities in pure ruby.\n Things like 1.meter / 1.second == 1 meter/second.\n"
37
37
  email: blavender@gmail.com
38
38
  executables: []
39
39
 
@@ -46,13 +46,14 @@ files:
46
46
  - README
47
47
  - UNLICENSE
48
48
  - VERSION
49
- - lib/quantity/unit/current.rb
50
- - lib/quantity/unit/length.rb
51
- - lib/quantity/unit/luminosity.rb
52
- - lib/quantity/unit/mass.rb
53
- - lib/quantity/unit/substance.rb
54
- - lib/quantity/unit/temperature.rb
55
- - lib/quantity/unit/time.rb
49
+ - lib/quantity/all.rb
50
+ - lib/quantity/dimension/base.rb
51
+ - lib/quantity/dimension.rb
52
+ - lib/quantity/systems/enumerable.rb
53
+ - lib/quantity/systems/imperial.rb
54
+ - lib/quantity/systems/information.rb
55
+ - lib/quantity/systems/si.rb
56
+ - lib/quantity/systems/us.rb
56
57
  - lib/quantity/unit.rb
57
58
  - lib/quantity/version.rb
58
59
  - lib/quantity.rb
@@ -1,9 +0,0 @@
1
- class Quantity
2
- class Unit
3
- ##
4
- # @see http://en.wikipedia.org/wiki/Electric_current
5
- class Current < Unit
6
- # TODO
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class Quantity
2
- class Unit
3
- ##
4
- # @see http://en.wikipedia.org/wiki/Length
5
- class Length < Unit
6
- # TODO
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class Quantity
2
- class Unit
3
- ##
4
- # @see http://en.wikipedia.org/wiki/Luminous_intensity
5
- class Luminosity < Unit
6
- # TODO
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class Quantity
2
- class Unit
3
- ##
4
- # @see http://en.wikipedia.org/wiki/Mass
5
- class Mass < Unit
6
- # TODO
7
- end
8
- end
9
- end