Skip to content

Deal with method_missing #422

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
HoneyryderChuck opened this issue Oct 16, 2020 · 3 comments
Open

Deal with method_missing #422

HoneyryderChuck opened this issue Oct 16, 2020 · 3 comments

Comments

@HoneyryderChuck
Copy link
Contributor

Will there be a way to deal with overriding method_missing? For instance, if I delegate the method calls to an instance variable, I'd like to "inherit" this object's API:

def method_missing(meth, *args)
  if @obj.respond_to?(meth)
    @obj.__send__(meth,  *args)
  else
    super
  end
end
@soutaro
Copy link
Member

soutaro commented Oct 17, 2020

There is no good support for #method_missing. If you have a set of methods available in the class, you can write method type definitions for them even if it is implemented by #method_missing.

You can include an interface to declare a class has a set of methods.

interface _FooBar
  def foo: () -> void
  def bar: () -> void
end

class FooBarBaz
  include _FooBar           # Equivalent to writing defs for #foo and #bar.

  def baz: () -> void
end

@HoneyryderChuck
Copy link
Contributor Author

I can, but that doesn't look very DRY. Looking at my example, if @obj is an array for instance; Array definitions are done in a class, not an interface. So in order for it to work for me, I'd have to "repeat" the Array class definitions in interface.

An alternative could be for rbs to allow inclusion of classes, or even a DSL method to extract a class interface:

# something like:
classFooBarBaz
  include Array.interface

@KieranP
Copy link

KieranP commented Nov 27, 2024

Have just run into this same issue but mine is a bit more complicated.

I'm making a game and have a subclass system like this:

module Entities
  class Base
    attr_reader :model
  
    def respond_to_missing?(name)
      @model&.respond_to?(name)
    end
  
    def method_missing(...)
      @model&.send(...)
    end
  end
  
  class Player < Base
    def initialize
      @model = ::Models::Player.new
    end
  end
  
  class Chest < Base
    def initialize
      @model = ::Models::Inventory.new
    end
  end
end

module Models
  class Base
  end

  class Player < Base
    attr_reader :x, :y, :inventory

    def initialize
      @x = 123
      @y = 321
      @inventory = ::Models::Inventory.new
    end
  end
  
  class Inventory < Base
    attr_reader :items

    def initialize
      @items = [...]
    end
  end
end

player = ::Entities::Player.new
player.x
player.y

chest = ::Entities::Chest.new
chest.items

The idea is that you deal with Entity classes, backed by models. The problem is that the method_missing part can't be typed with RBS, so steep is complaining when referring the player model x/y variables via player entity x/y method calls.

The way around it that I'm currently doing is to add attr_reader RBS declarations to the Player and Chest entity classes, but this means I am duplicating all attributes. If I had 100 attributes on the player model class, I would need 100 attribute readers on player entity class.

It would be nice, like @HoneyryderChuck mentioned, to be able to include a classes methods into another class in RBS. e.g.

module Entities
  class Player < Base
    include ::Models::Player.instance_methods
  end
end

Would be great to have something official for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants