Archive for the ‘Code’ Category

Better Way To Do Dynamic Methods

Tuesday, April 13th, 2010

Not sure how best to describe this, so I figure a few lines of code can say a hundred words at least:

# app/models/user.rb
  # This lets me call @user.home_page from within views or controllers to create a URL
  def home_page
    if self.referee?
      return "/bids" # sends user to site.com/bids
    end
    if self.assignor?
      return "/assignors" # sends user to site.com/assignors
    end
    # [... etc ...]
  end
 
  # dynamically determine the role name as a method
  # can this be done to to_sym or something better,
  # or a missing_method call to make it dynamic?
  def referee?
    if self.role.name.downcase == "referee"
      return true
    end
    false
  end
 
  def assignor?
    if self.role.name.downcase == "assignor"
      return true
    end
    false
  end

What I really want to do is call “bids_path” or “assignors_path”, but those don’t seem to be available from within the model file. I’m sure there must be a better way to do this, from two different aspects:

  • The first part of the code, returning the actual path, or at least the dynamic resource path instead of having it hard coded. This is possible in the view, but I’d have to fill it up with checks for the user type and then calling either bids_path for referees or assignors_path for assignors
  • The second part, where I create the two methods to make the above determination. I could just trust that the @user.role.name will return The Right Thing, but I’m not sure how to auto-magically create the methods to do this for anything that the user.role.name contains.

Querying Multiple Tables With ActiveRecord

Saturday, April 10th, 2010

The other day at work I was doing some data mining for our business intelligence folks. They needed to get some stats from the last year of billing transactions, and the task came to me. I had to hack up a perl script to query the archived data to find a certain type of transaction. Easy enough, the only minor issue was that since the data set was both huge and old, the table were broken up by month.

My perl script looked something like this (cleaned up for brevity):

@months = qw( 200901 200902 200903 ); # up to 201004
$sql = "select * from BillingMONTH where resultCode=40";
foreach my $month( @months ) {
$sql_full = $sql;
$sql_full =~ s/MONTH/$month/; # change the table name for each record
$sth = $dbh->prepare($sql_full) or die;
$sth->execute();
[ ... ] # retrieve the data, save/process it
}

I started wondering if these sorts of quick and dirty, but non-trivial scripts were easily doable in Ruby using Active Record.

I set up a similar situation on my system, a mysql database with 3 tables and a couple of rows of garbage data in them.  I knew that I could get a database connection set up very fast by making a throwaway rails app, setting up the models in it, and then using script/console as my interface, but I’ve honestly never use activerecord in a scripting environment.

After some messing around I came up with this:

#!/usr/bin/ruby
require 'rubygems'
require 'active_record'
 
ActiveRecord::Base.establish_connection(
   :adapter => "mysql",
   :username => "root",
   :password => "password",
   :database => "test"
)
 
class Test < ActiveRecord::Base 
end 
 
%w( Archive201001 Archive201002 Archive201003 ).each do |tablename|
   res = Test.find( :all, :from => tablename, :conditions => "name like 'test%'" );
   puts "Total results from #{tablename}: #{res.size.to_s}"
   # [...] other awesome processing on the res object
end

Running it gives me:

alan@phoenix:~/code/ruby$ ./testdb.rb
Total results from Archive201001: 3
Total results from Archive201002: 6
Total results from Archive201003: 3

Whohoo!  The one thing I couldn’t figure out is how to get rid of having to create a sub-class of the ActiveRecord::Base class.  I technically can do it with:

ActiveRecord::Base.connection.execute("select * from #{tablename}")

But I don’t know how to get this into the “object oriented” version to use the :conditions and :o rder and such.  However, the main point of can I replace perl and DBI for quick and dirty database scripting has most definitively been answered.

Next step: seeing how to do the same thing in Data Mapper (to keep @dkubb happy) :)

Referee Site Code Online

Friday, April 9th, 2010

Got approval to put the oft-mentioned referee site code online, so you can find it here in my github account. I don’t see it being all that useful to anyone but me, but if you feel like commenting on the code, contributing any changes or suggestions, code or design, please do!

At Home With Nested Routes and Resources

Tuesday, April 6th, 2010

When I first heard about nested routes, as with most things in Rails, I completely ignored them, discounting them as something I’d get to learning when I needed to, but right now they were yet another one of those things that I didn’t quite grok as I didn’t have the other base knowledge of the Rails system to know why I needed them. To be fair, I’m not that much closer now, and started to realize the the other day I didn’t really quite understand REST and having a RESTful site.

But when I started looking at how my site was structured, and how things were really encapsulated in each other, such as clubs “having” fields, and fields “having” games, I started to understand the need. Actually it was more doing the scaffolding of the site and seeing that I had to set field in a dropdown each time I created a game, or the club each time I created a field. It didn’t make sense and it seemed like a stupid way to do it.

Back in the old days in perl, I’d pass the ID of the main object when I created a sub-object. So if I had a club editing page with a ‘create new field’ button on it, that button would pass a hidden “club_id=$id” when it was pressed.

Rails comes with a nicer way of doing it. It allows you to nest related resources. Probably the best place to start is with Ryan Bates excellent Nested Resources screencast, or Adam’s Nested Resources in Rails 2 page.

For this I set up the following in my routes.rb as such:

map.resources :clubs, :has_many => :areas, :shallow => true

And then running “rake routes” will show you what the routes will actually be:

     club_areas GET    /clubs/:club_id/areas(.:format)            {:action=>"index", :controller=>"areas"}
                POST   /clubs/:club_id/areas(.:format)            {:action=>"create", :controller=>"areas"}
  new_club_area GET    /clubs/:club_id/areas/new(.:format)        {:action=>"new", :controller=>"areas"}
          clubs GET    /clubs(.:format)                           {:action=>"index", :controller=>"clubs"}
                POST   /clubs(.:format)                           {:action=>"create", :controller=>"clubs"}
       new_club GET    /clubs/new(.:format)                       {:action=>"new", :controller=>"clubs"}
      edit_club GET    /clubs/:id/edit(.:format)                  {:action=>"edit", :controller=>"clubs"}
           club GET    /clubs/:id(.:format)                       {:action=>"show", :controller=>"clubs"}
                PUT    /clubs/:id(.:format)                       {:action=>"update", :controller=>"clubs"}
                DELETE /clubs/:id(.:format)                       {:action=>"destroy", :controller=>"clubs"}

The biggest, biggest thing to notice here is the “:id” and “:club_id” in some of the routes. This tripped me up a few times.

Nested routes let you automagically pass “club_id” (in this case) to paths. So in your controller or views you can now use the variable from the first column. So you can do a

redirect_to clubs_path
# instead of:
redirect_to :controller => "clubs", :action => "index"

for example, and it would go to the /clubs/index path and do the right thing. It also lets you do things like:

redirect_to new_club_area(@club)
# sends you to /clubs/1/areas/new

Note that you have to pass the club before this will work. This took me forever to figure out, cause sometimes it worked and sometimes it didn’t. What I found was looking for either :id or :[object]_id in the routes path would tell you both if you have to pass the route the object or not, but also if you’re processing the path in the controller, that :club_id will be passed auto-magically to it. So for the “/clubs/:club_id/areas/new” route above, you’d process it in your areas_controller, and in the ‘new’ def, you’d automatically get club_id for free. IE:

# in app/controllers/area_controller.rb, possibly via new_club_area_path(@club)
def new
   @club = Club.find(params[:club_id])
   @area = @club.areas.new
end

You know that club_id is being passed because the rake_route for that path has “club_id” in it: “/clubs/:club_id/areas/new”.

Next up, finishing converting as much of the rest of my code from the controller/action syntax for redirecting and rendering to a more “restful” setup.

Update: There is a cool sounding TextMate bundle to help you with some of the brain games needed to understand nested resources.  Hat tip to The Ruby Show.

Rails Authentication In a Gist

Monday, March 15th, 2010

I’ve been trying to get some more use onto my github account, and to this end I’ve copied my work on the rails authentication (a rip from Adian’s work, he gets full credit) over to this gist.  Basically this is just exactly what Aidan did originally, but in a more cut-and-paste friendly format.  I’ve also included a couple of minor fixes to it (ie: preventing the user password from being nuked on save and some other minor changes that were found in the comments on the original blogpost).  I also haven’t included any of the tests (bad Alan, I know), but my thought is that you’re going to have your own test system, be it Rspec, Cucumber, or whatever) and putting in some tests there might be counter-productive if you’re already rocking with [insert your testing framework here].

The idea really is just that the next time I need to put in some authentication I don’t have to search as far, or go through as long a page, but can just grab the gist, copy and paste the needed chunks, and go.