0
+> The tutorial has been partially updated to work with Hobo 0.8 pre 1. The end of the updated content is marked below with a note like this one.
0
# Hobo Tutorial -- Agility
0
The full source code for the Agility app is available at [http://github.com/tablatom/agility](http://github.com/tablatom/agility/tree/master).
0
@@ -36,13 +40,13 @@ The `hobo` command is like the Rails command, it will create a new blank Rails a
0
With Hobo, you get a bare-bones application immediately. Let's create the database and then run the app. We first need to use the migration generator to create the basic database:
0
$ ./script/generate hobo_migration
0
(Tip: Windows users: Whenever you see `./script/something`, you will need to instead do `ruby script/something`)
0
Respond to the prompt with `m` and then give the migration a name. Then:
0
You should be able to sign up. In the next section we'll be starting to flesh out the basics of the app.
0
@@ -92,7 +96,10 @@ Here's how we create these with a Hobo generator:
0
$ ./script/generate hobo_model_resource project name:string
0
$ ./script/generate hobo_model_resource story title:string body:text status:string
0
$ ./script/generate hobo_model_resource task description:string
0
- $ ./script/generate hobo_model_resource task_assignment
0
+Task assignments are just a back-end model. They don't need a controller, so:
0
+ $ ./script/generate hobo_model task_assignment
0
The field declarations have been created by the generators, but not the associations. Go ahead and add the associations, just below the `fields do ... end` declaration in each model, as follows:
0
@@ -152,16 +159,14 @@ Now watch how Hobo can create a single migration for all of these:
0
$ ./script/generate hobo_migration
0
-Note: Hobo's automatic routing happens when the application starts. You'll need to stop and start the web-server in order for the application to reflect all these new models and controllers.
0
-Fire up the app. It's kind of a weird UI at this stage, but we do actually have a working application. Make sure you are logged in as an administrator (e.g. the user who signed up first), and spend a few minutes populating the app with projects, stories and tasks.
0
+Fire up the app. It's not a polished UI of course, but we do actually have a working application. Make sure you are logged in as an administrator (e.g. the user who signed up first), and spend a few minutes populating the app with projects, stories and tasks.
0
With some more very simple changes, and without even touching the views, we can get surprisingly close to a decent UI.
0
# Part 2 -- Removing actions
0
-By default Hobo has given us a full set of restful actions for every single model/controller pair. Many of these routes are inappropriate for our application. For example, why would
be want an index page listing every Task in the database? We only really want to see tasks listed against stories and users. We need to disable the routes we don't want.
0
+By default Hobo has given us a full set of restful actions for every single model/controller pair. Many of these routes are inappropriate for our application. For example, why would
we want an index page listing every Task in the database? We only really want to see tasks listed against stories and users. We need to disable the routes we don't want.
0
There's an interesting change of approach here that often crops up with Hobo development. Normally you'd expect to have to build everything yourself. With Hobo, you often get given everything you want and more besides. Your job is to take away the parts that you *don't* want.
0
@@ -177,14 +182,30 @@ To
0
Restart the server and you'll notice that Tasks has been removed from the main nav-bar. Hobo's generic pages (which are just clever defaults that you can override as much or as little as you like) know how to adapt to changes in the actions that you make available.
0
-Here's another similar trick. Browse to one of your stories. See that "New Task" link at the bottom? That's kind of clunky for the user -- it would be much nicer if the new task form (which only has one field after all) was in-line in the same page. Edit the `auto_actions` declaration in `stories_controller.rb` to look like this:
0
+Here's another similar trick. Browse to one of your projects. You'll see the page says "No stories to display" but there's no way to add one. Hobo has support for this but we need to switch it on. Add the following declaration to the stories controller:
0
+ auto_actions_for :project, [:new, :create]
0
+This creates nested routes and their corresponding actions:
0
+ - `/project/123/stories/new` routed to `StoriesController#new_for_project`
0
+ - `/project/123/stories` (POST) routed to `StoriesController#create_for_project`
0
+Hobo's page generators will respond to the existence of these routes and add a "New Story" link to the project page, and an appropriate "New Story" page.
0
- auto_actions :all, :except => :new_task
0
+Create a story and you'll see the story has the same issue with it's task - there's no way to create one. Again we can add the `auto_actions_for` declaration to the tasks controller, but this time we'll only ask for a `create` action and not a `new` action:
0
+ auto_actions_for :project, :create
0
-Restart the server and refresh the story page. Ta da!
0
-So far we've seen the black-list style where you list what you *don't* want. There's also white-list style where you list what you do want, e.g.
0
+Hobo's page generator can cope with the lack of a 'New Task' page -- it gives you an in-line form on the story page.
0
+We'll now continue and configure the available actions for all of the controllers. So far we've seen the black-list style where you list what you *don't* want:
0
+ auto_actions :all, :except => :index
0
+There's also white-list style where you list what you do want, e.g.
0
auto_actions :index, :show
0
@@ -201,17 +222,16 @@ The opposite is handy for things that are manipulated by ajax but never viewed d
0
Work through your controllers and have a think about which actions you want. You need to end up with:
0
- * Stories `:write_only, :show, :edit, :new, :create_task`
0
- * Tasks: `:write_only, :edit`
0
- * TaskAssignments: `:write_only` (or maybe we don't even need this controller?)
0
+ * Projects: `auto_actions :all`
0
+ * Stories `auto_actions :all, :except => :index`
0
+ * Tasks: `auto_actions :write_only, :edit`
0
-Have a play with the application with this set of actions in place
(don't forget to restart the server). Looking pretty good!
0
+Have a play with the application with this set of actions in place
. Looking pretty good!
0
# Part 3 -- Permissions
0
-So far we've only done two things to
are app: created some models & controllers, and specified which actions are available. There's one more thing we typically do when creating a new Hobo app, before we even touch the view layer. We modify permissions in the model layer.
0
+So far we've only done two things to
our app: created some models & controllers, and specified which actions are available. There's one more thing we typically do when creating a new Hobo app, before we even touch the view layer. We modify permissions in the model layer.
0
## Introduction to permissions
0
@@ -224,11 +244,14 @@ You might have noticed methods like this one in the generated models:
0
That tells Hobo that only administrators are allowed to create this kind of model. Before every create, update and delete (destroy) operation, Hobo calls one of these methods passing the current user. Only if the method returns true is the operation allowed to continue.
0
-Furthermore, the *Rapid* DRYML tag library (that's the part of Hobo that creates the UI automatically for you) knows how to interrogate the permissions and adapt accordingly. For example, Rapid w
on't generate a "New Project" link if the current user does not have permission to create a project.
0
+Furthermore, the *Rapid* DRYML tag library (that's the part of Hobo that creates the UI automatically for you) knows how to interrogate the permissions and adapt accordingly. For example, Rapid w
ill only generate a "New Project" link if the current user has permission to create a project.
0
-You can see this feature in action by logging out and browsing around the app. You'll notice that all the 'new' and 'edit' links have disappeared. If you experiment by chang
e `user.administrator?` to `true` in some permission methods, you'll see operations start to become available.
0
+You can see this feature in action by logging out and browsing around the app. You'll notice that all the 'new' and 'edit' links have disappeared. If you experiment by chang
ing `user.administrator?` to `true` in some permission methods, you'll see operations start to become available.
0
-## Customize the permissions in Agility
0
+Tip: Hobo provides an easy way to switch user so you can see how things look to different people. In development mode you should see the change-user menu in the top-left.
0
+## Customise the permissions in Agility
0
For the purposes of the tutorial you can make your own decisions about who should be allowed to do what. In the spirit of agile methods, we probably don't want to lock things down too much. Here's a suggestion:
0
@@ -244,23 +267,20 @@ A permission that says "only signed up users" looks like this:
0
(There's also `user.guest?`)
0
-You might need to sign up a new user so you've got a non-admin to test things with.
0
-Tip: Hobo provides an easy way to switch user so you can see how things look to different people. In development mode you should see the change-user menu in the top-left.
0
+You might need to sign up a new user so you've got a non-admin to test things with.
0
## Permissions for data integrity
0
-The permissions system is not just for opening operations for some users but not others, it is also used to prevent operations that don't make sense for anyone. For example, you've probably noticed that the default UI allows stories to be moved from one project to another. That's arguably not a sensible operation for *anyone* to be doing.
We can prevent it with this method in `story.rb`:
0
+The permissions system is not just for opening operations for some users but not others, it is also used to prevent operations that don't make sense for anyone. For example, you've probably noticed that the default UI allows stories to be moved from one project to another. That's arguably not a sensible operation for *anyone* to be doing.
Before we fix this, browse to an "Edit Story" page and notice the menu that lets you chose a different project. Now prevent the project from changing with this method in `story.rb`:
0
def updatable_by?(user, new)
0
user.signed_up? && same_fields?(new, :project)
0
-
The `updatable_by?` method has a parameter: `new`. This is a duplicate instance of `self` with the proposed changes applied. So for example, if you wanted to know if the user is clearing the association to the project, you could test for `new.project.nil?`. The `same_fields?` helper is a convenient way to assert that certain fields have not been changed. There's also `only_changed_fields?` which is more convenient if you want to prevent changes to all but a certain few fields.0
+
Refresh the browser and you'll see that menu removed from the form automatically. 0
-
Rapid will respond to this change by removing the project selector from the edit-story page.
0
+
The `updatable_by?` method has a parameter: `new`. This is a duplicate instance of `self` with the proposed changes applied. So for example, if you wanted to know if the user is clearing the association to the project, you could test for `new.project.nil?`. The `same_fields?` helper is a convenient way to assert that certain fields have not been changed. There's also `only_changed_fields?` which is more convenient if you want to prevent changes to all but a certain few fields.
0
Make a similar change to prevent tasks being moved from one story to another.
0
@@ -274,12 +294,7 @@ Sometimes however, Hobo can't figure out edit permission unless you tell it expl
0
-We also need to tell Hobo that it's ok for the TaskAssigment objects can be created and deleted automatically as a side effect of saving changes to the Task. Change the `has_many :users` declaration in `task.rb` to:
0
- has_many :users, :through => :task_assignments, :managed => true
0
You should now get a nice javascript powered control for assigning users in the edit-task page.
0
@@ -302,20 +317,26 @@ DRYML has a feature called *polymorphic tags*. These are tags that are defined d
0
The file `app/views/taglibs/application.dryml` is a place to put tag definitions that will be available throughout the site. Add this definition to that file:
0
- <def tag="card" for="Task">
0
- <creation-details: replace>
0
- Assigned users: <repeat:users join=", "><a/></repeat><else>None</else>
0
+ <extend tag="card" for="Task">
0
+ Assigned users: <repeat:users join=", "><a/></repeat><else>None</else>
0
OK there's a lot of new concepts thrown at you at once there :-)
0
-First off, refresh the story page. You'll see that in the cards for each task, we've replaced the creation time and date (which we don't really want to see) with the list of assigned users. The users are clickable - they link to each users home page (which doesn't have much on it at the moment).
0
+First off, refresh the story page. You'll see that in the cards for each task there is now a list of assigned users. The users are clickable - they link to each users home page (which doesn't have much on it at the moment).
0
+The `<extend>` tag is used to extend any tag that's already defined. The body of `<extend>` is our new definition. It's very common to want to base the new definition on the old one, e.g. just inserting a bit of extra content as we've done here. We can do that by calling the "old" definition, which is available as `<old-card>`.
0
+> The content below here has *not* been updated for Hobo 0.8 pre 1
0
The card that we defined calls `<base-card>`, but it overrides some of the content using DRYML's named parameters (`<creation-details:>`). The `replace` attribute means we want to remove the creation-details entirely. For the replacement we insert a `<div>` and use the `<repeat>` tag to insert the list of links. Some things to note:
Comments
No one has commented yet.