quantify 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +14 -0
- data/README +10 -10
- data/Rakefile +65 -0
- data/VERSION +1 -0
- data/lib/quantify/config.rb +10 -14
- data/lib/quantify/dimensions.rb +1 -1
- data/lib/quantify/quantify.rb +2 -35
- data/lib/quantify/unit/base_unit.rb +29 -25
- data/lib/quantify/unit/compound_base_unit.rb +21 -22
- data/lib/quantify/unit/compound_base_unit_list.rb +260 -0
- data/lib/quantify/unit/compound_unit.rb +78 -218
- data/lib/quantify/unit/unit.rb +119 -4
- data/lib/quantify.rb +1 -0
- data/quantify.gemspec +90 -0
- data/spec/compound_unit_spec.rb +124 -13
- data/spec/quantify_spec.rb +0 -14
- data/spec/quantity_spec.rb +6 -6
- data/spec/unit_spec.rb +57 -39
- metadata +130 -32
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@quantify
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "activesupport"
|
4
|
+
gem "i18n"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "bundler", "~> 1.0.0"
|
10
|
+
gem "jeweler", "~> 1.6.4"
|
11
|
+
gem 'rspec', '1.3.0'
|
12
|
+
gem 'rcov'
|
13
|
+
gem 'rspec_spinner', '1.1.3'
|
14
|
+
end
|
data/README
CHANGED
@@ -9,7 +9,7 @@ Quick introduction
|
|
9
9
|
|
10
10
|
12.feet + 12.feet => "24.0 feet"
|
11
11
|
|
12
|
-
6.m ** 2 => "36.0 m
|
12
|
+
6.m ** 2 => "36.0 m²"
|
13
13
|
|
14
14
|
100.km / 2.h => "50 kilometers per hour"
|
15
15
|
|
@@ -124,11 +124,11 @@ Convert a quantity to a different unit
|
|
124
124
|
|
125
125
|
Convert the units of a quantity with a compound unit
|
126
126
|
|
127
|
-
speed = 70.mi/1.h => "70.0 mi
|
127
|
+
speed = 70.mi/1.h => "70.0 mi/h"
|
128
128
|
|
129
|
-
speed_in_kms = speed.to_km => "112.65408 km
|
129
|
+
speed_in_kms = speed.to_km => "112.65408 km/h"
|
130
130
|
|
131
|
-
speed_in_mins = speed_in_kms.to_min => "1.877568 km
|
131
|
+
speed_in_mins = speed_in_kms.to_min => "1.877568 km/min"
|
132
132
|
|
133
133
|
# Note: all of the above results are string representations of the actual
|
134
134
|
# objects which result from these operations.
|
@@ -180,18 +180,18 @@ and the user may not necessarily prefer a conversion into consistent mass units
|
|
180
180
|
area = 12.yd * 36.ft => <Quantify::Quantity:0xb7332bbc ... >
|
181
181
|
area.to_s => "432.0 yd ft"
|
182
182
|
area.to_yd
|
183
|
-
area.to_s => "144.0 yd
|
183
|
+
area.to_s => "144.0 yd²"
|
184
184
|
|
185
185
|
# Alternatively, all units within the numerator and denominator respectively
|
186
186
|
# can be standardized.
|
187
187
|
quantity = (12.ft*8.mi)/(1.s*8.min)
|
188
|
-
quantity.to_s => 12.0 ft mi
|
188
|
+
quantity.to_s => 12.0 ft mi/s min
|
189
189
|
quantity.rationalize_units!
|
190
|
-
quantity.to_s => 1056.0 ft
|
190
|
+
quantity.to_s => 1056.0 ft²/s²
|
191
191
|
|
192
192
|
# A quantity with arbitrary cancelable units can be cancelled manually
|
193
193
|
quantity = (12.m**6) / 2.m**2
|
194
|
-
quantity.to_s => "746496.0 m^6
|
194
|
+
quantity.to_s => "746496.0 m^6/m²"
|
195
195
|
quantity.cancel_base_units! :m
|
196
196
|
quantity.to_s => "746496.0 m^4"
|
197
197
|
|
@@ -228,14 +228,14 @@ Initialize a unit object
|
|
228
228
|
|
229
229
|
another_unit = unit / other_unit => <Quantify::Unit::Compound:0xb74af323 ... >
|
230
230
|
another_unit.name => 'kilometer per hour'
|
231
|
-
another_unit.symbol => 'km
|
231
|
+
another_unit.symbol => 'km/h'
|
232
232
|
another_unit.measures => 'velocity'
|
233
233
|
another_unit.base_units.map(&:name) => ['kilogram','hour']
|
234
234
|
|
235
235
|
last_unit = Unit.m
|
236
236
|
last.unit.measures => 'length'
|
237
237
|
square = last_unit ** 2 => <Quantify::Unit::Compound:0xb446f12f ... >
|
238
|
-
square.symbol => 'm
|
238
|
+
square.symbol => 'm²'
|
239
239
|
square.measures => 'area'
|
240
240
|
|
241
241
|
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
require 'spec'
|
14
|
+
require 'spec/rake/spectask'
|
15
|
+
|
16
|
+
task :default => [:spec]
|
17
|
+
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
t.rcov = true
|
22
|
+
t.rcov_opts = ['--exclude', 'spec,/*ruby*,']
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'jeweler'
|
26
|
+
|
27
|
+
Jeweler::Tasks.new do |gem|
|
28
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
29
|
+
gem.name = "quantify"
|
30
|
+
gem.homepage = "https://github.com/spatchcock/quantify"
|
31
|
+
gem.license = "MIT"
|
32
|
+
gem.summary = %Q{Support for handling physical quantities, unit conversions, etc}
|
33
|
+
gem.description = %Q{A gem to support physical quantities and unit conversions}
|
34
|
+
gem.email = "andrew.berkeley.is@googlemail.com"
|
35
|
+
gem.authors = ["Andrew Berkeley"]
|
36
|
+
# dependencies defined in Gemfile
|
37
|
+
end
|
38
|
+
Jeweler::RubygemsDotOrgTasks.new
|
39
|
+
|
40
|
+
require 'rake/testtask'
|
41
|
+
Rake::TestTask.new(:test) do |test|
|
42
|
+
test.libs << 'lib' << 'test'
|
43
|
+
test.pattern = 'test/**/test_*.rb'
|
44
|
+
test.verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
require 'rcov/rcovtask'
|
48
|
+
Rcov::RcovTask.new do |test|
|
49
|
+
test.libs << 'test'
|
50
|
+
test.pattern = 'test/**/test_*.rb'
|
51
|
+
test.verbose = true
|
52
|
+
test.rcov_opts << '--exclude "gems/*"'
|
53
|
+
end
|
54
|
+
|
55
|
+
task :default => :test
|
56
|
+
|
57
|
+
require 'rake/rdoctask'
|
58
|
+
Rake::RDocTask.new do |rdoc|
|
59
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
60
|
+
|
61
|
+
rdoc.rdoc_dir = 'rdoc'
|
62
|
+
rdoc.title = "quantify #{version}"
|
63
|
+
rdoc.rdoc_files.include('README*')
|
64
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
65
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.2.0
|
data/lib/quantify/config.rb
CHANGED
@@ -177,24 +177,24 @@ Unit::SI.configure do
|
|
177
177
|
load :name => 'kilogram', :physical_quantity => :mass, :symbol => 'kg', :label => 'kg'
|
178
178
|
load :name => 'gram', :physical_quantity => :mass, :symbol => 'g', :label => 'g', :factor => 1e-3
|
179
179
|
|
180
|
-
# Define units on the basis of
|
180
|
+
# Define compound units on the basis of SI units
|
181
181
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
182
|
+
(metre**2).load
|
183
|
+
(metre**3).load
|
184
|
+
(metre/second).load
|
185
|
+
(metre/second**2).load
|
186
186
|
|
187
|
-
|
187
|
+
(1/centimetre).configure do |unit|
|
188
188
|
unit.name = 'inverse centimetre'
|
189
|
-
end
|
189
|
+
end.load
|
190
190
|
|
191
|
-
|
191
|
+
(centimetre/second**2).configure do |unit|
|
192
192
|
unit.name = 'galileo'
|
193
193
|
unit.symbol = 'Gal'
|
194
194
|
unit.label = 'galileo'
|
195
|
-
end
|
195
|
+
end.load
|
196
196
|
|
197
|
-
# add required prefixed
|
197
|
+
# add required prefixed units individually
|
198
198
|
|
199
199
|
kilometre.load
|
200
200
|
|
@@ -215,8 +215,6 @@ Unit::SI.configure do
|
|
215
215
|
|
216
216
|
si_base_units.each { |unit| unit.acts_as_equivalent_unit = true }
|
217
217
|
|
218
|
-
cubic_metre.acts_as_equivalent_unit = true
|
219
|
-
|
220
218
|
joule.acts_as_equivalent_unit = true
|
221
219
|
|
222
220
|
newton.acts_as_equivalent_unit = true
|
@@ -225,8 +223,6 @@ Unit::SI.configure do
|
|
225
223
|
|
226
224
|
pascal.acts_as_equivalent_unit = true
|
227
225
|
|
228
|
-
square_metre.acts_as_equivalent_unit = true
|
229
|
-
|
230
226
|
end
|
231
227
|
|
232
228
|
Unit::NonSI.configure do
|
data/lib/quantify/dimensions.rb
CHANGED
@@ -51,7 +51,7 @@ module Quantify
|
|
51
51
|
#
|
52
52
|
def self.base_dimensions
|
53
53
|
@@dimensions.select do |dimensions|
|
54
|
-
BASE_QUANTITIES.map {|
|
54
|
+
BASE_QUANTITIES.map {|quantity| quantity.remove_underscores}.include?(dimensions.describe)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
data/lib/quantify/quantify.rb
CHANGED
@@ -1,40 +1,7 @@
|
|
1
1
|
module Quantify
|
2
2
|
|
3
|
-
def self.configure
|
4
|
-
self.module_eval
|
5
|
-
end
|
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
|
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
|
-
)
|
3
|
+
def self.configure(&block)
|
4
|
+
self.module_eval(&block) if block
|
38
5
|
end
|
39
6
|
|
40
7
|
module ExtendedMethods
|
@@ -52,13 +52,13 @@ module Quantify
|
|
52
52
|
# Syntactic sugar for defining the units known to the system, enabling the
|
53
53
|
# required associated units to be loaded at runtime, e.g.
|
54
54
|
#
|
55
|
-
#
|
55
|
+
# Unit::[Base|SI|NonSI].configure do |config|
|
56
56
|
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
57
|
+
# load :name => :metre, :physical_quantity => :length
|
58
|
+
# load :name => 'hectare', :physical_quantity => :area, :factor => 10000
|
59
|
+
# load :name => :watt, :physical_quantity => :power, :symbol => 'W'
|
60
60
|
#
|
61
|
-
#
|
61
|
+
# end
|
62
62
|
#
|
63
63
|
def self.configure &block
|
64
64
|
class_eval &block if block
|
@@ -138,7 +138,7 @@ module Quantify
|
|
138
138
|
#
|
139
139
|
def name=(name)
|
140
140
|
name = name.to_s.remove_underscores.singularize
|
141
|
-
@name =
|
141
|
+
@name = Unit.use_superscript_characters? ? name.with_superscript_characters : name.without_superscript_characters
|
142
142
|
end
|
143
143
|
|
144
144
|
# Set the symbol attribute of self. Symbols are stringified and stripped of
|
@@ -151,7 +151,7 @@ module Quantify
|
|
151
151
|
#
|
152
152
|
def symbol=(symbol)
|
153
153
|
symbol = symbol.to_s.remove_underscores
|
154
|
-
@symbol =
|
154
|
+
@symbol = Unit.use_superscript_characters? ? symbol.with_superscript_characters : symbol.without_superscript_characters
|
155
155
|
end
|
156
156
|
|
157
157
|
# Set the label attribute of self. This represents the unique identifier for
|
@@ -165,13 +165,13 @@ module Quantify
|
|
165
165
|
#
|
166
166
|
def label=(label)
|
167
167
|
label = label.to_s.gsub(" ","_")
|
168
|
-
@label =
|
168
|
+
@label = Unit.use_superscript_characters? ? label.with_superscript_characters : label.without_superscript_characters
|
169
169
|
end
|
170
170
|
|
171
171
|
# Refresh the name, symbol and label attributes of self with respect to the
|
172
172
|
# configuration found in Quantify.use_superscript_characters?
|
173
173
|
#
|
174
|
-
def
|
174
|
+
def refresh_attributes
|
175
175
|
self.name = name
|
176
176
|
self.symbol = symbol
|
177
177
|
self.label = label
|
@@ -448,7 +448,7 @@ module Quantify
|
|
448
448
|
# "T", "P" ... ]
|
449
449
|
#
|
450
450
|
def valid_prefixes(by=nil)
|
451
|
-
return [] if
|
451
|
+
return [] if is_compound_unit? && has_multiple_base_units?
|
452
452
|
return Unit::Prefix.si_prefixes.map(&by) if is_si_unit?
|
453
453
|
return Unit::Prefix.non_si_prefixes.map(&by) if is_non_si_unit?
|
454
454
|
end
|
@@ -513,25 +513,20 @@ module Quantify
|
|
513
513
|
# of self, complete with modified name, symbol, factor, etc..
|
514
514
|
#
|
515
515
|
def with_prefix(name_or_symbol)
|
516
|
-
|
517
|
-
|
518
|
-
end
|
516
|
+
raise Exceptions::InvalidArgumentError, "No valid prefixes exist for unit: #{self.name}" if valid_prefixes.empty?
|
517
|
+
raise Exceptions::InvalidArgumentError, "Cannot add prefix where one already exists: #{self.name}" if @name =~ /\A(#{valid_prefixes(:name).join("|")})/
|
519
518
|
|
520
519
|
prefix = Unit::Prefix.for(name_or_symbol,valid_prefixes)
|
521
|
-
|
522
|
-
|
523
|
-
new_unit_options = {}
|
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
|
529
|
-
self.class.new(new_unit_options)
|
520
|
+
if !prefix.nil?
|
521
|
+
self.class.new(options_for_prefixed_version(prefix))
|
530
522
|
else
|
531
523
|
raise Exceptions::InvalidArgumentError, "Prefix unit is not known: #{prefix}"
|
532
524
|
end
|
533
525
|
end
|
534
526
|
|
527
|
+
# Return an array of new unit instances based upon self, together with the
|
528
|
+
# prefixes specified by <tt>prefixes</tt>
|
529
|
+
#
|
535
530
|
def with_prefixes(*prefixes)
|
536
531
|
[prefixes].map { |prefix| self.with_prefix(prefix) }
|
537
532
|
end
|
@@ -561,6 +556,18 @@ module Quantify
|
|
561
556
|
raise Exceptions::InvalidArgumentError, "Cannot coerce #{self.class} into #{object.class}"
|
562
557
|
end
|
563
558
|
end
|
559
|
+
|
560
|
+
private
|
561
|
+
|
562
|
+
def options_for_prefixed_version(prefix)
|
563
|
+
options = {}
|
564
|
+
options[:name] = "#{prefix.name}#{@name}"
|
565
|
+
options[:symbol] = "#{prefix.symbol}#{@symbol}"
|
566
|
+
options[:label] = "#{prefix.symbol}#{@label}"
|
567
|
+
options[:factor] = prefix.factor * @factor
|
568
|
+
options[:physical_quantity] = @dimensions
|
569
|
+
return options
|
570
|
+
end
|
564
571
|
|
565
572
|
# Clone self and explicitly clone the associated Dimensions object located
|
566
573
|
# at @dimensions.
|
@@ -573,9 +580,6 @@ module Quantify
|
|
573
580
|
def initialize_copy(source)
|
574
581
|
super
|
575
582
|
instance_variable_set("@dimensions", @dimensions.clone)
|
576
|
-
if self.is_compound_unit?
|
577
|
-
instance_variable_set("@base_units", @base_units.map {|base| base.clone })
|
578
|
-
end
|
579
583
|
end
|
580
584
|
|
581
585
|
# Provides syntactic sugar for several methods. E.g.
|
@@ -31,7 +31,7 @@ module Quantify
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Absolute index as names always contain 'per' before denominator units
|
34
|
-
def name
|
34
|
+
def name(reciprocal=false)
|
35
35
|
name_to_power(@unit.name, @index.abs)
|
36
36
|
end
|
37
37
|
|
@@ -39,20 +39,12 @@ module Quantify
|
|
39
39
|
name_to_power(@unit.pluralized_name, @index.abs)
|
40
40
|
end
|
41
41
|
|
42
|
-
def symbol
|
43
|
-
@unit.symbol
|
42
|
+
def symbol(reciprocal=false)
|
43
|
+
@unit.symbol + index_as_string(reciprocal)
|
44
44
|
end
|
45
45
|
|
46
|
-
def label
|
47
|
-
@unit.label + (
|
48
|
-
end
|
49
|
-
|
50
|
-
# Reciprocalized version of label, i.e. sign changed. This is used to make
|
51
|
-
# a denominator unit renderable in cases where there are no numerator units,
|
52
|
-
# i.e. where no '/' appears in the label
|
53
|
-
#
|
54
|
-
def reciprocalized_label
|
55
|
-
@unit.label + (@index == -1 ? "" : formatted_index(@index * -1))
|
46
|
+
def label(reciprocal=false)
|
47
|
+
@unit.label + index_as_string(reciprocal)
|
56
48
|
end
|
57
49
|
|
58
50
|
def factor
|
@@ -67,8 +59,9 @@ module Quantify
|
|
67
59
|
@index < 0
|
68
60
|
end
|
69
61
|
|
70
|
-
|
71
|
-
|
62
|
+
def measures
|
63
|
+
dimensions.describe
|
64
|
+
end
|
72
65
|
|
73
66
|
def is_base_quantity_si_unit?
|
74
67
|
@unit.is_base_quantity_si_unit?
|
@@ -97,10 +90,6 @@ module Quantify
|
|
97
90
|
def is_derived_unit?
|
98
91
|
@unit.is_derived_unit?
|
99
92
|
end
|
100
|
-
|
101
|
-
def measures
|
102
|
-
@unit.measures
|
103
|
-
end
|
104
93
|
|
105
94
|
def initialize_copy(source)
|
106
95
|
instance_variable_set("@unit", @unit.clone)
|
@@ -109,11 +98,21 @@ module Quantify
|
|
109
98
|
private
|
110
99
|
|
111
100
|
# Returns a string representation of the unit index, formatted according to
|
112
|
-
# the global superscript configuration
|
101
|
+
# the global superscript configuration.
|
102
|
+
#
|
103
|
+
# The index value can be overridden by specifying as an argument.
|
113
104
|
#
|
114
105
|
def formatted_index(index=nil)
|
115
106
|
index = "^#{index.nil? ? @index : index}"
|
116
|
-
|
107
|
+
Unit.use_superscript_characters? ? index.with_superscript_characters : index
|
108
|
+
end
|
109
|
+
|
110
|
+
def index_as_string(reciprocal=false)
|
111
|
+
if reciprocal == true
|
112
|
+
@index == -1 ? "" : formatted_index(@index * -1)
|
113
|
+
else
|
114
|
+
@index.nil? || @index == 1 ? "" : formatted_index
|
115
|
+
end
|
117
116
|
end
|
118
117
|
|
119
118
|
def name_to_power(string,index)
|
@@ -123,7 +122,7 @@ module Quantify
|
|
123
122
|
when 2 then "square #{name}"
|
124
123
|
when 3 then "cubic #{name}"
|
125
124
|
else
|
126
|
-
ordinal = ActiveSupport::Inflector.ordinalize
|
125
|
+
ordinal = ActiveSupport::Inflector.ordinalize(index)
|
127
126
|
name << " to the #{ordinal} power"
|
128
127
|
end
|
129
128
|
end
|