RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

Anybody got an idea how to generate a google map in PDF?

Avatar

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 %>

Avatar

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

Avatar

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?

Avatar

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...

Avatar

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

Avatar

@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!

Avatar

@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. :-)

Avatar

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.

Avatar

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.

Avatar

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

Avatar

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

Avatar

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 ^^

Avatar

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é

Avatar

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!

Avatar

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/

Avatar

Thanks Josh! It worked :)

Avatar

I just had to say "W00000000T!"

Avatar

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%>

Avatar

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!

Avatar

Does this still work with the latest Rails trunk? I get this error: Can't mass-assign these protected attributes: id

Avatar

"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/

Avatar

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/

Avatar

I dont know where do you place the projects_path, or what it is.

Great tutorial!

Avatar

I'd recommend calling capture before markaby and then setting a local variable for it. Railsforum could also be of help. Keep us updated

Avatar

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.

Avatar

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

Avatar

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'

Avatar

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.

Avatar

@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.

Avatar

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.

Avatar

<script>alert('Hi!')</script> :D

Avatar

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?

Avatar

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.

Avatar

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.

Avatar

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

Avatar

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 :)

Avatar

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

Avatar

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

Avatar

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.

Avatar

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)

Avatar

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 ;-)

Avatar

Yuou should add javascript.rb to coderay scanners it will be nicer :)
Thanks for all casts!!

Avatar

@HappyCoder - html inside of RSS/Atom feeds, read thru a feed reader will often have javascript disabled or striped out.

Avatar

If a user doesn't have js enabled, then f*ck him.

Avatar

Thank you, Henry. I finally was able to found out the problem, it's actually caused by the failure of validation in task model.

Avatar

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?

Avatar

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

Avatar

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

Avatar

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!