class SparkComponents::Element

Attributes

attr[R]
parents[R]
yield[RW]

Public Class Methods

add_class(*args) click to toggle source
# File lib/spark_components/element.rb, line 48
def self.add_class(*args)
  tag_attrs.add_class(*args)
end
aria_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 56
def self.aria_attr(*args)
  arg = attribute(*args)
  tag_attrs.aria(arg)
end
attribute(*args) click to toggle source
# File lib/spark_components/element.rb, line 22
def self.attribute(*args)
  args.each_with_object({}) do |arg, obj|
    if arg.is_a?(Hash)
      arg.each do |attr, default|
        obj[attr.to_sym] = default
        set_attribute(attr.to_sym, default: default)
      end
    else
      obj[arg.to_sym] = nil
      set_attribute(arg.to_sym)
    end
  end
end
attributes() click to toggle source
# File lib/spark_components/element.rb, line 14
def self.attributes
  @attributes ||= {}
end
base_class(name = nil) click to toggle source
# File lib/spark_components/element.rb, line 44
def self.base_class(name = nil)
  tag_attrs.base_class(name)
end
data_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 52
def self.data_attr(*args)
  tag_attrs.data(attribute(*args))
end
element(name, multiple: false, component: nil, &config) click to toggle source

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity

# File lib/spark_components/element.rb, line 84
def self.element(name, multiple: false, component: nil, &config)
  plural_name = name.to_s.pluralize.to_sym if multiple

  # Extend components by string or class; e.g., "core/header" or Core::HeaderComponent
  component = "#{component}_component".classify.constantize if component.is_a?(String)

  elements[name] = {
    multiple: plural_name || false, class: Class.new((component || Element), &config)
  }

  define_method_or_raise(name) do |attributes = nil, &block|
    return get_instance_variable(multiple ? plural_name : name) unless attributes || block

    element = self.class.elements[name][:class].new(@view, attributes, &block)
    element.parent = self

    if multiple
      get_instance_variable(plural_name) << element
    else
      set_instance_variable(name, element)
    end

    if element.respond_to?(:render)
      element.before_render
      element.yield = element.render
    end
  end

  return if !multiple || name == plural_name

  define_method_or_raise(plural_name) do
    get_instance_variable(plural_name)
  end
end
elements() click to toggle source
# File lib/spark_components/element.rb, line 18
def self.elements
  @elements ||= {}
end
inherited(subclass) click to toggle source
# File lib/spark_components/element.rb, line 132
def self.inherited(subclass)
  attributes.each { |name, options| subclass.set_attribute(name, options.dup) }
  elements.each   { |name, options| subclass.elements[name] = options.dup }

  subclass.tag_attrs.merge!(tag_attrs.dup)
end
model_name() click to toggle source
# File lib/spark_components/element.rb, line 10
def self.model_name
  ActiveModel::Name.new(SparkComponents::Element)
end
new(view, attributes = nil, &block) click to toggle source
# File lib/spark_components/element.rb, line 139
def initialize(view, attributes = nil, &block)
  @view = view
  attributes ||= {}
  initialize_tag_attrs
  assign_tag_attrs(attributes)
  initialize_attributes(attributes)
  initialize_elements
  extend_view_methods
  after_init
  @yield = render_block(&block)
  validate!
end
root_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 61
def self.root_attr(*args)
  tag_attrs.root(attribute(*args))
end
set_attribute(name, default: nil) click to toggle source
# File lib/spark_components/element.rb, line 36
def self.set_attribute(name, default: nil)
  attributes[name] = { default: default }

  define_method_or_raise(name) do
    get_instance_variable(name)
  end
end
tag_attrs() click to toggle source
# File lib/spark_components/element.rb, line 65
def self.tag_attrs
  @tag_attrs ||= SparkComponents::Attributes::Tag.new
end
validates_choice(name, choices, required: true) click to toggle source
# File lib/spark_components/element.rb, line 69
def self.validates_choice(name, choices, required: true)
  choices = choices.dup
  choices = [choices] unless choices.is_a?(Array)
  supported_choices = choices.map { |c| c.is_a?(String) ? c.to_sym : c.to_s }.concat(choices)

  choices = choices.to_sentence(last_word_connector: ", or ")
  message = "\"%<value>s\" is not valid. Options for #{name} include: #{choices}"

  validates(name, inclusion: { in: supported_choices, message: message }, allow_blank: !required)
end

Private Class Methods

define_method_or_raise(method_name, &block) click to toggle source

rubocop:enable Metrics/AbcSize rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/MethodLength rubocop:enable Metrics/PerceivedComplexity

# File lib/spark_components/element.rb, line 123
def self.define_method_or_raise(method_name, &block)
  # Select instance methods but not those which are intance methods received by extending a class
  methods = (instance_methods - superclass.instance_methods(false))
  raise(SparkComponents::Error, "Method '#{method_name}' already exists.") if methods.include?(method_name.to_sym)

  define_method(method_name, &block)
end

Public Instance Methods

add_class(*args) click to toggle source
# File lib/spark_components/element.rb, line 173
def add_class(*args)
  classnames(*args)
end
after_init() click to toggle source
# File lib/spark_components/element.rb, line 152
def after_init; end
aria_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 185
def aria_attr(*args)
  @tag_attrs.aria(*args)
end
base_class(name = nil) click to toggle source
# File lib/spark_components/element.rb, line 168
def base_class(name = nil)
  classnames.base = name unless name.nil?
  classnames.base
end
before_render() click to toggle source
# File lib/spark_components/element.rb, line 154
def before_render; end
blank?() click to toggle source

blank? is aliased to an element's content to easily determine if content is blank. This is because template conditionals may render an element's content empty.

# File lib/spark_components/element.rb, line 204
def blank?
  @yield.blank?
end
classnames(*args) click to toggle source
# File lib/spark_components/element.rb, line 164
def classnames(*args)
  @tag_attrs.classnames(*args)
end
data_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 181
def data_attr(*args)
  @tag_attrs.data(*args)
end
join_class(*args) click to toggle source
# File lib/spark_components/element.rb, line 177
def join_class(*args)
  classnames.join_class(*args)
end
parent() click to toggle source
# File lib/spark_components/element.rb, line 160
def parent
  @parents.last
end
parent=(obj) click to toggle source
# File lib/spark_components/element.rb, line 156
def parent=(obj)
  @parents = [obj.parents, obj].flatten.compact
end
root_attr(*args) click to toggle source
# File lib/spark_components/element.rb, line 189
def root_attr(*args)
  @tag_attrs.root(*args)
end
tag_attrs() click to toggle source
# File lib/spark_components/element.rb, line 193
def tag_attrs
  @tag_attrs.attrs
end
to_s() click to toggle source
# File lib/spark_components/element.rb, line 197
def to_s
  @yield
end

Protected Instance Methods

assign_tag_attrs(attributes) click to toggle source

Assign tag attributes from arguments

# File lib/spark_components/element.rb, line 232
def assign_tag_attrs(attributes)
  # support default data, class, and aria attribute names
  data_attr(attributes.delete(:data)) if attributes[:data]
  aria_attr(attributes.delete(:aria)) if attributes[:aria]
  add_class(*attributes.delete(:class)) if attributes[:class]
  root_attr(attributes.delete(:splat)) if attributes[:splat]
end
extend_view_methods() click to toggle source
# File lib/spark_components/element.rb, line 262
def extend_view_methods
  view_methods.each do |name|
    next if respond_to?(name) || !@view.respond_to?(name)

    self.class.define_method(name) do |*args, &block|
      @view.send(name, *args, &block)
    end
  end
end
get_instance_variable(name) click to toggle source
# File lib/spark_components/element.rb, line 272
def get_instance_variable(name)
  instance_variable_get(:"@#{name}")
end
initialize_attributes(attributes) click to toggle source
# File lib/spark_components/element.rb, line 240
def initialize_attributes(attributes)
  self.class.attributes.each do |name, options|
    set_instance_variable(name, attributes[name] || (options[:default] && options[:default].dup))
    update_tag_attr(name)
  end
end
initialize_elements() click to toggle source
# File lib/spark_components/element.rb, line 247
def initialize_elements
  self.class.elements.each do |name, options|
    if (plural_name = options[:multiple])
      set_instance_variable(plural_name, [])
    else
      set_instance_variable(name, nil)
    end
  end
end
initialize_tag_attrs() click to toggle source
# File lib/spark_components/element.rb, line 227
def initialize_tag_attrs
  @tag_attrs = self.class.tag_attrs.dup
end
render_partial(file) click to toggle source
# File lib/spark_components/element.rb, line 223
def render_partial(file)
  @view.render(partial: file, object: self)
end
set_instance_variable(name, value) click to toggle source
# File lib/spark_components/element.rb, line 276
def set_instance_variable(name, value)
  instance_variable_set(:"@#{name}", value)
end
update_tag_attr(name) click to toggle source

Set tag attribute values from from parameters

# File lib/spark_components/element.rb, line 217
def update_tag_attr(name)
  %i[aria data root].each do |el|
    @tag_attrs.send(el)[name] = get_instance_variable(name) if @tag_attrs.send(el).key?(name)
  end
end
view_methods() click to toggle source

Define common view methods to “alias”

# File lib/spark_components/element.rb, line 258
def view_methods
  %i[tag content_tag image_tag concat content_for link_to component capture]
end

Private Instance Methods

render_block(&block) click to toggle source
# File lib/spark_components/element.rb, line 210
def render_block(&block)
  block_given? ? @view.capture(self, &block) : nil
end