It’s really easy to setup a many-to-many relationships in Rails. There are 2 ways of doing this:
1) has_and_belongs_to_many association
2) has_many :through
I’m not going to touch on the details of each because there’s great documentation provided in the above links. However, I do want to point out the flaws and limitations of has_and_belongs_to_many association.
Like in this example,

Photo Credit: http://guides.rubyonrails.org
the way to connect the assemblies and parts models is to create a table (named after both models, assemblies_parts) with 2 columns, assembly_id and part_id. It’s a table not a model. It’s definitely much easier to create a has_and_belongs_to_many association than has_many :through but has_and_belongs_to_many association doesn’t work in most real life situations.
1) Notice there’s no id column in the assemblies_parts model? Because of this, there’s no way to add additional attribute to this table. Let’s say you want to know which assembly/part combination is the most popular. In a has_and_belongs_to_many association, you can’t add an additional attribute, e.g. popular_count to the table.
2) As there’s no model created, there’s no way to add validations or callbacks in a has_and_belongs_to_many setup.
I learned it the hard way and had to convert my code from has_and_belongs_to_many to has_many :through. As your app gets more complicated with rules and processes, make sure your associations is setup in a way that’s flexible enough to incorporate the changes easily. Moving forward, my choice is to always use has_many :through because I don’t want to create road blocks for myself down the road.
Soon-to-be released Rails 3.0 only works on Ruby 1.8.7 and above. Can’t wait but I still have other projects on Ruby 1.8.6. How can I install 2 or more versions of ruby on one machine? No biggy, Ruby Version Manager (RVM) is here to the rescue. With RVM installed, I’m now allowed to install multiple ruby environments with it’s own sets of gems. Life prior to RVM, I used to create different virtual boxes, each box with it’s own versions of ruby, rails and gems so that I can keep things separate for testing. It’s not the best solution till RVM came along. This is why I made a pledge and donated to show my support for the RVM project!
Here’s some instructions to install RVM, Ruby and Rails on Mac OS 10.5:
1) Go to http://rvm.beginrescueend.com/rvm/install/
2) Follow direction to install rvm via Github Repository
Run this command in Terminal
mkdir -p ~/.rvm/src/ && cd ~/.rvm/src && rm -rf ./rvm/ && git clone —depth 1 git://github.com/wayneeseguin/rvm.git && cd rvm && ./install
3) Add this line (after path) to .bash_profile
a) sudo nano ~/.bash_profile
b) then add this line of code below PATH
if [[ -s /Users/aong/.rvm/scripts/rvm ]] ; then source /Users/aong/.rvm/scripts/rvm ; fi
c) refresh .bash_profile => . ~/.bash_profile
4) rvm -v in Terminal (to check if it’s working)
5) install ruby using rvm
rvm install 1.8.7
6) Type rvm list in Terminal => to check what ruby versions were created
7) To switch between ruby version => rvm 1.8.7, then ruby -v, it should show you 1.8.7 (or whatever version you’ve installed in step 5)
8) To go back to system default version, type this in terminal => rvm system
9) Install Rails. Make sure you are in the right version of ruby.
gem install rails -v 2.3.5 —include-dependencies (SUDO is not needed when using rvm)
10) Because gems in each rvm version is independent, you’ll need to reinstall relevant gems that are needed for this ruby version, e.g. install database gem
gem install sqlite3-ruby
gem install rspec-rails
There’s also a cool Railscast about Rails 3 and RVM by Ryan Bates here
Recently I took a Ruby class, taught by Sarah Allen and one of the most fun things I learned was profiling. Performance testing is such a crucial part of software development. Prior to Sarah’s class, the most common way to store and retrieve data is via an array. For example in a dictionary example, I would normally do this:
To add a word to a dictionary
def add(word)
@words « word
endTo find a word in a dictionary
def find(prefix)
@words.find_all {|w| w =~ /^#{prefix}/}
end
See dictionary_array.rb in my github account for more details.
In class, we were introduced a new data storing structure, called Trie, inspired by Tyler McMullen’s LA RubyConf 2010 talk: http://www.scribd.com/doc/27149829/Alternative-Data-Structures-in-Ruby It’s like storing each character in a tree and creating branches of a tree based on the word inserted into the dictionary.
To add a word to a dictionary
def add(word,subtree=@tree)
if word.size == 0
subtree[:terminal] = true
else
first_char = word[0]
rest = word[1..-1]
subtree[first_char] ||= {}
add(rest, subtree[first_char])
end
end
To find a word in a dictionary
def find(prefix)
subtree = walk(prefix)
return [] unless subtree
return words(subtree, prefix)
end
See trie_dictionary.rb in my github account for more details.
So which performs better, an ARRAY or TRIE?
We used a profiling/performance testing tool called Ruby-Prof to find out! These were the steps taken to perform the test:
1) Install ruby-prof (http://ruby-prof.rubyforge.org/)
sudo gem install ruby-prof
2) Create a script to do the following:
a) Open and read a large data set, e.g. more than 10,000 rows of data. In my experiment, I used a file with lots of products.
http://github.com/aihui/ruby_prof_test/blob/master/products.csv
b) Insert the large data set (products.csv) into the array or trie. Add RubyProf.start to this section of code so that we can monitor the performance.
#read file and put words into dictionary
RubyProf.start
file.each do |data|
d.add(data)
end
result = RubyProf.stop
c) Find products that begin with “fr” in the array or trie. Add RubyProf.start to this section of code so that we can monitor the performance.
RubyProf.start
found = d.find(search_text)
foundresult = RubyProf.stop
d) Write the results of each test into their respective files, e.g. results for adding words and results for finding words.
A dictionary_test.rb file was create for step(2). I switched the “require dictionary_array” and “require trie_dictionary” accordingly, depending on what test I’m running.
3) Run the ruby file and you’ll see the results file being produced too.
ruby dictionary_test.rb
Results
Based on the results below, it shows adding to an array is definitely faster. However, searching through a trie yields way better performance! It took 0.069465 seconds to search through an array while it ONLY took 0.000049 seconds to search through a trie!



