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,28 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'pp'
5
+ require 'inversion/template/calltag'
6
+
7
+ # Inversion object inspection tag.
8
+ #
9
+ # This tag dumps the result of the attribute or method chain.
10
+ #
11
+ # == Syntax
12
+ #
13
+ # <?pp foo.bar ?>
14
+ #
15
+ class Inversion::Template::PpTag < Inversion::Template::CallTag
16
+ include Inversion::Escaping
17
+
18
+ ### Render the method chains against the attributes of the specified +render_state+
19
+ ### and return them.
20
+ def render( render_state )
21
+ raw = super
22
+ buf = ''
23
+ PP.pp( raw, buf )
24
+ return self.escape( buf.chomp, render_state )
25
+ end
26
+
27
+ end # class Inversion::Template::PpTag
28
+
@@ -0,0 +1,72 @@
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
+ # Inversion publish tag.
8
+ #
9
+ # The publish tag exports one or more subnodes to enclosing templates.
10
+ #
11
+ # == Syntax
12
+ #
13
+ # <!-- Outer template -->
14
+ # <html>
15
+ # <head>
16
+ # <?subscribe headers ?>
17
+ # </head>
18
+ # <body><?attr body ?></body>
19
+ # </html>
20
+ #
21
+ # <!-- In the body template, add a stylesheet link to the outer
22
+ # template's <head> -->
23
+ # <?publish headers ?>
24
+ # <link rel="stylesheet" ... />
25
+ # <?end ?>
26
+ # <div>(page content)</div>
27
+ #
28
+ class Inversion::Template::PublishTag < Inversion::Template::Tag
29
+ include Inversion::Template::ContainerTag
30
+
31
+
32
+ ### Create a new PublishTag with the given +body+.
33
+ def initialize( body, line=nil, column=nil )
34
+ super
35
+
36
+ key = self.body[ /^([a-z]\w+)$/ ] or
37
+ raise Inversion::ParseError,
38
+ "malformed key: expected simple identifier, got %p" % [ self.body ]
39
+ @key = key.to_sym
40
+ end
41
+
42
+
43
+ ######
44
+ public
45
+ ######
46
+
47
+ # The name of the key the nodes will be published under
48
+ attr_reader :key
49
+
50
+
51
+ ### Render the published subnodes in the context of the given +renderstate+, but
52
+ ### save them for publication after the render is done.
53
+ def render( renderstate )
54
+ self.log.debug "Publishing %d nodes as %s" % [ self.subnodes.length, self.key ]
55
+ rendered_nodes = []
56
+ renderstate.with_destination( rendered_nodes ) do
57
+ self.render_subnodes( renderstate )
58
+ end
59
+
60
+ renderstate.publish( self.key, *rendered_nodes ) unless rendered_nodes.empty?
61
+
62
+ return nil
63
+ end
64
+
65
+
66
+ ### Render the tag as the body of a comment, suitable for template debugging.
67
+ def as_comment_body
68
+ return "Published %d nodes as %s" % [ self.subnodes.length, self.key ]
69
+ end
70
+
71
+ end # class Inversion::Template::PublishTag
72
+
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'inversion/template/tag'
5
+
6
+ # Inversion subscription tag.
7
+ #
8
+ # The subscribe tag places one or more published nodes from subtemplates.
9
+ #
10
+ # == Syntax
11
+ #
12
+ # <!-- Outer template -->
13
+ # <html>
14
+ # <head>
15
+ # <title><?subscribe title || Untitled ?></title>
16
+ # <?subscribe headers ?>
17
+ # </head>
18
+ # <body><?attr body ?></body>
19
+ # </html>
20
+ #
21
+ # <!-- In the body template, add a stylesheet link to the outer
22
+ # template's <head> -->
23
+ # <?publish headers ?>
24
+ # <link rel="stylesheet" ... />
25
+ # <?end ?>
26
+ # <div>(page content)</div>
27
+ #
28
+ class Inversion::Template::SubscribeTag < Inversion::Template::Tag
29
+
30
+ ### Create a new SubscribeTag with the given +body+.
31
+ def initialize( body, line=nil, column=nil )
32
+ super
33
+
34
+ unless self.body =~ /^([a-z]\w+)(?:\s*\|\|\s*(.+))?$/
35
+ raise Inversion::ParseError,
36
+ "malformed subscribe: %p" % [ self.body ]
37
+ end
38
+
39
+ key, default = $1, $2
40
+
41
+ @key = key.to_sym
42
+ @content = []
43
+ @default = default
44
+ end
45
+
46
+
47
+ ######
48
+ public
49
+ ######
50
+
51
+ # The name of the key the nodes will be published under
52
+ attr_reader :key
53
+
54
+
55
+ ### Tell the +renderstate+ that this tag is interested in nodes that are published with
56
+ ### its key.
57
+ def before_rendering( renderstate )
58
+ renderstate.subscribe( self.key, self )
59
+ end
60
+
61
+
62
+ ### Return the subscribe node itself to act as a placeholder for subscribed nodes.
63
+ def render( renderstate )
64
+ return self
65
+ end
66
+
67
+
68
+ ### Pub/sub callback. Called from the RenderState when a PublishTag publishes +nodes+
69
+ ### with the same key as the current tag.
70
+ def publish( *nodes )
71
+ @content.push( *nodes )
72
+ end
73
+
74
+
75
+ ### Stringify and join all of the published nodes for this subscription and return them
76
+ ### as a String.
77
+ def to_s
78
+ if @content.empty?
79
+ self.log.debug "Nothing published with the %p key, defaulting to %p" %
80
+ [ self.key, @default ]
81
+ return @default.to_s
82
+ else
83
+ return @content.map( &:to_s ).join( '' )
84
+ end
85
+ end
86
+
87
+ end # class Inversion::Template::SubscribeTag
88
+
@@ -0,0 +1,150 @@
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
+ require 'inversion/template/node'
8
+ require 'inversion/mixins'
9
+
10
+ # Inversion template tag node base class. Represents a directive in a template
11
+ # that defines behavior and/or state.
12
+ #
13
+ # This class supports the RubyGems plugin API: to provide one or more Inversion tags
14
+ # in a gem of your own, put them into a directory named 'inversion/template' and
15
+ # name the files <tt><tagname>tag.rb</tt> and the classes <tagname.capitalize>Tag.
16
+ class Inversion::Template::Tag < Inversion::Template::Node
17
+ include Inversion::Loggable,
18
+ Inversion::AbstractClass
19
+
20
+ # The glob pattern for matching template tag plugins
21
+ TAG_PLUGIN_PATTERN = 'inversion/template/*tag.rb'
22
+
23
+
24
+ ########################################################################
25
+ ### C L A S S M E T H O D S
26
+ ########################################################################
27
+
28
+ # The hash of loaded tag types
29
+ @types = nil
30
+
31
+ # Derivatives of this class
32
+ @derivatives = []
33
+
34
+ class << self
35
+ attr_reader :types, :derivatives
36
+ end
37
+
38
+
39
+ ### Inheritance hook -- keep track of loaded derivatives.
40
+ def self::inherited( subclass )
41
+ # Inversion.log.debug "%p inherited from %p" % [ subclass, self ]
42
+ Inversion::Template::Tag.derivatives << subclass
43
+ super
44
+ end
45
+
46
+
47
+ ### Return a Hash of all loaded tag types, loading them if they haven't been loaded already.
48
+ def self::types
49
+ self.load_all unless @types
50
+ return @types
51
+ end
52
+
53
+
54
+ ### Load all available template tags and return them as a Hash keyed by their name.
55
+ def self::load_all
56
+ tags = {}
57
+
58
+ Gem.find_files( TAG_PLUGIN_PATTERN ).each do |tagfile|
59
+ tagname = tagfile[ %r{/(\w+)tag\.rb$}, 1 ].untaint
60
+
61
+ Inversion.log.debug "Loading tag type %p from %p" % [ tagname, tagfile ]
62
+ self.load( tagfile )
63
+
64
+ # Inversion.log.debug "Looking for class for %p tag" % [ tagname ]
65
+ tagclass = self.derivatives.find do |derivclass|
66
+ if derivclass.name.nil? || derivclass.name.empty?
67
+ # Inversion.log.debug " skipping anonymous class %p" % [ derivclass ]
68
+ nil
69
+ elsif !derivclass.respond_to?( :new )
70
+ # Inversion.log.debug " skipping abstract class %p" % [ derivclass ]
71
+ nil
72
+ else
73
+ derivclass.name.downcase =~ /\b#{tagname}tag$/
74
+ end
75
+ end
76
+
77
+ unless tagclass
78
+ Inversion.log.debug " no class found for %p tag" % [ tagname ]
79
+ next
80
+ end
81
+
82
+ Inversion.log.debug " found: %p" % [ tagclass ]
83
+ tags[ tagname.to_sym ] = tagclass
84
+ end
85
+
86
+ @types ||= {}
87
+ @types.merge!( tags )
88
+
89
+ return @types
90
+ end
91
+
92
+
93
+ ### Safely load the specified +tagfile+.
94
+ def self::load( tagfile )
95
+ require( tagfile )
96
+ rescue => err
97
+ Inversion.log.error "%s while loading tag plugin %p: %s" %
98
+ [ err.class.name, tagfile, err.message ]
99
+ Inversion.log.debug " " + err.backtrace.join( "\n " )
100
+ return false
101
+ end
102
+
103
+
104
+ ### Create a new Inversion::Template::Tag from the specified +tagname+ and +body+.
105
+ def self::create( tagname, body, linenum=nil, colnum=nil )
106
+ tagname =~ /^(\w+)$/i or raise ArgumentError, "invalid tag name %p" % [ tagname ]
107
+ tagtype = $1.downcase.untaint
108
+
109
+ unless tagclass = self.types[ tagtype.to_sym ]
110
+ Inversion.log.warn "Unknown tag type %p; registered: %p" %
111
+ [ tagtype, self.types.keys ]
112
+ return nil
113
+ end
114
+
115
+ return tagclass.new( body, linenum, colnum )
116
+ end
117
+
118
+
119
+ ########################################################################
120
+ ### I N S T A N C E M E T H O D S
121
+ ########################################################################
122
+
123
+ ### Create a new Inversion::Template::Tag with the specified +body+.
124
+ def initialize( body, linenum=nil, colnum=nil )
125
+ super
126
+ @body = body.to_s.strip
127
+ end
128
+
129
+
130
+ ######
131
+ public
132
+ ######
133
+
134
+ # the body of the tag
135
+ attr_reader :body
136
+
137
+
138
+ ### Render the tag as the body of a comment, suitable for template debugging.
139
+ def as_comment_body
140
+ return "%s %s at %s" % [ self.tagname, self.body.to_s.dump, self.location ]
141
+ end
142
+
143
+
144
+ ### Return the human-readable name of the tag class
145
+ def tagname
146
+ return self.class.name.sub(/Tag$/, '').sub( /^.*::/, '' )
147
+ end
148
+
149
+ end # class Inversion::Template::Tag
150
+
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'inversion/template/node'
5
+
6
+ # Inversion text node class -- static content in templates between tags are contained
7
+ # in a text node.
8
+ class Inversion::Template::TextNode < Inversion::Template::Node
9
+ include Inversion::Loggable
10
+
11
+ ### Create a new TextNode with the specified +source+.
12
+ def initialize( body, linenum=nil, colnum=nil )
13
+ @body = body
14
+ super
15
+ end
16
+
17
+
18
+ ######
19
+ public
20
+ ######
21
+
22
+ # The node body
23
+ attr_reader :body
24
+
25
+
26
+ ### Render the node.
27
+ def render( renderstate )
28
+ body = self.body.dup
29
+ body.sub!( /\A\r?\n/, '' ) if renderstate && renderstate.options[:strip_tag_lines]
30
+
31
+ return body
32
+ end
33
+
34
+
35
+ ### Render the text node as the body of a comment.
36
+ def as_comment_body
37
+ comment_body = self.body[0,40].dump
38
+ comment_body[-1,0] = '...' unless comment_body == self.body.dump
39
+ return "Text (%d bytes): %s" % [ self.body.length, comment_body ]
40
+ end
41
+
42
+ end # class Inversion::Template::TextNode
43
+
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'inversion/mixins'
5
+ require 'inversion/template/attrtag'
6
+ require 'inversion/template/containertag'
7
+ require 'inversion/template/conditionaltag'
8
+ require 'inversion/template/elsetag'
9
+
10
+
11
+ # Inversion 'unless' tag.
12
+ #
13
+ # This tag causes a section of the template to be rendered only if its methodchain or attribute
14
+ # is a *false* value.
15
+ #
16
+ # == Syntax
17
+ #
18
+ # <?unless attr ?>...<?end?>
19
+ # <?unless obj.method ?>...<?end?>
20
+ #
21
+ class Inversion::Template::UnlessTag < Inversion::Template::AttrTag
22
+ include Inversion::Loggable,
23
+ Inversion::Template::ContainerTag,
24
+ Inversion::Template::ConditionalTag
25
+
26
+ # Inherits AttrTag's tag patterns
27
+
28
+ ### Render the tag's contents if the condition is true, or any else or elsif sections
29
+ ### if the condition isn't true.
30
+ def render( state )
31
+ self.enable_rendering unless super
32
+ self.render_subnodes( state )
33
+
34
+ return state
35
+ end
36
+
37
+ ### Render the tag's subnodes according to the tag's logical state.
38
+ def render_subnodes( renderstate )
39
+ self.log.debug "Rendering subnodes. Rendering initially %s" %
40
+ [ self.rendering_enabled? ? "enabled" : "disabled" ]
41
+
42
+ # walk the subtree, modifying the logic flags for else and elsif tags,
43
+ # and rendering nodes if rendering is enabled
44
+ self.subnodes.each do |node|
45
+ if node.is_a?( Inversion::Template::ElseTag )
46
+ self.log.debug " logic switch: %p..." % [ node ]
47
+ if !self.rendering_was_enabled?
48
+ self.enable_rendering
49
+ else
50
+ self.disable_rendering
51
+ end
52
+
53
+ else
54
+ renderstate << node if self.rendering_enabled?
55
+ end
56
+ end
57
+ end
58
+
59
+ end # class Inversion::Template::UnlessTag
60
+
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'uri'
5
+ require 'inversion/template/attrtag'
6
+
7
+ # Inversion URL encoding tag.
8
+ #
9
+ # This tag is a derivative of the 'attr' tag that encodes the results of its method call
10
+ # according to RFC 3986.
11
+ #
12
+ # == Syntax
13
+ #
14
+ # <?uriencode foo.bar ?>
15
+ #
16
+ class Inversion::Template::UriencodeTag < Inversion::Template::AttrTag
17
+
18
+ # Unreserved characters from section 2.3 of RFC 3986
19
+ # ALPHA / DIGIT / "-" / "." / "_" / "~"
20
+ DEFAULT_ENCODED_CHARACTERS = /[^\w\-\.~]/
21
+
22
+ ### Render the method chains against the attributes of the specified +render_state+
23
+ ### and return them.
24
+ def render( render_state )
25
+ raw = super
26
+ return URI.encode( raw.to_s, DEFAULT_ENCODED_CHARACTERS )
27
+ end
28
+
29
+ end # class Inversion::Template::UriencodeTag
30
+