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

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

  •  14-10-2022
  •  | 
  •  

문제

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.

도움이 되었습니까?

해결책

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.

다른 팁

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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top