arrow 1.0.7

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 (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. metadata +339 -0
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'arrow/exceptions'
4
+ require 'arrow/path'
5
+ require 'arrow/template'
6
+
7
+ # The Arrow::Template::Iterator class, instances of which can
8
+ # be used to provide an iteration context to nodes in an Arrow template.
9
+ #
10
+ # Lots of the ideas for this class were stolen/influenced in no small way by Hal
11
+ # Fulton's "super-iterator" post to the Ruby-talk ML [ruby-talk: 46337].
12
+ #
13
+ # == Examples
14
+ #
15
+ # ### Render the directive's bracketed nodes once for each item in the
16
+ # ### iterated content.
17
+ # def render_subnodes( attribute, template, scope )
18
+ # res = []
19
+ #
20
+ # iterator = Arrow::Template::Iterator.new( attribute )
21
+ # iterator.each {|iter,*blockArgs|
22
+ #
23
+ # # Process the nodes
24
+ # template.with_overridden_attributes( scope, 'iterator' => iter ) {|template|
25
+ # res << template.render( @subnodes, scope )
26
+ # }
27
+ # }
28
+ #
29
+ # return *res
30
+ # end
31
+ #
32
+ # == Authors
33
+ #
34
+ # * Michael Granger <ged@FaerieMUD.org>
35
+ #
36
+ # Please see the file LICENSE in the top-level directory for licensing details.
37
+ #
38
+ class Arrow::Template::Iterator < Arrow::Object
39
+ include Enumerable
40
+
41
+
42
+
43
+ #############################################################
44
+ ### I N S T A N C E M E T H O D S
45
+ #############################################################
46
+
47
+ ### Create a new Arrow::Template::Iterator object for the given +items+.
48
+ def initialize( *items )
49
+ if items.length == 1 && items[0].is_a?( Enumerable )
50
+ @items = items[0]
51
+ else
52
+ @items = items
53
+ end
54
+
55
+ @iteration = nil
56
+ @lastItem = nil
57
+ @item = nil
58
+ @nextItem = nil
59
+ @iterating = false
60
+ @skipped = false
61
+ @marker = nil
62
+ end
63
+
64
+
65
+ ######
66
+ public
67
+ ######
68
+
69
+ # The list of items in this iteration
70
+ attr_accessor :items
71
+
72
+ # The index of the current iteration
73
+ attr_accessor :iteration
74
+
75
+ # The item previous to the currently iterated one. If this is the first
76
+ # iteration, this will be +nil+.
77
+ attr_reader :lastItem
78
+
79
+ # The item which succeeds the currently iterated one. If this is the
80
+ # last iteration, this will be +nil+.
81
+ attr_reader :nextItem
82
+
83
+
84
+ ### The primary iteration interface.
85
+ def each
86
+ items = @items.dup
87
+ @items = @items.entries
88
+ raise LocalJumpError, "no block given" unless block_given?
89
+
90
+ #self.log.debug "Iterating over @items = %p" % [ @items ]
91
+
92
+ # Save this point so #restart can jump back here later. This is in a
93
+ # loop because it needs to be remade after it's used the first time.
94
+ until @marker
95
+ @marker = callcc {|cc| cc}
96
+ end
97
+ @iterating = true
98
+ @iteration = 0
99
+
100
+ # Mark the outer loop for #break
101
+ catch( :break ) {
102
+ until @iteration >= @items.length
103
+
104
+ # Catch a skip with the number of items to skip. Unskipped
105
+ # iterations "skip" 0 items.
106
+ n = catch( :skip ) {
107
+ @lastItem = self.first? ? nil : @items[ @iteration - 1 ]
108
+ @item = @items[ @iteration ]
109
+ @nextItem = self.last? ? nil : @items[ @iteration + 1 ]
110
+
111
+ if @item.is_a?( Array )
112
+ yield( self, *@item )
113
+ else
114
+ yield( self, @item )
115
+ end
116
+
117
+ 0
118
+ }
119
+
120
+ # Set the skipped flag for next iteration if we're skipping
121
+ @skipped = n.nonzero?
122
+ @iteration += n + 1
123
+ end
124
+ }
125
+
126
+ #self.log.debug "Returning from Iterator#each"
127
+
128
+ return @items
129
+ ensure
130
+ @items = items
131
+ @iteration = nil
132
+ @lastItem = nil
133
+ @item = nil
134
+ @nextItem = nil
135
+ @iterating = false
136
+ @skipped = false
137
+ @marker = nil
138
+ end
139
+
140
+
141
+ ### Cause the next +n+ items to be skipped
142
+ def skip( n=1 )
143
+ # Jump back into #each with the number of iterations to skip
144
+ throw( :skip, n ) if @iterating
145
+ end
146
+
147
+
148
+ ### Redo the current iteration
149
+ def redo
150
+ throw( :skip, -1 ) if @iterating
151
+ end
152
+
153
+
154
+ ### Cause iteration to immediately terminate, ala the 'break' keyword
155
+ def break
156
+ # Jump back into the outer loop of #each
157
+ throw( :break ) if @iterating
158
+ end
159
+
160
+
161
+ ### Cause iteration to begin over again
162
+ def restart
163
+ # Call back into the continuation that was saved at the beginning of
164
+ # #each
165
+ @marker.call if @iterating
166
+ end
167
+
168
+
169
+ ### Returns +true+ if the last iteration skipped one or more items.
170
+ def skipped?
171
+ @skipped
172
+ end
173
+
174
+
175
+ ### Returns +true+ if the current iteration is the first one.
176
+ def first?
177
+ return @iteration == 0
178
+ end
179
+
180
+
181
+ ### Returns either "even" if the iteration is in even-numbered iteration, or "odd".
182
+ def even_or_odd
183
+ return self.odd? ? "odd" : "even"
184
+ end
185
+
186
+
187
+ ### Returns +true+ if the current iteration is an odd-numbered
188
+ ### iteration.
189
+ def odd?
190
+ return @iterating && ( @iteration % 2 ).nonzero?
191
+ end
192
+
193
+
194
+ ### Return +true+ if the current iteration is an even-numbered
195
+ ### iteration.
196
+ def even?
197
+ return !self.odd?
198
+ end
199
+
200
+
201
+ ### Returns +true+ if the current iteration is the last one.
202
+ def last?
203
+ return @iteration == @items.length - 1
204
+ end
205
+
206
+
207
+ end # class Arrow::Template::Iterator
208
+
@@ -0,0 +1,734 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pluginfactory'
4
+
5
+ require 'arrow/path'
6
+ require 'arrow/mixins'
7
+ require 'arrow/template'
8
+
9
+ class Arrow::Template
10
+
11
+ ### The abstract base node class.
12
+ class Node < Arrow::Object
13
+ include Arrow::HTMLUtilities
14
+
15
+ # SVN Revision
16
+ SVNRev = %q$Rev$
17
+
18
+ # SVN Id
19
+ SVNId = %q$Id$
20
+
21
+
22
+ #############################################################
23
+ ### I N S T A N C E M E T H O D S
24
+ #############################################################
25
+
26
+ ### Provide initialization for all derivative Arrow::Template::Node
27
+ ### objects.
28
+ def initialize( type='generic' ) # :notnew:
29
+ @type = type
30
+ super()
31
+ end
32
+
33
+
34
+ ######
35
+ public
36
+ ######
37
+
38
+ # The type of the node
39
+ attr_reader :type
40
+
41
+
42
+ ### Returns +true+ for nodes which generate output themselves (as
43
+ ### opposed to ones which generate output through subnodes). This is
44
+ ### used for eliding blank lines from the node tree.
45
+ def is_rendering_node?
46
+ false
47
+ end
48
+ alias_method :rendering?, :is_rendering_node?
49
+
50
+
51
+ ### Install the node object into the given +template+ object.
52
+ def add_to_template( template )
53
+ template.install_node( self )
54
+ end
55
+
56
+
57
+ ### Return the node as a String.
58
+ def to_s
59
+ ""
60
+ end
61
+
62
+
63
+ ### Return the receiver and any subnodes as a flattened Array of nodes.
64
+ def to_a
65
+ [self]
66
+ end
67
+
68
+
69
+ ### Render the node to text.
70
+ def render( template, scope )
71
+ return [ self.to_s ]
72
+ end
73
+
74
+
75
+ ### Return a human-readable version of the Node object suitable for
76
+ ### debugging messages.
77
+ def inspect
78
+ "<%s Node>" % @type.capitalize
79
+ end
80
+
81
+
82
+ ### Return an HTML fragment that can be used to represent the node
83
+ ### symbolically in a web-based introspection interface.
84
+ def to_html
85
+ content = nil
86
+
87
+ if block_given?
88
+ content = yield
89
+ else
90
+ content = ""
91
+ end
92
+
93
+ nodeclass = self.css_class
94
+
95
+ %q{<div class="node %s"><div class="node-head %s-head">%s</div>
96
+ <div class="node-body %s-body">%s</div></div>} % [
97
+ nodeclass, nodeclass,
98
+ self.class.name.sub(/Arrow::Template::/, ''),
99
+ nodeclass,
100
+ content,
101
+ ]
102
+ end
103
+
104
+
105
+ #########
106
+ protected
107
+ #########
108
+
109
+ ### Return the HTML element class attribute that corresponds to this node.
110
+ def css_class
111
+ nodeclass = self.class.name.
112
+ sub(/Arrow::Template::/, '').
113
+ gsub( /::/, '-' ).
114
+ gsub( /([a-z])([A-Z])/, "\\1-\\2" ).
115
+ gsub( /[^-\w]+/, '' ).
116
+ downcase
117
+ nodeclass << "-node" unless /-node$/.match( nodeclass )
118
+
119
+ return nodeclass
120
+ end
121
+
122
+
123
+ end # class Node
124
+
125
+
126
+ ### The plain-content node object class. Instances of this class are
127
+ ### nodes in a syntax tree which represents the textual contents of an
128
+ ### Arrow::Template object.
129
+ class TextNode < Arrow::Template::Node
130
+
131
+ # SVN Revision
132
+ SVNRev = %q$Rev$
133
+
134
+ # SVN Id
135
+ SVNId = %q$Id$
136
+
137
+
138
+ #############################################################
139
+ ### I N S T A N C E M E T H O D S
140
+ #############################################################
141
+
142
+ ### Create a new Arrow::Template::TextNode object with the given +body+.
143
+ def initialize( body, type="text" )
144
+ @body = body
145
+ super( type )
146
+ end
147
+
148
+ ######
149
+ public
150
+ ######
151
+
152
+ # The node body
153
+ attr_accessor :body
154
+
155
+
156
+ ### Match operator -- if +obj+ is a Regexp, use it as a pattern to match
157
+ ### against the node's body. If +obj+ is a String, look for it in the
158
+ ### node's body, similar to String#index. Returns the position the match
159
+ ### starts, or nil if there is no match. Otherwise, invokes obj#=~,
160
+ ### passing the node's body as an argument.
161
+ def =~( obj )
162
+ case obj
163
+ when Regexp
164
+ obj.match( self.body )
165
+ when String
166
+ self.body.index( obj )
167
+ else
168
+ obj.=~( self.body )
169
+ end
170
+ end
171
+
172
+
173
+ ### Returns +true+ for nodes which generate output themselves (as
174
+ ### opposed to ones which generate output through subnodes). This is
175
+ ### used for eliding blank lines from the node tree.
176
+ def is_rendering_node?
177
+ true
178
+ end
179
+
180
+
181
+ ### Return the node as a String.
182
+ def to_s
183
+ self.body.to_s
184
+ end
185
+
186
+
187
+ ### Return an HTML fragment that can be used to represent the node
188
+ ### symbolically in a web-based introspection interface.
189
+ def to_html
190
+ super { self.escape_html(@body) }
191
+ end
192
+
193
+
194
+ ### Return a human-readable version of the object suitable for debugging
195
+ ### messages.
196
+ def inspect
197
+ %Q{<%s Node: %s>} % [ @type.capitalize, @body.inspect ]
198
+ end
199
+
200
+ end # class TextNode
201
+
202
+
203
+ ### A comment node object class. Instances of this class are nodes in a
204
+ ### syntax tree which represent the invisible markup useful for diagnosis or
205
+ ### debugging.
206
+ class CommentNode < Arrow::Template::TextNode
207
+
208
+ # SVN Revision
209
+ SVNRev = %q$Rev$
210
+
211
+ # SVN Id
212
+ SVNId = %q$Id$
213
+
214
+
215
+ #############################################################
216
+ ### I N S T A N C E M E T H O D S
217
+ #############################################################
218
+
219
+ ### Create a new Arrow::Template::TextNode object with the given +body+.
220
+ def initialize( body, type='comment' )
221
+ super
222
+ end
223
+
224
+ ######
225
+ public
226
+ ######
227
+
228
+ ### Render the comment in the context of the specified +template+ and
229
+ ### +scope+.
230
+ def render( template, scope )
231
+ [ template.render_comment( self.to_s ) ]
232
+ end
233
+
234
+
235
+ ### Return an HTML fragment that can be used to represent the node
236
+ ### symbolically in a web-based introspection interface.
237
+ def to_html
238
+ super { self.escape_html(self.to_s) }
239
+ end
240
+
241
+ end # class CommentNode
242
+
243
+
244
+ ### The abstract directive superclass. Instances of derivatives of this
245
+ ### class define template behaviour and content.
246
+ class Directive < Arrow::Template::Node
247
+ include PluginFactory
248
+
249
+ # SVN Revision
250
+ SVNRev = %q$Rev$
251
+
252
+ # SVN Id
253
+ SVNId = %q$Id$
254
+
255
+
256
+ #############################################################
257
+ ### C L A S S M E T H O D S
258
+ #############################################################
259
+
260
+ ### Return the list of subdirectories to search for template nodes.
261
+ def self::derivativeDirs
262
+ ["arrow/template"]
263
+ end
264
+
265
+
266
+ ### Factory method: overridden from PluginFactory.create to
267
+ ### pass the name into constructors for parsing context.
268
+ def self::create( tag, parser, state )
269
+ super( tag, tag, parser, state )
270
+ end
271
+
272
+
273
+ #############################################################
274
+ ### I N S T A N C E M E T H O D S
275
+ #############################################################
276
+
277
+ ### Initialize a new Directive with the given +type+ (the directive
278
+ ### name), +parser+ (Arrow::Template::Parser), and +state+
279
+ ### (Arrow::Template::Parser::State object).
280
+ def initialize( type, parser, state )
281
+ super( type )
282
+ self.parse_directive_contents( parser, state )
283
+ end
284
+
285
+
286
+ ######
287
+ public
288
+ ######
289
+
290
+ ### Render the directive as a String and return it.
291
+ def render( template, scope )
292
+ rary = []
293
+ rary << template.render_comment( self.inspect ) if
294
+ template._config[:debuggingComments]
295
+
296
+ return rary
297
+ end
298
+
299
+
300
+ ### Return a human-readable version of the object suitable for debugging
301
+ ### messages.
302
+ def inspect
303
+ %Q{<%s Directive>} % [ @type.capitalize ]
304
+ end
305
+
306
+
307
+ ### Return an HTML fragment that can be used to represent the node
308
+ ### symbolically in a web-based introspection interface.
309
+ def to_html
310
+
311
+ if block_given?
312
+ callback = Proc.new
313
+ super( &callback )
314
+ else
315
+ fields = instance_variables.sort.collect {|ivar|
316
+ val = instance_variable_get( ivar )
317
+ %q{<span class="ivar"><em>%s:</em> %s</span>} %
318
+ [ ivar, self.escape_html(val) ]
319
+ }
320
+
321
+ super { fields.join(", ") }
322
+ end
323
+ end
324
+
325
+
326
+ #########
327
+ protected
328
+ #########
329
+
330
+ ### Parse the contents of the directive. This is a no-op for this class;
331
+ ### it's here to allow delegation of this task to subclasses.
332
+ def parse_directive_contents( parser, state )
333
+ end
334
+
335
+
336
+ end # class Directive
337
+
338
+
339
+ ### The attribute directive superclass. Attribute directives are those that
340
+ ### present an exterior interface to the controlling system for
341
+ ### message-passing and content-injection (e.g., <?attr?>, <?set?>,
342
+ ### <?config?>, etc.)
343
+ class AttributeDirective < Arrow::Template::Directive
344
+
345
+ # SVN Revision
346
+ SVNRev = %q$Rev$
347
+
348
+ # SVN Id
349
+ SVNId = %q$Id$
350
+
351
+
352
+ #############################################################
353
+ ### C L A S S M E T H O D S
354
+ #############################################################
355
+
356
+ ### Returns +true+ for classes that support a prepended format. (e.g.,
357
+ ### <?call "%15s" % foo ?>).
358
+ def self::allows_format?
359
+ true
360
+ end
361
+
362
+
363
+ #############################################################
364
+ ### I N S T A N C E M E T H O D S
365
+ #############################################################
366
+
367
+ ### Initialize a new AttributeDirective with the given tag +name+,
368
+ ### template +parser+, and parser +state+.
369
+ def initialize( type, parser, state ) # :notnew:
370
+ @name = nil
371
+ @format = nil
372
+ @methodchain = nil
373
+ super
374
+ end
375
+
376
+
377
+ ######
378
+ public
379
+ ######
380
+
381
+ # The source code for the methodchain that will be used to render the
382
+ # attribute.
383
+ attr_accessor :methodchain
384
+
385
+ # The format string that was specified with the directive, if any
386
+ attr_accessor :format
387
+
388
+ # The name of the directive, which is used to associate it with a
389
+ # attribute in the template the node belongs to.
390
+ attr_reader :name
391
+
392
+
393
+ ### Returns +true+ for nodes which generate output themselves (as
394
+ ### opposed to ones which generate output through subnodes). This is
395
+ ### used for eliding blank lines from the node tree.
396
+ def is_rendering_node?
397
+ true
398
+ end
399
+
400
+
401
+ ### Try to pre-render any attributes which correspond to this node.
402
+ def before_rendering( template )
403
+ if attrib = template[ self.name ]
404
+ # self.log.debug " got %s attribute in #before_rendering for %p" %
405
+ # [ attrib.class.name, self.name ]
406
+
407
+ if attrib.respond_to?( :before_rendering )
408
+ # self.log.debug " pre-rendering attribute %p" % [attrib]
409
+ attrib.before_rendering( template )
410
+ elsif attrib.respond_to?( :each )
411
+ # self.log.debug " iterating over attribute %p" % [attrib]
412
+ attrib.each do |obj|
413
+ obj.before_rendering if obj.respond_to?( :before_rendering )
414
+ end
415
+ end
416
+ else
417
+ # No-op
418
+ # self.log.debug " no value for node %p in #before_rendering" %
419
+ # self.name
420
+ end
421
+ end
422
+
423
+
424
+ ### Render the directive node's contents as a String and return it.
425
+ def render( template, scope )
426
+ # self.log.debug "Rendering %p" % self
427
+ rary = super
428
+
429
+ rary.push( *(self.render_contents( template, scope )) )
430
+ return rary
431
+ end
432
+
433
+
434
+ ### Return a human-readable version of the object suitable for debugging
435
+ ### messages.
436
+ def inspect
437
+ %Q{<%s %s%s (Format: %p)>} % [
438
+ @type.capitalize,
439
+ @name,
440
+ @methodchain.strip.empty? ? "" : @methodchain,
441
+ @format,
442
+ ]
443
+ end
444
+
445
+
446
+ ### Return an HTML fragment that can be used to represent the node
447
+ ### symbolically in a web-based introspection interface.
448
+ def to_html
449
+ html = ''
450
+ if @format
451
+ html << %q{"%s" %% } % self.escape_html( @format )
452
+ end
453
+ html << %q{<strong>#%s</strong>} % @name
454
+ if @methodchain
455
+ html << self.escape_html( @methodchain )
456
+ end
457
+
458
+ if block_given?
459
+ html << " " << yield
460
+ end
461
+
462
+ super { html }
463
+ end
464
+
465
+
466
+ #########
467
+ protected
468
+ #########
469
+
470
+ ### Parse the contents of the directive, looking for an optional format
471
+ ### for tags like <?directive "%-15s" % foo ?>, then a required
472
+ ### identifier, then an optional methodchain attached to the identifier.
473
+ def parse_directive_contents( parser, state )
474
+ super
475
+
476
+ # Look for a format
477
+ if self.class.allows_format?
478
+ if fmt = parser.scan_for_quoted_string( state )
479
+ state.scanner.skip( /\s*%\s*/ ) or
480
+ raise Arrow::ParseError, "Format missing modulus operator?"
481
+ @format = fmt[1..-2]
482
+ #self.log.debug "Found format %p" % @format
483
+ else
484
+ #self.log.debug "No format string"
485
+ @format = nil
486
+ end
487
+ end
488
+
489
+ # Look for the identifier
490
+ @name = parser.scan_for_identifier( state ) or
491
+ raise Arrow::ParseError, "missing or malformed indentifier"
492
+ #self.log.debug "Set name of %s to %p" %
493
+ # [ self.class.name, @name ]
494
+
495
+ # Now pick up the methodchain if there is one
496
+ @methodchain = parser.scan_for_methodchain( state )
497
+
498
+ return true
499
+ end
500
+
501
+
502
+ ### Render the contents of the node
503
+ def render_contents( template, scope )
504
+ return self.call_methodchain( template, scope )
505
+ end
506
+
507
+
508
+ ### Build a Proc object that encapsulates the execution necessary to
509
+ ### render the directive.
510
+ def build_rendering_proc( template, scope )
511
+ return nil if self.format.nil? && self.methodchain.nil?
512
+
513
+ if self.format
514
+ code = %(Proc.new {|%s| "%s" %% %s%s}) %
515
+ [ self.name, self.format, self.name, self.methodchain ]
516
+ else
517
+ code = "Proc.new {|%s| %s%s}" %
518
+ [ self.name, self.name, self.methodchain ]
519
+ end
520
+ code.untaint
521
+
522
+ #self.log.debug "Rendering proc code is: %p" % code
523
+ desc = "[%s (%s): %s]" %
524
+ [ self.class.name, __FILE__, code ]
525
+
526
+ return eval( code, scope.get_binding, desc, __LINE__ )
527
+ end
528
+
529
+
530
+ ### Call the node's methodchain, if any, passing the associated
531
+ ### attribute as the first argument and any additional +args+ as second
532
+ ### and succeeding arguments. Returns the results of the call.
533
+ def call_methodchain( template, scope, *args )
534
+ chain = self.build_rendering_proc( template, scope )
535
+ # self.log.debug "Rendering proc is: %p" % chain
536
+
537
+ # self.log.debug "Fetching attribute %p of template %p" %
538
+ # [ self.name, template ]
539
+ attribute = template.send( self.name )
540
+ # self.log.debug "Attribute to be rendered (%s) is: %p" %
541
+ # [ self.name, attribute ]
542
+
543
+ if chain
544
+ return chain.call( attribute, *args )
545
+ else
546
+ return attribute
547
+ end
548
+ end
549
+
550
+
551
+ end # class AttributeDirective
552
+
553
+
554
+ ### The base bracketing directive class. Bracketing directives are
555
+ ### branching, filtering, and repetition directives in the template's AST
556
+ ### (e.g., <?foreach?>, <?if?>).
557
+ class BracketingDirective < AttributeDirective
558
+
559
+ # SVN Revision
560
+ SVNRev = %q$Rev$
561
+
562
+ # SVN Id
563
+ SVNId = %q$Id$
564
+
565
+
566
+ #############################################################
567
+ ### I N S T A N C E M E T H O D S
568
+ #############################################################
569
+
570
+ ### Initialize a new BracketingDirective object with the specified
571
+ ### +type+, +parser+, and +state+.
572
+ def initialize( type, parser, state ) # :notnew:
573
+ @subnodes = []
574
+ super
575
+ end
576
+
577
+
578
+ ######
579
+ public
580
+ ######
581
+
582
+ # The node's contained subnodes tree
583
+ attr_reader :subnodes
584
+
585
+
586
+ ### Returns +true+ for nodes which generate output themselves (as
587
+ ### opposed to ones which generate output through subnodes). This is
588
+ ### used for eliding blank lines from the node tree.
589
+ def is_rendering_node?
590
+ false
591
+ end
592
+
593
+
594
+ ### Install the behaviour defined by the directive and its subnodes
595
+ ### into the given +template+ object. This by default just installs
596
+ ### each of its subnodes.
597
+ def add_to_template( template )
598
+ super
599
+ self.subnodes.each do |node|
600
+ template.install_node( node )
601
+ end
602
+ end
603
+
604
+
605
+ ### Return a human-readable version of the object suitable for debugging
606
+ ### messages.
607
+ def inspect
608
+ %Q{<%s %s%s: %p>} % [
609
+ @type.capitalize,
610
+ @name,
611
+ @methodchain.strip.empty? ? "" : @methodchain,
612
+ @subnodes,
613
+ ]
614
+ end
615
+
616
+
617
+ ### Return the receiver and any subnodes as a flattened Array of nodes.
618
+ def to_a
619
+ ary = [self]
620
+ @subnodes.each {|node| ary += node.to_a }
621
+
622
+ return ary
623
+ end
624
+
625
+
626
+ ### Return an HTML fragment that can be used to represent the node
627
+ ### symbolically in a web-based introspection interface.
628
+ def to_html
629
+ nodeclass = self.css_class
630
+
631
+ super {
632
+ %q{<div class="node-subtree %s-subtree">
633
+ <div class="node-subtree-head %s-subtree-head"
634
+ >Subnodes</div>%s</div>} % [
635
+ nodeclass, nodeclass,
636
+ @subnodes.collect {|node| node.to_html}.join
637
+ ]
638
+ }
639
+ end
640
+
641
+
642
+
643
+ #########
644
+ protected
645
+ #########
646
+
647
+ ### Parse the contents of the directive. If a block is given (ie., by a
648
+ ### subclass's implementation), call it immediately after parsing an
649
+ ### optional format, mandatory identifier, and optional
650
+ ### methodchain. Then look for the end of the current directive tag, and
651
+ ### recurse into the parser for any nodes contained between this
652
+ ### directive and its <?end?>.
653
+ def parse_directive_contents( parser, state )
654
+ super
655
+
656
+ # Let subclasses implement further inner-tag parsing if they want
657
+ # to.
658
+ if block_given?
659
+ rval = yield( parser, state )
660
+ return nil if !rval
661
+ end
662
+
663
+ # Put the pointer after the closing tag
664
+ parser.scan_for_tag_ending( state ) or
665
+ raise Arrow::ParseError, "couldn't find tag end for '#@name'"
666
+
667
+ # Parse the content between this directive and the next <?end?>.
668
+ @subnodes.replace( parser.scan_for_nodes(state, type, self) )
669
+
670
+ return true
671
+ end
672
+
673
+
674
+ ### Use the contents of the associated attribute to render the
675
+ ### receiver's subnodes in the specified +scope+.
676
+ def render_contents( template, scope )
677
+ res = super
678
+ self.render_subnodes( res, template, scope )
679
+ end
680
+
681
+
682
+ ### Render each of the directive's bracketed nodes with the given
683
+ ### +item+, +template+, and evaluation +scope+.
684
+ def render_subnodes( item, template, scope )
685
+ template.with_overridden_attributes( scope, self.name => item ) do |template|
686
+ template.render( @subnodes, scope )
687
+ end
688
+ end
689
+
690
+ end # class BracketingDirective
691
+
692
+
693
+ ### Mixin which adds the notion of boolean evaluability to a directive.
694
+ module ConditionalDirective
695
+
696
+ # SVN Revision
697
+ SVNRev = %q$Rev$
698
+
699
+ # SVN Id
700
+ SVNId = %q$Id$
701
+
702
+
703
+ #############################################################
704
+ ### I N S T A N C E M E T H O D S
705
+ #############################################################
706
+
707
+ ######
708
+ public
709
+ ######
710
+
711
+ ### Returns +true+ for nodes which generate output themselves (as
712
+ ### opposed to ones which generate output through subnodes). This is
713
+ ### used for eliding blank lines from the node tree.
714
+ def is_rendering_node?
715
+ false
716
+ end
717
+
718
+
719
+ ### Returns +true+ if this Directive, in the context of the given
720
+ ### +template+ (an Arrow::Template) and +scope+ (a Binding object),
721
+ ### should be considered "true".
722
+ def evaluate( template, scope )
723
+ rval = self.call_methodchain( template, scope )
724
+
725
+ #self.log.debug "Methodchain evaluated to %s: %p" %
726
+ # [ rval ? "true" : "false", rval ]
727
+ return rval ? true : false
728
+ end
729
+
730
+ end # module ConditionalDirective
731
+
732
+ end # class Arrow::Template
733
+
734
+