1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
# @title Controllers
# Controllers
When developing web applications controllers are the elements that are called
by a browser. When visiting a page a request is made that is processed by Rack
and sent to Ramaze. Ramaze in turn will determine what controller to call.
To make understanding controllers a bit easier will use a real world example.
Let's say we're in a restaurant and want to order some food. The waiter of the
restaurant can be seen as a controller. We'll talk to it and tell him what we
want to have for dinner but the waiter itself won't actually prepare our dinner,
instead it will merely tell the cooks to make the dinner and bring it to you once
it's done. The waiter is our controller, the cook is our model and our meal can
be seen as the view.
In a typical application the entire flow of a request is as following:
Request --> Server (Thin, Unicorn, etc) --> Rack --> Ramaze --> Controller
## Ramaze & Controllers
In Ramaze controllers are plain Ruby classes that extend Ramaze::Controller. The
most basic controller looks like the following:
class ControllerName < Ramaze::Controller
map '/uri'
def index
end
end
Let's walk through this snippet step by step. The first line declares a new
class that extends {Ramaze::Controller}. Extending this base class is very
important, without it we won't be able to call controller specific methods such
as the `map()` method. This method, which is called on line 2, is used to instruct
Ramaze what controller is bound to what URI (Uniform Resource Identifier). The
argument of this method should be a string starting with a /. The reason for
this is that the URIs are relative to URL the application is running on. For
example, if our application was running at ramaze.net a URI of "/blog" would
mean the controller can be found at ramaze.net/blog.
Let's move to the next lines of code. The next lines of code define a new method
called "index". By default Ramaze will try to call this method if no URI after
the mapped URI is given. In our /blog example a call to ramaze.net/blog would
call `BlogController#index` but a call to `ramaze.net/blog/entry/10` would call
`BlogController#entry(10)`.
All methods that are declared as public can be accessed by a user and by default
the `index()` method is called if no other method is specified. Don't like the
`index()` method? No problem, you can specify a different default method to call
as following:
class ControllerName < Ramaze::Controller
trait :default_action_name => 'default'
def default
end
end
We're not going to cover Traits too much in this chapter as there's a dedicated
chapter for them but in short they're a way of setting configuration options. In
this case we're using the trait `default_action_name` to specify what the name
of the default method should be. By default this is set to "index" but in the
above example it was changed to "default".
### Method Arguments
As mentioned above methods are bound to URLs given they're declared as public
methods. The same applies to the arguments of such methods, if the method is
public these arguments can be set from the URL. This means that you don't have
to use a special DSL just to bind methods to certain URLs while taking various
parameters into account. An example is the following:
class Pages < Ramaze::Controller
map '/pages'
def index
# Overview of all pages
end
def edit(id)
# Edit the page for the given ID
end
end
This controller would allow users to navigate to `/pages/edit/10` which would
invoke `Pages#edit("10")`. There's no restriction on the values of parameters
(as long as they don't include slashes), they are however always passed as
strings to the method.
One thing to keep in mind is that if a method takes a set of required parameters
that are *not* specified Ramaze will *not* call the method, it will instead show
a message that the request could not be executed due to a missing
method/controller (unless your application has a custom handler for this).
Using the code above navigating to `/pages/edit/10` would work but navigating to
`/pages/edit` would not since the "id" parameter is specified as a required
parameter but wasn't given in the URL. Don't worry, working around this is as
easy as specifying a default value for your parameters:
class Pages < Ramaze::Controller
map '/pages'
def index
# Overview of all pages
end
def edit(id = nil)
# Edit the page for the given ID
end
end
With this modification the `edit` method will be called for URLs such as
`/pages/edit`, `/pages/edit/10` and so on.
### Catch-all Methods
Sometimes you want to create a controller in which a single method handles all
the requests. This can be done by creating an `index` method that takes a
variable amount of parameters:
class Pages < Ramaze::Controller
map '/pages'
def index(*args)
end
end
In this example `Pages#index` would be called for URLs such as `/pages`,
`/pages/example`, `/pages/edit/10` and so on. The exception to this is URLs that
point to existing methods. An example:
class Pages < Ramaze::Controller
map '/pages'
def index(*args)
return 'index'
end
def example
return 'example'
end
end
If a user were to browse to `/pages/hello` the index method would be called and
"index" would be displayed, when the user instead goes to `/pages/example` the
text "example" would be displayed as there's an existing method for this URI.
However, if the user would request `/pages/example/10` the index method would
again be called, this is because the example method does not take any
parameters. Below is a list of various URLs and what method calls they'd result
in.
/pages # => Pages#index
/pages/index # => Pages#index
/pages/edit/10 # => Pages#index("edit", "10")
/pages/example # => Pages#example
/pages/example/10 # => Pages#index("example", "10")
## Registering Controllers
By now you might be thinking "How does Ramaze know what controller to call? I
didn't initialize the controller!". It's true, you don't have to manually
initialize the controller and save it in a hash or somewhere else. The entire
process of registering a controller is done by the map() method and thus is one
of the most important methods available. When calling this method it will store
the name of the class that invoked it and bind it to the given URI. Whenever a
request is made Ramaze simply creates an instance of the matching controller
for a given URI.
The basic process of the map() method is as following:
1. Call `map()`.
2. Validate the given URI.
3. Store the controller constant.
4. Done.
## Base Controllers
In many applications you'll have separate areas such as an admin panel and the
frontend. Usually you want to authenticate users for certain controllers, such
as those used for an admin panel. An easy way of doing this is by putting the
authentication call in a controller. By creating a base controller and extending
it you don't have to call the method that authenticates the user over and over
again. Because Ramaze is just Ruby all you have to do to achieve this is the
following:
class AdminController < Ramaze::Controller
end
class Users < AdminController
end
If your base controller has an initialize() method defined you should always
call the parent's initialize() method to ensure everything is working properly.
This can be done by calling super():
class AdminController < Ramaze::Controller
def initialize
# Calls Ramaze::Controller#initialize
super
# Custom calls can be placed here...
# ...
end
end
|