Thursday, November 11, 2010

Ruby - some mysterious language features

Here I will share some of the interesting ruby features that you will notice when looking into mature ruby code by advanced level coders.

#the new tap method of ruby will always return itself, but lets you play with!
data = (1..10).to_a
data.tap{|x| print x}.to_a.join(', ')
p
#alias will redirect method calls to method with a different name, useful for api changing
class SomeClass
def new_method(x)
p "The value is #{x}"
end
alias :old_method :new_method
end
SomeClass.new.old_method(20)
# Array.grep - the beauty of unix grep right into Ruby Array
puts((1..100).grep(38..44)) #=> [38, 39, 40, 41, 42, 43, 44]
c = IO.constants
puts c.grep(/SEEK/) #=> ["SEEK_END", "SEEK_SET", "SEEK_CUR"]
puts c.grep(/SEEK/) {|v| IO.const_get(v) } #=> [2, 0, 1]
#delegate - get rid of all the method declarations that just delegate others using Ruby's delegate support
require 'delegate'
class Actual
def a_method
puts "Method inside Actual"
end
end
class MyDelegator < DelegateClass(Actual)
def initialize
@actual = Actual.new
super(@actual)
end
end
MyDelegator.new.a_method
#freeze - make your variables pseudo readonly
my_hash = {:a => 1, :b =>2}.freeze
# my_hash[:a] = 20 #=> throws exception
#included vs. extended - included makes module methods your instance methods, extend will make the same methods as class methods
module AModuleToInclude
def self.included(mod)
puts "#{self} included in #{mod}"
end
def self.extended(mod)
puts "#{self} extended in #{mod}"
end
def a_method
puts "From a_method"
end
end
class AClass
include AModuleToInclude
extend AModuleToInclude
end
#this line makes use of the include
AClass.new.a_method
#this line makes use of the extend
AClass.a_method
#Use of * for argument list
def many_args(*args)
puts "Inside many_args = " ,*args
end
many_args( [1, 2, 3, 4])
#class_eval - write a code in string without making it look like a string using this
class ClassWithEval
class_eval <<-EVAL
def string_method(arg)
puts arg
end
EVAL
end
ClassWithEval.new.string_method('hi')
#instance_eval - write code as if your are right inside a class for your instance
class ClassWithInstanceEval
def initialize
@secret = 99
end
end
k = ClassWithInstanceEval.new
k.instance_eval { puts @secret } #=> 99
#inherited - fires this hook when a new subclassing takes place
class Foo
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
end
class Bar < Foo
end
class Baz < Bar
end

Wednesday, November 10, 2010

Upgrading to Ruby on Rails 3 - beware!

Ryan Bates had a series of posts (1, 2 and 3) on upgrading your Rails 2.3.x apps to Rails 3.x and sure they are useful. But if you are really doing that, beware of the following changes that you will need to do. It will take a lot of time for sure if you you have a moderate sized app.

Upgrading your models:

  1. Get rid of all validates_xxx_of with appropriate validates
  2. Get rid of all def validate ... end methods with custom validator
  3. Find out all occurrences of :conditions, :limit, :order and use new active record methods instead
  4. Replace all named_scope with scope
  5. Make sure your acts_as_xxxx plugins are all updated to use Ruby on Rails 3. I had troubles with Authlogic as it shows Based.named_scope is DEPRICATED. Still looking for a solution.
  6. Ruby 1.9.2 doesn't work with soap42 wsdl driver. Haven't found a solution yet as it keeps reporting an error regarding "XML processor module" missing.
Upgrading your controllers:
  1. Find out all occurrences of find method and replace with where.
  2. Find out all calls to Mailer that looks like deliver_xxx and make it xxx.deliver
Upgrading your views:
  1. All erb blocks must start with a has to be changed to  . Do the same for all such erb blocks with a do.
  2. All link_to_remote or remote_form_for or other remote form helpers need to be changed to their non-remote counterparts with a param :remote => true.
Upgrading your mailers:
  1. body method is gone, instead of the hashed params to this method, just define instance variables.
  2. All xxx.text.html.erb now becomes xxx.html.erb in the mailer views.
Upgrading the config files:
  1. The best way is to crate a new rails app and then copy the config folder into yours.
  2. Look at the initializers and clean up any required initializers.
  3. Make sure you have autoload_paths setup to include the lib folder. It is not included by default, so your code from the lib folder won't be accessible.
  4. Look at deprecation warning and you will see lots of config.action_controller.* need to changed to config.*
Plugins:
  1. All plugin/lib/rails/tasks/*.rake needs to be copied in to plugin/lib/tasks/*.rake
  2. Make sure your plugins don't use named_scope, if you find any, replace with scope.
  3. Whatever applies to your models should also be applied to your plugins that act on models.
Testing:
  1. Check you have updated shoulda, rspec or whatever lib you use.
  2. Update test codes according to your new lib.
Upgrading IDE to use RVM:
  1. Your IDE is not smart enough to use RVM. However use this to get TextMate ready.