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