This is great. Thanks Ryan for an excellent screencast, and congrats on breaking 100 episodes.
I'm a bit confused with how to get this process to work with a model that relies on virtual attributes.
For example, I have a Person with :first_name and :last_name attributes, and I use a virtual attribute :name as a shortcut to concatenate them and make it easier to search.
Unfortunately, find_by_name and its dynamic cousins don't exist because in this case, :name is a virtual attribute of Person.
The example I provided in my comment above, http://thebalance.metautonomo.us/2008/02/07/simple-model-search-with-rails/ , shows the use of will_paginate. If you use get instead of post (as you should anyway, on an index action) you will end up with those search parameters as part of your URL.
I have a text_field_with_auto_complete functioning normally. But when I try to use as a text_field, I can not use the <% task_form.text_field_with_auto_complete ... In what way can I get the value of text_field_with_auto_complete and send along with other parameters?
Find conditions are so much easier to contstruct in datamapper. Also you really need to just start teaching the viewers a little javascript. The built in helpers are not DRY at all.
While everything works, I am not able to update tasks using Firefox. I can in IE! I can create new records though! Any ideas what I need to check? Thanks.
Just to prove that my contributions here aren't totally frivolous, adding an index is usually only of any value if the tables are large, which seems unlikely in this example. Remember that accessing a table through an index requires at least two buffer gets (and potentially physical I/O's) - one to the index and one to the data, so on very small tables it can actually be worse than just a table scan. Most databases with cost based optimizers will not even consider using an index for tables less than a couple of thousand rows.
It would seem Rails is trying to load from port 80 (default for apache), not port 3000 (default for rails).
Furthermore, I believe categories are stored in {RAILS_ROOT}/public/javascripts/categories.js, not {RAILS_ROOT}/public/category.js where is appears you are trying to find it.
For something like this, would y'all put an index on the announcement date fields?
There's going to be a query against them for every page view and the index won't be updated very often. I always struggle with what to index and what not to index!
i know this is wrong place to write this but i need a solution i want to know how mapping should be done mine case is " A has many B , A has many C, every B has some C which is added to above list which is related to A. so tell me the way to map them . i have mapped B and C to A
@Simon2, I find Ryan's presentation skills to be excellent as well, but there's just that unfortunate resemblance to this character that creates a disturbing mental image for me while I'm listening.
Great work Ryan! I'm a rails beginner and this website has been a huge help.
@FJuan, I'm trying to do a similar thing using the autocomplete in conjunction with Ryan's complex forms. I'm going to look into the source code as Ryan suggested, and hope I dont mess things up. Thanks again Ryan!
Ryan - thanks for the great screencasts - I hope you keep them coming!
WARNING: Solution doesn't work with Edge Rails (2.0.3) when Task has nested fields! (Unless I'm not building the names of the inputs correctly... but I'm fairly certain I am!)
I've found this (Rails2) solution has problems when Task itself has nested fields - at least in Edge Rails (2.0.3). The request parameter parser won't parse the request into the new_task_attributes array correctly.
Example: Let's say Task has_one :person, and Person comprises fields 'first_name' and 'last_name'. You'd probably end up with a form input similar to the following (of course you'd choose what the actual name of the attribute 'person_attributes' would be):
@Matthew - haha... I actually find Ryan's voice okay. He's concise and focused in the way he presents. With all due respect, I actually prefer his voice than to the guy from Peepcode.
Hi Ryan, I think
name += middle_initial + ". " unless middle_initial.nil?
still work, won't cause any error even if middle_initial returns nil.
B/c the priority should be:
(name += middle_initial + ". ") unless middle_initial.nil?
Please let me know if I'm wrong.
The Railscasts site is awesome, and you totally rock, but has anybody ever told you that your voice is a little reminiscent of Kenneth the NBC page from the TV show 30 Rock ? I find it somewhat disturbing when I am trying to absorb the pearls of Rails wisdom that you are trying to impart that I have a mental image of them coming from the mouth of Kenneth. Could you perhaps consider adding a photograph of yourself on the site to fix this problem for me ?
Your sincerely
Matthew
PS. I realise this isn't exactly a flattering comparison. Sorry about that.
I have been watching railscasts for months learning so much about rails. This episode is one of the best yet, chock-full of not only useful techniques but also programming theory and application. I loved it! Thanks for a good show. I am a better programmer because of it.
You twittered wondering if people like the longer screencasts. I say the longer the better. They are more engaging and in the end, more helpful. Thanks so much!
Great screencast. I've had to do this many times and it is a really useful tool. Also, I like the idea of the javascripts controller for these odd little bits that don't quite fit in.
One thing that I might also suggest for those using newer versions of Rails is the is the use of named_scope in the model instead of a function. This would give you all of the benefits of active record collections (like counting when querying for size instead of doing a whole find, etc.). Also, and while admittedly nitpicky, you can split the function a little so that you aren't combining current_announcements with the announcements to display (which would most likely come into play while delving into the management of the announcements).
Refactoring the model to include a since attribute might be overkill (though I find it comes in handy for other reasons more often than not), but this is an excellent place for the use of named_scope.
@FJuan, I haven't tried it, but you should be able to do this. The main thing you have to watch out for is that text_field_with_auto_complete can't be called through the form builder. This means you'll somehow have to get the name of the field to match what a normal text field would be. You may need to dig into the source code of text_field_with_auto_complete to figure out how to do this.
Thanks, Ryan.
I solved this problem (a variation of it) just by creating boolean column in the database, updating it through an Ajax Updater, and then checking whether the column is true or false.
Your solution is much simpler and I appreciate seeing a different solution!
I don't mind the length, as it's really worth my time... Great work!
@Simon, you'r right! I missed that edge case. I'll update the code in the show notes. Thanks.
@QuBiT, to avoid complications I didn't want to go into that for this episode. However you could store the hide time in some place that is more persistant. Whether that be the database or in a cookie with a longer expiration date.
I have, probably, a dumb question:
For forms such as this, where we're adding multiple tasks at once to a new form, I understand (I believe) that they're adding new post variables like project[]task[] etc, and editing existing values like project[1]task[3] etc.
My question: wouldn't it be clearer, and perhaps more semantically correct, to ALWAYS assign a sequential integer as the array index (so that new record combinations might also look like project[4]task[2]) and simply rely on a rested id tag in order to identify which records have previously been saved (existing) and those that lack an id (new)?
I'm sure I'm missing something, but it just feels like that postulated format ties in better with the object model by utilizing the correct location for the id data, and relieves the problem of the post parser having to make educated guesses as to which data pairs should be grouped into an individual object.
@QuBiT, Zach Inglis
Or simply stores that value into the database along side with any other user preferences. For performace reason though, these user preference are usually stored, or at least cached in a client side cookie.
I've got a little question: is there an easy way to combine "comlex forms" and "autocomplete with association"?
What I'm trying to do is a "purchase order" form with differents "purchases" (like a porject with different tasks) and I'd like to autocomplete the name of the product of each purchase
nice episode again, but i think there is one litte problem unsolved.
Just think about a user which signs in to your webside (if there is authentication) and hides the message after reading.
After he closes his browser (and cleans his session variable) he will have to hide the same message again for every time he views your side until the event has ended for every message in your database.
So maybe you or someone else has a really efficient solution for this ;) and shares it with us.
For completeness, you may want to change find conditions to take into account announcements that start after the user chose to hide a previous announcement:
Otherwise if you had, for example an announcement set for today and a different one set for tomorrow, a user hiding today's wouldn't see the later announcement unless it had been edited in the mean time.
I made some progress and discovered that my problem is trying to use this technique with a DIV that gets updated with RJS. I can't seem to get the javascript to be interpreted unless I put it on the main page, but then the observed fields are not available.
Any ideas on how to use this technique for partials that come and go with RJS/Ajax?
I love these tips! It would be very nice if you could package the files for examination. I'm having trouble understanding were your view code includes the state field.
I'm using rails 2.0.2. The params hash *does* have the value that I'm passing through the form, however the attr_accessor that I created is *not* getting populated. I am having to explicitly set the value of attr_accessor from the params hash.
This is great. Thanks Ryan for an excellent screencast, and congrats on breaking 100 episodes.
I'm a bit confused with how to get this process to work with a model that relies on virtual attributes.
For example, I have a Person with :first_name and :last_name attributes, and I use a virtual attribute :name as a shortcut to concatenate them and make it easier to search.
Unfortunately, find_by_name and its dynamic cousins don't exist because in this case, :name is a virtual attribute of Person.
Shikha,
The example I provided in my comment above, http://thebalance.metautonomo.us/2008/02/07/simple-model-search-with-rails/ , shows the use of will_paginate. If you use get instead of post (as you should anyway, on an index action) you will end up with those search parameters as part of your URL.
I have a text_field_with_auto_complete functioning normally. But when I try to use as a text_field, I can not use the <% task_form.text_field_with_auto_complete ... In what way can I get the value of text_field_with_auto_complete and send along with other parameters?
Thanks.
Find conditions are so much easier to contstruct in datamapper. Also you really need to just start teaching the viewers a little javascript. The built in helpers are not DRY at all.
While everything works, I am not able to update tasks using Firefox. I can in IE! I can create new records though! Any ideas what I need to check? Thanks.
How can we pass get parameters when user navigates to enother pages using will_paginate?
How can we pass get parameters when user navigates to another pages using will_paginate?
@Wes
Just to prove that my contributions here aren't totally frivolous, adding an index is usually only of any value if the tables are large, which seems unlikely in this example. Remember that accessing a table through an index requires at least two buffer gets (and potentially physical I/O's) - one to the index and one to the data, so on very small tables it can actually be worse than just a table scan. Most databases with cost based optimizers will not even consider using an index for tables less than a couple of thousand rows.
From your log: "[http://localhost/categories.js]"
It would seem Rails is trying to load from port 80 (default for apache), not port 3000 (default for rails).
Furthermore, I believe categories are stored in {RAILS_ROOT}/public/javascripts/categories.js, not {RAILS_ROOT}/public/category.js where is appears you are trying to find it.
Can you post some of your code?
For something like this, would y'all put an index on the announcement date fields?
There's going to be a query against them for every page view and the index won't be updated very often. I always struggle with what to index and what not to index!
i know this is wrong place to write this but i need a solution i want to know how mapping should be done mine case is " A has many B , A has many C, every B has some C which is added to above list which is related to A. so tell me the way to map them . i have mapped B and C to A
Thanks for this screencast - a really nice solution to drop down lists!
I'm having some troubles though... when I goto the url http://localhost:3000/categories.js nothing shows up... This is my dev.log
<pre>
Processing CategoriesController#index (for 127.0.0.1 at 2008-05-01 20:25:12) [GET]
Session ID: BAh7BzoMY3NyZl9pZCIlZGU1MWNkNzQ2MTYzYTQ0MmY3ZDZmNDhhMjljYjU2%0ANjYiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh%0Ac2h7AAY6CkB1c2VkewA%3D--9533e6fe70bd20e0155ec58df1e28d241ac42676
Parameters: {"format"=>"js", "action"=>"index", "controller"=>"categories"}
Category Load (0.000386) SELECT * FROM `categories` WHERE (name LIKE '%%')
Completed in 0.00667 (149 reqs/sec) | Rendering: 0.00005 (0%) | DB: 0.00039 (5%) | 406 Not Acceptable [http://localhost/categories.js]
</pre>
Can anyone see something going on?
@Kieran, thanks, that's much better than <a href="http://amysrobot.com/files/30rock_kenneth.JPG">this</a>
@Matthew, http://workingwithrails.com/person/6491-ryan-bates
That what you looking for?
@Simon2, I find Ryan's presentation skills to be excellent as well, but there's just that unfortunate resemblance to this character that creates a disturbing mental image for me while I'm listening.
Is there any way to set this up so that each task is an array of data?
Great work Ryan! I'm a rails beginner and this website has been a huge help.
@FJuan, I'm trying to do a similar thing using the autocomplete in conjunction with Ryan's complex forms. I'm going to look into the source code as Ryan suggested, and hope I dont mess things up. Thanks again Ryan!
Ryan - thanks for the great screencasts - I hope you keep them coming!
WARNING: Solution doesn't work with Edge Rails (2.0.3) when Task has nested fields! (Unless I'm not building the names of the inputs correctly... but I'm fairly certain I am!)
I've found this (Rails2) solution has problems when Task itself has nested fields - at least in Edge Rails (2.0.3). The request parameter parser won't parse the request into the new_task_attributes array correctly.
Example: Let's say Task has_one :person, and Person comprises fields 'first_name' and 'last_name'. You'd probably end up with a form input similar to the following (of course you'd choose what the actual name of the attribute 'person_attributes' would be):
project[new_task_attributes][][name]=taskname1
project[new_task_attributes][][person_attributes][first_name]=person1-first
project[new_task_attributes][][person_attributes][last_name]=person1-last
project[new_task_attributes][][name]=taskname2
project[new_task_attributes][][person_attributes][first_name]=person2-first
project[new_task_attributes][][person_attributes][last_name]=person2-last
Edge Rails (2.0.3) creates an very wrong parameters hash for this:
"project"=>
{ ...project-data...,
"new_task_attributes"=> [
{"person_attributes"=>{"first_name"=>"person1-first"}},
{"person_attributes"=>{"last_name"=>"person1-last"}, "name" => "taskname1"},
{"person_attributes"=>{"first_name"=>"person2-first"}},
{"person_attributes"=>{"last_name"=>"person2-last"}, "name" => "taskname2"}
],
}
Just a warning to folks out there who are trying this solution, but have nested fields as well.
@Matthew - haha... I actually find Ryan's voice okay. He's concise and focused in the way he presents. With all due respect, I actually prefer his voice than to the guy from Peepcode.
Is it a ok to access directly model methods in views? I mean mixing presentation and persistence level together.
Hi Ryan, I think
name += middle_initial + ". " unless middle_initial.nil?
still work, won't cause any error even if middle_initial returns nil.
B/c the priority should be:
(name += middle_initial + ". ") unless middle_initial.nil?
Please let me know if I'm wrong.
@just_curious
Maybe the below could work? May require further refactoring...
def first_name
full_name.split(' ', 2)[0]
end
def first_name=(name)
first_last_name(name + ' ')
end
def last_name
full_name.split(' ', 2)[1]
end
def last_name=(name)
first_last_name(' ' + name)
end
def first_last_name(name)
if self.full_name.empty?
self.full_name = name
else
buffer = []
self.full_name.split(' ').zip(name.split(' '), 2) { |old_name, new_name|
buffer << (new_name.nil? ? old_name : new_name)
}
self.full_name = buffer.join(' ')
end
end
Is there anyway for fields_for to look at the scope of the enclosing form_for and change it's scope accordingly?
Depending on the form, sometimes I need the fields_for to be scoped as
"parent[object_attributes]"
other times I would like it to be
"parent's parent[parent][object_attributes]"
I have been passing it around to the partial using :locals, but it seems like there must be a better way.
Too bad i didn't get here to be a part of the contest until now :p
@Matt: I think you're referring to "tailing the development log". It's a technique for watching what's getting written to the log in real time.
Railscasts are terrific. Thanks so much for creating them.
How about using a partial and then doing: render :partial => "announcements" unless announcements.empty?
This would significantly clean up the view and eliminate the the if / end clause.
Dear Ryan
The Railscasts site is awesome, and you totally rock, but has anybody ever told you that your voice is a little reminiscent of Kenneth the NBC page from the TV show 30 Rock ? I find it somewhat disturbing when I am trying to absorb the pearls of Rails wisdom that you are trying to impart that I have a mental image of them coming from the mouth of Kenneth. Could you perhaps consider adding a photograph of yourself on the site to fix this problem for me ?
Your sincerely
Matthew
PS. I realise this isn't exactly a flattering comparison. Sorry about that.
I have been watching railscasts for months learning so much about rails. This episode is one of the best yet, chock-full of not only useful techniques but also programming theory and application. I loved it! Thanks for a good show. I am a better programmer because of it.
I personally like the "show_announcement" boolean column better.
You twittered wondering if people like the longer screencasts. I say the longer the better. They are more engaging and in the end, more helpful. Thanks so much!
Great screencast. I've had to do this many times and it is a really useful tool. Also, I like the idea of the javascripts controller for these odd little bits that don't quite fit in.
One thing that I might also suggest for those using newer versions of Rails is the is the use of named_scope in the model instead of a function. This would give you all of the benefits of active record collections (like counting when querying for size instead of doing a whole find, etc.). Also, and while admittedly nitpicky, you can split the function a little so that you aren't combining current_announcements with the announcements to display (which would most likely come into play while delving into the management of the announcements).
So, in the model I'd have:
named_scope :current, :conditions => {'starts_at <= current_timestamp() AND ends_at >= current_timestamp()'}
named_scope :since, lambda {|hide_time|
{ :conditions => (hide_time ? ['updated_at > ? or starts_at > ? )', hide_time, hide_time] : nil) }
}
def self.to_display(hide_time)
current.since(hide_time)
end
And since I changed the function name, I'd have to change the helper to:
@current_announcements ||= Announcement.to_display(session[:announcement_hide_time])
Refactoring the model to include a since attribute might be overkill (though I find it comes in handy for other reasons more often than not), but this is an excellent place for the use of named_scope.
Ryan, now() is not a SQL ANSII standard, but you can replace now() by current_timestamp, which is a standard :D
@FJuan, I haven't tried it, but you should be able to do this. The main thing you have to watch out for is that text_field_with_auto_complete can't be called through the form builder. This means you'll somehow have to get the name of the field to match what a normal text field would be. You may need to dig into the source code of text_field_with_auto_complete to figure out how to do this.
Thanks, Ryan.
I solved this problem (a variation of it) just by creating boolean column in the database, updating it through an Ajax Updater, and then checking whether the column is true or false.
Your solution is much simpler and I appreciate seeing a different solution!
I don't mind the length, as it's really worth my time... Great work!
@Simon, you'r right! I missed that edge case. I'll update the code in the show notes. Thanks.
@QuBiT, to avoid complications I didn't want to go into that for this episode. However you could store the hide time in some place that is more persistant. Whether that be the database or in a cookie with a longer expiration date.
I have, probably, a dumb question:
For forms such as this, where we're adding multiple tasks at once to a new form, I understand (I believe) that they're adding new post variables like project[]task[] etc, and editing existing values like project[1]task[3] etc.
My question: wouldn't it be clearer, and perhaps more semantically correct, to ALWAYS assign a sequential integer as the array index (so that new record combinations might also look like project[4]task[2]) and simply rely on a rested id tag in order to identify which records have previously been saved (existing) and those that lack an id (new)?
I'm sure I'm missing something, but it just feels like that postulated format ties in better with the object model by utilizing the correct location for the id data, and relieves the problem of the post parser having to make educated guesses as to which data pairs should be grouped into an individual object.
@QuBiT, Zach Inglis
Or simply stores that value into the database along side with any other user preferences. For performace reason though, these user preference are usually stored, or at least cached in a client side cookie.
If you are really worried about that, create a join table.
Thanks Ryan, you're doing a great job!
I've got a little question: is there an easy way to combine "comlex forms" and "autocomplete with association"?
What I'm trying to do is a "purchase order" form with differents "purchases" (like a porject with different tasks) and I'd like to autocomplete the name of the product of each purchase
Thanks again
Hi Ryan,
nice episode again, but i think there is one litte problem unsolved.
Just think about a user which signs in to your webside (if there is authentication) and hides the message after reading.
After he closes his browser (and cleans his session variable) he will have to hide the same message again for every time he views your side until the event has ended for every message in your database.
So maybe you or someone else has a really efficient solution for this ;) and shares it with us.
Then this would be a really helpful feature.
lg
Nice cast as always, thanks.
For completeness, you may want to change find conditions to take into account announcements that start after the user chose to hide a previous announcement:
<pre>
["updated_at > ? OR starts_at > ?", hide_time, hide_time]
</pre>
Otherwise if you had, for example an announcement set for today and a different one set for tomorrow, a user hiding today's wouldn't see the later announcement unless it had been edited in the mean time.
Nice tip with using a controller by the name 'javascripts'.
Thanks for another good episode! You've helped me become a better rails dev. Cheers!
GregH, akatako,
Try adding those after the
Rails::Initializer.run do |config|
..
end
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
...
)
Hope that helps.
I had a similar issue with ActionMailer as well.
I made some progress and discovered that my problem is trying to use this technique with a DIV that gets updated with RJS. I can't seem to get the javascript to be interpreted unless I put it on the main page, but then the observed fields are not available.
Any ideas on how to use this technique for partials that come and go with RJS/Ajax?
I love these tips! It would be very nice if you could package the files for examination. I'm having trouble understanding were your view code includes the state field.
I am writing a plugin and would. My friends also would like to use it, however I haven't found any tutorials on how to "correctly" deploy a plugin.
Any pointers?
I realized I am using attr_accessible - got to remember to add the virtual attribute to the list.
I'm using rails 2.0.2. The params hash *does* have the value that I'm passing through the form, however the attr_accessor that I created is *not* getting populated. I am having to explicitly set the value of attr_accessor from the params hash.
Any thoughts?
For some reason as soon as I followed this screencast I get this error. Anyone have any ideas?
I go to /login use the openid then I get this.
uninitialized constant SessionsController::User