Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render in #1

Merged
merged 11 commits into from
Dec 9, 2022
3 changes: 3 additions & 0 deletions arbre.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]

s.required_ruby_version = '>= 2.5'

s.add_dependency("activesupport", ">= 3.0.0")
s.add_dependency("ruby2_keywords", ">= 0.0.2")
end
1 change: 1 addition & 0 deletions lib/arbre.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'active_support/core_ext/string/output_safety'
require 'active_support/deprecation'
require 'active_support/hash_with_indifferent_access'
require 'active_support/inflector'

Expand Down
9 changes: 7 additions & 2 deletions lib/arbre/context.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'arbre/element'
require 'ruby2_keywords'

module Arbre

Expand Down Expand Up @@ -74,7 +75,7 @@ def respond_to_missing?(method, include_all)
# Webservers treat Arbre::Context as a string. We override
# method_missing to delegate to the string representation
# of the html.
def method_missing(method, *args, &block)
ruby2_keywords def method_missing(method, *args, &block)
if cached_html.respond_to? method
cached_html.send method, *args, &block
else
Expand All @@ -94,6 +95,10 @@ def with_current_arbre_element(tag)
end
alias_method :within, :with_current_arbre_element

def output_buffer
@output_buffer ||= ActiveSupport::SafeBuffer.new
end

private


Expand All @@ -103,7 +108,7 @@ def cached_html
if defined?(@cached_html)
@cached_html
else
html = to_s
html = render_in(self)
@cached_html = html if html.length > 0
html
end
Expand Down
36 changes: 32 additions & 4 deletions lib/arbre/element.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'arbre/element/builder_methods'
require 'arbre/element/proxy'
require 'arbre/element_collection'
require 'ruby2_keywords'

module Arbre

Expand Down Expand Up @@ -132,17 +133,36 @@ def each(&block)
end

def inspect
to_s
content
end

def to_str
to_s
ActiveSupport::Deprecation.warn("don't rely on implicit conversion of Element to String")
content
end

def to_s
ActiveSupport::Deprecation.warn("#render_in should be defined for rendering #{method_owner(:to_s)} instead of #to_s")
content
end

# Rendering strategy that visits all elements and appends output to a buffer.
def render_in(context = arbre_context)
children.collect do |element|
element.render_in_or_to_s(context)
end.join('')
end

# Use render_in to render element unless closer ancestor overrides :to_s only.
def render_in_or_to_s(context)
if method_distance(:render_in) <= method_distance(:to_s)
render_in(context)
else
ActiveSupport::Deprecation.warn("#render_in should be defined for rendering #{method_owner(:to_s)} instead of #to_s")
to_s.tap { |s| context.output_buffer << s }
end
end

def +(element)
case element
when Element, ElementCollection
Expand Down Expand Up @@ -172,7 +192,7 @@ def clear_children!
# 3. Call the method on the helper object
# 4. Call super
#
def method_missing(name, *args, &block)
ruby2_keywords def method_missing(name, *args, &block)
if current_arbre_element.respond_to?(name)
current_arbre_element.send name, *args, &block
elsif assigns && assigns.has_key?(name)
Expand All @@ -188,10 +208,18 @@ def method_missing(name, *args, &block)
# which will be rendered (#to_s) inside ActionView::Base#capture.
# We do not want such elements added to self, so we push a dummy
# current_arbre_element.
def helper_capture(name, *args, &block)
ruby2_keywords def helper_capture(name, *args, &block)
s = ""
within(Element.new) { s = helpers.send(name, *args, &block) }
s.is_a?(Element) ? s.to_s : s
end

def method_distance(name)
self.class.ancestors.index method_owner(name)
end

def method_owner(name)
self.class.instance_method(name).owner
end
end
end
6 changes: 6 additions & 0 deletions lib/arbre/element_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ def to_s
element.to_s
end.join('').html_safe
end

def render_in(context)
self.collect do |element|
element.render_in(context)
end.join('').html_safe
end
end

end
6 changes: 6 additions & 0 deletions lib/arbre/html/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def to_s
doctype + super
end

def render_in(context = arbre_context)
context.output_buffer << doctype
super
context.output_buffer
end

protected

def build_head
Expand Down
30 changes: 30 additions & 0 deletions lib/arbre/html/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ def to_s
indent(opening_tag, content, closing_tag).html_safe
end

def render_in(context = arbre_context)
indent_in_context(context)
end

private

def opening_tag
Expand Down Expand Up @@ -136,6 +140,32 @@ def indent(open_tag, child_content, close_tag)
html
end

def indent_in_context(context)
spaces = ' ' * indent_level * INDENT_SIZE

pos = context.output_buffer.length

if no_child? || child_is_text?
if self_closing_tag?
context.output_buffer << spaces << opening_tag.sub( />$/, '/>' ).html_safe
else
# one line
context.output_buffer << spaces << opening_tag.html_safe
children.render_in(context)
context.output_buffer << closing_tag.html_safe
end
else
# multiple lines
context.output_buffer << spaces << opening_tag.html_safe << "\n"
children.render_in(context)
context.output_buffer << spaces << closing_tag.html_safe
end

context.output_buffer << "\n"

context.output_buffer[pos..].html_safe
end

def self_closing_tag?
SELF_CLOSING_ELEMENTS.include?(tag_name.to_sym)
end
Expand Down
4 changes: 4 additions & 0 deletions lib/arbre/html/text_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def tag_name
def to_s
ERB::Util.html_escape(@content.to_s)
end

def render_in(context)
to_s.tap { |s| context.output_buffer << s }
end
end

end
Expand Down
6 changes: 6 additions & 0 deletions lib/arbre/rails/forms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ def to_s
children.to_s
end

def render_in(context)
children.collect do |element|
element.render_in_or_to_s(context)
end.join('')
end

end

end
Expand Down
2 changes: 1 addition & 1 deletion lib/arbre/rails/template_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def call(template, source = nil)
<<-END
Arbre::Context.new(assigns, self) {
#{source}
}.to_s
}.render_in(self).html_safe
END
end
end
Expand Down
Loading