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.
Files changed (78) hide show
  1. data.tar.gz.sig +2 -0
  2. data/.gemtest +0 -0
  3. data/ChangeLog +836 -0
  4. data/History.md +4 -0
  5. data/Manifest.txt +74 -0
  6. data/README.rdoc +171 -0
  7. data/Rakefile +55 -0
  8. data/bin/inversion +276 -0
  9. data/lib/inversion.rb +98 -0
  10. data/lib/inversion/exceptions.rb +21 -0
  11. data/lib/inversion/mixins.rb +236 -0
  12. data/lib/inversion/monkeypatches.rb +20 -0
  13. data/lib/inversion/renderstate.rb +337 -0
  14. data/lib/inversion/sinatra.rb +35 -0
  15. data/lib/inversion/template.rb +250 -0
  16. data/lib/inversion/template/attrtag.rb +120 -0
  17. data/lib/inversion/template/calltag.rb +16 -0
  18. data/lib/inversion/template/codetag.rb +164 -0
  19. data/lib/inversion/template/commenttag.rb +54 -0
  20. data/lib/inversion/template/conditionaltag.rb +49 -0
  21. data/lib/inversion/template/configtag.rb +60 -0
  22. data/lib/inversion/template/containertag.rb +45 -0
  23. data/lib/inversion/template/elsetag.rb +62 -0
  24. data/lib/inversion/template/elsiftag.rb +49 -0
  25. data/lib/inversion/template/endtag.rb +55 -0
  26. data/lib/inversion/template/escapetag.rb +26 -0
  27. data/lib/inversion/template/fortag.rb +120 -0
  28. data/lib/inversion/template/iftag.rb +69 -0
  29. data/lib/inversion/template/importtag.rb +70 -0
  30. data/lib/inversion/template/includetag.rb +51 -0
  31. data/lib/inversion/template/node.rb +102 -0
  32. data/lib/inversion/template/parser.rb +297 -0
  33. data/lib/inversion/template/pptag.rb +28 -0
  34. data/lib/inversion/template/publishtag.rb +72 -0
  35. data/lib/inversion/template/subscribetag.rb +88 -0
  36. data/lib/inversion/template/tag.rb +150 -0
  37. data/lib/inversion/template/textnode.rb +43 -0
  38. data/lib/inversion/template/unlesstag.rb +60 -0
  39. data/lib/inversion/template/uriencodetag.rb +30 -0
  40. data/lib/inversion/template/yieldtag.rb +51 -0
  41. data/lib/inversion/tilt.rb +82 -0
  42. data/lib/inversion/utils.rb +235 -0
  43. data/spec/data/sinatra/hello.inversion +1 -0
  44. data/spec/inversion/mixins_spec.rb +177 -0
  45. data/spec/inversion/monkeypatches_spec.rb +35 -0
  46. data/spec/inversion/renderstate_spec.rb +291 -0
  47. data/spec/inversion/sinatra_spec.rb +59 -0
  48. data/spec/inversion/template/attrtag_spec.rb +216 -0
  49. data/spec/inversion/template/calltag_spec.rb +30 -0
  50. data/spec/inversion/template/codetag_spec.rb +51 -0
  51. data/spec/inversion/template/commenttag_spec.rb +84 -0
  52. data/spec/inversion/template/configtag_spec.rb +105 -0
  53. data/spec/inversion/template/containertag_spec.rb +54 -0
  54. data/spec/inversion/template/elsetag_spec.rb +105 -0
  55. data/spec/inversion/template/elsiftag_spec.rb +87 -0
  56. data/spec/inversion/template/endtag_spec.rb +78 -0
  57. data/spec/inversion/template/escapetag_spec.rb +59 -0
  58. data/spec/inversion/template/fortag_spec.rb +98 -0
  59. data/spec/inversion/template/iftag_spec.rb +241 -0
  60. data/spec/inversion/template/importtag_spec.rb +106 -0
  61. data/spec/inversion/template/includetag_spec.rb +108 -0
  62. data/spec/inversion/template/node_spec.rb +81 -0
  63. data/spec/inversion/template/parser_spec.rb +170 -0
  64. data/spec/inversion/template/pptag_spec.rb +51 -0
  65. data/spec/inversion/template/publishtag_spec.rb +69 -0
  66. data/spec/inversion/template/subscribetag_spec.rb +60 -0
  67. data/spec/inversion/template/tag_spec.rb +97 -0
  68. data/spec/inversion/template/textnode_spec.rb +86 -0
  69. data/spec/inversion/template/unlesstag_spec.rb +84 -0
  70. data/spec/inversion/template/uriencodetag_spec.rb +49 -0
  71. data/spec/inversion/template/yieldtag_spec.rb +54 -0
  72. data/spec/inversion/template_spec.rb +269 -0
  73. data/spec/inversion/tilt_spec.rb +47 -0
  74. data/spec/inversion_spec.rb +95 -0
  75. data/spec/lib/constants.rb +9 -0
  76. data/spec/lib/helpers.rb +160 -0
  77. metadata +316 -0
  78. 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
+