camping 2.1.532 → 3.0.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.
- checksums.yaml +7 -0
- data/README.md +72 -53
- data/Rakefile +25 -20
- data/bin/camping +1 -0
- data/book/01_introduction.md +6 -6
- data/book/02_getting_started.md +348 -267
- data/book/03_more_about_controllers.md +124 -0
- data/book/04_more_about_views.md +118 -0
- data/book/05_more_about_markaby.md +173 -0
- data/book/06_more_about_models.md +58 -0
- data/book/06_rules_of_thumb.md +143 -0
- data/book/07_philosophy.md +23 -0
- data/book/08_publishing_an_app.md +118 -0
- data/book/09_upgrade_notes.md +96 -0
- data/book/10_middleware.md +69 -0
- data/book/11_gear.md +50 -0
- data/examples/blog.rb +38 -38
- data/lib/camping/ar.rb +20 -5
- data/lib/camping/commands.rb +388 -0
- data/lib/camping/gear/filters.rb +48 -0
- data/lib/camping/gear/inspection.rb +32 -0
- data/lib/camping/gear/kuddly.rb +178 -0
- data/lib/camping/gear/nancy.rb +170 -0
- data/lib/camping/loads.rb +15 -0
- data/lib/camping/mab.rb +1 -1
- data/lib/camping/reloader.rb +22 -17
- data/lib/camping/server.rb +145 -70
- data/lib/camping/session.rb +8 -5
- data/lib/camping/template.rb +1 -2
- data/lib/camping/tools.rb +43 -0
- data/lib/camping/version.rb +6 -0
- data/lib/camping-unabridged.rb +360 -133
- data/lib/camping.rb +78 -47
- data/lib/campingtrip.md +341 -0
- data/test/app_camping_gear.rb +121 -0
- data/test/app_camping_tools.rb +1 -0
- data/test/app_config.rb +30 -0
- data/test/app_cookies.rb +1 -1
- data/test/app_file.rb +3 -3
- data/test/app_goes_meta.rb +23 -0
- data/test/app_inception.rb +39 -0
- data/test/app_markup.rb +5 -20
- data/test/app_migrations.rb +16 -0
- data/test/app_partials.rb +1 -1
- data/test/app_prefixed.rb +88 -0
- data/test/app_reloader.rb +1 -2
- data/test/app_route_generating.rb +69 -2
- data/test/app_sessions.rb +24 -2
- data/test/app_simple.rb +18 -18
- data/test/apps/migrations.rb +82 -82
- data/test/apps/misc.rb +1 -1
- data/test/gear/gear_nancy.rb +129 -0
- data/test/test_helper.rb +69 -12
- metadata +152 -92
- data/CHANGELOG +0 -145
- data/book/51_upgrading.md +0 -110
data/lib/camping-unabridged.rb
CHANGED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
# nicely with piles of documentation everywhere. This documentation is entirely
|
|
10
10
|
# generated from lib/camping-unabridged.rb using RDoc and our "flipbook" template
|
|
11
11
|
# found in the extras directory of any camping distribution.
|
|
12
|
-
require "
|
|
13
|
-
require "rack"
|
|
12
|
+
require "cam\ping/loads"
|
|
14
13
|
|
|
15
14
|
$LOADED_FEATURES << "camping.rb"
|
|
15
|
+
E ||= "content-type"
|
|
16
|
+
Z ||= "text/html"
|
|
16
17
|
|
|
17
18
|
class Object #:nodoc:
|
|
18
19
|
def meta_def(m,&b) #:nodoc:
|
|
@@ -22,7 +23,7 @@ end
|
|
|
22
23
|
|
|
23
24
|
# If you're new to Camping, you should probably start by reading the first
|
|
24
25
|
# chapters of {The Camping Book}[file:book/01_introduction.html#toc].
|
|
25
|
-
#
|
|
26
|
+
#
|
|
26
27
|
# Okay. So, the important thing to remember is that <tt>Camping.goes :Nuts</tt>
|
|
27
28
|
# copies the Camping module into Nuts. This means that you should never use
|
|
28
29
|
# any of these methods/classes on the Camping module, but rather on your own
|
|
@@ -37,10 +38,10 @@ end
|
|
|
37
38
|
#
|
|
38
39
|
# Camping also ships with:
|
|
39
40
|
#
|
|
40
|
-
# * Camping::Session adds states to your app.
|
|
41
|
+
# * Camping::Session adds states to your app.
|
|
41
42
|
# * Camping::Server starts up your app in development.
|
|
42
43
|
# * Camping::Reloader automatically reloads your apps when a file has changed.
|
|
43
|
-
#
|
|
44
|
+
#
|
|
44
45
|
# More importantly, Camping also installs The Camping Server,
|
|
45
46
|
# please see Camping::Server.
|
|
46
47
|
module Camping
|
|
@@ -48,12 +49,13 @@ module Camping
|
|
|
48
49
|
S = IO.read(__FILE__) rescue nil
|
|
49
50
|
P = "<h1>Cam\ping Problem!</h1><h2>%s</h2>"
|
|
50
51
|
U = Rack::Utils
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
Apps = [] # Our array of Apps
|
|
53
|
+
SK = "camping" #Key for r.session
|
|
54
|
+
G = [] # Our array of Gear
|
|
55
|
+
|
|
54
56
|
# An object-like Hash.
|
|
55
57
|
# All Camping query string and cookie variables are loaded as this.
|
|
56
|
-
#
|
|
58
|
+
#
|
|
57
59
|
# To access the query string, for instance, use the <tt>@input</tt> variable.
|
|
58
60
|
#
|
|
59
61
|
# module Blog::Controllers
|
|
@@ -84,6 +86,8 @@ module Camping
|
|
|
84
86
|
undef id, type if ?? == 63
|
|
85
87
|
end
|
|
86
88
|
|
|
89
|
+
O=H.new;O[:url_prefix]="" # Our Hash of Options
|
|
90
|
+
|
|
87
91
|
class Cookies < H
|
|
88
92
|
attr_accessor :_p
|
|
89
93
|
#
|
|
@@ -101,7 +105,7 @@ module Camping
|
|
|
101
105
|
set k, v, v.is_a?(Hash) ? v : {}
|
|
102
106
|
end
|
|
103
107
|
end
|
|
104
|
-
|
|
108
|
+
|
|
105
109
|
# Helpers contains methods available in your controllers and views. You may
|
|
106
110
|
# add methods of your own to this module, including many helper methods from
|
|
107
111
|
# Rails. This is analogous to Rails' <tt>ApplicationHelper</tt> module.
|
|
@@ -115,9 +119,9 @@ module Camping
|
|
|
115
119
|
# class, you'll find that it's loaded from the <tt>action_view/helpers/form_tag_helper.rb</tt>
|
|
116
120
|
# file. You'll need to have the ActionPack gem installed for this to work.
|
|
117
121
|
#
|
|
118
|
-
#
|
|
122
|
+
# A helper often depends on other helpers, so you would have to look up
|
|
119
123
|
# the dependencies too. <tt>FormTagHelper</tt> for instance required the
|
|
120
|
-
# <tt>content_tag</tt> provided by <tt>TagHelper</tt>.
|
|
124
|
+
# <tt>content_tag</tt> provided by <tt>TagHelper</tt>.
|
|
121
125
|
#
|
|
122
126
|
# require 'action_view/helpers/form_tag_helper'
|
|
123
127
|
#
|
|
@@ -127,7 +131,7 @@ module Camping
|
|
|
127
131
|
# end
|
|
128
132
|
#
|
|
129
133
|
# == Return a response immediately
|
|
130
|
-
# If you need to return a response inside a helper, you can use <tt>throw :halt</tt>.
|
|
134
|
+
# If you need to return a response inside a helper, you can use <tt>throw :halt</tt>.
|
|
131
135
|
#
|
|
132
136
|
# module Nuts::Helpers
|
|
133
137
|
# def requires_login!
|
|
@@ -137,7 +141,7 @@ module Camping
|
|
|
137
141
|
# end
|
|
138
142
|
# end
|
|
139
143
|
# end
|
|
140
|
-
#
|
|
144
|
+
#
|
|
141
145
|
# module Nuts::Controllers
|
|
142
146
|
# class Admin
|
|
143
147
|
# def get
|
|
@@ -203,9 +207,9 @@ module Camping
|
|
|
203
207
|
p,h=/\(.+?\)/,g.grep(Hash)
|
|
204
208
|
g-=h
|
|
205
209
|
raise "bad route" if !u = c.urls.find{|x|
|
|
206
|
-
break x if x.scan(p).size == g.size &&
|
|
210
|
+
break x if x.scan(p).size == g.size &&
|
|
207
211
|
/^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
|
|
208
|
-
x.sub p,U.escape((a.to_param rescue a))}.gsub(
|
|
212
|
+
x.sub p,U.escape((a.to_param rescue a))}.gsub(CampTools.descape){$1})
|
|
209
213
|
}
|
|
210
214
|
h.any?? u+"?"+U.build_query(h[0]) : u
|
|
211
215
|
end
|
|
@@ -217,8 +221,10 @@ module Camping
|
|
|
217
221
|
# self / "styles.css" #=> "styles.css"
|
|
218
222
|
# self / R(Edit, 1) #=> "/blog/edit/1"
|
|
219
223
|
#
|
|
220
|
-
def /(p)
|
|
221
|
-
|
|
224
|
+
def /(p)
|
|
225
|
+
p[0] == ?/ ? (@root + @url_prefix.dup.prepend("/").chop + p) : p
|
|
226
|
+
end
|
|
227
|
+
|
|
222
228
|
# Builds a URL route to a controller or a path, returning a URI object.
|
|
223
229
|
# This way you'll get the hostname and the port number, a complete URL.
|
|
224
230
|
#
|
|
@@ -243,9 +249,14 @@ module Camping
|
|
|
243
249
|
def URL c='/',*a
|
|
244
250
|
c = R(c, *a) if c.respond_to? :urls
|
|
245
251
|
c = self/c
|
|
246
|
-
c = @request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/
|
|
252
|
+
c = @request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/ #/
|
|
247
253
|
URI(c)
|
|
248
254
|
end
|
|
255
|
+
|
|
256
|
+
# Just a helper to tell you the App Name
|
|
257
|
+
# During the instantiation of the app, "Camping" is replaced with the Apps namespace.
|
|
258
|
+
def app_name;"Camping"end
|
|
259
|
+
|
|
249
260
|
end
|
|
250
261
|
|
|
251
262
|
# Camping::Base is built into each controller by way of the generic routing
|
|
@@ -256,28 +267,44 @@ module Camping
|
|
|
256
267
|
# Everything in this module is accessible inside your controllers.
|
|
257
268
|
module Base
|
|
258
269
|
attr_accessor :env, :request, :root, :input, :cookies, :state,
|
|
259
|
-
:status, :headers, :body
|
|
260
|
-
|
|
270
|
+
:status, :headers, :body, :url_prefix
|
|
271
|
+
|
|
261
272
|
T = {}
|
|
262
273
|
L = :layout
|
|
263
|
-
|
|
274
|
+
|
|
264
275
|
# Finds a template, returning either:
|
|
265
|
-
#
|
|
276
|
+
#
|
|
266
277
|
# false # => Could not find template
|
|
267
278
|
# true # => Found template in Views
|
|
268
279
|
# instance of Tilt # => Found template in a file
|
|
269
280
|
def lookup(n)
|
|
270
|
-
T.fetch(n.to_sym)
|
|
281
|
+
T.fetch(n.to_sym) { |k|
|
|
282
|
+
# Find a view defined in the Views module first
|
|
271
283
|
t = Views.method_defined?(k) ||
|
|
284
|
+
# Find inline templates (delimited by @@), and then put it in a new Template and return that.
|
|
285
|
+
# `:_t` is the options key for inline templates. Inline templates are added in `Camping#goes`.
|
|
272
286
|
(t = O[:_t].keys.grep(/^#{n}\./)[0]and Template[t].new{O[:_t][t]}) ||
|
|
287
|
+
|
|
288
|
+
# Find templates in a views directory, and return the first view that matches the symbol provided.
|
|
289
|
+
# Then pipe that template file into Template, which is just Tilt.
|
|
273
290
|
(f = Dir[[O[:views] || "views", "#{n}.*"]*'/'][0]) &&
|
|
291
|
+
|
|
292
|
+
# Grab any settings set for the template files, as set by their filename extension
|
|
293
|
+
# and add that to the options of Template (Tilt), or an empty Hash
|
|
294
|
+
# What does adding settings for a template look like? :
|
|
295
|
+
# module Nuts
|
|
296
|
+
# def r404(path)
|
|
297
|
+
# @path = path
|
|
298
|
+
# render :not_found
|
|
299
|
+
# end
|
|
300
|
+
# end
|
|
274
301
|
Template.new(f, O[f[/\.(\w+)$/, 1].to_sym] || {})
|
|
275
|
-
|
|
302
|
+
|
|
276
303
|
O[:dynamic_templates] ? t : T[k] = t
|
|
277
|
-
|
|
304
|
+
}
|
|
278
305
|
end
|
|
279
|
-
|
|
280
|
-
# Display a view, calling it by its method name +v+.
|
|
306
|
+
|
|
307
|
+
# Display a view, calling it by its method name +v+. If a <tt>layout</tt>
|
|
281
308
|
# method is found in Camping::Views, it will be used to wrap the HTML.
|
|
282
309
|
#
|
|
283
310
|
# module Nuts::Controllers
|
|
@@ -291,9 +318,12 @@ module Camping
|
|
|
291
318
|
#
|
|
292
319
|
def render(v, *a, &b)
|
|
293
320
|
if t = lookup(v)
|
|
294
|
-
|
|
321
|
+
# Has this controller rendered before?
|
|
322
|
+
r = @_r
|
|
323
|
+
# Set @_r to truthy value
|
|
324
|
+
@_r = (o = Hash === a[-1] ? a.pop : {})
|
|
295
325
|
s = (t == true) ? mab { send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b)
|
|
296
|
-
s = render(L, o.merge(L => false)) { s } if o[L] or o[L].nil? && lookup(L) &&
|
|
326
|
+
s = render(L, o.merge(L => false)) { s } if o[L] or o[L].nil? && lookup(L) && !r && v.to_s[0] != ?_
|
|
297
327
|
s
|
|
298
328
|
else
|
|
299
329
|
raise "no template: #{v}"
|
|
@@ -302,7 +332,7 @@ module Camping
|
|
|
302
332
|
|
|
303
333
|
# You can directly return HTML from your controller for quick debugging
|
|
304
334
|
# by calling this method and passing some Markaby to it.
|
|
305
|
-
#
|
|
335
|
+
#
|
|
306
336
|
# module Nuts::Controllers
|
|
307
337
|
# class Info
|
|
308
338
|
# def get; mab{ code @headers.inspect } end
|
|
@@ -314,7 +344,7 @@ module Camping
|
|
|
314
344
|
extend Mab
|
|
315
345
|
mab(&b)
|
|
316
346
|
end
|
|
317
|
-
|
|
347
|
+
|
|
318
348
|
# A quick means of setting this controller's status, body and headers
|
|
319
349
|
# based on a Rack response:
|
|
320
350
|
#
|
|
@@ -364,13 +394,13 @@ module Camping
|
|
|
364
394
|
P % "#{p} not found"
|
|
365
395
|
end
|
|
366
396
|
|
|
367
|
-
# Called when an exception is raised. However,
|
|
397
|
+
# Called when an exception is raised. However, if there is a parse error
|
|
368
398
|
# in Camping or in your application's source code, it will not be caught.
|
|
369
399
|
#
|
|
370
400
|
# +k+ is the controller class, +m+ is the request method (GET, POST, etc.)
|
|
371
401
|
# and +e+ is the Exception which can be mined for useful info.
|
|
372
402
|
#
|
|
373
|
-
#
|
|
403
|
+
# By default this simply re-raises the error so a Rack middleware can
|
|
374
404
|
# handle it, but you are free to override it here:
|
|
375
405
|
#
|
|
376
406
|
# module Nuts
|
|
@@ -378,7 +408,7 @@ module Camping
|
|
|
378
408
|
# send_email_alert(klass, method, exception)
|
|
379
409
|
# render :server_error
|
|
380
410
|
# end
|
|
381
|
-
# end
|
|
411
|
+
# end
|
|
382
412
|
def r500(k,m,e)
|
|
383
413
|
raise e
|
|
384
414
|
end
|
|
@@ -390,12 +420,12 @@ module Camping
|
|
|
390
420
|
end
|
|
391
421
|
|
|
392
422
|
# Serves the string +c+ with the MIME type of the filename +p+.
|
|
393
|
-
#
|
|
423
|
+
# Defaults to text/html.
|
|
394
424
|
def serve(p, c)
|
|
395
|
-
t = Rack::Mime.mime_type(p[/\..*$/],
|
|
425
|
+
t = Rack::Mime.mime_type(p[/\..*$/], Z) and @headers[E] = t
|
|
396
426
|
c
|
|
397
427
|
end
|
|
398
|
-
|
|
428
|
+
|
|
399
429
|
# Turn a controller into a Rack response. This is designed to be used to
|
|
400
430
|
# pipe controllers into the <tt>r</tt> method. A great way to forward your
|
|
401
431
|
# requests!
|
|
@@ -415,23 +445,29 @@ module Camping
|
|
|
415
445
|
end
|
|
416
446
|
r.to_a
|
|
417
447
|
end
|
|
418
|
-
|
|
419
|
-
|
|
448
|
+
|
|
449
|
+
# initialize
|
|
450
|
+
# Turns a camping controller class into an object and sets up
|
|
451
|
+
# the environment with input, cookies, state, headers, etc...
|
|
452
|
+
def initialize(env, m, p) #:nodoc:
|
|
420
453
|
r = @request = Rack::Request.new(@env = env)
|
|
421
454
|
@root, @input, @cookies, @state,
|
|
422
|
-
@headers, @status, @method =
|
|
455
|
+
@headers, @status, @method, @url_prefix =
|
|
423
456
|
r.script_name.sub(/\/$/,''), n(r.params),
|
|
424
457
|
Cookies[r.cookies], H[r.session[SK]||{}],
|
|
425
|
-
{
|
|
458
|
+
{E=>Z}, m =~ /r(\d+)/ ? $1.to_i : 200, m, p
|
|
426
459
|
@cookies._p = self/"/"
|
|
427
460
|
end
|
|
428
|
-
|
|
461
|
+
|
|
462
|
+
# n method
|
|
463
|
+
# accepts parameters and converts them to a hash.
|
|
464
|
+
# helper method for initialize
|
|
429
465
|
def n(h) # :nodoc:
|
|
430
466
|
if Hash === h
|
|
431
|
-
h.inject(H[])
|
|
467
|
+
h.inject(H[]) { |m, (k, v)|
|
|
432
468
|
m[k] = n(v)
|
|
433
469
|
m
|
|
434
|
-
|
|
470
|
+
}
|
|
435
471
|
else
|
|
436
472
|
h
|
|
437
473
|
end
|
|
@@ -441,13 +477,12 @@ module Camping
|
|
|
441
477
|
# Some magic in Camping can be performed by overriding this method.
|
|
442
478
|
def service(*a)
|
|
443
479
|
r = catch(:halt){send(@method, *a)}
|
|
444
|
-
@body ||= r
|
|
480
|
+
@body ||= r
|
|
445
481
|
self
|
|
446
482
|
end
|
|
447
483
|
end
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
# Controllers receive the requests and sends a response back to the client.
|
|
484
|
+
|
|
485
|
+
# Controllers receive the requests and send a response back to the client.
|
|
451
486
|
# A controller is simply a class which must implement the HTTP methods it
|
|
452
487
|
# wants to accept:
|
|
453
488
|
#
|
|
@@ -457,27 +492,31 @@ module Camping
|
|
|
457
492
|
# "Hello World"
|
|
458
493
|
# end
|
|
459
494
|
# end
|
|
460
|
-
#
|
|
495
|
+
#
|
|
461
496
|
# class Posts
|
|
462
497
|
# def post
|
|
463
|
-
# Post.create(@input)
|
|
498
|
+
# Post.create(@input)
|
|
464
499
|
# redirect Index
|
|
465
500
|
# end
|
|
466
|
-
# end
|
|
501
|
+
# end
|
|
467
502
|
# end
|
|
468
|
-
#
|
|
503
|
+
#
|
|
469
504
|
# == Defining a controller
|
|
470
|
-
#
|
|
471
|
-
# There are two ways to define controllers:
|
|
472
|
-
#
|
|
473
|
-
#
|
|
505
|
+
#
|
|
506
|
+
# There are two ways to define controllers:
|
|
507
|
+
#
|
|
508
|
+
# 1. Define a class and let Camping figure out the route.
|
|
509
|
+
# 2. Add the route explicitly using R.
|
|
510
|
+
#
|
|
474
511
|
# If you don't use R, Camping will first split the controller name up by
|
|
475
|
-
# words (HelloWorld => Hello and World).
|
|
476
|
-
#
|
|
512
|
+
# words (HelloWorld => Hello and World).
|
|
513
|
+
#
|
|
514
|
+
# After that, it will do the following:
|
|
515
|
+
#
|
|
477
516
|
# * Replace Index with /
|
|
478
517
|
# * Replace X with ([^/]+)
|
|
479
518
|
# * Replace N with (\\\d+)
|
|
480
|
-
# *
|
|
519
|
+
# * Turn everything else into lowercase
|
|
481
520
|
# * Join the words with slashes
|
|
482
521
|
#
|
|
483
522
|
#--
|
|
@@ -485,28 +524,29 @@ module Camping
|
|
|
485
524
|
# here in order to work correctly with RDoc.
|
|
486
525
|
#++
|
|
487
526
|
#
|
|
488
|
-
# Here
|
|
489
|
-
#
|
|
527
|
+
# Here are a few examples:
|
|
528
|
+
#
|
|
490
529
|
# Index # => /
|
|
491
530
|
# PostN # => /post/(\d+)
|
|
492
531
|
# PageX # => /page/([^/]+)
|
|
493
532
|
# Pages # => /pages
|
|
494
|
-
#
|
|
533
|
+
#
|
|
495
534
|
# == The request
|
|
496
|
-
#
|
|
497
|
-
#
|
|
498
|
-
#
|
|
499
|
-
# * @env contains the environment as defined in
|
|
535
|
+
#
|
|
536
|
+
# The following variables aid in describing a request:
|
|
537
|
+
#
|
|
538
|
+
# * @env contains the environment as defined in https://github.com/rack/rack/blob/main/SPEC.rdoc
|
|
500
539
|
# * @request is Rack::Request.new(@env)
|
|
501
540
|
# * @root is the path where the app is mounted
|
|
502
541
|
# * @cookies is a hash with the cookies sent by the client
|
|
503
542
|
# * @state is a hash with the sessions (see Camping::Session)
|
|
504
543
|
# * @method is the HTTP method in lowercase
|
|
544
|
+
# * @url_prefix is the set prefix of the route matched by your controller
|
|
505
545
|
#
|
|
506
546
|
# == The response
|
|
507
547
|
#
|
|
508
548
|
# You can change these variables to your needs:
|
|
509
|
-
#
|
|
549
|
+
#
|
|
510
550
|
# * @status is the HTTP status (defaults to 200)
|
|
511
551
|
# * @headers is a hash with the headers
|
|
512
552
|
# * @body is the body (a string or something which responds to #each)
|
|
@@ -515,13 +555,13 @@ module Camping
|
|
|
515
555
|
# If you haven't set @body, it will use the return value of the method:
|
|
516
556
|
#
|
|
517
557
|
# module Nuts::Controllers
|
|
518
|
-
# class Index
|
|
558
|
+
# class Index < Camper
|
|
519
559
|
# def get
|
|
520
560
|
# "This is the body"
|
|
521
561
|
# end
|
|
522
562
|
# end
|
|
523
563
|
#
|
|
524
|
-
# class Posts
|
|
564
|
+
# class Posts < Camper
|
|
525
565
|
# def get
|
|
526
566
|
# @body = "Hello World!"
|
|
527
567
|
# "This is ignored"
|
|
@@ -530,9 +570,15 @@ module Camping
|
|
|
530
570
|
# end
|
|
531
571
|
module Controllers
|
|
532
572
|
@r = []
|
|
573
|
+
|
|
574
|
+
# An empty controller class that our other Classes inherit from.
|
|
575
|
+
# Camper is used by the R method internally.
|
|
576
|
+
class Camper end
|
|
577
|
+
|
|
533
578
|
class << self
|
|
579
|
+
|
|
534
580
|
# Add routes to a controller class by piling them into the R method.
|
|
535
|
-
#
|
|
581
|
+
#
|
|
536
582
|
# The route is a regexp which will match the request path. Anything
|
|
537
583
|
# enclosed in parenthesis will be sent to the method as arguments.
|
|
538
584
|
#
|
|
@@ -545,18 +591,37 @@ module Camping
|
|
|
545
591
|
# end
|
|
546
592
|
# end
|
|
547
593
|
# end
|
|
594
|
+
#
|
|
595
|
+
# Routes may be inherited using the R command as well. In this case you'll
|
|
596
|
+
# pass the ancestor Controller as the first argument to R.
|
|
597
|
+
#
|
|
598
|
+
# module Camping::Controllers
|
|
599
|
+
# class Post < R Edit, '/edit/(\d+)', '/new'
|
|
600
|
+
# def get(id)
|
|
601
|
+
# if id # edit
|
|
602
|
+
# else # new
|
|
603
|
+
# end
|
|
604
|
+
# end
|
|
605
|
+
# end
|
|
606
|
+
# end
|
|
607
|
+
#
|
|
548
608
|
def R *u
|
|
549
|
-
r=@r
|
|
550
|
-
Class.new {
|
|
609
|
+
r,uf=@r,u.first
|
|
610
|
+
Class.new((uf.is_a?(Class) && (uf.ancestors.include?(Camper))) ? u.shift : Camper) {
|
|
551
611
|
meta_def(:urls){u}
|
|
552
|
-
meta_def(:inherited){|x|r<<x}
|
|
612
|
+
meta_def(:inherited){|x|r<< x}
|
|
553
613
|
}
|
|
554
614
|
end
|
|
555
615
|
|
|
616
|
+
# A Helper method to map and return the actual routes of our controllers
|
|
617
|
+
def v
|
|
618
|
+
@r.map(&:urls)
|
|
619
|
+
end
|
|
620
|
+
|
|
556
621
|
# Dispatch routes to controller classes.
|
|
557
622
|
# For each class, routes are checked for a match based on their order in the routing list
|
|
558
|
-
# given to Controllers::R.
|
|
559
|
-
# by the name of the controller
|
|
623
|
+
# given to Controllers::R. If no routes were given, the dispatcher uses a slash followed
|
|
624
|
+
# by the lowercased name of the controller.
|
|
560
625
|
#
|
|
561
626
|
# Controllers are searched in this order:
|
|
562
627
|
#
|
|
@@ -576,9 +641,20 @@ module Camping
|
|
|
576
641
|
[I, 'r404', p]
|
|
577
642
|
end
|
|
578
643
|
|
|
644
|
+
# A lambda to avoid internal controller route
|
|
645
|
+
A = -> (c, u, p) {
|
|
646
|
+
d = p.dup
|
|
647
|
+
d.chop! if u == ''
|
|
648
|
+
u.prepend("/"+d) if !["I"].include? c.to_s
|
|
649
|
+
if c.to_s == "Index"
|
|
650
|
+
while d[-1] == "/"; d.chop! end
|
|
651
|
+
u.prepend("/"+d)
|
|
652
|
+
end
|
|
653
|
+
u
|
|
654
|
+
}
|
|
655
|
+
|
|
579
656
|
N = H.new { |_,x| x.downcase }.merge! "N" => '(\d+)', "X" => '([^/]+)', "Index" => ''
|
|
580
|
-
# The route maker,
|
|
581
|
-
# need to call it.
|
|
657
|
+
# The route maker, called by Camping internally.
|
|
582
658
|
#
|
|
583
659
|
# Still, it's worth know what this method does. Since Ruby doesn't keep
|
|
584
660
|
# track of class creation order, we're keeping an internal list of the
|
|
@@ -588,16 +664,24 @@ module Camping
|
|
|
588
664
|
#
|
|
589
665
|
# Anyway, if you are calling the URI dispatcher from outside of a
|
|
590
666
|
# Camping server, you'll definitely need to call this to set things up.
|
|
591
|
-
# Don't call it too early though
|
|
592
|
-
# method
|
|
593
|
-
def M
|
|
594
|
-
def M #:nodoc:
|
|
667
|
+
# Don't call it too early though - any controllers added after this
|
|
668
|
+
# method was called won't work properly.
|
|
669
|
+
def M(p)
|
|
670
|
+
def M(p) #:nodoc:
|
|
595
671
|
end
|
|
596
|
-
|
|
672
|
+
# TODO: Refactor this to make it less convoluted around making urls.
|
|
673
|
+
constants.filter {|c| c.to_s != 'Camper'}.map { |c|
|
|
597
674
|
k = const_get(c)
|
|
598
675
|
k.send :include,C,X,Base,Helpers,Models
|
|
599
676
|
@r=[k]+@r if @r-[k]==@r
|
|
600
|
-
|
|
677
|
+
mu = false # Should we make urls?
|
|
678
|
+
ka = k.ancestors
|
|
679
|
+
# This complicated code checks the ancestor chain of a controller to see it has it's own urls,
|
|
680
|
+
# or if it's urls are from one of it's ancestors. ancestor URLs need to be discarded.
|
|
681
|
+
if (k.respond_to?(:urls) && ka[1].respond_to?(:urls)) && (k.urls == ka[1].urls)
|
|
682
|
+
mu = true unless ka[1].name == nil
|
|
683
|
+
end
|
|
684
|
+
k.meta_def(:urls){[A.(k,"#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}", p)]} if (!k.respond_to?(:urls) || mu == true)
|
|
601
685
|
}
|
|
602
686
|
end
|
|
603
687
|
end
|
|
@@ -608,50 +692,41 @@ module Camping
|
|
|
608
692
|
X = Controllers
|
|
609
693
|
|
|
610
694
|
class << self
|
|
611
|
-
|
|
612
|
-
#
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
#
|
|
619
|
-
#
|
|
620
|
-
#
|
|
695
|
+
|
|
696
|
+
# Create method to setup routes for Camping upon reload.
|
|
697
|
+
def make_camp
|
|
698
|
+
X.M prx
|
|
699
|
+
Apps.map(&:make_camp)
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Helper method for getting routes from the controllers.
|
|
703
|
+
# helps Camping::Server map routes to multiple apps.
|
|
704
|
+
# Usage:
|
|
621
705
|
#
|
|
622
|
-
#
|
|
623
|
-
#
|
|
624
|
-
#
|
|
625
|
-
# code for a worker process together:
|
|
626
|
-
#
|
|
627
|
-
# module YourApplication
|
|
628
|
-
# Camping.goes :Web, binding()
|
|
629
|
-
# module Web
|
|
630
|
-
# ...
|
|
631
|
-
# end
|
|
632
|
-
# module Worker
|
|
633
|
-
# ...
|
|
634
|
-
# end
|
|
635
|
-
# end
|
|
706
|
+
# Nuts.routes
|
|
707
|
+
# Camping.routes
|
|
708
|
+
# Nuts.routes
|
|
636
709
|
#
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
710
|
+
def routes
|
|
711
|
+
(Apps.map(&:routes)<<X.v).flatten
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# An internal method used to return the current app's url_prefix.
|
|
715
|
+
# the prefix is processed to make sure that it's not all wonky. excessive
|
|
716
|
+
# trailing and leading slashes are removed. A trailing slash is added.
|
|
717
|
+
def prx
|
|
718
|
+
@_prx ||= CampTools.normalize_slashes(O[:url_prefix])
|
|
644
719
|
end
|
|
645
|
-
|
|
720
|
+
|
|
646
721
|
# Ruby web servers use this method to enter the Camping realm. The +e+
|
|
647
722
|
# argument is the environment variables hash as per the Rack specification.
|
|
648
|
-
#
|
|
723
|
+
# Array with [status, headers, body] is expected at the output.
|
|
649
724
|
#
|
|
650
|
-
# See:
|
|
725
|
+
# See: https://github.com/rack/rack/blob/main/SPEC.rdoc
|
|
651
726
|
def call(e)
|
|
652
|
-
|
|
653
|
-
k,m,*a=X.D e[
|
|
654
|
-
k.new(e,m).service(*a).to_a
|
|
727
|
+
make_camp # TODO: Find a better, more consistent place for setting everything up.
|
|
728
|
+
k,m,*a=X.D e["PATH_INFO"],e['REQUEST_METHOD'].downcase,e
|
|
729
|
+
k.new(e,m,prx).service(*a).to_a
|
|
655
730
|
rescue
|
|
656
731
|
r500(:I, k, m, $!, :env => e).to_a
|
|
657
732
|
end
|
|
@@ -675,40 +750,187 @@ module Camping
|
|
|
675
750
|
# #=> #<Blog::Controllers::Info @headers={'HTTP_HOST'=>'wagon'} ...>
|
|
676
751
|
#
|
|
677
752
|
def method_missing(m, c, *a)
|
|
678
|
-
X.M
|
|
679
753
|
h = Hash === a[-1] ? a.pop : {}
|
|
680
754
|
e = H[Rack::MockRequest.env_for('',h.delete(:env)||{})]
|
|
681
|
-
|
|
755
|
+
# puts "method missing failure for controller: #{c}, method: #{m} "
|
|
756
|
+
k = X.const_get(c).new(e,m.to_s,prx)
|
|
682
757
|
h.each { |i, v| k.send("#{i}=", v) }
|
|
683
758
|
k.service(*a)
|
|
684
759
|
end
|
|
685
|
-
|
|
760
|
+
|
|
686
761
|
# Injects a middleware:
|
|
687
762
|
#
|
|
688
763
|
# module Blog
|
|
689
764
|
# use Rack::MethodOverride
|
|
690
765
|
# use Rack::Session::Memcache, :key => "session"
|
|
691
766
|
# end
|
|
767
|
+
#
|
|
768
|
+
# This piece of code feels a bit confusing, but let's walk through it.
|
|
769
|
+
# Rack apps all implement a Call method. This is how Rub web servers
|
|
770
|
+
# pass call the app, or code that you're set up. In our case, our camping
|
|
771
|
+
# apps.
|
|
772
|
+
#
|
|
773
|
+
# The Use method is setting up a new middleware, it first shifts the first
|
|
774
|
+
# argument supplied to Use, which should be the Middleware name, then
|
|
775
|
+
# initializes it. That's your new middleware. Rack based middleware accept
|
|
776
|
+
# a single argument to their initialize methods, which is an app. Optionally
|
|
777
|
+
# settings and a block are supplied.
|
|
778
|
+
#
|
|
779
|
+
# So a new app is made, and its settings are supplied, then immediately
|
|
780
|
+
# sent to the new middleware we just added. But the cool part is where we
|
|
781
|
+
# call meta_def. meta_def takes a symbol and a block, and defines a class
|
|
782
|
+
# method into the current context. Our current context is our camping app.
|
|
783
|
+
# So when we call it below we're redefining the call method to call the new
|
|
784
|
+
# middleware that we just added. The `m` variable below represents our
|
|
785
|
+
# newly created middleware object, that we initialized with our old app. and
|
|
786
|
+
# because we're defining a new call method with a block, it's captured in
|
|
787
|
+
# that block.
|
|
788
|
+
#
|
|
789
|
+
# This creates a sequence of middleware that isn't recorded anywhere, but
|
|
790
|
+
# nonetheless is set up in the proper order and called in the proper order.
|
|
692
791
|
def use(*a, &b)
|
|
693
792
|
m = a.shift.new(method(:call), *a, &b)
|
|
694
793
|
meta_def(:call) { |e| m.call(e) }
|
|
695
794
|
end
|
|
696
|
-
|
|
795
|
+
|
|
796
|
+
# Add gear to your app:
|
|
797
|
+
#
|
|
798
|
+
# module Blog
|
|
799
|
+
# pack Camping::Gear::CSRF
|
|
800
|
+
# end
|
|
801
|
+
#
|
|
802
|
+
# Why have plugins in the first place if we can just include and extend our
|
|
803
|
+
# modules and classes directly? To perform setup actions!
|
|
804
|
+
#
|
|
805
|
+
# Sometimes you might have ClassMethods that you want to modify Camping with,
|
|
806
|
+
# This gives us a way to do that. In your gear:
|
|
807
|
+
#
|
|
808
|
+
# module MyGear
|
|
809
|
+
# module ClassMethods
|
|
810
|
+
# # Define Class Methods here
|
|
811
|
+
# end
|
|
812
|
+
# def self.included(mod)
|
|
813
|
+
# mod.extend(ClassMethods)
|
|
814
|
+
# end
|
|
815
|
+
# end
|
|
816
|
+
#
|
|
817
|
+
# Optionally a plugin may have a setup method and a ClassMethods module:
|
|
818
|
+
#
|
|
819
|
+
# module MyGear
|
|
820
|
+
# def self.setup(s)
|
|
821
|
+
# # Perform setup actions
|
|
822
|
+
# end
|
|
823
|
+
# module ClassMethods
|
|
824
|
+
# # Define Class Methods here
|
|
825
|
+
# end
|
|
826
|
+
# end
|
|
827
|
+
#
|
|
828
|
+
def pack(*a, &b)
|
|
829
|
+
G << g = a.shift
|
|
830
|
+
include g
|
|
831
|
+
g.setup(self, *a, &b) # if g.respond_to?(:setup) # Force all gear to have a setup function
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
# Helper method to list gear
|
|
835
|
+
def gear
|
|
836
|
+
G
|
|
837
|
+
end
|
|
838
|
+
|
|
697
839
|
# A hash where you can set different settings.
|
|
698
840
|
def options
|
|
699
841
|
O
|
|
700
842
|
end
|
|
701
|
-
|
|
843
|
+
|
|
702
844
|
# Shortcut for setting options:
|
|
703
|
-
#
|
|
845
|
+
#
|
|
704
846
|
# module Blog
|
|
705
847
|
# set :secret, "Hello!"
|
|
706
848
|
# end
|
|
707
849
|
def set(k, v)
|
|
708
850
|
O[k] = v
|
|
709
851
|
end
|
|
852
|
+
|
|
853
|
+
# When you are running multiple applications, you may want to create
|
|
854
|
+
# independent modules for each Camping application. Camping::goes
|
|
855
|
+
# defines a top level constant with the whole MVC rack inside:
|
|
856
|
+
#
|
|
857
|
+
# require 'camping'
|
|
858
|
+
# Camping.goes :Nuts
|
|
859
|
+
#
|
|
860
|
+
# module Nuts::Controllers; ... end
|
|
861
|
+
# module Nuts::Models; ... end
|
|
862
|
+
# module Nuts::Views; ... end
|
|
863
|
+
#
|
|
864
|
+
# Additionally, you can pass a Binding as the second parameter,
|
|
865
|
+
# which enables you to create a Camping-based application within
|
|
866
|
+
# another module.
|
|
867
|
+
#
|
|
868
|
+
# Here's an example of namespacing your web interface and
|
|
869
|
+
# code for a worker process together:
|
|
870
|
+
#
|
|
871
|
+
# module YourApplication
|
|
872
|
+
# Camping.goes :Web, binding()
|
|
873
|
+
# module Web
|
|
874
|
+
# ...
|
|
875
|
+
# end
|
|
876
|
+
# module Worker
|
|
877
|
+
# ...
|
|
878
|
+
# end
|
|
879
|
+
# end
|
|
880
|
+
#
|
|
881
|
+
# All the applications will be available in Camping::Apps.
|
|
882
|
+
#
|
|
883
|
+
# Camping offers a shortcut for adding thin files, and templates to your apps.
|
|
884
|
+
# Add them at the end of the same ruby file that you call `Camping.goes`:
|
|
885
|
+
#
|
|
886
|
+
# require 'camping'
|
|
887
|
+
# Camping.goes :Nuts
|
|
888
|
+
#
|
|
889
|
+
# module Nuts::Controllers; ... end
|
|
890
|
+
# module Nuts::Models; ... end
|
|
891
|
+
# module Nuts::Views; ... end
|
|
892
|
+
#
|
|
893
|
+
# __END__
|
|
894
|
+
#
|
|
895
|
+
# @@ /style.css
|
|
896
|
+
# * { margin: 0; padding: 0 }
|
|
897
|
+
#
|
|
898
|
+
# @@ /test.foo
|
|
899
|
+
# <H1>Hello friends! Nice to meet you.<H1>
|
|
900
|
+
#
|
|
901
|
+
# @@ index.erb
|
|
902
|
+
# Hello <%= @world %>
|
|
903
|
+
#
|
|
904
|
+
# Also sets the apps Meta Data. Can be found at O[:_meta]
|
|
905
|
+
#
|
|
906
|
+
# @app: {String} - The app in question
|
|
907
|
+
# @parent: {String} -
|
|
908
|
+
# @root: {String} -
|
|
909
|
+
# @line_number: {Int} - The line number that the app was declared
|
|
910
|
+
# @file: {String} - The file location for this
|
|
911
|
+
#
|
|
912
|
+
def goes(m, g=TOPLEVEL_BINDING)
|
|
913
|
+
|
|
914
|
+
# setup caller data
|
|
915
|
+
sp = caller[0].split('`')[0].split(":")
|
|
916
|
+
fl, ln, pr = sp[0], sp[1].to_i, nil
|
|
917
|
+
|
|
918
|
+
# Create the app
|
|
919
|
+
Apps << a = eval(S.gsub(/Camping/,m.to_s), g, fl, ln)
|
|
920
|
+
|
|
921
|
+
caller[0]=~/:/
|
|
922
|
+
IO.read(a.set:__FILE__,$`)=~/^__END__/ &&
|
|
923
|
+
(b=$'.split(/^@@\s*(.+?)\s*\r?\n/m)).shift rescue nil
|
|
924
|
+
a.set :_t,H[*b||[]]
|
|
925
|
+
|
|
926
|
+
# setup parental data
|
|
927
|
+
a.set :_meta, H[file: fl, line_number: ln, parent: self, root: (name != "Cam\ping" ? '/' + CampTools.to_snake(name) : '/')]
|
|
928
|
+
|
|
929
|
+
# configure the app?
|
|
930
|
+
C.configure(a)
|
|
931
|
+
end
|
|
710
932
|
end
|
|
711
|
-
|
|
933
|
+
|
|
712
934
|
# Views is an empty module for storing methods which create HTML. The HTML
|
|
713
935
|
# is described using the Markaby language.
|
|
714
936
|
#
|
|
@@ -720,7 +942,7 @@ module Camping
|
|
|
720
942
|
# def index
|
|
721
943
|
# p "Welcome to my blog"
|
|
722
944
|
# end
|
|
723
|
-
#
|
|
945
|
+
#
|
|
724
946
|
# def show
|
|
725
947
|
# h1 @post.title
|
|
726
948
|
# self << @post.content
|
|
@@ -745,7 +967,7 @@ module Camping
|
|
|
745
967
|
# end
|
|
746
968
|
# end
|
|
747
969
|
module Views; include X, Helpers end
|
|
748
|
-
|
|
970
|
+
|
|
749
971
|
# Models is an empty Ruby module for housing model classes derived
|
|
750
972
|
# from ActiveRecord::Base. As a shortcut, you may derive from Base
|
|
751
973
|
# which is an alias for ActiveRecord::Base.
|
|
@@ -770,14 +992,19 @@ module Camping
|
|
|
770
992
|
# end
|
|
771
993
|
# end
|
|
772
994
|
#
|
|
773
|
-
# Models cannot be referred
|
|
995
|
+
# Models cannot be referred from Views at this time.
|
|
774
996
|
module Models
|
|
775
|
-
autoload :Base,'camping/ar'
|
|
776
997
|
Helpers.send(:include, X, self)
|
|
777
998
|
end
|
|
778
999
|
|
|
779
1000
|
autoload :Mab, 'camping/mab'
|
|
780
1001
|
autoload :Template, 'camping/template'
|
|
1002
|
+
|
|
1003
|
+
# Load default Gear
|
|
1004
|
+
pack Gear::Inspection
|
|
1005
|
+
pack Gear::Filters
|
|
1006
|
+
pack Gear::Nancy
|
|
1007
|
+
pack Gear::Kuddly
|
|
1008
|
+
|
|
781
1009
|
C
|
|
782
1010
|
end
|
|
783
|
-
|