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.
- data/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- 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
|
+
|