lib/ecoportal/api/common/content/class_helpers.rb in ecoportal-api-v2-0.8.30 vs lib/ecoportal/api/common/content/class_helpers.rb in ecoportal-api-v2-0.8.31

- old
+ new

@@ -1,5 +1,6 @@ +require 'securerandom' module Ecoportal module API module Common module Content module ClassHelpers @@ -8,13 +9,14 @@ # Class resolver # @note it caches the resolved `klass`es # @raise [Exception] when could not resolve if `exception` is `true` # @param klass [Class, String, Symbol] the class to resolve + # @param source_class [Class] when the reference to `klass` belongs to a different inheritance chain. # @param exception [Boolean] if it should raise exception when could not resolve # @return [Class] the `Class` constant - def resolve_class(klass, exception: true) + def resolve_class(klass, source_class: self, exception: true) @resolved ||= {} @resolved[klass] ||= case klass when Class klass @@ -23,22 +25,28 @@ Kernel.const_get(klass) rescue NameError => e raise if exception end when Symbol - resolve_class(self.send(klass)) + 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 # Helper to normalize `key` into a correct `ruby` **constant name** + # @note it removes namespace syntax `::` # @param key [String, Symbol] to be normalized # @return [String] a correct constant name def to_constant(key) - str_name = key.to_s.strip.split(/[\-\_ ]/i).compact.map do |str| - str.slice(0).upcase + str.slice(1..-1).downcase + 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 # Helper to create an instance variable `name` # @param [String, Symbol] the name of the variable @@ -47,24 +55,31 @@ str = name.to_s str = "@#{str}" unless str.start_with?("@") str end + # Generates random ids in hexadecimal to use in class name generation. + # @param len [Integeter] length of the `uid` + # @return [String] a random unique id of length `len` + def uid(len = 8); + SecureRandom.hex(len/2) + 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 namespace [Class, String] an existing `constant` (class or module) the new class will be namespaced on # @yield [child_class] configure the new class # @yieldparam child_class [Class] the new class # @return [Class] the new generated class - def new_class(name, inherits:) + def new_class(name = "Child#{uid}", inherits: self, namespace: inherits) name = name.to_sym.freeze class_name = to_constant(name) - full_class_name = "#{inherits}::#{class_name}" - unless target_class = resolve_class(full_class_name, exception: false) + unless target_class = resolve_class("#{namespace}::#{class_name}", exception: false) target_class = Class.new(inherits) - self.const_set class_name, target_class + Kernel.const_get(namespace.to_s).const_set class_name, target_class end target_class.tap do |klass| yield(klass) if block_given? end @@ -136,10 +151,9 @@ 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 end end