Подтвердить что ты не робот

Как вложенный класс может получить доступ к методу во внешнем классе в Ruby?

def class A
  def a
    raise "hi" #can't be reached
  end

  class B
    def b
      a() #doesn't find method a.
    end
  end
end

Я хочу вызвать a из b и поднять исключение. Как я могу?

4b9b3361

Ответ 1

Ruby не имеет вложенных классов.

Единственный способ унаследовать поведение - это, ну, через наследование.

Если вы хотите, чтобы ваш код работал, вам нужно использовать язык, который поддерживает вложенные классы. Хотя это невероятно опрятная и мощная функция, я, к сожалению, знаю только два языка с вложенными классами:

  • BETA, язык, на котором вводились вложенные классы (и его преемник gbeta)
  • Newspeak

Я не знаю другого.

Java имеет конструкцию с именами вложенных классов, но у них есть некоторые неудачные конструктивные ограничения.

В приведенном выше примере не вложенный внутри A класс B, это константа B, вложенная внутри A. Подумайте об этом:

C = A::B

Теперь класс доступен под двумя именами: A::B и C. Непосредственно очевидно, что C является глобальным и не вложен внутри A. (Ну, на самом деле, C вложен внутри Object, потому что на самом деле также нет глобальных констант, но это рядом с точкой.) Но так как C и A::B являются одним и тем же классом, это, очевидно, не может быть как вложенные, так и не вложенные. Единственный логический вывод состоит в том, что сам класс не вложен.

Определяющая особенность вложенных классов заключается в том, что поиск метода проходит по двум измерениям: вверх по цепочке наследования, а также через вложенность. Ruby, как и 99,9% всех языков OO, поддерживает только первый. (В некотором смысле вложенные классы наследуют не только функции своего суперкласса, но также и функции их окружающего класса.)

Ответ 2

Это только для lulz:

class A
  def a
    puts "hello from a"
  end

  class B
    def b
      Module.nesting[1].new.a()
    end
  end
end

Ответ 3

Я обычно делаю что-то вроде этого:

class A
  def a
    puts "hi"
  end

  def createB
    B.new self
  end

  class B
    def initialize(parent)
      @parent=parent
    end

    def b
      @parent.a
    end
  end
end

A.new.createB.b

Ответ 4

Если вы хотите, чтобы вложенный класс расширил внешний класс, сделайте следующее:

class Outer

  class Inner < Outer
    def use_outer_method
      outer_method("hi mom!")
    end
  end

  def outer_method(foo)
    puts foo
  end

end

foo = Outer::Inner.new
foo.use_outer_method        #<= "hi mom"
foo.outer_method("hi dad!") #<= "hi dad"

Ответ 5

Хорошо, в зависимости от ваших обстоятельств есть на самом деле решение, довольно легкое в этом. Ruby позволяет захватывать вызовы методов, которые не захватываются объектом. Итак, для вашего примера вы можете сделать:

def class A
  def a
    raise "hi" #can't be reached
  end

  class B
    def initialize()
      @parent = A.new
    end

    def b
      a() #does find method a.
    end

    def method_missing(*args)
      if @parent.respond_to?(method)
        @parent.send(*args)
      else
        super
      end
    end
  end
end

Итак, если вы это сделаете:

A::B.new().b

вы получаете:

!! #<RuntimeError: hi>

Это, вероятно, более простой способ сделать что-то вроде SubController, который обрабатывает только определенные действия, но может легко вызвать базовые методы контроллера (вы хотите отправить родительский контроллер в качестве аргумента в инициализаторе, хотя).

Очевидно, что это следует использовать экономно, и это может действительно запутать, если вы используете его повсюду, но может быть очень полезно упростить ваш код.

Ответ 6

Предполагалось ли a быть метод класса для класса a?

class A
  def self.a
    raise "hi"
  end
  class B
    def b
      A::a 
    end
  end
end

A::B.new.b

Если вы хотите сохранить его как метод экземпляра, у вас, очевидно, будет обращение к нему на экземпляр, например, A.new.a.