picnic 0.7.1 → 0.8.0

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 (45) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +30 -13
  3. data/Rakefile +2 -0
  4. data/lib/picnic.rb +5 -120
  5. data/lib/picnic/authentication.rb +40 -4
  6. data/lib/picnic/cli.rb +86 -43
  7. data/lib/picnic/conf.rb +45 -43
  8. data/lib/picnic/logger.rb +41 -0
  9. data/lib/picnic/server.rb +99 -0
  10. data/lib/picnic/version.rb +2 -2
  11. data/picnic.gemspec +44 -0
  12. data/vendor/{camping-1.5.180 → camping-2.0.20090212}/CHANGELOG +17 -10
  13. data/vendor/{camping-1.5.180 → camping-2.0.20090212}/COPYING +0 -0
  14. data/vendor/{camping-1.5.180 → camping-2.0.20090212}/README +2 -2
  15. data/vendor/{camping-1.5.180 → camping-2.0.20090212}/Rakefile +62 -5
  16. data/vendor/camping-2.0.20090212/bin/camping +99 -0
  17. data/vendor/camping-2.0.20090212/doc/camping.1.gz +0 -0
  18. data/vendor/camping-2.0.20090212/examples/README +5 -0
  19. data/vendor/camping-2.0.20090212/examples/blog.rb +375 -0
  20. data/vendor/camping-2.0.20090212/examples/campsh.rb +629 -0
  21. data/vendor/camping-2.0.20090212/examples/tepee.rb +242 -0
  22. data/vendor/camping-2.0.20090212/extras/Camping.gif +0 -0
  23. data/vendor/camping-2.0.20090212/extras/flipbook_rdoc.rb +491 -0
  24. data/vendor/camping-2.0.20090212/extras/permalink.gif +0 -0
  25. data/vendor/{camping-1.5.180 → camping-2.0.20090212}/lib/camping-unabridged.rb +168 -294
  26. data/vendor/camping-2.0.20090212/lib/camping.rb +54 -0
  27. data/vendor/{camping-1.5.180/lib/camping/db.rb → camping-2.0.20090212/lib/camping/ar.rb} +4 -4
  28. data/vendor/{camping-1.5.180/lib/camping → camping-2.0.20090212/lib/camping/ar}/session.rb +23 -14
  29. data/vendor/camping-2.0.20090212/lib/camping/mab.rb +26 -0
  30. data/vendor/camping-2.0.20090212/lib/camping/reloader.rb +163 -0
  31. data/vendor/camping-2.0.20090212/lib/camping/server.rb +158 -0
  32. data/vendor/camping-2.0.20090212/lib/camping/session.rb +74 -0
  33. data/vendor/camping-2.0.20090212/setup.rb +1551 -0
  34. data/vendor/camping-2.0.20090212/test/apps/env_debug.rb +65 -0
  35. data/vendor/camping-2.0.20090212/test/apps/forms.rb +95 -0
  36. data/vendor/camping-2.0.20090212/test/apps/misc.rb +86 -0
  37. data/vendor/camping-2.0.20090212/test/apps/sessions.rb +38 -0
  38. data/vendor/camping-2.0.20090212/test/test_camping.rb +54 -0
  39. metadata +43 -16
  40. data/lib/picnic/postambles.rb +0 -292
  41. data/lib/picnic/utils.rb +0 -36
  42. data/vendor/camping-1.5.180/lib/camping.rb +0 -57
  43. data/vendor/camping-1.5.180/lib/camping/fastcgi.rb +0 -244
  44. data/vendor/camping-1.5.180/lib/camping/reloader.rb +0 -163
  45. data/vendor/camping-1.5.180/lib/camping/webrick.rb +0 -65
@@ -21,14 +21,18 @@
21
21
  # ActiveRecord is an object-to-relational database mapper with adapters
22
22
  # for SQLite3, MySQL, PostgreSQL, SQL Server and more.
23
23
  # * Markaby, used in your views to describe HTML in plain Ruby.
24
- # * MetAid, a few metaprogramming methods which Camping uses.
25
- # * Tempfile, for storing file uploads.
26
24
  #
27
25
  # Camping also works well with Mongrel, the swift Ruby web server.
28
26
  # http://rubyforge.org/projects/mongrel Mongrel comes with examples
29
27
  # in its <tt>examples/camping</tt> directory.
30
28
  #
31
- %w[active_support markaby tempfile uri].each { |lib| require lib }
29
+ %w[uri stringio rack].map { |l| require l }
30
+
31
+ class Object #:nodoc:
32
+ def meta_def(m,&b) #:nodoc:
33
+ (class<<self;self end).send(:define_method,m,&b)
34
+ end
35
+ end
32
36
 
33
37
  # == Camping
34
38
  #
@@ -72,7 +76,6 @@
72
76
  # unless Blog::Models::Post.table_exists?
73
77
  # ActiveRecord::Schema.define do
74
78
  # create_table :blog_posts, :force => true do |t|
75
- # t.column :id, :integer, :null => false
76
79
  # t.column :user_id, :integer, :null => false
77
80
  # t.column :title, :string, :limit => 255
78
81
  # t.column :body, :text
@@ -83,25 +86,17 @@
83
86
  #
84
87
  # For more tips, see http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
85
88
  module Camping
86
- # Stores an +Array+ of all Camping applications modules. Modules are added
87
- # automatically by +Camping.goes+.
88
- #
89
- # Camping.goes :Blog
90
- # Camping.goes :Tepee
91
- # Camping::Apps # => [Blog, Tepee]
92
- #
93
- Apps = []
94
89
  C = self
95
- S = IO.read(__FILE__).sub(/^ S = I.+$/,'')
96
- P="Cam\ping Problem!"
97
-
98
- H = HashWithIndifferentAccess
99
- # An object-like Hash, based on ActiveSupport's HashWithIndifferentAccess.
90
+ S = IO.read(__FILE__) rescue nil
91
+ P = "<h1>Cam\ping Problem!</h1><h2>%s</h2>"
92
+ U = Rack::Utils
93
+ Apps = []
94
+ # An object-like Hash.
100
95
  # All Camping query string and cookie variables are loaded as this.
101
96
  #
102
97
  # To access the query string, for instance, use the <tt>@input</tt> variable.
103
98
  #
104
- # module Blog::Models
99
+ # module Blog::Controllers
105
100
  # class Index < R '/'
106
101
  # def get
107
102
  # if page = @input.page.to_i > 0
@@ -118,8 +113,7 @@ module Camping
118
113
  # to get the value for the <tt>page</tt> query variable.
119
114
  #
120
115
  # Use the <tt>@cookies</tt> variable in the same fashion to access cookie variables.
121
- # Also, the <tt>@env</tt> variable is an H containing the HTTP headers and server info.
122
- class H
116
+ class H < Hash
123
117
  # Gets or sets keys in the hash.
124
118
  #
125
119
  # @cookies.my_favorite = :macadamian
@@ -127,9 +121,9 @@ module Camping
127
121
  # => :macadamian
128
122
  #
129
123
  def method_missing(m,*a)
130
- m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
124
+ m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super
131
125
  end
132
- alias_method :u, :regular_update
126
+ undef id, type
133
127
  end
134
128
 
135
129
  # Helpers contains methods available in your controllers and views. You may add
@@ -204,30 +198,15 @@ module Camping
204
198
  #
205
199
  def R(c,*g)
206
200
  p,h=/\(.+?\)/,g.grep(Hash)
207
- (g-=h).inject(c.urls.find{|x|x.scan(p).size==g.size}.dup){|s,a|
208
- s.sub p,C.escape((a[a.class.primary_key]rescue a))
209
- }+(h.any?? "?"+h[0].map{|x|x.map{|z|C.escape z}*"="}*"&": "")
201
+ g-=h
202
+ raise "bad route" unless u = c.urls.find{|x|
203
+ break x if x.scan(p).size == g.size &&
204
+ /^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
205
+ x.sub p,U.escape((a[a.class.primary_key]rescue a))})
206
+ }
207
+ h.any?? u+"?"+U.build_query(h[0]) : u
210
208
  end
211
209
 
212
- # Shows AR validation errors for the object passed.
213
- # There is no output if there are no errors.
214
- #
215
- # An example might look like:
216
- #
217
- # errors_for @post
218
- #
219
- # Might (depending on actual data) render something like this in Markaby:
220
- #
221
- # ul.errors do
222
- # li "Body can't be empty"
223
- # li "Title must be unique"
224
- # end
225
- #
226
- # Add a simple ul.errors {color:red; font-weight:bold;} CSS rule and you
227
- # have built-in, usable error checking in only one line of code. :-)
228
- #
229
- # See AR validation documentation for details on validations.
230
- def errors_for(o); ul.errors { o.errors.each_full { |er| li er } } if o.errors.any?; end
231
210
  # Simply builds a complete path from a path +p+ within the app. If your application is
232
211
  # mounted at <tt>/blog</tt>:
233
212
  #
@@ -235,37 +214,32 @@ module Camping
235
214
  # self / "styles.css" #=> "styles.css"
236
215
  # self / R(Edit, 1) #=> "/blog/edit/1"
237
216
  #
238
- def /(p); p[/^\//]?@root+p:p end
217
+ def /(p); p[0]==?/?@root+p:p end
239
218
  # Builds a URL route to a controller or a path, returning a URI object.
240
219
  # This way you'll get the hostname and the port number, a complete URL.
241
- # No scheme is given (http or https).
242
220
  #
243
221
  # You can use this to grab URLs for controllers using the R-style syntax.
244
222
  # So, if your application is mounted at <tt>http://test.ing/blog/</tt>
245
223
  # and you have a View controller which routes as <tt>R '/view/(\d+)'</tt>:
246
224
  #
247
- # URL(View, @post.id) #=> #<URL://test.ing/blog/view/12>
225
+ # URL(View, @post.id) #=> #<URL:http://test.ing/blog/view/12>
248
226
  #
249
227
  # Or you can use the direct path:
250
228
  #
251
- # self.URL #=> #<URL://test.ing/blog/>
252
- # self.URL + "view/12" #=> #<URL://test.ing/blog/view/12>
253
- # URL("/view/12") #=> #<URL://test.ing/blog/view/12>
254
- #
255
- # Since no scheme is given, you will need to add the scheme yourself:
256
- #
257
- # "http" + URL("/view/12") #=> "http://test.ing/blog/view/12"
229
+ # self.URL #=> #<URL:http://test.ing/blog/>
230
+ # self.URL + "view/12" #=> #<URL:http://test.ing/blog/view/12>
231
+ # URL("/view/12") #=> #<URL:http://test.ing/blog/view/12>
258
232
  #
259
233
  # It's okay to pass URL strings through this method as well:
260
234
  #
261
- # URL("http://google.com") #=> #<URI:http://google.com>
235
+ # URL("http://google.com") #=> #<URL:http://google.com>
262
236
  #
263
237
  # Any string which doesn't begin with a slash will pass through
264
238
  # unscathed.
265
239
  def URL c='/',*a
266
240
  c = R(c, *a) if c.respond_to? :urls
267
241
  c = self/c
268
- c = "//"+@env.HTTP_HOST+c if c[/^\//]
242
+ c = @request.url[/.{8,}?(?=\/)/]+c if c[0]==?/
269
243
  URI(c)
270
244
  end
271
245
  end
@@ -304,9 +278,7 @@ module Camping
304
278
  # end
305
279
  #
306
280
  module Base
307
- include Helpers
308
- attr_accessor :input, :cookies, :env, :headers, :body, :status, :root
309
- Z = "\r\n"
281
+ attr_accessor :input, :cookies, :headers, :body, :status, :root
310
282
 
311
283
  # Display a view, calling it by its method name +m+. If a <tt>layout</tt>
312
284
  # method is found in Camping::Views, it will be used to wrap the HTML.
@@ -320,27 +292,51 @@ module Camping
320
292
  # end
321
293
  # end
322
294
  #
323
- def render(m); end; undef_method :render
324
-
325
- # Any stray method calls will be passed to Markaby. This means you can reply
326
- # with HTML directly from your controller for quick debugging.
295
+ # You can also return directly html by just passing a block
327
296
  #
297
+ def render(v,*a,&b)
298
+ mab(/^_/!~v.to_s){send(v,*a,&b)}
299
+ end
300
+
301
+ # You can directly return HTML form your controller for quick debugging
302
+ # by calling this method and pass some Markaby to it.
303
+ #
328
304
  # module Camping::Controllers
329
305
  # class Info
330
- # def get; code @env.inspect end
306
+ # def get; mab{ code @headers.inspect } end
331
307
  # end
332
308
  # end
333
309
  #
334
- # If you have a <tt>layout</tt> method in Camping::Views, it will be used to
335
- # wrap the HTML.
336
- def method_missing(*a,&b)
337
- a.shift if a[0]==:render
310
+ # You can also pass true to use the :layout HTML wrapping method
311
+ #
312
+ def mab(l=nil,&b)
338
313
  m=Mab.new({},self)
339
- s=m.capture{send(*a,&b)}
340
- s=m.capture{send(:layout){s}} if /^_/!~a[0].to_s and m.respond_to?:layout
314
+ s=m.capture(&b)
315
+ s=m.capture{layout{s}} if l && m.respond_to?(:layout)
341
316
  s
342
317
  end
343
318
 
319
+ # A quick means of setting this controller's status, body and headers.
320
+ # Used internally by Camping, but... by all means...
321
+ #
322
+ # r(302, '', 'Location' => self / "/view/12")
323
+ #
324
+ # Is equivalent to:
325
+ #
326
+ # redirect "/view/12"
327
+ #
328
+ # You can also switch the body and the header in order to support Rack:
329
+ #
330
+ # r(302, {'Location' => self / "/view/12"}, '')
331
+ #
332
+ # See also: #r404, #r500 and #r501
333
+ def r(s, b, h = {})
334
+ b, h = h, b if Hash === b
335
+ @status = s
336
+ @headers.merge!(h)
337
+ @body = b
338
+ end
339
+
344
340
  # Formulate a redirect response: a 302 status with <tt>Location</tt> header
345
341
  # and a blank body. Uses Helpers#URL to build the location from a controller
346
342
  # route or path.
@@ -354,19 +350,44 @@ module Camping
354
350
  # You'll need to <tt>return redirect(...)</tt> if this isn't the last statement
355
351
  # in your code.
356
352
  def redirect(*a)
357
- r(302,'','Location'=>URL(*a))
353
+ r(302,'','Location'=>URL(*a).to_s)
358
354
  end
359
355
 
360
- # A quick means of setting this controller's status, body and headers.
361
- # Used internally by Camping, but... by all means...
356
+ # Called when a controller was not found. It is mainly used internally, but it can
357
+ # also be useful for you, if you want to filter some parameters.
358
+ #
359
+ # module Camping
360
+ # def r404(p=env.PATH)
361
+ # @status = 404
362
+ # div do
363
+ # h1 'Camping Problem!'
364
+ # h2 "#{p} not found"
365
+ # end
366
+ # end
367
+ # end
362
368
  #
363
- # r(302, '', 'Location' => self / "/view/12")
369
+ # See: I
370
+ def r404(p=env.PATH)
371
+ r(404, P % "#{p} not found")
372
+ end
373
+
374
+ # If there is a parse error in Camping or in your application's source code, it will not be caught
375
+ # by Camping. The controller class +k+ and request method +m+ (GET, POST, etc.) where the error
376
+ # took place are passed in, along with the Exception +e+ which can be mined for useful info.
364
377
  #
365
- # Is equivalent to:
378
+ # You can overide it, but if you have an error in here, it will be uncaught !
366
379
  #
367
- # redirect "/view/12"
380
+ # See: I
381
+ def r500(k,m,x)
382
+ r(500, P % "#{k}.#{m}" + "<h3>#{x.class} #{x.message}: <ul>#{x.backtrace.map{|b|"<li>#{b}</li>"}}</ul></h3>")
383
+ end
384
+
385
+ # Called if an undefined method is called on a Controller, along with the request method +m+ (GET, POST, etc.)
368
386
  #
369
- def r(s, b, h = {}); @status = s; @headers.merge!(h); @body = b; end
387
+ # See: I
388
+ def r501(m=@method)
389
+ r(501, P % "#{m.upcase} not implemented")
390
+ end
370
391
 
371
392
  # Turn a controller into an array. This is designed to be used to pipe
372
393
  # controllers into the <tt>r</tt> method. A great way to forward your
@@ -376,55 +397,36 @@ module Camping
376
397
  # def get(id)
377
398
  # Post.find(id)
378
399
  # rescue
379
- # r *Blog.get(:NotFound, @env.REQUEST_URI)
400
+ # r *Blog.get(:NotFound, @headers.REQUEST_URI)
380
401
  # end
381
402
  # end
382
- #
383
- def to_a;[@status, @body, @headers] end
384
-
385
- def initialize(r, e, m) #:nodoc:
386
- e = H[e.to_hash]
387
- @status, @method, @env, @headers, @root = 200, m.downcase, e,
388
- {'Content-Type'=>'text/html'}, e.SCRIPT_NAME.sub(/\/$/,'')
389
- @k = C.kp(e.HTTP_COOKIE)
390
- qs = C.qsp(e.QUERY_STRING)
391
- @in = r
392
- if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(e.CONTENT_TYPE)
393
- b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
394
- until @in.eof?
395
- fh=H[]
396
- for l in @in
397
- case l
398
- when Z: break
399
- when /^Content-Disposition: form-data;/
400
- fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten]
401
- when /^Content-Type: (.+?)(\r$|\Z)/m
402
- puts "=> fh[type] = #$1"
403
- fh[:type] = $1
404
- end
405
- end
406
- fn=fh[:name]
407
- o=if fh[:filename]
408
- o=fh[:tempfile]=Tempfile.new(:C)
409
- o.binmode
410
- else
411
- fh=""
412
- end
413
- while l=@in.read(16384)
414
- if l=~b
415
- o<<$`.chomp
416
- @in.seek(-$'.size,IO::SEEK_CUR)
417
- break
418
- end
419
- o<<l
420
- end
421
- C.qsp(fn,'&;',fh,qs) if fn
422
- fh[:tempfile].rewind if fh.is_a?H
403
+ def to_a
404
+ @response.body = (@body.respond_to?(:each) ? @body : '')
405
+ @response.status = @status
406
+ @response.headers.merge!(@headers)
407
+ @cookies.each do |k, v|
408
+ v = {:value => v, :path => self / "/"} if String===v
409
+ @response.set_cookie(k, v) if @request.cookies[k] != v
410
+ end
411
+ @response.to_a
412
+ end
413
+
414
+ def initialize(env) #:nodoc:
415
+ @request, @response, @env =
416
+ Rack::Request.new(env), Rack::Response.new, env
417
+ @root, @input, @cookies,
418
+ @headers, @status =
419
+ @env.SCRIPT_NAME.sub(/\/$/,''),
420
+ H[@request.params], H[@request.cookies],
421
+ @response.headers, @response.status
422
+
423
+ @input.each do |k, v|
424
+ if k[-2..-1] == "[]"
425
+ @input[k[0..-3]] = @input.delete(k)
426
+ elsif k =~ /(.*)\[([^\]]+)\]$/
427
+ (@input[$1] ||= H[])[$2] = @input.delete(k)
423
428
  end
424
- elsif @method == "post"
425
- qs.merge!(C.qsp(@in.read))
426
429
  end
427
- @cookies, @input = @k.dup, qs.dup
428
430
  end
429
431
 
430
432
  # All requests pass through this method before going to the controller. Some magic
@@ -433,21 +435,10 @@ module Camping
433
435
  # See http://code.whytheluckystiff.net/camping/wiki/BeforeAndAfterOverrides for more
434
436
  # on before and after overrides with Camping.
435
437
  def service(*a)
436
- @body = send(@method, *a) if respond_to? @method
437
- @headers['Set-Cookie'] = @cookies.map { |k,v|
438
- "#{k}=#{C.escape(v[:value] || v)}; path=#{self/"/"}; expires=#{v[:expires] && v[:expires].strftime('%a, %d-%b-%Y %H:%M:%S %Z') || nil}" if v != @k[k]
439
- } - [nil]
438
+ r = catch(:halt){send(@env.REQUEST_METHOD.downcase, *a)}
439
+ @body ||= r
440
440
  self
441
441
  end
442
-
443
- # Used by the web server to convert the current request to a string. If you need to
444
- # alter the way Camping builds HTTP headers, consider overriding this method.
445
- def to_s
446
- a=[]
447
- @headers.map{|k,v|[*v].map{|x|a<<"#{k}: #{x}"}}
448
- "Status: #{@status}#{Z+a*Z+Z*2+@body}"
449
- end
450
-
451
442
  end
452
443
 
453
444
  # Controllers is a module for placing classes which handle URLs. This is done
@@ -463,15 +454,10 @@ module Camping
463
454
  # If no route is set, Camping will guess the route from the class name.
464
455
  # The rule is very simple: the route becomes a slash followed by the lowercased
465
456
  # class name. See Controllers::D for the complete rules of dispatch.
466
- #
467
- # == Special classes
468
- #
469
- # There are two special classes used for handling 404 and 500 errors. The
470
- # NotFound class handles URLs not found. The ServerError class handles exceptions
471
- # uncaught by your application.
472
457
  module Controllers
473
458
  @r = []
474
459
  class << self
460
+ # An array containing the various controllers available for dispatch.
475
461
  def r #:nodoc:
476
462
  @r
477
463
  end
@@ -513,15 +499,18 @@ module Camping
513
499
  # # Classes with routes are searched in order of their creation.
514
500
  #
515
501
  # So, define your catch-all controllers last.
516
- def D(path)
502
+ def D(p, m)
503
+ p = '/' if !p || !p[0]
517
504
  r.map { |k|
518
505
  k.urls.map { |x|
519
- return k, $~[1..-1] if path =~ /^#{x}\/?$/
506
+ return (k.instance_method(m) rescue nil) ?
507
+ [k, m, *$~[1..-1]] : [I, 'r501', m] if p =~ /^#{x}\/?$/
520
508
  }
521
509
  }
522
- [NotFound, [path]]
510
+ [I, 'r404', p]
523
511
  end
524
512
 
513
+ N = H.new { |_,x| x.downcase }.merge! "N" => '(\d+)', "X" => '(\w+)', "Index" => ''
525
514
  # The route maker, this is called by Camping internally, you shouldn't need to call it.
526
515
  #
527
516
  # Still, it's worth know what this method does. Since Ruby doesn't keep track of class
@@ -536,66 +525,15 @@ module Camping
536
525
  end
537
526
  constants.map { |c|
538
527
  k=const_get(c)
539
- k.send :include,C,Base,Models
540
- r[0,0]=k if !r.include?k
541
- k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls
528
+ k.send :include,C,Base,Helpers,Models
529
+ @r=[k]+r if r-[k]==r
530
+ k.meta_def(:urls){["/#{c.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls
542
531
  }
543
532
  end
544
533
  end
545
534
 
546
- # The NotFound class is a special controller class for handling 404 errors, in case you'd
547
- # like to alter the appearance of the 404. The path is passed in as +p+.
548
- #
549
- # module Camping::Controllers
550
- # class NotFound
551
- # def get(p)
552
- # @status = 404
553
- # div do
554
- # h1 'Camping Problem!'
555
- # h2 "#{p} not found"
556
- # end
557
- # end
558
- # end
559
- # end
560
- #
561
- class NotFound < R()
562
- def get(p)
563
- r(404, Mab.new{h1(P);h2("#{p} not found")})
564
- end
565
- end
566
-
567
- # The ServerError class is a special controller class for handling many (but not all) 500 errors.
568
- # If there is a parse error in Camping or in your application's source code, it will not be caught
569
- # by Camping. The controller class +k+ and request method +m+ (GET, POST, etc.) where the error
570
- # took place are passed in, along with the Exception +e+ which can be mined for useful info.
571
- #
572
- # module Camping::Controllers
573
- # class ServerError
574
- # def get(k,m,e)
575
- # @status = 500
576
- # div do
577
- # h1 'Camping Problem!'
578
- # h2 "in #{k}.#{m}"
579
- # h3 "#{e.class} #{e.message}:"
580
- # ul do
581
- # e.backtrace.each do |bt|
582
- # li bt
583
- # end
584
- # end
585
- # end
586
- # end
587
- # end
588
- # end
589
- #
590
- class ServerError < R()
591
- def get(k,m,e)
592
- r(500, Mab.new {
593
- h1(P)
594
- h2 "#{k}.#{m}"
595
- h3 "#{e.class} #{e.message}:"
596
- ul { e.backtrace.each { |bt| li bt } }
597
- }.to_s)
598
- end
535
+ # Internal controller with no route. Used by #D and C.call to show internal messages.
536
+ class I < R()
599
537
  end
600
538
  end
601
539
  X = Controllers
@@ -613,74 +551,18 @@ module Camping
613
551
  # module Blog::Views; ... end
614
552
  #
615
553
  def goes(m)
616
- eval S.gsub(/Camping/,m.to_s).gsub("A\pps = []","Cam\ping::Apps<<self"), TOPLEVEL_BINDING
554
+ Apps << eval(S.gsub(/Camping/,m.to_s), TOPLEVEL_BINDING)
617
555
  end
618
-
619
- # URL escapes a string.
620
- #
621
- # Camping.escape("I'd go to the museum straightway!")
622
- # #=> "I%27d+go+to+the+museum+straightway%21"
623
- #
624
- def escape(s); s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ', '+') end
625
-
626
- # Unescapes a URL-encoded string.
627
- #
628
- # Camping.un("I%27d+go+to+the+museum+straightway%21")
629
- # #=> "I'd go to the museum straightway!"
630
- #
631
- def un(s); s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')} end
632
-
633
- # Parses a query string into an Camping::H object.
634
- #
635
- # input = Camping.qsp("name=Philarp+Tremain&hair=sandy+blonde")
636
- # input.name
637
- # #=> "Philarp Tremaine"
638
- #
639
- # Also parses out the Hash-like syntax used in PHP and Rails and builds
640
- # nested hashes from it.
641
- #
642
- # input = Camping.qsp("post[id]=1&post[user]=_why")
643
- # #=> {'post' => {'id' => '1', 'user' => '_why'}}
644
- #
645
- def qsp(qs, d='&;', y=nil, z=H[])
646
- m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
647
- (qs||'').
648
- split(/[#{d}] */n).
649
- inject((b,z=z,H[])[0]) { |h,p| k, v=un(p).split('=',2)
650
- h.u(k.split(/[\]\[]+/).reverse.
651
- inject(y||v) { |x,i| H[i,x] },&m)
652
- }
653
- end
654
-
655
- # Parses a string of cookies from the <tt>Cookie</tt> header.
656
- def kp(s); c = qsp(s, ';,'); end
657
-
658
- # Fields a request through Camping. For traditional CGI applications, the method can be
659
- # executed without arguments.
660
- #
661
- # if __FILE__ == $0
662
- # Camping::Models::Base.establish_connection :adapter => 'sqlite3',
663
- # :database => 'blog3.db'
664
- # Camping::Models::Base.logger = Logger.new('camping.log')
665
- # puts Camping.run
666
- # end
667
- #
668
- # The Camping controller returned from <tt>run</tt> has a <tt>to_s</tt> method in case you
669
- # are running from CGI or want to output the full HTTP output. In the above example, <tt>puts</tt>
670
- # will call <tt>to_s</tt> for you.
671
- #
672
- # For FastCGI and Webrick-loaded applications, you will need to use a request loop, with <tt>run</tt>
673
- # at the center, passing in the read +r+ and write +w+ streams. You will also need to mimick or
674
- # pass in the <tt>ENV</tt> replacement as part of your wrapper.
675
- #
676
- # See Camping::FastCGI and Camping::WEBrick for examples.
677
- #
678
- def run(r=$stdin,e=ENV)
556
+
557
+ # Ruby web servers use this method to enter the Camping realm. The e
558
+ # argument is the environment variables hash as per the Rack specification.
559
+ # And array with [statuc, headers, body] is expected at the output.
560
+ def call(e)
679
561
  X.M
680
- k,a=X.D un("/#{e['PATH_INFO']}".gsub(/\/+/,'/'))
681
- k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).Y.service *a
682
- rescue Object=>x
683
- X::ServerError.new(r,e,'get').service(k,m,x)
562
+ e = H[e.to_hash]
563
+ k,m,*a=X.D e.PATH_INFO,(e.REQUEST_METHOD||'get').downcase
564
+ e.REQUEST_METHOD = m
565
+ k.new(e).service(*a).to_a
684
566
  end
685
567
 
686
568
  # The Camping scriptable dispatcher. Any unhandled method call to the app module will
@@ -698,18 +580,28 @@ module Camping
698
580
  # Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
699
581
  # #=> #<Blog::Controllers::Login @user=... >
700
582
  #
701
- # Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'})
702
- # #=> #<Blog::Controllers::Info @env={'HTTP_HOST'=>'wagon'} ...>
583
+ # Blog.get(:Info, :env => {'HTTP_HOST' => 'wagon'})
584
+ # #=> #<Blog::Controllers::Info @headers={'HTTP_HOST'=>'wagon'} ...>
703
585
  #
704
586
  def method_missing(m, c, *a)
705
587
  X.M
706
- k = X.const_get(c).new(StringIO.new,
707
- H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s)
708
- H.new(a.pop).each { |e,f| k.send("#{e}=",f) } if Hash === a[-1]
709
- k.service *a
588
+ h=Hash===a[-1]?H[a.pop]:{}
589
+ e=H[h[:env]||{}].merge!({'rack.input'=>StringIO.new,'REQUEST_METHOD'=>m.to_s})
590
+ k = X.const_get(c).new(H[e])
591
+ k.send("input=",h[:input]) if h[:input]
592
+ k.service(*a)
710
593
  end
711
594
  end
712
595
 
596
+ # Views is an empty module for storing methods which create HTML. The HTML is described
597
+ # using the Markaby language.
598
+ #
599
+ # == Using the layout method
600
+ #
601
+ # If your Views module has a <tt>layout</tt> method defined, it will be called with a block
602
+ # which will insert content from your view.
603
+ module Views; include X, Helpers end
604
+
713
605
  # Models is an empty Ruby module for housing model classes derived
714
606
  # from ActiveRecord::Base. As a shortcut, you may derive from Base
715
607
  # which is an alias for ActiveRecord::Base.
@@ -736,29 +628,11 @@ module Camping
736
628
  #
737
629
  # Models cannot be referred to in Views at this time.
738
630
  module Models
739
- autoload :Base,'camping/db'
631
+ autoload :Base,'camping/ar'
740
632
  def Y;self;end
741
633
  end
742
-
743
- # Views is an empty module for storing methods which create HTML. The HTML is described
744
- # using the Markaby language.
745
- #
746
- # == Using the layout method
747
- #
748
- # If your Views module has a <tt>layout</tt> method defined, it will be called with a block
749
- # which will insert content from your view.
750
- module Views; include Controllers, Helpers end
751
-
752
- # The Mab class wraps Markaby, allowing it to run methods from Camping::Views
753
- # and also to replace :href, :action and :src attributes in tags by prefixing the root
754
- # path.
755
- class Mab < Markaby::Builder
756
- include Views
757
- def tag!(*g,&b)
758
- h=g[-1]
759
- [:href,:action,:src].each{|a|(h[a]=self/h[a])rescue 0}
760
- super
761
- end
762
- end
634
+
635
+ autoload :Mab, 'camping/mab'
636
+ C
763
637
  end
764
638