lib/amber_component/base.rb in amber_component-0.0.3 vs lib/amber_component/base.rb in amber_component-0.0.4

- old
+ new

@@ -46,10 +46,12 @@ extend Views::ClassMethods include Assets::InstanceMethods extend Assets::ClassMethods include Rendering::InstanceMethods extend Rendering::ClassMethods + include Props::InstanceMethods + extend Props::ClassMethods class << self include ::Memery memoize :asset_dir_path @@ -65,58 +67,75 @@ # @param subclass [Class] # @return [void] def inherited(subclass) super - # @type [Module] - method_body = proc do |**kwargs, &block| + method_body = lambda do |**kwargs, &block| subclass.render(**kwargs, &block) end parent_module = subclass.module_parent if parent_module.equal?(::Object) method_name = subclass.name - define_helper_method(subclass, Helpers::ComponentHelper, method_name, method_body) define_helper_method(subclass, Helpers::ComponentHelper, method_name.underscore, method_body) return end method_name = subclass.const_name - define_helper_method(subclass, parent_module.singleton_class, method_name, method_body) define_helper_method(subclass, parent_module.singleton_class, method_name.underscore, method_body) end - # @param component [Class] + # Gets or defines an anonymous module that + # will store all dynamically generated helper methods + # for the received module/class. + # # @param mod [Module, Class] + # @return [Module] + def helper_module(mod) + ivar_name = :@__amber_component_helper_module + mod.instance_variable_get(ivar_name)&.then { return _1 } + + helper_mod = mod.instance_variable_set(ivar_name, ::Module.new) + mod.include helper_mod + helper_mod + end + + # Defines an instance method on the given `mod` Module/Class. + # + # @param component [Class] + # @param target_mod [Module, Class] # @param method_name [String, Symbol] # @param body [Proc] - def define_helper_method(component, mod, method_name, body) - mod.define_method(method_name, &body) + def define_helper_method(component, target_mod, method_name, body) + helper_module(target_mod).define_method(method_name, &body) return if ::ENV['RAILS_ENV'] == 'production' - ::Warning.warn <<~WARN if mod.instance_methods.include?(method_name) + ::Warning.warn <<~WARN if target_mod.instance_methods.include?(method_name) #{caller(0, 1).first}: warning: - `#{component}` shadows the name of an already existing `#{mod}` method `#{method_name}`. - Consider renaming this component, because the original method will be overwritten. + `#{component}` shadows the name of an already existing `#{target_mod}` method `#{method_name}`. + Consider renaming this component, because the original method will be overridden. WARN end end define_model_callbacks :initialize, :render # @param kwargs [Hash{Symbol => Object}] + # @raise [AmberComponent::MissingPropsError] when required props are missing def initialize(**kwargs) run_callbacks :initialize do - bind_variables(kwargs) + next if bind_props(kwargs) + + bind_instance_variables(kwargs) end end private # @param kwargs [Hash{Symbol => Object}] # @return [void] - def bind_variables(kwargs) + def bind_instance_variables(kwargs) kwargs.each do |key, value| instance_variable_set("@#{key}", value) end end end