public
Fork of tablatom/hobocentral
Description: Markdown sources for http://hobocentral.net
Homepage: http://hobocentral.net
Clone URL: git://github.com/drnic/hobocentral.git
Search Repo:
drnic (author)
Mon May 12 18:53:51 -0700 2008
commit  3a3c8578c31082d16fffa52a5fd057c3964b04b4
tree    6eb6cb79f68201ae15e3b5293d2ccace14864a88
parent  7aeb1956a77ddd4f1afcd217d008a46ff83ca790
hobocentral / agility-tutorial.markdown
100644 591 lines (358 sloc) 32.466 kb
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# Hobo Tutorial -- Agility
 
**NOTE: This is the tutorial that was recently delivered at the Ruby Fools conference in Copenhagen. It hasn't been properly chapterised for the web yet.**
 
The full source code for the Agility app is available [here](http://github.com/tablatom/agility/tree/master).
 
In this tutorial we'll be creating a simple "Agile Development" application -- _Agillity_. The application tracks projects which consist of a number of user stories. Stories have a status (e.g. accepted, under development...) as well as a number of associated tasks. Tasks can be assigned to users, and each user can see a heads-up of all the tasks they've been assigned to on their home page.
 
This is a bit of an in-at-the-deep-end tutorial -- we build the app the way you would assuming you had already got the hang of the way Hobo works. In the later stages new concepts start coming thick and fast. The goal here is to show you what's possible, and give you a flavour of Hobo-style application development, rather that to provide detailed background on how everything workds. Don't worry about it, it's fun! If you'd rather take things a bit slower, you might prefer the [POD tutorial](http://hobocentral.net/pod-tutorial).
 
 
# Part 1 -- Getting Started
 
 
## Introduction to Hobo
 
Hobo is a bunch of extensions to Ruby on Rails that are designed to make developing any kind of web application a good deal faster and more fun. This tutorial is designed to show off Hobo's ability to get quite sophisticated applications up and running extremely quickly.
 
While Hobo is very well suited to this kind of throw-it-together-in-an-afternoon application, it is equally useful for longer term projects, where the end result needs to be very meticulously crafted to the needs of its users. Hopefully the tutorial will give you an idea of how to take your Hobo/Rails application much further.
 
For more info on Hobo please see [hobocentral.net](http://hobocentral.net)
 
## Before you start
 
You'll need a working Ruby on Rails setup. We're assuming you know at least the basics of Rails. If not, you should probably work through a Rails tutorial before attempting this one.
 
And you'll need Hobo! Although Hobo is in fact a group of Rails plugins, it is also available as a gem which gives you the useful `hobo` command:
 
    $ gem install hobo
 
Let's get started!
 
## Create the app
 
The `hobo` command is like the Rails command, it will create a new blank Rails app that is set-up for Hobo. It's the equivalent of running the `rails` command, installing a few plugins and running a few generators.
 
    $ hobo agility
 
With Hobo, you get a bare-bones application immediately. Let's run it:
 
    $ cd agility
    $ ./script/server
 
(Tip: Windows users: Whenever you see `./script/something`, you will need to instead do `ruby script/something`)
 
If you try and sign up you'll see that part is not working yet - because we haven't created the `users` table. There's a good reason why the `hobo` command doesn't do that automatically but we'll gloss over that for now. To create the table, use Hobo's migration generator:
 
(Tip: leave the server running and open a new terminal window)
 
    $ ./script/generate hobo_migration
 
Respond to the prompt with `m` and then give the migration a name
 
You should now be able to sign up. By default, Hobo gives administrator status to the first user that signs in. (The concept of an administrator is built into Hobo, but if it doesn't make sense for your application it's trivial to do away with).
 
In the next section we'll be starting to flesh out the basics of the app.
 
 
## Interface first Hobo style
 
The next thing we're going to do is sketch out our models. If you're a fully signed up devotee of the Rails Way, that should ring a few alarm bells. What happened to [interface first][]? We do believe in Interface First. Absolutely. But for Hobos, interface first means first priority, not first task.
 
[Interface First]: http://gettingreal.37signals.com/ch09_Interface_First.php
 
The reason is, we think we've rewritten this rule:
 
> Design is relatively light. A paper sketch is cheap and easy to change. html designs are still relatively simple to modify (or throw out). That's not true of programming. Designing first keeps you flexible. Programming first fences you in and sets you up for additional costs.
 
In our experience, experimenting with an app by actually building a prototype with Hobo, is _far_ quicker than creating html designs. How's that for getting real? We could waffle for a good while on this point, but that's probably best saved for a blog post. For now let's dive in and get this app running.
 
 
## The models
 
Let's review what we want this app to do:
 
 * Track multiple projects
 * Each having a collection of stories
 * Stories are just a brief chunk of text
 * A story can be assigned a current status and a set of outstanding tasks
 * Tasks can be assigned to users
 * Users can get an easy heads up of the tasks they are assigned to
 
Sounds to me like we just designed our models. We'll need:
 
 * `Project` (with a name)
  has many stories
 * `Story` (with a title, description and status)
  belongs to a project
  has many tasks
 * `Task` (with a description)
  belongs to a story
  has many users (through task-assignments)
 * `User` (we'll stick with the standard fields provided by Hobo)
  has many tasks (through task-assignments)
  
Hopefully the connection between the goal and those models is clear. If not, you'll probably find it gets easier once you've done it a few times. Before long you'll be throwing models into your app without even stopping to write the names down. Of course -- chances are you've got something wrong, made a bad decision. So? Just throw them away and create some new ones when the time comes. We're sketching here!
 
Here's how we create these with a Hobo generator:
 
  $ ./script/generate hobo_model_resource project name:string
  $ ./script/generate hobo_model_resource story title:string body:text status:string
  $ ./script/generate hobo_model_resource task description:string
  $ ./script/generate hobo_model_resource task_assignment
  
The field declarations have been created by the generators, but not the associations. Go ahead and edit the associations in the models to reflect the description above.
 
The associations go just below the `fields do ... end` declaration in each model.
 
#### `app/models/project.rb`
 
    class Project < ActiveRecord::Base
      ...
      has_many :stories, :dependent => :destroy
      ...
    end
{: .ruby}
### `app/models/story.rb`
 
    class Story < ActiveRecord::Base
      ...
      belongs_to :project
      
      has_many :tasks, :dependent => :destroy
      ...
    end
{: .ruby}
 
### `app/models/task.rb`
 
    class Task < ActiveRecord::Base
      ...
      belongs_to :story
 
      has_many :task_assignments, :dependent => :destroy
      has_many :users, :through => :task_assignments, :managed => true
      ...
    end
{: .ruby}
 
### `app/models/task_assignment.rb`
 
    class TaskAssignment < ActiveRecord::Base
      ...
      belongs_to :user
      belongs_to :task
      ...
    end
{: .ruby}
 
### `app/models/user.rb`
 
    class User < ActiveRecord::Base
      ...
      has_many :task_assignments, :dependent => :destroy
      has_many :tasks, :through => :task_assignments
      ...
    end
{: .ruby}
 
Now watch how Hobo can create a single migration for all of these:
 
  $ ./script/generate hobo_migration
  
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.
  
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 the user who signed up first, and spend a few minutes populating the app with projects, stories and tasks.
 
With some more very simple changes, and without even touching the views, we can get surprisingly close to a decent UI.
 
 
# Part 2 -- Removing actions
 
By default Hobo has given us a full set of restful actions routes 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.
 
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.
 
Taking that example -- removing the index action from TasksController, here's how we'd do that. in `app/controllers/tasks_controller.rb`, change
 
  auto_actions :all
{: .ruby}
  
To
 
  auto_actions :all, :except => :index
{: .ruby}
  
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.
 
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` to look like this:
 
  auto_actions :all, :except => :new_task
{: .ruby}
  
Restart the server and refresh the story page. Ta da!
  
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.
 
    auto_actions :index, :show
{: .ruby}
 
There's also a handy shortcut to get just the read-only routes (i.e. the ones that don't modify the database)
 
  auto_actions :read_only
{: .ruby}
  
The opposite is handy for things that are manipulated by ajax but never viewed directly:
 
  auto_actions :write_only
{: .ruby}
 
Work through your controllers and have a think about which actions you want. You might end up with something like:
 
 * Projects: `:all`
 * Stories `:write_only, :show, :edit, :new`
 * Tasks: `:write_only, :edit`
 * TaskAssignments: `:write_only` (or maybe we don't even need this controller?)
 
Have a play with the application with this set of actions in place (don't forget to restart the server). Looking pretty good!
 
 
# Part 3 -- Permissions
 
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.
 
## Introduction to permissions
 
You might have noticed methods like this one in the generated models:
 
  def creatable_by?(user)
   user.administrator?
  end
{: .ruby}
 
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.
 
Furthermore, the *Rapid* DRYML tag library (that's the part of Hobo that creates the UI automatically for you) knows how to interrogate the models permissions and adapt accordingly. For example, Rapid won't generate a "New Project" link if the current user does not have permission to create a project.
 
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 change `user.administrator?` to `true` in some permission methods, you'll see operations start to become available.
 
## Customize the permissions in Agility
 
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:
 
 * Only administrators can create, edit and delete projects
 * Stories and tasks are open to change by all logged in users.
 
A permission that says "only logged in users" looks like this:
 
  def creatable_by?(user)
   !user.guest?
  end
{: .ruby}
  
You might need to sign up a new user so you've got a non-admin to test things with.
 
Tip: Hobo provides an easy way to switch user so you can see how things look to different people. Just visit (substituting `localhost` and `3000` as appropriate):
 
  http://localhost:3000/dev/set_current_user?name=Fred
  
*Not available in production mode!* Once you've typed this once, your can use your browser's history completion to quickly flick between different users.
 
## Permissions for data integrity
 
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 probably not a sensible operation for *anyone* to be doing. We can prevent it with this method in `story.rb`:
 
  def updatable_by?(user, new)
   !user.guest? && same_fields?(new, :project)
  end
{: .ruby}
 
Note that the `updatable_by?` method is called with `self` in the current state, and the `new` argument is an instance of the same class in the proposed new state. 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.
 
Rapid will respond to this change by removing the project selector from the edit-story page.
 
Make a similar change to prevent tasks being moved from one story to another.
 
## Edit permission
 
One permission method that is not present by default is`editable_by?`. This method tells Rapid if the user is allowed to see an edit control for a given field. The reason this method is often omitted is that Hobo does a pretty good job of figuring out edit permission automatically from the rules you've given for update permission (if you think about it, these are two sides of the same coin).
 
Sometimes however, Hobo can't figure out edit permission unless you tell it explicitly. A common example is `has_many` associations. If you don't specify edit permission for these, Hobo just defaults to not-editable. That's why, up until now, the task assignments are completely absent from the UI. We can fix that by adding this to `task.rb`:
  
  def users_editable_by?(user)
    !user.guest?
  end
{: .ruby}
  
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