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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd4b854cf2fceae1fb55d2e657f219807f88f6c6
4
- data.tar.gz: 730fec5dad579f42e482d49cbaad66b56e901685
3
+ metadata.gz: 666dbe08efdd97d407e931045726b9668f5bf3c2
4
+ data.tar.gz: aa13e418672f7223bcdb63683eb42c511974b2f2
5
5
  SHA512:
6
- metadata.gz: 2b2c9f2994e1d30db45eeeed40f952aad46e619a3935db909b2446f2b08f169525216cca37f49747abaf3dbc30213f38da505dd3e0ef0d4e84e678bf00d67807
7
- data.tar.gz: ca82955c039d47f775292f3b1ee9c92ef6cba91ea0b0b99af4ae4f2666f90b0f9366b267b8e98299d195d94902d82dcb50f68fb632a6e32a3fb9fe4d0f314030
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: ( fail ArgumentError, ":of argument missing!" ),
9
- amount: nil )
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
- ç.module_eval write_unit_method( ß, prefixes, units, exps )
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 parse_unit ß end
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.( unit_ς ).name.to_s.upcase,
71
- prefix_method_ς.( pfx ),
72
- exponentiation_ς.( exp ) ]
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 StandardError # mixing incompatible quantities
6
- DimensionError = Class.new StandardError # mixing incompatible dimensions
7
- MagnitudeError = Class.new StandardError # creating impossible magnitude
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
- module_function :Dimension, :Quantity, :Unit, :Magnitude
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 class here represents absolute magnitude – physical number of unit
3
- # objects, that make up the amount of some metrological quantity. Amount of
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
- return amount / m2.amount if quantity == m2.quantity
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 ) ).tap { |u| ( units << u ).uniq! }
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? ? ::SY::SignedMagnitude : ::SY::AbsoluteMagnitude
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
@@ -1,22 +1,21 @@
1
- #encoding: utf-8
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 **named_args
10
- @quantity = named_args[:quantity] || named_args[:of]
11
- amnt = named_args[:amount]
12
- @amount = case amnt
13
- when Numeric then amnt
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
- amnt.amount
16
+ amount.( @quantity ).amount
18
17
  rescue NameError, NoMethodError
19
- amnt
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
- # Tweaking instance accessor from NameMagic to make it accept unit
17
- # abbreviations and unit names regardless of capitalization
18
- #
19
- def instance arg
20
- begin
21
- super # let's first try the original method
22
- rescue NameError # if we fail...
23
- begin # second in order, let's try whether it's an abbreviation
24
- super instances.find { |inst|
25
- inst.abbreviation.to_s == arg.to_s if inst.abbreviation
26
- }
27
- rescue NameError, TypeError
28
- begin # finally, let's try upcase if we have all-downcase arg
29
- super arg.to_s.upcase
30
- rescue NameError # if not, tough luck
31
- raise NameError, "Unknown unit symbol: #{which}"
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 # class << target
37
- end # def self.pre_included
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 == 'kilogram' if conflicter
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 *args
84
- = args.extract_options!
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 :short :abbreviation
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( **nn )
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
- to_magnitude.reframe( other_quantity )
236
+ to_magnnitude.reframe( other_quantity )
217
237
  end
218
238
 
219
239
  # Unit as string.
data/lib/sy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module SY
2
- VERSION = "2.0.4"
2
+ VERSION = "2.0.5"
3
3
  DEBUG = false # debug mode switch - sometimes there are lines like
4
4
  # puts "something" if SY::DEBUG
5
5
  end
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, short: '°C', measure: SY::Measure.simple_offset( TRIPLE_POINT_OF_WATER.in( :K ) )
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 ).must_equal 25.4
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
- ( 1.m / 3.s ).to_s.must_equal( "0.333.m.s⁻¹" )
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
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-19 00:00:00.000000000 Z
11
+ date: 2013-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport