Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending a Ruby class with a standalone piece of code

I have a Rails app with several models with the same structure:

class Item1 < ActiveRecord::Base
  WIDTH = 100
  HEIGHT = 100
  has_attached_file :image, styles: {original: "#{WIDTH}x#{HEIGHT}"}
  validates_attachment :image, :presence => true
end


class Item2 < ActiveRecord::Base
  WIDTH = 200
  HEIGHT = 200
  has_attached_file :image, styles: {original: "#{WIDTH}x#{HEIGHT}"}
  validates_attachment :image, :presence => true
end

The actual code is more complicated but that's enough for simplicity.

I think I can put the common part of the code in one place and then use it in all models.

Here is what comes to my mind:

class Item1 < ActiveRecord::Base
  WIDTH = 100
  HEIGHT = 100
  extend CommonItem
end

module CommonItem
  has_attached_file :image, styles: {original: "#{WIDTH}x#{HEIGHT}"}
  validates_attachment :image, :presence => true
end

Obviously it doesn't work for two reasons:

  1. CommonItem has no idea about class methods I invoke.
  2. WIDTH and HEIGHT constants are looked up in CommonItem instead of Item1.

I tried to use include instead of extend, some ways of class_eval and class inheritance, but none work.

It seems I am missing something obvious. Please tell me what.

like image 683
Ivan Avatar asked Nov 11 '12 16:11

Ivan


1 Answers

Here's how I would do it:

class Model
  def self.model_method
    puts "model_method"
  end
end

module Item
  def self.included(base)
    base.class_eval do
      p base::WIDTH, base::HEIGHT
      model_method
    end
  end
end

class Item1 < Model
  WIDTH = 100
  HEIGHT = 100
  include Item
end

class Item2 < Model
  WIDTH = 200
  HEIGHT = 200
  include Item
end

The included method is called on a module when it's included.

I think I've managed to create a similar structure that your problem has. The module is calling the method inherited by the items classes from Model class.

Output:

100
100
model_method
200
200
model_method
like image 55
Dogbert Avatar answered Sep 28 '22 08:09

Dogbert