rubycas-server 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,117 +0,0 @@
1
- require 'rake'
2
- require 'rake/clean'
3
- require 'rake/gempackagetask'
4
- require 'rake/rdoctask'
5
- require 'rake/testtask'
6
- require 'fileutils'
7
- include FileUtils
8
-
9
- NAME = "camping"
10
- REV = File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
11
- VERS = ENV['VERSION'] || ("1.5" + (REV ? ".#{REV}" : ""))
12
- CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log']
13
- RDOC_OPTS = ['--quiet', '--title', "Camping, the Documentation",
14
- "--opname", "index.html",
15
- "--line-numbers",
16
- "--main", "README",
17
- "--inline-source"]
18
-
19
- desc "Packages up Camping."
20
- task :default => [:package]
21
- task :package => [:clean]
22
-
23
- task :doc => [:before_doc, :rdoc, :after_doc]
24
-
25
- task :before_doc do
26
- mv "lib/camping.rb", "lib/camping-mural.rb"
27
- mv "lib/camping-unabridged.rb", "lib/camping.rb"
28
- end
29
-
30
- Rake::RDocTask.new do |rdoc|
31
- rdoc.rdoc_dir = 'doc/rdoc'
32
- rdoc.options += RDOC_OPTS
33
- rdoc.template = "extras/flipbook_rdoc.rb"
34
- rdoc.main = "README"
35
- rdoc.title = "Camping, the Documentation"
36
- rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/camping.rb', 'lib/camping/*.rb']
37
- end
38
-
39
- task :after_doc do
40
- mv "lib/camping.rb", "lib/camping-unabridged.rb"
41
- mv "lib/camping-mural.rb", "lib/camping.rb"
42
- cp "extras/Camping.gif", "doc/rdoc/"
43
- cp "extras/permalink.gif", "doc/rdoc/"
44
- sh %{scp -r doc/rdoc/* #{ENV['USER']}@rubyforge.org:/var/www/gforge-projects/camping/}
45
- end
46
-
47
- spec =
48
- Gem::Specification.new do |s|
49
- s.name = NAME
50
- s.version = VERS
51
- s.platform = Gem::Platform::RUBY
52
- s.has_rdoc = true
53
- s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
54
- s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
55
- s.summary = "minature rails for stay-at-home moms"
56
- s.description = s.summary
57
- s.author = "why the lucky stiff"
58
- s.email = 'why@ruby-lang.org'
59
- s.homepage = 'http://code.whytheluckystiff.net/camping/'
60
- s.executables = ['camping']
61
-
62
- s.add_dependency('activesupport', '>=1.3.1')
63
- s.add_dependency('markaby', '>=0.5')
64
- s.add_dependency('metaid')
65
- s.required_ruby_version = '>= 1.8.2'
66
-
67
- s.files = %w(COPYING README Rakefile) +
68
- Dir.glob("{bin,doc,test,lib,extras}/**/*") +
69
- Dir.glob("ext/**/*.{h,c,rb}") +
70
- Dir.glob("examples/**/*.rb") +
71
- Dir.glob("tools/*.rb")
72
-
73
- s.require_path = "lib"
74
- # s.extensions = FileList["ext/**/extconf.rb"].to_a
75
- s.bindir = "bin"
76
- end
77
-
78
- omni =
79
- Gem::Specification.new do |s|
80
- s.name = "camping-omnibus"
81
- s.version = VERS
82
- s.platform = Gem::Platform::RUBY
83
- s.summary = "the camping meta-package for updating ActiveRecord, Mongrel and SQLite3 bindings"
84
- s.description = s.summary
85
- %w[author email homepage].each { |x| s.__send__("#{x}=", spec.__send__(x)) }
86
-
87
- s.add_dependency('camping', "=#{VERS}")
88
- s.add_dependency('activerecord')
89
- s.add_dependency('sqlite3-ruby', '>=1.1.0.1')
90
- s.add_dependency('mongrel')
91
- s.add_dependency('acts_as_versioned')
92
- s.add_dependency('RedCloth')
93
- end
94
-
95
- Rake::GemPackageTask.new(spec) do |p|
96
- p.need_tar = true
97
- p.gem_spec = spec
98
- end
99
-
100
- Rake::GemPackageTask.new(omni) do |p|
101
- p.gem_spec = omni
102
- end
103
-
104
- task :install do
105
- sh %{rake package}
106
- sh %{sudo gem install pkg/#{NAME}-#{VERS}}
107
- end
108
-
109
- task :uninstall => [:clean] do
110
- sh %{sudo gem uninstall #{NAME}}
111
- end
112
-
113
- Rake::TestTask.new(:test) do |t|
114
- t.test_files = FileList['test/test_*.rb']
115
- # t.warning = true
116
- # t.verbose = true
117
- end
@@ -1,762 +0,0 @@
1
- # == About camping.rb
2
- #
3
- # Camping comes with two versions of its source code. The code contained in
4
- # lib/camping.rb is compressed, stripped of whitespace, using compact algorithms
5
- # to keep it tight. The unspoken rule is that camping.rb should be flowed with
6
- # no more than 80 characters per line and must not exceed four kilobytes.
7
- #
8
- # On the other hand, lib/camping-unabridged.rb contains the same code, laid out
9
- # nicely with piles of documentation everywhere. This documentation is entirely
10
- # generated from lib/camping-unabridged.rb using RDoc and our "flipbook" template
11
- # found in the extras directory of any camping distribution.
12
- #
13
- # == Requirements
14
- #
15
- # Camping requires at least Ruby 1.8.2.
16
- #
17
- # Camping depends on the following libraries. If you install through RubyGems,
18
- # these will be automatically installed for you.
19
- #
20
- # * ActiveRecord, used in your models.
21
- # ActiveRecord is an object-to-relational database mapper with adapters
22
- # for SQLite3, MySQL, PostgreSQL, SQL Server and more.
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
- #
27
- # Camping also works well with Mongrel, the swift Ruby web server.
28
- # http://rubyforge.org/projects/mongrel Mongrel comes with examples
29
- # in its <tt>examples/camping</tt> directory.
30
- #
31
- %w[active_support markaby tempfile uri].each { |lib| require lib }
32
-
33
- # == Camping
34
- #
35
- # The camping module contains three modules for separating your application:
36
- #
37
- # * Camping::Models for your database interaction classes, all derived from ActiveRecord::Base.
38
- # * Camping::Controllers for storing controller classes, which map URLs to code.
39
- # * Camping::Views for storing methods which generate HTML.
40
- #
41
- # Of use to you is also one module for storing helpful additional methods:
42
- #
43
- # * Camping::Helpers which can be used in controllers and views.
44
- #
45
- # == The Camping Server
46
- #
47
- # How do you run Camping apps? Oh, uh... The Camping Server!
48
- #
49
- # The Camping Server is, firstly and thusly, a set of rules. At the very least, The Camping Server must:
50
- #
51
- # * Load all Camping apps in a directory.
52
- # * Load new apps that appear in that directory.
53
- # * Mount those apps according to their filename. (e.g. blog.rb is mounted at /blog.)
54
- # * Run each app's <tt>create</tt> method upon startup.
55
- # * Reload the app if its modification time changes.
56
- # * Reload the app if it requires any files under the same directory and one of their modification times changes.
57
- # * Support the X-Sendfile header.
58
- #
59
- # In fact, Camping comes with its own little The Camping Server.
60
- #
61
- # At a command prompt, run: <tt>camping examples/</tt> and the entire <tt>examples/</tt> directory will be served.
62
- #
63
- # Configurations also exist for Apache and Lighttpd. See http://code.whytheluckystiff.net/camping/wiki/TheCampingServer.
64
- #
65
- # == The <tt>create</tt> method
66
- #
67
- # Many postambles will check for your application's <tt>create</tt> method and will run it
68
- # when the web server starts up. This is a good place to check for database tables and create
69
- # those tables to save users of your application from needing to manually set them up.
70
- #
71
- # def Blog.create
72
- # unless Blog::Models::Post.table_exists?
73
- # ActiveRecord::Schema.define do
74
- # create_table :blog_posts, :force => true do |t|
75
- # t.column :id, :integer, :null => false
76
- # t.column :user_id, :integer, :null => false
77
- # t.column :title, :string, :limit => 255
78
- # t.column :body, :text
79
- # end
80
- # end
81
- # end
82
- # end
83
- #
84
- # For more tips, see http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
85
- 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
- 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.
100
- # All Camping query string and cookie variables are loaded as this.
101
- #
102
- # To access the query string, for instance, use the <tt>@input</tt> variable.
103
- #
104
- # module Blog::Models
105
- # class Index < R '/'
106
- # def get
107
- # if page = @input.page.to_i > 0
108
- # page -= 1
109
- # end
110
- # @posts = Post.find :all, :offset => page * 20, :limit => 20
111
- # render :index
112
- # end
113
- # end
114
- # end
115
- #
116
- # In the above example if you visit <tt>/?page=2</tt>, you'll get the second
117
- # page of twenty posts. You can also use <tt>@input[:page]</tt> or <tt>@input['page']</tt>
118
- # to get the value for the <tt>page</tt> query variable.
119
- #
120
- # 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
123
- # Gets or sets keys in the hash.
124
- #
125
- # @cookies.my_favorite = :macadamian
126
- # @cookies.my_favorite
127
- # => :macadamian
128
- #
129
- def method_missing(m,*a)
130
- m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
131
- end
132
- alias_method :u, :regular_update
133
- end
134
-
135
- # Helpers contains methods available in your controllers and views. You may add
136
- # methods of your own to this module, including many helper methods from Rails.
137
- # This is analogous to Rails' <tt>ApplicationHelper</tt> module.
138
- #
139
- # == Using ActionPack Helpers
140
- #
141
- # If you'd like to include helpers from Rails' modules, you'll need to look up the
142
- # helper module in the Rails documentation at http://api.rubyonrails.org/.
143
- #
144
- # For example, if you look up the <tt>ActionView::Helpers::FormHelper</tt> class,
145
- # you'll find that it's loaded from the <tt>action_view/helpers/form_helper.rb</tt>
146
- # file. You'll need to have the ActionPack gem installed for this to work.
147
- #
148
- # require 'action_view/helpers/form_helper.rb'
149
- #
150
- # # This example is unfinished.. soon..
151
- #
152
- module Helpers
153
- # From inside your controllers and views, you will often need to figure out
154
- # the route used to get to a certain controller +c+. Pass the controller class
155
- # and any arguments into the R method, a string containing the route will be
156
- # returned to you.
157
- #
158
- # Assuming you have a specific route in an edit controller:
159
- #
160
- # class Edit < R '/edit/(\d+)'
161
- #
162
- # A specific route to the Edit controller can be built with:
163
- #
164
- # R(Edit, 1)
165
- #
166
- # Which outputs: <tt>/edit/1</tt>.
167
- #
168
- # You may also pass in a model object and the ID of the object will be used.
169
- #
170
- # If a controller has many routes, the route will be selected if it is the
171
- # first in the routing list to have the right number of arguments.
172
- #
173
- # == Using R in the View
174
- #
175
- # Keep in mind that this route doesn't include the root path.
176
- # You will need to use <tt>/</tt> (the slash method above) in your controllers.
177
- # Or, go ahead and use the Helpers#URL method to build a complete URL for a route.
178
- #
179
- # However, in your views, the :href, :src and :action attributes automatically
180
- # pass through the slash method, so you are encouraged to use <tt>R</tt> or
181
- # <tt>URL</tt> in your views.
182
- #
183
- # module Blog::Views
184
- # def menu
185
- # div.menu! do
186
- # a 'Home', :href => URL()
187
- # a 'Profile', :href => "/profile"
188
- # a 'Logout', :href => R(Logout)
189
- # a 'Google', :href => 'http://google.com'
190
- # end
191
- # end
192
- # end
193
- #
194
- # Let's say the above example takes place inside an application mounted at
195
- # <tt>http://localhost:3301/frodo</tt> and that a controller named <tt>Logout</tt>
196
- # is assigned to route <tt>/logout</tt>. The HTML will come out as:
197
- #
198
- # <div id="menu">
199
- # <a href="//localhost:3301/frodo/">Home</a>
200
- # <a href="/frodo/profile">Profile</a>
201
- # <a href="/frodo/logout">Logout</a>
202
- # <a href="http://google.com">Google</a>
203
- # </div>
204
- #
205
- def R(c,*g)
206
- 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}*"="}*"&": "")
210
- end
211
-
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
- # Simply builds a complete path from a path +p+ within the app. If your application is
232
- # mounted at <tt>/blog</tt>:
233
- #
234
- # self / "/view/1" #=> "/blog/view/1"
235
- # self / "styles.css" #=> "styles.css"
236
- # self / R(Edit, 1) #=> "/blog/edit/1"
237
- #
238
- def /(p); p[/^\//]?@root+p:p end
239
- # Builds a URL route to a controller or a path, returning a URI object.
240
- # This way you'll get the hostname and the port number, a complete URL.
241
- # No scheme is given (http or https).
242
- #
243
- # You can use this to grab URLs for controllers using the R-style syntax.
244
- # So, if your application is mounted at <tt>http://test.ing/blog/</tt>
245
- # and you have a View controller which routes as <tt>R '/view/(\d+)'</tt>:
246
- #
247
- # URL(View, @post.id) #=> #<URL://test.ing/blog/view/12>
248
- #
249
- # Or you can use the direct path:
250
- #
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"
258
- #
259
- # It's okay to pass URL strings through this method as well:
260
- #
261
- # URL("http://google.com") #=> #<URI:http://google.com>
262
- #
263
- # Any string which doesn't begin with a slash will pass through
264
- # unscathed.
265
- def URL c='/',*a
266
- c = R(c, *a) if c.respond_to? :urls
267
- c = self/c
268
- c = "//"+@env.HTTP_HOST+c if c[/^\//]
269
- URI(c)
270
- end
271
- end
272
-
273
- # Camping::Base is built into each controller by way of the generic routing
274
- # class Camping::R. In some ways, this class is trying to do too much, but
275
- # it saves code for all the glue to stay in one place.
276
- #
277
- # Forgivable, considering that it's only really a handful of methods and accessors.
278
- #
279
- # == Treating controller methods like Response objects
280
- #
281
- # Camping originally came with a barebones Response object, but it's often much more readable
282
- # to just use your controller as the response.
283
- #
284
- # Go ahead and alter the status, cookies, headers and body instance variables as you
285
- # see fit in order to customize the response.
286
- #
287
- # module Camping::Controllers
288
- # class SoftLink
289
- # def get
290
- # redirect "/"
291
- # end
292
- # end
293
- # end
294
- #
295
- # Is equivalent to:
296
- #
297
- # module Camping::Controllers
298
- # class SoftLink
299
- # def get
300
- # @status = 302
301
- # @headers['Location'] = "/"
302
- # end
303
- # end
304
- # end
305
- #
306
- module Base
307
- include Helpers
308
- attr_accessor :input, :cookies, :env, :headers, :body, :status, :root
309
- Z = "\r\n"
310
-
311
- # Display a view, calling it by its method name +m+. If a <tt>layout</tt>
312
- # method is found in Camping::Views, it will be used to wrap the HTML.
313
- #
314
- # module Camping::Controllers
315
- # class Show
316
- # def get
317
- # @posts = Post.find :all
318
- # render :index
319
- # end
320
- # end
321
- # end
322
- #
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.
327
- #
328
- # module Camping::Controllers
329
- # class Info
330
- # def get; code @env.inspect end
331
- # end
332
- # end
333
- #
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
338
- 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
341
- s
342
- end
343
-
344
- # Formulate a redirect response: a 302 status with <tt>Location</tt> header
345
- # and a blank body. Uses Helpers#URL to build the location from a controller
346
- # route or path.
347
- #
348
- # So, given a root of <tt>http://localhost:3301/articles</tt>:
349
- #
350
- # redirect "view/12" # redirects to "//localhost:3301/articles/view/12"
351
- # redirect View, 12 # redirects to "//localhost:3301/articles/view/12"
352
- #
353
- # <b>NOTE:</b> This method doesn't magically exit your methods and redirect.
354
- # You'll need to <tt>return redirect(...)</tt> if this isn't the last statement
355
- # in your code.
356
- def redirect(*a)
357
- r(302,'','Location'=>URL(*a))
358
- end
359
-
360
- # A quick means of setting this controller's status, body and headers.
361
- # Used internally by Camping, but... by all means...
362
- #
363
- # r(302, '', 'Location' => self / "/view/12")
364
- #
365
- # Is equivalent to:
366
- #
367
- # redirect "/view/12"
368
- #
369
- def r(s, b, h = {}); @status = s; @headers.merge!(h); @body = b; end
370
-
371
- # Turn a controller into an array. This is designed to be used to pipe
372
- # controllers into the <tt>r</tt> method. A great way to forward your
373
- # requests!
374
- #
375
- # class Read < '/(\d+)'
376
- # def get(id)
377
- # Post.find(id)
378
- # rescue
379
- # r *Blog.get(:NotFound, @env.REQUEST_URI)
380
- # end
381
- # 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
423
- end
424
- elsif @method == "post"
425
- qs.merge!(C.qsp(@in.read))
426
- end
427
- @cookies, @input = @k.dup, qs.dup
428
- end
429
-
430
- # All requests pass through this method before going to the controller. Some magic
431
- # in Camping can be performed by overriding this method.
432
- #
433
- # See http://code.whytheluckystiff.net/camping/wiki/BeforeAndAfterOverrides for more
434
- # on before and after overrides with Camping.
435
- def service(*a)
436
- @body = send(@method, *a) if respond_to? @method
437
- @headers['Set-Cookie'] = @cookies.map { |k,v| "#{k}=#{C.escape(v)}; path=#{self/"/"}" if v != @k[k] } - [nil]
438
- self
439
- end
440
-
441
- # Used by the web server to convert the current request to a string. If you need to
442
- # alter the way Camping builds HTTP headers, consider overriding this method.
443
- def to_s
444
- a=[]
445
- @headers.map{|k,v|[*v].map{|x|a<<"#{k}: #{x}"}}
446
- "Status: #{@status}#{Z+a*Z+Z*2+@body}"
447
- end
448
-
449
- end
450
-
451
- # Controllers is a module for placing classes which handle URLs. This is done
452
- # by defining a route to each class using the Controllers::R method.
453
- #
454
- # module Camping::Controllers
455
- # class Edit < R '/edit/(\d+)'
456
- # def get; end
457
- # def post; end
458
- # end
459
- # end
460
- #
461
- # If no route is set, Camping will guess the route from the class name.
462
- # The rule is very simple: the route becomes a slash followed by the lowercased
463
- # class name. See Controllers::D for the complete rules of dispatch.
464
- #
465
- # == Special classes
466
- #
467
- # There are two special classes used for handling 404 and 500 errors. The
468
- # NotFound class handles URLs not found. The ServerError class handles exceptions
469
- # uncaught by your application.
470
- module Controllers
471
- @r = []
472
- class << self
473
- def r #:nodoc:
474
- @r
475
- end
476
- # Add routes to a controller class by piling them into the R method.
477
- #
478
- # module Camping::Controllers
479
- # class Edit < R '/edit/(\d+)', '/new'
480
- # def get(id)
481
- # if id # edit
482
- # else # new
483
- # end
484
- # end
485
- # end
486
- # end
487
- #
488
- # You will need to use routes in either of these cases:
489
- #
490
- # * You want to assign multiple routes to a controller.
491
- # * You want your controller to receive arguments.
492
- #
493
- # Most of the time the rules inferred by dispatch method Controllers::D will get you
494
- # by just fine.
495
- def R *u
496
- r=@r
497
- Class.new {
498
- meta_def(:urls){u}
499
- meta_def(:inherited){|x|r<<x}
500
- }
501
- end
502
-
503
- # Dispatch routes to controller classes.
504
- # For each class, routes are checked for a match based on their order in the routing list
505
- # given to Controllers::R. If no routes were given, the dispatcher uses a slash followed
506
- # by the name of the controller lowercased.
507
- #
508
- # Controllers are searched in this order:
509
- #
510
- # # Classes without routes, since they refer to a very specific URL.
511
- # # Classes with routes are searched in order of their creation.
512
- #
513
- # So, define your catch-all controllers last.
514
- def D(path)
515
- r.map { |k|
516
- k.urls.map { |x|
517
- return k, $~[1..-1] if path =~ /^#{x}\/?$/
518
- }
519
- }
520
- [NotFound, [path]]
521
- end
522
-
523
- # The route maker, this is called by Camping internally, you shouldn't need to call it.
524
- #
525
- # Still, it's worth know what this method does. Since Ruby doesn't keep track of class
526
- # creation order, we're keeping an internal list of the controllers which inherit from R().
527
- # This method goes through and adds all the remaining routes to the beginning of the list
528
- # and ensures all the controllers have the right mixins.
529
- #
530
- # Anyway, if you are calling the URI dispatcher from outside of a Camping server, you'll
531
- # definitely need to call this at least once to set things up.
532
- def M
533
- def M #:nodoc:
534
- end
535
- constants.map { |c|
536
- k=const_get(c)
537
- k.send :include,C,Base,Models
538
- r[0,0]=k if !r.include?k
539
- k.meta_def(:urls){["/#{c.downcase}"]}if !k.respond_to?:urls
540
- }
541
- end
542
- end
543
-
544
- # The NotFound class is a special controller class for handling 404 errors, in case you'd
545
- # like to alter the appearance of the 404. The path is passed in as +p+.
546
- #
547
- # module Camping::Controllers
548
- # class NotFound
549
- # def get(p)
550
- # @status = 404
551
- # div do
552
- # h1 'Camping Problem!'
553
- # h2 "#{p} not found"
554
- # end
555
- # end
556
- # end
557
- # end
558
- #
559
- class NotFound < R()
560
- def get(p)
561
- r(404, Mab.new{h1(P);h2("#{p} not found")})
562
- end
563
- end
564
-
565
- # The ServerError class is a special controller class for handling many (but not all) 500 errors.
566
- # If there is a parse error in Camping or in your application's source code, it will not be caught
567
- # by Camping. The controller class +k+ and request method +m+ (GET, POST, etc.) where the error
568
- # took place are passed in, along with the Exception +e+ which can be mined for useful info.
569
- #
570
- # module Camping::Controllers
571
- # class ServerError
572
- # def get(k,m,e)
573
- # @status = 500
574
- # div do
575
- # h1 'Camping Problem!'
576
- # h2 "in #{k}.#{m}"
577
- # h3 "#{e.class} #{e.message}:"
578
- # ul do
579
- # e.backtrace.each do |bt|
580
- # li bt
581
- # end
582
- # end
583
- # end
584
- # end
585
- # end
586
- # end
587
- #
588
- class ServerError < R()
589
- def get(k,m,e)
590
- r(500, Mab.new {
591
- h1(P)
592
- h2 "#{k}.#{m}"
593
- h3 "#{e.class} #{e.message}:"
594
- ul { e.backtrace.each { |bt| li bt } }
595
- }.to_s)
596
- end
597
- end
598
- end
599
- X = Controllers
600
-
601
- class << self
602
- # When you are running many applications, you may want to create independent
603
- # modules for each Camping application. Namespaces for each. Camping::goes
604
- # defines a toplevel constant with the whole MVC rack inside.
605
- #
606
- # require 'camping'
607
- # Camping.goes :Blog
608
- #
609
- # module Blog::Controllers; ... end
610
- # module Blog::Models; ... end
611
- # module Blog::Views; ... end
612
- #
613
- def goes(m)
614
- eval S.gsub(/Camping/,m.to_s).gsub("A\pps = []","Cam\ping::Apps<<self"), TOPLEVEL_BINDING
615
- end
616
-
617
- # URL escapes a string.
618
- #
619
- # Camping.escape("I'd go to the museum straightway!")
620
- # #=> "I%27d+go+to+the+museum+straightway%21"
621
- #
622
- def escape(s); s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ', '+') end
623
-
624
- # Unescapes a URL-encoded string.
625
- #
626
- # Camping.un("I%27d+go+to+the+museum+straightway%21")
627
- # #=> "I'd go to the museum straightway!"
628
- #
629
- def un(s); s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')} end
630
-
631
- # Parses a query string into an Camping::H object.
632
- #
633
- # input = Camping.qsp("name=Philarp+Tremain&hair=sandy+blonde")
634
- # input.name
635
- # #=> "Philarp Tremaine"
636
- #
637
- # Also parses out the Hash-like syntax used in PHP and Rails and builds
638
- # nested hashes from it.
639
- #
640
- # input = Camping.qsp("post[id]=1&post[user]=_why")
641
- # #=> {'post' => {'id' => '1', 'user' => '_why'}}
642
- #
643
- def qsp(qs, d='&;', y=nil, z=H[])
644
- m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
645
- (qs||'').
646
- split(/[#{d}] */n).
647
- inject((b,z=z,H[])[0]) { |h,p| k, v=un(p).split('=',2)
648
- h.u(k.split(/[\]\[]+/).reverse.
649
- inject(y||v) { |x,i| H[i,x] },&m)
650
- }
651
- end
652
-
653
- # Parses a string of cookies from the <tt>Cookie</tt> header.
654
- def kp(s); c = qsp(s, ';,'); end
655
-
656
- # Fields a request through Camping. For traditional CGI applications, the method can be
657
- # executed without arguments.
658
- #
659
- # if __FILE__ == $0
660
- # Camping::Models::Base.establish_connection :adapter => 'sqlite3',
661
- # :database => 'blog3.db'
662
- # Camping::Models::Base.logger = Logger.new('camping.log')
663
- # puts Camping.run
664
- # end
665
- #
666
- # The Camping controller returned from <tt>run</tt> has a <tt>to_s</tt> method in case you
667
- # are running from CGI or want to output the full HTTP output. In the above example, <tt>puts</tt>
668
- # will call <tt>to_s</tt> for you.
669
- #
670
- # For FastCGI and Webrick-loaded applications, you will need to use a request loop, with <tt>run</tt>
671
- # at the center, passing in the read +r+ and write +w+ streams. You will also need to mimick or
672
- # pass in the <tt>ENV</tt> replacement as part of your wrapper.
673
- #
674
- # See Camping::FastCGI and Camping::WEBrick for examples.
675
- #
676
- def run(r=$stdin,e=ENV)
677
- X.M
678
- k,a=X.D un("/#{e['PATH_INFO']}".gsub(/\/+/,'/'))
679
- k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).Y.service *a
680
- rescue Object=>x
681
- X::ServerError.new(r,e,'get').service(k,m,x)
682
- end
683
-
684
- # The Camping scriptable dispatcher. Any unhandled method call to the app module will
685
- # be sent to a controller class, specified as an argument.
686
- #
687
- # Blog.get(:Index)
688
- # #=> #<Blog::Controllers::Index ... >
689
- #
690
- # The controller object contains all the @cookies, @body, @headers, etc. formulated by
691
- # the response.
692
- #
693
- # You can also feed environment variables and query variables as a hash, the final
694
- # argument.
695
- #
696
- # Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
697
- # #=> #<Blog::Controllers::Login @user=... >
698
- #
699
- # Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'})
700
- # #=> #<Blog::Controllers::Info @env={'HTTP_HOST'=>'wagon'} ...>
701
- #
702
- def method_missing(m, c, *a)
703
- X.M
704
- k = X.const_get(c).new(StringIO.new,
705
- H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s)
706
- H.new(a.pop).each { |e,f| k.send("#{e}=",f) } if Hash === a[-1]
707
- k.service *a
708
- end
709
- end
710
-
711
- # Models is an empty Ruby module for housing model classes derived
712
- # from ActiveRecord::Base. As a shortcut, you may derive from Base
713
- # which is an alias for ActiveRecord::Base.
714
- #
715
- # module Camping::Models
716
- # class Post < Base; belongs_to :user end
717
- # class User < Base; has_many :posts end
718
- # end
719
- #
720
- # == Where Models are Used
721
- #
722
- # Models are used in your controller classes. However, if your model class
723
- # name conflicts with a controller class name, you will need to refer to it
724
- # using the Models module.
725
- #
726
- # module Camping::Controllers
727
- # class Post < R '/post/(\d+)'
728
- # def get(post_id)
729
- # @post = Models::Post.find post_id
730
- # render :index
731
- # end
732
- # end
733
- # end
734
- #
735
- # Models cannot be referred to in Views at this time.
736
- module Models
737
- autoload :Base,'camping/db'
738
- def Y;self;end
739
- end
740
-
741
- # Views is an empty module for storing methods which create HTML. The HTML is described
742
- # using the Markaby language.
743
- #
744
- # == Using the layout method
745
- #
746
- # If your Views module has a <tt>layout</tt> method defined, it will be called with a block
747
- # which will insert content from your view.
748
- module Views; include Controllers, Helpers end
749
-
750
- # The Mab class wraps Markaby, allowing it to run methods from Camping::Views
751
- # and also to replace :href, :action and :src attributes in tags by prefixing the root
752
- # path.
753
- class Mab < Markaby::Builder
754
- include Views
755
- def tag!(*g,&b)
756
- h=g[-1]
757
- [:href,:action,:src].each{|a|(h[a]=self/h[a])rescue 0}
758
- super
759
- end
760
- end
761
- end
762
-