Saturday, 17 January 2015

Ruby2 :Private v/s Protected Methods

Recently someone asked me the difference between private method and protected methods in Ruby.  It was then, that it dawned me that though I have used private methods a lot many times in Ruby, I have never used protected methods. What's more, I was not even quite sure on the difference between the two in Ruby context. So I decided to dig into it and here is my little post on what I understood with examples :)

Private methods in Ruby:

If a method is private in Ruby, then it cannot be called by an explicit receiver (object). It can only be call implicitly. It can be called implicitly by the class in which it has been described in as well as by the subclasses of this class.
The following examples will illustrate it better:
1) A Animal class with private method class_name
class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end
In this case:
n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>private method `class_name' called
2) A subclass of Animal called Amphibian:
class Amphibian << Animal
  def intro_amphibian
    class_name
  end 
end 
In this case:
  n= Amphibian.new
  n.intro_amphibian #=>I am a Amphibian
  n.class_name #=>private method `class_name' called
As you can see, private methods can be called only implicitly. They cannot be called by explicit receivers. For the same reason, private methods cannot be called outside the hirearchy of the defining class.

Protected Methods in Ruby:

If a method is protected in Ruby, then it can be called implicitly by both the defining class and its subclasses. Additionally they can also be called by an explicit receiver as long as the receiver is self or of same class as that of self:
1) A Animal class with proteced method protect_me
class Animal
  def animal_call
    protect_me
  end
  protected
  def protect_me
    p "protect_me called from #{self.class}"
  end  
end
In this case:
n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>protected method `protect_me' called
2) A mammal class which is inherited from animal class
class Mammal < Animal
  def mammal_call
    protect_me
  end
end 
In this case
n= Mammal.new
n.mammal_call #=> protect_me called from Mammal
3) A amphibian class inherited from Animal class (same as mammal class)
class Amphibian < Animal
  def amphi_call
    Mammal.new.protect_me #Receiver same as self
    self.protect_me  #Receiver is self
  end   
end
In this case
n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
             #=> protect_me called from Amphibian  
4) A class called Tree
class Tree
  def tree_call
    Mammal.new.protect_me #Receiver is not same as self
  end
end
In this case:
n= Tree.new
n.tree_call #=>protected method `protect_me' called for #<Mammal:0x13410c0>





Monday, 24 November 2014

Rails4: joins v/s includes

I was recently reading more on difference between :joins and :includes in rails. Here is an explanation of what I understood (with examples :))
Consider this scenario: A User has_many comments and a comment_belongs to a User. The User model has the following attributes: Name(string), Age(integer), Admin(boolean). The Comment model has the following attributes:Content, user_id.

Joins:

:joins performs a inner join between two tables. Thus
Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>
will fetch all records where user_id (of comments table) is equal to user.id (users table). Thus if you do
Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>
You will get a empty array as shown.
Moreover joins does not load the joined table in memory. Thus if you do
comment_1 = Comment.joins(:user).first

comment_1.user.age
#=>←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m  [["id", 1]]
#=> 24
As you see, comment_1.user.age will fire a database query again in the background to get the results

Includes:

:includes performs a left outer join between the two tables. Thus
Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>
will result in a joined table with all the records from comments table. Thus if you do
Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>
it will fetch records where comments.user_id is nil as shown.
Moreover includes loads both the tables in the memory. Thus if you do
comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24
As you can notice comment_1.user.age simply loads the result from memory without firing a database query in the background.

Tuesday, 16 September 2014

Concerns in Rails Part 2 : Using concerns in Routes

With Rails 4, the concept of using concerns has been in highlights. Concerns can be used in routing to DRY up your routes.rb file.
Consider the following example: Consider an Events, a Articles, a Photos and a Comments controller. A event , photo and a article has_many comments. A comment belongs_to either a event, a article or a photo.
Typically, the route.rb file will have nested resourcing and may look like this:
  Rails.application.routes.draw do
      resources :events do
         resources :comments
     end
      resources :articles do
         resources :comments
     end
     resources :photos  do
         resources :comments
     end
   end
Using Concerns you can DRY up you routes.rb  as shown below:

 Rails.application.routes.draw do
   concern :commentable do
     resources :comments
   end
   resources :articles,events, photos, concerns: :commentable 
 end

Concerns in Rails Part 1 : Using concerns in Models

With Rails 4, the concept of using concerns has been in highlights. Concerns provide a easy yet powerful technique to group similar code of a model or across multiple models  in a module (that extends ActiveSupport::Concern ). This concern module is then included in the required models. 

Concerns in models can be used both for 
1) DRYing up model codes (by domain based grouping of common codes between models )
2) Skin-nizing Fat Models (by dividing the code based on domains and creating a concern for each domain.)

1) DRYing up model codes
Consider a Article model, a Event model and a Comment Model. A article or A event has many comments. A comment belongs to either article or event.
Traditionally, the models may look like this:
Comment Model:
class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end
Article Model:
class Article < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #return the article with least number of comments
  end
end
Event Model
class Event < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #returns the event with least number of comments
  end
end
As we can notice, there is a significant piece of code common to both Event and Article Model. Using concerns we can extract this common code in a separate module Commentable.
For this create a commentable.rb file in app/model/concerns.
module Commentable
    extend ActiveSupport::Concern

    included do 
        has_many :comments, as: :commentable 
    end

    # for the given article/event returns the first comment
    def find_first_comment
        comments.first(created_at DESC)
    end

    module ClassMethods     
        def least_commented
           #returns the article/event which has the least number of comments
        end
    end 
end

As you can see while using concerns:

     * The included block is executed within the context of the class that is including the module.
     *  All the class methods go into the module ClassMethods of the concern (no need to add
        'self.' to these methods).
     * The instance methods are written directly in the concern module.   

And Now your models look like this :
Comment Model:
    class Comment < ActiveRecord::Base
      belongs_to :commentable, polymorphic: true
    end
Article Model:
class Article < ActiveRecord::Base
    include Commentable
end
Event Model:
class Event < ActiveRecord::Base    
    include Commentable
end

2) Skin-nizing Fat Models.

Consider a Event model. A event has many notes and comments.Typically, the event model might look like this:
 class Event < ActiveRecord::Base   
    has_many :comments
    has_many :attenders


    def find_first_comment
        # for the given article/event returns the first comment
    end

    def find_comments_with_word(word)
        # for the given event returns an array of comments which contain the given word
    end 

    def self.least_commented
        # finds the event which has the least number of comments
    end

    def self.most_attended
        # returns the event with most number of attendes
    end

    def has_attendee(attendee_id)
        # returns true if the event has the mentioned attendee
    end
end
Models with many associations and otherwise have tendency to accumulate more and more code and become unmanageable.Concerns provide a way to skin-nize fat modules making them more modularized and easy to understand.
The above model can be refactored using concerns as below: Create a attendable.rd and commentable.rb file in app/model/concern/event folder
attendable.rb
module Attendable
    extend ActiveSupport::Concern

    included do 
        has_many :attenders
    end

    def has_attender(attender_id)
        # returns true if the event has the mentioned attendee
    end

    module ClassMethods
      def most_attended
        # returns the event with most number of attendes
      end
    end
end
commentable.rb
module Commentable
    extend ActiveSupport::Concern

    included do 
        has_many :comments
    end

    def find_first_comment
        # for the given article/event returns the first comment
    end

    def find_comments_with_word(word)
        # for the given event returns an array of comments which contain the given word
    end   

    module ClassMethods
      def least_commented
        # finds the event which has the least number of comments
      end
    end
end
And now using the above Concerns , your Event model reduces to
class Event < ActiveRecord::Base    
    include Commentable
    include Attendable
end


* It is advisable to make concerns domain based rather than technical based. For instance,  concerns like 'Commentable', 'Photoable' are examples of domain based concerns. While concerns like 'FinderMethods', 'ValidationMethods' are examples of technically grouped concerns.


Here are links of some write ups for Model Concerns that I found very useful :