sy 2.0.4 → 2.0.5
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.
- 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
|