Module: Eco::Language::Models::ClassHelpers

Included in:
Data::Hashes::ArrayDiff
Defined in:
lib/eco/language/models/class_helpers.rb

Constant Summary collapse

NOT_USED =
"no_used!"

Instance Method Summary collapse

Instance Method Details

#class_resolver(name, klass) ⇒ Object



12
13
14
15
# File 'lib/eco/language/models/class_helpers.rb', line 12

def class_resolver(name, klass)
  define_singleton_method(name) { resolve_class(klass) }
  define_method(name) { self.class.resolve_class(klass) }
end

#inheritable_attrs(*attrs) ⇒ Object

Builds the attr_reader and attr_writer of attrs and registers the associated instance variable as inheritable.



109
110
111
112
113
114
115
116
# File 'lib/eco/language/models/class_helpers.rb', line 109

def inheritable_attrs(*attrs)
  attrs.each do |attr|
    class_eval %(
      class << self; attr_accessor :#{attr} end
    )
  end
  inheritable_class_vars(*attrs)
end

#inheritable_class_vars(*vars) ⇒ Object

Note:

Keeps track on class instance variables that should be inherited by child classes. TODO: this separates the logic of the method to the instance var. Think if would be possible to join them somehow.



103
104
105
106
# File 'lib/eco/language/models/class_helpers.rb', line 103

def inheritable_class_vars(*vars)
  @inheritable_class_vars ||= [:inheritable_class_vars]
  @inheritable_class_vars += vars
end

#inherited(subclass) ⇒ Object

Note:
  • values of the instance variables are copied as they are (no dups or clones)
  • the above means: avoid methods that change the state of the mutable object on it
  • mutating methods would reflect the changes on other classes as well
  • therefore, freeze will be called on the values that are inherited.

This callback method is called whenever a subclass of the current class is created.



124
125
126
127
128
129
130
# File 'lib/eco/language/models/class_helpers.rb', line 124

def inherited(subclass)
  inheritable_class_vars.each do |var|
    instance_var = instance_variable_name(var)
    value        = instance_variable_get(instance_var)
    subclass.instance_variable_set(instance_var, value.freeze)
  end
end

#instance_variable_name(name) ⇒ Object

Helper to create an instance variable name

Parameters:

  • the (String, Symbol)

    name of the variable



61
62
63
64
65
# File 'lib/eco/language/models/class_helpers.rb', line 61

def instance_variable_name(name)
  str = name.to_s
  str = "@#{str}" unless str.start_with?("@")
  str
end

#new_class(name = "Child#{uid}", inherits: self, namespace: inherits) {|child_class| ... } ⇒ Class

If the class for name exists, it returns it. Otherwise it generates it.

Parameters:

  • name (String, Symbol) (defaults to: "Child#{uid}")

    the name of the new class

  • inherits (Class) (defaults to: self)

    the parent class to inherit from

  • namespace (Class, String) (defaults to: inherits)

    an existing constant (class or module) the new class will be namespaced on

Yields:

  • (child_class)

    configure the new class

Yield Parameters:

  • child_class (Class)

    the new class

Returns:

  • (Class)

    the new generated class



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/eco/language/models/class_helpers.rb', line 74

def new_class(name = "Child#{uid}", inherits: self, namespace: inherits)
  name            = name.to_s.to_sym.freeze
  class_name      = to_constant(name)

  unless target_class = resolve_class("#{namespace}::#{class_name}", exception: false)
    target_class = Class.new(inherits)
    Kernel.const_get(namespace.to_s).const_set class_name, target_class
  end

  target_class.tap do |klass|
    yield(klass) if block_given?
  end
end

#redef_without_warning(const, value) ⇒ Object



7
8
9
10
# File 'lib/eco/language/models/class_helpers.rb', line 7

def redef_without_warning(const, value)
  self.class.send(:remove_const, const) if self.class.const_defined?(const)
  self.class.const_set(const, value)
end

#resolve_class(klass, source_class: self, exception: true) ⇒ Class

Note:

it caches the resolved klasses

Class resolver

Parameters:

  • klass (Class, String, Symbol)

    the class to resolve

  • source_class (Class) (defaults to: self)

    when the reference to klass belongs to a different inheritance chain.

  • exception (Boolean) (defaults to: true)

    if it should raise exception when could not resolve

Returns:

  • (Class)

    the Class constant

Raises:

  • (Exception)

    when could not resolve if exception is true



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/eco/language/models/class_helpers.rb', line 24

def resolve_class(klass, source_class: self, exception: true)
  @resolved ||= {}
  @resolved[klass] ||=
    case klass
      when Class
        klass
      when String
        begin
          Kernel.const_get(klass)
        rescue NameError => e
          raise if exception
        end
      when Symbol
        source_class.resolve_class(source_class.send(klass))
      when Hash
        referrer, referred = klass.first
        resolve_class(referred, source_class: referrer, exception: exception)
      else
        raise "Unknown class: #{klass}" if exception
    end
end

#to_constant(key) ⇒ String

Note:

it removes namespace syntax ::

Helper to normalize key into a correct ruby constant name

Parameters:

  • key (String, Symbol)

    to be normalized

Returns:

  • (String)

    a correct constant name



50
51
52
53
54
55
56
# File 'lib/eco/language/models/class_helpers.rb', line 50

def to_constant(key)
  str_name = key.to_s.strip.split(/::/).compact.map do |str|
    str.slice(0).upcase + str.slice(1..-1)
  end.join("").split(/[\-\_ :]+/i).compact.map do |str|
    str.slice(0).upcase + str.slice(1..-1)
  end.join("")
end

#used_param?(val) ⇒ Boolean

Note:

to effectivelly use this helper, you should initialize your target paramters with the constant NOT_USED

Helper to determine if a paramter has been used

Parameters:

  • val

    [] the value of the paramter

Returns:

  • (Boolean)

    true if value other than NOT_USED, false otherwise



93
94
95
# File 'lib/eco/language/models/class_helpers.rb', line 93

def used_param?(val)
  val != NOT_USED
end