quantify 1.0.5 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/quantify.rb +3 -1
- data/lib/quantify/config.rb +243 -243
- data/lib/quantify/core_extensions/numeric.rb +19 -0
- data/lib/quantify/core_extensions/string.rb +29 -0
- data/lib/quantify/core_extensions/symbol.rb +7 -0
- data/lib/quantify/dimensions.rb +19 -21
- data/lib/quantify/quantify.rb +32 -1
- data/lib/quantify/quantity.rb +37 -37
- data/lib/quantify/unit/base_unit.rb +113 -61
- data/lib/quantify/unit/compound_base_unit.rb +54 -14
- data/lib/quantify/unit/compound_unit.rb +42 -18
- data/lib/quantify/unit/non_si_unit.rb +1 -1
- data/lib/quantify/unit/prefix/base_prefix.rb +5 -5
- data/lib/quantify/unit/prefix/prefix.rb +4 -4
- data/lib/quantify/unit/unit.rb +55 -24
- data/spec/compound_unit_spec.rb +205 -0
- data/spec/dimension_spec.rb +49 -1
- data/spec/quantify_spec.rb +20 -0
- data/spec/quantity_spec.rb +9 -9
- data/spec/string_spec.rb +55 -0
- data/spec/unit_spec.rb +292 -10
- metadata +24 -19
- data/lib/quantify/core_extensions.rb +0 -63
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
class Numeric
|
3
|
+
# Syntactic sugar for defining instances of the Quantity class.
|
4
|
+
#
|
5
|
+
# Enables quantities to be specified by using unit names, symbols or JScience
|
6
|
+
# labels as argments on Numeric objects, e.g.
|
7
|
+
#
|
8
|
+
# 1.5.metre is equivalent to Quantity. new 1.5, :metre
|
9
|
+
#
|
10
|
+
# 1000.t is equivalent to Quantity. new 1000, :t
|
11
|
+
#
|
12
|
+
def method_missing(method, *args, &block)
|
13
|
+
if unit = Unit.for(method.to_s)
|
14
|
+
Quantify::Quantity.new self, unit
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
|
4
|
+
def with_superscript_characters
|
5
|
+
self.gsub(/\^2\b/,"²").gsub(/\^3\b/,"³")
|
6
|
+
end
|
7
|
+
|
8
|
+
def without_superscript_characters
|
9
|
+
self.gsub(/¹\b/,"").gsub(/²\b/,"^2").gsub(/³\b/,"^3")
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_underscores
|
13
|
+
self.gsub("_"," ")
|
14
|
+
end
|
15
|
+
|
16
|
+
def words
|
17
|
+
split(/\s+/)
|
18
|
+
end
|
19
|
+
|
20
|
+
def word_count
|
21
|
+
words.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_quantity
|
25
|
+
Quantify::Quantity.parse(self)
|
26
|
+
end
|
27
|
+
alias :to_q :to_quantity
|
28
|
+
|
29
|
+
end
|
data/lib/quantify/dimensions.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#! usr/bin/ruby
|
2
1
|
|
3
2
|
module Quantify
|
4
3
|
|
@@ -52,7 +51,7 @@ module Quantify
|
|
52
51
|
#
|
53
52
|
def self.base_dimensions
|
54
53
|
@@dimensions.select do |dimensions|
|
55
|
-
BASE_QUANTITIES.map
|
54
|
+
BASE_QUANTITIES.map {|q| q.remove_underscores}.include?(dimensions.describe)
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
@@ -83,8 +82,8 @@ module Quantify
|
|
83
82
|
# Remove a dimension from the system of known dimensions
|
84
83
|
def self.unload(*unloaded_dimensions)
|
85
84
|
[unloaded_dimensions].flatten.each do |unloaded_dimension|
|
86
|
-
unloaded_dimension = Dimensions.for(
|
87
|
-
@@dimensions.delete_if { |
|
85
|
+
unloaded_dimension = Dimensions.for(unloaded_dimension)
|
86
|
+
@@dimensions.delete_if { |dimension| dimension.has_same_identity_as? unloaded_dimension }
|
88
87
|
end
|
89
88
|
end
|
90
89
|
|
@@ -97,7 +96,7 @@ module Quantify
|
|
97
96
|
# ... ]
|
98
97
|
#
|
99
98
|
def self.physical_quantities
|
100
|
-
@@dimensions.map
|
99
|
+
@@dimensions.map {|dimension| dimension.physical_quantity }
|
101
100
|
end
|
102
101
|
|
103
102
|
# Retrieve a known quantity - returns a Dimensions instance, which is a
|
@@ -116,10 +115,9 @@ module Quantify
|
|
116
115
|
#
|
117
116
|
def self.for(name)
|
118
117
|
return name if name.is_a? Dimensions
|
119
|
-
if name.is_a?
|
120
|
-
|
121
|
-
|
122
|
-
end
|
118
|
+
if (name.is_a?(String) || name.is_a?(Symbol))
|
119
|
+
name = name.remove_underscores.downcase
|
120
|
+
if quantity = @@dimensions.find { |quantity| quantity.physical_quantity == name }
|
123
121
|
return quantity.clone
|
124
122
|
else
|
125
123
|
raise Exceptions::InvalidArgumentError, "Physical quantity not known: #{name}"
|
@@ -141,8 +139,8 @@ module Quantify
|
|
141
139
|
#
|
142
140
|
# end
|
143
141
|
#
|
144
|
-
def self.configure
|
145
|
-
self.class_eval
|
142
|
+
def self.configure(&block)
|
143
|
+
self.class_eval(&block) if block
|
146
144
|
end
|
147
145
|
|
148
146
|
# Provides a shorthand for retrieving known quantities, e.g.:
|
@@ -185,7 +183,7 @@ module Quantify
|
|
185
183
|
#
|
186
184
|
def initialize(options={})
|
187
185
|
if options.has_key?(:physical_quantity)
|
188
|
-
@physical_quantity = options.delete(:physical_quantity).
|
186
|
+
@physical_quantity = options.delete(:physical_quantity).remove_underscores.downcase
|
189
187
|
end
|
190
188
|
enumerate_base_quantities(options)
|
191
189
|
describe
|
@@ -199,7 +197,7 @@ module Quantify
|
|
199
197
|
# description of the physical quantity represented.
|
200
198
|
#
|
201
199
|
def load
|
202
|
-
if describe
|
200
|
+
if describe && !loaded?
|
203
201
|
@@dimensions << self
|
204
202
|
elsif describe
|
205
203
|
raise Exceptions::InvalidDimensionError, "A dimension instance with the same physical quantity already exists"
|
@@ -218,7 +216,7 @@ module Quantify
|
|
218
216
|
end
|
219
217
|
|
220
218
|
def has_same_identity_as?(other)
|
221
|
-
|
219
|
+
@physical_quantity == other.physical_quantity && !@physical_quantity.nil?
|
222
220
|
end
|
223
221
|
|
224
222
|
# Return a description of what physical quantity self represents. If no
|
@@ -246,7 +244,7 @@ module Quantify
|
|
246
244
|
#
|
247
245
|
def get_description
|
248
246
|
similar = @@dimensions.find { |quantity| quantity == self }
|
249
|
-
@physical_quantity =
|
247
|
+
@physical_quantity = similar.nil? ? nil : similar.physical_quantity
|
250
248
|
end
|
251
249
|
|
252
250
|
# Returns an array containing the known units which represent the physical
|
@@ -287,8 +285,8 @@ module Quantify
|
|
287
285
|
# #=> 'metre squared per second'
|
288
286
|
#
|
289
287
|
def si_unit
|
290
|
-
return Unit.steridian if
|
291
|
-
return Unit.radian if
|
288
|
+
return Unit.steridian if describe == 'solid angle'
|
289
|
+
return Unit.radian if describe == 'plane angle'
|
292
290
|
return si_base_units.inject(Unit.unity) do |compound,unit|
|
293
291
|
compound * unit
|
294
292
|
end.or_equivalent
|
@@ -319,7 +317,7 @@ module Quantify
|
|
319
317
|
def si_base_units(by=nil)
|
320
318
|
self.to_hash.map do |dimension,index|
|
321
319
|
Unit.base_quantity_si_units.select do |unit|
|
322
|
-
unit.measures == dimension.
|
320
|
+
unit.measures == dimension.remove_underscores
|
323
321
|
end.first.clone ** index
|
324
322
|
end.map(&by)
|
325
323
|
end
|
@@ -346,7 +344,7 @@ module Quantify
|
|
346
344
|
# Returns true if self represents one of the base quantities (i.e. length,
|
347
345
|
# mass, time, etc.)
|
348
346
|
def is_base?
|
349
|
-
base_quantities.size == 1
|
347
|
+
base_quantities.size == 1 &&
|
350
348
|
self.instance_variable_get(base_quantities.first) == 1 ? true : false
|
351
349
|
end
|
352
350
|
|
@@ -495,7 +493,7 @@ module Quantify
|
|
495
493
|
def enumerate_base_quantities(options)
|
496
494
|
options.each_pair do |base_quantity,index|
|
497
495
|
base_quantity = base_quantity.to_s.downcase.to_sym
|
498
|
-
unless index.is_a?
|
496
|
+
unless index.is_a?(Integer) && BASE_QUANTITIES.include?(base_quantity)
|
499
497
|
raise Exceptions::InvalidDimensionError, "An invalid base quantity was specified (#{base_quantity})"
|
500
498
|
end
|
501
499
|
variable = "@#{base_quantity}"
|
@@ -514,7 +512,7 @@ module Quantify
|
|
514
512
|
|
515
513
|
# Make object represent a dimensionless quantity.
|
516
514
|
def make_dimensionless
|
517
|
-
|
515
|
+
@physical_quantity = 'dimensionless'
|
518
516
|
base_quantities.each { |var| remove_instance_variable(var) }
|
519
517
|
end
|
520
518
|
|
data/lib/quantify/quantify.rb
CHANGED
@@ -4,7 +4,38 @@ module Quantify
|
|
4
4
|
self.module_eval &block if block
|
5
5
|
end
|
6
6
|
|
7
|
+
# Check whether superscript characters are turned on.
|
8
|
+
def self.use_superscript_characters?
|
9
|
+
@use_superscript_characters.nil? ? true : @use_superscript_characters
|
10
|
+
end
|
11
|
+
|
12
|
+
# Shorthand method for Quantify.use_superscript_characters=true
|
13
|
+
def self.use_superscript_characters!
|
14
|
+
self.use_superscript_characters=true
|
15
|
+
end
|
7
16
|
|
17
|
+
# Declare whether superscript characters should be used for unit names, symbols
|
18
|
+
# and labels - i.e. "²" and "³" rather than "^2" and "^3". Set to either true or
|
19
|
+
# false. If not set, superscript characters are used by default.
|
20
|
+
#
|
21
|
+
def self.use_superscript_characters=(true_or_false)
|
22
|
+
raise Exceptions::InvalidArgumentError,
|
23
|
+
"Argument must be true or false" unless true_or_false == true || true_or_false == false
|
24
|
+
@use_superscript_characters = true_or_false
|
25
|
+
refresh_all_unit_identifiers!
|
26
|
+
end
|
27
|
+
|
28
|
+
# Switch all unit identifiers (name, symbol, label) to use the currently
|
29
|
+
# configured system for superscripts.
|
30
|
+
#
|
31
|
+
def refresh_all_unit_identifiers!
|
32
|
+
Unit.units.replace(
|
33
|
+
Unit.units.map do |unit|
|
34
|
+
unit.refresh_identifiers!
|
35
|
+
unit
|
36
|
+
end
|
37
|
+
)
|
38
|
+
end
|
8
39
|
|
9
40
|
module ExtendedMethods
|
10
41
|
|
@@ -17,7 +48,7 @@ module Quantify
|
|
17
48
|
#
|
18
49
|
def method_missing(method, *args, &block)
|
19
50
|
if method.to_s =~ /((si|non_si|compound)_)?(non_(prefixed)_)?((base|derived|benchmark)_)?units(_by_(name|symbol|label))?/
|
20
|
-
if $2
|
51
|
+
if $2 || $4 || $6
|
21
52
|
conditions = []
|
22
53
|
conditions << "unit.is_#{$2}_unit?" if $2
|
23
54
|
conditions << "!unit.is_prefixed_unit?" if $4
|
data/lib/quantify/quantity.rb
CHANGED
@@ -81,19 +81,19 @@ module Quantify
|
|
81
81
|
# Quantity.new(123.456, :degree_celsius).represents
|
82
82
|
# #=> :temperature
|
83
83
|
def represents
|
84
|
-
|
84
|
+
@unit.measures
|
85
85
|
end
|
86
86
|
|
87
87
|
# Returns a string representation of the quantity, using the unit symbol
|
88
88
|
def to_s format=:symbol
|
89
89
|
if format == :name
|
90
|
-
if
|
91
|
-
"#{
|
90
|
+
if @value == 1 || @value == -1
|
91
|
+
"#{@value} #{@unit.name}"
|
92
92
|
else
|
93
|
-
"#{
|
93
|
+
"#{@value} #{@unit.pluralized_name}"
|
94
94
|
end
|
95
95
|
else
|
96
|
-
"#{
|
96
|
+
"#{@value} #{@unit.send format}"
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
@@ -116,20 +116,20 @@ module Quantify
|
|
116
116
|
def to(new_unit)
|
117
117
|
new_unit = Unit.for new_unit
|
118
118
|
if is_basic_conversion_with_scalings? new_unit
|
119
|
-
Quantity.new(value
|
119
|
+
Quantity.new(@value,@unit).conversion_with_scalings! new_unit
|
120
120
|
elsif self.unit.is_alternative_for? new_unit
|
121
|
-
Quantity.new(value
|
121
|
+
Quantity.new(@value,@unit).convert_to_equivalent_unit! new_unit
|
122
122
|
elsif self.unit.is_compound_unit?
|
123
|
-
Quantity.new(value
|
123
|
+
Quantity.new(@value,@unit).convert_compound_unit_to_non_equivalent_unit! new_unit
|
124
124
|
else
|
125
125
|
nil # raise? or ...
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
129
|
def is_basic_conversion_with_scalings?(new_unit)
|
130
|
-
return true if (
|
131
|
-
|
132
|
-
|
130
|
+
return true if (@unit.has_scaling? || new_unit.has_scaling?) &&
|
131
|
+
!@unit.is_compound_unit? &&
|
132
|
+
!new_unit.is_compound_unit?
|
133
133
|
return false
|
134
134
|
end
|
135
135
|
|
@@ -139,14 +139,14 @@ module Quantify
|
|
139
139
|
# base units
|
140
140
|
#
|
141
141
|
def convert_to_equivalent_unit!(new_unit)
|
142
|
-
old_unit = unit
|
142
|
+
old_unit = @unit
|
143
143
|
self.multiply!(Unit.ratio new_unit, old_unit)
|
144
144
|
old_base_units = old_unit.base_units.map { |base| base.unit } if old_unit.is_compound_unit?
|
145
145
|
self.cancel_base_units!(*old_base_units || old_unit)
|
146
146
|
end
|
147
147
|
|
148
148
|
def conversion_with_scalings!(new_unit)
|
149
|
-
@value = (((
|
149
|
+
@value = (((@value + @unit.scaling) * @unit.factor) / new_unit.factor) - new_unit.scaling
|
150
150
|
@unit = new_unit
|
151
151
|
return self
|
152
152
|
end
|
@@ -158,7 +158,7 @@ module Quantify
|
|
158
158
|
# Unit.kilowatt_hour.to :second #=> 'kilowatt second'
|
159
159
|
#
|
160
160
|
def convert_compound_unit_to_non_equivalent_unit!(new_unit)
|
161
|
-
|
161
|
+
@unit.base_units.select do |base|
|
162
162
|
base.unit.is_alternative_for? new_unit
|
163
163
|
end.inject(self) do |quantity,base|
|
164
164
|
factor = Unit.ratio(new_unit**base.index, base.unit**base.index)
|
@@ -168,16 +168,16 @@ module Quantify
|
|
168
168
|
|
169
169
|
# Converts a quantity to the equivalent quantity using only SI units
|
170
170
|
def to_si
|
171
|
-
if
|
172
|
-
Quantity.new(value
|
171
|
+
if @unit.is_compound_unit?
|
172
|
+
Quantity.new(@value,@unit).convert_compound_unit_to_si!
|
173
173
|
else
|
174
|
-
self.to(unit.si_unit)
|
174
|
+
self.to(@unit.si_unit)
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
178
|
def convert_compound_unit_to_si!
|
179
|
-
until
|
180
|
-
unit =
|
179
|
+
until @unit.is_base_quantity_si_unit? do
|
180
|
+
unit = @unit.base_units.find do |base|
|
181
181
|
!base.is_base_quantity_si_unit?
|
182
182
|
end.unit
|
183
183
|
self.convert_compound_unit_to_non_equivalent_unit!(unit.si_unit)
|
@@ -192,9 +192,9 @@ module Quantify
|
|
192
192
|
#
|
193
193
|
def add_or_subtract!(operator,other)
|
194
194
|
if other.is_a? Quantity
|
195
|
-
other = other.to(unit) if other.unit.is_alternative_for?(unit)
|
196
|
-
if
|
197
|
-
@value = value.send operator, other.value
|
195
|
+
other = other.to(@unit) if other.unit.is_alternative_for?(@unit)
|
196
|
+
if @unit.is_equivalent_to? other.unit
|
197
|
+
@value = @value.send operator, other.value
|
198
198
|
return self
|
199
199
|
else
|
200
200
|
raise Quantify::Exceptions::InvalidObjectError "Cannot add or subtract Quantities with different dimensions"
|
@@ -206,11 +206,11 @@ module Quantify
|
|
206
206
|
|
207
207
|
def multiply_or_divide!(operator,other)
|
208
208
|
if other.kind_of? Numeric
|
209
|
-
@value = value.send(operator,other)
|
209
|
+
@value = @value.send(operator,other)
|
210
210
|
return self
|
211
211
|
elsif other.kind_of? Quantity
|
212
|
-
@unit = unit.send(operator,other.unit).or_equivalent
|
213
|
-
@value = value.send(operator,other.value)
|
212
|
+
@unit = @unit.send(operator,other.unit).or_equivalent
|
213
|
+
@value = @value.send(operator,other.value)
|
214
214
|
return self
|
215
215
|
else
|
216
216
|
raise Quantify::Exceptions::InvalidArgumentError "Cannot multiply or divide a Quantity by a non-Quantity or non-Numeric object"
|
@@ -219,8 +219,8 @@ module Quantify
|
|
219
219
|
|
220
220
|
def pow!(power)
|
221
221
|
raise Exceptions::InvalidArgumentError, "Argument must be an integer" unless power.is_a? Integer
|
222
|
-
@value = value ** power
|
223
|
-
@unit = unit ** power
|
222
|
+
@value = @value ** power
|
223
|
+
@unit = @unit ** power
|
224
224
|
return self
|
225
225
|
end
|
226
226
|
|
@@ -241,34 +241,34 @@ module Quantify
|
|
241
241
|
end
|
242
242
|
|
243
243
|
def add(other)
|
244
|
-
Quantity.new(value
|
244
|
+
Quantity.new(@value,@unit).add!(other)
|
245
245
|
end
|
246
246
|
alias :+ :add
|
247
247
|
|
248
248
|
def subtract(other)
|
249
|
-
Quantity.new(value
|
249
|
+
Quantity.new(@value,@unit).subtract!(other)
|
250
250
|
end
|
251
251
|
alias :- :subtract
|
252
252
|
|
253
253
|
def multiply(other)
|
254
|
-
Quantity.new(value
|
254
|
+
Quantity.new(@value,@unit).multiply!(other)
|
255
255
|
end
|
256
256
|
alias :times :multiply
|
257
257
|
alias :* :multiply
|
258
258
|
|
259
259
|
def divide(other)
|
260
|
-
Quantity.new(value
|
260
|
+
Quantity.new(@value,@unit).divide!(other)
|
261
261
|
end
|
262
262
|
alias :/ :divide
|
263
263
|
|
264
264
|
def pow(power)
|
265
|
-
Quantity.new(value
|
265
|
+
Quantity.new(@value,@unit).pow!(power)
|
266
266
|
end
|
267
267
|
alias :** :pow
|
268
268
|
|
269
269
|
def rationalize_units
|
270
|
-
return self unless unit.is_a? Unit::Compound
|
271
|
-
self.to unit.clone.rationalize_base_units!
|
270
|
+
return self unless @unit.is_a? Unit::Compound
|
271
|
+
self.to @unit.clone.rationalize_base_units!
|
272
272
|
end
|
273
273
|
|
274
274
|
def rationalize_units!
|
@@ -279,7 +279,7 @@ module Quantify
|
|
279
279
|
end
|
280
280
|
|
281
281
|
def cancel_base_units!(*units)
|
282
|
-
@unit = unit.cancel_base_units!(*units) if unit.is_compound_unit?
|
282
|
+
@unit = @unit.cancel_base_units!(*units) if @unit.is_compound_unit?
|
283
283
|
return self
|
284
284
|
end
|
285
285
|
|
@@ -304,8 +304,8 @@ module Quantify
|
|
304
304
|
def <=>(other)
|
305
305
|
raise Exceptions::InvalidArgumentError unless other.is_a? Quantity
|
306
306
|
raise Exceptions::InvalidArgumentError unless other.unit.is_alternative_for?(unit)
|
307
|
-
other = other.to unit
|
308
|
-
value.to_f <=> other.value.to_f
|
307
|
+
other = other.to @unit
|
308
|
+
@value.to_f <=> other.value.to_f
|
309
309
|
end
|
310
310
|
|
311
311
|
def ===(range)
|
@@ -45,7 +45,7 @@ module Quantify
|
|
45
45
|
#
|
46
46
|
def self.construct(unit,&block)
|
47
47
|
new_unit = self.new unit.to_hash
|
48
|
-
|
48
|
+
block.call(new_unit) if block_given?
|
49
49
|
return new_unit
|
50
50
|
end
|
51
51
|
|
@@ -64,8 +64,7 @@ module Quantify
|
|
64
64
|
class_eval &block if block
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
attr_reader :dimensions
|
67
|
+
attr_reader :name, :symbol, :label, :factor, :dimensions
|
69
68
|
attr_reader :acts_as_alternative_unit, :acts_as_equivalent_unit
|
70
69
|
|
71
70
|
# Create a new Unit::Base instance.
|
@@ -100,34 +99,88 @@ module Quantify
|
|
100
99
|
# representation in the Dimensions class. This dimensions attribute is to
|
101
100
|
# provide much of the unit functionality
|
102
101
|
#
|
103
|
-
def initialize(options=nil)
|
102
|
+
def initialize(options=nil,&block)
|
104
103
|
@acts_as_alternative_unit = true
|
105
104
|
@acts_as_equivalent_unit = false
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
self.factor = 1.0
|
106
|
+
self.symbol = nil
|
107
|
+
self.label = nil
|
109
108
|
if options.is_a? Hash
|
110
109
|
self.dimensions = options[:dimensions] || options[:physical_quantity]
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
110
|
+
self.name = options[:name]
|
111
|
+
self.factor = options[:factor] if options[:factor]
|
112
|
+
self.symbol = options[:symbol] if options[:symbol]
|
113
|
+
self.label = options[:label] if options[:label]
|
115
114
|
end
|
116
|
-
|
115
|
+
block.call(self) if block_given?
|
117
116
|
valid?
|
118
117
|
end
|
119
118
|
|
119
|
+
# Set the dimensions attribute of self. Valid arguments passed in are (1)
|
120
|
+
# instances of the Dimensions class; or (2) string or symbol matching
|
121
|
+
# the physical quantity attribute of a known Dimensions object.
|
122
|
+
#
|
120
123
|
def dimensions=(dimensions)
|
121
124
|
if dimensions.is_a? Dimensions
|
122
125
|
@dimensions = dimensions
|
123
|
-
elsif dimensions.is_a?
|
124
|
-
@dimensions = Dimensions.for
|
126
|
+
elsif dimensions.is_a?(String) || dimensions.is_a?(Symbol)
|
127
|
+
@dimensions = Dimensions.for(dimensions)
|
125
128
|
else
|
126
129
|
raise Exceptions::InvalidArgumentError, "Unknown physical_quantity specified"
|
127
130
|
end
|
128
131
|
end
|
129
132
|
alias :physical_quantity= :dimensions=
|
130
133
|
|
134
|
+
# Set the name attribute of self. Names are stringified, singlularized and
|
135
|
+
# stripped of underscores. Superscripts are formatted according to the
|
136
|
+
# configuration found in Quantify.use_superscript_characters? Names are NOT
|
137
|
+
# case sensitive (retrieving units by name can use any or mixed cases).
|
138
|
+
#
|
139
|
+
def name=(name)
|
140
|
+
name = name.to_s.remove_underscores.singularize
|
141
|
+
@name = Quantify.use_superscript_characters? ? name.with_superscript_characters : name.without_superscript_characters
|
142
|
+
end
|
143
|
+
|
144
|
+
# Set the symbol attribute of self. Symbols are stringified and stripped of
|
145
|
+
# any underscores. Superscripts are formatted according to the configuration
|
146
|
+
# found in Quantify.use_superscript_characters?
|
147
|
+
#
|
148
|
+
# Conventional symbols for units and unit prefixes prescribe case clearly
|
149
|
+
# (e.g. 'm' => metre, 'M' => mega-; 'g' => gram, 'G' => giga-) and
|
150
|
+
# therefore symbols ARE case senstive.
|
151
|
+
#
|
152
|
+
def symbol=(symbol)
|
153
|
+
symbol = symbol.to_s.remove_underscores
|
154
|
+
@symbol = Quantify.use_superscript_characters? ? symbol.with_superscript_characters : symbol.without_superscript_characters
|
155
|
+
end
|
156
|
+
|
157
|
+
# Set the label attribute of self. This represents the unique identifier for
|
158
|
+
# the unit, and follows JScience for many standard units and general
|
159
|
+
# formatting (e.g. underscores, forward slashes, middots).
|
160
|
+
#
|
161
|
+
# Labels are stringified and superscripts are formatted according to the
|
162
|
+
# configuration found in Quantify.use_superscript_characters?
|
163
|
+
#
|
164
|
+
# Labels are a unique consistent reference and are therefore case senstive.
|
165
|
+
#
|
166
|
+
def label=(label)
|
167
|
+
label = label.to_s.gsub(" ","_")
|
168
|
+
@label = Quantify.use_superscript_characters? ? label.with_superscript_characters : label.without_superscript_characters
|
169
|
+
end
|
170
|
+
|
171
|
+
# Refresh the name, symbol and label attributes of self with respect to the
|
172
|
+
# configuration found in Quantify.use_superscript_characters?
|
173
|
+
#
|
174
|
+
def refresh_identifiers!
|
175
|
+
self.name = name
|
176
|
+
self.symbol = symbol
|
177
|
+
self.label = label
|
178
|
+
end
|
179
|
+
|
180
|
+
def factor=(factor)
|
181
|
+
@factor = factor.to_f
|
182
|
+
end
|
183
|
+
|
131
184
|
# Permits a block to be used, operating on self. This is useful for modifying
|
132
185
|
# the attributes of an already instantiated unit, especially when defining
|
133
186
|
# units on the basis of operation on existing units for adding specific
|
@@ -139,17 +192,17 @@ module Quantify
|
|
139
192
|
# unit.name = 'pound per square inch'
|
140
193
|
# end
|
141
194
|
#
|
142
|
-
def configure
|
143
|
-
|
195
|
+
def configure(&block)
|
196
|
+
block.call(self) if block_given?
|
144
197
|
return self if valid?
|
145
198
|
end
|
146
199
|
|
147
200
|
# Similar to #configure but makes the new unit configuration the canonical
|
148
201
|
# unit for self.label
|
149
202
|
#
|
150
|
-
def configure_as_canonical
|
203
|
+
def configure_as_canonical(&block)
|
151
204
|
unload if loaded?
|
152
|
-
configure
|
205
|
+
configure(&block) if block_given?
|
153
206
|
make_canonical
|
154
207
|
end
|
155
208
|
|
@@ -158,8 +211,8 @@ module Quantify
|
|
158
211
|
# If a block is given, the unit can be configured prior to loading, in a
|
159
212
|
# similar to way to the #configure method.
|
160
213
|
#
|
161
|
-
def load
|
162
|
-
|
214
|
+
def load(&block)
|
215
|
+
block.call(self) if block_given?
|
163
216
|
raise Exceptions::InvalidArgumentError, "A unit with the same label: #{self.name}) already exists" if loaded?
|
164
217
|
Quantify::Unit.units << self if valid?
|
165
218
|
return self
|
@@ -224,14 +277,14 @@ module Quantify
|
|
224
277
|
end
|
225
278
|
|
226
279
|
def pluralized_name
|
227
|
-
|
280
|
+
@name.pluralize
|
228
281
|
end
|
229
282
|
|
230
283
|
# Determine if the unit represents one of the base quantities, length,
|
231
284
|
# mass, time, temperature, etc.
|
232
285
|
#
|
233
286
|
def is_base_unit?
|
234
|
-
Dimensions::BASE_QUANTITIES.map
|
287
|
+
Dimensions::BASE_QUANTITIES.map {|base| base.remove_underscores }.include? self.measures
|
235
288
|
end
|
236
289
|
|
237
290
|
# Determine if the unit is THE canonical SI unit for a base quantity (length,
|
@@ -239,20 +292,20 @@ module Quantify
|
|
239
292
|
# returning true only for metre, kilogram, second, Kelvin, etc.
|
240
293
|
#
|
241
294
|
def is_base_quantity_si_unit?
|
242
|
-
is_si_unit?
|
295
|
+
is_si_unit? && is_base_unit? && is_benchmark_unit?
|
243
296
|
end
|
244
297
|
|
245
298
|
# Determine is the unit is a derived unit - that is, a unit made up of more
|
246
299
|
# than one of the base quantities
|
247
300
|
#
|
248
301
|
def is_derived_unit?
|
249
|
-
|
302
|
+
!is_base_unit?
|
250
303
|
end
|
251
304
|
|
252
305
|
# Determine if the unit is a prefixed unit
|
253
306
|
def is_prefixed_unit?
|
254
|
-
return true if valid_prefixes.size > 0
|
255
|
-
self.name =~ /\A(#{valid_prefixes.map
|
307
|
+
return true if valid_prefixes.size > 0 &&
|
308
|
+
self.name =~ /\A(#{valid_prefixes.map {|p| p.name}.join("|")})/
|
256
309
|
return false
|
257
310
|
end
|
258
311
|
|
@@ -263,7 +316,7 @@ module Quantify
|
|
263
316
|
# containing a prefix. This oddity makes this method useful/necessary.
|
264
317
|
#
|
265
318
|
def is_benchmark_unit?
|
266
|
-
|
319
|
+
@factor == 1.0
|
267
320
|
end
|
268
321
|
|
269
322
|
# Determine is a unit object represents an SI named unit
|
@@ -283,18 +336,18 @@ module Quantify
|
|
283
336
|
end
|
284
337
|
|
285
338
|
def is_dimensionless?
|
286
|
-
|
339
|
+
@dimensions.is_dimensionless?
|
287
340
|
end
|
288
341
|
|
289
|
-
# Determine if self is
|
342
|
+
# Determine if self is equivalent to another. Equivalency is based on
|
290
343
|
# representing the same physical quantity (i.e. dimensions) and the same
|
291
344
|
# factor and scaling values.
|
292
345
|
#
|
293
|
-
# Unit.metre.
|
346
|
+
# Unit.metre.is_equivalent_to? Unit.foot #=> false
|
294
347
|
#
|
295
|
-
# Unit.metre.
|
348
|
+
# Unit.metre.is_equivalent_to? Unit.gram #=> false
|
296
349
|
#
|
297
|
-
# Unit.metre.
|
350
|
+
# Unit.metre.is_equivalent_to? Unit.metre #=> true
|
298
351
|
#
|
299
352
|
# The base_units attr of Compound units are not compared. Neither are the
|
300
353
|
# names or symbols. This is because we want to recognise cases where units
|
@@ -303,25 +356,24 @@ module Quantify
|
|
303
356
|
# example, if we build a unit for energy using only SI units, we want to
|
304
357
|
# recognise this as a joule, rather than a kg m^2 s^-2, e.g.
|
305
358
|
#
|
306
|
-
# (Unit.kg*Unit.m*Unit.m/Unit.s/Unit.s).
|
359
|
+
# (Unit.kg*Unit.m*Unit.m/Unit.s/Unit.s).is_equivalent_to? Unit.joule
|
307
360
|
#
|
308
361
|
# #=> true
|
309
362
|
#
|
310
|
-
def
|
363
|
+
def is_equivalent_to?(other)
|
311
364
|
[:dimensions,:factor,:scaling].all? do |attr|
|
312
365
|
self.send(attr) == other.send(attr)
|
313
366
|
end
|
314
367
|
end
|
315
|
-
|
316
|
-
alias :== :is_same_as?
|
317
368
|
|
318
369
|
# Check if unit has the identity as another, i.e. the same label. This is
|
319
370
|
# used to determine if a unit with the same accessors already exists in
|
320
371
|
# the module variable @@units
|
321
372
|
#
|
322
373
|
def has_same_identity_as?(other)
|
323
|
-
|
374
|
+
@label == other.label && !@label.nil?
|
324
375
|
end
|
376
|
+
alias :== :has_same_identity_as?
|
325
377
|
|
326
378
|
# Determine if another unit is an alternative unit for self, i.e. do the two
|
327
379
|
# units represent the same physical quantity. This is established by compraing
|
@@ -334,7 +386,7 @@ module Quantify
|
|
334
386
|
# Unit.metre.is_alternative_for? Unit.metre #=> true
|
335
387
|
#
|
336
388
|
def is_alternative_for?(other)
|
337
|
-
other.dimensions ==
|
389
|
+
other.dimensions == @dimensions
|
338
390
|
end
|
339
391
|
|
340
392
|
# List the alternative units for self, i.e. the other units which share
|
@@ -348,8 +400,8 @@ module Quantify
|
|
348
400
|
# are returned within the array
|
349
401
|
#
|
350
402
|
def alternatives(by=nil)
|
351
|
-
|
352
|
-
unit.
|
403
|
+
@dimensions.units(nil).reject do |unit|
|
404
|
+
unit.is_equivalent_to?(self) || !unit.acts_as_alternative_unit
|
353
405
|
end.map(&by)
|
354
406
|
end
|
355
407
|
|
@@ -357,18 +409,18 @@ module Quantify
|
|
357
409
|
# by self, e.g.
|
358
410
|
#
|
359
411
|
def si_unit
|
360
|
-
|
412
|
+
@dimensions.si_unit
|
361
413
|
end
|
362
414
|
|
363
415
|
def valid?
|
364
|
-
return true if valid_descriptors?
|
416
|
+
return true if valid_descriptors? && valid_dimensions?
|
365
417
|
raise Exceptions::InvalidArgumentError, "Unit definition must include a name, a symbol, a label and physical quantity"
|
366
418
|
end
|
367
419
|
|
368
420
|
def valid_descriptors?
|
369
421
|
[:name, :symbol, :label].all? do |attr|
|
370
422
|
attribute = send(attr)
|
371
|
-
attribute.is_a?
|
423
|
+
attribute.is_a?(String) && !attribute.empty?
|
372
424
|
end
|
373
425
|
end
|
374
426
|
|
@@ -396,7 +448,7 @@ module Quantify
|
|
396
448
|
# "T", "P" ... ]
|
397
449
|
#
|
398
450
|
def valid_prefixes(by=nil)
|
399
|
-
return
|
451
|
+
return [] if self.is_compound_unit?
|
400
452
|
return Unit::Prefix.si_prefixes.map(&by) if is_si_unit?
|
401
453
|
return Unit::Prefix.non_si_prefixes.map(&by) if is_non_si_unit?
|
402
454
|
end
|
@@ -406,7 +458,7 @@ module Quantify
|
|
406
458
|
#
|
407
459
|
def multiply(other)
|
408
460
|
options = []
|
409
|
-
self.instance_of?(Unit::Compound) ? options +=
|
461
|
+
self.instance_of?(Unit::Compound) ? options += @base_units : options << self
|
410
462
|
other.instance_of?(Unit::Compound) ? options += other.base_units : options << other
|
411
463
|
Unit::Compound.new(*options)
|
412
464
|
end
|
@@ -421,7 +473,7 @@ module Quantify
|
|
421
473
|
#
|
422
474
|
def divide(other)
|
423
475
|
options = []
|
424
|
-
self.instance_of?(Unit::Compound) ? options +=
|
476
|
+
self.instance_of?(Unit::Compound) ? options += @base_units : options << self
|
425
477
|
|
426
478
|
if other.instance_of? Unit::Compound
|
427
479
|
options += other.base_units.map { |base| base.index *= -1; base }
|
@@ -461,7 +513,7 @@ module Quantify
|
|
461
513
|
# of self, complete with modified name, symbol, factor, etc..
|
462
514
|
#
|
463
515
|
def with_prefix(name_or_symbol)
|
464
|
-
if
|
516
|
+
if @name =~ /\A(#{valid_prefixes(:name).join("|")})/
|
465
517
|
raise Exceptions::InvalidArgumentError, "Cannot add prefix where one already exists: #{self.name}"
|
466
518
|
end
|
467
519
|
|
@@ -469,11 +521,11 @@ module Quantify
|
|
469
521
|
|
470
522
|
unless prefix.nil?
|
471
523
|
new_unit_options = {}
|
472
|
-
new_unit_options[:name] = "#{prefix.name}#{
|
473
|
-
new_unit_options[:symbol] = "#{prefix.symbol}#{
|
474
|
-
new_unit_options[:label] = "#{prefix.symbol}#{
|
475
|
-
new_unit_options[:factor] = prefix.factor *
|
476
|
-
new_unit_options[:physical_quantity] =
|
524
|
+
new_unit_options[:name] = "#{prefix.name}#{@name}"
|
525
|
+
new_unit_options[:symbol] = "#{prefix.symbol}#{@symbol}"
|
526
|
+
new_unit_options[:label] = "#{prefix.symbol}#{@label}"
|
527
|
+
new_unit_options[:factor] = prefix.factor * @factor
|
528
|
+
new_unit_options[:physical_quantity] = @dimensions
|
477
529
|
self.class.new(new_unit_options)
|
478
530
|
else
|
479
531
|
raise Exceptions::InvalidArgumentError, "Prefix unit is not known: #{prefix}"
|
@@ -503,7 +555,7 @@ module Quantify
|
|
503
555
|
# (1/unit).symbol #=> "m^-1"
|
504
556
|
#
|
505
557
|
def coerce(object)
|
506
|
-
if object.kind_of?
|
558
|
+
if object.kind_of?(Numeric) && object == 1
|
507
559
|
return Unit.unity, self
|
508
560
|
else
|
509
561
|
raise Exceptions::InvalidArgumentError, "Cannot coerce #{self.class} into #{object.class}"
|
@@ -520,9 +572,9 @@ module Quantify
|
|
520
572
|
#
|
521
573
|
def initialize_copy(source)
|
522
574
|
super
|
523
|
-
instance_variable_set("@dimensions", dimensions.clone)
|
575
|
+
instance_variable_set("@dimensions", @dimensions.clone)
|
524
576
|
if self.is_compound_unit?
|
525
|
-
instance_variable_set("@base_units", base_units.map {|base| base.clone })
|
577
|
+
instance_variable_set("@base_units", @base_units.map {|base| base.clone })
|
526
578
|
end
|
527
579
|
end
|
528
580
|
|
@@ -537,12 +589,12 @@ module Quantify
|
|
537
589
|
# is equaivalent to Unit.m.alternatives :name
|
538
590
|
#
|
539
591
|
def method_missing(method, *args, &block)
|
540
|
-
if method.to_s =~ /(to_)(.*)/
|
541
|
-
return self.with_prefix
|
542
|
-
elsif method.to_s =~ /(alternatives_by_)(.*)/
|
543
|
-
return self.alternatives
|
544
|
-
elsif method.to_s =~ /(valid_prefixes_by_)(.*)/
|
545
|
-
return self.valid_prefixes
|
592
|
+
if method.to_s =~ /(to_)(.*)/ && prefix = Prefix.for($2.to_sym)
|
593
|
+
return self.with_prefix(prefix)
|
594
|
+
elsif method.to_s =~ /(alternatives_by_)(.*)/ && self.respond_to?($2.to_sym)
|
595
|
+
return self.alternatives($2.to_sym)
|
596
|
+
elsif method.to_s =~ /(valid_prefixes_by_)(.*)/ && Prefix::Base.instance_methods.include?($2.to_s)
|
597
|
+
return self.valid_prefixes($2.to_sym)
|
546
598
|
end
|
547
599
|
super
|
548
600
|
end
|