RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

@riki

don't just say that you got it working. what did you do wrong? What was the fix? always give details since it could help someone else.

Avatar

Never mind, still mistake on my part, it's working now.

Avatar

I got as far as installing the pane, but it's giving me the following error message:

Can’t find the Phusion Passenger Apache module. 
Visit http://www.modrails.com for installation instructions.

Avatar

Hello.

I just installed passenger last night on a Ubuntu Dapper Box using the prepacked .deb from brightbox:
 
http://www.modrails.org/install.html

Installing without any problems - seems to work fine!

Great job guys, great job ryan!

Avatar

I'm wondering if I did something stupid (I did make some changes to fit it into my app), since nobody else has mentioned this problem, but my tests (luckily) turned up a nasty bug when I implemented this solution (which is fantastic otherwise, btw!)

The problem comes from existing_task_attributes=. Since that runs before the parent model (Project) is validated, existing children (Tasks) will be changed/deleted in the DB even if the parent model fails validation. I haven't actually tried Ryan's Project/Task code myself, but in my own app this would allow these sorts of scenarios:

1. Delete all children on edit page, but parent requires children (here if a Project required 1 or more tasks). Parent fails validation and goes back to edit page, but children have already been deleted. If you cancel back out of edit, children are gone from DB even though parent never updated.

2. Delete all existing children, and add new one that is invalid. (Say a task requires a name and you don't add one, so that child fails validation.) Parent will fail validation if it validates associated records and will go back to edit page, but existing records will have been deleted. Again, if you cancel back out of edit, children are gone from DB even though parent never updated.

3. Modify existing child(ren), and add new one that is invalid. Like above, parent validation fails, but child is still updated.

Is this just a quirk of my app, or a bug nobody has hit/noticed/commented on?

My fix was just to wrap a transaction around the update_attributes call (and make it update_attributes! so it would throw an exception on failure) and move the else clause to a rescue block. This way, if the parent model validation fails all the other changes to the children are rolled back as well. Tested and it works, though check out my blog post for a gotcha about testing Rails transactions: http://freevirusesandspyware.com/2008/08/testing-rails-transactions.html

Avatar

If anyone does work out the best route for highlighting search terms, please post back. I intend on giving them a solid background colour rather than using the highlight visual effect.

Sean Hess, I'm a new to js and Rails, are you suggesting an application.js script that takes the params and highlights the DOM elements that match?

Indexing is taking ages on my MBP...

p.s. thanks again, Ryan.

Avatar

Another thing to look out for with partial updates is that changes to a serialized attribute do not mark the attribute as changed, and as a result, will not get saved. So if you have a model called EmailQueue and a serialized attribute called "email" that stores a TMail object, and you try this:

c = EmailQueue.first
c.email.body = "new email body text"
c.save

You will find that the change was not saved. Perhaps there is an easy way to add a :before_save callback that marks the serialized attribute as dirty? But then I guess it's probably just as easy to turn off partial updates for the whole model.

Avatar

Hi Ryan,

I can't get my head around something. When providing a new object to your session... why not just override the session method on the controller? This way no matter if you wrote the code or not you could make sure you are controlling the usage of the session data.

Any thoughts on that?

Jean-Marc

Avatar

I get the same message No definition for prof_get_cpu_frequency both under OS X (Leopard) & Linux (CentOS 5).

When I run the request script I get following:
Warming up once
0.03 sec, 1 requests, 29 req/sec

Profiling 100x
`gem install ruby-prof` to use the profiler

Avatar

Hi!

Thanks for great screencast!

Any ideas how to use it to test app which serves different subdomains eg using account_location plugin?

--Yury

Avatar

@Ryan, it just feels a little more clear to me. To me, "all" isn't the dataset, the collection of letters is.

Also, I tend to preemptively factor things out that I know I'm going to refer to in a lot of different places.

Maybe, I'm getting ahead of the implementation but, I could easily see it becoming appropriate as the class grows. So why not just do it up front?

Avatar

Hi Ryan, I`m your big fan
Thanks for the screencast.

But I have a question. I think including password in session is danger isn`t it?

Avatar

One thing I've noticed with these 'scope' methods is that performing count doesn't work if you're selecting from more than one table (eg :from => 'table1, table2') It only uses the first table. It seems I have to use the size method on the returned array since the actual scope is handled properly. Where would I report a bug like this?

Avatar

@Andy, what is the reasoning behind moving the letters into their own method? Is it for self documentation? I think the "all" method is simple enough to not warrant further refactoring.

However, if you need to add more characters to the "all" method in addition to alphabetical letters, then I would say refactoring out just those letters is worth it.

Avatar

I think that the letters should be factored out of the 'all' method.

For example:
http://pastie.textmate.org/249561

What do you think Ryan?

Avatar

I just built and released a little Rails plugin that uses this technique to easily search your models using a scope. This can be really useful if you want to combine a search with another scope or will_paginate:

class MyModel < ActiveRecord::Base
  named_scope :my_scope, { ... }
  searchable_on :several, :fields
end

MyModel.my_scope.search_for("search query").paginate( ... )

Get the source at http://github.com/wvanbergen/scoped_search/

Avatar

I discovered a few weeks ago that if you use a form_for for these models, you need to define the "id" method or you get nasty-grams in your web server log file.

Avatar

hey ryan! i'm trying this out today. it looks great in your tutorial! so hopefully it'll work for me.

noticed that the installer changed too, after the plugin just refused to get itself installed. please be advised :)

http://errtheblog.com/posts/56-im-paginating-again

Avatar

Hey Ryan,

Great Railcast man, I am integrating this saving multiple model concept while creating a Company and multiple People inside it on a single page for , Company.new action.

I have been able to save multiple People corresponding to a single company until I started using link_to_function and the partial alternative for saving unlimited TASKS for a PROJECT or PEOPLE for a COMPANY(in my case).
Actually when I saw the HTML which was being appended inside my <div id="persons">
with the help of code below, I was surprised to find, the div with id="persons" totally empty -

code for company/new.html.erb
_______________________________
.
.
.
<div id="persons" >
<%= render :partial =>'person', :collection=>@company.people %>
</div>

<%= link_to_function "Add a Person" do |page|
page.insert_html :bottom, :persons, :partial=>"person", :object=>Person.new end %>
.
.
.
________________________________
And below is my Partial _person.html.erb

<div>
  <% fields_for "company[person_attributes][]", person do |p| %>
 <table width="96%">
<tr>
<td width="21%" align="right">First Name</td>
                                  <td colspan="2"><%= p.text_field :first_name, :class=>"textbox", :style=>"width:180px" %>
                                    &nbsp;Last Name&nbsp;
                                    <%= p.text_field :last_name, :class=>"textbox", :style=>"width:180px" %></td>
 </tr>
 <tr>
 <td align="right">Designation</td>
 <td colspan="2"><%= p.text_field :jobtitle, :class=>"textbox", :style=>"width:180px" %></td>
</tr>
</table>
<% end %>
 </div>

.............................

Please tell me why nothing rendered inside my <div id="persons"> however it kept adding on the page outside the div, to the HTML being generated below with all weird escape characters, like
______________________________
<a href="#" onclick="try {
new Insertion.Bottom(&quot;persons&quot;, &quot; \u003Cdiv \u003E\n \n \n \u003Ctable width=\&quot;96%\&quot; border=\&quot;0\&quot; align=\&quot;left\&quot; cellpadding=\&quot;5\&quot; cellspacing=\&quot;0\&quot;\u003E\n \u003Ctr\u003E\n \u003Ctd width=\&quot;21%\&quot; align=\&quot;right\&quot;\u003EFirst Name\u003C/td\u003E\n \u003Ctd colspan=\&quot;2\&quot;\u003E\u003Cinput class=\&quot;textbox\&quot; id=\&quot;company_person_attributes__first_name\&quot; name=\&quot;company[person_attributes][][first_name]\&quot; size=\&quot;30\&quot; style=\&quot;width:180px\&quot; type=\&quot;text\&quot; /\u003E\n \u0026nbsp;Last Name\u0026nbsp;\n \u003Cinput class=\&quot;textbox\&quot; id=\&quot;company_person_attributes__last_name\&quot; name=\&quot;company[person_attributes][][last_name]\&quot; size=\&quot;30\&quot; style=\&quot;width:180px\&quot; type=\&quot;text\&quot; /\u003E\u003C/td\u003E\n...>Add a Person</a>
_____________________________

Avatar

Hi Ryan, and everybody, i have the following question, i am using the following code:

map.static ':permalink', :controller => 'pages', :action => 'show'

but in my application when i want to link from a different controller for example the session controller to a static page.

It is always add the previous controller to the url, like this.
http://0.0.0.0:3000/session/permalink

How can i fix this, thank you for all your great job.

Avatar

Ryan, thanks a bunch for this (and all screencasts).

I had a case similar to this in an app i'm working on and this is how i originally designed it.

But I was hesitant at first to have ActiveRecord methods inside a non-ActiveRecord class ... somehow it just didn't seem right. i thought the find methods should have been helpers in the controller, but this seems to make more sense.

Avatar

@Clemens, I considered defining to_s here too, but decided not to. Although it may work well in this example, for the majority of cases I don't think it does. There are often many different ways to represent a model as a string.

I prefer to be explicit each time so there's no confusion as to how it's being displayed. For example, let's say we had a Month model which represented a specific month of the year. How would you display this as a string? The full month name? The abbreviated version? Would you include the year? etc.

I think this decision of how to represent a model as a string belongs in the view, not the model.

Avatar

Finally ... I've seen questions regarding this stuff at Railsforum and WorkingWithRails. Now, instead of explaining it myself, I can link to this screencast! ;-)

Small suggestion: You override the to_param method which is really cool. However, if you do that, you might as well override the to_s method for the Letter class because I think it's a really underused feature in Rails (and probably Ruby in general).

Avatar

IMO the comment should know if it can be edited ( http://pragmatig.wordpress.com/2008/06/29/separate-rights-management-from-controllers/ ) and then link_to_edit(comment) ( http://pragmatig.wordpress.com/2008/07/09/generic-smart-link_to_s-link_to_edit-link_to_destroy/ )

Avatar

Nice tutorial. This has probably saved me a few hours of searching. Thanks!

Avatar

@Rob, I'm looking forward to Active Model as well. I think it will have a big impact in how we work with these kinds of models. I was hesitating to make this episode yet for that very reason, but figured it will be a while before Active Model is released.

Avatar

how can download the file via rjs

Avatar

I'm looking forward to the day when ActiveModel (http://github.com/rails/rails/tree/master/activemodel) is complete and stable enough to use: it'll be good to create tableless models that can still use the cool stuff like validations and observers.

Until that day though...thanks for the screencast Ryan.

ps. oooh a preview button :D

Avatar

Great stuff & good timing for a current project :)

Avatar

@Kenneth,

Here's what I did for quick and dirty validations without DB backing:

http://pastie.org/247701

[Admin Edit: moved to pastie]

Avatar

Good way to do it, just prompted me to update my activerecord-tableless-models plugin. Find the plugin at http://github.com/kennethkalmer/activerecord-tableless-models

Avatar

@Eric, yep, caching the "all" method is a possibility. But I tend to not add this kind of caching up front unless I know for sure it is worth it.

Caching usually comes with some hidden costs. I couldn't, for example, cache the "products" method as well because that will result in all products staying in memory for my entire app.

If you determine adding a global cache is worth it (with @@all) then it's best to call "freeze" on each object in the cache. This way you ensure the state of the object doesn't change.

@Tim, that's a great idea. Using scopes instead of a simple find adds a lot of potential.

Avatar

Thanks, it is great. in fact i need it in my application WebVZ

Avatar

How about using an anonymous scope for the products method. That way products would behave even more like an ar association.

Thanks for the great screencasts. Alway looking forward to Mondays.

Avatar

Nice!

Note that if no model uses ActiveRecord in your app, you can add

<<<<<<<<<<<<<<
Rails::Initializer.run do |config|
  config.frameworks -= [:active_record ]
end
>>>>>>>>>>>>>>>

to your environment.rb

And what about caching Letter.all with

<<<<<<<<<<<<<<
 def self.all
    @@all||=('A'..'Z').map { |c| new(c) }
  end
>>>>>>>>>>>>>>>

?

Avatar

@andrej: Don't you guys use Javascript for anything? :) Thanks for the great episode Ryan!

Avatar

I see no benefits from the new naming convention.
How can I migrate to VERSION?

The way it was:

rake db:migrate VERSION=1

The way it is?

Avatar

How would this work with caching?

Avatar

Great tip; It's really useful for including JavaScript/CSS that you only need on specific pages of your app.

I've used this technique to load some extra jQuery files and CSS that I needed for a sortable list and autocomplete widget. Very cool.

Avatar

@rob you can find the full code by clicking the "Full Source Code" link at the bottom of the code samples.

Avatar

I would do something like this:

cons = {:keywords=>["products.name LIKE ?","%#{keywords}%"], :minimum_price=>["products.price >= ?",minimum_price], :maximum_price=>["products.price <= ?",maximum_price], :category_id=>["products.category_id = ?",category_id]}

scope = Product.scoped({})
cons.each{|attribute,carray| scope = scope.conditions carray[0], carray[1] unless send(attribute).blank?}
scope

Avatar

So would this replace the need to use the scope out plugin?

Avatar

This could be shortened with code similar to:

a = Array.new(rating.floor){"full"}
(rating.ceil-rating.floor).times{a<<"half"}
(5-rating.ceil).times{a<<"empty"}

Avatar

my second impression is, that an endless list is nice, but it hides what we are scrolling. does the list contains bockwursts or plums or whatever... i mean the list header gets lost by scrolling.
an iframe thingie could be a solution?

best regards
chris

Avatar

I want to add the name of the author for each item. How do I do that?

Avatar

Jeff: Sphinx has support for fuzzy matching, but it's not enabled by default.

If you're using Thinking Sphinx, you can enable this by adding allow_star: true for every environment in config/sphinx.yml (ie: in a similar format to database.yml).

Avatar

hi Ryan....is there any possibility that you can zip and publish this project on your site (and all future projects)...

Avatar

Has anyone tested this in IE7? IE7 does not seem to like the prototype calls to remove the element from the form IF it was added via "Add a task" or other javascript call. Works fine in IE6 and FF (and Safari I'd imagine)

Any ideas?

Avatar

@John Bracy

I guess you could use a block to ensure the self.site thing gets set back to what it was before, without you having to remember to reset it.

something like:

class Pie < ActiveResource::Base
  self.site = "https://api.pie.com/"
  def self.for_user(user)
    old_site = self.site
    self.site = "https://#{user}@api.pie.com/"
    yield self
    self.site = old_site
  end
end

Pie.for_user(1) do |pie|
  rs = pie.find(:all)
  puts rs
end