4. Hobo Model Controller
To enable
class MyController < ApplicationController
hobo_model_controller
end
(or use the hobo_model_controller generator)
CRUD Actions
The controller automatically provides:
* index * show * new * create * edit * update * destroy
index will automatically paginate and orders the records according to their default_order (see Model Extensions).
Default page tags
The viewable actions - index, show, new, and edit are rendered using corresponding view templates as with regular Rails. If the template is missing, Hobo will render a tag in order to create a default page.
e.g. if app/views/blog_posts/show.dryml is missing, Hobo will render <show_page/> instead.
The full set of default page tags are:
<index_page><show_page><new_page><edit_page>
POSTing and PUTing related objects
If the parameters to create or update contains values for belongs_to or has_many associations, the controller will create / update the related objects, within some limits.
(To be continued)
POSTING and PUTing dates
Dates can be set either with year, month, day (etc.) fields or with strings that can be parsed as dates.
HTTP Parameters
event[date][year]=2007&event[date][month]=March&event[date][day]=14
Or as a string
HTTP Parameters
event[date]=March+14th+2007
If Chronic is installed it will be used
HTTP Parameters
event[date]=next+wednesday
Permission System
Before creating, updating or destroying any record, the permissions are checked for the current user. Note there is always a current user in a Hobo app. If no one is logged in, the current user is an instance of the Guest class which typically defines nothing but the permission methods.
The controller will also refuse to render show or edit for a record the user is not permitted to view, and will refuse to render new for a record the user is not allowed to create. Note the creator_attr (see Model Extensions) will be set to the current user before this check is performed.
permission_denied method
If any permission check fails, the permission_denied method is called, and the request goes no further. This method is intended to be overridden. The default renders a curt message and sets a 403 header.
Collection Actions
For every has_many collection in your model, the controller will add two actions. For example, a BlogPost which has_many comments. The actions created will be
* show_comments * new_in_comments
For a BlogPost The routes look like, respectively,
* /blog_posts/12/comments * /blog_posts/12/comments/new
Which are available as the named routes:
* blog_post_comments_url(id) * new_blog_post_comment_url(id)
Both actions will result in a permission_denied call if the current user does not have view permission for the collection (can_view?(post, :comments)).
show_comments
The template for this action is either views/blog_posts/show_comments.dryml or, if that is not found, the more general views/comments/show_in_collection.dryml. (In fact they don’t have to be DRYML templates. A .rhtml suffix would also work).
If neither template is available, Hobo will render <show_collection_page/>
In the view, the owner of the collection is available as both @owner and @blog_post (for this example), and the collection is @this (i.e. the DRYML context).
new_in_comments
This is intended for a form to create a new comment for that particular post.
The template is either views/blog_posts/new_comment.dryml or failing that views/comments/new_in_collection.
If neither template is available, Hobo will render <new_in_collection_page/>
In the view, the owner of the collection is available as both @owner and @blog_post (for this example), and the new object (e.g. the Comment) is @this (i.e. the DRYML context).
If you use Hobo Rapid to render the form, a hidden field will be included to make sure the created object (Comment) belongs to the correct owner (the BlogPost).
Customising the controller
Overview
If you need custom behaviour, the automatic actions provided by Hobo can simply be overridden
Controller class
def show
... custom show action ...
end
Often there is a need to partially customise the action, but keep the rest of Hobo’s behaviour. For every action, there is a corresponding Hobo action that provides the default behaviour.
By just calling hobo_show, this overridden show behaves the same as Hobo’s provided show action.
Controller class
def show
hobo_show
end
To customise the behaviour, hobo_show can be passed various parameters, e.g. to optimise database performance with eager loading:
Controller class
def show
hobo_show :this => Post.find(params[:id], :include => [ :comments ])
end
You can also override the response by providing a block
def show
hobo_show do
render :text => "show what?"
end
end
You can also customise the response to “object not found”, and “permission denied”:
def show
hobo_show :permission_denied_response => proc {
render :text => "On yer bike sunshine!"
}, :not_found_response => proc {
redirect_to homepage_url
}
end
General customisation parameters
| Parameter | Applies to | Usage |
|---|---|---|
:this | Non-collection actions | Sets the object to show/create/edit/destroy |
| Provide a block | All actions | Implement the render or redirect response |
:html_response | CRUD actions | A proc that implements the response if the client wants HTML |
:js_response | CRUD actions | A proc that implements the response if the client wants JavaScript (i.e. an Ajax call) |
:invalid_html_response | create and update | A proc that implements the HTML response if the create/update had validation errors |
:invalid_js_response | create and update | A proc that implements the JavaScript (ajax) response if the create/update had validation errors |
:permission_denied_response | All actions | A proc that implements the response |
:not_found_response | All except new and new_in_collection | A proc that implements the response |
:set_creator | new and new_in_collection | Pass false to prevent Hobo from calling the set_creator method with the current user. |
Customising collection actions
For every collection that is published, Hobo provides, e.g. for a comments collection, show_comments and new_comment actions. In order to customise these, override the default actions like this:
Controller class
def show_comments
hobo_show_collection :comments
end
def new_comment
hobo_new_in_collection :comments
end
Parameters can be passed to those methods in order to customise the behaviour (see next section).
Customisation parameters for collection actions
| Parameter | Applies to | Usage |
|---|---|---|
:total_number | index, show_* | Set’s the total number of records for pagination |
:page_size | index, show_* | Number of records per page |
:page | index, show_* | Current page to show |
:owner | show_*, new_* | Set the owner of the collection |
:collection | show_*, new_* | Set the collection |
Extra Show Actions
Sometimes one show method is not enough. E.g. A User might have a main page plus an “options” page, where that user’s settings are edited.
class UsersController < ApplicationController
show_method :options
end
This adds an alias of show, with the route
/users/3;options
Available as a named route:
user_options_url(id)
Extra Collections
Models often define methods that return arrays of other models. That is, custom versions of the associations created by has_many. To publish these the same way Hobo publishes has_many collections:
class UsersController < ApplicationController
publish_collection :comments_for_moderation
end
The collection is then available just as your has_many collections are available
Web Methods
Sometimes, a purely RESTful controller is not possible. Sometimes you need a “remote procedure call”, for example, to request a password reset.
class UsersController < ApplicationController
web_method :password_reset
def password_reset
...
end
end
The model in question must then define password_reset_callable_by?(caller) to determine if the current user is allowed to call the method. If the method is not defined, only the super-user can call the method.
Data Filters
The records included on the index page can be filtered according to defined queries. These queries can be customised via HTTP parameters.
Controller class
class UsersController < ApplicationController
def_data_filter :name_starts do |s|
name_starts s
end
end
The block is a Hobo block-query which is passed to the controller’s model. Request parameters are passed to the block
URL with query
/users?where_name_starts=a
Another example
Controller class
class UsersController < ApplicationController
def_data_filter :not_in_group do |group|
not_in(Group.find(group).members)
end
end
URL with query
/users?where_not_in_group=45
Auto-completion
Controller class
class UsersController < ApplicationController
autocomplete_for :name
end
This creates an action completions that expects a for parameter
Auto-complete URL
/users/completions?for=name
The result is a Scriptaculous friendly <ul>.
Completions and data-filters can be combined. A good example is a using auto-complete when adding users to a group. People already in the group should not be included in the completions.
Add to group example – Controller code
class UsersController < ApplicationController
def_data_filter :not_in_group do |group|
not_in(Group.find(group).members)
end
autocomplete_for :name
end
Add to group example – URL
/users/completions?for=name¬_in_group=45
Back-button caching
It is very common for dynamic Rails sites to suffer from problems with the back-button. The browser displays the previous page from its cache, and so the user sees stale information. By default Hobo sets headers to tell the browser not to cache these pages, so the back button results in a fresh GET request.
No-Cache Headers
headers["Pragma"] = "no-cache"
headers["Cache-Control"] = "no-store"
headers["Expires"] ='0'
Scriptaculous InPlaceEditor support
If a hobo_model_controller gets an Ajax call to update and only a single attribute is updated, the new value of that attribute will be rendered using DRYML’s core <show> tag, and the result set as a result update (see Hobo Ajax) with the result name new_field_value. Hobo rapid uses this feature to support in-place-editing functionality.
Debug support
(The following are also available as view helpers)
In your actions:
debug a, b
Will pass a pretty-print of a and b to logger.debug. debug also returns its first argument so you can do things like changing
x = some_nasty_expression
To
x = debug some_nasty_expression
You get the debug info, and x is still assigned
There’s also a shorthand for the common raise x.inspect idiom:
abort_with a, b
This raises a pretty-printed string of both a and b