How to mixin some class methods and some instance methods from a module in Ruby?

StackOverflow https://stackoverflow.com/questions/21918056

  •  14-10-2022
  •  | 
  •  

Pregunta

I have a module and would like to mixin some methods as class methods and some as instance methods.

For example:

module Foo
  def self.class_method
  end

  def instance_method
  end
end

class Bar
  include Foo
end

Usage:

Bar.class_method
Bar.new.instance_method

Is it possible to do this in Ruby?

If not, is it possible to define which methods are class methods and which are instance methods within the Bar class?

I don't want the same method defined as both a class and instance method.

¿Fue útil?

Solución

This pattern is very common in Ruby. So common, in fact, that ActiveSupport::Concern abstracts it a bit.

Your typical implementation looks like this:

module Foo
  def self.included(other_mod)
    other_mod.extend ClassMethods
  end

  def instance_method
  end

  module ClassMethods
    def class_method
    end
  end
end

class Bar
  include Foo
end

You can't accomplish this easily as you describe without somehow splitting the included module into multiple pieces, though, unfortunately.

Otros consejos

You can, but not quite like that. This is a common pattern for including both instance and class methods in one module.

module Foo
  def self.included(base)
    base.extend ClassMethods
  end

  def instance_method
    puts 'instance'
  end

  module ClassMethods
    def class_method
      puts 'class'
    end
  end
end

class Bar
  include Foo
end

bar = Bar.new
Bar.class_method    #=> 'class'
bar.instance_method #=> 'instance'

You are close. You probably noticed that the instance method works fine. The problem with the class method is that self => Foo when it's defined, so it does not respond to Bar. If you add the line puts "I'm a module method" in self.class_method, you will find

Foo.class_method => "I'm a module method"

Here's an easy way to accomplish what you want to do:

module Foo_class
  attr_accessor :cat
  def class_method
    puts "I'm a class method" 
  end
end

module Foo_instance
  def instance_method
    puts "I'm an instance method" 
  end
end

class Bar
  extend Foo_class
  include Foo_instance
end

Bar.class_method        #=> I'm a class method
Bar.cat = "meow"
Bar.cat                 #=> "meow"
Bar.new.instance_method #=> I'm an instance method

I added a class instance variable, @cat, and an accessor for it, just to show how easy that is to do.

Object#extend is great, because you can just add instance variables and methods to a module, just as you would do with Object#include to mixin instance variables and methods, and extend mixes them in as class instance variables and class methods. You can also do this:

bar = Bar.new
bar.extend Foo_class

to have the instance variables and methods in Foo_class apply to the instance bar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top