sy 2.0.4 → 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sy/absolute_magnitude.rb +15 -4
- data/lib/sy/expressible_in_units.rb +119 -19
- data/lib/sy/fixed_assets_of_the_module.rb +11 -4
- data/lib/sy/magnitude.rb +15 -15
- data/lib/sy/quantity.rb +5 -9
- data/lib/sy/signed_magnitude.rb +9 -10
- data/lib/sy/unit.rb +57 -37
- data/lib/sy/version.rb +1 -1
- data/lib/sy.rb +18 -12
- data/test/sy_test.rb +20 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 666dbe08efdd97d407e931045726b9668f5bf3c2
|
4
|
+
data.tar.gz: aa13e418672f7223bcdb63683eb42c511974b2f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: deb58fde2b519570d6fc891a9e259aeafca50d541e6807c26075e4e1cd7415355378070baa05f231d449a36252d8cbd286120fdd37245fde87b79501a2e19dd2
|
7
|
+
data.tar.gz: 695f6feedb083dd82b35ebc7720a6aa89586dba97bee1ce4ff0a28a93fcf012e1957f0e7a8cabd618e328b89226f272eb5673dec1770c0ffcc211c1f847ad420
|
@@ -1,19 +1,30 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
# Qualities specific to absolute magnitudes.
|
2
|
+
# Qualities specific to absolute magnitudes (mixin).
|
3
|
+
#
|
4
|
+
# Absolute magnitude may not be negative – physical amounts cannot have
|
5
|
+
# negative number of unit objects. (<em>Difference</me> between magnitudes
|
6
|
+
# (relative magnitude) can be positive as well as negative.
|
7
|
+
#
|
8
|
+
# While ordinary #+ and #- methods of absolute magnitudes return relative
|
9
|
+
# magnitudes, absolute magnitudes have additional methods #add and #subtract,
|
10
|
+
# that return absolute magnitudes (it is the responsibility of the caller to
|
11
|
+
# avoid negative results). Furthermore, absolute magnitudes have one more
|
12
|
+
# special method #take, which perfoms #subtract whilst protecting against
|
13
|
+
# subtraction of more than, there is to take.
|
3
14
|
#
|
4
15
|
module SY::AbsoluteMagnitude
|
5
16
|
# Absolute magnitude constructor takes :quantity (alias :of) named argument,
|
6
17
|
# and :amount named argument, where amount must be nonnegative.
|
7
18
|
#
|
8
|
-
def initialize( of:
|
9
|
-
|
19
|
+
def initialize( of: nil, amount: nil )
|
20
|
+
fail ArgumentError, "Quantity (:of) argument missing!" if of.nil?
|
10
21
|
@quantity = of
|
11
22
|
@amount = case amount
|
12
23
|
when Numeric then amount
|
13
24
|
when nil then 1
|
14
25
|
else
|
15
26
|
begin
|
16
|
-
amount.amount
|
27
|
+
amount.( @quantity ).amount
|
17
28
|
rescue NameError, NoMethodError
|
18
29
|
amount
|
19
30
|
end
|
@@ -2,8 +2,114 @@
|
|
2
2
|
# This mixin provides ability to respond to SY unit symbol methods.
|
3
3
|
#
|
4
4
|
module SY::ExpressibleInUnits
|
5
|
+
COLLISION_WARNING = "Unit %s collision, method already defined on %s!"
|
6
|
+
REDEFINE_WARNING = "Method %s being defined on %s shadows SY unit method!"
|
5
7
|
RecursionError = Class.new StandardError
|
6
8
|
|
9
|
+
# This is a mixin for the target class of this mixin, that causes it to warn
|
10
|
+
# upon detecting newly defined methods shadowing the SY unit methods.
|
11
|
+
#
|
12
|
+
module DetectRedefine
|
13
|
+
def method_added ß
|
14
|
+
# warn "#{self}: method added: :#{ß}"
|
15
|
+
uu = ::SY::ExpressibleInUnits.known_units
|
16
|
+
nn = uu.map &:name
|
17
|
+
aa = uu.map &:abbreviation
|
18
|
+
ꜧ = Hash[ nn.zip( uu ) ].merge Hash[ aa.zip( uu ) ]
|
19
|
+
w = ::SY::ExpressibleInUnits::REDEFINE_WARNING % [ ß, self ]
|
20
|
+
if nn.include? ß then
|
21
|
+
if instance_methods.include? ß then
|
22
|
+
im = instance_method ß
|
23
|
+
warn w unless ::SY::ExpressibleInUnits.method_family.include? im if
|
24
|
+
ꜧ[ß].warns? unless instance_variable_get( :@no_collision ) == ß
|
25
|
+
instance_variable_set( :@no_collision, nil ) # FIXME: This is too clumsy
|
26
|
+
else
|
27
|
+
warn w if ꜧ[ß].warns?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
if aa.include? ß then
|
31
|
+
if instance_methods.include? ß then
|
32
|
+
im = instance_method ß
|
33
|
+
warn w unless ::SY::ExpressibleInUnits.method_family.include? im if
|
34
|
+
ꜧ[ß].warns? unless instance_variable_get( :@no_collision ) == ß
|
35
|
+
instance_variable_set( :@no_collision, nil ) # FIXME: This is too clumsy
|
36
|
+
else
|
37
|
+
warn w if ꜧ[ß].warns?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
# #included hook of this module is set to perfom a casual check for blatant
|
45
|
+
# name collisions between SY::Unit-implied methods, and existing methods of
|
46
|
+
# the include receiver.
|
47
|
+
#
|
48
|
+
def included receiver
|
49
|
+
included_in << receiver # keep track of where the mixin has been included
|
50
|
+
# Warn if the receiver has potentially colliding methods.
|
51
|
+
inst_methods = receiver.instance_methods
|
52
|
+
w = COLLISION_WARNING % ["%s", receiver]
|
53
|
+
known_units.each do |unit|
|
54
|
+
next unless unit.warns?
|
55
|
+
name, short = unit.name, unit.abbreviation
|
56
|
+
warn w % "name method ##{name}" if inst_methods.include? name
|
57
|
+
warn w % "abbreviation method ##{short}" if inst_methods.include? short
|
58
|
+
end
|
59
|
+
# Warn if shadowing methods are defined on the receiver later.
|
60
|
+
if receiver.is_a? Class
|
61
|
+
receiver.extend ::SY::ExpressibleInUnits::DetectRedefine
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Modules in which this mixin has been included.
|
66
|
+
#
|
67
|
+
def included_in
|
68
|
+
@included_in ||= []
|
69
|
+
end
|
70
|
+
|
71
|
+
# Currently defined unit instances, if any.
|
72
|
+
#
|
73
|
+
def known_units
|
74
|
+
unit_namespace = begin
|
75
|
+
SY::Unit
|
76
|
+
rescue NameError
|
77
|
+
return [] # no SY::Unit yet
|
78
|
+
end
|
79
|
+
begin
|
80
|
+
unit_namespace.instances
|
81
|
+
rescue NoMethodError
|
82
|
+
[] # no #instances method defined yet
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# All methods defined by this mixin.
|
87
|
+
#
|
88
|
+
def method_family
|
89
|
+
@method_family ||= []
|
90
|
+
end
|
91
|
+
|
92
|
+
# Find unit based on name / abbreviation.
|
93
|
+
#
|
94
|
+
def find_unit ς
|
95
|
+
known_units.find { |u| u.name.to_s == ς || u.short.to_s == ς }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return prefix method or empty string, if prefix method not necessary.
|
99
|
+
#
|
100
|
+
def prefix_method_string prefix
|
101
|
+
puts "About to call PREFIX TABLE.to_full with #{prefix}" if SY::DEBUG
|
102
|
+
full_prefix = SY::PREFIX_TABLE.to_full( prefix )
|
103
|
+
full_prefix == '' ? '' : ".#{full_prefix}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return exponentiation string (suffix) or empty ς if not necessary.
|
107
|
+
#
|
108
|
+
def exponentiation_string exp
|
109
|
+
exp == 1 ? '' : " ** #{exp}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
7
113
|
def method_missing ß, *args, &block
|
8
114
|
return self if ß.to_s =~ /begin|end/ # 3rd party bug workaround
|
9
115
|
super if ß.to_s =~ /to_.+/ # dissmiss :to_..., esp. :to_ary
|
@@ -12,7 +118,12 @@ module SY::ExpressibleInUnits
|
|
12
118
|
puts "Method missing: '#{ß}'" if SY::DEBUG
|
13
119
|
prefixes, units, exps = parse_unit_symbol ß
|
14
120
|
# Define the unit method on self.class:
|
15
|
-
|
121
|
+
# I'D HAVE TO PERFORM THE COLLISION CHECK HERE
|
122
|
+
# IF NO COLLISION, INFORM THE SUBSEQUENT METHOD DEFINED CALL ON
|
123
|
+
# SELF.CLASS
|
124
|
+
self.class.instance_variable_set "@no_collision", ß # FIXME: This is too clumsy
|
125
|
+
self.class.module_eval write_unit_method( ß, prefixes, units, exps )
|
126
|
+
SY::ExpressibleInUnits.method_family << self.class.instance_method( ß )
|
16
127
|
end
|
17
128
|
rescue NameError => err
|
18
129
|
puts "NameError raised: #{err}" if SY::DEBUG
|
@@ -27,8 +138,10 @@ module SY::ExpressibleInUnits
|
|
27
138
|
def respond_to_missing? ß, *args, &block
|
28
139
|
# dismiss :to_... methods and /begin|end/ (3rd party bug workaround)
|
29
140
|
return false if ß.to_s =~ /to_.+|begin|end/
|
30
|
-
begin
|
31
|
-
anti_recursion_exec token: ß, var: :@SY_Units_rmiss do
|
141
|
+
!! begin
|
142
|
+
anti_recursion_exec token: ß, var: :@SY_Units_rmiss do
|
143
|
+
parse_unit_symbol ß
|
144
|
+
end
|
32
145
|
rescue NameError, SY::ExpressibleInUnits::RecursionError
|
33
146
|
false
|
34
147
|
else
|
@@ -50,26 +163,13 @@ module SY::ExpressibleInUnits
|
|
50
163
|
# Arrays must be of equal length. (Note: 'ß' is 'symbol', 'ς' is 'string')
|
51
164
|
#
|
52
165
|
def write_unit_method ß, prefixes, units, exponents
|
53
|
-
known_units = SY::Unit.instances
|
54
|
-
# A procedure to find unit based on name or abbreviation:
|
55
|
-
find_unit = lambda do |ς|
|
56
|
-
known_units.find { |u| u.name.to_s == ς || u.short.to_s == ς }
|
57
|
-
end
|
58
|
-
# Return prefix method or empty ς if not necessary.
|
59
|
-
prefix_method_ς = lambda do |prefix|
|
60
|
-
puts "About to call PREFIX TABLE.to_full with #{prefix}" if SY::DEBUG
|
61
|
-
full_prefix = SY::PREFIX_TABLE.to_full( prefix )
|
62
|
-
full_prefix == '' ? '' : ".#{full_prefix}"
|
63
|
-
end
|
64
|
-
# Return exponentiation string (suffix) or empty ς if not necessary.
|
65
|
-
exponentiation_ς = lambda do |exp| exp == 1 ? '' : " ** #{exp}" end
|
66
166
|
# Prepare prefix / unit / exponent triples for making factor strings:
|
67
167
|
triples = [ prefixes, units, exponents ].transpose
|
68
168
|
# A procedure for triple processing before use:
|
69
169
|
process_triple = lambda do |pfx, unit_ς, exp|
|
70
|
-
[ find_unit
|
71
|
-
|
72
|
-
|
170
|
+
[ ::SY::ExpressibleInUnits.find_unit( unit_ς ).name.to_s.upcase,
|
171
|
+
::SY::ExpressibleInUnits.prefix_method_string( pfx ),
|
172
|
+
::SY::ExpressibleInUnits.exponentiation_string( exp ) ]
|
73
173
|
end
|
74
174
|
# Method skeleton:
|
75
175
|
if triples.size == 1 && triples.first[-1] == 1 then
|
@@ -2,9 +2,9 @@
|
|
2
2
|
# Here, fixed assets of the main module are set up.
|
3
3
|
#
|
4
4
|
module SY
|
5
|
-
QuantityError = Class.new
|
6
|
-
DimensionError = Class.new
|
7
|
-
MagnitudeError = Class.new
|
5
|
+
QuantityError = Class.new TypeError # mixing incompatible quantities
|
6
|
+
DimensionError = Class.new TypeError # mixing incompatible dimensions
|
7
|
+
MagnitudeError = Class.new TypeError # creating impossible magnitude
|
8
8
|
|
9
9
|
BASE_DIMENSIONS = { # Basic physical dimensions.
|
10
10
|
L: :LENGTH,
|
@@ -273,5 +273,12 @@ module SY
|
|
273
273
|
SY::Magnitude.of qnt, args
|
274
274
|
end
|
275
275
|
|
276
|
-
|
276
|
+
# Convenitence constructor of amounts (SY::Amount if the standard
|
277
|
+
# dimensionless quantity of SY).
|
278
|
+
#
|
279
|
+
def Amount number
|
280
|
+
SY::Amount.relative.magnitude( number )
|
281
|
+
end
|
282
|
+
|
283
|
+
module_function :Dimension, :Quantity, :Unit, :Magnitude, :Amount
|
277
284
|
end
|
data/lib/sy/magnitude.rb
CHANGED
@@ -1,17 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
# This
|
3
|
-
#
|
4
|
-
# an absolute magnitudes may not be negative – physical amounts cannot have
|
5
|
-
# negative number of unit objects. But as for the <em>difference</me> between
|
6
|
-
# magnitudes, this can be positive as well as negative – relative magnitudes
|
7
|
-
# are used for this purpose.
|
8
|
-
#
|
9
|
-
# While ordinary #+ and #- methods of absolute magnitudes return relative
|
10
|
-
# magnitudes, absolute magnitudes have additional methods #add and #subtract,
|
11
|
-
# that return absolute magnitudes (it is the responsibility of the caller to
|
12
|
-
# avoid negative results). Furthermore, absolute magnitudes have one more
|
13
|
-
# special method #take, which perfoms #subtract whilst protecting against
|
14
|
-
# subtraction of more than, there is to take.
|
2
|
+
# This module stores assets pertaining to a magnitude – be it absolute magnitude
|
3
|
+
# (physical number of unit objects), or relative magnitude (magnitude differnce).
|
15
4
|
#
|
16
5
|
module SY::Magnitude
|
17
6
|
class << self
|
@@ -169,6 +158,18 @@ module SY::Magnitude
|
|
169
158
|
end
|
170
159
|
end
|
171
160
|
|
161
|
+
# Same magnitudes <em>and</em> same (#eql) quantities.
|
162
|
+
#
|
163
|
+
def eql other
|
164
|
+
raise NotImplementedError
|
165
|
+
end
|
166
|
+
|
167
|
+
# Percent operator (remainder after division)
|
168
|
+
#
|
169
|
+
def %
|
170
|
+
raise NotImplementedError
|
171
|
+
end
|
172
|
+
|
172
173
|
# Type coercion for magnitudes.
|
173
174
|
#
|
174
175
|
def coerce m2
|
@@ -192,8 +193,7 @@ module SY::Magnitude
|
|
192
193
|
"method collision with another library?"
|
193
194
|
end
|
194
195
|
when SY::Magnitude then
|
195
|
-
|
196
|
-
amount / m2.( quantity ).amount # reframe before division
|
196
|
+
quantity.measure( of: m2.quantity ).w.( amount ) / m2.amount
|
197
197
|
else
|
198
198
|
raise TypeError, "Unexpected type for Magnitude#in method! (#{m2.class})"
|
199
199
|
end
|
data/lib/sy/quantity.rb
CHANGED
@@ -12,7 +12,7 @@ class SY::Quantity
|
|
12
12
|
RELATIVE_QUANTITY_NAME_SUFFIX = "±"
|
13
13
|
|
14
14
|
attr_reader :MagnitudeModule, :Magnitude, :Unit
|
15
|
-
attr_reader :dimension, :composition
|
15
|
+
attr_reader :dimension, :composition, :units
|
16
16
|
|
17
17
|
class << self
|
18
18
|
# Dimension-based quantity constructor. Examples:
|
@@ -58,6 +58,7 @@ class SY::Quantity
|
|
58
58
|
#
|
59
59
|
def initialize( relative: nil, composition: nil, of: nil, measure: nil, amount: nil, **nn )
|
60
60
|
puts "Quantity init relative: #{relative}, composition: #{composition}, measure: #{measure}, #{nn}" if SY::DEBUG
|
61
|
+
@units = [] # array of units as favored by this quantity
|
61
62
|
@relative = relative
|
62
63
|
if composition.nil? then
|
63
64
|
puts "Composition not given, dimension expected." if SY::DEBUG
|
@@ -233,12 +234,6 @@ class SY::Quantity
|
|
233
234
|
Unit().standard
|
234
235
|
end
|
235
236
|
|
236
|
-
# Presents an array of units ordered as favored by this quantity.
|
237
|
-
#
|
238
|
-
def units
|
239
|
-
@units ||= []
|
240
|
-
end
|
241
|
-
|
242
237
|
# Constructs a new absolute magnitude of this quantity.
|
243
238
|
#
|
244
239
|
def magnitude amount
|
@@ -248,7 +243,8 @@ class SY::Quantity
|
|
248
243
|
# Constructs a new unit of this quantity.
|
249
244
|
#
|
250
245
|
def unit **nn
|
251
|
-
Unit().new( nn.update( of: self ) )
|
246
|
+
Unit().new( nn.update( of: self ) )
|
247
|
+
.tap { |u| ( units << u ).uniq! } # add it to the @units array
|
252
248
|
end
|
253
249
|
|
254
250
|
# Constructor of a new standard unit (replacing current @standard_unit).
|
@@ -380,7 +376,7 @@ class SY::Quantity
|
|
380
376
|
def Magnitude
|
381
377
|
@Magnitude or
|
382
378
|
( mmod = MagnitudeModule()
|
383
|
-
mixin = relative? ?
|
379
|
+
mixin = relative? ? SY::SignedMagnitude : SY::AbsoluteMagnitude
|
384
380
|
qnt_ɴ_λ = -> { name ? "#{name}@%s" : "#<Quantity:#{object_id}@%s>" }
|
385
381
|
|
386
382
|
@Magnitude = Class.new do
|
data/lib/sy/signed_magnitude.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Qualities specific to relative magnitudes.
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Qualities specific to relative magnitudes (mixin).
|
4
3
|
#
|
5
4
|
module SY::SignedMagnitude
|
6
5
|
# Relative magnitude constructor takes :quantity (alias :of) argument and
|
7
6
|
# :amount argument. Amount is allowed to be negative.
|
8
7
|
#
|
9
|
-
def initialize
|
10
|
-
|
11
|
-
|
12
|
-
@amount = case
|
13
|
-
when Numeric then
|
8
|
+
def initialize( of: nil, amount: nil )
|
9
|
+
fail ArgumentError, "Quantity (:of) argument missing!" if of.nil?
|
10
|
+
@quantity = of
|
11
|
+
@amount = case amount
|
12
|
+
when Numeric then amount
|
14
13
|
when nil then 1
|
15
14
|
else
|
16
15
|
begin
|
17
|
-
|
16
|
+
amount.( @quantity ).amount
|
18
17
|
rescue NameError, NoMethodError
|
19
|
-
|
18
|
+
amount
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
data/lib/sy/unit.rb
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
# of a metrological quantity.
|
5
5
|
#
|
6
6
|
module SY::Unit
|
7
|
+
PROTECTED_NAMES = [ "kilogram" ]
|
8
|
+
|
7
9
|
def self.pre_included target
|
8
10
|
class << target
|
9
11
|
# Overriding this method from NameMagic mixin ensures, that all Unit
|
@@ -12,29 +14,29 @@ module SY::Unit
|
|
12
14
|
def namespace
|
13
15
|
SY::Unit
|
14
16
|
end
|
17
|
+
end
|
18
|
+
end # def self.pre_included
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
20
|
+
# Tweaking instance accessor from NameMagic to make it accept unit
|
21
|
+
# abbreviations and unit names regardless of capitalization
|
22
|
+
#
|
23
|
+
def self.instance arg
|
24
|
+
begin
|
25
|
+
super # let's first try the original method
|
26
|
+
rescue NameError # if we fail...
|
27
|
+
begin # second in order, let's try whether it's an abbreviation
|
28
|
+
super instances.find { |inst|
|
29
|
+
inst.abbreviation.to_s == arg.to_s if inst.abbreviation
|
30
|
+
}
|
31
|
+
rescue NameError, TypeError
|
32
|
+
begin # finally, let's try upcase if we have all-downcase arg
|
33
|
+
super arg.to_s.upcase
|
34
|
+
rescue NameError # if not, tough luck
|
35
|
+
raise NameError, "Unknown unit symbol: #{which}"
|
34
36
|
end
|
35
37
|
end
|
36
|
-
end
|
37
|
-
end # def self.
|
38
|
+
end
|
39
|
+
end # def self.instance
|
38
40
|
|
39
41
|
def self.included target
|
40
42
|
target.class_exec do
|
@@ -42,13 +44,30 @@ module SY::Unit
|
|
42
44
|
name_set_closure do |name, new_instance, old_name|
|
43
45
|
ɴ = name.to_s
|
44
46
|
up, down = ɴ.upcase, ɴ.downcase
|
47
|
+
# Check case (only all-upper or all-lower is acceptable).
|
45
48
|
unless ɴ == up || ɴ = down
|
46
49
|
raise NameError, "Unit must be either all-upper or all-lower case!"
|
47
50
|
end
|
51
|
+
# Reject the names starting with a full prefix.
|
48
52
|
conflicter = SY::PREFIX_TABLE.full_prefixes
|
49
53
|
.find { |prefix| down.starts_with? prefix unless prefix.empty? }
|
50
54
|
raise NameError, "Name #{ɴ} starts with #{conflicter}- prefix" unless
|
51
|
-
down
|
55
|
+
SY::Unit::PROTECTED_NAMES.include? down if conflicter
|
56
|
+
# Warn about the conflicts in modules where the SY::ExpressibleInUnits
|
57
|
+
# mixin is included.
|
58
|
+
if new_instance.warns? then
|
59
|
+
w = ::SY::ExpressibleInUnits::COLLISION_WARNING
|
60
|
+
::SY::ExpressibleInUnits.included_in.each do |ɱ|
|
61
|
+
im = ɱ.instance_methods
|
62
|
+
# puts ɱ, "class: #{ɱ.class}"
|
63
|
+
# puts im.size
|
64
|
+
# puts down
|
65
|
+
# puts im.include? down
|
66
|
+
warn w % [down, ɱ] if im.include? down
|
67
|
+
abbrev = new_instance.abbreviation
|
68
|
+
warn w % [abbrev, ɱ] if im.include? abbrev
|
69
|
+
end
|
70
|
+
end
|
52
71
|
up.to_sym
|
53
72
|
end
|
54
73
|
|
@@ -80,17 +99,8 @@ module SY::Unit
|
|
80
99
|
class << self
|
81
100
|
# Constructor of units of a given quantity.
|
82
101
|
#
|
83
|
-
def of
|
84
|
-
|
85
|
-
qnt = case args.size
|
86
|
-
when 0 then
|
87
|
-
ꜧ.must_have( :quantity, syn!: :of )
|
88
|
-
ꜧ.delete :quantity
|
89
|
-
when 1 then args.shift
|
90
|
-
else
|
91
|
-
raise AErr, "Too many ordered arguments!"
|
92
|
-
end
|
93
|
-
return qnt.unit *( ꜧ.empty? ? args : args << ꜧ )
|
102
|
+
def of quantity, **nn
|
103
|
+
quantity.unit **nn
|
94
104
|
end
|
95
105
|
|
96
106
|
# Standard unit constructor. In absence of other named arguments, standard
|
@@ -132,7 +142,13 @@ module SY::Unit
|
|
132
142
|
# Unlike ordinary magnitudes, units can have names and abbreviations.
|
133
143
|
#
|
134
144
|
attr_reader :abbreviation
|
135
|
-
alias
|
145
|
+
alias short abbreviation
|
146
|
+
|
147
|
+
# Whether the unit warns when the module in which unit method mixin is
|
148
|
+
# included contains blatant name collisions with this unit name/abbreviation.
|
149
|
+
#
|
150
|
+
attr_accessor :warns
|
151
|
+
alias warns? warns
|
136
152
|
|
137
153
|
# Unit abbreviation setter.
|
138
154
|
#
|
@@ -153,16 +169,20 @@ module SY::Unit
|
|
153
169
|
ɴ = super
|
154
170
|
return ɴ ? ɴ.to_s.downcase.to_sym : nil
|
155
171
|
end
|
172
|
+
alias ɴ name
|
156
173
|
|
157
174
|
# Constructor of units provides support for one additional named argument:
|
158
175
|
# :abbreviation, alias :short. (This is in addition to :name, alias :ɴ named
|
159
176
|
# argument provided by NameMagic.) As a general rule, only named units unit
|
160
177
|
# should be given abbreviations. In choosing unit names and abbreviations,
|
161
178
|
# ambiguity with regard to standard prefixes and abbreviations thereof should
|
162
|
-
# also be avoided.
|
179
|
+
# also be avoided. Another argument, :warns, Boolean, <em>true</em> by
|
180
|
+
# default, determines whether the method warns about name collisions with
|
181
|
+
# other methods defined where the SY::ExpressibleInUnits mixin is included.
|
163
182
|
#
|
164
|
-
def initialize( short: nil, **nn )
|
183
|
+
def initialize( short: nil, warns: true, **nn )
|
165
184
|
@abbreviation = short.to_sym if short
|
185
|
+
@warns = warns # does this unit care about blatant name collisions?
|
166
186
|
|
167
187
|
# FIXME: Here, we would have to watch out for :amount being set
|
168
188
|
# if it is a number, amount is in standard units
|
@@ -170,7 +190,7 @@ module SY::Unit
|
|
170
190
|
# it estableshes a relationship between this and that quantity. It means that
|
171
191
|
# the unit amount automatically becomes ... one ... and such relationship can
|
172
192
|
# only be established for standard quantity
|
173
|
-
super
|
193
|
+
super nn
|
174
194
|
end
|
175
195
|
|
176
196
|
# Addition: Unit is converted to a magnitude before the operation.
|
@@ -213,7 +233,7 @@ module SY::Unit
|
|
213
233
|
# Reframing: Unit is converted to a magnitude before reframing.
|
214
234
|
#
|
215
235
|
def reframe other_quantity
|
216
|
-
|
236
|
+
to_magnnitude.reframe( other_quantity )
|
217
237
|
end
|
218
238
|
|
219
239
|
# Unit as string.
|
data/lib/sy/version.rb
CHANGED
data/lib/sy.rb
CHANGED
@@ -62,22 +62,17 @@ module SY
|
|
62
62
|
# Let SY::Amount be a standard dimensionless quantity:
|
63
63
|
Amount = Quantity.standard of: Dimension.zero
|
64
64
|
|
65
|
-
# Convenience constructor of amounts:
|
66
|
-
def self.Amount number
|
67
|
-
SY::Amount.relative.magnitude number
|
68
|
-
end
|
69
|
-
|
70
|
-
# AVOGADRO_CONSTANT (Nᴀ) is a certain well-known amount of things:
|
71
|
-
Nᴀ = AVOGADRO_CONSTANT = SY.Amount 6.02214e23
|
72
|
-
|
73
65
|
# Let SY::UNIT be a standard unit of SY::Amount. Note that the upcase name
|
74
66
|
# of the constant "UNIT" implies, via YSupport's NameMagic mixin, that the
|
75
67
|
# name of the object becomes :unit and that it is possible to use syntax
|
76
68
|
# such as 42.unit to create magnitudes of SY::Amount.
|
77
69
|
UNIT = Unit.standard of: Amount
|
78
70
|
|
71
|
+
# AVOGADRO_CONSTANT (Nᴀ) is a certain well-known amount of things:
|
72
|
+
Nᴀ = AVOGADRO_CONSTANT = 6.02214e23
|
73
|
+
|
79
74
|
# Let SY::MoleAmount be another dimensionless quantity:
|
80
|
-
MoleAmount = Quantity.dimensionless
|
75
|
+
MoleAmount = Quantity.dimensionless # TODO: coerces: Amount
|
81
76
|
|
82
77
|
# And let SY::MOLE be its standard unit, related to SY::Amount via Nᴀ:
|
83
78
|
MOLE = Unit.standard of: MoleAmount, short: "mol", amount: Nᴀ.unit
|
@@ -138,13 +133,24 @@ module SY
|
|
138
133
|
# Celsius temperature is a little bit peculiar in that it has offset of
|
139
134
|
# 273.15.K with respect to Kelvin temperature, and I am not sure whether
|
140
135
|
# at this moment SY is handling this right. But nevertheless:
|
141
|
-
CelsiusTemperature = Quantity.of :Θ
|
136
|
+
CelsiusTemperature = Quantity.of :Θ # coerces_to: Temperature
|
137
|
+
|
138
|
+
CELSIUS_MEASURE = SY::Measure.simple_offset( TRIPLE_POINT_OF_WATER.in( :K ) )
|
142
139
|
|
143
140
|
# Degree celsius is SY::CELSIUS
|
144
|
-
CELSIUS = Unit.standard of: CelsiusTemperature,
|
141
|
+
CELSIUS = Unit.standard( of: CelsiusTemperature,
|
142
|
+
short: '°C', measure: CELSIUS_MEASURE )
|
145
143
|
|
146
144
|
class << CelsiusTemperature
|
147
145
|
# FIXME: Patch CelsiusTemperature to make it work with SY::Temperature
|
146
|
+
# 1.°C + 1.K #=> 2.°C
|
147
|
+
# 1.°C - 1.°C #=> 0.K (unambiguous)
|
148
|
+
# 1.°C + 1.°C #=> QuantityError (ambiguous)
|
149
|
+
# 1.K +- 1.°C #=> QuantityError (ambiguous)
|
150
|
+
# 1.°C +- 1.K #=> 2.°C
|
151
|
+
# 1.mm.°C⁻¹ #=> 1.mm.K⁻¹ etc.
|
152
|
+
# 1.mm.°C #=> 1.mm.K etc.
|
153
|
+
# 1.mm */ 1.°C #=> QuantityError (ambiguous)
|
148
154
|
end
|
149
155
|
|
150
156
|
# alias :°C :celsius # with U+00B0 DEGREE SIGN
|
@@ -182,7 +188,7 @@ module SY
|
|
182
188
|
Volume = Length ** 3
|
183
189
|
|
184
190
|
# SY::LitreVolume is another quantity of the same dimension as SY::Volume:
|
185
|
-
LitreVolume = Quantity.of Volume.dimension
|
191
|
+
LitreVolume = Quantity.of Volume.dimension # TODO: coerces_to: Volume
|
186
192
|
|
187
193
|
# SY::LITRE is the standard unit of SY::LitreVolume:
|
188
194
|
LITRE = Unit.standard of: LitreVolume, short: "l", amount: 1.dm³
|
data/test/sy_test.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#encoding: utf-8
|
2
1
|
#! /usr/bin/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
# **************************************************************************
|
5
5
|
# THIS IS SPEC-STYLE TEST FILE FOR SY PHYSICAL UNITS LIBRARY
|
@@ -169,12 +169,15 @@ describe SY::Quantity, SY::Magnitude do
|
|
169
169
|
1.inch.( @inch_length ).must_equal 1.inch
|
170
170
|
1.inch.( SY::Length ).must_equal 2.54.cm
|
171
171
|
@inch_length.magnitude( 1 ).to_s.must_equal "1.”"
|
172
|
-
1.inch.in( :mm ).
|
172
|
+
1.inch.in( :mm ).must_be_within_epsilon 25.4
|
173
|
+
assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
176
177
|
describe "expected behavior" do
|
177
178
|
it "should" do
|
179
|
+
assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
180
|
+
|
178
181
|
# Length quantity and typical units
|
179
182
|
SY::METRE.must_be_kind_of SY::Unit
|
180
183
|
SY::METRE.absolute?.must_equal true
|
@@ -289,7 +292,7 @@ describe SY::Quantity, SY::Magnitude do
|
|
289
292
|
1.s⁻¹.must_equal 1.s ** -1
|
290
293
|
q1 = ( 1.s⁻¹ ).quantity
|
291
294
|
q1.composition.to_hash.must_equal( { SY::Time => -1 } )
|
292
|
-
|
295
|
+
|
293
296
|
q2 = ( 1 / 1.s ).quantity
|
294
297
|
q2.composition.to_hash.must_equal( { SY::Time => -1 } )
|
295
298
|
|
@@ -311,8 +314,8 @@ describe SY::Quantity, SY::Magnitude do
|
|
311
314
|
end.must_include :M
|
312
315
|
SY::Unit.instance_names.must_include :mole
|
313
316
|
# Avogadro's number is defined directly in SY
|
314
|
-
1.mol.quantity.object_id.must_equal SY::Nᴀ.( SY::MoleAmount ).quantity.object_id
|
315
|
-
SY::Nᴀ.( SY::MoleAmount ).must_equal 1.mol
|
317
|
+
1.mol.quantity.object_id.must_equal SY::Nᴀ.unit.( SY::MoleAmount ).quantity.object_id
|
318
|
+
SY::Nᴀ.unit.( SY::MoleAmount ).must_equal 1.mol
|
316
319
|
0.7.mol.l⁻¹.amount.must_equal 0.7
|
317
320
|
1.M.must_equal 1.mol.l⁻¹.( SY::Molarity )
|
318
321
|
# (if #reframe conversion method is not used, different quantities
|
@@ -345,16 +348,20 @@ describe SY::Quantity, SY::Magnitude do
|
|
345
348
|
1e-23.J.K⁻¹.must_equal 1.0e-20.mJ.K⁻¹
|
346
349
|
|
347
350
|
|
351
|
+
|
348
352
|
# pascal
|
349
353
|
( 1.N / 1.m ** 2 ).( SY::Pressure ).must_be_within_epsilon 1.Pa, 1e-9
|
350
354
|
|
351
355
|
# watt
|
352
356
|
( 1.V * 1.A ).( SY::Power ).must_be_within_epsilon 1.W, 1e-9
|
353
357
|
|
358
|
+
|
354
359
|
# pretty representation
|
355
|
-
|
360
|
+
assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
361
|
+
( 1.m / 3.s ).to_s.must_equal( "0.333.m.s⁻¹" ) # FIXME: Discovered a problem here, uncomment the line below
|
362
|
+
# assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
356
363
|
( 1.m / 7.01e7.s ).to_s.must_equal( "1.43e-08.m.s⁻¹" )
|
357
|
-
|
364
|
+
|
358
365
|
assert_equal 1.m, 1.s * 1.m.s⁻¹
|
359
366
|
assert_equal 1.µM.s⁻¹, 1.µM / 1.s
|
360
367
|
assert_equal 1.m.s⁻¹, 1.m.s( -1 )
|
@@ -365,7 +372,7 @@ describe SY::Quantity, SY::Magnitude do
|
|
365
372
|
assert_equal SY::Amount( 1 ), 1.µM / ( 1.µM + 0.µM )
|
366
373
|
assert_equal 1.µM, 1.µM * 1.µM / ( 1.µM + 0.µM )
|
367
374
|
assert_in_epsilon 1.µM, 1.µmol / 1.dm( 3 ).( SY::LitreVolume )
|
368
|
-
|
375
|
+
|
369
376
|
assert_equal SY::Molarity.relative, 1.mol.l⁻¹.quantity
|
370
377
|
|
371
378
|
assert_equal 1 / SY::Time, 1 / SY::Time
|
@@ -380,6 +387,11 @@ describe SY::Quantity, SY::Magnitude do
|
|
380
387
|
assert_equal Matrix[[2.m, 3.m], [4.m, 5.m]],
|
381
388
|
Matrix[[1.m, 2.m], [3.m, 4.m]] + Matrix[[1.m, 1.m], [1.m, 1.m]]
|
382
389
|
assert_equal Matrix[[5.µM]], Matrix[[1.µM]] + Matrix[[2.µM.s⁻¹]] * Matrix[[2.s]]
|
390
|
+
XOXO = SY::Unit.of SY::Volume, amount: 1.l
|
391
|
+
assert_equal 1.l.( SY::Volume ), 1.xoxo.( SY::Volume )
|
392
|
+
assert_equal SY::TRIPLE_POINT_OF_WATER, 0.°C.( SY::Temperature )
|
393
|
+
assert_equal 273.15, 0.°C.in( :K )
|
394
|
+
# assert_equal SY::TRIPLE_POINT_OF_WATER, 0.°C # so far unfinished coercion behavior
|
383
395
|
end
|
384
396
|
end
|
385
397
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- boris
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|