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.
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 +1 -1
  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
-