class Phlex::HTML
def _attributes(**attributes)
def _attributes(**attributes) if attributes[:href]&.start_with?(/\s*javascript/) attributes[:href] = attributes[:href].sub(/^\s*(javascript:)+/, "") end buffer = +"" _build_attributes(attributes, buffer: buffer) unless self.class.rendered_at_least_once Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze end buffer end
def _build_attributes(attributes, buffer:)
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 k.to_s end # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters. if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/) raise ArgumentError, "Unsafe attribute name detected: #{k}." end case v when true buffer << " " << name when String buffer << " " << name << '="' << CGI.escape_html(v) << '"' when Symbol buffer << " " << name << '="' << CGI.escape_html(v.name) << '"' when Hash _build_attributes(v.transform_keys { "#{k}-#{_1.name.tr('_', '-')}" }, buffer: buffer) else buffer << " " << name << '="' << CGI.escape_html(v.to_s) << '"' end end buffer end
def after_template
def after_template nil end
def around_template
def around_template before_template yield after_template end
def before_template
def before_template nil end
def call(buffer = +"", view_context: nil, parent: nil, &block)
def call(buffer = +"", view_context: nil, parent: nil, &block) return buffer unless render? raise "The same view instance shouldn't be rendered twice" if rendered? @_rendered = true @_target = buffer @_view_context = view_context @_parent = parent around_template { template { yield_content(&block) } } self.class.rendered_at_least_once ||= true buffer end
def capture(&block)
def capture(&block) return unless block_given? original_buffer = @_target new_buffer = +"" @_target = new_buffer yield @_target = original_buffer new_buffer end
def comment(&block)
def comment(&block) @_target << "<!-- " yield_content(&block) @_target << " -->" nil end
def doctype
def doctype @_target << DOCTYPE nil end
def format
def format :html end
def render(renderable, *args, **kwargs, &block)
def render(renderable, *args, **kwargs, &block) if renderable.is_a?(Phlex::HTML) renderable.call(@_target, view_context: @_view_context, parent: self, &block) elsif renderable.is_a?(Class) && renderable < Phlex::HTML raise ArgumentError, "You tried to render the Phlex view class: #{renderable.name} but you probably meant to render an instance of that class instead." else raise ArgumentError, "You can't render a #{renderable}." end nil end
def render?
def render? true end
def rendered?
def rendered? @_rendered ||= false end
def text(content)
def text(content) case content when String @_target << CGI.escape_html(content) when Symbol @_target << CGI.escape_html(content.name) when Integer, Float @_target << CGI.escape_html(content.to_s) end nil end
def unsafe_raw(content = nil, &block)
def unsafe_raw(content = nil, &block) @_target << (content || instance_exec(&block)) nil end
def whitespace
def whitespace @_target << " " if block_given? yield @_target << " " end nil end
def yield_content(&block)
def yield_content(&block) return unless block_given? original_length = @_target.length content = yield(self) unchanged = (original_length == @_target.length) if unchanged case content when String @_target << CGI.escape_html(content) when Symbol @_target << CGI.escape_html(content.name) when Integer, Float @_target << CGI.escape_html(content.to_s) end end nil end