class Phlex::SGML
**Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
def __attributes__(**attributes)
- 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:)
- 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)
- 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)
- 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)
- 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)
- Api: - private
Returns:
-
(nil)
-
def __vanish__(*args) return unless block_given? @_context.with_target(BlackHole) { yield(*args) } nil end
def after_template
-
(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
-
(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)
- 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
-
(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(...)
def call(...) new(...).call end
def call(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)
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)
-
(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)
-
(nil)
-
def comment(&block) target = @_context.target target << "<!-- " yield_content(&block) target << " -->" nil end
def element_method?(method_name)
- 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
- Api: - private
def flush target = @_context.target @_buffer << target.dup target.clear end
def format_object(object)
-
(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)
- 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)
- 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)
-
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?
-
(Boolean)
-
Other tags:
- Abstract: - Override to define your own predicate to prevent rendering.
def render? true end
def rendered_at_least_once!
- Api: - private
def rendered_at_least_once! alias_method :__attributes__, :__final_attributes__ alias_method :call, :__final_call__ end
def template
- 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)
-
(nil)
-
Parameters:
-
content
(String|nil
) --
def unsafe_raw(content = nil) return nil unless content @_context.target << content nil end
def whitespace
- 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
-
(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)
-
(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
- 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