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

Лучший способ превратить класс ruby ​​в модуль, чем использовать уточнения?

Module#refine метод принимает класс и блок и возвращает модуль уточнения, поэтому я думал, что могу определить:

class Class
  def include_refined(klass)
    _refinement = Module.new do
      include refine(klass) {
        yield if block_given?
      }
    end
    self.send :include, _refinement
  end
end

и следующие тестовые проходы

class Base
  def foo
    "foo"
  end
end

class Receiver
  include_refined(Base) {
    def foo
      "refined " + super
    end
  }
end

describe Receiver do
  it { should respond_to(:foo) }
  its(:foo) { should eq("refined foo") }
end

Итак, используя уточнения, я могу превратить класс в модуль, уточнить его поведение "на лету" и включить его в другие классы.

  • Есть ли более простой способ превратить класс в модуль в Ruby (скажем, в ruby ​​< 2)?
  • В C-реализации rb_mod_refine мы видим

    refinement = rb_module_new();
    RCLASS_SET_SUPER(refinement, klass);
    

    Это просто настройка суперкласса уточнения на klass, который копирует реализацию класса внутри модуля уточнения?

  • Я знаю, что множественное наследование IS сделанные с помощью модулей, но что подумает сообщество об этом выше Class#include_refined? Было бы разумным извлечь этот аспект из уточнений? "Локально" исправление внутри класса вместо использования "использования" переключателей для активации уточнений?
4b9b3361

Ответ 1

Я рад, конечно, с областями Ruby 2.1 (и более поздних версий) класса "private". Мой пример выше можно перефразировать как:

# spec/modulify_spec.rb
module Modulify
  refine(Class) do
    def include_refined(klass)
      _refined = Module.new do
        include refine(klass) { yield if block_given? }
      end
      include _refined
    end
  end
end

class A
  def a
    "I am an 'a'"
  end
end

class B
  using Modulify

  include_refined(A) do
    def a
      super + " and not a 'b'"
    end
  end

  def b
    "I cannot say: " + a
  end
end

RSpec.describe B do
  it "can use refined methods from A" do
    expect(subject.b).to eq "I cannot say: I am an 'a' and not a 'b'"
  end
end

и подходит для решения исходной задачи.

Ответ 2

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

Я не думаю, что нам нужно идти на такой низкий уровень, чтобы что-то делать в Rails.

Если я собираюсь сделать подобное на Engine, я попробую следующие идеи: от простого до сложного.

  • В routes.rb установите весь движок в правильном направлении.

    Я боюсь, что это наиболее распространенное использование не может соответствовать вашим потребностям

  • В routes.rb, Настройте маршрут двигателя для определенных контроллеров в маршруте приложения.

    Разработать, как движок, можно легко. Но я знаю, что не каждый двигатель мог это сделать.

  • В route.rb перенаправляет определенный или весь набор маршрутов на маршруты двигателей

  • В действии вашего приложения перенаправление на конкретное действие с движком в действии приложения.

    Это должно быть настроено достаточно для конкретного действия

    class FoosController < ApplicationController
      def foo
        redirect_to some_engine_path if params[:foo] == 'bar'
      end
    
  • Наследовать контроллер двигателя - для набора действий, и если все выше не может соответствовать

    * Классы двигателей доступны во всех приложениях, вы можете наследовать от них контроллер, а не обычный ApplicationController.

    # class FoosController < ApplicationController
    class FoosController < BarEngine::BarsController
    

    * Поскольку большинство контроллеров двигателей наследуются от ApplicationController, это наследование по-прежнему позволяет вам использовать свои собственные вещи из ApplicationController, вообще не плохо.

  • Если все вышеизложенное не может сделать, я могу попытаться выполнить настройку локально или из моего репозитория github.

В заключение вышесказанное должно быть в состоянии решить большинство случаев, и я сам предпочитаю # 5, когда это возможно и необходимо.