Rails 3 upgrade part 1: Booting the application
It’s time for another Rails upgrade! We all have our share of bad experiences and frustrations every time we upgrade a piece of software. Even for technical people who live and breath on the edge, upgrades are one of these things we try to avoid as much as possible. Still, there is always a sense of excitement in trying something new even if it adds problems to an already stable piece of code.
For a little background, I am upgrading a Rails app several of friends and I have written last year. The code is available at github.
In this post, I share the steps I did to boot the application. This doesn’t mean the upgrade went fine neither the app is ready to go. It only means all the required initialization are OK. In succeeding posts, I share my experiences in upgrading the app to a green state.
First, my environment.
greg@piccolo:~/dev/projects/propsify3$ rvm info
ruby-1.8.7-p299@propsify:
system:
uname: "Linux piccolo 2.6.31-22-generic #61-Ubuntu SMP Wed Jul 28 01:57:06 UTC 2010 i686 GNU/Linux"
shell: "bash"
version: "4.0.33(1)-release"
rvm:
version: "rvm 0.1.44 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]"
ruby:
interpreter: "ruby"
version: "1.8.7"
date: "2010-06-23"
platform: "i686-linux"
patchlevel: "2010-06-23 patchlevel 299"
full_version: "ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]"
greg@piccolo:~/dev/projects/propsify3$ script/about
About your application's environment
Ruby version 1.8.7 (i686-linux)
RubyGems version 1.3.7
Rack version 1.0 bundled
Rails version 2.3.2
Active Record version 2.3.2
Action Pack version 2.3.2
Active Resource version 2.3.2
Action Mailer version 2.3.2
Active Support version 2.3.2
Application root /mnt/hgfs/greg-mini/dev/projects/propsify
Environment development
Database adapter postgresql
Database schema version 20100113032723
greg@piccolo:~/dev/projects/propsify3$ gem list
*** LOCAL GEMS ***
actionmailer (2.3.2)
actionpack (2.3.2)
activerecord (2.3.2)
activeresource (2.3.2)
activesupport (2.3.2)
geokit (1.5.0)
json (1.4.5)
mime-types (1.16)
oauth (0.4.1)
pg (0.9.0)
rails (2.3.2)
rake (0.8.7)
RedCloth (4.2.2)
twitter_oauth (0.3.2)
greg@piccolo:~/dev/projects/propsify3$ ls vendor/gems/
authlogic-2.1.3 geokit-1.5.0 haml-2.2.16 macaddr-1.0.0 twitter_oauth-0.3.2 uuid-2.1.0
greg@piccolo:~/dev/projects/propsify3$ ls vendor/plugins/
acts_as_commentable geokit-rails is_taggable thinking-sphinx will_paginate
declarative_authorization gravatar-plugin jrails validates_date_time
exception_notification haml subdomain-fu vote_fu
Step 1: Install rails 3
gem install rails –pre
Step 2: Install the plugin tool
script/plugin install git://github.com/rails/rails_upgrade.git
Step 3: Show upgrade checklist
rake rails:upgrade:check
This task lists the items you should watch out for when doing the upgrade. You don’t need to fix everything right away (some are deprecation notice) but review the checklist nevertheless.
Step 4: Generate the new routes
rake rails:upgrade:routes
This task reads the current config/routes.rb and outputs a Rails 3 version.
Don’t worry, it doesn’t override your routes file. Keep this in a safe place for later use.
IMPORTANT: I actually didn’t realize I did the right thing until after the actual code upgrade. When I tried generating the new routes after the code change, it outputted an empty block. I have no idea if this is unique to my case but just to be sure, generate the routes beforehand and keep a copy.
Step 5: Create Gemfiles
rails:upgrade:gems
Next is to generate the file ‘Gemfile’. In Rails 2, the gems you need are listed in config/environment.rb while in Rails 3 the gems are listed in the Gemfile. Gemfile is used by the program ‘bundler’ to manage the gems required by your application. Unfortunately, this task didn’t include the gems I listed in environment.rb so I have to add it later.
Step 6: Backup your files
rails:upgrade:backup
I hope you are working on another branch (or a copy) but just in case you are not, run this task to make copies of the files that will be affected during the upgrade.
Now comes the juicy part.
Step 7: Generate the Rails 3 app on top of your Rails 2 app
rails new propsify3 -d postgresql
Run this command in your app’s parent folder. In my case, my app’s name and pathname is ‘propsify3′ and I am using postgresql as my database. This command created and replaced a bunch of files. Since you’ve backed-up everything, there’s nothing to worry.
Step 8: Move code from environment.rb to application.rb
Your new config/environment.rb file looks like it went through a rigorous diet. You can leave this file for now. What is important now is you move the initializer code from your config/environment.rb.rails2 to config/application.rb. These are the config.* lines except the config.gem which goes to Gemfile.
Step 9: Convert the new routes
You can still use the existing routes until 3.1 but since there’s a tool to help you migrate, I suggest doing it. At this point, when I tried the rails:upgrade:routes, no routes were generated. So make sure you generate the routes before Step 7.
Step 10: Delete new_rails_defaults.rb
rm config/initializers/new_rails_defaults
Step 11: Upgrade the plugins and gems
Many plugins are now available as gems. Check your plugins and gems at http://railsplugins.org. In my case, the following plugins were converted to gems:
acts_as_commentable
declarative_authorization
haml
will_paginate
thinking-sphinx
Unfortunately, the plugins below are not yet ready for Rails 3. I removed them for now and all code that references them.
jrails
subdomain-fu
vote_fu
IMPORTANT: In your Gemfile, make sure you check specify the right version that is compatible with Rails 3. Some gems are still in the pre-release version and will not be downloaded if you don’t specify a version in your Gemfile. For example, this is a snippet from my Gemfile:
gem 'pg' gem 'acts_as_commentable' gem 'declarative_authorization' gem 'haml' gem 'thinking-sphinx', '2.0.0.rc1', :require => 'thinking_sphinx' gem 'will_paginate', '3.0.pre2' gem 'uuid' gem 'geokit'
Step 12: Update initialization code
After step 10 you are good to go, if you’re lucky. In my case, I had to remove some patches and change code to boot the application.
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(date_time_formats)
This fails in Rails 3 because core extensions have been moved out of their modules and are now included in classes they extend. For example, to fix the date format problem do:
Date::DATE_FORMATS.merge!(date_time_formats)
Step 13: Boot the app
rails server
Yay! If you are wondering what happened to script/server command, Rails went the “Merb way” and consolidated the script/* commands into the rails script.
By now, you should see the famous Rails’ “Welcome aboard” message in your browser.
Step 14: Remove public/index.html
Now, you can try if your application is working.
There are still more work to do like moving to the ActiveRecord/ActiveRelation API and removing the deprecation notices. Before moving on, I still need to fix the problems in my routes and unsupported gems which I will tackle in my next post.
Coding gems 31-40
#31 All non-trivial abstractions, to some degree, are leaky. Joel Spolsky
#32 Five different programmers can solve the same problem five different ways
#33 Don’t write 200 lines of code when 10 will do
#34 If the “box” is the boundary of constraints and conditions, don’t think outside the box—find the tbox. Andy Hunt, Dave Thomas
#35 Complexity and communication costs rise with the square of the number of developers, while work done rises linearly. Fred Brooks
#36 Tests only prove the presence of errors – not the absence of them
#37 A metaprogrammer is someone who writes code that writes code for food
#38 The task on a project is not to try for complete communication but to manage the incompleteness of our communications. Cockburn
#39 A good plan violently executed now is better than a perfect plan next week. Patton
#40 Tests first, then code…. or the kitten gets it
I got re-acquianted with this scenario while working on the OpenAmplify gem – a wrapper for the OpenAmplify API. When you give the api a text like a blog comment, it will return a list of common terms, opinion scores, named locations, and other information that can be used for text mining operations.
The OpenAmplify returns key-value pairs in an XML string by default, but it can also be in JSON, CSV, or RDF format. From a Ruby client’s point of view, we want it in Hash. You can choose to use an XML library like Nokogiri but in my opinion, working with a Hash fits nicely with Ruby.
Anyway, back to the problem. I have an instance variable that holds the data. One approach is to give clients access to the instance variable.
class Response
attr_reader :data
def initialize
@data = {}
end
end
data = response.data
topics = data[‘Topics’]
One major issue with this approach is you’re exposing the internals of your class. What if you decided to rename the variable into ‘@results_in_hash_form’? Then, all programs that uses your code will break. Worse, you will be limited from enhancing the behavior of your class like lazy loading of the data. You can wrap the access to your data inside a method but that still presents the problem of exposing the internals of your class. Also, that’s an unnecessary extra line of code
My suggestion is to make ‘Response’ behave like a Hash so we can do these:
topics = response['Topics']
response.has_key?('Topics')
# And still have our own methods:
response.some_method_we_defined
So, how can we do this? The trick is to delegate the calls to the instance variable. One approach is to define the Hash methods you want to support:
class Response
[‘[]’, ‘has_key?’, ‘fetch’, ‘empty?’, ‘keys’].each do |method_name|
class_eval <<-EOS
def #{method}(*args)
@data.send("#{method_name}", *args)
end
EOS
end
end
The code above is a shortcut to writing every method by hand. If you want to support all Hash methods, that would be a lot of typing.
A better approach is to just take advantage of Ruby’s ‘method_missing’ which is called every time an undefined method is called.
class Response
def method_missing(name, *args, &block)
@data.send(name, *args, &block)
end
end
Of course, how your ‘method_missing’ will look like depends on your requirements. In our simple case, we can simply delegate to @data.
This approach is called a “Dynamic Proxy” from the book Metaprogramming Ruby by Paolo Perrota. If you want to take your Ruby skills to the next level, I highly recommend this book.
Canada work visa questions?
Applying for a Canada work visa could sometimes get confusing. Sometimes reading and re-reading the Canada Immigration Center guide for foreign workers is not enough. A lot of questions about your Canadian work visa sometimes can only be answered by someone who went through the same process. Misery loves company, so they say.
After a discussions with my wife and her cousing, we decided to create a question and answer site dedicated to Canada work visa applicants, would-be-applicants and just plain curious Georges. It is similar to a regular forum – members gets to ask their questions and other members gets to answer, and then some. What’s good about this Q&A format is that members also get to vote for the answer that they think is the most helpful. The most voted answers then get the top spots in the thread and not buried deep in the topic conversation 3 pages back.
Need help with your Canadian work visa application? Or you just want to know what to put in Line xx of your application form and do not want to bother and pay an immigration consultant? Maybe it’s time you pay the Canada work visa Q&A a visit.
Ruby 101: Hash initialization gotcha
I have a code that counts how many times a word occurs – a perfect fit for Hash.
def word_counts(words)
counts = Hash.new(0)
words.each do |word|
counts[word] += 1
end
end
categories = {
:a => word_counts(‘some text’)
:b => word_counts(‘another set of text’)
}
Somewhere, I use the hash returned by the word_counts method to do some calculation.
def score(word_scores, words)
words.each do |word|
v = word_scores[word]
v = 0.1 if v.nil?
score += Math.log( v / some_value )
end
end
categories.each do |category, word_counts|
score(word_counts, %w{some random text})
end
When I run the score, I always get an ‘Infinity’. After some debugging, the problem is this piece of code:
v = word_scores[word] v = 0.1 if v.nil?
‘word_scores’ returns 0 if ‘word’ doesn’t exist; not nil which is the default behavior. Later, I realized I initialized it via Hash.new(0) which makes 0 the default value. In fact, it is not even necessary to check for nil or 0. All we want is to retrieve the value referenced by the key, and if the key does not exist, give me 0.1.
v = word_counts.fetch word, 0.1
Ruby 101: How to filter an Array using proc
Over at the PhRUG, a Ruby developer community based in the Philippines, we conduct code review sessions via our mailing list. A code is posted and members share alternative implementations. So far, it’s been effective and newbies and veterans alike are learning new things in Ruby. Here’s a recent code that filters an array based on several conditions.
matches = []
(0..9).each do |i|
if (i > 5) && (i % 2).zero? && (i % 3).zero?
matches << i
end
end
The first improvement made by Bong is to use ‘select’.
matches = (0..9).select {|i| (i > 5) && (i % 2).zero? && (i % 3).zero?}
What if you are going to include another condition? Of course, you can argue to simply hard code the new condition but that wouldn’t be fun
The solution by Tim and Neil is to use procs which I’m sure would make Matz very happy.
conditions = [
proc { |i| i > 5 },
proc { |i| (i % 2).zero? },
proc { |i| (i % 3).zero? }
]
elements = (1..10).to_a
matches = elements.select do |i|
conditions.all? { |c| c[i] }
end
puts matches.join(',')
We can even add this filtering to the Array class, just in case we need to use this across our application.
class Array
def matches(*conditions)
select do |i|
conditions.all? { |c| c[i] }
end
end
end
matches = elements.matches(*conditions)
puts matches.join(',')
matches = elements.matches( proc{ |i| i > 5 })
puts matches.join(',')
matches = elements.matches( proc{ |i| i > 5 }, proc{ |i| (i%3).zero? })
puts matches.join(',')
Ruby 101: How to add methods to a Ruby class
Let’s add a method that checks whether an Array has many elements.
a = [1,2,3] a.many? # NoMethodError: undefined method `many?'
Let’s fix this by adding a new method to the class Array.
class Array
def many?
size > 1
end
end
a = [1,2,3]
a.many? # true
b = [1]
b.many? # false
c = []
b.many? # false
Let’s implement Rails’ fancy ‘days.ago’ method:
5.days.ago # NoMethodError: undefined method `days' for 3:Fixnum
Now, add the days and ago methods to Fixnum.
class Fixnum
def days
self * 60 * 60 * 24 # we store seconds in a day
end
def ago
Time.now - self
end
end
What is the right way to build a startup?
* Define a market problem that you believe you can solve
* Research this market by doing market sizing, looking at existing products, talking to customers and deciding how you will make money
* Validate that you can make money before starting. This means looking at what your buyer pays for similar products now, what the history of other people who have tried to monetize in this way have experienced, what your costs to acquire customers will be and what you believe you can make over the customers’ lifetimes. These are all assumptions – nothing more. I believe passionately that if you don’t do a financial model you shouldn’t spend any time or money building a product. You want to talk about the ultimate “fail fast” – how about if you fail before you’ve spent any money building product because you validate there isn’t a big enough market or you can’t make money?
* If you believe there is a market then build a prototype product that you can show customers, investors and potential employees.
* From there build the MVP (minimum viable product). I believe in launching with a small set of features and learning from the market before you spend too much money building out a feature rich product or before you put serious capital to work.
* If you validate that there’s a market then go for it! If you don’t believe that your product is resonating then pivot and find one!
from Mark Suster’s post Why The ‘Fail Fast’ Mantra Needs to Fail. Check out his article because it’s a well-written post against the “Fail Fast” idea.
The History of Marketing
via flowtown


