rubycas-server 0.5.1 → 0.6.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.
@@ -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
-