Hopefully you're still reading comments for this Railscast. This series has helped me out with my current application, thanks!
One quick question: if I use a check_box in my partial when using fields_for, it always tries to submit two entries for every one displayed on the page. I'm assuming this is because the check_box helper adds a hidden field for when the box is not checked. Is there any way around this?
`@project[service_attributes]' is not allowed as an instance variable name
I would simply pass locals as in the first example which works fine except that when I add link_to_function and pass in a new object I get the same error.
I've double checked everything and can't work out what the problem is. On Rails 2.02
With rails 2.0.2 and after adding ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS[:rfc3339] = '%Y-%m-%dT%H:%M:00.000Z' to environment.rb I get a "rake aborted! uninitialized constant ActiveSupport" each time I run rake db:migrate. I am unable to find why... someone can help?
@Artūras, Ted, and Aaron. Very good points. I agree your solution is better. Thanks for pointing this out.
@Brian, I thought of using a YAML file too, and I agree it is better than placing it in the code. But I still prefer having a full model. This way it will have a ruby class in front and allow us to refactor more code into there if need be. We can also move calculations into the database to improve performance if we need to as Aaron mentioned.
Hi Ryan (and all the rails guys out there)
after some trouble I have finally gotten it and now I can add and remove articles. What I would like to know now is showing the remove link only if the article is not the first so I surrounded the link_to_function helper with a if but basically I don't know how I can check out if it is the first element of the collection.
partial: http://pastie.caboo.se/149027
In the log the array is messed.
"set_mappings"=>[{"file_name_pattern"=>"*pricelistsample*"}, {"format"=>"1", "file_name_pattern"=>"*availability*"}, {"format"=>"1", "id"=>"953125641"}, {"id"=>"996332877"}]
It looks like rails collect the array in a wrong way. What could it be?
@levi I too am seeing the parameters get wonky when using multiple attributes in the child object. Let me know if you discover a solution, I will do the same.
I think having a 4 row db for shipping prices is a bit much. Using a case statement could reduce that down. If you didn't want the numbers in the file I'd store them in a yaml file or plain text before a database.
So i realize this is late, but no one on railsforum seems to know how to help, or just dont care to...
Will this not work for creating a model with multiple attributes? My parameters are keep getting screwed up. Lets say we are creating or updating a task that also has a completed_on attribute, well instead of getting a task model for the ones im editing/creating, it somehow sends a seperate set of paramaters for each attribute. So say I have 2 tasks, I will have params like so...
"task_attributes => [{"task" => "foo"}, {{"task" => "bar"}, {"completed_on" => "1/2/3"}, {"completed_on" => ""}, {"should_destroy" => ""},{"should_destroy" => ""}]"
Hi Ryan,
thanks for your wonderful series. We had some trouble with your javascript. If there is only one state in a selected country, your script displayed that state twice. To fix that we changed "options.length = 1;" to "options.length = 0;" and also the if clause to "if(options.length == 0)"
@Ted - the instance variables will only be cached for the lifecycle of the object, which unless you're doing something really bizarre will be contained in the lifecycle of the request
i was trying to download the file and store this video in another PC,
but i was unable to download it,is it not possible with out quicktime player installed in my PC ?.
aah,but later i installed player but this time the movie itself is playing in the browser..
plz help me how to download the file seperately
thanks,,
First off, great screencasts. Love them all and look forward to new ones every week.
I want to jump in and agree with Artūras and Ted. In this situation, using the instance variable could introduce unusual behavior down the line that is difficult to track down. In fact, I was surprised when the screencast ended that you didn't show that.
(In an effort to help any rails noobs out there with specific code for this, I thought I'd include the actual code I'd use).
def shipping_price
case total_weight
when 0
0.00
when 0..3
8.00
when 3..5
10.00
else
12.00
end
end
Finally, you can also leverage the DB's sum calls to get total amounts as well. For example, your cart model could have:
has_many :products, :through => :line_items
Then, your total weight function would look like
def total_weight
products.sum :weight, :conditions => {:for_download => false}
end
If you had a cart with a huge number of items, this would be quite beneficial - especially if you haven't loaded (and won't need) all of the line items.
Ted:
You have a point. Depends if he is saving the cart instance (as in a session). If he just saves the cart.id in the session and does Cart.find(session[:cart_id]) then he's safe.
I think you've introduced a bug by caching total_weight. If you add items to the cart after calling total_weight, the cached value will not be updated, no?
@Anks
I had the same problem.
Apparently render :partial passes the partial name as the variable name, not the child model name in the collection, as one would assume.
It seems the culprit was the call to parent.children.size right after parent.children.build. Once I removed that call in my view (where the number of children was to be displayed), the child fields appeared as they should!
Ryan, what a wonderful screencast, thank you.
However, when I follow your instructions, the text_fields for my child model do not appear. Playing with the model in the ruby script/console I discovered that parent.children.build creates an empty array of children the very first time it is called, and a @parent.children.size of 1, but the correct number of children the second time it is called.
My parent class is Organ (organization) which inherits from Party. Party has many Addresses.
>> o = Organ.new
=> [#<Organ:0x4a3001c @attributes={ etc.}, @new_record=true>]
>> o.addresses.build
=> [#<Address:0x4a1bf40 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> []
>> o.addresses.build
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
Okay, sorry, I just answered the question myself. I had to generate the "home" controller and put the index.rhtml in the corresponding views directory.
Thanks for the great screencast! One (possibly stupid) question though: Where in the rails app does the third quoted file (index.rhtml) go to make it work?
Ryan. I have followed the tutorial to the letter and i still can't get it to work. When i look at the javascript file /javascripts/dynamic/states.js i get the same as you do. However when i load a page the dynamicness just doesn't work... any ideas?
What about putting configuration in the database? I know wordpress does something like this and I'd be interested in what the Rails community thinks about it.
Has anyone come up with a way to get rid of the HTML validation errors rails creates with double underscores and non-unique id attributes when setting :index => nil ?
Hey Ryan, do you know of a way, to use the Application Layout for one section all the time (say header and footer) and then use different layouts in the yielded section.
I don't like having the cached files appear in public/ -- it makes life hard for doing svn (a whole bunch of spurious cached files appear there). I also don't like the idea of setting public/ world writeable (or even having world-writeable files in the cache) on the production end.
After some effort, I got all this working under nginx and rails 2.0; see instructions here if you're interested:
http://pastie.caboo.se/145116
that is, display the error msg for each field in asset (child model). If you have a grandchild model, you can do the same probably.
finally, here's my method for displaying the error msg
def error_msg_for( record, field=nil)
return '' if record.errors.empty?
if field
a = []
record.errors.each do |attr,msg|
a << msg if attr == field
end
%(<div class="field_error">#{field} #{a.join('<br>')}</div>) unless a.empty?
else # if field == nil
%(<div class="errorExplanation">There was a problem with the data you submitted. Please fix the error(s) below then re-submit.</div>)
end
end
so I can put
<%= error_msg_for(@post) %>
without a field parameter at the top of the form, it'll display the generic "you have an error" msg when there's any error in the parent or child.
you may notice I check for possible multiple error msgs for the same field. I don't know if this ever happens or not.
also, the field name needs to match exactly with the attribute. if you're not getting the error msg back as expected, dump out errors array to see what's in it.
that makes perfect sense. yes, i'm using a mongrel cluster so i added that to production.rb and /tmp/cache now stores all the cached partials.
i'd also noticed that without that line the cached partial wouldn't expire with the sweeper consistently - but i think it makes sense now. it was probably not getting refreshed on all mongrels. but now that problem is gone as well. thanks!
@sthapit, there are various places the cache could be stored. In development mode this defaults to a file store, but in production it defaults to a memory store. If you have a mongrel cluster then memory store probably isn't the best because it will need to be cached separately for each mongrel process and memory could get out of hand. Instead I recommend starting with a file based store by adding this line to your production.rb file:
Great screencast - things worked without a flaw on my development machine and the cached partials are stored in /tmp/cache directory. I noticed, however, on the production machine that neither the cached partials nor the /tmp/cache directory exist - am I missing something or are my partials not getting cached? Thanks.
p.s. I have this line in environments/production.rb: config.action_controller.perform_caching = true so I believe caching is turned on.
@Bryce, many developers don't like calling model finds directly from the view, but I usually don't have a problem with it. IMO views have as much access to models as controllers do in MVC.
But then you could say what's stopping the view from taking over the controllers role and doing all model processing? It's important to understand the role of a controller.
Don't think of the controller as a layer between the view and the model, instead think of it as a layer between the user and the application. The controller should process user input and filter that to the models and views. Many times this involves taking the params hash and fetching models for the view.
Notice in this case the @recent_products array had nothing to do with user input. Therefore it belongs in the view more than the controller. Why should the controller care that we want to display the recent products in addition to what the user requested?
I would have a much harder time moving the @product find itself into the view because this comes from the user input (params hash) and is directly what the user requested.
This is all my own theory. Other developers think differently so I encourage you to find what works best for you.
@chris, great question. Unfortunately I don't know the answer as I don't test caching. I imagine you will have to enable caching temporarily in your testing environment and check for the existance of the file. Alternatively you could use mocking and just make sure youre view calls the cache method with the proper parameters.
Hi Ryan,
Hopefully you're still reading comments for this Railscast. This series has helped me out with my current application, thanks!
One quick question: if I use a check_box in my partial when using fields_for, it always tries to submit two entries for every one displayed on the page. I'm assuming this is because the check_box helper adds a hidden field for when the box is not checked. Is there any way around this?
Thanks! -- Bryan
For me things are going ok until I try to re-factor into partials.
This works fine:
<% for task in @project.tasks%>
<%= render :partial => 'task', :locals => {:task => task} %>
<% end %>
but using collection...
<%= render :partial => 'task', :collection -> @project.tasks %>
results in an error:
`@project[service_attributes]' is not allowed as an instance variable name
I would simply pass locals as in the first example which works fine except that when I add link_to_function and pass in a new object I get the same error.
I've double checked everything and can't work out what the problem is. On Rails 2.02
Hello Ryan,
great tutorial, exactly what I was looking for.
Thanks for this and all your terrific podcasts
Ryan,
you just saved me a bunch of time in having to figure this out on my own. Thank you!!
Is you get sessioncontroller uninitialized controller. then
put those lines to your routes.rb
#-------------------
map.resources :users
map.resource :session#, :controller => 'sessions'
map.signup '/signup', :controller => 'users', :action => 'new'
map.login '/login', :controller => 'sessions', :action => 'new'
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
#----------------------
It is known problem which is not resolved in rails 2.0 and has the Ticket #10101
http://dev.rubyonrails.org/ticket/10101
Thanks for attention.
With rails 2.0.2 and after adding ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS[:rfc3339] = '%Y-%m-%dT%H:%M:00.000Z' to environment.rb I get a "rake aborted! uninitialized constant ActiveSupport" each time I run rake db:migrate. I am unable to find why... someone can help?
@Artūras, Ted, and Aaron. Very good points. I agree your solution is better. Thanks for pointing this out.
@Brian, I thought of using a YAML file too, and I agree it is better than placing it in the code. But I still prefer having a full model. This way it will have a ruby class in front and allow us to refactor more code into there if need be. We can also move calculations into the database to improve performance if we need to as Aaron mentioned.
Hi Ryan (and all the rails guys out there)
after some trouble I have finally gotten it and now I can add and remove articles. What I would like to know now is showing the remove link only if the article is not the first so I surrounded the link_to_function helper with a if but basically I don't know how I can check out if it is the first element of the collection.
Could you please help me with this?
Thanks and have a nice day!
Hello,
Great site and great coding! I would like to see a railscast on Rspec. Any plans for that?
David :)
partial: http://pastie.caboo.se/149027
In the log the array is messed.
"set_mappings"=>[{"file_name_pattern"=>"*pricelistsample*"}, {"format"=>"1", "file_name_pattern"=>"*availability*"}, {"format"=>"1", "id"=>"953125641"}, {"id"=>"996332877"}]
It looks like rails collect the array in a wrong way. What could it be?
@levi I too am seeing the parameters get wonky when using multiple attributes in the child object. Let me know if you discover a solution, I will do the same.
I think having a 4 row db for shipping prices is a bit much. Using a case statement could reduce that down. If you didn't want the numbers in the file I'd store them in a yaml file or plain text before a database.
So i realize this is late, but no one on railsforum seems to know how to help, or just dont care to...
Will this not work for creating a model with multiple attributes? My parameters are keep getting screwed up. Lets say we are creating or updating a task that also has a completed_on attribute, well instead of getting a task model for the ones im editing/creating, it somehow sends a seperate set of paramaters for each attribute. So say I have 2 tasks, I will have params like so...
"task_attributes => [{"task" => "foo"}, {{"task" => "bar"}, {"completed_on" => "1/2/3"}, {"completed_on" => ""}, {"should_destroy" => ""},{"should_destroy" => ""}]"
Can you show us how to get the autocomplete and in-place edit work in rails 2.0? I am not sure how.
What about that JRails with JQuery stuff? I think there is a plugin for it.
thanks as always
Hi Ryan,
thanks for your wonderful series. We had some trouble with your javascript. If there is only one state in a selected country, your script displayed that state twice. To fix that we changed "options.length = 1;" to "options.length = 0;" and also the if clause to "if(options.length == 0)"
@Ted - the instance variables will only be cached for the lifecycle of the object, which unless you're doing something really bizarre will be contained in the lifecycle of the request
So the memoization shouldn't be an issue
i was trying to download the file and store this video in another PC,
but i was unable to download it,is it not possible with out quicktime player installed in my PC ?.
aah,but later i installed player but this time the movie itself is playing in the browser..
plz help me how to download the file seperately
thanks,,
I have some problems using scope_out and ferret+acts_as_ferret. When I configure acts_as_ferret and try to start the server I always get an error:
(druby://localhost:9010) /....../vendor/plugins/scope_out/lib/scope_out.rb:70:in `method_missing': undefined method `aaf_configuration' for #<Class:0x251b3bc> (NoMethodError)
Does anyone experience the same problems?
Great work! I'm waiting for the next ;)
[]'s
First off, great screencasts. Love them all and look forward to new ones every week.
I want to jump in and agree with Artūras and Ted. In this situation, using the instance variable could introduce unusual behavior down the line that is difficult to track down. In fact, I was surprised when the screencast ended that you didn't show that.
(In an effort to help any rails noobs out there with specific code for this, I thought I'd include the actual code I'd use).
def shipping_price
case total_weight
when 0
0.00
when 0..3
8.00
when 3..5
10.00
else
12.00
end
end
Finally, you can also leverage the DB's sum calls to get total amounts as well. For example, your cart model could have:
has_many :products, :through => :line_items
Then, your total weight function would look like
def total_weight
products.sum :weight, :conditions => {:for_download => false}
end
If you had a cart with a huge number of items, this would be quite beneficial - especially if you haven't loaded (and won't need) all of the line items.
Ted:
You have a point. Depends if he is saving the cart instance (as in a session). If he just saves the cart.id in the session and does Cart.find(session[:cart_id]) then he's safe.
I like the case statements approach better too.
Check out my blog post on mass assignment..
http://www.snailrails.com/2008/1/mass-assignment
Hey, I wrote a quick informative tutorial about XSS on http://www.snailrails.com/2008/1/cross-site-scripting.
Check it out for some more information.
Here is a short informative post I wrote on SQL injections!
http://www.snailrails.com/2008/1/sql-injection
I think you've introduced a bug by caching total_weight. If you add items to the cart after calling total_weight, the cached value will not be updated, no?
I would not cache total_weight, but instead swap if/elsif statements for case statement. It only evaluetes condition once.
irb(main):001:0> def call_me; puts 'called!'; 16; end
=> nil
irb(main):002:0> call_me
called!
=> 16
irb(main):003:0> case call_me
irb(main):004:1> when (0..3)
irb(main):005:1> puts 'zero to three'
irb(main):006:1> when (4..10)
irb(main):007:1> puts 'something else'
irb(main):008:1> when (11..20)
irb(main):009:1> puts 'a lot'
irb(main):010:1> else
irb(main):011:1* puts 'whee'
irb(main):012:1> end
called!
a lot
=> nil
Nice cleaning up!
I also really like the one liners, it keeps your code clean.
Keep up the good work, I already learned a lot from your railscasts.
@Anks
I had the same problem.
Apparently render :partial passes the partial name as the variable name, not the child model name in the collection, as one would assume.
render :partial => 'organisation_with_index_nil_for_person'
is sending a variable named organisation_with_index_nil_for_person to your partial, but your are expecting a variable named organisation.
Try renaming your partial to _organisation.rhtml and call it with:
<%= render :partial => 'organisation', :collection => @person.organisations %>
It worked for me. Further reference, see http://dev.rubyonrails.org/ticket/5524
It seems the culprit was the call to parent.children.size right after parent.children.build. Once I removed that call in my view (where the number of children was to be displayed), the child fields appeared as they should!
Ryan, what a wonderful screencast, thank you.
However, when I follow your instructions, the text_fields for my child model do not appear. Playing with the model in the ruby script/console I discovered that parent.children.build creates an empty array of children the very first time it is called, and a @parent.children.size of 1, but the correct number of children the second time it is called.
My parent class is Organ (organization) which inherits from Party. Party has many Addresses.
>> o = Organ.new
=> [#<Organ:0x4a3001c @attributes={ etc.}, @new_record=true>]
>> o.addresses.build
=> [#<Address:0x4a1bf40 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> []
>> o.addresses.build
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
>> o.addresses.size
=> 1
>> o.addresses
=> [#<Address:0x4a15f50 @attributes={ etc.}, @new_record=true>]
What could be going on here?
Okay, sorry, I just answered the question myself. I had to generate the "home" controller and put the index.rhtml in the corresponding views directory.
Thanks for the great screencast! One (possibly stupid) question though: Where in the rails app does the third quoted file (index.rhtml) go to make it work?
@Quint, the sweeper snippet is custom. You can find it here.
http://pastie.caboo.se/144239
Ryan. I have followed the tutorial to the letter and i still can't get it to work. When i look at the javascript file /javascripts/dynamic/states.js i get the same as you do. However when i load a page the dynamicness just doesn't work... any ideas?
Since we are loading configs on server startups,
Is there anyway to change & reload config file/table without server restarts ?
Where did you get the sweeper snippet for TextMate? Couldn't find it in the RoR bundle.
What about putting configuration in the database? I know wordpress does something like this and I'd be interested in what the Rails community thinks about it.
Has anyone come up with a way to get rid of the HTML validation errors rails creates with double underscores and non-unique id attributes when setting :index => nil ?
Hey Ryan, do you know of a way, to use the Application Layout for one section all the time (say header and footer) and then use different layouts in the yielded section.
should i just use partials?
Juan, I found for some reason it only works if you have a file_field for ALL the pics. So for the existing pics, I'd simply hide them
<%= pic_form.file_field :uploaded_data , :index => nil, style=>'display:none' %>
not pretty, but it works
This is, as usual, quite awesome.
I don't like having the cached files appear in public/ -- it makes life hard for doing svn (a whole bunch of spurious cached files appear there). I also don't like the idea of setting public/ world writeable (or even having world-writeable files in the cache) on the production end.
After some effort, I got all this working under nginx and rails 2.0; see instructions here if you're interested:
http://pastie.caboo.se/145116
Thanks for this episode
What's the use of "*files" in the helper method, looks like some kind of dereferenced pointer to me - I'm certainly not a ruby guru ;-)
here's how I handle error msgs. If someone can suggest improvements, I'd appreciate it.
In this example: post = parent model, asset = child model
I check @post.errors. if it's not empty, I display some generic msg, like: you have one or more errors, pls fix then submit again.
@post.errors is an activerecord object, an array of attr=>msg like this
['title'=>"title can't be blank",'body'=>"body can't be blank',...]
Next, I display the error msg for each field right above it, like this
<label for="post_title">Title</label><br />
<%= error_msg_for @post, 'title' %>
<%= f.text_field :title, :size => 60 %>
<br />
<br />
<label for="post_body">Body</label><br />
<%= error_msg_for @post, 'body' %>
<%= f.text_area :body, :cols => 60, :rows => 15 %>
in my child model, asset, do similar thing
<% fields_for "post[asset_attributes][]", asset do |f| %>
<%= error_msg_for asset, 'name' %>
<%= f.text_field :name, :size => 15 %>
...
<% end %>
that is, display the error msg for each field in asset (child model). If you have a grandchild model, you can do the same probably.
finally, here's my method for displaying the error msg
def error_msg_for( record, field=nil)
return '' if record.errors.empty?
if field
a = []
record.errors.each do |attr,msg|
a << msg if attr == field
end
%(<div class="field_error">#{field} #{a.join('<br>')}</div>) unless a.empty?
else # if field == nil
%(<div class="errorExplanation">There was a problem with the data you submitted. Please fix the error(s) below then re-submit.</div>)
end
end
so I can put
<%= error_msg_for(@post) %>
without a field parameter at the top of the form, it'll display the generic "you have an error" msg when there's any error in the parent or child.
you may notice I check for possible multiple error msgs for the same field. I don't know if this ever happens or not.
also, the field name needs to match exactly with the attribute. if you're not getting the error msg back as expected, dump out errors array to see what's in it.
that makes perfect sense. yes, i'm using a mongrel cluster so i added that to production.rb and /tmp/cache now stores all the cached partials.
i'd also noticed that without that line the cached partial wouldn't expire with the sweeper consistently - but i think it makes sense now. it was probably not getting refreshed on all mongrels. but now that problem is gone as well. thanks!
@sthapit, there are various places the cache could be stored. In development mode this defaults to a file store, but in production it defaults to a memory store. If you have a mongrel cluster then memory store probably isn't the best because it will need to be cached separately for each mongrel process and memory could get out of hand. Instead I recommend starting with a file based store by adding this line to your production.rb file:
config.action_controller.fragment_cache_store = [:file_store, "#{RAILS_ROOT}/tmp/cache"]
Just as a note, all javascripts should be included at the bottom of the page right before the body close tag. It's better performance wise.
Ah the link is now working for me!
Thanks for the great screencasts!
Great screencast - things worked without a flaw on my development machine and the cached partials are stored in /tmp/cache directory. I noticed, however, on the production machine that neither the cached partials nor the /tmp/cache directory exist - am I missing something or are my partials not getting cached? Thanks.
p.s. I have this line in environments/production.rb: config.action_controller.perform_caching = true so I believe caching is turned on.
/current/tmp: ls only shows
pids
@Bryce, many developers don't like calling model finds directly from the view, but I usually don't have a problem with it. IMO views have as much access to models as controllers do in MVC.
But then you could say what's stopping the view from taking over the controllers role and doing all model processing? It's important to understand the role of a controller.
Don't think of the controller as a layer between the view and the model, instead think of it as a layer between the user and the application. The controller should process user input and filter that to the models and views. Many times this involves taking the params hash and fetching models for the view.
Notice in this case the @recent_products array had nothing to do with user input. Therefore it belongs in the view more than the controller. Why should the controller care that we want to display the recent products in addition to what the user requested?
I would have a much harder time moving the @product find itself into the view because this comes from the user input (params hash) and is directly what the user requested.
This is all my own theory. Other developers think differently so I encourage you to find what works best for you.
@chris, great question. Unfortunately I don't know the answer as I don't test caching. I imagine you will have to enable caching temporarily in your testing environment and check for the existance of the file. Alternatively you could use mocking and just make sure youre view calls the cache method with the proper parameters.