class Phlex::SGML

**Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.

def __attributes__(**attributes)

Other tags:
    Api: - private
def __attributes__(**attributes)
	__final_attributes__(**attributes).tap do |buffer|
		Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] = buffer.freeze
	end
end

def __build_attributes__(attributes, buffer:)

Other tags:
    Api: - private
def __build_attributes__(attributes, buffer:)
	attributes.each do |k, v|
		next unless v
		name = case k
			when String then k
			when Symbol then k.name.tr("_", "-")
			else raise ArgumentError, "Attribute keys should be Strings or Symbols."
		end
		lower_name = name.downcase
		next if lower_name == "href" && v.to_s.downcase.tr("\t \n", "").start_with?("javascript:")
		# Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
		if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
			raise ArgumentError, "Unsafe attribute name detected: #{k}."
		end
		case v
		when true
			buffer << " " << name
		when String
			buffer << " " << name << '="' << ERB::Escape.html_escape(v) << '"'
		when Symbol
			buffer << " " << name << '="' << ERB::Escape.html_escape(v.name) << '"'
		when Hash
			__build_attributes__(
				v.transform_keys { |subkey|
					case subkey
						when Symbol then"#{k}-#{subkey.name.tr('_', '-')}"
						else "#{k}-#{subkey}"
					end
				}, buffer: buffer
			)
		when Array
			buffer << " " << name << '="' << ERB::Escape.html_escape(v.compact.join(" ")) << '"'
		when Set
			buffer << " " << name << '="' << ERB::Escape.html_escape(v.to_a.compact.join(" ")) << '"'
		else
			raise ArgumentError, "Element attributes must be either a Boolean, a String, a Symbol, an Array of Strings or Symbols, or a Hash with values of one of these types"
		end
	end
	buffer
end

def __final_attributes__(**attributes)

Other tags:
    Api: - private
def __final_attributes__(**attributes)
	if respond_to?(:process_attributes)
		attributes = process_attributes(**attributes)
	end
	if attributes[:href]&.start_with?(/\s*javascript:/)
		attributes.delete(:href)
	end
	if attributes["href"]&.start_with?(/\s*javascript:/)
		attributes.delete("href")
	end
	buffer = +""
	__build_attributes__(attributes, buffer: buffer)
	buffer
end

def __final_call__(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)

Other tags:
    Api: - private
def __final_call__(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)
	@_buffer = buffer
	@_context = context
	@_view_context = view_context
	@_parent = parent
	block ||= @_content_block
	return unless render?
	around_template do
		if block
			if is_a?(DeferredRender)
				__vanish__(self, &block)
				template
			else
				template do |*args|
					if args.length > 0
						yield_content_with_args(*args, &block)
					else
						yield_content(&block)
					end
				end
			end
		else
			template
		end
	end
	buffer << context.target unless parent
end

def __text__(content)

Other tags:
    Api: - private
def __text__(content)
	case content
	when String
		@_context.target << ERB::Escape.html_escape(content)
	when Symbol
		@_context.target << ERB::Escape.html_escape(content.name)
	when Integer
		@_context.target << ERB::Escape.html_escape(content.to_s)
	when nil
		nil
	else
		if (formatted_object = format_object(content))
			@_context.target << ERB::Escape.html_escape(formatted_object)
		else
			return false
		end
	end
	true
end

def __vanish__(*args)

Other tags:
    Api: - private

Returns:
  • (nil) -
def __vanish__(*args)
	return unless block_given?
	@_context.with_target(BlackHole) { yield(*args) }
	nil
end

def after_template

Returns:
  • (nil) -

Other tags:
    Abstract: - Override this method to hook in right after a template is rendered. Please remember to call `super` so that callbacks can be added at different layers of the inheritance tree.
def after_template
	nil
end

def around_template

Returns:
  • (nil) -

Other tags:
    Abstract: - Override this method to hook in around a template render. You can do things before and after calling `super` to render the template. You should always call `super` so that callbacks can be added at different layers of the inheritance tree.
def around_template
	before_template
	yield
	after_template
	nil
end

def await(task)

Other tags:
    Api: - private
def await(task)
	if task.is_a?(Concurrent::IVar)
		flush if task.pending?
		task.wait.value
	elsif defined?(Async::Task) && task.is_a?(Async::Task)
		flush if task.running?
		task.wait
	else
		raise ArgumentError, "Expected an asynchronous task / promise."
	end
end

def before_template

Returns:
  • (nil) -

Other tags:
    Abstract: - Override this method to hook in right before a template is rendered. Please remember to call `super` so that callbacks can be added at different layers of the inheritance tree.
def before_template
	nil
end

def call(...)

Render the view to a String. Arguments are delegated to {.new}.
def call(...)
	new(...).call
end

def call(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)

Renders the view and returns the buffer. The default buffer is a mutable String.
def call(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)
	__final_call__(buffer, context: context, view_context: view_context, parent: parent, &block).tap do
		self.class.rendered_at_least_once!
	end
end

def capture(&block)

Returns:
  • (String) -

Other tags:
    Note: - This only works if the block's receiver is the current component or the block returns a String.
def capture(&block)
	return "" unless block
	@_context.with_target(+"") { yield_content(&block) }
end

def comment(&block)

Returns:
  • (nil) -
def comment(&block)
	target = @_context.target
	target << "<!-- "
	yield_content(&block)
	target << " -->"
	nil
end

def element_method?(method_name)

Other tags:
    Api: - private
def element_method?(method_name)
	return false unless instance_methods.include?(method_name)
	owner = instance_method(method_name).owner
	return true if owner.is_a?(Phlex::Elements) && owner.registered_elements[method_name]
	false
end

def flush

Other tags:
    Api: - private
def flush
	target = @_context.target
	@_buffer << target.dup
	target.clear
end

def format_object(object)

Returns:
  • (String) -

Other tags:
    Abstract: - Override to define your own format handling for different object types. Please remember to call `super` in the case that the passed object doesn't match, so that object formatting can be added at different layers of the inheritance tree.
def format_object(object)
	case object
	when Float
		object.to_s
	end
end

def new(*args, **kwargs, &block)

Other tags:
    Note: - The block will not be delegated {#initialize}. Instead, it will be sent to {#template} when rendering.
def new(*args, **kwargs, &block)
	if block
		object = super(*args, **kwargs, &nil)
		object.instance_variable_set(:@_content_block, block)
		object
	else
		super
	end
end

def plain(content)

Other tags:
    See: #format_object -

Returns:
  • (nil) -

Parameters:
  • content (String, Symbol, Integer, void) -- the content to be output on the buffer. Strings, Symbols, and Integers are handled by `plain` directly, but any object can be handled by overriding `format_object`
def plain(content)
	unless __text__(content)
		raise ArgumentError, "You've passed an object to plain that is not handled by format_object. See https://rubydoc.info/gems/phlex/Phlex/SGML#format_object-instance_method for more information"
	end
	nil
end

def render(renderable, &block)

Parameters:
  • enumerable (Enumerable) --
  • proc (Proc) --
  • component_class (Class) --
  • component (Phlex::SGML) --

Overloads:
  • render(enumerable)
  • render(proc)
  • render(component_class, &block)
  • render(component, &block)

Returns:
  • (nil) -
def render(renderable, &block)
	case renderable
	when Phlex::SGML
		renderable.call(@_buffer, context: @_context, view_context: @_view_context, parent: self, &block)
	when Class
		if renderable < Phlex::SGML
			renderable.new.call(@_buffer, context: @_context, view_context: @_view_context, parent: self, &block)
		end
	when Enumerable
		renderable.each { |r| render(r, &block) }
	when Proc
		if renderable.arity == 0
			yield_content_with_no_args(&renderable)
		else
			yield_content(&renderable)
		end
	else
		raise ArgumentError, "You can't render a #{renderable}."
	end
	nil
end

def render?

Returns:
  • (Boolean) -

Other tags:
    Abstract: - Override to define your own predicate to prevent rendering.
def render?
	true
end

def rendered_at_least_once!

Other tags:
    Api: - private
def rendered_at_least_once!
	alias_method :__attributes__, :__final_attributes__
	alias_method :call, :__final_call__
end

def template

Other tags:
    Example: Alternatively, you can delegate the content block to an element. -
    Example: Your template may yield a content block. -

Other tags:
    Abstract: - Override to define a template for your component.
def template
	yield
end

def unsafe_raw(content = nil)

Returns:
  • (nil) -

Parameters:
  • content (String|nil) --
def unsafe_raw(content = nil)
	return nil unless content
	@_context.target << content
	nil
end

def whitespace

Other tags:
    Yield: - If a block is given, it yields the block with no arguments.

Returns:
  • (nil) -
def whitespace
	target = @_context.target
	target << " "
	if block_given?
		yield
		target << " "
	end
	nil
end

def yield_content

Returns:
  • (nil) -

Other tags:
    Yieldparam: component -
def yield_content
	return unless block_given?
	target = @_context.target
	original_length = target.length
	content = yield(self)
	__text__(content) if original_length == target.length
	nil
end

def yield_content_with_args(*args)

Returns:
  • (nil) -

Other tags:
    Yield: - Yields the given arguments.
def yield_content_with_args(*args)
	return unless block_given?
	target = @_context.target
	original_length = target.length
	content = yield(*args)
	__text__(content) if original_length == target.length
	nil
end

def yield_content_with_no_args

Other tags:
    Yield: - Yields the block with no arguments.
def yield_content_with_no_args
	return unless block_given?
	target = @_context.target
	original_length = target.length
	content = yield
	__text__(content) if original_length == target.length
	nil
end