oh and i forgot:
following code gives me a set of column headers with the names of the fields which rails does not find using your code:
<% for column in user.emails.content_columns %>
<th><%= column.name %></th>
<% end %>
hi ryan,
this is drivin me crazy, it just does not work in my case.
i always get that undefined method error up there in my last post.
im trying to give a user(tablename = "users", model name = "User") the possibility to have many emails(table name = "emails", model name ="Email")
im using REST, might this be the problem?
thanks
Hello, I have will_paginate working with lowpro. It does ajax pagination just fine. Except that I have two partials in the same view, and I cannot figure out how to make each work separately. The first page renders fine, because each partial draws from a separate instance variable in the controller. But when you click on another page for each partial, each partial sends the same exact message to the controller. Each partial says "give me page two for this view", rather than "give me page two for this partial". Any hints?
For me it works perfectly but now i have to add several "third" models that are related to my second model. How can i do that. Anyway an issue with this code is the fact that we will have input fields with same ids...
@Ryan: I may have an easier method of removing tasks, but I'll need to test it when combined with the concepts in this set of screen casts.
I'm using line items of an invoice, but I'll use your terminology. So I have a collection of @tasks from the database, and a new empty array: new_tasks. I loop through the posted params, searching for an existing entry in @tasks, which I then update. If not found, I build a new task instead. Either way I append that task to the new_tasks array.
When I assign the @project.tasks = new_tasks and save @project, Rails handles the removals for me, simply by the fact that they don't exist in the collection... smart! They don't exist in the collection because they weren't posted and added to new_tasks. So I have no need to mark for destroy or even call destroy.
I have a bunch of code in the controller, and I'm not using the virtual attributes you mentioned... so I'm really looking forward to combining these to methods and seeing if it all works!
A cool extension to this would be to also generate a flash paper doc. I'm not sure of the exact implementation, but I know a lot of the social document sharing services are doing this. That way the doc can be viewed in the browser rather than needing a separate app.
I've got a Rails app that I'll be adding PDF generation to soon. I've collected some links to various blog entries and tutorials. Maybe others will find them useful.
2nd,
maybe you want to share your experience using other pdf-writing-tools(gems) in some words, which you mentioned to be not as easy as this one, but better.
(Additional Links in the resources of this episode would be enough)
3rd,
with the new rails (upcoming versions) you can put your mimetypes in a separate file in the folder 'configuration/initializers' which is called 'mime_types' and for example put the 'require'-stuff in a seperate file 'requirements.rb' to keep your envirements.rb file clean (no changes)
i tried to work with PDF::Writer in the last weeks, and i have to say that RGhost looks really good, but do you know about some translation, because http://rghost.rubyforge.org/doc/ looks english but is in fact Portuguese...
Or does anybody has some short sample apps to build on???
I must agree with others, your screencasts are really top notch. Keep up the good work!
Regarding this one, I have one question, but it is not about writing pdf documents - I want to read and parse pdf. Is there some useful gem/plugin available for this kind of stuff? I've looked around but without any success. I would really appreciate any tip or recommendation on how to get into pdf parsing with ruby/rails.
To generate PDF reports there's also *RGhost*. It's a gem (also available as a rails plugin) that wraps PostScript with pure Ruby syntax. The gem is very stable and tested with reports over 1k pages in various OS's. The downside is that you need ghostscript libs installed.
More info: "RGhost[Rubyforge]":http://rubyforge.org/projects/rghost/
hi ryan
thanks for the great railscasts they are a great help!
now, im having a little prroblem trying to implement this one.
When i call the New project view which includes the code for the childs, my app seem not to recognize the child because it says it doesnot find one of the tasks fields:
undefined method `description' for #<Task:0x455e8a4>
I couldnt find your email Ryan, so I'll just post this here.
I would like to make a suggestion for your next episode: parsing text files with callback methods. There seems to be much confusion around this topic and we had a hard time in the rubyonrails irc channel figuring out when the uploaded file becomes available as an instance so the parsing can be done. Another question that came up: how can it be done so that only one insert statement takes place?
We were using the file_column plugin to handle file uploads.
I think many people would appreciate if you presented your solution.
Thanks. Your screencasts rock! I've watched every single one!
instruction left by @The Batman are outdated (already!). Err, or whomever maintains will_paginate, really needs to add Ajax support. Without this plugin is not "game-day" ready.
I hacked up a version that works for me but it's not publishable.
I would look for another paginator that supports Ajax before using this one.
One thing that made me curious enough to dig thru the initializer code was the bit on having to set the config.log_level = :debug. This isn't necessary. The initializer only sets the log level to :info if the environment == 'production'. Otherwise, all other environments are set to :debug
You shouldn't use .detect to pull up the appropriate task. To keep the task associated with the Project, use the association finder method tasks.find_by_id(params[:id])
This way you avoid instantiating a lot of unnecessary task models. This is essential when a model may have thousands of sub-models.
The helper inserts a hidden field after checkboxes to have it send 0 if the box isn't checked.
Unfortunately it means that when it is checked both the 1 and the 0 get sent and it thinks there are 2 sets of attributes to save. It can also mean the values get applied to the wrong rows of data.
I have items that belong to a studyform. I am trying to save both an item name and and item type into each studyform. My _item partial has:
<% fields_for "studyform[item_attributes][]", item do |f| %>
<p>Item: <%= f.text_field :name, :index => nil %>
Type: <%= f.text_field :type, :index => nil %>
<% if item.new_record? %>etc
The name saves into the items database but the type is always null. Any help would be appreciated.
You may be interested in my solution, especially when it comes to doing ajax deletes that are supported by clients without javascript. http://www.eribium.org/blog/?p=165
I have always wondered why there was no "get a confirmation" page that was generated. I honestly agree this is the missing "restful rails" functionality, and without it stripped down browsers simply won't scale.
Not everyone CAN enable javascript, after all. And I certainly don't have it on in tests :)
if you use the after_update instead of the after_save callback and you are creating a project for the first time (aka, not updating) and you create a task then delete it and then hit save the deleted task will still be created. this happens because save_tasks callback is not called on the create of the project model so you don't do the check for should_destroy?. one fix is to just use after_save instead of after_update, although i guess this results in a double save on the initial creation of a project.
another fix is to add a before_create alongside the after_update:
before_create :bc
def bc
fields_to_destroy = []
fields.each do |field|
fields_to_destroy << field if field.marked_for_destroy?
end
fields_to_destroy.each {|field| fields.delete(field) }
end
i think this is optimal because it won't even hit the db for new tasks on a new project that are marked for delete. but changing the callback to before_save is much simpler and less prone to bugs
Finally! Someone else has picked up on this. I have been banging on about this for well over a year. Shortly after Simply Helpful was announced I posted an article called "Simply RESTful... The missing action":http://www.thelucid.com/articles/2006/07/26/simply-restful-the-missing-action
Ryan, I'm begging you please take a look at this post, as the amount of work in your screencast shouldn't be necessary. This is what I was trying to outline over a year ago. I'm glad that someone with some clout has picked up on it, as it seems that unless you're part of the Rails core in-crowd, your views rarely get taken seriously.
Great screencasts! I would love to hear your feedback on the above.
an afterthought to my comment:
an dialog-box to confirm the delete is not the best way from a usability/accessabilty standpoint to protect from unintended deletes.
better would be to do the confirm it the http://del.icio.us/ way:
generating an inline link "delete this post?" after the first delete click
or the best practice albeit very complex giving an undo-option after delete the http://mail.google.com/mail/ way.
(For an in depth-discussion see: http://www.alistapart.com/articles/neveruseawarning)
What about going for the classic progressive enhancement way?
1.) Generate the normal html-form to destroy
2.) Via Javascript:
2a.) hide the form
2a.) insert a link after the hidden form that submits the form on click.
Something else:
You can have a form with a button-tag instead of submit-tag - a button-tag can be made look like a normal link via css (quite some work due to inconsistencies in the css implementation from browser-vendor to browser-vendor). The sad thing is the naming for a helper for this should be "button_to" but this is already taken for something that should better be called "submit_to".
Yet an other way for an ajax-destroy:
1.) Generate the normal html-form to destroy
2.) Via Javascript:
2a.) replace the form with a link that makes an ajax-request to destroy
here the JS (requires Prototype 1.6! - but would be easyly adaptable for 1.5):
(implemented via PeriodicalExecuter because I also dynamically generate new list-elements)
provided html list of posts with id="posts" (requires Rails 2.0 - but would be easyly adaptable for 1.2):
_posts.html.erb:
<!-- ============ begin a summary ========= -->
<li id="<%= dom_id(post) %>">
<h2><%= h(post.title) %></h2>
<p><%= h(post.body) %>
<%= button_to "Destroy", post, :method => :delete, :id => "destroy_#{dom_id(post)}" %>
</li><!-- end a summary -->
Thanks for your Screencasts...
@HappyCoder: Back to school ;-)
Let me just say Ryan: thou are the man. My need for this and your creation of these podcasts came hand in hand. Props. Also, thanks a lot @Ilya for the error fixes when using the latest edge.
@Joan: try 'puts params[:project]' before that line of code in your controller and then check your development.log to see what it's spitting out. If it's not normal, then my guess if that you have a form builder problem in one of your views. Good Luck!
Anybody got an idea how to generate a google map in PDF?
oh and i forgot:
following code gives me a set of column headers with the names of the fields which rails does not find using your code:
<% for column in user.emails.content_columns %>
<th><%= column.name %></th>
<% end %>
hi ryan,
this is drivin me crazy, it just does not work in my case.
i always get that undefined method error up there in my last post.
im trying to give a user(tablename = "users", model name = "User") the possibility to have many emails(table name = "emails", model name ="Email")
im using REST, might this be the problem?
thanks
Hello, I have will_paginate working with lowpro. It does ajax pagination just fine. Except that I have two partials in the same view, and I cannot figure out how to make each work separately. The first page renders fine, because each partial draws from a separate instance variable in the controller. But when you click on another page for each partial, each partial sends the same exact message to the controller. Each partial says "give me page two for this view", rather than "give me page two for this partial". Any hints?
For me it works perfectly but now i have to add several "third" models that are related to my second model. How can i do that. Anyway an issue with this code is the fact that we will have input fields with same ids...
Hi Brian,
Flash paper is mostly generated via printing method not authored per se.
we have build a rails app urwords.com which uses it that way.
you can contact us in case you need any help
@Ryan: I may have an easier method of removing tasks, but I'll need to test it when combined with the concepts in this set of screen casts.
I'm using line items of an invoice, but I'll use your terminology. So I have a collection of @tasks from the database, and a new empty array: new_tasks. I loop through the posted params, searching for an existing entry in @tasks, which I then update. If not found, I build a new task instead. Either way I append that task to the new_tasks array.
When I assign the @project.tasks = new_tasks and save @project, Rails handles the removals for me, simply by the fact that they don't exist in the collection... smart! They don't exist in the collection because they weren't posted and added to new_tasks. So I have no need to mark for destroy or even call destroy.
I have a bunch of code in the controller, and I'm not using the virtual attributes you mentioned... so I'm really looking forward to combining these to methods and seeing if it all works!
@Ryan Bates: Thanks for showing how to do this client-side. The idea of doing server requests just seemed silly. You make me luv Rails again. :-)
A cool extension to this would be to also generate a flash paper doc. I'm not sure of the exact implementation, but I know a lot of the social document sharing services are doing this. That way the doc can be viewed in the browser rather than needing a separate app.
Any thoughts on the rails PDF plugin?
http://railspdfplugin.rubyforge.org
Seems a lot cleaner to me to use an .rpdf view than putting a "Drawer" class in lib.
Thanks for the (as always) well-done screencast!
I've got a Rails app that I'll be adding PDF generation to soon. I've collected some links to various blog entries and tutorials. Maybe others will find them useful.
http://del.icio.us/tjstankus/pdf
There is nothing excellent about the PDF::Writer stuff... right now.
The Ruports[1] guys have just taken this over, and hopefully they make it the awesome package is could be.
1: http://www.rubyreports.org
Hi Ryan,
1st,
thx for this one, just great as always.
2nd,
maybe you want to share your experience using other pdf-writing-tools(gems) in some words, which you mentioned to be not as easy as this one, but better.
(Additional Links in the resources of this episode would be enough)
3rd,
with the new rails (upcoming versions) you can put your mimetypes in a separate file in the folder 'configuration/initializers' which is called 'mime_types' and for example put the 'require'-stuff in a seperate file 'requirements.rb' to keep your envirements.rb file clean (no changes)
4th,
keep going ^^
Hello Marcello,
i tried to work with PDF::Writer in the last weeks, and i have to say that RGhost looks really good, but do you know about some translation, because http://rghost.rubyforge.org/doc/ looks english but is in fact Portuguese...
Or does anybody has some short sample apps to build on???
Thanks
René
Hello Ryan,
I must agree with others, your screencasts are really top notch. Keep up the good work!
Regarding this one, I have one question, but it is not about writing pdf documents - I want to read and parse pdf. Is there some useful gem/plugin available for this kind of stuff? I've looked around but without any success. I would really appreciate any tip or recommendation on how to get into pdf parsing with ruby/rails.
Thanks and best regards!
Hi Ryan!
To generate PDF reports there's also *RGhost*. It's a gem (also available as a rails plugin) that wraps PostScript with pure Ruby syntax. The gem is very stable and tested with reports over 1k pages in various OS's. The downside is that you need ghostscript libs installed.
More info: "RGhost[Rubyforge]":http://rubyforge.org/projects/rghost/
Thanks Josh! It worked :)
I just had to say "W00000000T!"
hi ryan
thanks for the great railscasts they are a great help!
now, im having a little prroblem trying to implement this one.
When i call the New project view which includes the code for the childs, my app seem not to recognize the child because it says it doesnot find one of the tasks fields:
undefined method `description' for #<Task:0x455e8a4>
Extracted source (around line #11):
8: <tr><td><%= "Task:".t%></td><td><%= f.text_field :task, :size => 60 %></td></tr>
9: <% for task in @user.tasks %>
10: <%fields_for "...", task do |task_form| %>
11: <tr><td><%= "Task:".t%></td><td><%= task_form.text_field :description, :size => 60 %></td></tr>
12: <%end %>
13: <%end%>
I couldnt find your email Ryan, so I'll just post this here.
I would like to make a suggestion for your next episode: parsing text files with callback methods. There seems to be much confusion around this topic and we had a hard time in the rubyonrails irc channel figuring out when the uploaded file becomes available as an instance so the parsing can be done. Another question that came up: how can it be done so that only one insert statement takes place?
We were using the file_column plugin to handle file uploads.
I think many people would appreciate if you presented your solution.
Thanks. Your screencasts rock! I've watched every single one!
Does this still work with the latest Rails trunk? I get this error: Can't mass-assign these protected attributes: id
"Counter Cache migration article":http://josh.the-owens.com/archives/2007/11/03/rails-edge-change-how-to-add-a-counter-cache-to-an-existing-db-table/
Beate,
I recently posted about this very issue on my blog. I ran into the same problem about a week ago.
Check it out:http://josh.the-owens.com/archives/2007/11/03/rails-edge-change-how-to-add-a-counter-cache-to-an-existing-db-table/
I dont know where do you place the projects_path, or what it is.
Great tutorial!
I'd recommend calling capture before markaby and then setting a local variable for it. Railsforum could also be of help. Keep us updated
Ajaxify will_paginate
instruction left by @The Batman are outdated (already!). Err, or whomever maintains will_paginate, really needs to add Ajax support. Without this plugin is not "game-day" ready.
I hacked up a version that works for me but it's not publishable.
I would look for another paginator that supports Ajax before using this one.
Thanks for the tip.
One thing that made me curious enough to dig thru the initializer code was the bit on having to set the config.log_level = :debug. This isn't necessary. The initializer only sets the log level to :info if the environment == 'production'. Otherwise, all other environments are set to :debug
I'm finding scope_out fails to assert key when using the :group (as in "group by") clause of find method.
Am I the only to notice this?
ArgumentError (Unknown key(s): group):
/var/lib/gems/1.8/gems/activesupport-1.4.4/lib/active_support/core_ext/hash/keys.rb:48:in `assert_valid_keys'
You shouldn't use .detect to pull up the appropriate task. To keep the task associated with the Project, use the association finder method tasks.find_by_id(params[:id])
This way you avoid instantiating a lot of unnecessary task models. This is essential when a model may have thousands of sub-models.
@Jamie, your solution is intriguing, although I'm not sure it belongs in core. I would really like to see a well supported plugin which does this.
Has anyone made this work with check boxes?
The helper inserts a hidden field after checkboxes to have it send 0 if the box isn't checked.
Unfortunately it means that when it is checked both the 1 and the 0 get sent and it thinks there are 2 sets of attributes to save. It can also mean the values get applied to the wrong rows of data.
<script>alert('Hi!')</script> :D
I think it's not possible any more in rails edge (2.0) to fill the count-colums afterwards, because (from the edge api):
"Note: Specifying a counter_cache will add it to that model’s list of readonly attributes using attr_readonly."
Some other idea for this?
I have items that belong to a studyform. I am trying to save both an item name and and item type into each studyform. My _item partial has:
<% fields_for "studyform[item_attributes][]", item do |f| %>
<p>Item: <%= f.text_field :name, :index => nil %>
Type: <%= f.text_field :type, :index => nil %>
<% if item.new_record? %>etc
The name saves into the items database but the type is always null. Any help would be appreciated.
Steve, I believe that issue is actually handled by the logic in the view code:
<% if task.new_record? %>
<%= link_to_function "remove", "$(this).up('.task').remove()" %>
<% else %>
When you're creating a new project, you just delete the entire field for the task rather than marking it for deletion.
You may be interested in my solution, especially when it comes to doing ajax deletes that are supported by clients without javascript. http://www.eribium.org/blog/?p=165
I have always wondered why there was no "get a confirmation" page that was generated. I honestly agree this is the missing "restful rails" functionality, and without it stripped down browsers simply won't scale.
Not everyone CAN enable javascript, after all. And I certainly don't have it on in tests :)
sorry for any confusion: replace 'field' with 'task'
here is a better example:
before_create :bc
def bc
tasks.dup.each do |task|
tasks.delete(field) if task.marked_for_destroy?
end
end
nice cast, there is one bug though.
if you use the after_update instead of the after_save callback and you are creating a project for the first time (aka, not updating) and you create a task then delete it and then hit save the deleted task will still be created. this happens because save_tasks callback is not called on the create of the project model so you don't do the check for should_destroy?. one fix is to just use after_save instead of after_update, although i guess this results in a double save on the initial creation of a project.
another fix is to add a before_create alongside the after_update:
before_create :bc
def bc
fields_to_destroy = []
fields.each do |field|
fields_to_destroy << field if field.marked_for_destroy?
end
fields_to_destroy.each {|field| fields.delete(field) }
end
i think this is optimal because it won't even hit the db for new tasks on a new project that are marked for delete. but changing the callback to before_save is much simpler and less prone to bugs
Finally! Someone else has picked up on this. I have been banging on about this for well over a year. Shortly after Simply Helpful was announced I posted an article called "Simply RESTful... The missing action":http://www.thelucid.com/articles/2006/07/26/simply-restful-the-missing-action
Ryan, I'm begging you please take a look at this post, as the amount of work in your screencast shouldn't be necessary. This is what I was trying to outline over a year ago. I'm glad that someone with some clout has picked up on it, as it seems that unless you're part of the Rails core in-crowd, your views rarely get taken seriously.
Great screencasts! I would love to hear your feedback on the above.
an afterthought to my comment:
an dialog-box to confirm the delete is not the best way from a usability/accessabilty standpoint to protect from unintended deletes.
better would be to do the confirm it the http://del.icio.us/ way:
generating an inline link "delete this post?" after the first delete click
or the best practice albeit very complex giving an undo-option after delete the http://mail.google.com/mail/ way.
(For an in depth-discussion see: http://www.alistapart.com/articles/neveruseawarning)
What about going for the classic progressive enhancement way?
1.) Generate the normal html-form to destroy
2.) Via Javascript:
2a.) hide the form
2a.) insert a link after the hidden form that submits the form on click.
Something else:
You can have a form with a button-tag instead of submit-tag - a button-tag can be made look like a normal link via css (quite some work due to inconsistencies in the css implementation from browser-vendor to browser-vendor). The sad thing is the naming for a helper for this should be "button_to" but this is already taken for something that should better be called "submit_to".
Yet an other way for an ajax-destroy:
1.) Generate the normal html-form to destroy
2.) Via Javascript:
2a.) replace the form with a link that makes an ajax-request to destroy
here the JS (requires Prototype 1.6! - but would be easyly adaptable for 1.5):
(implemented via PeriodicalExecuter because I also dynamically generate new list-elements)
<script type="text/javascript">
//<![CDATA[
document.observe("contentloaded", function() {
new PeriodicalExecuter(function(pe) {
$$("#posts form").each(function(element) {
url = element.getAttribute("action");
id = "destroy_"+element.up("li").getAttribute("id");
className = "destroy";
element.replace(new Element("a", { href: url, id: id, className: className }).update("Destroy"));
$(id).observe("click", function(e) {
e.stop();
new Ajax.Request(e.target.getAttribute("href"), { method:'delete' });
});
});
}, 0.1);
});
//]]>
</script>
provided html list of posts with id="posts" (requires Rails 2.0 - but would be easyly adaptable for 1.2):
_posts.html.erb:
<!-- ============ begin a summary ========= -->
<li id="<%= dom_id(post) %>">
<h2><%= h(post.title) %></h2>
<p><%= h(post.body) %>
<%= button_to "Destroy", post, :method => :delete, :id => "destroy_#{dom_id(post)}" %>
</li><!-- end a summary -->
Thanks for your Screencasts...
@HappyCoder: Back to school ;-)
Yuou should add javascript.rb to coderay scanners it will be nicer :)
Thanks for all casts!!
@HappyCoder - html inside of RSS/Atom feeds, read thru a feed reader will often have javascript disabled or striped out.
If a user doesn't have js enabled, then f*ck him.
Thank you, Henry. I finally was able to found out the problem, it's actually caused by the failure of validation in task model.
Found this on the net:
You don't have to make a new field in your table, yu could just use this instead:
def to_param
"#{id}-#{name.gsub(/[^a-z1-9]+/i, '-')}"
end
replace name with the field you want to slug/permalink.
What do you think Ryan?
Sup Ryan,
When you did:
map.task_archive 'tasks/:year/:month', :controller => 'tasks', :action => 'archive'
Doesnt this conflict with the standard routing:
tasks/show/2
thnxs
Hi, how do you combine this with eagerLoading?
Task.find(:all, :include => projects, :conditions => ["tasks.complete=? and tasks.priority = ?", false, nil])
Does this work?
tHnx
Let me just say Ryan: thou are the man. My need for this and your creation of these podcasts came hand in hand. Props. Also, thanks a lot @Ilya for the error fixes when using the latest edge.
@Joan: try 'puts params[:project]' before that line of code in your controller and then check your development.log to see what it's spitting out. If it's not normal, then my guess if that you have a form builder problem in one of your views. Good Luck!