Wednesday, July 11, 2012

Ruby : Rails : Mixins and Refactoring

I really love coding and to try out new things whenever possible. Though it's not always possible to do what I want but, one should fully utilize the opportunity when it presents itself.

Refactoring, it's one of the best things you can do for your code. After refactoring code becomes more readable, more secure and if everything work out good then refactoring also results in better performance.

And I got the opportunity I have been looking for, the Job was "To do refactoring" for one of the projects I've been working on.

Considering the project is for stores, there were 2 models rewards and promotions for store. The only difference between two was rewards do have points and not for promotions.

In reward.rb
class Reward < ActiveRecord::Base
  belongs_to :store

  validates_presence_of :store_id
  validates_presence_of :points
  validates_presence_of :description
  
  validates_numericality_of :points, :greater_than => 0
  
  validates :valid_from, :date => {}
  validates :valid_to, :date => { :after_or_equal_to => :valid_from }, :allow_blank => true

  scope :first_scope, lambda{ # some code }
  scope :second_scope, lambda { # some code }
  scope :third_scope, lambda { # some code }
end
In promotion.rb
class Promotion < ActiveRecord::Base
  belongs_to :store
  
  validates_presence_of :store_id
  validates_presence_of :description
  
  validates :valid_from, :date => {}
  validates :valid_to, :date => { :after_or_equal_to => :valid_from }, :allow_blank => true
  
  scope :first_scope,  lambda{ # some code }
  scope :second_scope, lambda { # some code }
end
Except that every thing from validations to associations were same.
Got irritated for a while, but then was happy that I was going to change the code.

So I added a module in lib as rewards_promotions.rb and added following code to it.
module RewardPromotion
  def self.included(base)
#    base.extend(ClassMethods)
    base.class_eval do
      belongs_to :store

      validates_presence_of :store_id
      validates_presence_of :description

      scope :first_scope, lambda{ # some code }
      scope :second_scope, lambda { # some code }
    end
  end
end
Now I just removed the similar code from respective models(reward.rb and promotion.rb) and just added following line
  include RewardPromotion
Thats it. My work was completed and I just restarted the server and everything worked as before. Just that the code was at one place and I dont have to repeat the same code for both models.
This was done for other models too and the project is now really great.

For refactoring I also avoided using if !true conditions to unless true.

Also the major calculations were called directly in controller from model. Which was converted to private method and then called from other method.
Need to understand the important functionality needs to go in private methods and be called from other method rather that directly making it available for controllers.
Refactoring was fun for me to get the things out of controllers and shifting to model with a great security and performance.

Sunday, July 1, 2012

Revised: Sending mail through ruby on rails

I have previously added post Sending mail through ruby on rails for adding mail configuration to Ruby on Rails application. But as we know in rails 3 there are separate mailers and we dont have to add mailers in models folder any more.
So I though just to add revised code.

First I created a setup_mail.rb in config/initializers and added below code which is for my heroku app (sendgrid)
 ActionMailer::Base.delivery_method = :smtp
 ActionMailer::Base.smtp_settings = {
  :user_name => "send grid user name",
  :password => "sendgrid password",
  :domain => "your domain name",
  :address => "smtp.sendgrid.net",
  :port => 25,
  :authentication => :plain,
  :enable_starttls_auto => true
 }
Now just created user_mailer.rb in app/mailers and added a below code
=begin
  File Name : user_mailer.rb (UserMailer)
  Description : File added to hold mailer template actions for users.
  Date : 13/04/2012
  Dependencies : ActionMailer::Base
  Note : If file not in use please remove the method calls from the other controllers. Information for other controllers and actions is given above the respective actions.
=end

class UserMailer < ActionMailer::Base
  default :from => "info@testapp.com"
  # registration_confirmation(new_user_information) : 13/04/2012
  # This action is called from users_controller.rb in create action after successful user registration.
  # (Autoresponder Email on new Registration)Task
  def registration_confirmation(user)
    @user = user
    mail(:to => @user.email, :subject => "Registered with Test App") do |format|
      format.html
    end  
  end
end
Well, lots of comments. But I prefer to keep those for other programmers to know where the code is used or for what purpose. Thats really useful for me too to find the code quickly and reminding what the action does.

After setup files done, added 1 single line as below, in users_controller.rb in create action after the user is successfully stored.
UserMailer.registration_confirmation(@user).deliver
And at last created a user_mailer folder in app/views. Again in that created registration_confirmation.html.erb
  Hi <%= @user.name %>, 
  < br />< br />
  == Some Demo Text ==
  < br />< br />
  Thanks For Signing Up ==
  Test App Team
Thats it. We are done with mailer configuration and now just restart your server. For more information go through Action Mailer Basics from Guides Ruby On Rails.