quantity 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ GEMSPEC = Gem::Specification.new do |gem|
5
+ gem.version = File.read('VERSION').chomp
6
+ gem.date = File.mtime('VERSION').strftime('%Y-%m-%d')
7
+
8
+ gem.name = 'quantity'
9
+ gem.homepage = 'http://quantity.rubyforge.org/'
10
+ gem.license = 'Public Domain' if gem.respond_to?(:license=)
11
+ gem.summary = 'Units and quantities for Ruby.'
12
+ gem.description = <<-EOF
13
+ Quantity provides first-class quantities, units, and base quantities in pure ruby.
14
+ Things like 1.meter / 1.second == 1 meter/second.
15
+ EOF
16
+ gem.rubyforge_project = 'quantity'
17
+
18
+ gem.authors = ['Ben Lavender', 'Arto Bendiken']
19
+ gem.email = 'blavender@gmail.com'
20
+
21
+ gem.platform = Gem::Platform::RUBY
22
+ gem.files = %w(AUTHORS README UNLICENSE VERSION) + Dir.glob('lib/**/*.rb')
23
+ gem.bindir = %q(bin)
24
+ gem.executables = %w()
25
+ gem.default_executable = gem.executables.first
26
+ gem.require_paths = %w(lib)
27
+ gem.extensions = %w()
28
+ gem.test_files = %w()
29
+ gem.has_rdoc = false
30
+
31
+ gem.required_ruby_version = '>= 1.8.2'
32
+ gem.requirements = []
33
+ gem.add_development_dependency 'rspec', '>= 1.2.9'
34
+ gem.add_development_dependency 'yard' , '>= 0.5.2'
35
+ gem.post_install_message = nil
36
+ end
@@ -0,0 +1,10 @@
1
+ .rvmrc
2
+ .bundle
3
+ Gemfile.lock
4
+ .DS_Store
5
+ .tmp
6
+ .yardoc
7
+ pkg
8
+ tmp
9
+ coverage
10
+ *.gem
@@ -0,0 +1,13 @@
1
+ --title "Quantity: Quantities and units of measurements for Ruby"
2
+ --output-dir doc/yard
3
+ --protected
4
+ --no-private
5
+ --hide-void-return
6
+ --markup markdown
7
+ --readme README.md
8
+ -
9
+ AUTHORS
10
+ UNLICENSE
11
+ VERSION
12
+ lib/quantity.rb lib/**/*.rb
13
+
data/AUTHORS CHANGED
@@ -1,2 +1,3 @@
1
1
  * Ben Lavender <blavender@gmail.com> (Lead developer)
2
2
  * Arto Bendiken <arto.bendiken@gmail.com> (Contributions)
3
+ * Matt Todd <mtodd@highgroove.com> (Contributions)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in quantity.gemspec
4
+ gemspec
data/README CHANGED
@@ -13,7 +13,7 @@ Hopefully this readme will be all you need, but [there are yardocs](http://quant
13
13
  newton = 1.meter * 1.kilogram / 1.second**2 #=> 1 meter*kilogram/second^2
14
14
  newton.to_feet #=> 3.28083989501312 foot*kilogram/second^2
15
15
  newton.convert(:feet) #=> 3.28083989501312 foot*kilogram/second^2
16
- jerk_newton / 1.second #=> 1 meter*kilogram/second^3
16
+ jerk_newton = newton / 1.second #=> 1 meter*kilogram/second^3
17
17
  jerk_newton * 1.second == newton #=> true
18
18
 
19
19
  mmcubed = 1.mm.cubed #=> 1 millimeter^3
@@ -32,9 +32,8 @@ dimensions in Ruby. Some terminology:
32
32
  * Dimension: Some base quantity to be measured, such as 'length'
33
33
 
34
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 `/`.
35
+ including `+`, `-`, `\*`, `/`, `**`, `%`, `abs`, `divmod`, `<=>`, and negation. Units
36
+ and the dimensions they measure are fully represented and support `*` and `/`.
38
37
 
39
38
  Quantity extends Numeric to allow easy creation of quantities, but there
40
39
  are direct interfaces to the library as well.
@@ -210,7 +209,7 @@ Authors
210
209
 
211
210
  * [Ben Lavender](mailto:blavender@gmail.com) - <http://bhuga.net/>
212
211
  * [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
213
-
212
+ * [Matt Todd](mtodd@highgroove.com) - <http://maraby.org/>
214
213
  License
215
214
  -------
216
215
 
@@ -0,0 +1,217 @@
1
+ Quantity.rb: Units and Quantities for Ruby
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 = 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 `*` and `/`.
37
+
38
+ Quantity extends Numeric to allow easy creation of quantities, but there
39
+ are direct interfaces to the library as well.
40
+
41
+ 1.meter == Quantity.new(1,Quantity::Unit.for(:meter))
42
+ 1.meter.unit == Quantity::Unit.for(:meter)
43
+ 1.meter.unit.dimension == Quantity::Dimension.for(:length)
44
+
45
+ See the units section for supported units, and how to add your own.
46
+
47
+ Quantities are first-class citizens which do a fair job of imitating
48
+ Numeric. Quantities support coerce, and can thus be used in almost
49
+ any situation a numeric can:
50
+
51
+ 2.5 + 5.meters # => 7.5 meters
52
+ 5 == 5.meters # => true
53
+
54
+ ## Status and TODO
55
+ Quantity.rb is not ready for production use for some areas, but should be
56
+ fine for simple conversion use cases. If it breaks, please email the
57
+ author for a full refund.
58
+
59
+ Specifically broken in this version are some operations on named
60
+ higher dimensions:
61
+
62
+ 1.liter / 1.second #=> should be 1 liter/second, but explodes
63
+ 1.liter.convert(:'mm^3') / 1.second #=> 1000000.0 millimeter^3/second
64
+
65
+ If you just work with units derived from the base dimensions, there aren't
66
+ known bugs. Please add a spec if you find one.
67
+
68
+ ### TODO
69
+ * Lots more units are planned.
70
+ * BigDecimal support a la Rational.
71
+ * Supporting lambdas for unit values
72
+ * BigDecimal/Rational compatible values for existing units
73
+ * Some DSL sugar for adding derived dimension units
74
+
75
+ ## Units
76
+ Quantity.rb comes with a sizable collection of units, but still needs significant expansion.
77
+
78
+ A number of base unit sets exist:
79
+ require 'quantity/all' #=> load everything. uses US versions of foot, lb, etc
80
+ require 'quantity/systems/si' #=> load SI
81
+ require 'quantity/systems/us' #=> load US versions of foot, lb, etc
82
+ require 'quantity/systems/imperial' #=> load British versions of foot, lb, etc
83
+ require 'quantity/systems/information' #=> bits, bytes, and all that
84
+ require 'quantity/systems/enumerable' #=> countable things--dozen, score, etc
85
+
86
+ Note that US and Imperial conflict with each other. Loading both is unsupported.
87
+
88
+ Adding your own units is simple:
89
+
90
+ Quantity::Unit.add_unit :furlong, :length, 201168, :furlongs
91
+ 1.furlong #=> 1 furlong
92
+
93
+ 201168 represents 1 furlong in millimeters. Each base dimension, such as length, time,
94
+ current, temperature, etc, is represented by a reference unit, which is generally the
95
+ milli-version of the SI unit referencing that domain. [NIST](http://physics.nist.gov/cuu/Units/units.html)
96
+ has an explanation of how the SI system works, and how all units are actually derived from
97
+ very few.
98
+
99
+ All units for derived dimensions used the derived reference unit. For example, length
100
+ is referenced to millimeters, so each unit of length is defined in terms of them:
101
+
102
+ Quantity::Unit.add_unit :meter, :length, 1000
103
+ Quantity::Unit.add_unit :millimeter, :length, 1, :mm
104
+
105
+ Thus, the base unit for volume is 1 mm^3:
106
+ volume = Quantity::Dimension.add_dimension length**3, :volume
107
+ ml = Quantity::Dimension.add_unit :milliliter, :volume, 1000, :ml, :milliliters
108
+ 1.mm**3 * 1000 == 1.milliliter #=> true
109
+
110
+ See the bugs section for some current issues using units defined on derived dimensions.
111
+
112
+ The full list of included base dimensions and their reference units:
113
+ * :length => :millimeter
114
+ * :time => :millisecond
115
+ * :current => :milliampere
116
+ * :luminosity => :millicandela
117
+ * :substance => :millimole
118
+ * :temperature => :millikelvin
119
+ * :mass => :milligram
120
+ * :information => :bit # use :megabytes and :mebibytes
121
+ * :quantity => :item # for countable quantities. units include 1.dozen, for example
122
+ * :currency => :dollar # These are not really implemented yet
123
+
124
+ To determine the base unit for a derived dimension, you can use Quantity.rb itself:
125
+
126
+ force = Quantity::Dimension.for(:force)
127
+ newton = 1.meter * 1.kilogram / 1.second**2
128
+ newton.measures == force #=> true
129
+ newton_value = newton.to_mm.to_mg.to_ms #=> 1000.0 millimeter*milligram/millisecond^2
130
+
131
+ Thus, a newton would be 1000 when added specifically:
132
+
133
+ Quantity::Unit.add_unit :newton, :force, 1000, :newtons
134
+ 1.newton == newton #=> true
135
+
136
+ ## Dimensions
137
+ A dimension is a measurable thing, often called a 'base quantity' in scientific literature,
138
+ but Quantity.rb specifically avoids that nomenclature, reserving 'quantity' for the class
139
+ representing a unit and a value. As always, [wikipedia has the answers.](http://en.wikipedia.org/wiki/Physical_quantity)
140
+
141
+ Dimensions are not very useful by themselves, but you can play with them
142
+ if you want.
143
+
144
+ length = Quantity::Dimension.for(:length)
145
+ time = Quantity::Dimension.for(:time)
146
+ speed = length / time
147
+
148
+ A number of dimensions are enabled by default (see dimension/base.rb).
149
+
150
+ A DSL of sorts is provided for declaring dimensions:
151
+
152
+ length = Quantity::Dimension.add_dimenson :length
153
+ area = Quantity::Dimension.add_dimension length**2, :area
154
+
155
+ length = Quantity::Dimension.for(:length)
156
+ area = Quantity::Dimension.for(:area)
157
+ area == length * length #=> true
158
+
159
+ Quantity::Dimension is extended with empty subclasses for some base dimensions,
160
+ so you can do pattern patching on the class:
161
+
162
+ case 1.meter.measures
163
+ when Quantity::Dimension::Length
164
+ puts "I am printed"
165
+ end
166
+
167
+ ## I just want to convert things, this is all just too much
168
+ Quantity.rb provides you the ability to intuitively create the conversions
169
+ your application needs, and then bypass the rest of the library.
170
+
171
+ m_to_f = 1.meter.measures.convert_proc(:feet)
172
+ m_to_f.call(5) # => 5 meters in feet
173
+
174
+ This Proc object has been broken down into a single division; it no longer references
175
+ any units, dimensions, or quantities. It's hard to be faster in pure Ruby.
176
+
177
+ ### On precision and speed
178
+
179
+ By default, whatever Numeric you are using will be the stored value for the
180
+ quantity.
181
+
182
+ 5.meters
183
+ Rational(5).meters
184
+ 5.0.meters
185
+
186
+ This value will be held. However, divisions are required for conversions,
187
+ and the default is to force values into floats.
188
+
189
+ If accuracy is required, just require 'rational'. If Rational is defined,
190
+ you'll get rationals instead of divided floats everywhere. In tests, this
191
+ is an order of magnitude slower.
192
+
193
+ ## 'Why' and previous work
194
+ This is by no means the first unit conversion/quantity library for Ruby, but
195
+ none of the existing ones scratched my itch just right. My goal is that this will
196
+ be the last one I (and you) need. The abstractions go all the way down, and
197
+ any conceivable conversion or munging functionality should be buildable on top
198
+ of this.
199
+
200
+ Inspiration comes from:
201
+
202
+ * [Quanty](http://narray.rubyforge.org/quanty/quanty-en.html)
203
+ Why oh why did they involve yacc?
204
+ * [Ruby Units](http://ruby-units.rubyforge.org/ruby-units/)
205
+ * [Alchemist](http://github.com/toastyapps/alchemist)
206
+
207
+ Authors
208
+ -------
209
+
210
+ * [Ben Lavender](mailto:blavender@gmail.com) - <http://bhuga.net/>
211
+ * [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
212
+ * [Matt Todd](mtodd@highgroove.com) - <http://maraby.org/>
213
+ License
214
+ -------
215
+
216
+ Quantity.rb is free and unencumbered public domain software. For more
217
+ information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
3
+ require 'quantity'
4
+
5
+ require "rubygems"
6
+ require "bundler"
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ begin
10
+ Bundler.setup(:default, :development)
11
+ rescue Bundler::BundlerError => e
12
+ $stderr.puts e.message
13
+ $stderr.puts "Run `bundle install` to install missing gems"
14
+ exit e.status_code
15
+ end
16
+
17
+ require 'spec'
18
+ require 'spec/rake/spectask'
19
+ require 'yard'
20
+
21
+ desc "Run specs"
22
+ Spec::Rake::SpecTask.new('spec') do |t|
23
+ t.spec_files = FileList['spec/quantity.spec']
24
+ t.spec_opts = ["-cfn"]
25
+ end
26
+
27
+ desc "Run unit specs"
28
+ Spec::Rake::SpecTask.new('unit') do |t|
29
+ t.spec_files = FileList['spec/dimension.spec', 'spec/unit.spec', 'spec/systems.spec']
30
+ t.spec_opts = ["-cfn"]
31
+ end
32
+
33
+ desc "specs with backtrace"
34
+ Spec::Rake::SpecTask.new('tracespec') do |t|
35
+ t.spec_files = FileList['spec/quantity.spec']
36
+ t.spec_opts = ["-bcfn"]
37
+ end
38
+
39
+ desc "unit specs with backtrace"
40
+ Spec::Rake::SpecTask.new('traceunit') do |t|
41
+ t.spec_files = FileList['spec/dimension.spec', 'spec/unit.spec', 'spec/systems.spec']
42
+ t.spec_opts = ["-bcfn"]
43
+ end
44
+
45
+ desc "package yardocs"
46
+ YARD::Rake::YardocTask.new('yard') do |t|
47
+ # see .yardopts for the action
48
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -0,0 +1,2 @@
1
+ rdoc
2
+ yard
@@ -360,6 +360,16 @@ class Quantity
360
360
  end
361
361
  end
362
362
 
363
+ def respond_to?(method)
364
+ if method.to_s =~ /(to_|in_)(.*)/
365
+ if (Unit.is_unit?($2.to_sym))
366
+ return true
367
+ end
368
+ end
369
+
370
+ super
371
+ end
372
+
363
373
  end
364
374
 
365
375
  # @private
@@ -120,6 +120,13 @@ class Quantity
120
120
  @name.to_s
121
121
  end
122
122
 
123
+ # The reduced form of a named complex unit
124
+ # @example
125
+ # Quantity::Dimension.for(:force).reduced_name # => :'mass*length/time^2'
126
+ def reduced_name
127
+ string_form.to_sym
128
+ end
129
+
123
130
  # Dimensional multiplication
124
131
  # @param [Dimension] other
125
132
  # @return [Dimension]
@@ -178,7 +185,7 @@ class Quantity
178
185
  #
179
186
  # @return [String]
180
187
  def inspect
181
- sprintf("#<%s:%#0x %s (%s)>", self.class.name, object_id, string_form, @name)
188
+ sprintf("#<%s:0x%s @name=%s reduced_name=%s>", self.class.name, self.__id__.to_s(16), @name, reduced_name)
182
189
  end
183
190
 
184
191
 
@@ -0,0 +1,12 @@
1
+ ActiveSupport::Duration.class_eval do
2
+ def quantity
3
+ Quantity.new(@value, Quantity::Unit.for(:second))
4
+ end
5
+
6
+ def method_missing_with_quantity(method, *args, &block) #:nodoc:
7
+ return quantity.send(method, *args, &block) if quantity.respond_to?(method)
8
+
9
+ method_missing_without_quantity(method, *args, &block)
10
+ end
11
+ alias_method_chain :method_missing, :quantity
12
+ end
@@ -76,6 +76,12 @@ class Quantity
76
76
  self.class_eval(&block)
77
77
  end
78
78
 
79
+ # Reset the world. Useful in testing.
80
+ # @private
81
+ def self.__reset!
82
+ @@units = {}
83
+ end
84
+
79
85
  ### Instance-level methods/vars
80
86
  attr_reader :name, :value, :dimension, :aliases
81
87
 
@@ -85,6 +91,11 @@ class Quantity
85
91
  [@name] + @aliases
86
92
  end
87
93
 
94
+ # A reduced form of this unit
95
+ def reduced_name
96
+ to_string_form.to_sym
97
+ end
98
+
88
99
  # Can this unit be converted into the target unit?
89
100
  # @param [Symbol String Unit]
90
101
  # @return [Boolean]
@@ -150,7 +161,8 @@ class Quantity
150
161
  end
151
162
 
152
163
  def inspect
153
- "<Unit #{@name} (#{@object_id}), value #{@value}, dimension #{@dimension}>"
164
+ sprintf('#<%s:0x%s @name=%s @value=%s @dimension=%s>', self.class.name,
165
+ self.__id__.to_s(16), @name.inspect, @value.inspect, @dimension.inspect)
154
166
  end
155
167
 
156
168
  def <=>(other)
@@ -311,10 +323,10 @@ class Quantity
311
323
  string
312
324
  end
313
325
 
314
- # Reset the world. Useful in testing.
315
- # @private
316
- def self.__reset!
317
- @@units = {}
326
+ # A reduced form of this unit
327
+ def reduced_name
328
+ string_form.to_sym
318
329
  end
330
+
319
331
  end
320
332
  end