Module: Camping::Controllers
Overview
Controllers receive the requests and send a response back to the client. A controller is simply a class which must implement the HTTP methods it wants to accept:
module Nuts::Controllers
class Index
def get
"Hello World"
end
end
class Posts
def post
Post.create(@input)
redirect Index
end
end
end
Defining a controller
There are two ways to define controllers:
-
Define a class and let Camping figure out the route.
-
Add the route explicitly using R.
If you don’t use R, Camping will first split the controller name up by words (HelloWorld => Hello and World).
After that, it will do the following:
-
Replace Index with /
-
Replace X with ([^/]+)
-
Replace N with (\d+)
-
Turn everything else into lowercase
-
Join the words with slashes
– NB! N will actually be replaced with (d+), but it needs to be escaped here in order to work correctly with RDoc. ++
Here are a few examples:
Index # => /
PostN # => /post/(\d+)
PageX # => /page/([^/]+)
Pages # => /pages
The request
The following variables aid in describing a request:
-
@env contains the environment as defined in github.com/rack/rack/blob/main/SPEC.rdoc
-
@request is Rack::Request.new(@env)
-
@root is the path where the app is mounted
-
@cookies is a hash with the cookies sent by the client
-
@state is a hash with the sessions (see Camping::Session)
-
@method is the HTTP method in lowercase
-
@url_prefix is the set prefix of the route matched by your controller
The response
You can change these variables to your needs:
-
@status is the HTTP status (defaults to 200)
-
@headers is a hash with the headers
-
@body is the body (a string or something which responds to #each)
-
Any changes in @cookies and @state will also be sent to the client
If you haven’t set @body, it will use the return value of the method:
module Nuts::Controllers
class Index < Camper
def get
"This is the body"
end
end
class Posts < Camper
def get
@body = "Hello World!"
"This is ignored"
end
end
end
Defined Under Namespace
Classes: Camper
Constant Summary collapse
- A =
A lambda to avoid internal controller route
-> (c, u, p) { d = p.dup d.chop! if u == '' u.prepend("/"+d) if !["I"].include? c.to_s if c.to_s == "Index" while d[-1] == "/"; d.chop! end u.prepend("/"+d) end u }
- N =
H.new { |_,x| x.downcase }.merge! "N" => '(\d+)', "X" => '([^/]+)', "Index" => ''
- I =
Internal controller with no route. Used to show internal messages.
R()
Class Method Summary collapse
-
.D(p, m, e) ⇒ Object
Dispatch routes to controller classes.
-
.M(p) ⇒ Object
The route maker, called by Camping internally.
-
.R(*u) ⇒ Object
Add routes to a controller class by piling them into the R method.
-
.v ⇒ Object
A Helper method to map and return the actual routes of our controllers.
Instance Method Summary collapse
-
#M(p) ⇒ Object
:nodoc:.
Class Method Details
.D(p, m, e) ⇒ Object
Dispatch routes to controller classes. For each class, routes are checked for a match based on their order in the routing list given to Controllers::R. If no routes were given, the dispatcher uses a slash followed by the lowercased name of the controller.
Controllers are searched in this order:
-
Classes without routes, since they refer to a very specific URL.
-
Classes with routes are searched in order of their creation.
So, define your catch-all controllers last.
632 633 634 635 |
# File 'lib/camping-unabridged.rb', line 632 def D p,m,e;p='/'if !p||!p[0];(a=O[:_t].find{|n,_|n==p}) and return [I,:serve,*a] @r.map{|k|k.urls.map{|x|return(k.method_defined? m)?[k,m,*$~[1..-1].map{|x|U.unescape x}]: [I, 'r501',m]if p=~/^#{x}\/?$/}};[I,'r404',p] end |
.M(p) ⇒ Object
The route maker, called by Camping internally.
Still, it’s worth know what this method does. Since Ruby doesn’t keep track of class creation order, we’re keeping an internal list of the controllers which inherit from R(). This method goes through and adds all the remaining routes to the beginning of the list and ensures all the controllers have the right mixins.
Anyway, if you are calling the URI dispatcher from outside of a Camping server, you’ll definitely need to call this to set things up. Don’t call it too early though - any controllers added after this method was called won’t work properly.
669 670 671 672 673 674 675 676 |
# File 'lib/camping-unabridged.rb', line 669 def M p;def M p;end constants.filter{|c|c.to_s!='Camper'}.map{|c|k=const_get(c); k.include(C,X,Base,Helpers,Models) @r=[k]+@r if @r-[k]==@r;mu=false;ka=k.ancestors if (k.respond_to?(:urls) && ka[1].respond_to?(:urls)) && (k.urls == ka[1].urls) mu = true unless ka[1].name == nil end k.(:urls){[A.(k,"#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}",p)]} if (!k .respond_to?(:urls) || mu==true)};end |
.R(*u) ⇒ Object
Add routes to a controller class by piling them into the R method.
The route is a regexp which will match the request path. Anything enclosed in parenthesis will be sent to the method as arguments.
module Camping::Controllers
class Edit < R '/edit/(\d+)', '/new'
def get(id)
if id # edit
else # new
end
end
end
end
Routes may be inherited using the R command as well. In this case you’ll pass the ancestor Controller as the first argument to R.
module Camping::Controllers
class Post < R Edit, '/edit/(\d+)', '/new'
def get(id)
if id # edit
else # new
end
end
end
end
608 609 610 |
# File 'lib/camping-unabridged.rb', line 608 def R *u;r,uf=@r,u.first;Class.new((uf.is_a?(Class)&& (uf.ancestors.include?(Camper))) ? u.shift : Camper) { (:urls){u};(:inherited){|x|r<< x} } end |
.v ⇒ Object
A Helper method to map and return the actual routes of our controllers
617 |
# File 'lib/camping-unabridged.rb', line 617 def v;@r.map(&:urls);end |