# frozen_string_literal: true module Arbre class Element module BuilderMethods def self.included(klass) klass.extend ClassMethods end module ClassMethods def builder_method(method_name) BuilderMethods.class_eval <<-EOF, __FILE__, __LINE__ def #{method_name}(*args, &block) insert_tag ::#{self.name}, *args, &block end EOF end end def build_tag(klass, *args, &block) tag = klass.new(arbre_context) tag.parent = current_arbre_element with_current_arbre_element tag do if block && block.arity > 0 tag.build(*args, &block) else tag.build(*args) append_return_block(yield) if block end end tag end def insert_tag(klass, *args, &block) tag = build_tag(klass, *args, &block) current_arbre_element.add_child(tag) tag end def current_arbre_element arbre_context.current_arbre_element end def with_current_arbre_element(tag, &block) arbre_context.with_current_arbre_element(tag, &block) end alias_method :within, :with_current_arbre_element private # Appends the value to the current DOM element if there are no # existing DOM Children and it responds to #to_s def append_return_block(tag) return nil if current_arbre_element.children? if appendable_tag?(tag) current_arbre_element << Arbre::HTML::TextNode.from_string(tag.to_s) end end # Returns true if the object should be converted into a text node # and appended into the DOM. def appendable_tag?(tag) # Array.new.to_s prints out an empty array ("[]"). In # Arbre, we append the return value of blocks to the output, which # can cause empty arrays to show up within the output. To get # around this, we check if the object responds to #empty? if tag.respond_to?(:empty?) && tag.empty? false else !tag.is_a?(Arbre::Element) && tag.respond_to?(:to_s) end end end end end