Best ruby-on-rails-3 questions in March 2011

Rails: Why can't you set an association to nil in a where clause?

9 votes

So, I have photos that belong to collections and users. Photos always belong to a user, but may not be assigned to a collection.

In my controller, this works perfectly:

@collection_photos = Photo.where( :collection => @collection, :user => current_user )

However, this fails...

@other_photos = Photo.where( :collection => nil, :user => current_user )

...but this works:

@other_photos = Photo.where( :collection_id => nil, :user => current_user )

When collection is set to nil I get this error message: No attribute named 'collection' exists for tablephotos``

So, if I pass an object it knows to search for collection_id from the symbol :collection, but if I don't pass an object it doesn't seem to be aware of the association.

Am I understanding this correctly? Could anyone explain a little better why :collection=>nil doesn't work?

when you use pass in the conditions into ActiveRecord, it actually tries to analyze the objects that you passed in, is it a string? an array? a hash? and what's in the string, array or hash?

and in your case, a hash, so it's trying to analyze what's in the hash, in the first statement (which works), you passed in a model instance as the value, so it tries to find if there are any associations that mapped to the key your specified and voila, it found it and everything works as planned

in the second case, you passed in nil as the value, now, ActiveRecord sees that it's a nil object, so it decided that it's not an association. note that it doesn't look at the key, but it only looked at the value, thus it tries to find if there's any column that mapped to the key, but it couldn't find, returning an error

in the last case, you passed in nil as the value, same thing, it tried to find a column which mapped to :collection_id, thus it passed in nil as the value in the SQL statement, and it returned successfully

so it's just an unfortunate considerations taken by ActiveRecord that makes the second case not working =)

hope this clarifies! =D

STI, one controller

7 votes

Hi! I'm new to rails and I'm kind of stuck with this design problem, that might be easy to solve, but I don't get anywhere: I have two different kinds of advertisements: highlights and bargains. Both of them have the same attributes: title, description and one image (with paperclip). They also have the same kind of actions to apply on them: index, new, edit, create, update and destroy.

I set a STI like this:

Ad Model: ad.rb

class Ad < ActiveRecord::Base
end

Bargain Model: bargain.rb

class Bargain < Ad
end

Highlight Model: highlight.rb

class Highlight < Ad
end

The problem is that I'd like to have only one controller (AdsController) that executes the actions I said on bargains or highlights depending on the URL, say www.foo.com/bargains[/...] or www.foo.com/highlights[/...].

For example:

  • GET www.foo.com/highlights => a list of all the ads that are highlights.
  • GET www.foo.com/highlights/new => form to create a new highlight etc...

How can i do that?

Thanks!

Hi!

First. Add some new routes:

resources :highlights, :controller => "ads", :type => "Highlight"
resources :bargains, :controller => "ads", :type => "Bargain"

And fix some actions in AdsController. For example:

def new
  @ad = Ad.new()
  @ad.type = params[:type]
end

For best approach for all this controller job look this comment

That's all. Now you can go to localhost:3000/highlights/new and new Highlight will be initialized.

Index action can look like this:

def index
  @ads = Ad.where(:type => params[:type])
end

Go to localhost:3000/highlights and list of highlights will appear.
Same way for bargains: localhost:3000/bargains

etc

URLS

<%= link_to 'index', :highlights %>
<%= link_to 'new', [:new, :highlight] %>
<%= link_to 'edit', [:edit, @ad] %>
<%= link_to 'destroy', @ad, :method => :delete %>

for being polymorphic :)

<%= link_to 'index', @ad.class %>

Rails: Scheduled task to warm up the cache?

6 votes

I am using the following to cache a slow loading page using memcached:

caches_action :complex_report, :expires_in => 1.day

The controller action is protected by Devise authentication.

The page currently gets cached the first time a user requests it. Subsequent request that day are then pulled from the cache.

The problem with this is that the initial request takes 20-30 seconds to load. Is it possible to populate the cache in advance by way of a scheduled task?

Any suggestions much appreciated.

Here is an expansion on the previous cron based solution which uses curl's ability to store cookies so that you can auth in one step and then use the cookie again as an authenticated user in the next step. So if you put these lines in a script called "prepare_cache.sh"

rm /tmp/cookiejar
curl --request POST -d "login=<username>" -d "password=<password>" -c /tmp/cookiejar http://yourwebpages.url/login
curl --request GET -b -c /tmp/cookiejar http://yourwebpages.url/page_to_cache
rm /tmp/cookiejar

replacing the login and password parameters with ones which match the variables used in your login form and obviously the urls to call. I'm removing the cookiejar before to make sure there isn't a file there already and removing it at the end to make sure there isn't a cookie floating about with access levels it shouldn't have.

Then you can call this script with the cron job:

*/15 * * * * /home/myname/prepare_cache.sh > /dev/null 2>&1

And hopefully that should work. Seemed to work for me when I tried it.

Rails article helper - "a" or "an"

6 votes

Does anyone know of a Rails Helper which can automatically prepend the appropriate article to a given string? For instance, if I pass in "apple" to the function it would turn out "an apple", whereas if I were to send in "banana" it would return "a banana"

I already checked the Rails TextHelper module but could not find anything. Apologies if this is a duplicate but it is admittedly a hard answer to search for...

None that I know of but it seems simple enough to write a helper for this right? Off the top of my head

def indefinite_articlerize(params_word)
    %w(a e i o u).include?(params_word[0].downcase) ? "an #{params_word}" : "a #{params_word}"
end

hope that helps

edit 1: Also found this thread with a patch that might help you bulletproof this more https://rails.lighthouseapp.com/projects/8994/tickets/2566-add-aan-inflector-indefinitize

Ruby on Rails: what performance can I realistically aim for?

6 votes

I've been building an application in Ruby on Rails 3, and I'm starting to worry about performance optimization. Now I hope that my question is not too subjective for this site, but I'm interested in facts, not a discussion, so here goes:

While I'm trying to get my views to render faster, there is one thing I simply do not know: What should I aim for? Given a reasonably complex page, what load time is realistic? I simply don't have any reference.

What I'm typically seeing for my application is something like this:

Completed 200 OK in 397ms (Views: 341.1ms | ActiveRecord: 17.7ms)

  • This is on my production server, running Apache/Passenger.

  • I am the only one (!) making requests on that server, it's a root server (not virtual), running Ubuntu, AMD Athlon 64 X2 5600+, 4 GB RAM

  • That is, for most of my more complicated actions (not unusually complicated, just assume it's a paginated listing of 20 objects with 5 computed properties each or something) the ActiveRecord times are almost always fine (<20-30ms), but the "views" number is usually >200 ms.

  • Now, to my question: When I started using RoR my expectation (maybe unrealistic) was that for most consumer-oriented applications with average complexity (let's say something like Facebook, Twitter, etc. WITHOUT the millions of users) I would get < 20 ms load times as long as I was the only one making requests, and that for a single server load times would only approach 100ms or more if there were lots of people making requests at the same time.

  • My expectation was also that database requests would be the major bottleneck, since all the rest is just relatively simple computations without any real complexity. I thought that it might take 10ms to get all the objects from the database, and then maybe another 5 ms to run the controller code, build the view, etc.

Since I've never been in charge of any production app, I don't know if this expectation was in any way realistic. So I would like somebody with experience point out to me what my realistic expectation should be.

  • (e.g. "pretty much everything but really nasty stuff should render in 50 ms tops as long as you are the only one making requests")
  • or ("actually 300 ms is not unusual for RoR applications, even if you're the only user")
  • or ("Are you kidding? I get < 10 ms with 150 concurrent requests on a smaller server than yours. There must be something very wrong with your app)

Again, I hope this is not too subjective, but I'm not really interested in an opinion of whether or not RoR is fast, I want facts from someone with more experience on what numbers are average and to be expected from production RoR applications. Otherwise I simply have no clue at what point I should stop optimizing and just accept that I'll never get 10 ms load times.

I'm getting view times < 20ms on a $20/month linode server. That's well-optimized code, for a request of medium complexity, running on JRuby. You haven't hit Rails' performance limits by any means. Time to use a profiler and see what's taking so long.

Sort values using a specific collation in Ruby/Rails

6 votes

Is it possible to sort an array of values using a specific collation in Ruby? I have a need to sort according to the da_DK collation.

Given the array %w(Aarhus Aalborg Assens) I would like to have ['Assens', 'Aalborg', 'Aarhus'] back which is the correct order in Danish.

The standard sort method

%w(Aarhus Aalborg Assens).sort

returns something that looks like the ascii order (at least not the Danish order):

["Aalborg", "Aarhus", "Assens"]

The environment is both Snow Leopard and linux running ruby 1.9.2 and Rails 3.0.5.

I found the ffi-locale on Github and that solves my problem as far as I can see.

It allows the following code:

FFILocale::setlocale FFILocale::LC_COLLATE, 'da_DK.UTF-8'
%w(Aarhus Aalborg Assens).sort { |a,b| FFILocale::strcoll(a, b) }

Which returns the correct result:

=> ["Assens", "Aalborg", "Aarhus"]

I haven't investigated performance yet but it calls out to native code so it ought to be faster that Ruby character replacement code...

Update
It is not perfect :( It does not work properly on Snow Leopard - it seems that the strcoll function is broken on OS X and have been for some time. It is annoying to me but the main platform for deployment is linux - where it works - so it is my currently preferred solution.