picnic 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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