RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

Since we were just talking about optimizing Rails code I thought I'd post this:

http://lsrc2008.confreaks.com/02-james-edward-gray-ii-hidden-gems.html

The guys at Railsenvy.com just posted a video about the talks that were done at Lone Star Ruby Conf and Confreaks have their videos of the individual talks online now.

Avatar

Thanks so much for this, Ryan! It came in really handy in one of my projects, cleaned it up right nice.

@Brian (#40):

Your first example wasn't matching what you think it was. The /[^www]/ regex is equivalent to /[^w]/, and matches any string containing a character that isn't a "w". So "www" would fail, but so would "w" or "ww" or "wwwww". Obviously a little contrived, but still not really what was intended. This seems to work, although it looks awfully hacky:

/^(?!www).+|www.+$/

Your second solution is much better.

Avatar

Thanks, good one!

BTW, I also had to use sessions (in plural), got that "uninitialized constant SessionsController" error, no matter what I did in routes...

ruby script/generate authenticated user sessions

Avatar

I figured out the duplicate key issue, it would be nice to be able to tell populator about what should be unique within a database.

Avatar

Hi, I have a question.

I'm planning to use a reverse proxy on production. When I use Firebug to watch the response headers, I see that a header of "Set-Cookie" is set with the same session id on every request.

Now, with every request from the same user, the reverse proxy will always think it is a new session and hence prevent caching and affect the performance.

Can you help me to fix this?
Thank you.

Avatar

As per ur tutorial if i write the content_for in index.html.erb its working fine.other than that if write in any other file example news.html.erb its not taking.Please send ur reply to me

Avatar

I had problems with the www working too. The plugin is awesome, but would be a lot better if this area could be cleaned up.

Initially I had to do this to get it to work:

# routes.rb
map.blog_root '', :controller => 'blogs', :action => 'show', :conditions => { :subdomain => /[^www]/i }
map.root :blogs

which regex matches when the subdomain is not "www"

Later I changed it to this which worked too and seemed simpler:

# routes.rb
map.root :blogs, :conditions => { :subdomain => false }
map.blog_root '', :controller => 'blogs', :action => 'show'

Notice the root has to come before blog_root in that scenario.

-Brian
StartBreakingFree.com

Avatar

I believe the best way to protect yourself is to not escape at all, but instead use the multiple-argument syntax to system:

Rather than system("ls -l"), use:

system("ls", "-l")

I believe this will bypass the shell and therefore any risk of injected characters.

> system("ls", "`ps ax`")
ls: `ps ax`: No such file or directory

Clearly those back-ticks we so worry about were considered arguments, not parsed by the shell.

Warning though, if you pass those to poorly written shell scripts, you are still risking a lot. Be careful, perhaps converting known-integer arguments using params[:id].to_i

Also, should you write an 'escape' program, you might just consider not doing that at all, and instead using a whitelist (not blacklist) to pass only specific character types through, such as A-Z a-z 0-9 and so on. Whitelisting is preferred because if you miss one, the app won't work, but if you blacklist you could leave a hole open.

Avatar

Ruby 1.8.7 introduced Shellwords.escape and String#shellescape.

Avatar

Thanks for the excellent cast, it was really useful. One minor point, it might be useful to change 'validate :recipient_is_not_registered' in Invitation to 'validate_on_create :recipient_is_not_registered'; once a user is inevitably created, it seems that this would invalidate its associated invitation. This is pretty minor considering that you don't typically need to modify invitation objects after a user has accepted, but just in case...

Avatar

@Jeff,

Deamons is pretty easy to use and will prevent the "more than one copy running at a time" problem ( http://daemons.rubyforge.org/ ).

The Rails Way book goes into this a little bit (enough to get started anyways) and you can avoid most of the problems listed above by making updates to the database as tasks complete. If you have 1,000 emails you need to send and after you send each one you update the row to show it was sent you'd know where it stopped, and with only a little more work you could have it pick up there again latter, if you had to.

I suppose this wouldn't work as well if the tasks take a very long time to execute, but I generally find that if that's the case there are probably ways to make it faster. Some code that is inside a loop that should be outside, or a way to break it down into smaller tasks rather than one giant one. Ask for help if you get stuck, sometimes you just can't see the answer yourself because you are too close to the problem and need a fresh persective.

That's my 2 cents anyways.

Avatar

Hi Ryan,
Thanks for another great screencast. In your future ones about other ways to do background tasks, could you spend some time covering how/where to put the code that starts the process so that each time you redeploy your app it will automatically start up the background processes too? (and not wind up with a bunch of extra ones running either)

I've been using "spawn" for background stuff, but it requires a fair amount of manual starting and killing the way I'm doing it now. There must be a (much) better way!

thanks,
jp

Avatar

You probably want to add 2>&1 into that command somewhere so that you capture STDERR into your log file as well as STDOUT.

I've noticed that you tend to use Rake for all command line tasks in place of just a straight call to a Ruby script. Is there any reason for this other than the ability to easily load the Rails environment ? Is there any overhead to running Rake as opposed to a straight Ruby script ?

Avatar

@Ryan: Sure - it works, when it works. But if a task is killed for whatever reason (say, power outage), you've lost it along with all the data it carried. That is bad enough. And unless you check your logs religously, you won't even notice. Hell, it might not even be reflected in the log, that the task wasn't completed successfuly. Apart from that, there's the memory consumption and CPU time wasted. Seems ugly to me. I wouldn't dare rely on something like this in a serious production environment..

Avatar

@Amir,

How about sending an e-mail to the user (with an embedded link) when it's done?

Avatar

It would be nice to have the ability to supply a max length to Populator.words, Populator.sentences etc. as otherwise I think you'd hit the limits on some columns.

I thought I was hitting that problem but it appears that I'm hitting a problem with duplicate keys although it doesn't make any sense as the queries that it's complaining about aren't duplicating keys from what I can see.

Avatar

@Reuben, this can be accomplished by regularly updating the database record associated to the task. In this case it could be the given Mailing record. True this could be extracted out into a generic jobs table, but at that point I think you're better off going with another solution (covering in future episodes).

@Neil, it depends on how often the indexing will be done. This approach is not optimal for frequent tasks, so if you're calling it on each record update then I'd say no.

@Amir, yep, that's definitely possible. The hardest part is notifying the user after it's done. One way to do that is just have the browser reload periodically (or ask them to come back later). You can update a database record at the end of the task so your app knows when it's done building and can display the link.

@AC, I plan to cover more "proper" ways on doing this kind of thing in the future. But as far as reliability is concerned, I haven't had any problems with this approach. Admittedly it is a bit of a hack, but it works okay (in my testing) and is much simpler than most alternatives.

Avatar

I really don't mean to be an asshole, but how hard is it to set up backgroundrb and do this properly ?

Avatar

Unrealiable, suboptimal, fast and easy. Just the way the rails community likes it ;]

Avatar

Hey, this is a good idea. I just cloned Faker and spent a little time in the wee hours of the morning adding a couple features. I'll likely be adding a bit more too. I haven't created a patch yet for the SVN version, but I'll get around to it quickly.

Also I need to set up a gemspec so that it can be used with GitHub's gem thingy, but until then:

http://github.com/darrylring/faker/tree/master

Avatar

@grosser: yes you'd think that "the comment should know if it can be edited", but remember that in this case we're talking about *anonymous* comments, so their "ownership" is really tied to the session.

Avatar

I've got an excel file as a view that takes ages to render. Can I generate it in the background and direct it to disk so I can have a link to it appear later?

Avatar

Would 'rake thinking_sphinx:index' or delta indices make for good uses of the approach outlined in this screencast? Or, are we better off with backgroundrb and the convenience of its :trigger_args intervals?

Avatar

@Reuben

Taking the Mailing as an example, you could use an attributed called status where the background task updates it and your web application displays the current value (you could do it with a periodical refresh).

Avatar

This is great, but what about firing off processes whose status the user is interested in later? For example, whether it finished successfully, unsuccessfully, what percentage complete it is, etc.?

Perhaps you'll cover this in the upcoming background processing episodes, but it would be nice to have some sort of rails-model driven status mechanism that could be tied into the rake technique shown here.

Avatar

@tony and Jean-Marc, thanks for pointing out how to accomplish this in Windows. I don't have access to one at home so I can't fully test these. I'll add this to the show notes.

@Henrik, thanks for clarifying this. Along that line, does anyone know of a good way to escape a string to be entered into a shell command?

Avatar

Since it wasn't made fully explicit: the code in the screencast doesn't escape the env values (or keys), only upcases the key and puts some quotes around the value.

If regular users can pass in the parameters used, they can exploit it to run arbitrary code.

It was hinted at, but could be misunderstood to mean that the current code does proper escaping.

Avatar

@Jean-Marc (and others)

Thanks for the Windows tip.

It does work, except that the command shell window remains open after execution of the task, making subsequent executions be silently ignored (the window needs to be manually closed before clicking 'Deliver' again).

Avatar

@Cassiano

if you want to "spawn" a process under windows you can do so by typing "start rake ..." then again you're better off having the whole path to the rake command.

Start will trigger a new command shell.

Jean-Marc

Avatar

for those asking about background processes in windows.

the best way to accomplish this from the command line is to use the "start" command.

http://www.ss64.com/nt/start.html

has a pretty good reference.

Avatar

Interesting alternative to backgroundrb or starling, seems easier to set up.

I use rake tasks, launched with a cron job, for my background processing, but this is suitable only for periodical tasks. Your solution is fine for user-launched tasks.

Thanks!

Avatar

I was just about to mention, this won't work in Windows. The ampersand forking is a *nix only thing.

I don't believe there is a simple way to do this in windows, except to use the Ruby Process module with #fork. Not as easy, but similar functionality, just using the Ruby library.

http://www.ruby-doc.org/core-1.8.7/classes/Process.html#M000968

Avatar

Ryan,

Very nice article!

I believe the forking (&) won't work under Windows. Any ideas on how to accomplish it in this case?

Avatar

Ryan,

There is a missing '=' before 'render' in:

http://github.com/ryanb/railscasts-episodes/tree/master/episode-121/store/app/views/products/index.html.erb

so instead of:

<% render :partial => @products %>

it should read:

<%= render :partial => @products %>

I have also noticed that the 'log' folder is not present, so it needs to be created manually (not a big deal, but providing it would avoid an unnecessary error when trying to boot up the development server).

Avatar

Finally the series I've been waiting for so long! Thanks a lot for your brilliant screencasts! They're one of the best Rails resources I know and extraordinarily well prepared.

And, by the way, nice new intro!

Avatar

Finally! We can do encapsulation in the Model... wow ActiveRecord becomes slightly more Object-oriented! <gasp>

Avatar

Thanks, Ryan. For me, all your screencasts have been perfectly timed. And your casts always use the 'best practices' for smaller things (which others ignore sometimes) like the proxy.pac for the subdomain cast; using liquid (for security) for the templates; the active resource screencasts. I love each and every one of them and find them to be the best way to show my team how to do things the right way.

Avatar

Of course maybe you didn't mention it because it's too obvious--just put them in /public. I'd love to hear any downsides anyone can see (especially Ryan) to simply keeping static pages in /public, besides the obvious one of them being more of a pain to edit.

Avatar

Like several others in these comments, I had major problems with the routing working correctly like it does for Ryan in this screencast. FYI, I'm using webrick, and I'm on windows xp.

So I took Ryan's advice and installed his app.

What I discovered is that Ryan's app's routing worked at first, then stopped working once I added "www." in the main domain in my local machine's hosts file. The routing works fine for the blog app when you just have <code>127.0.0.1 personal.blog.local company.blog.local blog.local</code>. What's interesting is that I'm using webrick and not apache like Ryan is using in the screencast. You'll notice he didn't have to specify the main domain at all. If I leave out the main domain, I can't access it at all, so I have to specify it on my setup.

Taking out the "www." didn't fix my local app though, which drove me nuts for a long time. Then I tried installing the plugin instead of the gem I was using, and sure enough the routing started working properly. I don't know what the difference is, but I'm glad for routing that works now.

Summary: use the plugin, don't specify "www." for the main domain in your hosts file.

Avatar

please let me know how can i use will_paginate with alphabets. i.e i have a dictionary with words from A to Z. so i would like to paginate A, B, C, .., Z as pages and clicking A for instance will show all words start with alphabet A. kindly let me know. many thanks in advance.

Avatar

@Mark, Carl is correct, the "account" passed to the block is not a true instance of account. You can only set columns direction, not through virtual attributes.

I'm thinking of changing how this is done so it allows you to use virtual attributes as well. But it doesn't work yet.

Avatar

@Mark,

I wonder if that's because populator is working directly with the database for speed and bypassing the virtual attribute? Does it give you an error or just not set anything for the password, or just not create any users?

Avatar

How can I use this to load up a bunch of dummy users using restful_authentication?

I can't seem to set user.password = 'foo' here:

    Account.populate 200 do |account|
      account.name = Faker::Company.name
      account.email = Faker::Internet.email
      account.created_at = 2.years.ago..Time.now
      User.populate 10 do |user|
        user.login = Faker::Internet.user_name
        user.password = 'foo'
        user.first_name = Faker::Name.name
        user.last_name = Faker::Name.name
        user.email = Faker::Internet.email
        user.account_id = account.id
      end
    end

Avatar

You can give railsolver a try for wildcarding hosts.

This plugin will "hijack" Ruby host(s) resolver aka(resolv-replace.rb), allowing programmatic host file resolution (including wildcard host resolution). ™

http://github.com/retr0h/railsolver/tree/master

Avatar

Rick-

See my post here for info on how I solved the duplicate validation message problem:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/df8f450149b71b1/3f6aea404f962b30?lnk=gst&q=hagius#3f6aea404f962b30

Avatar

Pref pane works awesome, issue i am having is that when i run the site through passsenger, my css seems to be missing.
Also, even though the selection is set to DEVELOPMENT, it seems to look for PRODUCTION DBs(on another app)

Any Ideas?

Avatar

@chrisff (post 51) and @Kevin Triplett (post 114)

I was running into the following problem.

I had a page and the association was files. You could upload numerous files by clicking on the "Add file" link.

If I only had one file the validation code worked fine. If I had multiple it was running through the association and adding the errors to the base for the page model for every instance of the field.

For example. If I had a new page with 1 file (file required) and I didn't upload the file I would get 1 message saying:

File: no file uploaded

If I had two instances of the file added and I didn't upload the file I was getting 4 errors:

File: no file uploaded.

This is because 2 fields with the name :file were getting passed in so it was running the association validations twice so 2 errors each time yielded 4 errors for file.

I added the code to store and array of each field the the validation was run on and push the name of the field into the array after each. They use a statement to check if the name of the field is already in the array and not run the association validations if this is the case. This eliminated the extra validations on the association field. See the pastie:

http://pastie.org/271260

If anyone has a better way please respond.

chrisff your code was the basis and Kevin your error patch to remove the "'Association' is invalid" was a huge help.

I have used both of your pasties and modified them slightly for the error validation.

~ Tom

Avatar

When is the next version coming..

Avatar

Anyone else experiencing problems when using this in combination with will_paginate? When I visit the site with subdomain.domain.local, all the url_for and link_to's are correctly pointing to subdomain.domain.local/path, except for the will_paginate links. will_paginate generates links to domain.local/path.