quantity 0.0.0 → 0.1.1
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.
- 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
|