Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegating instance methods to the class method

In Ruby, suppose I have a class Foo to allow me to catalogue my large collection of Foos. It's a fundamental law of nature that all Foos are green and spherical, so I have defined class methods as follows:

class Foo
  def self.colour
    "green"
  end

  def self.is_spherical?
    true
  end
end

This lets me do

Foo.colour # "green"

but not

my_foo = Foo.new
my_foo.colour # Error!

despite the fact that my_foo is plainly green.

Obviously, I could define an instance method colour which calls self.class.colour, but that gets unwieldy if I have many such fundamental characteristics.

I can also presumably do it by defining method_missing to try the class for any missing methods, but I'm unclear whether this is something I should be doing or an ugly hack, or how to do it safely (especially as I'm actually under ActiveRecord in Rails, which I understand does some Clever Fun Stuff with method_missing).

What would you recommend?

like image 713
Chowlett Avatar asked Jan 28 '10 16:01

Chowlett


People also ask

Can a class method call an instance method Ruby?

In Ruby, a method provides functionality to an Object. A class method provides functionality to a class itself, while an instance method provides functionality to one instance of a class. We cannot call an instance method on the class itself, and we cannot directly call a class method on an instance.

How do you delegate a method in Ruby?

In Ruby, every object inherits from the Object class by default. That's why you get access to methods like puts , class & object_id . With composition a class creates (or is given) objects from another class… then it uses these objects to delegate work to them.

How do you call a method from an instance of a class?

To call an object's method, you must use the object name and the dot (.) operator followed by the method name, for example object.

What is delegate method in Ruby on Rails?

The delegate method allows you to optionally pass allow_nil and a prefix as well. Ultimately this allows us to query for data in custom ways.


2 Answers

The Forwardable module that comes with Ruby will do this nicely:

#!/usr/bin/ruby1.8

require 'forwardable'

class Foo

  extend Forwardable

  def self.color
    "green"
  end

  def_delegator self, :color

  def self.is_spherical?
    true
  end

  def_delegator self, :is_spherical?

end

p Foo.color                # "green"
p Foo.is_spherical?        # true
p Foo.new.color            # "green"
p Foo.new.is_spherical?    # true
like image 128
Wayne Conrad Avatar answered Sep 29 '22 02:09

Wayne Conrad


If it's plain Ruby then using Forwardable is the right answer

In case it's Rails I would have used delegate, e.g.

class Foo
  delegate :colour, to: :class

  def self.colour
    "green"
  end
end

irb(main):012:0> my_foo = Foo.new
=> #<Foo:0x007f9913110d60>
irb(main):013:0> my_foo.colour
=> "green"
like image 40
Vadym Tyemirov Avatar answered Sep 29 '22 01:09

Vadym Tyemirov