Put your message here! Contact me for more information
 
 







 

Archive for the ‘Ruby on Rails’ Category


 

I was hacking the acts_as_taggable_on_steroids plugin to support different categories for tags and taggings and I bumped right into the issue of how to have dynamic conditions for the has_many association. The problem with associations in general is that they are CLASS METHODs, e.g. they are run when the class declaration is being loaded into memory at runtime. However, our condition has to be evaluated at evaluation time. This means if we write (psuedo-ruby code lifted from my hack on the the acts_as_taggable_on_steroids Tag model)
class Tag
has_many :taggings, :conditions => “taggings.category_id= #{self.category_id}”
end

this will very well error out on us.  Since, at class loading time, Ruby has no idea about the particular category_id attribute of the “self” instance of the Tag object. On his blog, Dweedb proposed a solution of creating a new souped-up has_many association, e.g. has_many_with_args. I wasn’t convinced about this solution since it means I may very well end up with writing/extending every association within Rails with something else if I need to support some special dynamic conditions.

I was playing with lambda function (e.g. :conditions => Proc.new { “some_clever_dynamic_conditions_here”} ) but for some reasons I kept getting the “calling private gsub method” error for the Proc when ActiveRecord was interpolating (constructing?) the SQL.  Nonetheless, I got an important hint from the Dweedb’s post about the use of single quote on the :conditions to keep the it as string literal and delay the evaluation of the conditions till, well, evaluation time. “Excelente!”, as my friend Javier would shout in Espanol!

The solution is made possible by the use of the native Ruby #send(:action, *args) method to read the attribute of the model instance. Since if we wrap the conditions around the single quotes, when the model class is being loaded to the Ruby VM, everything still remains as-is. Only at runtime, when ActiveRecord is busy substituting the conditions string into the generated SQL statement, it inadvertently executes the code inside, which then invokes the send() method on the current instance to return the value of the attribute (You know what just popped into my mind? HOLY SMOKE! IT IS CODE-INJECTION through SQL-INJECTION!)

The updated code for the new Tag model is

has_many :taggings, :conditions => ‘#{Tagging.table_name}.category_id = #{self.send(:category_id)}’

Anyway, I was able to get all the tags and their associated taggins from the same category, dynamically and hacklessly. With this approach, any conditions for the various associations can be dynamicalized using single quotes and the (god)send method. Hurray!

view comments
 

RailsI’ve just released ActiveFixture, an enhancement to the Rails db:fixtures:load rake task. Currently db:fixtures:load doesn’t take into account the foreign key constraints hence with any tables that have foreign keys defined, db:fixtures:load would just fail miserably. As Rails is increasingly getting more into the enterprise world, the ability to handle foreign key correctly becomes more important.

I flew to New York last weekend for the VSLive! Conference. While attending VSLive! talks during the day, my mind was mostly occupied with how to solve this foreign key issue at night. After 2 days and trying multiple ways and still unable to solve the problem, I suddenly remembered about graph (good old CS classes). I turned the problem of determining the order of loading fixtures files into a graph problem of how to traverse the graph in a certain order. Breadth-first-search doesn’t give the correct answer, Depth-first-search seems to have some potentials. When I tried Depth-first-search Post order traversal, suddenly I realized that DFS post order gives the perfect solution. What’s even more beautiful is the entire traversal algorithm was only a couple lines of Ruby code as you can see from the source.

I’ve learned quite a bit about Rails and Ruby while writing ActiveFixture. I poked around the core ActiveRecord classes and see how fixture is being handled, I then learned how to use reflection to get meta data from the ActiveRecord models, then how to write rake task and componentize everything into a plugin. In the end, I feel I like working with Ruby and Rails even more. .NET is cool but somehow, to me, Ruby is more fun to work with, except for the fact that there’s no GUI debugger. I would say I would have been able to write ActiveFixture in much less amount of time had I had a Rails/Ruby IDE with an interactive debugger.

The next time I write something for Rails, I would probably give Ruby in Steel a shot. It’s quite frustrated when you are exploring Ruby, especially when someone like me isn’t that familiar with Ruby. I love the Watch and Intermediate Window in Visual Studio simply because I can hit a breakpoint and examine the application and all the variables at that moment. Currently we don’t have anything similar for Ruby and Rails for free. Ruby in Steel seems to have potentials, but $199 is quite a barrier to entry to any ruby-rails-fan. In fact, one of the main question that my boss at work asked me about Ruby on Rails, is if Ruby has any good IDE. My company is a Microsoft shop so we use Visual Studio extensively. It’s hard to convince someone who code in Visual Studio for a living to switch to a notepad-like editor (”no Intellisense, no thanks”) and start learning Ruby on Rails. Personally I use (and pay for) e-editor, however, I do miss the intellisense and the ability to hit F5 anytime to kill that freaking bug…Okay, enough of my ranting.

Anyway, here’s the good news in short: ActiveFixture is released and ready for abused. Current version is under-tested (I haven’t written any unit tests yet) and may contain bugs. For Wars of Earth, I have about 20 tables with a whole bunch of foreign key constraints (with circular references too) so I’m quite confident that ActiveFixture will work for you.

Download:

  • SVN: http://activefixture.rubyforge.org/svn/

Install:

  • Copy into the RAILS_ROOT/vendors/plugins
  • As always, RTFM. See README for more information. I wrote a little explanation on how ActiveFixture works so enjoy :) It’s a nice and elegant algorithm to solve a tricky problem.

Usage:

  • Make sure you have all the required yml/yaml/csv files
  • Defined all belongs_to relationships inside your models
  • Run
rake db:fixtures:activeload

And Support:
Please vote for ActiveFixture to get included in Rails core. I’ll have even more incentives to write even more useful Rails plugins.

keywords spam: db:fixtures:load, db:fixtures:activeload, rails foreign key constraints fixtures, fixtures loading order

view comments
 


{{http://alexle.net/wp-content/uploads/2007/06/rails.thumbnail.png }}Working on Wars of Earth by myself, I run into several situations when I’d like to move migration files around to get them executed in a particular order. The trick is simple: just rename the sequence of the migration files to get them to be executed in the order you want.

Besides sloppy schema design (hey, I’m trying to be agile here - fix it as you go), an example of a situation when you want to move migration files around: table A has a foreign key to table B. Migration file for A is at 005, while that of B is at 010. If you try to put in the FORIEGN KEY constraint in A migration file after the table creation, migration will fail since table B hasn’t been created yet, not until migration #010. You can add another migration file just to add the FORIEGN KEY constraint from A to B, say at #020. But then you will have fragmented migration code all over the place. For production environment, this is the only solid and proven way to perform database changes. However, during development, you have the luxury to drop and recreate the entire database from scratch, it’s just a lot more convenient and makes more sense to be able to move migration files around. Ideally, you just need to run migration for B first, then you can run migration for A, which also execute the SQL to add in the FOREIGN KEY constraint properly.

Until now, you have to it manually. Imagine if you have 20 migration files in between, from 5 to 25 (like what I have), you will probably have to renumber 20 migration files. And if your project is in SVN, it would be more time-consuming and error-prone. You can either do “svn rename”, or just rename in the shell, then deleted the previous migration files and added the newly-named ones back into the repository. But what would happen if you yawn for a second and misnumber a file at #6, so you have two 006 migration files. Oops.

Worry no more, here comes **numergrate** utility to the rescue. In short, numergrate is “to numerate migration files” (or to renumber migration files)

What you need is to drop in the numergrate script (download below) inside your Rails’ script folder and you are ready to go. With this utility, you can execute at the root of the Rails project

ruby script/numergrate 5 before 25
to (test) move 005 to position 024 instead.

or

ruby script/numergrate 10 after 2 --svn
to move 010 before 002 and also rename the files in the Subversion repository.

I also took the opportunity to integrate the utility with Subversion in case your migration files are in a repository. There’s no easy way for CVS so I didn’t implement it. However, implementing renaming mechanism for other SCM should be straight forward if you know their command line renaming tool.

I hope this comes handy for you.

==== Supported tools ====

* {{http://alexle.net/wp-content/uploads/2007/06/subversion_logo.thumbnail.jpg}} Subversion (renaming files in repository)
* {{http://alexle.net/wp-content/uploads/2007/06/utilities-terminal.png}} Shell (just like renaming manually)

==== Script ====

#!/usr/bin/env ruby
# (c) 2007 Alex Le
# www.alexle.net - nworld3d@yahoo.com
# This script is developed for www.warsofearth.com. (shameless self-promotion)
# Released under the same license as Ruby
# Disclaimer: The author is not responsible for any incorrect results from running
# the script. Use it at your own risk.
#
# USAGE:
# ------
# This script is used to re-number the migration files into the desired sequence
# In development, this comes handy as you can organize migration files into logical groups
# by running a simple command line utility instead of manually renaming the filenames.
#
# numergration = numerate migration files
#
# INSTALL
# -------
# Copy numergration into script/ folder of your Rails application.
# On linux system you may have to chmod the script to be executable (+x)
#
# HOW TO RUN
# ----------
# At your Rails application root, run:
# On Windows:
# > ruby script/numergrate before|after [mode]
# On Linux: users can just run the script without calling the ruby executable since
# there’s a #! on top, provided that you set the permission correctly. (chmod to +x)
# $ script/numergrate before|after [mode]
#
# The [mode] options are
# –test Default mode to test the result before you run
# with –shell or –svn
#
# –shell Renaming file as you would do manually in the shell
#
# –svn Integrate with Subversion by executing `svn rename` on each file
# This option alters your working copy so please be extra careful.
# (a.k.a. use it at your own risk)”
#
# Example:
# ——–
# a. Move Migration file 50 to position 3, hence shifting migration files
# from 3 to 49 to the right by 1.
# > ruby script/numergrate 50 before 3
# (the above will just execute with the –test default option)
#
# Or to actually rename the files,
# > ruby script/numergrate 50 before 3 –shell (or –svn)
#
# Or you can even run
# > ruby script/numergrate 50 after 3
# to put migration file 50 after migration file 3 (shifting migration
# file 4 to 49 to the right by 1)
#
# TODO:
# —-
# 1. Better sequence handling. Currently it’s default to 000 for the
# sequence series. However, there can be potentially a lot migration files.
# (more than 999 files). The solution is to find the max sequence and
# use that as the series template
# 2. Better SVN integration.
# a. Do some checking to see if the svn client exists before running.
# Otherwise throw an error
# 3. Better sequence checking. Currently it doesn’t check for input
# range so we can have “index out of bound” errors.
#

require ‘fileutils’
include FileUtils

# which folder we would skip while iterate through
SKIPPED_FILES = [’.', ‘..’,’.svn’]

# check for arguments
unless ARGV.size == 3 or ARGV.size == 4
puts ‘invalid syntax’
exit
end

# this class hold the information about the migration file
class MigrationFile
attr_accessor :sequence, :name, :new_sequence

def to_s(options={})
if @new_sequence != @sequence
s = sprintf(”%03d”,@new_sequence) << "_#{@name}"
else
s = sprintf("%03d",@sequence) << "_#{@name}"
end
end

def initialize(sequence, name)
@sequence = sequence
@name = name
@new_sequence = @sequence
end

def shift_left()
@new_sequence -= 1
end

def shift_right()
@new_sequence += 1
end

def is_changed?
return @new_sequence != @sequence
end

def old_name
s = sprintf("%03d",@sequence) << "_#{@name}"
end

def new_name
s = sprintf("%03d",@new_sequence) << "_#{@name}"
end
end

# 123 after 234 --test
src, task, dest, mode = [ARGV[0].to_i, ARGV[1], ARGV[2].to_i, ARGV[3]] # got to explicitly convert to number for comparision
# exit if don't have to move
exit if src == dest

# default mode to --test
mode ||= "--test"

#grab the migration files
files = []
Dir.entries("db/migrate").each { |file|
unless SKIPPED_FILES.include?file
files << MigrationFile.new(file.to_i, file.match(/.+?_(.*)/)[1])
end
}

# now perform shifting
files.each{ |file|
if src > dest
if file.sequence == src
if task == “before”
file.new_sequence = dest
elsif task == “after”
file.new_sequence = dest + 1
end
else
# shift the innner range files
if file.sequence >= dest && file.sequence < src
if task == "before"
file.shift_right
else
file.shift_right unless file.sequence == dest # if insert after, we don't need to shift the dest
end
end
end
elsif src < dest
if file.sequence == src
if task == "before"
file.new_sequence = dest - 1
elsif task == "after"
file.new_sequence = dest
end
else
# shift the innner range files
if file.sequence <= dest && file.sequence > src
if task == “before”
file.shift_left unless file.sequence == dest # if insert before, we don’t need to shift the dest
else
file.shift_left
end
end
end
end # if src > dest
}

#files.each{ |f| puts f if f.new_sequence != f.sequence }

# now issue
puts “”
puts ” Execute using #{mode} option”
puts “”
puts ” You can execute with these options: ”
puts “”
puts ” –test Default mode to test the result before you run”
puts ” with –shell or –svn”
puts “”
puts ” –shell Renaming file as you would do manually in the shell”
puts “”
puts ” –svn Integrate with Subversion by executing `svn rename` on each file”
puts ” This option alters your working copy so please be extra careful.”
puts ” (a.k.a. use it at your own risk)”
puts “”

files.each{ |file|
if file.is_changed?
if mode == “–shell”
puts ” rename ” << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
cp("db/migrate/" << file.old_name, "db/migrate/" << file.new_name )
rm("db/migrate/" << file.old_name)
elsif mode.downcase == "--svn"
#puts "executing svn commmand here"
puts " svn rename " << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
system 'svn rename --force db/migrate/' << file.old_name << " db/migrate/" << file.new_name
elsif mode.downcase == "--test"
puts " [TEST] rename " << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
end
end
}

==== Download ====
* To download, please click here: [[http://alexle.net/wp-content/uploads/2007/06/numergrate.zip|numergrate]] (version 1.0, 06/17/2007)

==== Note ====
* Moving migration files around can do serious damage to your database. Please be careful. This comes extremely dangerous if you are working with other people on the same repository. Please be smart about it. Use it at your own rick.
* Comments and suggestions are very welcome.
* Support me at [[http://www.warsofearth.com|Wars of Earth]] if you can. Another shameless self-promotion.

view comments
 


{{http://alexle.net/wp-content/uploads/2007/06/rails.png }}It’s been a week since I started messing with Ruby on Rails seriously. My current Rails project is rebuilding Wars of Earth, the game that my friend Giao and I started almost a year and a half ago. Since Rails is so flexible and powerful, within a span of a few days (like 5 days), I was able to achieve as much as 2 weeks worth of work compared to the past. Before, as Giao is much better at programming than I am, I asked him do the framework design in PHP (we used Pear’s DB_DataObject, Smarty, and home-grown controller for a MVC architecture.) I did the design (game designs, coming up with ideas, some coding) instead as well as preparing the site in Joomla (check it out [[http://www.warsofearth.com|here]]). Nonetheless, I was getting ready to graduate, and my buddy had to take on freelance work to make some money, I decided to put Wars of Earth aside and focus on what mattered most at the time (mainly job and money). As I have more free time on my own now, I am restarting the Woe project but this time, I am rebuilding it from scratch using Rails. And I don’t think I would go back to PHP for any web-based project if I don’t have to.

The major bottle-neck between Giao and I, as I would boldly claim for any other projects that involved geographically distributed team, was the communications. Giao is still back in Vietnam, and I’m in Chicago. I’m 12 hours behind him. Whatever we needed to do, we put in TOTO tasks and message in our Basecamp site. At the time I haven’t thought too thoroughly about the story and the game play, so we just kept on moving until we hit a problem, then we would sit down and talk. But words can only describe so much. Sometimes what I tried to get across, or what my buddy tried to tell me, just failed to get both of us on the same page. Giao would implemented something and it was not the same as I thought, so we just kept on building, discussing, and sadly, compromising. Moreover, I constantly having more ideas for the project, creating our eternal scope-creep: I just tried to add many features right at once (I was trying to get it right the first time, but of course it was almost impossible)

Now with Rails, things are much different. Rails are so flexible and powerful that what I only need to put down is mostly business logic, not low-level, database accessing code. The syntax of Ruby is much superior to that of Perl (I hated Perl with passion, and still do) and Rails is plainly a joy to use. Suddenly I realize the meaning of the [[http://www.loudthinking.com|blog]] of [[http://www.loudthinking.com/about/|David H. Hasson]], Rails’ creator, “**Loud Thinking**.” With Ruby, you can actually __think out loud__ through your code. Since Ruby’s syntax is so flexible, it allows the user to write the code in an unheard-of conversational way. I can almost read the code as I would read a novel (since reading code requires some imagination and the code is no longer boring in Ruby, so I can’t compare the program with a textbook - that would be too boring don’t you think?). I don’t have to restrain myself to the curly braces of C#/Java/PHP style, instead I can tell the Model to fetch all the record then smash the id’s in to an array with some logic in one line. Something like CharacteUnit.get_all_units().collect{|unit| unit.id unless unit.is_inactive} and of course you can add a lot more to that as you can stack as many operators/methods as possible. Pure programming joy.

One interesting part about Rails is the testing tool: unit test and functional test. I can’t wait until I setup the first functional test for the application to simulate a web user. And of course the best part is: everything comes in an elegant, well-thought, scalability-proven web framework that took you 10 minutes to get started. Suddenly the barrier to entry to mid/ large-scale web projects is dropped significantly (it’s __free__ now, only costs you your opportunity cost). In stead of just WYSIWYG, it’s now **What-You-Think-Is-What-You-Can-Get**, **WYTIWYCG** (weet-tee-wic). Imagination goes wild, how awesome.

Whomever I talk to nowadays, I can’t stop talking non-stop about Ruby on Rails. I feel good sharing it to anyone I know so they can start feeling the same way as I do, as sharing is caring.

I feel good. Do you?

view comments
 


Since I’m learning Ruby on Rails, I feel the need to know the basic syntax of the language. So here is my own primer guide to basic Ruby syntax. The guide was wrote largely based on the [[http://docs.huihoo.com/ruby/ruby-man-1.4/syntax.htmll|original manua]] by Ruby’s author, [[http://en.wikipedia.org/wiki/Yukihiro_Matsumoto|Yukihiro Matsumoto]]

===== Ruby =====
Ruby is case-sensitive. Whitespace characters are space, tab, vertical tab, backspace, carriage return, and form feed. Newlines works as whitespace only when expressions obviously continues to the next line.

==== String literals ====


"remeber to escape \" and \\ #{expression_substituted}" # streng enquoted with doublequote is subjected to expression substitution
'remember to escape \' and \\ #{expression_not_substituted}' # streng enquoted with doublequote is not evaluated
%q!Some string with "double quote" and 'single quote'! # equivalent to '' and don't have to escape double quote or single quote
%Q('Some string with "double quote" and \'single quote\' and back parenthesis \) ') # equivalent to "" and don't have to escape double quote or single quote
%!Some string with "double quote" and 'single quote'! # equivalent to "" and don't have to escape double quote or single quote

==== Expression subtitiion in strings ====

"hello my name is #{$name}" # sub #{$name} with $name

$ and @ variables don’t need to be enclosed within #{}. The # only has special meanings when it’s followed by {,@, and $

==== Regular Expressions ====

%r/STRING/
/regexp/

* **Modifiers**

/regexp/i # case insensitive
/regexp/x # extended expr, no whitespaces and comments are allowed
/regexp/p # POSIX mode, newlines are treated as normal charactor

==== Variables ====

$var # global
@var # instance variable of self
VAR # constant
SomeClass::VAR # constant within a class
var or _var # local variable

=== Pseudo Variables ===
* self
* nil
* true
* false
* __FILE__ : current source file
* __LINE__ : current line

=== Array ===
* [1,2,3]
* %w(foo bar baz): create a new string, space-separated array, equivalient to [”foo”, “bar”, “baz”]

=== Hash ===
* {expr1 => exprA, expr2 => exprB }

===== Method invocation =====
* **method(args1, arg2)**
* **method(*array)** is equivalent to **method(array_member1, array_member2, …)**. The * expands the argument array.)

===== Operators =====

+, -, *, /, %, **, &, |, ^, <<, >>, &&, ||
foo += 5
foo, bar, baz = 1, 2, 3
foo, bar = 1 # foo = 1; bar = nil
foo,*bar = 1, 2, 3 # equivalent to foo = 1; bar = [2, 3] ( the * multiple assignment is used to assign to an array)

===== Control Structure =====
**false** and **nil** are false, everything else are true.
* If statement
if expr [then]
expr...
[elsif expr [then]
expr...]...
[else
expr...]
end

* unless
unless expr [then]
expr...
[else
expr...]
end

=== if and unless modifier ===
* expr1 **if** expr2: if expr2 is true, execute expr1
* expr1 **unless** expr2: if expr2 is false, execute expr1

=== case ===
* case comparison is via the === operator
case expr
[when expr [, expr]...[then]
expr..]..
[else
expr..]
end


case $age
when 0 .. 2
"baby"
when 3 .. 6
"little child"
when 7 .. 12
"child"
when 12 .. 18
# Note: 12 already matched by "child"
"youth"
else
"adult"
end

==== Range ====
* expr1 .. expr2 (similar to awk)
* expr1 … expr2 (similar to sed)

==== Loop ====

while expr [do]
#code here
end

until expr [do]
#code here
end

=== while and until modifier ===

expr1 while expr2 # keep evaluating expr1 while expr2 is true.
expr1 untill expr2 # keep evaluating expr1 until expr2 is true
begin expr1 until expr2 # evaluating expr1 at least 1 time until expr2 is true

==== Iterators ====
* [1,2,3].each do |i| print i*2, “\n” end
* [1,2,3].each{|i| print i*2, “\n”}

==== For (similar to foreach) ====

for lhs... in expr [do]
expr..
end

* Example: for i in [1, 2, 3]
print i*2, "\n"
end

There are 2 special keywords that can be used in the loop body: **next** (jump to next iteration of the inner-most loop) and **redo** (restart the current iteration of the most inner-most loop without checking loop condition)

==== Raising Errors ====
* Raise syntax
raise # raise general exception
raise message_or_exception
raise error_type, message
raise error_type, message, traceback

* Examples
raise "you lose" # raise RuntimeError
# both raises SyntaxError
raise SyntaxError, "invalid syntax"
raise SyntaxError.new("invalid syntax")
raise # re-raise last exception

* $! contains the exception and $@ contains the position in source file.

==== begin block ====
**begin** is different from the uppercase **BEGIN**, which has a totally different meaning

begin
expr1..
[rescue [error_type,..]
expr2..]..
[else
expr3..]
[ensure
expr4..]
end

If exception occurs in **expr1**, **rescue** will execute **expr2**. The matching of **error_type** is done by **kind_of?**. **else** clause has to follow after **rescue** and is executed if no exception occurs in **expr1**. And example:
begin
do_something # exception raised
rescue
# handles error
retry # restart from beginning
end

for i in 1..5
retry if some_condition # restart from i == 1
end

# user defined "until loop"
def UNTIL(cond)
yield
retry if not cond
end

==== BEGIN and END blocks ====

BEGIN {
...
}

BEGIN registers initializing blocks in the appearing order, which will then get executed before any statement in the file. BEGIN has its own internal scope (don’t share local variables with outer scopes) and has to appear at toplevel.


END {
...
}

END register finalizing blocks. END blocks share their local variables. The END statement can only appear at the toplevel. Also you cannot cancel finalize routine registered by END.

==== Class ====

class Classname < SuperClass
expr..
end

class SingleTonClassname << SuperClass
expr..
end

==== Module ====

module Foo
def test
:
end
:
end

==== Method ====
Method has to be defined before it is invoked. It cannot be nested. Method’s return can be defined explicitly with “return”, or implicitly using the last evaluated expression

def fact(n)
if n == 1 then
1
else
n * fact(n-1)
end
end

Method can be declared private inside the function form. If Method is declared outside the class definition, it’s marked private by default. Method declared within class definition is marked public by default.

==== Singleton-method ====

def foo.test
print "this is foo\n"
end

The singleton-method definitions can be nested and are inherited to subclasses. Singleton-method acts as typical class’s method in other OOP languages.

==== alias, undef, and defined? ====

alias method-name method-name
alias global-variable-name global-variable-name

Aliasing numbered global variables is forbidden.

=== undef ===
undef method_name
Used to cancel method definition.

=== defined? ===
defined? expr
If expr is not defined, return false, otherwise return a string describing the kind of expression.

view comments