Module: SY::ExpressibleInUnits

Included in:
Magnitude
Defined in:
lib/sy/expressible_in_units.rb

Overview

This mixin provides ability to respond to SY unit symbol methods.

Defined Under Namespace

Modules: DetectRedefine

Constant Summary collapse

COLLISION_WARNING =
"Unit %s collision, method already defined on %s!"
REDEFINE_WARNING =
"Method %s being defined on %s shadows SY unit method!"
RecursionError =
Class.new StandardError

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(ß, *args, &block) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/sy/expressible_in_units.rb', line 123

def method_missing ß, *args, &block
  return self if ß.to_s =~ /begin|end/ # 3rd party bug workaround
  super if ß.to_s =~ /to_.+/ # dissmiss :to_..., esp. :to_ary
  begin # prevent recurrent call of method_missing for the same symbol
    anti_recursion_exec token: ß, var: :@SY_Units_mmiss do
      puts "Method missing: '#{ß}'" if SY::DEBUG
      prefixes, units, exps = parse_unit_symbol ß
      # Define the unit method on self.class:
      # I'D HAVE TO PERFORM THE COLLISION CHECK HERE
      # IF NO COLLISION, INFORM THE SUBSEQUENT METHOD DEFINED CALL ON
      # SELF.CLASS
      puts "parsed" if SY::DEBUG
      self.class.instance_variable_set "@no_collision", ß # FIXME: This is too clumsy
      self.class.module_eval write_unit_method( ß, prefixes, units, exps )
      SY::ExpressibleInUnits.method_family << self.class.instance_method( ß )
    end
  rescue NameError => err
    puts "NameError raised: #{err}" if SY::DEBUG
    super # give up
  rescue SY::ExpressibleInUnits::RecursionError
    super # give up
  else # actually invoke the method that we just defined
    send ß, *args, &block
  end
end

Class Method Details

.exponentiation_string(exp) ⇒ Object

Return exponentiation string (suffix) or empty ς if not necessary.



118
119
120
# File 'lib/sy/expressible_in_units.rb', line 118

def exponentiation_string exp
  exp == 1 ? '' : " ** #{exp}"
end

.find_unit(ς) ⇒ Object

Find unit based on name / abbreviation.



99
100
101
102
103
104
105
106
# File 'lib/sy/expressible_in_units.rb', line 99

def find_unit ς
  puts "searching for unit #{ς}" if SY::DEBUG
  known_units.find do |u|
    u.name.to_s.downcase == ς.downcase &&
      ( ς == ς.downcase || ς == ς.upcase ) ||
      u.short.to_s == ς
  end
end

.included(receiver) ⇒ Object

#included hook of this module is set to perfom a casual check for blatant name collisions between SY::Unit-implied methods, and existing methods of the include receiver.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/sy/expressible_in_units.rb', line 48

def included receiver
  included_in << receiver # keep track of where the mixin has been included
  # Warn if the receiver has potentially colliding methods.
  inst_methods = receiver.instance_methods
  w = COLLISION_WARNING % ["%s", receiver]
  known_units.each do |unit|
    next unless unit.warns?
    name, short = unit.name, unit.abbreviation
    warn w % "name method ##{name}" if inst_methods.include? name
    warn w % "abbreviation method ##{short}" if inst_methods.include? short
  end
  # Warn if shadowing methods are defined on the receiver later.
  if receiver.is_a? Class
    receiver.extend ::SY::ExpressibleInUnits::DetectRedefine
  end
end

.included_inObject

Modules in which this mixin has been included.



67
68
69
# File 'lib/sy/expressible_in_units.rb', line 67

def included_in
  @included_in ||= []
end

.known_unitsObject

Currently defined unit instances, if any.



82
83
84
85
86
87
88
89
# File 'lib/sy/expressible_in_units.rb', line 82

def known_units
  begin
    unit_namespace.instances
  rescue NoMethodError 
    [] # no #instances method defined yet
  end
    .tap { |r| puts "Known units are #{r}" if SY::DEBUG }
end

.method_familyObject

All methods defined by this mixin.



93
94
95
# File 'lib/sy/expressible_in_units.rb', line 93

def method_family
  @method_family ||= []
end

.prefix_method_string(prefix) ⇒ Object

Return prefix method or empty string, if prefix method not necessary.



110
111
112
113
114
# File 'lib/sy/expressible_in_units.rb', line 110

def prefix_method_string prefix
  puts "About to call PREFIX TABLE.to_full with #{prefix}" if SY::DEBUG
  full_prefix = SY::PREFIX_TABLE.to_full( prefix )
  full_prefix == '' ? '' : ".#{full_prefix}"
end

.unit_namespaceObject

Unit namespace.



73
74
75
76
77
78
# File 'lib/sy/expressible_in_units.rb', line 73

def unit_namespace
  begin
    SY::Unit
  rescue NameError # no SY::Unit defined yet
  end
end

Instance Method Details

#respond_to_missing?(ß, *args, &block) ⇒ Boolean

Returns:

  • (Boolean)


149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/sy/expressible_in_units.rb', line 149

def respond_to_missing? ß, *args, &block
  # dismiss :to_... methods and /begin|end/ (3rd party bug workaround)
  return false if ß.to_s =~ /to_.+|begin|end/
  !! begin
    anti_recursion_exec token: ß, var: :@SY_Units_rmiss do
      parse_unit_symbol ß
    end
  rescue NameError, SY::ExpressibleInUnits::RecursionError
    false
  else
    true
  end
end