express_templates 0.2.7 → 0.3.0
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/lib/core_extensions/proc.rb +72 -20
- data/lib/express_templates/compiler.rb +9 -1
- data/lib/express_templates/components/base.rb +1 -1
- data/lib/express_templates/components/capabilities/building.rb +8 -0
- data/lib/express_templates/components/capabilities/conditionality.rb +8 -6
- data/lib/express_templates/components/capabilities/configurable.rb +3 -4
- data/lib/express_templates/components/capabilities/iterating.rb +18 -23
- data/lib/express_templates/components/capabilities/parenting.rb +4 -18
- data/lib/express_templates/components/capabilities/rendering.rb +2 -65
- data/lib/express_templates/components/capabilities/templating.rb +24 -22
- data/lib/express_templates/components/capabilities/wrapping.rb +23 -39
- data/lib/express_templates/components/column.rb +1 -1
- data/lib/express_templates/components/for_each.rb +28 -0
- data/lib/express_templates/components/form_for.rb +19 -0
- data/lib/express_templates/components/form_rails_support.rb +1 -1
- data/lib/express_templates/components/null_wrap.rb +9 -0
- data/lib/express_templates/components/row.rb +1 -1
- data/lib/express_templates/components/table_for.rb +119 -0
- data/lib/express_templates/components/tree_for.rb +41 -0
- data/lib/express_templates/components/unless_block.rb +40 -0
- data/lib/express_templates/components.rb +4 -0
- data/lib/express_templates/expander.rb +15 -4
- data/lib/express_templates/indenter.rb +8 -5
- data/lib/express_templates/markup/tag.rb +62 -30
- data/lib/express_templates/markup/wrapper.rb +1 -1
- data/lib/express_templates/version.rb +1 -1
- data/test/components/base_test.rb +5 -38
- data/test/components/conditionality_test.rb +1 -1
- data/test/components/configurable_test.rb +2 -2
- data/test/components/container_test.rb +1 -1
- data/test/components/iterating_test.rb +30 -9
- data/test/components/table_for_test.rb +116 -0
- data/test/core_extensions/proc_test.rb +35 -5
- data/test/dummy/log/test.log +645 -0
- data/test/fixtures/{a_big_page.html → a_big_page2.html} +0 -0
- data/test/indenter_test.rb +6 -4
- data/test/markup/tag_test.rb +15 -2
- data/test/performance_test.rb +1 -1
- data/test/test_helper.rb +2 -0
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16e00189a36058717818d7a7c66638c0164fcef6
|
4
|
+
data.tar.gz: 559bf6b66e66bf2caacd9c7594a9227ed2208cee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfbbac386e1b3db30466f8517dc7c9a82f65ebce01c548357ba2754399f3808ef9601eb157cc78775fc67f571930f06dfc4da57ed7ee57e267b5624ad9a82b83
|
7
|
+
data.tar.gz: 20b4d748cb8780f9366ae4875f1795af827ce0db5b59c128d096a5cb47de38d7639bb90d55d53183ce4df2349341d68d52e6084f328e510090cc899ae706fb07
|
data/lib/core_extensions/proc.rb
CHANGED
@@ -2,9 +2,13 @@ require 'ripper'
|
|
2
2
|
require 'pp'
|
3
3
|
class Proc
|
4
4
|
|
5
|
-
|
5
|
+
TLAMBEG = [:on_tlambeg, "{"]
|
6
|
+
TLAMBDA = [:on_tlambda, "->"]
|
7
|
+
LBRACE = [:on_lbrace, '{']
|
8
|
+
|
9
|
+
TOKEN_PAIRS = {LBRACE => [:on_rbrace, '}'],
|
6
10
|
[:on_kw, 'do'] => [:on_kw, 'end'],
|
7
|
-
|
11
|
+
TLAMBDA => [:on_rbrace, '}']}
|
8
12
|
|
9
13
|
# Make a best effort to provide the original source for a block
|
10
14
|
# based on extracting a string from the file identified in
|
@@ -18,25 +22,73 @@ class Proc
|
|
18
22
|
# If you require only the source of blocks-within-other-blocks, start them
|
19
23
|
# on a new line as would be best practice for clarity and readability.
|
20
24
|
def source
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
@source ||= begin
|
26
|
+
file, line_no = source_location
|
27
|
+
raise "no line number provided for source_location: #{self}" if line_no.nil?
|
28
|
+
tokens = Ripper.lex File.read(file)
|
29
|
+
tokens_on_line = tokens.select {|pos, lbl, str| pos[0].eql?(line_no) }
|
30
|
+
starting_token = tokens_on_line.detect do |pos, lbl, str|
|
31
|
+
TOKEN_PAIRS.keys.include? [lbl, str]
|
32
|
+
end
|
33
|
+
starting_token_type = [starting_token[1], starting_token[2]]
|
34
|
+
ending_token_type = TOKEN_PAIRS[starting_token_type]
|
35
|
+
source_str = ""
|
36
|
+
remaining_tokens = tokens[tokens.index(starting_token)..-1]
|
37
|
+
nesting = -1
|
38
|
+
starting_nesting_token_types = if [TLAMBDA, LBRACE].include?(starting_token_type)
|
39
|
+
[TLAMBDA, LBRACE]
|
40
|
+
else
|
41
|
+
[starting_token_type]
|
42
|
+
end
|
43
|
+
|
44
|
+
while token = remaining_tokens.shift
|
45
|
+
token = [token[1], token[2]] # strip position
|
46
|
+
source_str << token[1]
|
47
|
+
nesting += 1 if starting_nesting_token_types.include? token
|
48
|
+
is_ending_token = token.eql?(ending_token_type)
|
49
|
+
break if is_ending_token && nesting.eql?(0)
|
50
|
+
nesting -= 1 if is_ending_token
|
51
|
+
end
|
52
|
+
source_str
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Examines the source of a proc to extract the body by
|
57
|
+
# removing the outermost block delimiters and any surrounding.
|
58
|
+
# whitespace.
|
59
|
+
#
|
60
|
+
# Raises exception if the block takes arguments.
|
61
|
+
#
|
62
|
+
def source_body
|
63
|
+
raise "Cannot extract proc body on non-zero arity" unless arity.eql?(0)
|
64
|
+
tokens = Ripper.lex source
|
65
|
+
body_start_idx = 2
|
66
|
+
body_end_idx = -1
|
67
|
+
if tokens[0][1].eql?(:on_tlambda)
|
68
|
+
body_start_idx = tokens.index(tokens.detect { |t| t[1].eql?(:on_tlambeg) }) + 1
|
27
69
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
while
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
70
|
+
body_tokens = tokens[body_start_idx..-1]
|
71
|
+
|
72
|
+
body_tokens.pop # ending token of proc
|
73
|
+
# remove trailing whitespace
|
74
|
+
whitespace = [:on_sp, :on_nl, :on_ignored_nl]
|
75
|
+
body_tokens.pop while whitespace.include?(body_tokens[-1][1])
|
76
|
+
# remove leading whitespace
|
77
|
+
body_tokens.shift while whitespace.include?(body_tokens[0][1])
|
78
|
+
|
79
|
+
# put them back together
|
80
|
+
body_tokens.map {|token| token[2] }.join
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.from_source(prc_src)
|
84
|
+
raise ArgumentError unless prc_src.kind_of?(String)
|
85
|
+
prc = begin
|
86
|
+
eval(prc_src)
|
87
|
+
rescue ArgumentError => e
|
88
|
+
binding.pry
|
39
89
|
end
|
40
|
-
|
90
|
+
prc.instance_variable_set(:@source, prc_src)
|
91
|
+
prc
|
41
92
|
end
|
93
|
+
|
42
94
|
end
|
@@ -5,7 +5,15 @@ module ExpressTemplates
|
|
5
5
|
|
6
6
|
expander = Expander.new(template)
|
7
7
|
|
8
|
-
|
8
|
+
Thread.current[:first_whitepace_removed] ||= 0
|
9
|
+
Thread.current[:first_whitepace_removed] += 1
|
10
|
+
begin
|
11
|
+
compiled = expander.expand(src, &block).map(&:compile)
|
12
|
+
compiled.first.sub!(/^"\n+/, '"') if Thread.current[:first_whitepace_removed].eql?(1)
|
13
|
+
Thread.current[:first_whitepace_removed] -= 1
|
14
|
+
ensure
|
15
|
+
Thread.current[:first_whitepace_removed] = nil if Thread.current[:first_whitepace_removed].eql?(0)
|
16
|
+
end
|
9
17
|
|
10
18
|
return compiled.join("+").gsub('"+"', '').tap do |s|
|
11
19
|
puts("\n"+template.inspect+"\n"+s) if ENV['DEBUG'].eql?('true')
|
@@ -37,12 +37,14 @@ module ExpressTemplates
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def only_if condition_proc
|
40
|
-
@condition_proc = condition_proc
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
@condition_proc = Proc.from_source "-> {!(#{condition_proc.source_body})}"
|
41
|
+
inner_src = self[:markup]
|
42
|
+
fragment_src = %Q(-> {
|
43
|
+
unless_block(Proc.from_source(#{@condition_proc.source.inspect})) {
|
44
|
+
#{inner_src.source_body}
|
45
|
+
}
|
46
|
+
})
|
47
|
+
_store :markup, Proc.from_source(fragment_src)
|
46
48
|
end
|
47
49
|
|
48
50
|
end
|
@@ -28,7 +28,6 @@ module ExpressTemplates
|
|
28
28
|
# Stores arguments for later processing, eg., compile time
|
29
29
|
def initialize(*args)
|
30
30
|
@args = args.dup
|
31
|
-
@config = {}
|
32
31
|
_process_args!(args)
|
33
32
|
super(*args)
|
34
33
|
end
|
@@ -57,7 +56,7 @@ module ExpressTemplates
|
|
57
56
|
module InstanceMethods
|
58
57
|
|
59
58
|
def config
|
60
|
-
@config
|
59
|
+
@config ||= {}
|
61
60
|
end
|
62
61
|
|
63
62
|
alias :my :config
|
@@ -67,8 +66,8 @@ module ExpressTemplates
|
|
67
66
|
end
|
68
67
|
|
69
68
|
# Override Templating#lookup to pass locals
|
70
|
-
def lookup(fragment_name)
|
71
|
-
|
69
|
+
def lookup(fragment_name, options = {})
|
70
|
+
super(fragment_name, options.merge(expand_locals))
|
72
71
|
end
|
73
72
|
|
74
73
|
|
@@ -47,31 +47,26 @@ module ExpressTemplates
|
|
47
47
|
# An <tt>:empty</tt> option specifies a fragment to use for the
|
48
48
|
# empty state when the iterator returns an empty collection.
|
49
49
|
def for_each(iterator, as: :item, emit: :markup, empty: nil)
|
50
|
-
|
51
|
-
|
50
|
+
as = as.to_sym
|
51
|
+
emit = emit.to_sym
|
52
|
+
iterator = iterator.kind_of?(Proc) ? iterator.source : iterator
|
53
|
+
fragment_src = if empty
|
54
|
+
%Q(-> {
|
55
|
+
unless_block(Proc.from_source("-> {#{iterator}.call.empty?}"), alt: #{self[empty].source}) {
|
56
|
+
for_each(Proc.from_source("#{iterator}"), as: #{as.inspect}) {
|
57
|
+
#{self[emit].source_body}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
})
|
52
61
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
62
|
+
%Q(-> {
|
63
|
+
for_each(Proc.from_source("#{iterator}"), as: #{as.inspect}) {
|
64
|
+
#{self[emit].source_body}
|
65
|
+
}
|
66
|
+
})
|
74
67
|
end
|
68
|
+
fragment = Proc.from_source(fragment_src)
|
69
|
+
_store :markup, fragment
|
75
70
|
end
|
76
71
|
end
|
77
72
|
end
|
@@ -30,20 +30,10 @@ module ExpressTemplates
|
|
30
30
|
#
|
31
31
|
def self.included(base)
|
32
32
|
base.class_eval do
|
33
|
-
extend ClassMethods
|
34
33
|
include InstanceMethods
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
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
37
|
module InstanceMethods
|
48
38
|
def children
|
49
39
|
@children ||= []
|
@@ -54,13 +44,9 @@ module ExpressTemplates
|
|
54
44
|
end
|
55
45
|
|
56
46
|
def compile
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
compiled_children = compile_children
|
61
|
-
args << compiled_children unless compiled_children.empty?
|
62
|
-
closing_paren = compiled_children.empty? ? ')' : "\n#{Indenter.for(:compile)})"
|
63
|
-
"#{self.class.to_s}.render_with_children(#{args.join(', ')}#{closing_paren}"
|
47
|
+
null_wrapped_children = "null_wrap { #{compile_children} }"
|
48
|
+
wrap_children_src = self.class[:markup].source.gsub(/\W_yield\W/, null_wrapped_children)
|
49
|
+
_compile_fragment(Proc.from_source(wrap_children_src))
|
64
50
|
end
|
65
51
|
|
66
52
|
def compile_children
|
@@ -69,7 +55,7 @@ module ExpressTemplates
|
|
69
55
|
compiled_children = children.map do |child|
|
70
56
|
indent_with_newline +
|
71
57
|
(child.compile rescue %Q("#{child}")) # Bare strings may be children
|
72
|
-
end.join("
|
58
|
+
end.join("+\n")
|
73
59
|
compiled_children.gsub!('"+"', '') # avoid unnecessary string concatenation
|
74
60
|
end
|
75
61
|
return compiled_children
|
@@ -15,78 +15,15 @@ module ExpressTemplates
|
|
15
15
|
def self.included(base)
|
16
16
|
base.class_eval do
|
17
17
|
extend ClassMethods
|
18
|
-
include InstanceMethods
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
21
|
module ClassMethods
|
23
|
-
|
24
|
-
|
25
|
-
# during rendering.
|
26
|
-
#
|
27
|
-
# The block will be passed a reference to the component's class.
|
28
|
-
#
|
29
|
-
def using_logic(&block)
|
30
|
-
@control_flow = block
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns a string containing markup generated by evaluating
|
34
|
-
# blocks of supplied logic or compiled template code in a <tt>context</tt>.
|
35
|
-
#
|
36
|
-
# The context may be any object however, it is generally an
|
37
|
-
# ActionView::Context.
|
38
|
-
#
|
39
|
-
# Components when compiled may yield something like:
|
40
|
-
#
|
41
|
-
# "MyComponent.render(self, {id: 'foo'})"
|
42
|
-
#
|
43
|
-
# The supplied options hash may be used to modify any fragments
|
44
|
-
# or the behavior of any logic.
|
45
|
-
#
|
46
|
-
# If a fragment identifier is passed as a symbol in the first
|
47
|
-
# option position, this will render that fragment only.
|
48
|
-
#
|
49
|
-
def render(context, *opts, &context_logic_block)
|
50
|
-
fragment = opts.shift if opts.first.is_a?(Symbol)
|
51
|
-
begin
|
52
|
-
if fragment
|
53
|
-
context.instance_eval(_lookup(fragment, opts)) || ''
|
54
|
-
else
|
55
|
-
flow = context_logic_block || @control_flow
|
56
|
-
exec_args = [self, opts].take(flow.arity)
|
57
|
-
context.instance_exec(*exec_args, &flow) || ''
|
58
|
-
end
|
59
|
-
rescue => e
|
60
|
-
binding.pry if ENV['DEBUG'].eql?("true")
|
61
|
-
raise "Rendering error in #{self}: #{e.to_s}"
|
62
|
-
end
|
22
|
+
def render_in(context, &view_code)
|
23
|
+
context.instance_eval(&view_code)
|
63
24
|
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def _control_flow
|
68
|
-
@control_flow
|
69
|
-
end
|
70
|
-
|
71
25
|
end
|
72
26
|
|
73
|
-
module InstanceMethods
|
74
|
-
|
75
|
-
def compile
|
76
|
-
if _provides_logic?
|
77
|
-
"#{self.class.to_s}.render(self)"
|
78
|
-
else
|
79
|
-
lookup :markup
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def _provides_logic?
|
86
|
-
!!self.class.send(:_control_flow)
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
27
|
end
|
91
28
|
end
|
92
29
|
end
|
@@ -51,11 +51,13 @@ module ExpressTemplates
|
|
51
51
|
#
|
52
52
|
def emits(*args, &template_code)
|
53
53
|
if args.first.respond_to?(:call) or template_code
|
54
|
-
|
54
|
+
fragment = (args.first||template_code)
|
55
|
+
raise "must use stabby lambda" unless fragment.lambda?
|
56
|
+
_store :markup, fragment# default fragment is named :markup
|
55
57
|
else
|
56
58
|
args.first.to_a.each do |name, block|
|
57
59
|
raise ArgumentError unless name.is_a?(Symbol) and block.is_a?(Proc)
|
58
|
-
_store(name,
|
60
|
+
_store(name, block)
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
@@ -116,26 +118,9 @@ module ExpressTemplates
|
|
116
118
|
# Returns a string containing ruby code which evaluates to markup.
|
117
119
|
def _lookup(name, options = {})
|
118
120
|
@fragments ||= Hash.new
|
119
|
-
|
120
|
-
if fragment.kind_of?(Proc)
|
121
|
-
_compile_fragment(fragment, options)
|
122
|
-
else
|
123
|
-
fragment
|
124
|
-
end
|
121
|
+
@fragments[name] or binding.pry #raise "no template fragment supplied for: #{name}"
|
125
122
|
end
|
126
123
|
|
127
|
-
# Expands and compiles the supplied block representing a
|
128
|
-
# template fragment.
|
129
|
-
#
|
130
|
-
# Any supplied options are passed as locals for use during expansion.
|
131
|
-
#
|
132
|
-
# Returns a string containing ruby code which evaluates to markup.
|
133
|
-
def _compile_fragment(block, options = {})
|
134
|
-
expander = ExpressTemplates::Expander.new(nil, special_handlers, options)
|
135
|
-
expander.expand(&block).map(&:compile).join("+")
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
124
|
private
|
140
125
|
|
141
126
|
|
@@ -183,10 +168,27 @@ module ExpressTemplates
|
|
183
168
|
|
184
169
|
module InstanceMethods
|
185
170
|
|
186
|
-
def lookup(fragment_name)
|
187
|
-
self.class[fragment_name]
|
171
|
+
def lookup(fragment_name, options={})
|
172
|
+
fragment = self.class[fragment_name]
|
173
|
+
if fragment.kind_of?(Proc)
|
174
|
+
_compile_fragment(fragment, options)
|
175
|
+
else
|
176
|
+
fragment
|
177
|
+
end
|
188
178
|
end
|
189
179
|
|
180
|
+
# Expands and compiles the supplied block representing a
|
181
|
+
# template fragment.
|
182
|
+
#
|
183
|
+
# Any supplied options are passed as locals for use during expansion.
|
184
|
+
#
|
185
|
+
# Returns a string containing ruby code which evaluates to markup.
|
186
|
+
def _compile_fragment(block, options = {})
|
187
|
+
initialize_expander(nil, self.class.special_handlers, options)
|
188
|
+
expand(&block).map(&:compile).join("+").gsub('"+"', '')
|
189
|
+
end
|
190
|
+
|
191
|
+
|
190
192
|
end
|
191
193
|
|
192
194
|
|
@@ -30,6 +30,13 @@ module ExpressTemplates
|
|
30
30
|
def self.included(base)
|
31
31
|
base.class_eval do
|
32
32
|
extend ClassMethods
|
33
|
+
include InstanceMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
def compile
|
39
|
+
lookup :markup
|
33
40
|
end
|
34
41
|
end
|
35
42
|
|
@@ -37,19 +44,24 @@ module ExpressTemplates
|
|
37
44
|
|
38
45
|
# Enclose whatever the component would already generate
|
39
46
|
# inside the specified fragment wherever we encounter _yield
|
40
|
-
|
41
|
-
# Note: this must come after any statements that affect the logic
|
42
|
-
# flow.
|
43
|
-
def wrap_with(fragment, dont_wrap_if: -> {false} )
|
47
|
+
def wrap_with(fragment, dont_wrap_if: false )
|
44
48
|
wrapper_name(fragment)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
wrapper_src = _lookup(fragment).source
|
50
|
+
inner_src = _lookup(:markup).source_body
|
51
|
+
wrapped_src = wrapper_src.gsub!(/\W_yield\W/, inner_src)
|
52
|
+
|
53
|
+
fragment_src = if dont_wrap_if
|
54
|
+
%Q(-> {
|
55
|
+
unless_block(Proc.from_source(#{dont_wrap_if.source.inspect}), alt: Proc.from_source(%q(-> {#{inner_src}}))) {
|
56
|
+
#{Proc.from_source(wrapped_src).source_body}
|
57
|
+
}
|
58
|
+
})
|
59
|
+
else
|
60
|
+
wrapped_src
|
52
61
|
end
|
62
|
+
|
63
|
+
_store :markup, Proc.from_source(fragment_src)
|
64
|
+
|
53
65
|
end
|
54
66
|
|
55
67
|
def wrapper_name(name = nil)
|
@@ -60,34 +72,6 @@ module ExpressTemplates
|
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
63
|
-
# added back in for compatability with prior interface
|
64
|
-
# should probably be refactored away
|
65
|
-
def _wrap_using(fragment, context=nil, options={}, &to_be_wrapped)
|
66
|
-
old_wrapper_name = @wrapper_name
|
67
|
-
@wrapper_name = fragment
|
68
|
-
result = _wrap_it(context, options, &to_be_wrapped)
|
69
|
-
@wrapper_name = old_wrapper_name
|
70
|
-
return result
|
71
|
-
end
|
72
|
-
|
73
|
-
def _wrap_it(context=nil, options={}, &to_be_wrapped)
|
74
|
-
body = ''
|
75
|
-
if to_be_wrapped
|
76
|
-
body = render((context||Object.new), &to_be_wrapped)
|
77
|
-
end
|
78
|
-
if compiled_src = _lookup(wrapper_name, options)
|
79
|
-
if context.nil?
|
80
|
-
eval(compiled_src).gsub(/\{\{_yield\}\}/, body)
|
81
|
-
else
|
82
|
-
ctx = context.instance_eval("binding")
|
83
|
-
ctx.local_variable_set(:_yield, body)
|
84
|
-
ctx.eval(compiled_src)
|
85
|
-
end
|
86
|
-
else
|
87
|
-
raise "No wrapper fragment provided for '#{wrapper_name}'"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
75
|
def _yield(*args)
|
92
76
|
"{{_yield}}"
|
93
77
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
class ForEach < Components::Container
|
4
|
+
attr :collection, :member
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
iterator = args.shift
|
8
|
+
options = args.first.kind_of?(Hash) ? args.shift : {}
|
9
|
+
expander = args.shift
|
10
|
+
@collection, @member = nil, (options[:as]||"item")
|
11
|
+
if iterator.kind_of?(Symbol)
|
12
|
+
@collection = iterator.to_s
|
13
|
+
@member = collection.sub(/^@/, '').singularize
|
14
|
+
elsif iterator.kind_of?(Proc)
|
15
|
+
@collection = "(#{iterator.source}.call)"
|
16
|
+
elsif iterator.kind_of?(String)
|
17
|
+
@collection = "(#{iterator}.call)"
|
18
|
+
else
|
19
|
+
raise "ForEach unknown iterator: #{iterator}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile
|
24
|
+
%Q((#{@collection}.each_with_index.map do |#{@member}, #{@member}_index|#{compile_children}\nend).join)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
#
|
4
|
+
# Create an html <tt>form</tt> for a model object.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# form_for(:people) do |t|
|
10
|
+
# t.text_field :name
|
11
|
+
# t.email_field :email
|
12
|
+
# t.phone_field :phone
|
13
|
+
# end
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
class FormFor < Container
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|