RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

@Tom, yep, that's how I would do it.

Avatar

@Tal, it's hard to say what the problem is without seeing the code and stack trace. I recommend posting on railsforum.com about it.

@Aslan, I'll be covering how to handle validation errors in the near future.

Avatar

@David, good question. You can create a more traditional TasksController which handles the CRUD operations (create, read, update, destroy). You can then link to these actions on the project show page or the tasks index page. This way the user can fallback to this approach if he doesn't have javascript enabled.

Being able to add/edit multiple models at once is more for convenience and doesn't have to be the only way it is done.

Avatar

Great last couple of Railscasts! How do you go about grace degradation with something like this?

Avatar

So having a form that deals with more than one model, what is the best way to handle errors?

Avatar

If you want to do this the other way around. Say fullname in database (just example) and you want to show first/last name. Do you add the get/set for first+lastname and merge them with a before save hook?

Avatar

This might be a preview to Ryan's railscast. I found this tidbit: http://www.railsforum.com/viewtopic.php?id=2548.

It helped me with my validation problem. Can't wait for the railscasts though. Keep up the great work Ryan.

Avatar

I've just discovered Railscasts and thought I'd start at the first episode. Low and behold, absolute genius, I cut one of my request times in half by caching the return from my ldap lookup. Thanks, hoping to find many more gems as I work my way through the episodes.

Avatar

How does the controller look like?
How does it know to render the rjs?

Avatar

I was using RJS doing similar tasks, this is a much DRY and faster approach (although the html source code is harder to read, I can live with it by gaining that much!). Using helper function for the RJS is cool. Shared forms in different viewers become cleaner by calling RJS function in the application_helper. I can't wait to see the proper way to do validations in your complex form editing episode!

Avatar

Nice episode. I'm waiting for Part 3! :)

Avatar

I'm having an issue, the model I'm using in a similar fashion to your Task model has no id column (it's a hm:t join table). I'm getting this error when i try and run it:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.column

any idea on how to fix this?

Thanks for the awesome screencasts

Avatar

你說中国話,我說中國話...

Avatar

@Kyle, you can do a "blank?" check to only create tasks where the fields aren't blank.
http://pastie.caboo.se/104922

@Dennis, this is a little out of the scope of this tutorial. I recommend posting your question on railsforum.com if you haven't already.

@Joel, collection_select has worked for me with this approach. Are you certain it's not working? I was on edge rails when I tested it so maybe that's part of the issue.

Avatar

Hey Ryan, this approach seems to have the same problem with collection_select as it does with date_select. May be an issue with all selects. Given this im not sure this approach is valuable unless your form will only have simple text fields.
thanks
Joel

Avatar

@AdulteratedJedi, I think UJS deserves its own episode. I'll consider doing one in the future. Thanks for the suggestion!

@Jlehman, there are ways to handle the validation properly, I just haven't had enough time to show it yet. Hopefully I will get around to it in the next episode.

@Mislav, I created the surrounding ".task" div so it is easier to add more fields to the task if you need to. Thanks for the tip on $(this), I'll adjust the sample code here to reflect that.

Avatar

Ryan,

Why did you create a ".task" DIV to remove the task? You could have just removed the paragraph. Also, "this" needs to be enclosed in a "$()" call:

$(this).up('p').remove()

This is because of IE, in which DOM elements are not extended by default.

Avatar

Is it a matter of passing an errors hash back to the parent model? Can you still have the additional form change to show that errors have occurred like it does when it is on it's own view page using it's own model?

Avatar

As always a well done screencast Ryan. I have no complaints about the javascript, it is the predominant scripting language of the web after all. :)

In the case of this simple single field child model you can add errors by hand to the parent model (which the form is tied to) easily

Avatar

Great episode. You teased me though. You mentioned in last episode that by moving the task creation into the projects view we lost the error handling of the task model. Will you be covering that in future episodes? Could you point in a direction to learn about that. Thank you again for your great work.

Avatar

I have Mixed thoughts about this episode, useful information as usual, however I do not like the Obtrusiveness of the javascript.

Maybe you could clean it up in a later episode using something Dan Webbs excellent Low Pro Library, or indeed UJS?

Avatar

I agree that holding on to _already persisted_ objects in the session is a bad idea.

A seemingly good reason to hold onto an object in session is when you have an object that needs to be composed over many actions, and should not be saved to the database until all of those actions are complete.

Imagine an application where a wizard-type interface is in effect, and you are using the wizard to build up the state of an object. Further assume that the user can decide to abandon this wizard process at any time.

One way to handle this is to hold the new, unsaved object in session and that way, it never gets saved to the database unless that's what the user wants to do.

This way, we don't have to save objects in some intermediate (not yet valid) state, and then delete them if the user abandons them at some point. The abandoned objects will simply never ever be saved.

The downside of this approach is that some objects may be too big to hold in the session before they are saved. In this case, a caching solution may be used to hold these larger types of objects.

Avatar

How do I do if I have 3 models where
agent: mas_many :orders
orders habtm :channels
channels habtm :orders

How to save the channels within agent[channel_attribut] so it get the right order_id?

Avatar

Supposing every project can have upto 10 tasks but not necessarily 10 for every project and the controller and view look same as in the tutorial, how can I filter out the tasks fields that are left blank?

Thanks ryan :)

Avatar

I know i'm going to get destroyed for this comment: but maybe they want to ensure task is used solely for rake tasks? Doh...i'm sure thats wrong

Avatar

Do you know why one of the tests fails:
ruby test/unit/user_test.rb

 1) Failure:test_should_require_password(UserTest) [test/unit/user_test.rb:23:in `test_should_require_password' /Users/ia00stai/railsdev/signmeup/trunk/signmeup/config/../lib/authenticated_test_helper.rb:16:in `assert_difference' /Users/ia00stai/railsdev/signmeup/trunk/signmeup/config/../lib/authenticated_test_helper.rb:24:in `assert_no_difference' test/unit/user_test.rb:21:in `test_should_require_password']:<nil> is not true.

Avatar

Mislav,

I modified view_helpers.rb to add clean and simple ajax support.

Look here http://batmanrails.blogspot.com/2007/10/ajax-support-for-willpaginate.html

-Batman

Avatar

Good point, just noticed Task is in the reserved words list on the wiki:

http://wiki.rubyonrails.org/rails/pages/ReservedWords

Anyone know why? So far I haven't had any issues with it.

Avatar

Great Episode - saved a ton of ugly work.

Slightly Off Topic - I thought "Task" was a reserved word.

Avatar

Hi Ryan,

Testing is about proving that the code is fit for purpose.

Regression testing is about proving that the modified/new code does not impact on the existing code - which is what you are hinting at above.

Avatar

Note that you DON'T want to use "_" as the separator between the id and the permalink, if you rely on String#to_i. The screencast uses "-" which is fine.

If you use "_" and the permalink part begins with a number, you could get e.g. "123_456blah", and "123_456blah".to_i is 123456, not the expected 123. This is because "_" can be used in Ruby to make numbers more readable.

Avatar

@Mark, I picked up the practice after reading a Jay Fields article. He explains it better than I can:

http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html

Avatar

Thanks for the episode Ryan, although I wonder why you couldn't have done it a week earlier as it would have saved me 2 days of work!!

Avatar

In this episode you mentioned that it is a best practice to only do one assert per test. Why is that? I find that when testing a method I have several things I want to check to make sure they all work together.

Avatar

Thanks a lot Ryan,

Pragmatic REST is good too. I guess the TasksController could still be useful if I want to present the Projects and Tasks as ActiveResources

Avatar

@Rene, the tasks controller doesn't play any part here, it's all in the projects controller. Since we're going through only one form here it only uses one controller.

I suppose some REST extremists may not like this, but I don't see any problem with it. We aren't adding anymore actions to the controller. In fact there's very little code in the controller concerning tasks. It's primarily concerned with creating the project and the rest is done in the project model through the parameters which are passed to it.

Avatar

Ryan,

does this mean that the tasks are created by the ProjectsController (as opposed to by the TasksController generated by scaffold_resource)?

I was under the assumption that in order to be RESTful, one should use the TasksController.create action for creating a new task. (and TasksController.edit in order to update).

Avatar

Try this (pluralize sessions):

map.resource :session, :controller => 'sessions'

Avatar

If use 'sessions' as plural when i generate, i get this error when i try to start the login page: "uninitialized constant SessionController". I have "map.resource :session, :controller => 'session'" in my routes.rb.

Avatar

@Rene, this should work fine with the code generated by scaffold resource. The code I start out with in the screencast is not far from that so you just need to make the appropriate changes in the view and model.

@foozilla, tasks.build will only create the model in memory where as tasks.create will save it to the database. I don't want to save it to the database yet in case validation fails (it will be saved later when the project is saved) so I use tasks.build.

@Bryce, it's hard to say without knowing more about your problem domain. I recommend making a post on railsforum.com where you can discuss the details.

Avatar

Well I got dates to work using a hack, if anyone has this problem, not sure its the best way but I just used:

<%= select_date (task.duedate, :prefix => aprefix %>

I set the prefix in the loop to be a constant + id of the task. Then when im creating the tasks I fish out the param for the date. Not the prettiest and it nullifies some of the elegance of this approach but it works.

Avatar

Thanks for the great screen cast Ryan. You're right on time for an issue I was tackling. Thanks a lot.

I only kind of remember a way of setting up a class so that one name encompasses several models. For example: Employees have names, addresses and phone numbers. The class Employee contains the models names, addresses and phone_numbers. The point is that you can reference each of the models by calling on the Employee class. Could this fit into your solution or is this a whole new bag of worms?

Thanks again,
Bryce

Avatar

Will you do a cast on caching on rails.
is there way to cache part of the page?

thanks much Ryan

Avatar

Is @project.tasks.build same as
@project.tasks.create ???

thanks

Avatar

Ryan this is beautiful! You might remember me from the railsforum, we've talked about this topic.
Looks like you found it out. I'm still amazed with the amount of lines I removed from my code when I implemented these ideas.
Great screencast, thanks.

Avatar

Ryan,

Is there a way to build such complex form when the models have been generated using the scaffold resource generator, so that we could keep the generated REST code (i.e model + controllers, minus the views) ?

I'm working on a project where I have the equivalent of a Project resource with nested Task resources and I'm struggling to find a nice way to build such complex form while keeping the generated routes and controllers.

Avatar

@Joel, yeah, looking at the HTML source it generates you can see it removes the "[]" in the name of the field. Not sure why date_select does this or what the best alternative is.

Avatar

Ryan, have you ever tried this approach with date_select. I have a modified version of your example working (I backed off the fancy date widget) but when I add:
<%= task_form.date_select :duedate, :use_month_numbers => true,:order => [:month,:day,:year], :include_blank => true %>

I get the following cgi error:

Conflicting types for parameter containers. Expected an instance of Array, but found an instance of Hash. This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value.
/Applications/Locomotive2/Bundles/standardRailsSept2006.locobundle/i386/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/cgi_ext/cgi_methods.rb:204:in `type_conflict!'

Avatar

@Joel, you would call collection_select through the form builder like the other fields:

<%= task_form.collection_select :resource_id, entities, :id, :nickname %>

You can do the same for the "check_box" method and other form field helper methods.