user12882
user12882

Reputation: 4792

How to refactor model classes implementing similar methods?

I am using Ruby on Rails 3.2.9 and Ruby 1.9.3. I have many model classes implementing similar methods as-like the following:

class ClassName_1 < ActiveRecord::Base
  def great_method
    self.method_1
  end

  def method_1 ... end
end

class ClassName_2 < ActiveRecord::Base
  def great_method
    result_1 = self.method_1
    result_2 = self.method_2

    result_1 && result_2
  end

  def method_1 ... end
  def method_2 ... end
end

...

class ClassName_N < ActiveRecord::Base
  def great_method
    result_1 = self.method_1
    result_2 = self.method_2
    ...
    result_N = self.method_N

    result_1 && result_2 && ... && result_N
  end

  def method_1 ... end
  def method_2 ... end
  ...
  def method_N ... end      
end

Those model classes behaves almost the same (not the same) since some of those has an interface with some less or more methods. All methods are differently named (for instance, method_1 could be named bar and method_2 could be named foo), all return true or false, are always the same in each class and there is no relation between them.

What is the proper way to refactor those classes?


Note: At this time I am thinking to refactor classes by including the following module in each one:

module MyModule
  def great_method
    result_1 = self.respond_to?(:method_1) ? self.method_1 : true
    result_2 = self.respond_to?(:method_2) ? self.method_2 : true
    ...
    result_N = self.respond_to?(:method_N) ? self.method_N : true

    result_1 && result_2 && ... && result_N
  end
end

But I don't know if it is the proper way to accomplish what I am looking for. Furthermore, I am not sure of related advantages and disadvantages...

Upvotes: 0

Views: 821

Answers (3)

Finbarr
Finbarr

Reputation: 32126

I would use a metaprogramming solution to clean this up somewhat.

module BetterCode
  extend ActiveSupport::Concern

  module ClassMethods
    def boolean_method(name, *components)
      define_method name do
        components.all? { |c| send c }
      end
    end
  end
end

And in your models:

class MyModel < ActiveRecord::Base
  include BetterCode

  boolean_method :great_method, :foo, :bar, :baz, :quux
end

Instances of MyModel will then respond to great_method with a boolean value indicating whether or not foo, bar, baz and quux are all true.

Upvotes: 1

Ben Taitelbaum
Ben Taitelbaum

Reputation: 7403

You can abstract out the great_method with something like this:

require 'active_support/concern'

module Greatest
  extend ActiveSupport::Concern

  module ClassMethods
    attr_accessor :num_great_methods

    def has_great_methods(n)
      @num_great_methods = n
    end
  end

  def great_method
    (1..self.class.num_great_methods).each do |n|
      return false unless self.__send__("method_#{n}")
    end
    true
  end
end

class ClassName_3
  include Greatest

  has_great_method 3

  # stub out the "method_*" methods
  (1..3).each do |n|
    define_method "method_#{n}" do
      puts "method_#{n}"
      true
    end
  end
end

puts ClassName_1.new.greatest

Upvotes: 0

Dave Riess
Dave Riess

Reputation: 21

Looks like you're on the right track. If the method_n methods are unique to your classes then just build the module that you already have into a superclass that each ClassNameN inherits from:

class SuperClassName < ActiveRecord::Base
  def great_method
    #... what you have in your module
  end
end

class ClassNameN < SuperClassName
  def method_1 ... end
  def method_2 ... end
end

There may be additional ways for you to factor out code depending on what goes on in your method_n methods, but it's impossible to say without more detail.

Upvotes: 1

Related Questions