lib/eco/api/common/class_helpers.rb in eco-helpers-1.5.14 vs lib/eco/api/common/class_helpers.rb in eco-helpers-1.5.15
- old
+ new
@@ -1,11 +1,11 @@
module Eco
module API
module Common
module ClassHelpers
- # Creates an class and instance object methods with name `name` to resolve `klass` name
+ # Creates a class and instance object methods with name `name` to resolve `klass` name
def class_resolver(name, klass)
define_singleton_method(name) { resolve_class(klass) }
define_method(name) { self.class.resolve_class(klass) }
end
@@ -37,10 +37,19 @@
str_name = key.to_s.strip.split(/[\-\_ ]/i).compact.map do |str|
str.slice(0).upcase + str.slice(1..-1).downcase
end.join("")
end
+ # Helper to create an instance variable `name`
+ # @param [String, Symbol] the name of the variable
+ # @reutrn [String] the name of the created instance variable
+ def instance_variable_name(name)
+ str = name.to_s
+ str = "@#{str}" unless str.start_with?("@")
+ str
+ end
+
# If the class for `name` exists, it returns it. Otherwise it generates it.
# @param name [String, Symbol] the name of the new class
# @param inherits [Class] the parent class to _inherit_ from
# @param parent_space [String] parent namespace of the generated class, if not given: `self`
# @yield [child_class] configure the new class
@@ -88,9 +97,44 @@
# @param parent_class [Class] the parent class we want to find children of.
# @param direct [Boolean] it will only include direct child classes.
# @return [Boolean] `true` if the current class has child classes, and `false` otherwise.
def descendants?(parent_class: self, direct: false)
descendants(parent_class: parent_class, direct: direct).length > 0
+ end
+
+ # Keeps track on class instance variables that should be inherited by child classes.
+ # @note
+ # - subclasses will inherit the value as is at that moment
+ # - any change afterwards will be only on the specific class (in line with class instance variables)
+ # - adapted from https://stackoverflow.com/a/10729812/4352306
+ # TODO: this separates the logic of the method to the instance var. Think if would be possible to join them somehow.
+ def inheritable_class_vars(*vars)
+ @inheritable_class_vars ||= [:inheritable_class_vars]
+ @inheritable_class_vars += vars
+ end
+
+ # Builds the attr_reader and attr_writer of `attrs` and registers the associated instance variable as inheritable.
+ def inheritable_attrs(*attrs)
+ attrs.each do |attr|
+ class_eval %(
+ class << self; attr_accessor :#{attr} end
+ )
+ end
+ inheritable_class_vars(*attrs)
+ end
+
+ # This callback method is called whenever a subclass of the current class is created.
+ # @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.
+ 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
end
end
end