quantify 1.0.5 → 1.1.0
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/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
|