fallenrogue.com

Script vs Rake...

This is no how-to article. This is a process article. I’ve come to the conclusion that if you’re doing something in Rails that doesn’t feel good, then you’re more than likely doing it wrong. I’ve seen several articles online recently (from my rss reader) that point to “how to load your models into a random script to run a task for your Rails app”. First off, that’s a title that makes me scratch my head. Why? Because there is no book out there that helps us developers know the best way to do things, right? Wrong. There are Rails apps aplenty that enforce good standards and the best one for dealing with this particular problem is…Capistrano.


Let’s take a real world issue and then examine the best practice solution. I use the term best practice because that’s what I mean to say. Can you load half of Rails into a Ruby script and hack away and copy and paste away for every other script you want to run? Sure. But you’re wasting a crap-ton of energy on something that’s ALREADY PROVIDED FOR YOU. That is Rake. Rake, in the context of your Rails app, has everything you need to write a quick and DRY process that accepts arguments and can run all of your lovely tasks whenever you like. Can you write scripts outside of the context of your app? Sure, but then you may find yourself in a pinch recreating things that you’ve already got at hand or worse loading Rails into Ruby over and over and over again something that rake doesn’t do.


Let’s take an example from a blog that I saw a few weeks ago, here In this example the user is faced with a delima. They want reports generated to give information about how many users are in the system with the last name that begins with the letter A. The user then creates a ruby script that does the following…
ENV[’RAILS_ENV’] = ARGV.first || ENV[’RAILS_ENV’] || ‘development’
require File.dirname(__FILE__) + ‘/../../config/boot’
require “#{RAILS_ROOT}/config/environment”
class User < ActiveRecord::Base
end
user_count = User.count(:conditions =&amp;amp;amp;amp;gt; [”first_name like ?”, “A%”])
puts “There are #{user_count} users in the system beginning with ‘A’”

Yes, there are many things to comment on but first and foremost… this will “work”. It just doesn’t work well. It’s not that the code is bad, it’s just that not following the conventions of Rails can get you into a bind just like this. The convention is Rake. So, let’s rewrite this task in Rake and better yet let’s clean it up a bit too!


First, in our app we’ll add a rake file in lib/tasks called user_count.rake and we’ll add in the code from above written in a much DRYer way…
desc 'a rake task to tell us users by first_name criteria'
task :query_users => :environment do
  if ENV["CRITERIA"].nil?
    puts 'Please provide CRITERIA'
  else
    user_count = User.count(:conditions => ['name like ?', "#{ENV['CRITERIA']}%"])
    puts "There are #{user_count} users in the system beginning with: #{ENV['CRITERIA']}" 
  end
end


Because rake tasks can run in the context of your rails app you can easily take advantage of your existing models without randomly forming new ones. I hope this example from the real world will help guide people as we find the best practices of working with Rails day in and day out. As always, please feel free to comment, correct, question as you like!

articleStats

Here are some silly little facts about this Script vs Rake......

It was written by about 1 year ago.
It has 4119 letters in it.
It has 592 words in it.
It has a total of 6 comments in all.
So far Rich has the last word!

article Links

These are the links that appear in this article. They probably don't make sense out of context... but just in case. :)

rss
here
 

The other stuff...

What the kids are saying...

about 1 year ago Rich said...

Is there another way to pass arguments to the rake task rather than an environment variable? It seems like that's stretching what environment variables are for - setting a global, environment specific value, rather than feeding it into a script.

Granted, that's just me being picky about it. The rake certainly seems like a better approach.

about 1 year ago fallenrogue said...

that's a very interesting and complex question. The answer is, AFAIK, and I've not tested this extensively, that this example is somewhat limited. If you want to run these rake commands from the command line as needed, you'd have to use environmental variables. That's my guess, again, untested (perhaps a reader knows for sure.) That said, there is nothing preventing you from creating a ruby class, or a YAML file that contains variable information in it (often referred to as a recipe: see capistrano) that can be edited prior to running your rake tasks.

Rake gives you all the joys of Ruby and when running from your Rails app it gives you all the joys of Rails as well. If you're working with something large a recipe is probably best but small command stuff, like in this example the environmental variables are, and I reserve the right to prove myself wrong here, a simple way to get what you need.

about 1 year ago andrew said...

Rake tasks "generally" don't take ARGS, at least not without having a default for those ARGS. There is a scripts folder and those scripts accept all kinds of arguments ( think script/generate ). It is very easy to write a script and use all of your app's models. Use this ``#!/usr/bin/env script/runner'' as your shebang, and we aren't breaking from rails conventions.

about 1 year ago Vijay Rao said...

If I have a bunch of files that need to be parsed where do I store them and how do I read them from a rake task?

desc 'a rake task to load vta schedule data'
task :load_vta_data => :environment do
Dir.entries("C:\InstantRails\rails_apps\usepublictransit\lib\tasks\schedules").each do |dirfile|
puts dirfile
end
end

For eg the above code does not work. I tried relative path too?
Do you have any suggestions?

Thanks
Vijay

about 1 year ago Leon said...

Vijay! I didn't see your comment, man. I'm sorry. Interesting. the rake should execute from the app root. So, if you're trying to move up, then go relative from root to the dir you're looking for...

Dir.chdir("where/ever/you/want/to/go")
Dir.entries(Dir.getwd).each{|file| puts file.to_s}

that should do it. works for me. Again, let me know if I can be any further help and sorry on that delay. :)

about 1 year ago Rich said...

You know what's funny? I'm working on a rails app and was thinking "How would I pass command line arguments to a rails app?".

Completely forgot about this thread...

Leave a comment
*name:
*email: (never sold or published.)
url :

©2000-2009 fallenrogue.com | Some Rights reserved.