quantity 0.0.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +204 -0
- data/VERSION +1 -1
- data/lib/quantity.rb +365 -6
- data/lib/quantity/all.rb +8 -0
- data/lib/quantity/dimension.rb +269 -0
- data/lib/quantity/dimension/base.rb +54 -0
- data/lib/quantity/systems/enumerable.rb +12 -0
- data/lib/quantity/systems/imperial.rb +10 -0
- data/lib/quantity/systems/information.rb +30 -0
- data/lib/quantity/systems/si.rb +105 -0
- data/lib/quantity/systems/us.rb +19 -0
- data/lib/quantity/unit.rb +309 -17
- data/lib/quantity/version.rb +1 -1
- metadata +11 -10
- data/lib/quantity/unit/current.rb +0 -9
- data/lib/quantity/unit/length.rb +0 -9
- data/lib/quantity/unit/luminosity.rb +0 -9
- data/lib/quantity/unit/mass.rb +0 -9
- data/lib/quantity/unit/substance.rb +0 -9
- data/lib/quantity/unit/temperature.rb +0 -9
- data/lib/quantity/unit/time.rb +0 -9
data/README
CHANGED
@@ -1,5 +1,209 @@
|
|
1
1
|
Quantity.rb: Units and Quantities for Ruby
|
2
2
|
==========================================
|
3
|
+
Quantity.rb provides first-class support for units and quantities in Ruby.
|
4
|
+
The right abstractions for true quantity representation and complex conversions.
|
5
|
+
Hopefully this readme will be all you need, but [there are yardocs](http://quantity.rubyforge.org)
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
require 'quantity/all'
|
9
|
+
1.meter #=> 1 meter
|
10
|
+
1.meter.to_feet #=> 3.28083... foot
|
11
|
+
c = 299792458.meters / 1.second #=> 299792458 meter/second
|
12
|
+
|
13
|
+
newton = 1.meter * 1.kilogram / 1.second**2 #=> 1 meter*kilogram/second^2
|
14
|
+
newton.to_feet #=> 3.28083989501312 foot*kilogram/second^2
|
15
|
+
newton.convert(:feet) #=> 3.28083989501312 foot*kilogram/second^2
|
16
|
+
jerk_newton / 1.second #=> 1 meter*kilogram/second^3
|
17
|
+
jerk_newton * 1.second == newton #=> true
|
18
|
+
|
19
|
+
mmcubed = 1.mm.cubed #=> 1 millimeter^3
|
20
|
+
mmcubed * 1000 == 1.milliliter #=> true
|
21
|
+
|
22
|
+
[1.meter, 1.foot, 1.inch].sort #=> [1 inch, 1 foot, 1 meter]
|
23
|
+
|
24
|
+
m_to_f = Quantity::Unit.for(:meter).convert_proc(:feet)
|
25
|
+
m_to_f.call(1) #=> 3.28083... (or a Rational)
|
26
|
+
|
27
|
+
Quantity.rb provides full-featured support for quantities, units, and
|
28
|
+
dimensions in Ruby. Some terminology:
|
29
|
+
|
30
|
+
* Quantity: An amount of a unit, such as 12 meters.
|
31
|
+
* Unit: An amount of a given dimension to be measured, such as 'meter'
|
32
|
+
* Dimension: Some base quantity to be measured, such as 'length'
|
33
|
+
|
34
|
+
Quantities perform complete mathematical operations over their units,
|
35
|
+
including `+`, `-`, `\*`, `/`, `\*`\*`, `%`, `abs`, `divmod`, `<=>`, and negation. Units
|
36
|
+
and the dimensions they measure are fully represented and support
|
37
|
+
`\*` and `/`.
|
38
|
+
|
39
|
+
Quantity extends Numeric to allow easy creation of quantities, but there
|
40
|
+
are direct interfaces to the library as well.
|
41
|
+
|
42
|
+
1.meter == Quantity.new(1,Quantity::Unit.for(:meter))
|
43
|
+
1.meter.unit == Quantity::Unit.for(:meter)
|
44
|
+
1.meter.unit.dimension == Quantity::Dimension.for(:length)
|
45
|
+
|
46
|
+
See the units section for supported units, and how to add your own.
|
47
|
+
|
48
|
+
Quantities are first-class citizens which do a fair job of imitating
|
49
|
+
Numeric. Quantities support coerce, and can thus be used in almost
|
50
|
+
any situation a numeric can:
|
51
|
+
|
52
|
+
2.5 + 5.meters # => 7.5 meters
|
53
|
+
5 == 5.meters # => true
|
54
|
+
|
55
|
+
## Status and TODO
|
56
|
+
Quantity.rb is not ready for production use for some areas, but should be
|
57
|
+
fine for simple conversion use cases. If it breaks, please email the
|
58
|
+
author for a full refund.
|
59
|
+
|
60
|
+
Specifically broken in this version are some operations on named
|
61
|
+
higher dimensions:
|
62
|
+
|
63
|
+
1.liter / 1.second #=> should be 1 liter/second, but explodes
|
64
|
+
1.liter.convert(:'mm^3') / 1.second #=> 1000000.0 millimeter^3/second
|
65
|
+
|
66
|
+
If you just work with units derived from the base dimensions, there aren't
|
67
|
+
known bugs. Please add a spec if you find one.
|
68
|
+
|
69
|
+
### TODO
|
70
|
+
* Lots more units are planned.
|
71
|
+
* BigDecimal support a la Rational.
|
72
|
+
* Supporting lambdas for unit values
|
73
|
+
* BigDecimal/Rational compatible values for existing units
|
74
|
+
* Some DSL sugar for adding derived dimension units
|
75
|
+
|
76
|
+
## Units
|
77
|
+
Quantity.rb comes with a sizable collection of units, but still needs significant expansion.
|
78
|
+
|
79
|
+
A number of base unit sets exist:
|
80
|
+
require 'quantity/all' #=> load everything. uses US versions of foot, lb, etc
|
81
|
+
require 'quantity/systems/si' #=> load SI
|
82
|
+
require 'quantity/systems/us' #=> load US versions of foot, lb, etc
|
83
|
+
require 'quantity/systems/imperial' #=> load British versions of foot, lb, etc
|
84
|
+
require 'quantity/systems/information' #=> bits, bytes, and all that
|
85
|
+
require 'quantity/systems/enumerable' #=> countable things--dozen, score, etc
|
86
|
+
|
87
|
+
Note that US and Imperial conflict with each other. Loading both is unsupported.
|
88
|
+
|
89
|
+
Adding your own units is simple:
|
90
|
+
|
91
|
+
Quantity::Unit.add_unit :furlong, :length, 201168, :furlongs
|
92
|
+
1.furlong #=> 1 furlong
|
93
|
+
|
94
|
+
201168 represents 1 furlong in millimeters. Each base dimension, such as length, time,
|
95
|
+
current, temperature, etc, is represented by a reference unit, which is generally the
|
96
|
+
milli-version of the SI unit referencing that domain. [NIST](http://physics.nist.gov/cuu/Units/units.html)
|
97
|
+
has an explanation of how the SI system works, and how all units are actually derived from
|
98
|
+
very few.
|
99
|
+
|
100
|
+
All units for derived dimensions used the derived reference unit. For example, length
|
101
|
+
is referenced to millimeters, so each unit of length is defined in terms of them:
|
102
|
+
|
103
|
+
Quantity::Unit.add_unit :meter, :length, 1000
|
104
|
+
Quantity::Unit.add_unit :millimeter, :length, 1, :mm
|
105
|
+
|
106
|
+
Thus, the base unit for volume is 1 mm^3:
|
107
|
+
volume = Quantity::Dimension.add_dimension length**3, :volume
|
108
|
+
ml = Quantity::Dimension.add_unit :milliliter, :volume, 1000, :ml, :milliliters
|
109
|
+
1.mm**3 * 1000 == 1.milliliter #=> true
|
110
|
+
|
111
|
+
See the bugs section for some current issues using units defined on derived dimensions.
|
112
|
+
|
113
|
+
The full list of included base dimensions and their reference units:
|
114
|
+
* :length => :millimeter
|
115
|
+
* :time => :millisecond
|
116
|
+
* :current => :milliampere
|
117
|
+
* :luminosity => :millicandela
|
118
|
+
* :substance => :millimole
|
119
|
+
* :temperature => :millikelvin
|
120
|
+
* :mass => :milligram
|
121
|
+
* :information => :bit # use :megabytes and :mebibytes
|
122
|
+
* :quantity => :item # for countable quantities. units include 1.dozen, for example
|
123
|
+
* :currency => :dollar # These are not really implemented yet
|
124
|
+
|
125
|
+
To determine the base unit for a derived dimension, you can use Quantity.rb itself:
|
126
|
+
|
127
|
+
force = Quantity::Dimension.for(:force)
|
128
|
+
newton = 1.meter * 1.kilogram / 1.second**2
|
129
|
+
newton.measures == force #=> true
|
130
|
+
newton_value = newton.to_mm.to_mg.to_ms #=> 1000.0 millimeter*milligram/millisecond^2
|
131
|
+
|
132
|
+
Thus, a newton would be 1000 when added specifically:
|
133
|
+
|
134
|
+
Quantity::Unit.add_unit :newton, :force, 1000, :newtons
|
135
|
+
1.newton == newton #=> true
|
136
|
+
|
137
|
+
## Dimensions
|
138
|
+
A dimension is a measurable thing, often called a 'base quantity' in scientific literature,
|
139
|
+
but Quantity.rb specifically avoids that nomenclature, reserving 'quantity' for the class
|
140
|
+
representing a unit and a value. As always, [wikipedia has the answers.](http://en.wikipedia.org/wiki/Physical_quantity)
|
141
|
+
|
142
|
+
Dimensions are not very useful by themselves, but you can play with them
|
143
|
+
if you want.
|
144
|
+
|
145
|
+
length = Quantity::Dimension.for(:length)
|
146
|
+
time = Quantity::Dimension.for(:time)
|
147
|
+
speed = length / time
|
148
|
+
|
149
|
+
A number of dimensions are enabled by default (see dimension/base.rb).
|
150
|
+
|
151
|
+
A DSL of sorts is provided for declaring dimensions:
|
152
|
+
|
153
|
+
length = Quantity::Dimension.add_dimenson :length
|
154
|
+
area = Quantity::Dimension.add_dimension length**2, :area
|
155
|
+
|
156
|
+
length = Quantity::Dimension.for(:length)
|
157
|
+
area = Quantity::Dimension.for(:area)
|
158
|
+
area == length * length #=> true
|
159
|
+
|
160
|
+
Quantity::Dimension is extended with empty subclasses for some base dimensions,
|
161
|
+
so you can do pattern patching on the class:
|
162
|
+
|
163
|
+
case 1.meter.measures
|
164
|
+
when Quantity::Dimension::Length
|
165
|
+
puts "I am printed"
|
166
|
+
end
|
167
|
+
|
168
|
+
## I just want to convert things, this is all just too much
|
169
|
+
Quantity.rb provides you the ability to intuitively create the conversions
|
170
|
+
your application needs, and then bypass the rest of the library.
|
171
|
+
|
172
|
+
m_to_f = 1.meter.measures.convert_proc(:feet)
|
173
|
+
m_to_f.call(5) # => 5 meters in feet
|
174
|
+
|
175
|
+
This Proc object has been broken down into a single division; it no longer references
|
176
|
+
any units, dimensions, or quantities. It's hard to be faster in pure Ruby.
|
177
|
+
|
178
|
+
### On precision and speed
|
179
|
+
|
180
|
+
By default, whatever Numeric you are using will be the stored value for the
|
181
|
+
quantity.
|
182
|
+
|
183
|
+
5.meters
|
184
|
+
Rational(5).meters
|
185
|
+
5.0.meters
|
186
|
+
|
187
|
+
This value will be held. However, divisions are required for conversions,
|
188
|
+
and the default is to force values into floats.
|
189
|
+
|
190
|
+
If accuracy is required, just require 'rational'. If Rational is defined,
|
191
|
+
you'll get rationals instead of divided floats everywhere. In tests, this
|
192
|
+
is an order of magnitude slower.
|
193
|
+
|
194
|
+
## 'Why' and previous work
|
195
|
+
This is by no means the first unit conversion/quantity library for Ruby, but
|
196
|
+
none of the existing ones scratched my itch just right. My goal is that this will
|
197
|
+
be the last one I (and you) need. The abstractions go all the way down, and
|
198
|
+
any conceivable conversion or munging functionality should be buildable on top
|
199
|
+
of this.
|
200
|
+
|
201
|
+
Inspiration comes from:
|
202
|
+
|
203
|
+
* [Quanty](http://narray.rubyforge.org/quanty/quanty-en.html)
|
204
|
+
Why oh why did they involve yacc?
|
205
|
+
* [Ruby Units](http://ruby-units.rubyforge.org/ruby-units/)
|
206
|
+
* [Alchemist](http://github.com/toastyapps/alchemist)
|
3
207
|
|
4
208
|
Authors
|
5
209
|
-------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
data/lib/quantity.rb
CHANGED
@@ -1,17 +1,376 @@
|
|
1
1
|
require 'quantity/version'
|
2
|
+
require 'quantity/dimension'
|
3
|
+
require 'quantity/dimension/base'
|
4
|
+
require 'quantity/unit'
|
5
|
+
require 'quantity/systems/si'
|
6
|
+
require 'quantity/systems/us'
|
2
7
|
|
8
|
+
#
|
9
|
+
# A quantity of something. Quantities are immutable; conversions and other operations return
|
10
|
+
# a new quantity.
|
11
|
+
#
|
12
|
+
# ## General Use
|
13
|
+
# require 'quantity/all'
|
14
|
+
#
|
15
|
+
# 12.meters #=> Quantity
|
16
|
+
# 12.meters.measures #=> :length
|
17
|
+
# 12.meters.units #=> :meters
|
18
|
+
# 12.meters.unit #=> Quantity::Unit::Length
|
19
|
+
# 12.meters.in_centimeters == 1200.centimeters #=> true
|
20
|
+
# 12.meters == 12 #=> true
|
21
|
+
# 12.meters == 12.centimeters #=> false
|
22
|
+
# 12.meters + 5.centimeters == 12.05.meters #=> true
|
23
|
+
# 12.meters.in_picograms #=> raises ArgumentError
|
24
|
+
#
|
25
|
+
# ## Derived Units
|
26
|
+
# require 'quantity/si'
|
27
|
+
# speed_of_light = 299_752_458.meters / 1.second #=>Quantity::Unit::Derived
|
28
|
+
# speed_of_light.measures #=> "meters per second"
|
29
|
+
# speed_of_light.units #=> "meters per second"
|
30
|
+
#
|
31
|
+
# ludicrous_speed = speed_of_light * 1000
|
32
|
+
# ludicrous_speed.measures #=> "meters per second" #TODO: velocity, accleration ?
|
33
|
+
# ludicrous_speed.to_s #=> "299752458000 meters per second"
|
34
|
+
#
|
35
|
+
# If the default to_s isn't what you want, you can buld it with 12.meters.value and 12.meters.units
|
36
|
+
#
|
37
|
+
# @see Quantity::Unit
|
3
38
|
class Quantity
|
39
|
+
include Comparable
|
4
40
|
autoload :Unit, 'quantity/unit'
|
5
41
|
|
6
|
-
undef_method *(instance_methods - %w(__id__ __send__ __class__ __eval__ instance_eval inspect))
|
42
|
+
#undef_method *(instance_methods - %w(__id__ __send__ __class__ __eval__ instance_eval inspect should))
|
7
43
|
|
44
|
+
# User-visible value, i.e. 2.meters.value == 2
|
8
45
|
attr_reader :value
|
46
|
+
|
47
|
+
# Unit of measurement
|
9
48
|
attr_reader :unit
|
10
49
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
50
|
+
# This quantity in terms of the reference value, declared by fiat for everything measurable
|
51
|
+
attr_reader :reference_value
|
52
|
+
|
53
|
+
#
|
54
|
+
# Initialize a new, immutable quantity
|
55
|
+
# @overload initialize(value, unit, options)
|
56
|
+
# @param [Numeric] value
|
57
|
+
# @param [Unit] unit
|
58
|
+
# @return [Quantity]
|
59
|
+
#
|
60
|
+
# @overload initialize(options)
|
61
|
+
# Only one of value or reference value can be used, if both are given, reference
|
62
|
+
# value will be used.
|
63
|
+
# @param [Hash{Symbol => Object}] options
|
64
|
+
# @option options [Numeric] :value Visible value
|
65
|
+
# @option options [Numeric] :reference_value Reference value
|
66
|
+
# @option options [Symbol Unit] :unit Units
|
67
|
+
# @return [Quantity]
|
68
|
+
#
|
69
|
+
def initialize(value, unit = nil )
|
70
|
+
case value
|
71
|
+
when Hash
|
72
|
+
@unit = Unit.for(value[:unit])
|
73
|
+
@reference_value = value[:reference_value] || (value[:value] * @unit.value)
|
74
|
+
@value = @unit.value_for(@reference_value) #dimension.reference.convert_proc(@unit).call(@reference_value)
|
75
|
+
#@value = @unit.convert_proc(@unit).call(@reference_value)
|
76
|
+
when Numeric
|
77
|
+
@unit = Unit.for(unit)
|
78
|
+
if @unit.nil?
|
79
|
+
@unit = Unit.from_string_form(unit)
|
80
|
+
end
|
81
|
+
@value = value
|
82
|
+
@reference_value = value * @unit.value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# String version of this quantity
|
87
|
+
# @param [String] format Format for sprintf, will be given
|
88
|
+
# @return [String]
|
89
|
+
def to_s
|
90
|
+
@unit.s_for(value)
|
91
|
+
end
|
92
|
+
|
93
|
+
# What this measures
|
94
|
+
# @return [Symbol String] What this measures. Derived types will be a string
|
95
|
+
def measures
|
96
|
+
@unit.dimension
|
97
|
+
end
|
98
|
+
|
99
|
+
# Units of measurement
|
100
|
+
# @return [Symbol String] Units of measurement. Derived types will be a string
|
101
|
+
def units
|
102
|
+
@unit.name
|
103
|
+
end
|
104
|
+
|
105
|
+
# Abs implementation
|
106
|
+
# @return [Quantity]
|
107
|
+
def abs
|
108
|
+
if @reference_value < 0
|
109
|
+
-self
|
110
|
+
else
|
111
|
+
self
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Ruby coercion. Allows things like 2 + 5.meters
|
116
|
+
# @return [Quantity, Quantity]
|
117
|
+
def coerce(other)
|
118
|
+
if other.class == @value.class
|
119
|
+
[Quantity.new(other, @unit),self]
|
120
|
+
elsif defined?(Rational) && (@value.is_a?(Fixnum)) && (other.is_a?(Fixnum))
|
121
|
+
[Quantity.new(Rational(other), @unit), self]
|
122
|
+
elsif defined?(Rational) && (other.is_a?(Rational))
|
123
|
+
[Quantity.new(other, @unit), self]
|
124
|
+
else
|
125
|
+
[Quantity.new(other.to_f, @unit),Quantity.new(@value.to_f, @unit)]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Addition. Add two quantities of the same type. Do not need to have the same units.
|
131
|
+
# @param [Quantity Numeric] other
|
132
|
+
# @return [Quantity]
|
133
|
+
def +(other)
|
134
|
+
if (other.is_a?(Numeric))
|
135
|
+
Quantity.new(@value + other, @unit)
|
136
|
+
elsif(other.is_a?(Quantity) && @unit.dimension == other.unit.dimension)
|
137
|
+
Quantity.new({:unit => @unit,:reference_value => @reference_value + other.reference_value})
|
138
|
+
else
|
139
|
+
raise ArgumentError,"Cannot add #{self} to #{other}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Subtraction. Subtract a quantity from another of the same type. They do not need
|
144
|
+
# to share units.
|
145
|
+
# @param [Quantity Numeric] other
|
146
|
+
# @return [Quantity]
|
147
|
+
def -(other)
|
148
|
+
if (other.is_a?(Numeric))
|
149
|
+
Quantity.new(@value - other, @unit)
|
150
|
+
elsif(other.is_a?(Quantity) && @unit.dimension == other.unit.dimension)
|
151
|
+
Quantity.new({:unit => @unit,:reference_value => @reference_value - other.reference_value})
|
152
|
+
else
|
153
|
+
raise ArgumentError, "Cannot subtract #{other} from #{self}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Comparison. Compare this to another quantity or numeric. Compared to a numeric,
|
158
|
+
# this will assume a numeric of the same unit as self.
|
159
|
+
# @param [Quantity Numeric] other
|
160
|
+
# @return [-1 0 1]
|
161
|
+
def <=>(other)
|
162
|
+
if (other.is_a?(Numeric))
|
163
|
+
@value <=> other
|
164
|
+
elsif(other.is_a?(Quantity) && measures == other.measures)
|
165
|
+
@reference_value <=> other.reference_value
|
166
|
+
else
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Type-aware equality
|
172
|
+
# @param [Any]
|
173
|
+
# @return [Boolean]
|
174
|
+
def eql?(other)
|
175
|
+
other.is_a?(Quantity) && other.units == units && self == other
|
176
|
+
end
|
177
|
+
|
178
|
+
# Multiplication.
|
179
|
+
# @param [Numeric, Quantity]
|
180
|
+
# @return [Quantity]
|
181
|
+
def *(other)
|
182
|
+
if (other.is_a?(Numeric))
|
183
|
+
Quantity.new(@value * other, @unit)
|
184
|
+
elsif(other.is_a?(Quantity))
|
185
|
+
Quantity.new({:unit => other.unit * @unit, :reference_value => @reference_value * other.reference_value})
|
186
|
+
else
|
187
|
+
raise ArgumentError, "Cannot multiply #{other} with #{self}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Division
|
192
|
+
# @param [Numeric, Quantity]
|
193
|
+
# @return [Quantity]
|
194
|
+
def /(other)
|
195
|
+
if (other.is_a?(Numeric))
|
196
|
+
Quantity.new(@value / other, @unit)
|
197
|
+
elsif(other.is_a?(Quantity))
|
198
|
+
ref = nil
|
199
|
+
if defined?(Rational) && (@value.is_a?(Fixnum)) && (other.is_a?(Fixnum))
|
200
|
+
ref = Rational(@reference_value,other.reference_value)
|
201
|
+
elsif defined?(Rational) && (@value.is_a?(Rational)) && (other.is_a?(Rational))
|
202
|
+
ref = @reference_value / other.reference_value
|
203
|
+
else
|
204
|
+
ref = @reference_value / other.reference_value.to_f
|
205
|
+
end
|
206
|
+
Quantity.new({:unit => @unit / other.unit, :reference_value => ref})
|
207
|
+
else
|
208
|
+
raise ArgumentError, "Cannot multiply #{other} with #{self}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Exponentiation. Quantities cannot be raised to negative or fractional powers, only
|
213
|
+
# positive Fixnum.
|
214
|
+
# @param [Numeric]
|
215
|
+
# @return [Quantity]
|
216
|
+
def **(power)
|
217
|
+
unless power.is_a?(Fixnum) && power > 0
|
218
|
+
raise ArgumentError, "Quantities can only be raised to fixed powers (given #{power})"
|
219
|
+
end
|
220
|
+
if power == 1
|
221
|
+
self
|
222
|
+
else
|
223
|
+
self * self**(power - 1)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Square the units of this quantity
|
228
|
+
# @example
|
229
|
+
# 4.meters.squared == Quantity.new(4.'m^2')
|
230
|
+
# @return [Quantity]
|
231
|
+
def squared
|
232
|
+
Quantity.new(@value, @unit * @unit)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Cube the units of this quantity
|
236
|
+
# @example
|
237
|
+
# 4.meters.cubed == Quantity.new(4.'m^3')
|
238
|
+
# @return [Quantity]
|
239
|
+
def cubed
|
240
|
+
Quantity.new(@value, @unit * @unit * @unit)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Mod
|
244
|
+
# @return [Quantity]
|
245
|
+
def %(other)
|
246
|
+
if (other.is_a?(Numeric))
|
247
|
+
Quantity.new(@value % other, @unit)
|
248
|
+
elsif(other.is_a?(Quantity) && self.measures == other.measures)
|
249
|
+
Quantity.new({:unit => @unit, :reference_value => @reference_value % other.reference_value})
|
250
|
+
else
|
251
|
+
raise ArgumentError, "Cannot modulo #{other} with #{self}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# Both names for modulo
|
256
|
+
alias_method :modulo, :%
|
257
|
+
|
258
|
+
# Negation
|
259
|
+
# @return [Quantity]
|
260
|
+
def -@
|
261
|
+
Quantity.new({:unit => @unit, :reference_value => @reference_value * -1})
|
262
|
+
end
|
263
|
+
|
264
|
+
# Unary + (self)
|
265
|
+
# @return [Quantity]
|
266
|
+
def +@
|
267
|
+
self
|
268
|
+
end
|
269
|
+
|
270
|
+
# Integer representation
|
271
|
+
# @return [Fixnum]
|
272
|
+
def to_i
|
273
|
+
@value.to_i
|
274
|
+
end
|
275
|
+
|
276
|
+
# Float representation
|
277
|
+
# @return [Float]
|
278
|
+
def to_f
|
279
|
+
@value.to_f
|
280
|
+
end
|
281
|
+
|
282
|
+
# Round this value to the nearest integer
|
283
|
+
# @return [Quantity]
|
284
|
+
def round
|
285
|
+
Quantity.new(@value.round, @unit)
|
286
|
+
end
|
287
|
+
|
288
|
+
# Truncate this value to an integer
|
289
|
+
# @return [Quantity]
|
290
|
+
def truncate
|
291
|
+
Quantity.new(@value.truncate, @unit)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Largest integer quantity less than or equal to this
|
295
|
+
# @return [Quantity]
|
296
|
+
def floor
|
297
|
+
Quantity.new(@value.floor, @unit)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Smallest integer quantity greater than or equal to this
|
301
|
+
# @return [Quantity]
|
302
|
+
def ceil
|
303
|
+
Quantity.new(@value.ceil, @unit)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Divmod
|
307
|
+
# @return [Quantity,Quantity]
|
308
|
+
def divmod(other)
|
309
|
+
if (other.is_a?(Numeric))
|
310
|
+
(q, r) = @value.divmod(other)
|
311
|
+
[Quantity.new(q,@unit),Quantity.new(r,@unit)]
|
312
|
+
elsif (other.is_a?(Quantity) && measures == other.measures)
|
313
|
+
(q, r) = @value.divmod(other.value)
|
314
|
+
[Quantity.new(q,@unit),Quantity.new(r,@unit)]
|
315
|
+
else
|
316
|
+
raise ArgumentError, "Cannot divmod #{other} with #{self}"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns true if self has a zero value
|
321
|
+
# @return [Boolean]
|
322
|
+
def zero?
|
323
|
+
@value.zero?
|
324
|
+
end
|
325
|
+
|
326
|
+
# Convert to another unit of measurement.
|
327
|
+
# For most uses, Quantity#to_<unit> is what you want, but this can be handy
|
328
|
+
# for variable units.
|
329
|
+
# @param [Unit Symbol]
|
330
|
+
def convert(to)
|
331
|
+
Quantity.new({:unit => @unit.convert(to), :reference_value => @reference_value})
|
332
|
+
end
|
333
|
+
|
334
|
+
#
|
335
|
+
# :method to_unit
|
336
|
+
# Convert this quantity to another quantity.
|
337
|
+
# unit can be any unit that measures the same thing as this quantity, i.e.
|
338
|
+
# 12.meters can call .to_feet, .to_centimeters, etc. An error is raised with
|
339
|
+
# other types, i.e. 12.meters.to_grams
|
340
|
+
# @raises ArgumentError
|
341
|
+
# @return [Quantity]
|
342
|
+
|
343
|
+
# Developer-friendly string representation
|
344
|
+
# @return [String]
|
345
|
+
def inspect
|
346
|
+
to_s
|
347
|
+
end
|
348
|
+
|
349
|
+
# this creates the conversion methods of .to_* and .in_*
|
350
|
+
# @private
|
351
|
+
def method_missing(method, *args, &block)
|
352
|
+
if method.to_s =~ /(to_|in_)(.*)/
|
353
|
+
if (Unit.is_unit?($2.to_sym))
|
354
|
+
convert($2.to_sym)
|
355
|
+
else
|
356
|
+
raise ArgumentError, "Unknown target unit type: #{$2}"
|
357
|
+
end
|
358
|
+
else
|
359
|
+
raise NoMethodError, "Undefined method `#{method}` for #{self}:#{self.class}"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
# @private
|
366
|
+
# Plug our constructors into Numeric
|
367
|
+
class Numeric
|
368
|
+
alias_method :quantity_method_missing, :method_missing
|
369
|
+
def method_missing(method, *args, &block)
|
370
|
+
if Quantity::Unit.is_unit?(method)
|
371
|
+
Quantity.new(self,Quantity::Unit.for(method))
|
372
|
+
else
|
373
|
+
quantity_method_missing(method,*args, &block)
|
374
|
+
end
|
16
375
|
end
|
17
376
|
end
|