Ruby: Calling class method from instance

2020-05-10 19:02发布

In Ruby, how do you call a class method from one of that class's instances? Say I have

class Truck
  def self.default_make
    # Class method.
    "mac"
  end

  def initialize
    # Instance method.
    Truck.default_make  # gets the default via the class's method.
    # But: I wish to avoid mentioning Truck. Seems I'm repeating myself.
  end
end

the line Truck.default_make retrieves the default. But is there a way of saying this without mentioning Truck? It seems like there should be.

9条回答
三岁会撩人
2楼-- · 2020-05-10 19:13

Rather than referring to the literal name of the class, inside an instance method you can just call self.class.whatever.

class Foo
    def self.some_class_method
        puts self
    end

    def some_instance_method
        self.class.some_class_method
    end
end

print "Class method: "
Foo.some_class_method

print "Instance method: "
Foo.new.some_instance_method

Outputs:

Class method: Foo
Instance method: Foo
查看更多
smile是对你的礼貌
3楼-- · 2020-05-10 19:13

Similar your question, you could use:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
查看更多
趁早两清
4楼-- · 2020-05-10 19:14

If you have access to the delegate method you can do this:

[20] pry(main)> class Foo
[20] pry(main)*   def self.bar
[20] pry(main)*     "foo bar"
[20] pry(main)*   end  
[20] pry(main)*   delegate :bar, to: 'self.class'
[20] pry(main)* end  
=> [:bar]
[21] pry(main)> Foo.new.bar
=> "foo bar"
[22] pry(main)> Foo.bar
=> "foo bar"

Alternatively, and probably cleaner if you have more then a method or two you want to delegate to class & instance:

[1] pry(main)> class Foo
[1] pry(main)*   module AvailableToClassAndInstance
[1] pry(main)*     def bar
[1] pry(main)*       "foo bar"
[1] pry(main)*     end  
[1] pry(main)*   end  
[1] pry(main)*   include AvailableToClassAndInstance
[1] pry(main)*   extend AvailableToClassAndInstance
[1] pry(main)* end  
=> Foo
[2] pry(main)> Foo.new.bar
=> "foo bar"
[3] pry(main)> Foo.bar
=> "foo bar"

A word of caution:

Don't just randomly delegate everything that doesn't change state to class and instance because you'll start running into strange name clash issues. Do this sparingly and only after you checked nothing else is squashed.

查看更多
可以哭但决不认输i
5楼-- · 2020-05-10 19:17
self.class.default_make
查看更多
唯我独甜
6楼-- · 2020-05-10 19:17

One more:

class Truck
  def self.default_make
    "mac"
  end

  attr_reader :make

  private define_method :default_make, &method(:default_make)

  def initialize(make = default_make)
    @make = make
  end
end

puts Truck.new.make # => mac
查看更多
叛逆
7楼-- · 2020-05-10 19:22

Here's an approach on how you might implement a _class method that works as self.class for this situation. Note: Do not use this in production code, this is for interest-sake :)

From: Can you eval code in the context of a caller in Ruby? and also http://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

# Rabid monkey-patch for Object
require 'continuation' if RUBY_VERSION >= '1.9.0'
class Object
  def __; eval 'self.class', caller_binding; end
  alias :_class :__
  def caller_binding
    cc = nil; count = 0
    set_trace_func lambda { |event, file, lineno, id, binding, klass|
      if count == 2
        set_trace_func nil
        cc.call binding
      elsif event == "return"
        count += 1
      end
    }
    return callcc { |cont| cc = cont }
  end
end

# Now we have awesome
def Tiger
  def roar
    # self.class.roar
    __.roar
    # or, even
    _class.roar
  end
  def self.roar
    # TODO: tigerness
  end
end

Maybe the right answer is to submit a patch for Ruby :)

查看更多
登录 后发表回答