Лучший способ превратить класс 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
?
Было бы разумным извлечь этот аспект из уточнений?
"Локально" исправление внутри класса вместо использования "использования" переключателей для активации уточнений?
Ответы
Ответ 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, когда это возможно и необходимо.