inversion 0.0.1
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.
- data.tar.gz.sig +2 -0
- data/.gemtest +0 -0
- data/ChangeLog +836 -0
- data/History.md +4 -0
- data/Manifest.txt +74 -0
- data/README.rdoc +171 -0
- data/Rakefile +55 -0
- data/bin/inversion +276 -0
- data/lib/inversion.rb +98 -0
- data/lib/inversion/exceptions.rb +21 -0
- data/lib/inversion/mixins.rb +236 -0
- data/lib/inversion/monkeypatches.rb +20 -0
- data/lib/inversion/renderstate.rb +337 -0
- data/lib/inversion/sinatra.rb +35 -0
- data/lib/inversion/template.rb +250 -0
- data/lib/inversion/template/attrtag.rb +120 -0
- data/lib/inversion/template/calltag.rb +16 -0
- data/lib/inversion/template/codetag.rb +164 -0
- data/lib/inversion/template/commenttag.rb +54 -0
- data/lib/inversion/template/conditionaltag.rb +49 -0
- data/lib/inversion/template/configtag.rb +60 -0
- data/lib/inversion/template/containertag.rb +45 -0
- data/lib/inversion/template/elsetag.rb +62 -0
- data/lib/inversion/template/elsiftag.rb +49 -0
- data/lib/inversion/template/endtag.rb +55 -0
- data/lib/inversion/template/escapetag.rb +26 -0
- data/lib/inversion/template/fortag.rb +120 -0
- data/lib/inversion/template/iftag.rb +69 -0
- data/lib/inversion/template/importtag.rb +70 -0
- data/lib/inversion/template/includetag.rb +51 -0
- data/lib/inversion/template/node.rb +102 -0
- data/lib/inversion/template/parser.rb +297 -0
- data/lib/inversion/template/pptag.rb +28 -0
- data/lib/inversion/template/publishtag.rb +72 -0
- data/lib/inversion/template/subscribetag.rb +88 -0
- data/lib/inversion/template/tag.rb +150 -0
- data/lib/inversion/template/textnode.rb +43 -0
- data/lib/inversion/template/unlesstag.rb +60 -0
- data/lib/inversion/template/uriencodetag.rb +30 -0
- data/lib/inversion/template/yieldtag.rb +51 -0
- data/lib/inversion/tilt.rb +82 -0
- data/lib/inversion/utils.rb +235 -0
- data/spec/data/sinatra/hello.inversion +1 -0
- data/spec/inversion/mixins_spec.rb +177 -0
- data/spec/inversion/monkeypatches_spec.rb +35 -0
- data/spec/inversion/renderstate_spec.rb +291 -0
- data/spec/inversion/sinatra_spec.rb +59 -0
- data/spec/inversion/template/attrtag_spec.rb +216 -0
- data/spec/inversion/template/calltag_spec.rb +30 -0
- data/spec/inversion/template/codetag_spec.rb +51 -0
- data/spec/inversion/template/commenttag_spec.rb +84 -0
- data/spec/inversion/template/configtag_spec.rb +105 -0
- data/spec/inversion/template/containertag_spec.rb +54 -0
- data/spec/inversion/template/elsetag_spec.rb +105 -0
- data/spec/inversion/template/elsiftag_spec.rb +87 -0
- data/spec/inversion/template/endtag_spec.rb +78 -0
- data/spec/inversion/template/escapetag_spec.rb +59 -0
- data/spec/inversion/template/fortag_spec.rb +98 -0
- data/spec/inversion/template/iftag_spec.rb +241 -0
- data/spec/inversion/template/importtag_spec.rb +106 -0
- data/spec/inversion/template/includetag_spec.rb +108 -0
- data/spec/inversion/template/node_spec.rb +81 -0
- data/spec/inversion/template/parser_spec.rb +170 -0
- data/spec/inversion/template/pptag_spec.rb +51 -0
- data/spec/inversion/template/publishtag_spec.rb +69 -0
- data/spec/inversion/template/subscribetag_spec.rb +60 -0
- data/spec/inversion/template/tag_spec.rb +97 -0
- data/spec/inversion/template/textnode_spec.rb +86 -0
- data/spec/inversion/template/unlesstag_spec.rb +84 -0
- data/spec/inversion/template/uriencodetag_spec.rb +49 -0
- data/spec/inversion/template/yieldtag_spec.rb +54 -0
- data/spec/inversion/template_spec.rb +269 -0
- data/spec/inversion/tilt_spec.rb +47 -0
- data/spec/inversion_spec.rb +95 -0
- data/spec/lib/constants.rb +9 -0
- data/spec/lib/helpers.rb +160 -0
- metadata +316 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template/tag'
|
5
|
+
require 'inversion/template/containertag'
|
6
|
+
|
7
|
+
|
8
|
+
# Inversion 'comment' tag.
|
9
|
+
#
|
10
|
+
# This tag hides its contents from the rendered output.
|
11
|
+
#
|
12
|
+
# == Syntax
|
13
|
+
#
|
14
|
+
# <?comment ?><?end?>
|
15
|
+
# <?comment Disabled for now ?>
|
16
|
+
# <?attr some_hidden_attribute ?>
|
17
|
+
# <?end comment ?>
|
18
|
+
#
|
19
|
+
class Inversion::Template::CommentTag < Inversion::Template::Tag
|
20
|
+
include Inversion::Loggable,
|
21
|
+
Inversion::Template::ContainerTag
|
22
|
+
|
23
|
+
######
|
24
|
+
public
|
25
|
+
######
|
26
|
+
|
27
|
+
### Render (or don't render, actually) the comment's subnodes.
|
28
|
+
def render( state )
|
29
|
+
return ''
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
### Render the tag as the body of a comment, suitable for template
|
34
|
+
### debugging.
|
35
|
+
def as_comment_body
|
36
|
+
firstnode, lastnode = self.subnodes.first, self.subnodes.last
|
37
|
+
nodecount = self.subnodes.length
|
38
|
+
|
39
|
+
linedesc = if firstnode.linenum == lastnode.linenum
|
40
|
+
"on line %d" % [ firstnode.linenum ]
|
41
|
+
else
|
42
|
+
"from line %d to %d" % [ firstnode.linenum, lastnode.linenum ]
|
43
|
+
end
|
44
|
+
|
45
|
+
return "Commented out %d nodes %s%s" % [
|
46
|
+
nodecount,
|
47
|
+
linedesc,
|
48
|
+
self.body.empty? ? '' : ': ' + self.body,
|
49
|
+
]
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end # class Inversion::Template::CommentTag
|
54
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template'
|
5
|
+
|
6
|
+
# A mixin for a tag that manages conditional rendering of its subnodes.
|
7
|
+
module Inversion::Template::ConditionalTag
|
8
|
+
|
9
|
+
### Add conditional instance variables.
|
10
|
+
def initialize( *args ) # :notnew:
|
11
|
+
super
|
12
|
+
|
13
|
+
@rendering_enabled = false
|
14
|
+
@rendering_was_enabled = false
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
#########
|
19
|
+
protected
|
20
|
+
#########
|
21
|
+
|
22
|
+
### Enable rendering of subnodes.
|
23
|
+
def enable_rendering
|
24
|
+
self.log.debug " enabling rendering."
|
25
|
+
@rendering_enabled = @rendering_was_enabled = true
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
### Disable rendering of subnodes.
|
30
|
+
def disable_rendering
|
31
|
+
self.log.debug " disabling rendering."
|
32
|
+
@rendering_enabled = false
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
### Return +true+ if rendering is enabled.
|
37
|
+
def rendering_enabled?
|
38
|
+
return @rendering_enabled
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
### Return +true+ if rendering has been enabled since the tag started rendering.
|
43
|
+
def rendering_was_enabled?
|
44
|
+
return @rendering_was_enabled
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end # module Inversion::Template::ConditionalTag
|
49
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'inversion/mixins'
|
6
|
+
require 'inversion/template/tag'
|
7
|
+
|
8
|
+
|
9
|
+
# Inversion 'config' tag.
|
10
|
+
#
|
11
|
+
# A tag that dynamically alters the behavior of the template.
|
12
|
+
#
|
13
|
+
# == Examples
|
14
|
+
#
|
15
|
+
# <?config comment_start: /* ?>
|
16
|
+
# <?config comment_end: */ ?>
|
17
|
+
#
|
18
|
+
# <?config
|
19
|
+
# on_render_error: propagate
|
20
|
+
# debugging_comments: true
|
21
|
+
# comment_start: /*
|
22
|
+
# comment_end: */
|
23
|
+
# ?>
|
24
|
+
#
|
25
|
+
# <?config { comment_start: "/*", comment_end: "*/" } ?>
|
26
|
+
#
|
27
|
+
#
|
28
|
+
class Inversion::Template::ConfigTag < Inversion::Template::Tag
|
29
|
+
include Inversion::Loggable,
|
30
|
+
Inversion::HashUtilities
|
31
|
+
|
32
|
+
|
33
|
+
### Create a new ConfigTag with the specified +body+.
|
34
|
+
def initialize( body, linenum=nil, colnum=nil )
|
35
|
+
raise Inversion::ParseError, 'Empty config settings' if
|
36
|
+
body.nil? || body.strip.empty?
|
37
|
+
|
38
|
+
opts = YAML.load( body )
|
39
|
+
@options = symbolify_keys( opts )
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
######
|
46
|
+
public
|
47
|
+
######
|
48
|
+
|
49
|
+
# The config options that will be modified
|
50
|
+
attr_reader :options
|
51
|
+
|
52
|
+
|
53
|
+
### Override the options in the +parsestate+ when the config is appended to
|
54
|
+
### tree.
|
55
|
+
def before_appending( parsestate )
|
56
|
+
parsestate.options.merge!( self.options )
|
57
|
+
end
|
58
|
+
|
59
|
+
end # class Inversion::Template::ConfigTag
|
60
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion' unless defined?( Inversion )
|
5
|
+
require 'inversion/template' unless defined?( Inversion::Template )
|
6
|
+
|
7
|
+
# A mixin for a tag that allows it to contain other nodes.
|
8
|
+
module Inversion::Template::ContainerTag
|
9
|
+
|
10
|
+
### Setup subnodes for including classes. :notnew:
|
11
|
+
def initialize( * )
|
12
|
+
@subnodes = []
|
13
|
+
super
|
14
|
+
yield( self ) if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# The nodes the tag contains
|
19
|
+
attr_reader :subnodes
|
20
|
+
|
21
|
+
|
22
|
+
### Append operator: add nodes to the correct part of the parse tree.
|
23
|
+
def <<( node )
|
24
|
+
@subnodes << node
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
### Tell the parser to expect a matching <?end ?> tag.
|
30
|
+
def is_container?
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
alias_method :container?, :is_container?
|
34
|
+
|
35
|
+
|
36
|
+
### Append the container's subnodes to the +renderstate+.
|
37
|
+
def render_subnodes( renderstate )
|
38
|
+
self.subnodes.each do |node|
|
39
|
+
renderstate << node
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end # module Inversion::Template::ContainerTag
|
44
|
+
|
45
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template/tag'
|
5
|
+
|
6
|
+
|
7
|
+
# Inversion 'else' tag.
|
8
|
+
#
|
9
|
+
# This tag adds a logical switch to an IfTag. If the IfTag's condition was false,
|
10
|
+
# start rendering.
|
11
|
+
#
|
12
|
+
# == Syntax
|
13
|
+
#
|
14
|
+
# <?if attr ?>
|
15
|
+
# ...
|
16
|
+
# <?else ?>
|
17
|
+
# ...
|
18
|
+
# <?end?>
|
19
|
+
#
|
20
|
+
class Inversion::Template::ElseTag < Inversion::Template::Tag
|
21
|
+
include Inversion::Loggable
|
22
|
+
|
23
|
+
|
24
|
+
### Overridden to default body to nothing, and raise an error if it has one.
|
25
|
+
def initialize( body='', linenum=nil, colnum=nil ) # :notnew:
|
26
|
+
raise Inversion::ParseError, "else can't have a condition" unless body.to_s.strip == ''
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
######
|
32
|
+
public
|
33
|
+
######
|
34
|
+
|
35
|
+
|
36
|
+
### Parsing callback -- check to be sure the node tree can have an
|
37
|
+
### 'else' tag appended to it.
|
38
|
+
def before_appending( parsestate )
|
39
|
+
condtag = parsestate.node_stack.reverse.find do |node|
|
40
|
+
case node
|
41
|
+
|
42
|
+
# If there was a previous 'if' or 'unless', the else belongs to it. Also
|
43
|
+
# allow it to be appended to a 'comment' section so you can comment out an
|
44
|
+
# else clause
|
45
|
+
when Inversion::Template::IfTag,
|
46
|
+
Inversion::Template::UnlessTag,
|
47
|
+
Inversion::Template::CommentTag
|
48
|
+
break node
|
49
|
+
|
50
|
+
# If it's some other kind of container, it's an error
|
51
|
+
when Inversion::Template::ContainerTag
|
52
|
+
raise Inversion::ParseError, "'%s' tags can't have '%s' clauses" %
|
53
|
+
[ node.tagname.downcase, self.tagname.downcase ]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# If there wasn't a valid container, it's an error too
|
58
|
+
raise Inversion::ParseError, "orphaned '%s' tag" % [ self.tagname.downcase ] unless condtag
|
59
|
+
end
|
60
|
+
|
61
|
+
end # class Inversion::Template::ElseTag
|
62
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template/attrtag'
|
5
|
+
require 'inversion/template/iftag'
|
6
|
+
require 'inversion/template/commenttag'
|
7
|
+
|
8
|
+
|
9
|
+
# Inversion 'elsif' tag.
|
10
|
+
#
|
11
|
+
# This tag adds a conditional logical switch to an IfTag. If the IfTag's condition was false,
|
12
|
+
# but the attribute or methodchain of the elsif is true, start rendering.
|
13
|
+
#
|
14
|
+
# == Syntax
|
15
|
+
#
|
16
|
+
# <?if attr ?>
|
17
|
+
# ...
|
18
|
+
# <?elsif attr ?>
|
19
|
+
# ...
|
20
|
+
# <?elsif attr.methodchain ?>
|
21
|
+
# ...
|
22
|
+
# <?end?>
|
23
|
+
#
|
24
|
+
class Inversion::Template::ElsifTag < Inversion::Template::AttrTag
|
25
|
+
include Inversion::Loggable
|
26
|
+
|
27
|
+
# Inherits AttrTag's tag patterns
|
28
|
+
|
29
|
+
### Parsing callback -- check to be sure the node tree can have an
|
30
|
+
### 'elsif' tag appended to it (i.e., it has an opening 'if' tag).
|
31
|
+
def before_appending( parsestate )
|
32
|
+
condtag = parsestate.node_stack.reverse.find do |node|
|
33
|
+
case node
|
34
|
+
when Inversion::Template::IfTag,
|
35
|
+
Inversion::Template::CommentTag
|
36
|
+
break node
|
37
|
+
when Inversion::Template::ContainerTag
|
38
|
+
raise Inversion::ParseError, "'%s' tags can't have '%s' clauses" %
|
39
|
+
[ node.tagname.downcase, self.tagname.downcase ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
unless condtag
|
44
|
+
raise Inversion::ParseError, "orphaned '%s' tag" % [ self.tagname.downcase ]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class Inversion::Template::ElsifTag
|
49
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion' unless defined?( Inversion )
|
5
|
+
require 'inversion/template' unless defined?( Inversion::Template )
|
6
|
+
require 'inversion/template/tag' unless defined?( Inversion::Template::Tag )
|
7
|
+
|
8
|
+
# Closing tag class
|
9
|
+
class Inversion::Template::EndTag < Inversion::Template::Tag
|
10
|
+
include Inversion::Loggable
|
11
|
+
|
12
|
+
|
13
|
+
### Overridden to provide a default +body+.
|
14
|
+
def initialize( body='', linenum=nil, colnum=nil )
|
15
|
+
super
|
16
|
+
@opener = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
######
|
21
|
+
public
|
22
|
+
######
|
23
|
+
|
24
|
+
# The ContainerTag that this end tag closes
|
25
|
+
attr_reader :opener
|
26
|
+
|
27
|
+
|
28
|
+
### Parser callback -- close the given +state+'s currently-open container node.
|
29
|
+
def before_appending( state )
|
30
|
+
@opener = state.pop
|
31
|
+
self.log.debug "End tag for %s at %s" % [ @opener.tagname, @opener.location ]
|
32
|
+
|
33
|
+
# If the end tag has a body, it should match the container that's just
|
34
|
+
# been popped.
|
35
|
+
if self.body &&
|
36
|
+
!self.body.empty? &&
|
37
|
+
self.body.downcase != @opener.tagname.downcase
|
38
|
+
|
39
|
+
raise Inversion::ParseError, "unbalanced end: expected %p, got %p" % [
|
40
|
+
@opener.tagname.downcase,
|
41
|
+
self.body.downcase,
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
### Render the tag as the body of a comment, suitable for template debugging.
|
50
|
+
def as_comment_body
|
51
|
+
return "End of %s" % [ self.opener.as_comment_body ]
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class Inversion::Template::EndTag
|
55
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template/attrtag'
|
5
|
+
|
6
|
+
# Inversion escaping tag.
|
7
|
+
#
|
8
|
+
# This tag is a derivative of the 'attr' tag that escapes the results of its method call
|
9
|
+
# via the format specified in the template config option 'escape_format'.
|
10
|
+
#
|
11
|
+
# == Syntax
|
12
|
+
#
|
13
|
+
# <?escape foo.bar ?>
|
14
|
+
# <?escape "Got <%d> items at <$%0.2f>" % [ line_item.count, line_item.price ] ?>
|
15
|
+
#
|
16
|
+
class Inversion::Template::EscapeTag < Inversion::Template::AttrTag
|
17
|
+
include Inversion::Escaping
|
18
|
+
|
19
|
+
### Render the method chains against the attributes of the specified +render_state+
|
20
|
+
### and return them.
|
21
|
+
def render( render_state )
|
22
|
+
result = self.escape( super, render_state )
|
23
|
+
end
|
24
|
+
|
25
|
+
end # class Inversion::Template::EscapeTag
|
26
|
+
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'inversion/template/codetag'
|
5
|
+
require 'inversion/template/containertag'
|
6
|
+
|
7
|
+
|
8
|
+
# Inversion 'for' tag.
|
9
|
+
#
|
10
|
+
# Iteration tag for outputting a template part for each member of a collection (i.e.,
|
11
|
+
# an object that is Enumerable).
|
12
|
+
#
|
13
|
+
# == Syntax
|
14
|
+
#
|
15
|
+
# <?for var in attribute ?>
|
16
|
+
# <?for var in attribute.methodchain ?>
|
17
|
+
# <?for var1, var2 in attribute.methodchain ?>
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# == Examples
|
21
|
+
#
|
22
|
+
# <?for employee in company.employees ?>
|
23
|
+
#
|
24
|
+
# Hey <?call employee.name ?>! You're fired!
|
25
|
+
#
|
26
|
+
# <?end ?>
|
27
|
+
#
|
28
|
+
class Inversion::Template::ForTag < Inversion::Template::CodeTag
|
29
|
+
include Inversion::Loggable,
|
30
|
+
Inversion::Template::ContainerTag
|
31
|
+
|
32
|
+
# <?for var in attribute ?>
|
33
|
+
# <?for var in attribute.methodchain ?>
|
34
|
+
tag_pattern 'kw sp $(ident) sp $(kw) sp $(ident) $( .* )' do |tag, match|
|
35
|
+
raise Inversion::ParseError, "invalid keyword: expected 'in', got %p for %p" %
|
36
|
+
[ match.string(2), tag.body ] unless match.string(2) == 'in'
|
37
|
+
|
38
|
+
tag.block_args << match.string( 1 ).untaint.to_sym
|
39
|
+
tag.identifiers << match.string( 3 ).untaint.to_sym
|
40
|
+
|
41
|
+
tag.enumerator = match.string( 3 )
|
42
|
+
tag.enumerator << match.string( 4 ) if match.string( 4 )
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# <?for var1, var2, var3 in attribute ?>
|
47
|
+
# <?for var1, var2, var3 in attribute.methodchain ?>
|
48
|
+
tag_pattern 'kw sp $(ident (comma sp? ident)+) sp $(kw) sp $(ident) $( .* )' do |tag, match|
|
49
|
+
raise Inversion::ParseError, "invalid keyword: expected 'in', got %p for %p" %
|
50
|
+
[ match.string(2), tag.body ] unless match.string(2) == 'in'
|
51
|
+
|
52
|
+
tag.block_args += match.string( 1 ).untaint.split(/,\s?/).map( &:to_sym )
|
53
|
+
tag.identifiers << match.string( 3 ).untaint.to_sym
|
54
|
+
|
55
|
+
tag.enumerator = match.string( 3 )
|
56
|
+
tag.enumerator << match.string( 4 ) if match.string( 4 )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
### Create a new ForTag with the specified +body+.
|
62
|
+
def initialize( body, linenum=nil, colnum=nil )
|
63
|
+
@block_args = []
|
64
|
+
@enumerator = nil
|
65
|
+
|
66
|
+
super( 'for ' + body, linenum, colnum )
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
######
|
71
|
+
public
|
72
|
+
######
|
73
|
+
|
74
|
+
# The array of attribute names that will be assigned to the rendering scope
|
75
|
+
# by the block for each iteration
|
76
|
+
attr_accessor :block_args
|
77
|
+
|
78
|
+
# The attribute or methodchain that yields the enumerable object
|
79
|
+
attr_accessor :enumerator
|
80
|
+
|
81
|
+
|
82
|
+
### Iterate over the enumerator in +state+ and render the tag's
|
83
|
+
### contents for each iteration.
|
84
|
+
def render( state )
|
85
|
+
result = []
|
86
|
+
lvalue = state.eval( self.enumerator ) or return nil
|
87
|
+
|
88
|
+
self.log.debug "Rendering %p via block args: %p" % [ lvalue, self.block_args ]
|
89
|
+
|
90
|
+
lvalue.each do |*args|
|
91
|
+
# Turn the block arguments into an overrides hash by zipping up
|
92
|
+
# the arguments names and values
|
93
|
+
overrides = Hash[ self.block_args.zip(args) ]
|
94
|
+
|
95
|
+
# Overlay the block args from the 'for' over the template attributes and render
|
96
|
+
# each subnode
|
97
|
+
state.with_attributes( overrides ) do
|
98
|
+
self.subnodes.each do |node|
|
99
|
+
result << node.render( state )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
return result.join
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
### Render the tag as the body of a comment, suitable for template debugging.
|
109
|
+
def as_comment_body
|
110
|
+
comment = "%s: { %s IN template.%s }" % [
|
111
|
+
self.tagname,
|
112
|
+
self.block_args.join(', '),
|
113
|
+
self.enumerator
|
114
|
+
]
|
115
|
+
|
116
|
+
return comment
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class Inversion::Template::ForTag
|
120
|
+
|