user12882
user12882

Reputation: 4792

How to override methods stated in the scope of a class?

I am using Ruby 1.9.2 and Ruby on Rails 3.2.2. I have following statements:

class Category < ActiveRecord::Base
  include Commentable

  acts_as_list :scope => 'category_comment_id'

  has_many :comments, :class_name => 'CategoryComment'

  # ...
end

module Commentable
  extend ActiveSupport::Concern

  included do
    acts_as_list :scope => 'comment_id'

    has_many :comments, :class_name => 'Comment'

    # Other useful method statements...
  end

  # Other useful method statements...
end

In the above code I am trying to override both acts_as_something and has_many methods added to the Category class by the including Commentable module. Both methods are stated "in the scope of" Category so the above code does not work as expected: methods are not overrode.

Is it possible to override those methods? If so, how?

Upvotes: 2

Views: 2105

Answers (1)

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230561

You should include your module in the end of your class definition. As it is now, methods from the module get injected before the class defines its method. This is so because ruby processes and evaluates code in the top-down manner. Therefore, a little bit later it encounters class' own definitions of the methods and overwrites those that came from the module.

So, use this knowledge according to your intention: who should override whom. If methods from the module should take dominance over those in the class, include it in the end.

Edit

Given this code

require 'active_support/core_ext'

class Base
  def self.has_many what, options = {}
    define_method "many_#{what}" do
      "I am having many #{what} with #{options}"
    end
  end
end

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, class_name: 'Comment'
  end
end

Then

class Foo < Base
  include Commentable
  has_many :comments
end

# class overrides module
Foo.new.many_comments # => "I am having many comments with {}"

And

class Foo < Base
  has_many :comments
  include Commentable
end

# module overrides class 
Foo.new.many_comments # => "I am having many comments with {:class_name=>\"Comment\"}"

Upvotes: 4

Related Questions