I dont know how to do that in SQLite. My best advice is to use google and see if that helps. I should have been more clear in my post. My change is MySQL specific.
I ended up changing my 'hide message' store to cookies as was suggested above. I think cookies is the best solution because it doesn't require registered users, and it's more scalable. Here's a link with some code:
For those who may not know, if you don't want to save your searches in the database, but would like all benefits of using an AR model (like validations), you can use virtual attributes:
class Search < ActiveRecord::Base
attr_accessor :keywords, :category_id, : minimum_price, :maximum_price
validates_presence_of :keywords
# blah...
end
Everything works the same, just doesn't save anything. And no need for a migration or 'searches' table in db.
@Marcos, I'm not sure how a polymorphic association would help in that case. I'd probably just make a separate search model for each one (ProductSearch, CommentSearch, etc.). Unless you need the same attributes for each one but they will likely need to be different. Then if you have some duplication you can move that out into a generic Search module.
@Jose, yep, this will look for a partial under "restaurants/_restaurants.html.erb", so you should make one there which has your restaurant display. Alternatively you can use a "for" loop to loop around the restaurants array instead of using a partial.
Secondly, I'm having some issues getting this sort of thing implemented in my Rails 2.0 project. I'm working with layers in films so that a film has many layers (instead of project has many tasks). I went ahead and tossed in a couple of extra fields for my layer parameters, including a datetime select, a system select and a material select so that my new.html.erb file has something like this in it
<% for layer in @film.layers %>
<% fields_for "...", layer do |layer_form| %>
<p>
Time: <%= layer_form.datetime_select :time%>
Material: <%= collection_select(:layer, :material_id, Material.find(:all, :order=>"name"), :id, :name)%>
System: <%= collection_select(:layer, :system_id, System.find(:all, :order=>"name"), :id, :name)%>
</p>
<% end %>
<% end %>
This was working fine until I did the bit about changing the "..." to (in my case) "film[layer_attributes][]". I also put the accompanying code in my model:
def layer_attributes=(layer_attributes)
layer_attributes.each do |attributes|
layers.build(attributes)
end
end
When I save those and try to submit a new film request I get
ndefined method `stringify_keys!' for ["time(1i)", "2008"]:Array
from the browser. If I take out the bit of the new.html.erb file that says
Time: <%= layer_form.datetime_select :time%>
everything works fine with the layer_attributes in there. Any idea why I'm getting trouble here? Does this have anything to do with the fact that I'm running Rails 2.0?
Hello Ryan (or anyone else who knows),
Great screencast. In your demo, you are typing the commands in the console and showing the SQL queries in the console below. How do you isolate the SQL Queries display in the window below? It is quite useful. A screencast summarizing the tips on using Rails Console for these kinds of tricks would be very useful.
Keep up the good work.
Regards,
Bharat
@Dandre, good question. What I would probably do is pass the page number into the Search model and call paginate inside the search model. This opens up the ability of storing pagination parameters in the searches table (such as how many results are displayed on a page).
Since we have a model dedicated to searching, to me it seems best for it to handle as much of the searching as it can.
@Avalon, that's a possibility, but the problem is then we have no way to fetch the results again. There's no way to bookmark the URL, or add pagination if you need to. Storing the search in the database allows you to do this. However it really depends on the requirements for your app.
How would you implement pagination?
I've done something similar, the only difference is the search class generates a conditions block passed to another model. This makes it easy for me to tack on pagination.
@taelor, it's not an original idea. I heard about it elsewhere but don't remember where.
@Aditya, the nifty_scaffold generator will generate the create action automatically if there's a new action.
@Maciej, good point! I normally use the decimal type for prices, but thought a float would be okay in this case since this isn't really a price. It's just a number that's used to search prices. I wouldn't be performing calculations on this value.
But please, please never ever use floats in real applications when it comes to money. I don't want to have rounding errors on my bank account in the future.
The scaffolded controller only had new and show, right? Wouldnt the search form invoke the non-existent create action on the server? Have you modified the controller code for the above to make it work?
Dude, I would never thought, nor prolly would have ever thought about making a search a resource. Was this an original idea or something that someone else hinted at for you?
I tried the autocompletion exactly like the screencast and it works perfectly ....except for safari ( 3.1.1) in production mode. I get no requests at all when typing in the autocomplete field.
It does work in development mode and it works in production mode for firefox, IE6 en IE7.
Great screencast, and a wonderful concept. Though, I wonder which is better to do (or more likely safer from an sql injection point of view); using the "updated_at > ?" hide_time or "updated_at #{hide_time}"
all viewpoints would be great to hear from on the different methods of queries.
Thanks,
Master Denzuko
Feed your head free you mind
I have moved my audit_mass_assignment plugin to GitHub:
http://github.com/ryanlowe/audit_mass_assignment
The plugin scans the models in your project and lets you know which ones do not use the attr_accessible whitelist approach to protect attributes.
It's very handy for projects that have a lot of models, get updated sporadically or are updated by many people. An audit like this could be run as part of a continuous integration test suite.
Nevermind. Seconds after posting, I realized I could actually use Ruby, what a concept, in my layout! DOH!.
I wrapped the divs in a <% unless flash[:notice].blank? %>
Could you post your application layout somewhere? It plus the CSS (which I grabbed from cast #58) make for a clean demo. But, I can't get the flash working right -- I always get a green and pink narrow band when the flash is empty. How do I make the flash_notice div go away when the flash is empty?
I have used this sublime recipe as the basis for a kicked-up-a-notch version, with a more complex database schema and it's working perfectly so far.
The point where I'm having problems is if, for instance you also have a Category model that has_many :tasks and you have to select which category the task :belongs_to before clicking the add_task_link.
What I have tried -unsuccessfully- to do is when you call page.inset_html, to pass in something like :object => Task.new(:category_id => what_should_i_put_here?), being (as you might have figured out) "what_should_i_put_here?" the main problem. I can get the category_id value from the form field, but I can not insert it within the page.insert_html method.
Thank you.
Great screencast... I was trying to get this to work for days and this made it really easy.
I want to take it a step further though, and allow people to enter the name, but actually save the ID of the found object back into the database, which would be useful when adding existing items to a list. I've gotten the results back as JSON, but now I'm wondering how to actually take them and create the list from the JSON rather than an HTML ul.
@Aditya, anything that uses "config" I like to keep in the environment.rb. Anything that doesn't (where you can specify it after the config) then move that into an initializer.
@Henrik, yeah, my quote finger was a little twitchy last night it seems. ;)
@HappyCoder, I haven't done any benchmarking so I can't say for sure if that would degrade performance. My guess is that it would be minimal.
As for your original question. The reason you may want to do this is to ensure the record hasn't been changed since you last fetched it. This way you don't mistakenly overwrite another change on the save.
I was probably wrong about "performance issues" :-) I don't know how databases work in this situation. Does it search all records with such id (which is 1) and then finds from them just one with age=29?
@Virginian, not that I know of. Validations may not necessarily be tied to one attribute. It's possible a validation may rely on multiple attributes.
However, you can add this behavior manually when you specify the validations. To do this, add a condition to see if the attribute has changed before running the validation.
@Peter Theill
I don't understand. Why do you want that feature? The PK provides record's uniqueness. Isn't it enough? Besides, it has a performance issue. I mean, setting an index for every column in a table will not do any good.
[quote]
it should generate
UPDATE users SET age = 30 WHERE id = ? AND age = 29
if "age" attribute changed from 29 to being 30."
[/quote]
One question, when using partial updates rails is still validating all the attributes not just the ones changed. Is there a way to restrict it to only run validations on the changed attributes?
It would be helpful to see the full command that you are issuing that is giving you errors, but I'm guessing your not providing the generator script the initial argument.
@Peter, that's an interesting idea to only update if the existing record hasn't changed. It has some differences from optimistic locking in the fact that it only checks the columns it's updating. It doesn't consider changes to other columns a conflict.
This still suffers from the invalid record possibility problem. I would also hesitate adding it before fully exploring other potential problems. But it's worth investigating.
Since tracking of attribute changes have been introduced, I'm wondering if it's possible (through a configuration setting or similar) to implement a lost update check by extending the where clause, i.e. instead of Rails generating:
UPDATE users SET age = 30 WHERE id = ?
it should generate
UPDATE users SET age = 30 WHERE id = ? AND age = 29
Ryan, this screencast looks like it's going to be a HUGE help in making some of my resources more restful.
Thanks for the great work
Instead of the complicated conditions why not use criteria_query http://agilewebdevelopment.com/plugins/criteria_query
@Jeremy,
I dont know how to do that in SQLite. My best advice is to use google and see if that helps. I should have been more clear in my post. My change is MySQL specific.
Good Luck!
@Ryan thanks for the screencast!
@Trevor thanks for point out --depth 1
Thanks Steve Purcell for the new script/dbconsole!!
Ryan, great screencast! You Rock!
I ended up changing my 'hide message' store to cookies as was suggested above. I think cookies is the best solution because it doesn't require registered users, and it's more scalable. Here's a link with some code:
<a href="http://geoff.evason.name/2008/05/27/railscasts-does-it-again-site-wide-announcements/">http://geoff.evason.name/2008/05/27/railscasts-does-it-again-site-wide-announcements/</a>
For those who may not know, if you don't want to save your searches in the database, but would like all benefits of using an AR model (like validations), you can use virtual attributes:
class Search < ActiveRecord::Base
attr_accessor :keywords, :category_id, : minimum_price, :maximum_price
validates_presence_of :keywords
# blah...
end
Everything works the same, just doesn't save anything. And no need for a migration or 'searches' table in db.
@Marcos, I'm not sure how a polymorphic association would help in that case. I'd probably just make a separate search model for each one (ProductSearch, CommentSearch, etc.). Unless you need the same attributes for each one but they will likely need to be different. Then if you have some duplication you can move that out into a generic Search module.
@Jose, yep, this will look for a partial under "restaurants/_restaurants.html.erb", so you should make one there which has your restaurant display. Alternatively you can use a "for" loop to loop around the restaurants array instead of using a partial.
In the show page I'm getting:
Couldn't find template file for restaurants/_restaurant
Where line of error is:
<%= render :partial => @search.restaurants %>
What could be wrong, anything else needs to be added?
Episode 112 could show us how to make this search polymorphic, cause I would like a search for products, one for comments, other for posts...
Amazing!, Ryan but I emailed you yesterday! lol
Thanks a lot, you don't know how much for this one!!
Hi - firstly, great railscast - very groovey!
Secondly, I'm having some issues getting this sort of thing implemented in my Rails 2.0 project. I'm working with layers in films so that a film has many layers (instead of project has many tasks). I went ahead and tossed in a couple of extra fields for my layer parameters, including a datetime select, a system select and a material select so that my new.html.erb file has something like this in it
<% for layer in @film.layers %>
<% fields_for "...", layer do |layer_form| %>
<p>
Time: <%= layer_form.datetime_select :time%>
Material: <%= collection_select(:layer, :material_id, Material.find(:all, :order=>"name"), :id, :name)%>
System: <%= collection_select(:layer, :system_id, System.find(:all, :order=>"name"), :id, :name)%>
</p>
<% end %>
<% end %>
This was working fine until I did the bit about changing the "..." to (in my case) "film[layer_attributes][]". I also put the accompanying code in my model:
def layer_attributes=(layer_attributes)
layer_attributes.each do |attributes|
layers.build(attributes)
end
end
When I save those and try to submit a new film request I get
ndefined method `stringify_keys!' for ["time(1i)", "2008"]:Array
from the browser. If I take out the bit of the new.html.erb file that says
Time: <%= layer_form.datetime_select :time%>
everything works fine with the layer_attributes in there. Any idea why I'm getting trouble here? Does this have anything to do with the fact that I'm running Rails 2.0?
Any help is greatly appreciated.
Hello Ryan (or anyone else who knows),
Great screencast. In your demo, you are typing the commands in the console and showing the SQL queries in the console below. How do you isolate the SQL Queries display in the window below? It is quite useful. A screencast summarizing the tips on using Rails Console for these kinds of tricks would be very useful.
Keep up the good work.
Regards,
Bharat
@Dandre, good question. What I would probably do is pass the page number into the Search model and call paginate inside the search model. This opens up the ability of storing pagination parameters in the searches table (such as how many results are displayed on a page).
Since we have a model dedicated to searching, to me it seems best for it to handle as much of the searching as it can.
@Avalon, that's a possibility, but the problem is then we have no way to fetch the results again. There's no way to bookmark the URL, or add pagination if you need to. Storing the search in the database allows you to do this. However it really depends on the requirements for your app.
Hello!
Why don't you create a simple virtual model (non-activerecord class) with instance attribute parameters?
How would you implement pagination?
I've done something similar, the only difference is the search class generates a conditions block passed to another model. This makes it easy for me to tack on pagination.
@Ryan, well that's nifty! Yes it absolutely is.
@taelor, it's not an original idea. I heard about it elsewhere but don't remember where.
@Aditya, the nifty_scaffold generator will generate the create action automatically if there's a new action.
@Maciej, good point! I normally use the decimal type for prices, but thought a float would be okay in this case since this isn't really a price. It's just a number that's used to search prices. I wouldn't be performing calculations on this value.
Great screencast.
But please, please never ever use floats in real applications when it comes to money. I don't want to have rounding errors on my bank account in the future.
Cheers,
Maciej
Uhh... like it... ...that simple.
I've done something similar to store it for customized RSS Feeds.
But I have it to simplify with a search model.
Thank you Ryan
Rafael
Hi Ryan,
The scaffolded controller only had new and show, right? Wouldnt the search form invoke the non-existent create action on the server? Have you modified the controller code for the above to make it work?
Cheers,
Aditya
Dude, I would never thought, nor prolly would have ever thought about making a search a resource. Was this an original idea or something that someone else hinted at for you?
Once again Ryan, Bravo.
I tried the autocompletion exactly like the screencast and it works perfectly ....except for safari ( 3.1.1) in production mode. I get no requests at all when typing in the autocomplete field.
It does work in development mode and it works in production mode for firefox, IE6 en IE7.
Any ideas ...?
auto-complete. oh yes, this good!
but if i have 100 users on-line, this very-very slowly
Great screencast, and a wonderful concept. Though, I wonder which is better to do (or more likely safer from an sql injection point of view); using the "updated_at > ?" hide_time or "updated_at #{hide_time}"
all viewpoints would be great to hear from on the different methods of queries.
Thanks,
Master Denzuko
Feed your head free you mind
I have moved my audit_mass_assignment plugin to GitHub:
http://github.com/ryanlowe/audit_mass_assignment
The plugin scans the models in your project and lets you know which ones do not use the attr_accessible whitelist approach to protect attributes.
It's very handy for projects that have a lot of models, get updated sporadically or are updated by many people. An audit like this could be run as part of a continuous integration test suite.
But, is there a new way for testing it?
Extremely useful, thanks! This just saved me a couple of hours of searching the net. Thanks, Ryan!
@sam:
utc_timestamp does not work in SQLlite. Any alternatives, or must we input that directly into the code?
I can't get this code to work on my Rails 2.1 app.
Ryan,
Nevermind. Seconds after posting, I realized I could actually use Ruby, what a concept, in my layout! DOH!.
I wrapped the divs in a <% unless flash[:notice].blank? %>
Thanks anyway,
Bill
Ryan,
Thanks for the railscasts! Awesome!
Could you post your application layout somewhere? It plus the CSS (which I grabbed from cast #58) make for a clean demo. But, I can't get the flash working right -- I always get a green and pink narrow band when the flash is empty. How do I make the flash_notice div go away when the flash is empty?
Thanks,
Bill
Another great rails cast!
Could you do some on ActionMailer?
Hello Ryan,
I have used this sublime recipe as the basis for a kicked-up-a-notch version, with a more complex database schema and it's working perfectly so far.
The point where I'm having problems is if, for instance you also have a Category model that has_many :tasks and you have to select which category the task :belongs_to before clicking the add_task_link.
What I have tried -unsuccessfully- to do is when you call page.inset_html, to pass in something like :object => Task.new(:category_id => what_should_i_put_here?), being (as you might have figured out) "what_should_i_put_here?" the main problem. I can get the category_id value from the form field, but I can not insert it within the page.insert_html method.
Thank you.
Ryan, great screencast, but with the new timezone stuff in rails 2.1, you gotta be careful with UTC.
Instead of using now(), i'm using utc_timestamp on mysql.
with_scope :find => { :conditions => "starts_at <= utc_timestamp AND ends_at >= utc_timestamp" } do
I have to convert central time to UTC. Just a helpful hint, and keep up the great work!
for the record, I got my little problem cleared up a couple of days after-the-fact
http://rails.lighthouseapp.com/projects/8994/tickets/176-2-1_rc1-won-t-load-on-stock-leopard-setup#ticket-176-3
Great screencast... I was trying to get this to work for days and this made it really easy.
I want to take it a step further though, and allow people to enter the name, but actually save the ID of the found object back into the database, which would be useful when adding existing items to a list. I've gotten the results back as JSON, but now I'm wondering how to actually take them and create the list from the JSON rather than an HTML ul.
Does anybody know?
@Aditya, anything that uses "config" I like to keep in the environment.rb. Anything that doesn't (where you can specify it after the config) then move that into an initializer.
@Henrik, yeah, my quote finger was a little twitchy last night it seems. ;)
Minor thing: you can do RAILS_ENV=test instead of RAILS_ENV='test'.
Hi,
I wonder how i get an error " undefined method `pdf'". I think I follow all you stpes.
thanks
wing
Git looks great, but can it work with deprec/capistrano/mongrel as well as subversion does?
Hi Ryan,
Is it still the norm to change environment.rb or should we be doing this in the initializers?
Cheers,
Aditya
@HappyCoder, I haven't done any benchmarking so I can't say for sure if that would degrade performance. My guess is that it would be minimal.
As for your original question. The reason you may want to do this is to ensure the record hasn't been changed since you last fetched it. This way you don't mistakenly overwrite another change on the save.
I was probably wrong about "performance issues" :-) I don't know how databases work in this situation. Does it search all records with such id (which is 1) and then finds from them just one with age=29?
@Virginian, not that I know of. Validations may not necessarily be tied to one attribute. It's possible a validation may rely on multiple attributes.
However, you can add this behavior manually when you specify the validations. To do this, add a condition to see if the attribute has changed before running the validation.
@Peter Theill
I don't understand. Why do you want that feature? The PK provides record's uniqueness. Isn't it enough? Besides, it has a performance issue. I mean, setting an index for every column in a table will not do any good.
[quote]
it should generate
UPDATE users SET age = 30 WHERE id = ? AND age = 29
if "age" attribute changed from 29 to being 30."
[/quote]
Another great Railscasts, Thanks Ryan.
One question, when using partial updates rails is still validating all the attributes not just the ones changed. Is there a way to restrict it to only run validations on the changed attributes?
jg,
It would be helpful to see the full command that you are issuing that is giving you errors, but I'm guessing your not providing the generator script the initial argument.
Correct:
script/generate migration add_stuff_to_table
Incorrect:
script/generate add_stuff_to_table
Make sense?
@Peter, that's an interesting idea to only update if the existing record hasn't changed. It has some differences from optimistic locking in the fact that it only checks the columns it's updating. It doesn't consider changes to other columns a conflict.
This still suffers from the invalid record possibility problem. I would also hesitate adding it before fully exploring other potential problems. But it's worth investigating.
@Peter Theill
go back and watch episode 59,
that's what you are looking for :)
http://railscasts.com/episodes/59
FYI: I had problems in Rails 2.0.2 until I added this line to that dynamic states action
respond_to do |format|
format.js
end
I also had to add this to routes.rb:
map.js ':controller/:action.:format'
And now everything works
Hope it helps someone :)
Since tracking of attribute changes have been introduced, I'm wondering if it's possible (through a configuration setting or similar) to implement a lost update check by extending the where clause, i.e. instead of Rails generating:
UPDATE users SET age = 30 WHERE id = ?
it should generate
UPDATE users SET age = 30 WHERE id = ? AND age = 29
if "age" attribute changed from 29 to being 30.