camping 2.1.531 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -53
  3. data/Rakefile +25 -20
  4. data/bin/camping +1 -0
  5. data/book/01_introduction.md +6 -6
  6. data/book/02_getting_started.md +348 -267
  7. data/book/03_more_about_controllers.md +124 -0
  8. data/book/04_more_about_views.md +118 -0
  9. data/book/05_more_about_markaby.md +173 -0
  10. data/book/06_more_about_models.md +58 -0
  11. data/book/06_rules_of_thumb.md +143 -0
  12. data/book/07_philosophy.md +23 -0
  13. data/book/08_publishing_an_app.md +118 -0
  14. data/book/09_upgrade_notes.md +96 -0
  15. data/book/10_middleware.md +69 -0
  16. data/book/11_gear.md +50 -0
  17. data/examples/blog.rb +38 -38
  18. data/lib/camping/ar.rb +20 -5
  19. data/lib/camping/commands.rb +388 -0
  20. data/lib/camping/gear/filters.rb +48 -0
  21. data/lib/camping/gear/inspection.rb +32 -0
  22. data/lib/camping/gear/kuddly.rb +178 -0
  23. data/lib/camping/gear/nancy.rb +170 -0
  24. data/lib/camping/loads.rb +15 -0
  25. data/lib/camping/mab.rb +2 -2
  26. data/lib/camping/reloader.rb +22 -17
  27. data/lib/camping/server.rb +145 -70
  28. data/lib/camping/session.rb +8 -5
  29. data/lib/camping/template.rb +1 -2
  30. data/lib/camping/tools.rb +43 -0
  31. data/lib/camping/version.rb +6 -0
  32. data/lib/camping-unabridged.rb +360 -133
  33. data/lib/camping.rb +78 -47
  34. data/lib/campingtrip.md +341 -0
  35. data/test/app_camping_gear.rb +121 -0
  36. data/test/app_camping_tools.rb +1 -0
  37. data/test/app_config.rb +30 -0
  38. data/test/app_cookies.rb +1 -1
  39. data/test/app_file.rb +3 -3
  40. data/test/app_goes_meta.rb +23 -0
  41. data/test/app_inception.rb +39 -0
  42. data/test/app_markup.rb +5 -20
  43. data/test/app_migrations.rb +16 -0
  44. data/test/app_partials.rb +1 -1
  45. data/test/app_prefixed.rb +88 -0
  46. data/test/app_reloader.rb +1 -2
  47. data/test/app_route_generating.rb +69 -2
  48. data/test/app_sessions.rb +24 -2
  49. data/test/app_simple.rb +18 -18
  50. data/test/apps/migrations.rb +82 -82
  51. data/test/apps/misc.rb +1 -1
  52. data/test/gear/gear_nancy.rb +129 -0
  53. data/test/test_helper.rb +69 -12
  54. metadata +152 -92
  55. data/CHANGELOG +0 -145
  56. data/book/51_upgrading.md +0 -110
@@ -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 "uri"
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
- O = {}
52
- Apps = []
53
- SK = :camping #Key for r.session
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
- # Often the helpers depends on other helpers, so you would have to look up
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(/\\(.)/){$1})
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); p[0] == ?/ ? @root + p : p end
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) do |k|
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
- end
304
+ }
278
305
  end
279
-
280
- # Display a view, calling it by its method name +v+. If a <tt>layout</tt>
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
- r, @_r = @_r, o = Hash === a[-1] ? a.pop : {}
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) && (!r && v.to_s[0] != ?_)
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, if there is a parse error
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
- # Be default this simply re-raises the error so a Rack middleware can
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
- # Default text/html
423
+ # Defaults to text/html.
394
424
  def serve(p, c)
395
- t = Rack::Mime.mime_type(p[/\..*$/], "text/html") and @headers['Content-Type'] = t
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
- def initialize(env, m) #:nodoc:
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
- {'Content-Type'=>'text/html'}, m =~ /r(\d+)/ ? $1.to_i : 200, m
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[]) do |m, (k, v)|
467
+ h.inject(H[]) { |m, (k, v)|
432
468
  m[k] = n(v)
433
469
  m
434
- end
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: Just defining a class and let
472
- # Camping figure out the route, or add the route explicitly using R.
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). Then it would do the following:
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
- # * Everything else turns into lowercase
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's a few examples:
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
- # You have these variables which describes the request:
498
- #
499
- # * @env contains the environment as defined in http://rack.rubyforge.org/doc/SPEC.html
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. If no routes were given, the dispatcher uses a slash followed
559
- # by the name of the controller lowercased.
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, this is called by Camping internally, you shouldn't
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. Any controllers added after this
592
- # method is called won't work properly
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
- constants.map { |c|
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
- k.meta_def(:urls){["/#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}"]}if !k.respond_to?:urls
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
- # When you are running many applications, you may want to create
612
- # independent modules for each Camping application. Camping::goes
613
- # defines a toplevel constant with the whole MVC rack inside:
614
- #
615
- # require 'camping'
616
- # Camping.goes :Nuts
617
- #
618
- # module Nuts::Controllers; ... end
619
- # module Nuts::Models; ... end
620
- # module Nuts::Views; ... end
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
- # Additionally, you can pass a Binding as the second parameter,
623
- # which enables you to create a Camping-based application within
624
- # another module, for example to namespace your web interface and
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
- # All the applications will be available in Camping::Apps.
638
- def goes(m, g=TOPLEVEL_BINDING)
639
- Apps << a = eval(S.gsub(/Camping/,m.to_s), g)
640
- caller[0]=~/:/
641
- IO.read(a.set:__FILE__,$`)=~/^__END__/ &&
642
- (b=$'.split(/^@@\s*(.+?)\s*\r?\n/m)).shift rescue nil
643
- a.set :_t,H[*b||[]]
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
- # And array with [status, headers, body] is expected at the output.
723
+ # Array with [status, headers, body] is expected at the output.
649
724
  #
650
- # See: http://rack.rubyforge.org/doc/SPEC.html
725
+ # See: https://github.com/rack/rack/blob/main/SPEC.rdoc
651
726
  def call(e)
652
- X.M
653
- k,m,*a=X.D e['PATH_INFO'],e['REQUEST_METHOD'].downcase,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
- k = X.const_get(c).new(e,m.to_s)
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 to in Views at this time.
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
-