units-system 0.2.5 → 0.3.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.
@@ -148,6 +148,34 @@ New units can be defined with +Units.define+
148
148
  Units.define :kph, 1, Units.u{km/h}
149
149
  puts Units.u{270*kph.to(m/s)} # => 75.0*m/s
150
150
 
151
+ === Constants
152
+
153
+ Constants could be define practically as units, but to avoid introducing too much
154
+ noise in the units namespace, they can be defined separately with:
155
+
156
+ Units.constant :g, 'standard gravity', u{9.80665*m/s**2}
157
+
158
+ A constant can be used anywhere with the Units::Const prefix:
159
+
160
+ puts Units::Const.g # => 9.80665*m/s**2
161
+ # gram-force:
162
+ puts u{g*Const.g} # => 9.80665*(g*m)/s**2
163
+
164
+ To avoid using the prefix, constants to be used unprefixed can be declared with a +with_constants+;
165
+ Note in the first example, that by introducing a constant named +g+ we're hiding the gram units and
166
+ would not be able to use it in the block.
167
+
168
+ # kilopond:
169
+ puts Units.with_constants(:g){kg*g} # => 9.80665*(kg*m)/s**2
170
+ # 1 GeV mass
171
+ puts Units.with_constants(:c){1*GeV/c**2}.to(:kg) # => 1.782661844855044e-27*kg
172
+ # Planck units
173
+ Units.with_constants :c, :G, :hbar do
174
+ puts sqrt(hbar*G/c**3) # => 1.6161992557033346e-35*m
175
+ puts sqrt(hbar*c/G) # => 2.176509252445312e-08*kg
176
+ puts sqrt(hbar*G/c**5) # => 5.391060423886096e-44*s
177
+ end
178
+
151
179
  == Caveat
152
180
 
153
181
  Note that Ruby variable definition rules imply that this:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.5
1
+ 0.3.0
@@ -13,15 +13,36 @@ module Units
13
13
  # It is not needed in Ruby 1.9.1 due to they way constant look-up is done in that version,
14
14
  # but Ruby 1.9.2 has changed that an requires this again.
15
15
  module UseBlocks
16
+ @@constants = nil # an instance variable would not work here because const_missing is executed on other modules (which include this one)
16
17
  def self.append_features(target)
17
18
  def target.const_missing(name)
18
19
  begin
19
- Units.Measure(name)
20
+ name = name.to_sym
21
+ if name==:Const
22
+ Units::Const
23
+ else
24
+ result = @@constants[name] if @@constants
25
+ result || Units.Measure(name)
26
+ end
20
27
  rescue ArgumentError
21
28
  super
22
29
  end
23
30
  end
24
31
  end
32
+
33
+ def self.with_constants(*consts)
34
+ prev = @@constants
35
+ # @@constants = consts.map_hash{|c| Units.constant(c)}
36
+ @@constants = {}
37
+ consts.each do |c|
38
+ c = c.to_sym
39
+ @@constants[c] = Units.constant(c)
40
+ end
41
+ result = yield
42
+ @@constants = prev
43
+ result
44
+ end
45
+
25
46
  end
26
47
 
27
48
  include UseBlocks
@@ -78,17 +78,17 @@ module Units
78
78
  define :arcmin, 'arc-minute', 1, :′
79
79
  define :arcsec, 'arc-second', 1, :″
80
80
 
81
- define :g0, 'standard gravity', u{9.80665*m/s**2}
81
+ constant :g0, 'standard gravity', u{9.806_65*m/s**2}
82
82
 
83
83
  define :bar, 'bar', 1E5, :Pa
84
84
  define :atm, 'atmosphere', 101325.0, :Pa
85
- define :mWC, 'meters of water column', u{1E3*kg*g0/m**2}
85
+ define :mWC, 'meters of water column', with_constants(:g0){1E3*kg*g0/m**2}
86
86
  define :Torr, 'torricelli', u{atm/760}
87
- define :mHg, 'mHg', u{13.5951E3*kg*g0/m**2}
87
+ define :mHg, 'mHg', with_constants(:g0){13.5951E3*kg*g0/m**2}
88
88
 
89
89
  # define :kp, 'kilopond', :force, u{kg*g0} # or define pond?
90
- define :gf, 'gram-force', u{g*g0} # kilopond kp = kgf
91
- define :lbf, 'pound-force', u{lb*g0}
90
+ define :gf, 'gram-force', with_constants(:g0){g*g0} # kilopond kp = kgf
91
+ define :lbf, 'pound-force', with_constants(:g0){lb*g0}
92
92
 
93
93
  define :dyn, 'dyne', 10, :µN # u{1*g*cm/s**2}
94
94
  define :galUS, 'U.S. liquid gallon', u{231*self.in**3}
@@ -97,4 +97,27 @@ module Units
97
97
 
98
98
  define :psi, 'pounds-force per square inch', u{lbf/self.in**2}
99
99
 
100
+ constant :pi, 'Pi', ::Math::PI
101
+
102
+ # Constants (CODATA 2010 recommended values)
103
+ constant :c, 'speed of light', u{ 299_792_458*m/s }
104
+ constant :h, 'Planck constant', u{ 6.626_069_57E-34*J*s }
105
+ constant :hbar, 'Dirac constant', u{Const.h/(2*Const.pi)}
106
+ constant :ħ, 'Dirac constant', Const.hbar
107
+ constant :μ0, 'magnetic constant', u{4*Const.pi*1E-7*N/A**2}
108
+ constant :ϵ0, 'electric constant', 1/Const.μ0/Const.c**2
109
+ constant :G, 'gravitational constant', u{ 6.673_84E-11*m**3/kg/s**2 }
110
+ constant :e, 'elementary charge', u{1.602_176_565E-19*C}
111
+ constant :me, 'electron mass', u{9.109_382_91E-31*kg}
112
+ constant :mp, 'proton mass', u{1.672_621_777E-27*kg}
113
+ constant :α, 'fine-structure constant', with_constants(:e, :pi, :ϵ0, :ħ, :c){e**2/(4*pi*ϵ0*ħ*c)}
114
+ constant :R∞, 'Rydberg constant', with_constants(:α, :me, :c, :h){α**2*me*c/2/h}
115
+ constant :NA, 'Avogadro constant', u{6.022_141_29E22*mol}
116
+ constant :F, 'Faraday constant', Const.NA*Const.e
117
+ constant :R, 'molar gas constant', u{8.314_4621*J/mol/K}
118
+ constant :k, 'Boltzmann constant', Const.R/Const.NA
119
+
120
+ define :eV, 'electron volt', u{Const.e*V}
121
+ define :u, 'atomic mass unit', u{1.660_538_921E-27*kg}
122
+
100
123
  end # Units
@@ -44,6 +44,32 @@ module Units
44
44
  Units.u{::Math.atan2(x,y)*rad}
45
45
  end
46
46
 
47
+ def sqrt(x)
48
+ if x.kind_of?(Measure)
49
+ if x.units.empty?
50
+ Measure[::Math.sqrt(x.magnitude)]
51
+ else
52
+ y = x
53
+ all_even = x.units.values.map{|v| v.last % 2}.uniq == [0]
54
+ unless all_even
55
+ x = x.base
56
+ all_even = x.units.values.map{|v| v.last % 2}.uniq == [0]
57
+ end
58
+ if all_even
59
+ units = {}
60
+ x.units.each do |dim, (unit, exp)|
61
+ units[dim] = [unit, exp/2]
62
+ end
63
+ Measure[::Math.sqrt(x.magnitude), units]
64
+ else
65
+ raise ArgumentError, "Invalid dimensiones for sqrt argument #{y}"
66
+ end
67
+ end
68
+ else
69
+ ::Math.sqrt(x)
70
+ end
71
+ end
72
+
47
73
  end
48
74
 
49
75
  end # Units
@@ -232,5 +232,53 @@ module Units
232
232
  u_descr
233
233
  end
234
234
 
235
- end # Units
235
+ ConstantDefinition = Struct.new(:symbol, :description, :value)
236
+ CONSTANTS = {}
237
+ module Const
238
+ def self.define(name, description, value)
239
+ symbol = name.to_sym
240
+ cd = ConstantDefinition.new(symbol, description, value)
241
+ CONSTANTS[symbol] = cd
242
+ class_eval do
243
+ # Ruby 1.9.1 allows this nicer definition:
244
+ # define_singleton_method name do
245
+ # value
246
+ # end
247
+ eigenclass = class<<self; self; end
248
+ eigenclass.instance_eval{define_method(name){value}}
249
+ end
250
+ end
251
+ end
252
+
253
+ def self.constant(symbol, description=nil, value=nil)
254
+ if description.nil? && value.nil?
255
+ c = CONSTANTS[symbol]
256
+ c && c.value
257
+ else
258
+ Const.define symbol, description, value
259
+ end
260
+ end
236
261
 
262
+ def self.with_constants(*constants, &blk)
263
+ m = Module.new
264
+ m.extend Units::System
265
+ m.extend Units::Math
266
+ cap_constants = []
267
+ constants.each do |const|
268
+ m.instance_eval do
269
+ # Ruby 1.9.1 allows this nicer definition:
270
+ # define_singleton_method(const){Units.constant(const)}
271
+ eigenclass = class<<self; self; end
272
+ eigenclass.instance_eval{define_method(const){Units.constant(const)}}
273
+ name_initial = const.to_s[0,1]
274
+ if name_initial==name_initial.upcase && name_initial!=name_initial.downcase
275
+ cap_constants << const
276
+ end
277
+ end
278
+ end
279
+ UseBlocks.with_constants(*cap_constants) do
280
+ m.instance_eval &blk
281
+ end
282
+ end
283
+
284
+ end # Units
@@ -0,0 +1,68 @@
1
+ require 'helper'
2
+
3
+ class TestConstants < Test::Unit::TestCase
4
+
5
+ include Units::UseBlocks
6
+
7
+ should "have qualified constants" do
8
+ assert_equal 299792458, Units::Const.c.magnitude
9
+ assert_equal 6.67384E-11, Units::Const.G.magnitude
10
+ assert_equal [:m, 1], Units::Const.c.units[:length]
11
+ assert_equal 299792458**2, (Units::Const.c**2).magnitude
12
+ assert_equal [:m, 2], (Units::Const.c**2).units[:length]
13
+ assert_nil (Units::Const.c/Units.u{m}).units[:length]
14
+ end
15
+
16
+ should "have qualified constants in units blocks" do
17
+ assert_equal 299792458, Units.units{Units::Const.c}.magnitude
18
+ assert_equal 6.67384E-11, Units.units{Units::Const.G}.magnitude
19
+ assert_equal [:m, 1], Units.units{Units::Const.c}.units[:length]
20
+ assert_equal 299792458**2, Units.units{Units::Const.c**2}.magnitude
21
+ assert_equal [:m, 2], Units.units{Units::Const.c**2}.units[:length]
22
+ assert_nil Units.units{Units::Const.c/m}.units[:length]
23
+ end
24
+
25
+ should "not need qualification for Const inside a units block" do
26
+ assert_equal 299792458, Units.units{Const.c}.magnitude
27
+ assert_equal 6.67384E-11, Units.units{Const.G}.magnitude
28
+ assert_equal [:m, 1], Units.units{Const.c}.units[:length]
29
+ assert_equal 299792458**2, Units.units{Const.c**2}.magnitude
30
+ assert_equal [:m, 2], Units.units{Const.c**2}.units[:length]
31
+ assert_nil Units.units{Const.c/m}.units[:length]
32
+ end
33
+
34
+ should "provide constant values" do
35
+ c = Units::Const.c
36
+ assert_equal 299792458, Units.units{c}.magnitude
37
+ assert_equal [:m, 1], Units.units{c}.units[:length]
38
+ assert_equal 299792458**2, Units.units{c**2}.magnitude
39
+ assert_equal [:m, 2], Units.units{c**2}.units[:length]
40
+ assert_nil Units.units{c/m}.units[:length]
41
+ end
42
+
43
+ should "allow use of local unqualified declared constants" do
44
+ assert_equal 299792458, Units.with_constants(:c){c}.magnitude
45
+ assert_equal 299792458*6.67384E-11, Units.with_constants(:c,:G){G*c}.magnitude
46
+ assert_nil Units.with_constants(:c){c/m}.units[:length]
47
+ assert_in_delta 1.782661844855044e-27, Units.with_constants(:c){1*GeV/c**2}.to(:kg).magnitude, Float::EPSILON
48
+ end
49
+
50
+ should "not need qualification for Const inside a with_constants block" do
51
+ assert_equal 299792458, Units.with_constants{Const.c}.magnitude
52
+ assert_equal 6.67384E-11, Units.with_constants{Const.G}.magnitude
53
+ assert_equal [:m, 1], Units.with_constants{Const.c}.units[:length]
54
+ assert_equal 299792458**2, Units.with_constants{Const.c**2}.magnitude
55
+ assert_equal [:m, 2], Units.with_constants{Const.c**2}.units[:length]
56
+ assert_nil Units.with_constants{Const.c/m}.units[:length]
57
+ end
58
+
59
+ should "define new constants" do
60
+ Units.constant :cc, "test constant c", Units::Const.c
61
+ assert_equal Units::Const.c, Units::Const.cc
62
+ assert_equal 299792458*6.67384E-11, Units.with_constants(:cc,:G){G*cc}.magnitude
63
+ Units.constant :GG, "test constant G", Units::Const.G
64
+ assert_equal Units::Const.G, Units::Const.GG
65
+ assert_equal 299792458*6.67384E-11, Units.with_constants(:c,:GG){GG*c}.magnitude
66
+ end
67
+
68
+ end
@@ -132,7 +132,8 @@ class TestUnitsSystem < Test::Unit::TestCase
132
132
  should "render valid code when inspecting measures" do
133
133
  assert_equal Units.u{m}, eval(Units.u{m}.inspect)
134
134
  assert_equal Units.u{3*m/s}, eval(Units.u{3*m/s}.inspect)
135
- assert_equal Units.u{3*m/s+2*km/h}, eval(Units.u{3*m/s+2*km/h}.inspect)
135
+ assert_in_delta Units.u{3*m/s+2*km/h}.magnitude, eval(Units.u{3*m/s+2*km/h}.inspect).magnitude, 1E-12
136
+ assert_equal Units.u{3*m/s+2*km/h}.units, eval(Units.u{3*m/s+2*km/h}.inspect).units
136
137
  end
137
138
 
138
139
  should "allow arithmetic between measures and text" do
@@ -146,7 +147,7 @@ class TestUnitsSystem < Test::Unit::TestCase
146
147
  assert_equal "1.0 m", Units.u{m}.abr
147
148
  assert_equal "3.0 m", Units.u{3*m}.abr
148
149
  assert_equal "3.0 m/s", Units.u{3*m/s}.abr
149
- assert_equal "3.5555555555555554 m/s", Units.u{3*m/s + 2*km/h}.abr
150
+ assert_match /\A3\.555555555555\d+ m\/s\Z/, Units.u{3*m/s + 2*km/h}.abr
150
151
  assert_equal "1,5 m/s", Units.units{3*m/(2*s)}.abr{|v| v.to_s.tr('.',',') }
151
152
  end
152
153
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "units-system"
8
- s.version = "0.2.5"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Javier Goizueta"]
12
- s.date = "2012-09-20"
12
+ s.date = "2012-09-27"
13
13
  s.description = "Experimental unit conversion & arithmetic for Ruby 1.9"
14
14
  s.email = "jgoizueta@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  "lib/units/prefixes.rb",
32
32
  "lib/units/system.rb",
33
33
  "test/helper.rb",
34
+ "test/test_constants.rb",
34
35
  "test/test_units-system.rb",
35
36
  "units-system.gemspec"
36
37
  ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: units-system
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-20 00:00:00.000000000 Z
12
+ date: 2012-09-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: modalsupport
@@ -113,6 +113,7 @@ files:
113
113
  - lib/units/prefixes.rb
114
114
  - lib/units/system.rb
115
115
  - test/helper.rb
116
+ - test/test_constants.rb
116
117
  - test/test_units-system.rb
117
118
  - units-system.gemspec
118
119
  homepage: http://github.com/jgoizueta/units-system
@@ -129,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
129
130
  version: '0'
130
131
  segments:
131
132
  - 0
132
- hash: -2016240404196555066
133
+ hash: 3591103717823383579
133
134
  required_rubygems_version: !ruby/object:Gem::Requirement
134
135
  none: false
135
136
  requirements: