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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_extensions/proc.rb +72 -20
  3. data/lib/express_templates/compiler.rb +9 -1
  4. data/lib/express_templates/components/base.rb +1 -1
  5. data/lib/express_templates/components/capabilities/building.rb +8 -0
  6. data/lib/express_templates/components/capabilities/conditionality.rb +8 -6
  7. data/lib/express_templates/components/capabilities/configurable.rb +3 -4
  8. data/lib/express_templates/components/capabilities/iterating.rb +18 -23
  9. data/lib/express_templates/components/capabilities/parenting.rb +4 -18
  10. data/lib/express_templates/components/capabilities/rendering.rb +2 -65
  11. data/lib/express_templates/components/capabilities/templating.rb +24 -22
  12. data/lib/express_templates/components/capabilities/wrapping.rb +23 -39
  13. data/lib/express_templates/components/column.rb +1 -1
  14. data/lib/express_templates/components/for_each.rb +28 -0
  15. data/lib/express_templates/components/form_for.rb +19 -0
  16. data/lib/express_templates/components/form_rails_support.rb +1 -1
  17. data/lib/express_templates/components/null_wrap.rb +9 -0
  18. data/lib/express_templates/components/row.rb +1 -1
  19. data/lib/express_templates/components/table_for.rb +119 -0
  20. data/lib/express_templates/components/tree_for.rb +41 -0
  21. data/lib/express_templates/components/unless_block.rb +40 -0
  22. data/lib/express_templates/components.rb +4 -0
  23. data/lib/express_templates/expander.rb +15 -4
  24. data/lib/express_templates/indenter.rb +8 -5
  25. data/lib/express_templates/markup/tag.rb +62 -30
  26. data/lib/express_templates/markup/wrapper.rb +1 -1
  27. data/lib/express_templates/version.rb +1 -1
  28. data/test/components/base_test.rb +5 -38
  29. data/test/components/conditionality_test.rb +1 -1
  30. data/test/components/configurable_test.rb +2 -2
  31. data/test/components/container_test.rb +1 -1
  32. data/test/components/iterating_test.rb +30 -9
  33. data/test/components/table_for_test.rb +116 -0
  34. data/test/core_extensions/proc_test.rb +35 -5
  35. data/test/dummy/log/test.log +645 -0
  36. data/test/fixtures/{a_big_page.html → a_big_page2.html} +0 -0
  37. data/test/indenter_test.rb +6 -4
  38. data/test/markup/tag_test.rb +15 -2
  39. data/test/performance_test.rb +1 -1
  40. data/test/test_helper.rb +2 -0
  41. metadata +26 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b560214168f6753cf3a9d186ee89c8a12f277816
4
- data.tar.gz: 99a27ad570c78b032251ce6c6472c2aebbfb8dc0
3
+ metadata.gz: 16e00189a36058717818d7a7c66638c0164fcef6
4
+ data.tar.gz: 559bf6b66e66bf2caacd9c7594a9227ed2208cee
5
5
  SHA512:
6
- metadata.gz: 16523908d29f4f748c767a40b98a45e97b69c7e33b5ee5bb3e0085438e2a8282929ed60e176a12dfacc97ea6673cb04582c0ad3f1e6b2f3af69c93ba62a96e1d
7
- data.tar.gz: 2885c82c2e0b31dc34866324d701362d2ff007b316e821a1713588362dad690e873430be2f906b371e5201c9a9b6205aa1aa72ca8515a084fd5eb199edcde50d
6
+ metadata.gz: bfbbac386e1b3db30466f8517dc7c9a82f65ebce01c548357ba2754399f3808ef9601eb157cc78775fc67f571930f06dfc4da57ed7ee57e267b5624ad9a82b83
7
+ data.tar.gz: 20b4d748cb8780f9366ae4875f1795af827ce0db5b59c128d096a5cb47de38d7639bb90d55d53183ce4df2349341d68d52e6084f328e510090cc899ae706fb07
@@ -2,9 +2,13 @@ require 'ripper'
2
2
  require 'pp'
3
3
  class Proc
4
4
 
5
- TOKEN_PAIRS = {[:on_lbrace, '{'] => [:on_rbrace, '}'],
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
- [:on_tlambeg, '{'] => [:on_rbrace, '}']}
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
- 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]
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
- 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
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
- source_str
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
- compiled = expander.expand(src, &block).map(&:compile)
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,7 +37,7 @@ module ExpressTemplates
37
37
  # * Capabilities::Wrapping
38
38
  # * Capabilities::Iterating
39
39
  #
40
- class Base
40
+ class Base < Expander
41
41
  include ExpressTemplates::Macro
42
42
  include Capabilities::Templating
43
43
  include Capabilities::Rendering
@@ -0,0 +1,8 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ module Capabilities
4
+ module Building
5
+ end
6
+ end
7
+ end
8
+ end
@@ -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
- using_logic do |component, options|
43
- condition = instance_exec(&component.condition_proc)
44
- eval(component[:markup]) if condition
45
- end
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
- self.class.send(:_lookup, fragment_name, expand_locals)
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
- if iterator.kind_of?(Symbol)
51
- var_name = iterator.to_s.gsub(/^@/,'').singularize.to_sym
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
- 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
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
- locals = (expand_locals rescue nil).inspect
58
- args = %w(self)
59
- args << locals
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
- # Store a block of logic which will be evalutated in a context
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
- _store :markup, _compile_fragment(args.first||template_code) # default fragment is named :markup
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, _compile_fragment(block))
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
- fragment = @fragments[name] or raise "no template fragment supplied for: #{name}"
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
- prior_logic = @control_flow
46
- using_logic do |component|
47
- if instance_exec(&dont_wrap_if)
48
- eval(component.render((self||Object.new), &prior_logic))
49
- else
50
- component._wrap_it(self, &prior_logic)
51
- end
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
@@ -3,7 +3,7 @@ module ExpressTemplates
3
3
  class Column < Container
4
4
  include Capabilities::Configurable
5
5
 
6
- emits {
6
+ emits -> {
7
7
  div.column(my[:id]) {
8
8
  _yield
9
9
  }
@@ -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
@@ -7,7 +7,7 @@ module ExpressTemplates
7
7
  # An optional method may be speficied. Defaults to 'post'.
8
8
  class FormRailsSupport < Base
9
9
  include Capabilities::Configurable
10
- emits {
10
+ emits -> {
11
11
  div(style: 'display:none') {
12
12
  utf8_enforcer_tag
13
13
  method_tag(my[:id] || :post)
@@ -0,0 +1,9 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ class NullWrap < Components::Container
4
+ def compile
5
+ compile_children
6
+ end
7
+ end
8
+ end
9
+ end
@@ -18,7 +18,7 @@ module ExpressTemplates
18
18
  class Row < Container
19
19
  include Capabilities::Configurable
20
20
 
21
- emits {
21
+ emits -> {
22
22
  div.row(my[:id]) {
23
23
  _yield
24
24
  }