Thursday, October 13, 2011

Ruby Equality And Object Comparison

Ruby has three main equality test methods, ==, eql? and equal?. These methods normally live in the Object class. Inside the Object class all there methods do exactly the same thing, they test if two objects are exactly the same object_id.

Just take this example:-

string1 = "abc"
class MyObject
end
object1 = MyObject.new
object2 = object1
object3 = MyObject.new
 
puts "Object 1 is == to object 2: #{object1 == object2}" #its true
puts "Object 1 is eql? to object 2: #{object1.eql? object2}" #its true
puts "Object 1 is equal? to object 2: #{object1.equal? object2}" #its true
puts "Object 1 is == to object 3: #{object1 == object3}"  #its false
puts "Object 1 is eql? to object 3: #{object1.eql? object3}" #its false
puts "Object 1 is equal? to object 3: #{object1.equal? object3}" #its false
 
Usually with Ruby we will tend to leave the equal? method alone
to always give us the ability to find out if two objects are
exactly the same. The eql? and == methods however are open to
be redefined in any way we like. You can redefine the == method
to give your objects custom behavior when it comes to equality testing.

 
class Shirt
  attr_reader :size
  def initialize size
    @size = size
  end
 
  def ==(another_shirt)
    self.size == another_array.size
  end
end
 
More than that, by defining the == method on your object, 
you also get the != method for free.
 
shirt1 = Shirt.new(10)
shirt2 = Shirt.new(11)
shirt3 = Shirt.new(10)
 
puts "Are shirt1 and shirt2 equal? #{shirt1 == shirt2}" #its false
puts "Are shirt1 and shirt3 equal? #{shirt1 == shirt3}" #its true
puts "Are shirt1 and shirt2 NOT equal? #{shirt1 != shirt2}" #its true
 
 
There is only one case that I see where the eql? method 
might come in handy. If you have to defined the <=> method, 
therefore giving your object an implicit == method, it may no longer 
make sense to redefine == separately. But if for some reason you need to 
keep the <=> equality behavior but still need even more custom 
equality behavior, you can always use the eql? method for this 
purpose. I can't really envisage a situation where you might want to do 
this, but the capability is certainly there. 
 
 


Friday, March 18, 2011

Add MySQL foreign key constraints in Rails migrations.


I have written a small module which let you add MySQL foreign key constraints in Rails migrations. This functionality is missing in Rails 2.*.

migration_helper.rb

module MigrationHelper
  #add foreign key constraint
  #Options can be :override_column => string, :referential_action => string
  #:override_column - Override column name. It is inferred from 'from_table'
  #:referential_action - can be any or concatenation of both strings.
  #'ON UPDATE [RESTRICT | CASCADE | SET NULL | NO ACTION]'
  #'ON DELETE [RESTRICT | CASCADE | SET NULL | NO ACTION]'
  def add_foreign_key(from_table, to_table, options={})
    if options[:override_column].blank?
      column_name = foreignkey_column_name(to_table)
    else
      column_name = options[:override_column]
    end
    
    constraint_name = foreignkey_name(from_table, to_table, column_name) 
    
    query = "alter table #{from_table}
              add constraint #{constraint_name}
              foreign key (#{column_name})
              references #{to_table}(id) "
    query = query + options[:referential_action].to_s unless options[:referential_action].blank?
              
    execute query
  end
  
  #removes foreign key
  #Options: :override_column
  #override_column - Override column name. See above.
  def remove_foreign_key(from_table, to_table, options={})
    if options[:override_column].blank?
      column_name = foreignkey_column_name(to_table)
    else
      column_name = options[:override_column]
    end
    
    constraint_name = foreignkey_name(from_table, to_table, column_name) 

    execute %{alter table #{from_table} drop foreign key #{constraint_name}}
  end
  
  private 
  def foreignkey_column_name(table)
     "#{table.to_s.singularize}_id"
  end

  def foreignkey_name(from_table, to_table, column_name)
    "fk_#{from_table}_#{column_name}_to_#{to_table}"
  end
end



Usage:-
  • Copy migration_helper to lib folder of your rails project.
  • Add the following line of code in rails initializer. If you are using RAILS 2.3.5 or above add this line toinitializers/new_rails_defaults.rb:-
ActiveRecord::Migration.extend(MigrationHelper)
  • Now, in migrations you can add foreign key constraints as:-
add_foreign_key(from_table, to_table, options={})
  • To remove foreign_Key remove_foreign_key(from_table, to_table, options={})
To see the available options refer comments in a file.
Feel free to give your feedback or give me a call for any queries.

Tuesday, January 18, 2011

CSRF | Cross Site Request Forgery | Handling in Ruby On Rails

What happens: When the user views a form to create, update, or destroy a resource, the rails app would create a random authenticity_token, store this token in the session, and place it in a hidden field in the form. When the user submits the form, rails would look for the authenticity_token, compare it to the one stored in the session, and if they match the request is allowed to continue.

Why this happens: Since the authenticity token is stored in the session, the client can not know its value. This prevents people from submitting forms to a rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is ok. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://service%5FA.com/close%5Faccount. This is what is known as CSRF (Cross Site Request Forgery).

If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.
Notes: Keep in mind, rails only checks POST, PUT, and DELETE requests. GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests should NOT create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).

Lessons: Use authenticity_token to protect your POST, PUT, and DELETE requests. Also make sure not to make any GET requests that could potentially modify resources on the server.

Interested in using Java Libraries with Ruby??

Java now holds the lead in many areas of software development, but dynamic languages threaten to bypass it in this inexorable climb. Languages such as Python, Perl, Rexx, Groovy, TCL, and Ruby have been doing yeoman service in specialized domains such as file processing, test automation, software builds, glue code, and Web GUIs for years.Here is the technique to glue them together.

JRuby

JRuby, an open source Ruby interpreter for the JVM, simplifies Ruby-Java integration. You can call Java libraries from Ruby, script Java applications with an embedded interpreter, or even use Ruby libraries from Java. 

Monday, January 17, 2011

Interested in using .NET Libraries with Ruby??

Obsessed with .Net and want to use it as ruby. Here is the solution:-
IronRuby is a Open Source implementation of the Ruby programming language for .NET, heavily relying on Microsoft's Dynamic Language Runtime.

Difference between "and" and "&&"--"or" and "||" ruby operators

One of the subset of Ruby operators are
&&, and, ||, or 
everybody gets confused when to use which operator and what advantage one operator has on another. I came across a very nice article by Avdi on these four operators, just give it a glimpse...

Integerating Rails with Twitter using OAuth

To integerate twitter with your existing rails application follow the steps below:-
  1. Install required Gem as sudo gem install Twitter.
  2. Register your application in read-write mode with Twitter.
  3. Note down the ConsumerToken and ConsumerSecret when application is registered on Twitter.
  4. Refer the attached controller to authorize user on Twitter using OAuth and update the status there.

app/twitters_controller.rb

class TwittersController < ApplicationController
  
  #it is obtained from twitter when your application is registered
  ConsumerToken, ConsumerSecret  = 'consumer token', 'consumersecreat'
  
  before_filter :verify_oauth, :only => [:share_status]
  
  def share_status
    begin
      client.update("Voila! Status set from globallogic")
      flash[:notice] = "Twitter status updated successfully"
    rescue Exception => e 
      flash[:warning] = "Status is duplicate/invalid"
    end
    redirect_to root_path
  end
  
  def finalize
    oauth.authorize_from_request(session['rtoken'], session['rsecret'], params[:oauth_verifier])
    #profile = Twitter::Base.new(oauth).verify_credentials
    session['rtoken'] = session['rsecret'] = nil
    session[:atoken] = oauth.access_token.token
    session[:asecret] = oauth.access_token.secret
    redirect_to session[:redirect_to]
  end
  
  private
    def oauth
      @oauth ||= Twitter::OAuth.new(ConsumerToken, ConsumerSecret, :sign_in => false)
    end


    def client
      oauth.authorize_from_access(session[:atoken], session[:asecret])
      Twitter::Base.new(oauth)
    end
  
    def verify_oauth
      if session[:atoken].blank? or session[:asecret].blank?
        session[:redirect_to] = {:controller => params[:controller], :action => params[:action]}
        oauth.set_callback_url(finalize_twitters_url)


        session['rtoken']  = oauth.request_token.token
        session['rsecret'] = oauth.request_token.secret
    
        redirect_to oauth.request_token.authorize_url
      end
    end
  
end

Accessing session data using session_id

This is the very rare case but some times we need to access the data stored in ActiveRecordStore through session_id(specially when the request is not coming from browser). Here is a way I found and like to share:

@user_id = ActiveRecord::SessionStore::Session.
find_by_session_id('<session_id>').data[:user_id]