module Spark::Component::Element::ClassMethods
Public Instance Methods
define_element(name:, plural:, multiple:, klass:)
click to toggle source
Element
method will create a new element instance, or if no attributes or block is passed it will return the instance defined for that element.
For example when rendering a component, passing attributes or a block will create a new instance of that element.
# Some view (Slim) = render(Nav) do |nav| - nav.item(href: "#url") { "link text" }
Then when referencing the element in the component's template it the method will return the instance. Call yield to output an elemnet's block
# Nav template (Slim) nav - items.each do |item| a href=item.href = item.yield
# File lib/spark/component/element.rb, line 186 def define_element(name:, plural:, multiple:, klass:) define_method_if_able(name) do |attributes = nil, &block| # When initializing an element, blocks or arguments are passed. # If an element is being referenced without these, it will return its instance # This allows the elemnet method to initailize the object and return its instance # for template rendering. unless block || attributes # If an element is called, it will only exist if its parent's block has been exectued # Be sure the block has been executed so that sub elements are initialized self.yield if is_a?(Spark::Component::Element::Base) && !rendered? return get_element_variable(multiple ? plural : name) end attributes ||= {} attributes = merge_element_attribute_default(name, attributes) attributes.merge!(_name: name, _parent: self, _block: block, _view: view_context) element = klass.new(attributes) # If element supports multiple instances, inject instance # into array for later enumeration if multiple get_element_variable(plural) << element else set_element_variable(name, element) end end return if !multiple || name == plural # Define a pluralized method name to access enumerable element instances. define_method_if_able(plural) do get_element_variable(plural) end end
element(name, multiple: false, component: nil, &config)
click to toggle source
Class method for adding elements
Options:
name: Symbol Create a method for interacting with an element This name cannot be the same as another instance method multiple: Boolean (default: false) Defining `multiple: true` causes elements to be injected into an array. A pluralized method is created to access each element instance. For example, `element(:item, multiple: true)` will create an `:items` method and each time an item is executed, its instance will be added to items. component: Class By default all elements include Element and extend its class methods Passing a class like `component: Nav::Item` will extend that component adding Element, Attributes, TagAttr and render methods. &config: Block When defining a method, you may pass an optional block to configure attributes, nested elements, or even define methods.
# File lib/spark/component/element.rb, line 157 def element(name, multiple: false, component: nil, &config) plural_name = name.to_s.pluralize.to_sym if multiple klass = extend_class(component, &config) elements[name] = { multiple: plural_name, class: klass } define_element(name: name, plural: plural_name, multiple: multiple, klass: klass) end
elements()
click to toggle source
# File lib/spark/component/element.rb, line 123 def elements @elements ||= {} end
inherited(child)
click to toggle source
# File lib/spark/component/element.rb, line 114 def inherited(child) child.elements.replace(elements) child.attributes.replace(attributes) child.tag_attributes.replace(tag_attributes) child.data_attributes.replace(data_attributes) child.aria_attributes.replace(aria_attributes) child.attribute_default_groups.replace(attribute_default_groups) end
Private Instance Methods
define_method_if_able(method_name, &block)
click to toggle source
Prevent an element method from overwriting an existing method
# File lib/spark/component/element.rb, line 255 def define_method_if_able(method_name, &block) # Protect instance methods which are crucial to components and elements methods = [self, Element, Attribute] methods << Integration.base_class if defined? Spark::Component::Integration methods.map! { |c| c.instance_methods(false) } if methods.flatten.include?(method_name.to_sym) raise(Element::Error, "Method '#{method_name}' already exists.") end define_method(method_name, &block) end
define_model_name(klass)
click to toggle source
ActiveModel validations require a model_name. This connects new classes to their proper model names.
# File lib/spark/component/element.rb, line 246 def define_model_name(klass) klass.define_singleton_method(:model_name) do # try the current class, the parent class, or default to Spark::Component named_klass = [self.class, superclass, Spark::Component].reject { |k| k == Class }.first ActiveModel::Name.new(named_klass) end end
extend_class(component, &config)
click to toggle source
If an element extends a component, extend that class and include necessary modules
# File lib/spark/component/element.rb, line 227 def extend_class(component, &config) base = Class.new(component || Spark::Component::Element::Base, &config) define_model_name(base) if defined?(ActiveModel) return base unless component # Allow element to reference its source component base.define_singleton_method(:source_component) { component } # Override component when used as an element if defined?(Spark::Component::Integration) base.include(Spark::Component::Integration::Element) end base end