express_templates 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -6
- data/lib/core_extensions/proc.rb +42 -0
- data/lib/express_templates.rb +7 -2
- data/lib/express_templates/compiler.rb +27 -0
- data/lib/express_templates/components.rb +7 -4
- data/lib/express_templates/components/base.rb +54 -0
- data/lib/express_templates/components/capabilities/conditionality.rb +52 -0
- data/lib/express_templates/components/capabilities/configurable.rb +91 -0
- data/lib/express_templates/components/capabilities/iterating.rb +80 -0
- data/lib/express_templates/components/capabilities/parenting.rb +74 -0
- data/lib/express_templates/components/capabilities/rendering.rb +93 -0
- data/lib/express_templates/components/capabilities/templating.rb +196 -0
- data/lib/express_templates/components/capabilities/wrapping.rb +100 -0
- data/lib/express_templates/components/column.rb +13 -0
- data/lib/express_templates/components/container.rb +7 -0
- data/lib/express_templates/components/form_rails_support.rb +19 -0
- data/lib/express_templates/components/row.rb +28 -0
- data/lib/express_templates/expander.rb +51 -35
- data/lib/express_templates/indenter.rb +44 -0
- data/lib/express_templates/macro.rb +44 -0
- data/lib/express_templates/markup.rb +9 -0
- data/lib/express_templates/{components → markup}/html_tag.rb +9 -3
- data/lib/express_templates/markup/tag.rb +118 -0
- data/lib/express_templates/markup/wrapper.rb +88 -0
- data/lib/express_templates/{components → markup}/yielder.rb +2 -2
- data/lib/express_templates/renderer.rb +3 -8
- data/lib/express_templates/template/handler.rb +1 -1
- data/lib/express_templates/version.rb +1 -1
- data/lib/tasks/{gara_tasks.rake → express_templates.rake} +0 -0
- data/test/compiler_test.rb +9 -0
- data/test/components/base_test.rb +77 -0
- data/test/components/column_test.rb +11 -0
- data/test/components/conditionality_test.rb +37 -0
- data/test/components/configurable_test.rb +43 -0
- data/test/components/container_test.rb +49 -0
- data/test/components/iterating_test.rb +85 -0
- data/test/components/row_test.rb +16 -0
- data/test/core_extensions/proc_test.rb +41 -0
- data/test/dummy/log/test.log +72489 -0
- data/test/expander_test.rb +55 -13
- data/test/{gara_test.rb → express_templates_test.rb} +0 -0
- data/test/handler_test.rb +4 -4
- data/test/indenter_test.rb +25 -0
- data/test/markup/tag_test.rb +127 -0
- data/test/markup/wrapper_test.rb +42 -0
- data/test/markup/yielder_test.rb +9 -0
- data/test/performance_test.rb +2 -2
- data/test/test_helper.rb +6 -0
- metadata +78 -24
- data/lib/express_templates/component.rb +0 -92
- data/lib/express_templates/components/wrapper.rb +0 -51
- data/lib/express_templates/html5_emitter.rb +0 -45
- data/test/component_test.rb +0 -77
- data/test/wrapper_test.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00a0446a67ac0022ae2769db3a19f9c0399fa4c1
|
4
|
+
data.tar.gz: 5feea8d4b6a5732aec2f49edfc0ebd4024129f85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd40eaa5f1d713c36084fd91ae72c844cf129b83683aa4a15dd730cc4e5e05a50e2e360ca31eef7fc81b25596075caf9fb54d79af98ab67bbc5ef7f896de36f4
|
7
|
+
data.tar.gz: 873c79cbf53186da5931979d05f62183432ec14426f0f37a2984d9fac64f38ea1210a4156ce40e8fbcf454113bbb21a76141d75f64208a5716f0612083b6fc25
|
data/README.md
CHANGED
@@ -36,18 +36,22 @@ Set your editor syntax for .et files to Ruby.
|
|
36
36
|
|
37
37
|
ExpressTemplates works via a good-enough stand in for a true macro system in Ruby which would make this type of thing considerably easier.
|
38
38
|
|
39
|
-
Basically, we use these "macros" and Ruby's block structure to build up a tree of components corresponding to the HTML structure of a document fragment. Each HTML5 tag is a
|
39
|
+
Basically, we use these "macros" and Ruby's block structure to build up a tree of components corresponding to the HTML structure of a document fragment. Each HTML5 tag is a Component available in the form of a macro. Unrecognized identifiers are wrapped for later evaluation, presumably in a ViewContext.
|
40
40
|
|
41
|
-
yield and local variables which we may expect to be available in a
|
41
|
+
yield and local variables which we may expect to be available in a ViewContext are also wrapped for evaluation later.
|
42
|
+
|
43
|
+
Templates are first "expanded", then "compiled" and then finally "rendered."
|
44
|
+
|
45
|
+
Expanding results in a tree of Component like objects which have children and respond to #compile(). The result of #compile on a component is a Ruby code fragment which may be evaluated in a ViewContext to produce markup. Compiling is similar to what HAML or Erb does.
|
42
46
|
|
43
47
|
## Background
|
44
48
|
|
45
|
-
|
49
|
+
The bondage of HAML is unnecessary. The clutter of Erb is unsightly.
|
46
50
|
|
47
|
-
I
|
51
|
+
I generally prefer "one syntax per file" for reasons of cognative load and maintainability.
|
48
52
|
|
49
|
-
|
53
|
+
The introduction of an macro-like pre-processing step yielding a tree of Components as described above allows us to implement higher-level components which inherit behavior via normal OO Ruby. This points the way to a UX framework and component library that will play nice with Rails caching and conventions.
|
50
54
|
|
51
|
-
ExpressTemplates
|
55
|
+
ExpressTemplates form part of the AppExpress platform at [appexpress.io](http://appexpress.io).
|
52
56
|
|
53
57
|
This project rocks and uses MIT-LICENSE.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require 'pp'
|
3
|
+
class Proc
|
4
|
+
|
5
|
+
TOKEN_PAIRS = {[:on_lbrace, '{'] => [:on_rbrace, '}'],
|
6
|
+
[:on_kw, 'do'] => [:on_kw, 'end'],
|
7
|
+
[:on_tlambeg, '{'] => [:on_rbrace, '}']}
|
8
|
+
|
9
|
+
# Make a best effort to provide the original source for a block
|
10
|
+
# based on extracting a string from the file identified in
|
11
|
+
# Proc#source_location using Ruby's tokenizer.
|
12
|
+
#
|
13
|
+
# This works for first block declared on a line in a source
|
14
|
+
# file. If additional blocks are specified inside the first block
|
15
|
+
# on the same line as the start of the block, only the outer-most
|
16
|
+
# block declaration will be identified as a the block we want.
|
17
|
+
#
|
18
|
+
# If you require only the source of blocks-within-other-blocks, start them
|
19
|
+
# on a new line as would be best practice for clarity and readability.
|
20
|
+
def source
|
21
|
+
file, line_no = source_location
|
22
|
+
raise "no line number provided for source_location: #{self}" if line_no.nil?
|
23
|
+
tokens = Ripper.lex File.read(file)
|
24
|
+
tokens_on_line = tokens.select {|pos, lbl, str| pos[0].eql?(line_no) }
|
25
|
+
starting_token = tokens_on_line.detect do |pos, lbl, str|
|
26
|
+
TOKEN_PAIRS.keys.include? [lbl, str]
|
27
|
+
end
|
28
|
+
starting_token_type = [starting_token[1], starting_token[2]]
|
29
|
+
ending_token_type = TOKEN_PAIRS[starting_token_type]
|
30
|
+
source_str = ""
|
31
|
+
remaining_tokens = tokens.slice(tokens.index(starting_token)..-1)
|
32
|
+
nesting = -1
|
33
|
+
while token = remaining_tokens.shift
|
34
|
+
source_str << token[2]
|
35
|
+
nesting += 1 if [token[1], token[2]] == starting_token_type
|
36
|
+
is_ending_token = [token[1], token[2]].eql?(ending_token_type)
|
37
|
+
break if is_ending_token && nesting.eql?(0)
|
38
|
+
nesting -= 1 if is_ending_token
|
39
|
+
end
|
40
|
+
source_str
|
41
|
+
end
|
42
|
+
end
|
data/lib/express_templates.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module ExpressTemplates
|
2
|
+
require 'core_extensions/proc'
|
3
|
+
require 'express_templates/indenter'
|
4
|
+
require 'express_templates/macro'
|
5
|
+
require 'express_templates/markup'
|
6
|
+
require 'express_templates/components'
|
2
7
|
require 'express_templates/template/handler'
|
3
|
-
require 'express_templates/html5_emitter'
|
4
8
|
require 'express_templates/renderer'
|
5
9
|
require 'express_templates/expander'
|
6
|
-
require 'express_templates/
|
10
|
+
require 'express_templates/compiler'
|
7
11
|
extend Renderer
|
12
|
+
extend Compiler
|
8
13
|
if defined?(Rails)
|
9
14
|
::ActionView::Template.register_template_handler :et, ExpressTemplates::Template::Handler
|
10
15
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Compiler
|
3
|
+
def compile(template_or_src=nil, &block)
|
4
|
+
template, src = _normalize(template_or_src)
|
5
|
+
|
6
|
+
expander = Expander.new(template)
|
7
|
+
|
8
|
+
compiled = expander.expand(src, &block).map(&:compile)
|
9
|
+
|
10
|
+
return compiled.join("+").gsub('"+"', '').tap do |s|
|
11
|
+
puts("\n"+template.inspect+"\n"+s) if ENV['DEBUG'].eql?('true')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def _normalize(template_or_src)
|
17
|
+
template, src = nil, nil
|
18
|
+
if template_or_src.respond_to?(:source)
|
19
|
+
template = template_or_src
|
20
|
+
src = template_or_src.source
|
21
|
+
else
|
22
|
+
src = template_or_src
|
23
|
+
end
|
24
|
+
return template, src
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,7 +2,10 @@ module ExpressTemplates
|
|
2
2
|
module Components
|
3
3
|
end
|
4
4
|
end
|
5
|
-
|
6
|
-
require 'express_templates/
|
7
|
-
require 'express_templates/components/
|
8
|
-
require 'express_templates/components/
|
5
|
+
|
6
|
+
require 'express_templates/expander'
|
7
|
+
require 'express_templates/components/base'
|
8
|
+
require 'express_templates/components/container'
|
9
|
+
require 'express_templates/components/row'
|
10
|
+
require 'express_templates/components/column'
|
11
|
+
require 'express_templates/components/form_rails_support'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
capabilities = Dir.glob(File.join(File.dirname(__FILE__), 'capabilities', '*.rb'))
|
2
|
+
capabilities.each {|capability| require capability}
|
3
|
+
|
4
|
+
module ExpressTemplates
|
5
|
+
# Components provide self-contained reusable view code meant to be shared
|
6
|
+
# within a project or across many projects through a library of components
|
7
|
+
#
|
8
|
+
# Components gain their functionality through inclusion of Capabilities.
|
9
|
+
#
|
10
|
+
# Most Components are descendents of Components::Base.
|
11
|
+
#
|
12
|
+
module Components
|
13
|
+
|
14
|
+
# Components::Base is the base class for ExpressTemplates view components.
|
15
|
+
#
|
16
|
+
# View components are available as macros in ExpressTemplates and may be
|
17
|
+
# used to encapsulate common view patterns, behavior and functionality in
|
18
|
+
# reusable classes that can be shared within and across projects.
|
19
|
+
#
|
20
|
+
# Components intended to provide a base framework for a library of reusable
|
21
|
+
# components to cut development time across a multitude of projects.
|
22
|
+
#
|
23
|
+
# Components gain their functionality through including Capabilities.
|
24
|
+
#
|
25
|
+
# Example capabilities include:
|
26
|
+
#
|
27
|
+
# * Managing related ExpressTemplate fragments
|
28
|
+
# * Compiling template fragments for evaluation in a View Context
|
29
|
+
# * Specifying rendering logic to be executed in the View Context
|
30
|
+
# * Potentially referencing external assets that may be required
|
31
|
+
# for the component to work.
|
32
|
+
#
|
33
|
+
# Components::Base includes the following capabilities:
|
34
|
+
#
|
35
|
+
# * Capabilities::Templating
|
36
|
+
# * Capabilities::Rendering
|
37
|
+
# * Capabilities::Wrapping
|
38
|
+
# * Capabilities::Iterating
|
39
|
+
#
|
40
|
+
class Base
|
41
|
+
include ExpressTemplates::Macro
|
42
|
+
include Capabilities::Templating
|
43
|
+
include Capabilities::Rendering
|
44
|
+
include Capabilities::Wrapping
|
45
|
+
include Capabilities::Iterating
|
46
|
+
|
47
|
+
def self.inherited(klass)
|
48
|
+
ExpressTemplates::Expander.register_macros_for klass
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Capabilities
|
4
|
+
# Adds the capability for a component to only render
|
5
|
+
# its markup when a condition to be evaluated in the
|
6
|
+
# view is true.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# class PageHeader < ExpressTemplates::Components::Base
|
11
|
+
# include ExpressTemplates::Components::Capabilities::Conditionality
|
12
|
+
#
|
13
|
+
# emits {
|
14
|
+
# h1 { content_for(:page_header) }
|
15
|
+
# }
|
16
|
+
#
|
17
|
+
# only_if -> { content_for?(:page_header) }
|
18
|
+
#
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# The condition supplied to only if in the form of a proc
|
23
|
+
# is evaluated in the view context.
|
24
|
+
#
|
25
|
+
# The component will render an empty string if the proc returns false.
|
26
|
+
module Conditionality
|
27
|
+
def self.included(base)
|
28
|
+
base.class_eval do
|
29
|
+
extend ClassMethods
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
|
35
|
+
def condition_proc
|
36
|
+
@condition_proc
|
37
|
+
end
|
38
|
+
|
39
|
+
def only_if condition_proc
|
40
|
+
@condition_proc = condition_proc
|
41
|
+
|
42
|
+
using_logic do |component, options|
|
43
|
+
condition = instance_exec(&component.condition_proc)
|
44
|
+
eval(component[:markup]) if condition
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Capabilities
|
4
|
+
|
5
|
+
# Configurable components accept options which they can use to alter
|
6
|
+
# their markup each time they are invoked within a template.
|
7
|
+
#
|
8
|
+
# They do not compile their markup fragments at load time as simpler
|
9
|
+
# components do for efficiency. Rather they compile their fragments
|
10
|
+
# when they are themselves undergoing compilation. This facilitates
|
11
|
+
# access to arguments which were passed to the component at initialization.
|
12
|
+
#
|
13
|
+
# For example, if we have a Row component that is Configurable:
|
14
|
+
#
|
15
|
+
# row(:main)
|
16
|
+
#
|
17
|
+
# might process to:
|
18
|
+
#
|
19
|
+
# <div id="main" class="row" />
|
20
|
+
#
|
21
|
+
|
22
|
+
module Configurable
|
23
|
+
def self.included(base)
|
24
|
+
base.class_eval do
|
25
|
+
extend ClassMethods
|
26
|
+
include InstanceMethods
|
27
|
+
|
28
|
+
# Stores arguments for later processing, eg., compile time
|
29
|
+
def initialize(*args)
|
30
|
+
@args = args.dup
|
31
|
+
@config = {}
|
32
|
+
_process_args!(args)
|
33
|
+
super(*args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Override Templating._compile_fragment to delay compilation
|
43
|
+
def _compile_fragment(block, options = {})
|
44
|
+
if options.delete(:force_compile)
|
45
|
+
super(block, options)
|
46
|
+
else
|
47
|
+
block
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def _lookup(name, options = {})
|
52
|
+
super(name, options.merge(force_compile: true))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
module InstanceMethods
|
58
|
+
|
59
|
+
def config
|
60
|
+
@config
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :my :config
|
64
|
+
|
65
|
+
def expand_locals
|
66
|
+
{my: config}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Override Templating#lookup to pass locals
|
70
|
+
def lookup(fragment_name)
|
71
|
+
self.class.send(:_lookup, fragment_name, expand_locals)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def _process_args!(args)
|
78
|
+
if args.first.kind_of?(Symbol)
|
79
|
+
config.merge!(id: args.shift)
|
80
|
+
end
|
81
|
+
while arg = args.shift
|
82
|
+
if arg.kind_of?(Hash)
|
83
|
+
config.merge!(arg)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Capabilities
|
4
|
+
#
|
5
|
+
# Adds the capability to iterate over a collection repeating a markup
|
6
|
+
# fragment for each member.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# class ParagraphsComponent < ExpressTemplates::Components::Base
|
11
|
+
#
|
12
|
+
# emits -> { p { item } } # item is the default local variable name
|
13
|
+
#
|
14
|
+
# for_each -> { paragraphs } # evaluated in view context
|
15
|
+
#
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Must specify an <tt>iterator</tt> either as a proc to be evaluated in the
|
19
|
+
# view context or else as a variable name in the form of a symbol which is
|
20
|
+
# assumed to be available in the view context.
|
21
|
+
#
|
22
|
+
# Provides:
|
23
|
+
#
|
24
|
+
# * Iterating::ClassMethods (for_each)
|
25
|
+
#
|
26
|
+
module Iterating
|
27
|
+
def self.included(base)
|
28
|
+
base.class_eval do
|
29
|
+
extend ClassMethods
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
# Sets the component up to use iterating logic to reproduce a fragment
|
35
|
+
# for a collection.
|
36
|
+
#
|
37
|
+
# Parameters include an iterator that may be :@variable assumed to be
|
38
|
+
# available in context or a proc that when evaluated in the context
|
39
|
+
# should return a collection.
|
40
|
+
#
|
41
|
+
# An <tt>:emit</tt> option may specify a fragment to emit.
|
42
|
+
# Defaults to <tt>:markup</tt>
|
43
|
+
#
|
44
|
+
# An <tt>:as</tt> option specifies the local variable name for each
|
45
|
+
# item in the collection for use in the fragment. Defaults to: <tt>item</tt>
|
46
|
+
#
|
47
|
+
# An <tt>:empty</tt> option specifies a fragment to use for the
|
48
|
+
# empty state when the iterator returns an empty collection.
|
49
|
+
def for_each(iterator, as: :item, emit: :markup, empty: nil)
|
50
|
+
if iterator.kind_of?(Symbol)
|
51
|
+
var_name = iterator.to_s.gsub(/^@/,'').singularize.to_sym
|
52
|
+
else
|
53
|
+
var_name = as
|
54
|
+
end
|
55
|
+
using_logic do |component, options|
|
56
|
+
collection = if iterator.kind_of?(Proc)
|
57
|
+
if iterator.arity.eql?(1)
|
58
|
+
instance_exec(options, &iterator)
|
59
|
+
else
|
60
|
+
instance_exec &iterator
|
61
|
+
end
|
62
|
+
else
|
63
|
+
eval(iterator.to_s)
|
64
|
+
end
|
65
|
+
if collection.empty?
|
66
|
+
empty ? component[empty] : ''
|
67
|
+
else
|
68
|
+
collection.map do |item|
|
69
|
+
b = binding
|
70
|
+
b.local_variable_set(var_name, item)
|
71
|
+
b.eval(component[emit], __FILE__)
|
72
|
+
end.join
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Capabilities
|
4
|
+
module Parenting
|
5
|
+
|
6
|
+
# Parenting adds the capability for a component to have and render
|
7
|
+
# children that may be specified in the template fragment which
|
8
|
+
# includes the component.
|
9
|
+
#
|
10
|
+
# Example parenting component:
|
11
|
+
#
|
12
|
+
# class Line < ExpressTemplates::Components::Base
|
13
|
+
# include Capabilities::Parenting
|
14
|
+
#
|
15
|
+
# emits { p.line { _yield } }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# You might then use this component like so:
|
19
|
+
#
|
20
|
+
# line "In", "Xanadu", "did", "Kubla", "Khan"
|
21
|
+
# line { "A stately pleasure-dome decree :" }
|
22
|
+
# line { "Where Alph, the sacred river, ran" }
|
23
|
+
# line %q(Through caverns measureless to man)
|
24
|
+
# line %q|Down to a sunless sea.|
|
25
|
+
#
|
26
|
+
# Provides
|
27
|
+
#
|
28
|
+
# * ClassMethods for rendering
|
29
|
+
# * InstanceMethods for managing children
|
30
|
+
#
|
31
|
+
def self.included(base)
|
32
|
+
base.class_eval do
|
33
|
+
extend ClassMethods
|
34
|
+
include InstanceMethods
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def render_with_children(context, locals = {}, child_markup_src = nil)
|
40
|
+
_wrap_it(context, locals) do |component|
|
41
|
+
child_markup_src
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
def children
|
49
|
+
@children ||= []
|
50
|
+
end
|
51
|
+
|
52
|
+
def children=(children)
|
53
|
+
@children =children
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile
|
57
|
+
locals = (expand_locals rescue nil).inspect
|
58
|
+
compiled_children = nil
|
59
|
+
args = %w(self)
|
60
|
+
args << locals
|
61
|
+
Indenter.for(:compile) do |indent, indent_with_newline|
|
62
|
+
compiled_children = children.map { |child| indent_with_newline + child.compile }.join("+")
|
63
|
+
compiled_children.gsub!('"+"', '') # avoid unnecessary string concatenation
|
64
|
+
args << compiled_children unless compiled_children.empty?
|
65
|
+
end
|
66
|
+
closing_paren = compiled_children.empty? ? ')' : "\n#{Indenter.for(:compile)})"
|
67
|
+
"#{self.class.to_s}.render_with_children(#{args.join(', ')}#{closing_paren}"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|