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,409 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
+ require 'spec/lib/appletmatchers'
17
+
18
+ require 'arrow'
19
+ require 'arrow/applet'
20
+ require 'arrow/appletmixins'
21
+
22
+
23
+ include Arrow::TestConstants
24
+
25
+ #####################################################################
26
+ ### C O N T E X T S
27
+ #####################################################################
28
+
29
+ describe Arrow::AppletAuthentication do
30
+ include Arrow::SpecHelpers,
31
+ Arrow::AppletMatchers
32
+
33
+ before( :all ) do
34
+ setup_logging( :crit )
35
+ end
36
+
37
+ after( :all ) do
38
+ reset_logging()
39
+ end
40
+
41
+
42
+ before( :each ) do
43
+ @uri = '/testing'
44
+ @connection = stub( "apache connnection", :remote_host => 'host' )
45
+ @txn = stub( "transaction", :uri => @uri, :vargs => true, :user => nil,
46
+ :authorized => false, :the_request => 'the request',
47
+ :connection => @connection, :remote_host => '127.0.0.1' )
48
+ end
49
+
50
+
51
+ describe "included by an Applet" do
52
+
53
+ describe " that doesn't override any of the auth methods" do
54
+ before( :all ) do
55
+ @appletclass = Class.new( Arrow::Applet ) do
56
+ include Arrow::AppletAuthentication
57
+
58
+ applet_name "Hi, I'm a fixture applet!"
59
+
60
+ def initialize( *args )
61
+ @authenticated = false
62
+ @authorized = false
63
+ super
64
+ end
65
+
66
+ attr_reader :authenticated, :authorized
67
+
68
+ def authenticated_action( txn )
69
+ with_authentication( txn ) do |user|
70
+ @authenticated = true
71
+ end
72
+ end
73
+
74
+ def authorized_action( txn )
75
+ with_authorization( txn ) do
76
+ @authorized = true
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ before( :each ) do
83
+ @applet = @appletclass.new( nil, nil, nil )
84
+ end
85
+
86
+ it "returns an UNAUTHORIZED response for an action wrapped in authentication" do
87
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
88
+ @applet.authenticated_action( @txn ).should =~ /requires auth/i
89
+ @applet.authenticated.should be_false()
90
+ end
91
+
92
+ it "returns an UNAUTHORIZED response for an action wrapped in authorization" do
93
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
94
+ @applet.authorized_action( @txn ).should =~ /requires auth/i
95
+ @applet.authorized.should be_false()
96
+ end
97
+
98
+ end
99
+
100
+ describe " that provides an implementation of #get_authenticated_user" do
101
+ before( :all ) do
102
+ @appletclass = Class.new( Arrow::Applet ) do
103
+ include Arrow::AppletAuthentication
104
+
105
+ applet_name "Hi, I'm a fixture applet!"
106
+
107
+ def initialize( *args )
108
+ @authenticated = false
109
+ @authorized = false
110
+ super
111
+ end
112
+
113
+ attr_reader :authenticated, :authorized
114
+
115
+ def authenticated_action( txn )
116
+ with_authentication( txn ) do |user|
117
+ @authenticated = true
118
+ end
119
+ end
120
+
121
+ def authorized_action( txn )
122
+ with_authorization( txn ) do
123
+ @authorized = true
124
+ end
125
+ end
126
+
127
+ def get_authenticated_user( txn )
128
+ return txn.user
129
+ end
130
+ end
131
+ end
132
+
133
+ before( :each ) do
134
+ @applet = @appletclass.new( nil, nil, nil )
135
+ end
136
+
137
+ it "returns an UNAUTHORIZED response for an action wrapped in authentication if " +
138
+ "the transaction doesn't contain a user" do
139
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
140
+ @applet.authenticated_action( @txn ).should =~ /requires auth/i
141
+ @applet.authenticated.should be_false()
142
+ end
143
+
144
+ it "returns a normal response for an action wrapped in authentication if " +
145
+ "the transaction does contain a user" do
146
+ @txn.stub!( :user ).and_return( :barney_the_clown )
147
+ @applet.authenticated_action( @txn ).should == true
148
+ @applet.authenticated.should be_true()
149
+ end
150
+
151
+ it "returns an UNAUTHORIZED response for an action wrapped in authorization if " +
152
+ "the transaction doesn't contain a user" do
153
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
154
+ @applet.authenticated_action( @txn ).should =~ /requires auth/i
155
+ @applet.authorized.should be_false()
156
+ end
157
+
158
+ it "returns a FORBIDDEN response for an action wrapped in authorization if " +
159
+ "the transaction does contain a user" do
160
+ @txn.stub!( :user ).and_return( :blinky_the_wombat )
161
+ @txn.should_receive( :status= ).with( Apache::FORBIDDEN )
162
+ @applet.authorized_action( @txn ).should =~ /access denied/i
163
+ @applet.authorized.should be_false()
164
+ end
165
+
166
+ end
167
+
168
+ describe " that also provides an implementation of #user_is_authorized" do
169
+ before( :all ) do
170
+ @appletclass = Class.new( Arrow::Applet ) do
171
+ include Arrow::AppletAuthentication
172
+
173
+ applet_name "Hi, I'm a fixture applet!"
174
+
175
+ def initialize( *args )
176
+ @authenticated = false
177
+ @authorized = false
178
+ super
179
+ end
180
+
181
+ attr_reader :authenticated, :authorized
182
+
183
+ def authenticated_action( txn )
184
+ with_authentication( txn ) do |user|
185
+ @authenticated = true
186
+ end
187
+ end
188
+
189
+ def authorized_action( txn )
190
+ with_authorization( txn ) do
191
+ @authorized = true
192
+ end
193
+ end
194
+
195
+ def get_authenticated_user( txn )
196
+ # Simplified for testing -- lets the test set whether or not authentication
197
+ # exists
198
+ return txn.user
199
+ end
200
+
201
+ def user_is_authorized( user, txn )
202
+ # Simplified for testing -- lets the test set whether or not authorization
203
+ # exists
204
+ return txn.authorized
205
+ end
206
+ end
207
+ end
208
+
209
+ before( :each ) do
210
+ @applet = @appletclass.new( nil, nil, nil )
211
+ end
212
+
213
+ it "returns an UNAUTHORIZED response for an action wrapped in authentication if " +
214
+ "the transaction doesn't contain a user" do
215
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
216
+ @applet.authenticated_action( @txn ).should =~ /requires auth/i
217
+ @applet.authenticated.should be_false()
218
+ @applet.authorized.should be_false()
219
+ end
220
+
221
+ it "returns a FORBIDDEN response for an action wrapped in authentication if " +
222
+ "the transaction does contain a user" do
223
+ @txn.stub!( :user ).and_return( :barney_the_clown )
224
+ @applet.authenticated_action( @txn ).should == true
225
+ @applet.authenticated.should be_true()
226
+ @applet.authorized.should be_false()
227
+ end
228
+
229
+ it "returns an UNAUTHORIZED response for an action wrapped in authorization if " +
230
+ "the transaction doesn't contain a user" do
231
+ @txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
232
+ @applet.authenticated_action( @txn ).should =~ /requires auth/i
233
+ @applet.authorized.should be_false()
234
+ end
235
+
236
+ it "returns a FORBIDDEN response for an action wrapped in authorization if " +
237
+ "the transaction does contain a user, but the user isn't authorized" do
238
+ @txn.stub!( :user ).and_return( :gurney_halleck )
239
+ @txn.should_receive( :status= ).with( Apache::FORBIDDEN )
240
+ @applet.authorized_action( @txn ).should =~ /access denied/i
241
+ @applet.authorized.should be_false()
242
+ end
243
+
244
+ it "returns a normal response for an action wrapped in authorization if " +
245
+ "the transaction does contain a user, and the user is authorized" do
246
+ @txn.stub!( :user ).and_return( :alia_atreides )
247
+ @txn.stub!( :authorized ).and_return( true )
248
+ @applet.authorized_action( @txn ).should == true
249
+ @applet.authorized.should be_true()
250
+ end
251
+
252
+ end
253
+
254
+ end
255
+
256
+ end
257
+
258
+ describe Arrow::AccessControls do
259
+ include Arrow::SpecHelpers,
260
+ Arrow::AppletMatchers
261
+
262
+ before( :all ) do
263
+ setup_logging( :crit )
264
+ end
265
+
266
+ before( :each ) do
267
+ @uri = '/testing'
268
+ end
269
+
270
+ after( :all ) do
271
+ reset_logging()
272
+ end
273
+
274
+
275
+ it "adds a declarative method to including applet classes for adding to the list" +
276
+ " of methods which can be run without authentication" do
277
+ @applet_class = Class.new( Arrow::Applet ) do
278
+ include Arrow::AccessControls
279
+ end
280
+
281
+ @applet_class.respond_to?( :unauthenticated_actions )
282
+ @applet_class.unauthenticated_actions.should have(3).members
283
+ @applet_class.unauthenticated_actions.should include( :login, :logout, :deny_access )
284
+ end
285
+
286
+
287
+ describe "included in an Applet" do
288
+
289
+ describe " that doesn't declare any other unauthenticated actions" do
290
+ before( :all ) do
291
+ @applet_class = Class.new( Arrow::Applet ) do
292
+ include Arrow::AccessControls
293
+
294
+ def action_missing_action( txn, action, *args )
295
+ return action
296
+ end
297
+
298
+ def login_action( txn, *args )
299
+ return :login
300
+ end
301
+
302
+ def logout_action( txn, *args )
303
+ return :logout
304
+ end
305
+
306
+ def deny_access_action( txn, *args )
307
+ return :deny_access
308
+ end
309
+ end
310
+ end
311
+
312
+ before( :each ) do
313
+ @applet = @applet_class.new( nil, nil, @uri )
314
+ @fakerequest = Apache::Request.new( @uri )
315
+ @txn = Arrow::Transaction.new( @fakerequest, nil, nil )
316
+ end
317
+
318
+
319
+ it "doesn't require authentication for the :login action" do
320
+ @applet.run( @txn, :login ).should == :login
321
+ end
322
+
323
+ it "doesn't require authentication for the :logout action" do
324
+ @applet.run( @txn, :logout ).should == :logout
325
+ end
326
+
327
+ it "doesn't require authentication for the :deny_access action" do
328
+ @applet.run( @txn, :deny_access ).should == :deny_access
329
+ end
330
+
331
+ it "requires authentication for any other action" do
332
+ @applet.run( @txn, :serenity ).should == :login
333
+ end
334
+
335
+ end
336
+
337
+ describe " that declares a custom unauthenticated action" do
338
+ before( :all ) do
339
+ @applet_class = Class.new( Arrow::Applet ) do
340
+ include Arrow::AccessControls
341
+
342
+ unauthenticated_actions :willy_nilly, :action_missing
343
+
344
+ def action_missing_action( txn, action, *args )
345
+ return :action_missing
346
+ end
347
+
348
+ def willy_nilly_action( txn, *args )
349
+ return :willy_nilly
350
+ end
351
+
352
+ def serenity_action( txn, *args )
353
+ return :serenity
354
+ end
355
+
356
+ def login_action( txn, *args )
357
+ return :login
358
+ end
359
+
360
+ def logout_action( txn, *args )
361
+ return :logout
362
+ end
363
+
364
+ def deny_access_action( txn, *args )
365
+ return :deny_access
366
+ end
367
+ end
368
+ end
369
+
370
+ before( :each ) do
371
+ @applet = @applet_class.new( nil, nil, @uri )
372
+ @fakerequest = Apache::Request.new( @uri )
373
+ @txn = Arrow::Transaction.new( @fakerequest, nil, nil )
374
+ end
375
+
376
+
377
+ it "doesn't require authentication for the :login action" do
378
+ @applet.run( @txn, :login ).should == :login
379
+ end
380
+
381
+ it "doesn't require authentication for the :logout action" do
382
+ @applet.run( @txn, :logout ).should == :logout
383
+ end
384
+
385
+ it "doesn't require authentication for the :deny_access action" do
386
+ @applet.run( @txn, :deny_access ).should == :deny_access
387
+ end
388
+
389
+ it "doesn't require authentication for actions declared as unauthenticated" do
390
+ @applet.run( @txn, :willy_nilly ).should == :willy_nilly
391
+ end
392
+
393
+ it "doesn't require authentication for remapped actions declared as unauthenticated" do
394
+ @applet.run( @txn, :klang_locke ).should == :action_missing
395
+ end
396
+
397
+ it "requires authentication for any other action" do
398
+ @applet.run( @txn, :serenity ).should == :login
399
+ end
400
+
401
+ end
402
+
403
+ end
404
+
405
+
406
+ end # describe "Applet mixins"
407
+
408
+
409
+
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Specification for the Arrow::Applet class
4
+ # $Id$
5
+ #
6
+ # Copyright (c) 2004-2008 The FaerieMUD Consortium. Most rights reserved.
7
+ #
8
+
9
+ BEGIN {
10
+ require 'pathname'
11
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
12
+
13
+ libdir = basedir + "lib"
14
+
15
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
16
+ }
17
+
18
+ begin
19
+ require 'spec'
20
+ require 'apache/fakerequest'
21
+ require 'arrow'
22
+ require 'arrow/applet'
23
+ require 'arrow/appletregistry'
24
+ require 'arrow/config'
25
+ require 'spec/lib/helpers'
26
+ rescue LoadError
27
+ unless Object.const_defined?( :Gem )
28
+ require 'rubygems'
29
+ retry
30
+ end
31
+ raise
32
+ end
33
+
34
+
35
+ #####################################################################
36
+ ### C O N T E X T S
37
+ #####################################################################
38
+
39
+ describe Arrow::AppletRegistry do
40
+ include Arrow::SpecHelpers
41
+
42
+ APPLETREGISTRY_TEST_CONFIG = {
43
+ :applets => {
44
+ :path => Arrow::Path.new( "applets:specs/data/applets" ),
45
+ :pattern => '*.rb',
46
+ :pollInterval => 5,
47
+ :missingApplet => '/missing',
48
+ :errorApplet => '/error',
49
+
50
+ :layout => {
51
+ "/" => "Setup",
52
+ "/missing" => "NoSuchAppletHandler",
53
+ "/error" => "ErrorHandler",
54
+ "/status" => "ServerStatus",
55
+ "/hello" => "Hello",
56
+ "/args" => "ArgumentTester",
57
+ "/protected" => "ProtectedDelegator",
58
+ "/protected/hello" => "Hello",
59
+ "/counted" => "AccessCounter",
60
+ "/counted/hello" => "Hello",
61
+
62
+ "/test" => "TestApplet",
63
+ "/foo" => "BargleApplet",
64
+ },
65
+ :config => {},
66
+ },
67
+ }
68
+
69
+ GEM_CONFIG = APPLETREGISTRY_TEST_CONFIG.merge({
70
+ :gems => {
71
+ :require_signed => false,
72
+ :autoinstall => false,
73
+ :path => Arrow::Path.new([ "gems", *Gem.path ]),
74
+ :applets => {
75
+ 'arrow-demo-apps' => '>= 0.0.3',
76
+ 'arrow-management-apps' => '= 0.9.4',
77
+ 'arrow-laikapedia' => nil,
78
+ },
79
+ },
80
+ })
81
+
82
+
83
+ ### Set up a stubbed applet instance that can be loaded via
84
+ ### Arrow::Applet.load.
85
+ def fixture_appletclass( path, name, classname )
86
+ applet = stub( "#{classname} instance (path)" )
87
+ appletclass = stub( "#{classname} class",
88
+ :name => name,
89
+ :normalized_name => classname,
90
+ :new => applet
91
+ )
92
+
93
+ Arrow::Applet.should_receive( :load ).with( path ).once.
94
+ and_return([ appletclass ])
95
+ File.should_receive( :mtime ).with( path ).and_return( Time.now )
96
+
97
+ return applet, appletclass
98
+ end
99
+
100
+
101
+
102
+ before( :all ) do
103
+ setup_logging( :crit )
104
+ end
105
+
106
+ after( :all ) do
107
+ reset_logging()
108
+ end
109
+
110
+
111
+ before( :each ) do
112
+ Arrow::Applet.stub!( :load ).and_return( [] )
113
+ end
114
+
115
+
116
+ it "ignores the gem home if it isn't a directory" do
117
+ homepath = mock( 'Pathname object for gem home' )
118
+
119
+ Gem.should_receive( :user_home ).and_return( :user_home )
120
+ Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
121
+ homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
122
+ homepath.should_receive( :directory? ).and_return( false )
123
+
124
+ Arrow::AppletRegistry.get_safe_gemhome.should be_nil()
125
+ end
126
+
127
+
128
+ it "ignores the gem home if it's world-writable" do
129
+ homepath = mock( 'Pathname object for gem home' )
130
+ homepath_stat = mock( 'Stat data for the gem home' )
131
+
132
+ Gem.should_receive( :user_home ).and_return( :user_home )
133
+ Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
134
+ homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
135
+ homepath.should_receive( :directory? ).and_return( true )
136
+ homepath.should_receive( :stat ).and_return( homepath_stat )
137
+
138
+ homepath_stat.should_receive( :mode ).and_return( 0777 )
139
+
140
+ Arrow::AppletRegistry.get_safe_gemhome.should be_nil()
141
+ end
142
+
143
+
144
+ it "returns an untainted Pathname for the gem home if it's sane" do
145
+ homepath = mock( 'Pathname object for gem home' )
146
+ homepath.taint
147
+ homepath_stat = mock( 'Stat data for the gem home' )
148
+
149
+ Gem.should_receive( :user_home ).and_return( :user_home )
150
+ Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
151
+ homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
152
+ homepath.should_receive( :directory? ).and_return( true )
153
+ homepath.should_receive( :stat ).and_return( homepath_stat )
154
+
155
+ homepath_stat.should_receive( :mode ).and_return( 0755 )
156
+
157
+ rval = Arrow::AppletRegistry.get_safe_gemhome
158
+
159
+ rval.should == homepath
160
+ rval.should_not be_tainted()
161
+ end
162
+
163
+
164
+ it "loads any configured gems when it is created, and adds their template/ and applet/ " +
165
+ "directories to the template factory and path" do
166
+ Gem.should_receive( :activate ).with( 'arrow-demo-apps', '>= 0.0.3' ).
167
+ and_return( true )
168
+ Gem.should_receive( :datadir ).with( 'arrow-demo-apps' ).
169
+ and_return( '/some/path/arrow-demo-apps/data' )
170
+ Gem.should_receive( :activate ).with( 'arrow-management-apps', '= 0.9.4' ).
171
+ and_return( true )
172
+ Gem.should_receive( :datadir ).with( 'arrow-management-apps' ).
173
+ and_return( '/some/path/arrow-management-apps/data' )
174
+ Gem.should_receive( :activate ).with( 'arrow-laikapedia', Gem::Requirement.default ).
175
+ and_return( true )
176
+ Gem.should_receive( :datadir ).with( 'arrow-laikapedia' ).
177
+ and_return( '/some/path/arrow-laikapedia/data' )
178
+
179
+ config = Arrow::Config.new( GEM_CONFIG )
180
+ registry = Arrow::AppletRegistry.new( config )
181
+
182
+ registry.template_factory.path.should have(5).members
183
+ registry.template_factory.path.dirs.should include(
184
+ '/some/path/arrow-demo-apps/data/templates',
185
+ '/some/path/arrow-management-apps/data/templates',
186
+ '/some/path/arrow-laikapedia/data/templates'
187
+ )
188
+
189
+ registry.path.should have(5).members
190
+ registry.path.dirs.should include(
191
+ '/some/path/arrow-demo-apps/data/applets',
192
+ '/some/path/arrow-management-apps/data/applets',
193
+ '/some/path/arrow-laikapedia/data/applets'
194
+ )
195
+ end
196
+
197
+
198
+ it "searches for applets in its list of paths, and loads all of them" do
199
+ test_applet, test_appletclass = fixture_appletclass( 'test.rb', 'test', 'TestApplet' )
200
+ bargle_applet, bargle_appletclass = fixture_appletclass( 'bargle.rb', 'bargle', 'BargleApplet' )
201
+
202
+ File.stub!( :exist? ).and_return( true )
203
+
204
+ Dir.should_receive( :[] ).with( 'applets/*.rb' ).and_return([ 'test.rb', 'bargle.rb' ])
205
+
206
+ applets_path = stub( 'applets path object', :dirs => ['applets'] )
207
+ applets_path.stub!( :each ).and_yield( 'applets' )
208
+ config = Arrow::Config.new( APPLETREGISTRY_TEST_CONFIG )
209
+ config.applets.path = applets_path
210
+ registry = Arrow::AppletRegistry.new( config )
211
+
212
+ registry.urispace.should have(2).members
213
+ registry.urispace.values.should include( bargle_applet, test_applet )
214
+ end
215
+
216
+
217
+ describe "instance" do
218
+
219
+ before( :all ) do
220
+ setup_logging( :crit )
221
+ end
222
+
223
+ TEST_URISPACE = {
224
+ "" => "Setup",
225
+ "hello" => "Hello",
226
+ "args" => "ArgumentTester",
227
+ "protected" => "ProtectedDelegator",
228
+ "protected/hello" => "Hello",
229
+ "counted" => "AccessCounter",
230
+ "counted/hello" => "Hello",
231
+ }
232
+
233
+ before( :each ) do
234
+ @config = Arrow::Config.new( APPLETREGISTRY_TEST_CONFIG )
235
+ @registry = Arrow::AppletRegistry.new( @config )
236
+ @registry.instance_variable_set( :@urispace, TEST_URISPACE )
237
+ end
238
+
239
+
240
+ it "can create an applet chain for a uri" do
241
+ @registry.find_applet_chain( '/protected/hello' ).should have(3).members
242
+ @registry.find_applet_chain( '/protected/hello' ).
243
+ collect {|cl| cl.path }.should == [ '', 'protected', 'protected/hello' ]
244
+ @registry.find_applet_chain( '/protected/hello' ).
245
+ collect {|cl| cl.applet }.should == [ 'Setup', 'ProtectedDelegator', 'Hello' ]
246
+
247
+ @registry.find_applet_chain( '/protected' ).should have(2).members
248
+ @registry.find_applet_chain( '/protected' ).
249
+ collect {|cl| cl.path }.should == [ '', 'protected' ]
250
+ @registry.find_applet_chain( '/protected' ).
251
+ collect {|cl| cl.applet }.should == [ 'Setup', 'ProtectedDelegator' ]
252
+
253
+ @registry.find_applet_chain( '/' ).should have(1).members
254
+ @registry.find_applet_chain( '/' ).
255
+ collect {|cl| cl.path }.should == [ '' ]
256
+ @registry.find_applet_chain( '/' ).
257
+ collect {|cl| cl.applet }.should == [ 'Setup' ]
258
+
259
+ @registry.find_applet_chain( '/args' ).should have(2).members
260
+ @registry.find_applet_chain( '/args' ).
261
+ collect {|cl| cl.path }.should == [ '', 'args' ]
262
+ @registry.find_applet_chain( '/args' ).
263
+ collect {|cl| cl.applet }.should == [ 'Setup', 'ArgumentTester' ]
264
+ end
265
+
266
+
267
+ it "reloads its applets if the configured interval has passed since applets " +
268
+ "were loaded" do
269
+ # Make sure the load time is far enough in the past
270
+ @registry.load_time = Time.now - 2000
271
+ @registry.should_receive( :reload_applets ).once
272
+ @registry.check_for_updates
273
+ end
274
+
275
+ it "doesn't reload its applets if the configured interval hasn't passed since " +
276
+ "applets were loaded" do
277
+ # Make sure the load time is far enough in the past
278
+ @registry.load_time = Time.now
279
+ @registry.should_not_receive( :reload_applets )
280
+ @registry.check_for_updates
281
+ end
282
+
283
+ it "doesn't reload its applets if reloading is turned off (interval is zero)" do
284
+ # Make sure the load time is far enough in the past
285
+ @config.applets.pollInterval = 0
286
+ @registry.load_time = Time.now - 2000
287
+ @registry.should_not_receive( :reload_applets )
288
+ @registry.check_for_updates
289
+ end
290
+
291
+ end
292
+
293
+ end
294
+