express_templates 0.2.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|