modulos - modulo ruby
¿Mejor manera de convertir una clase de ruby en un módulo que usando refinamientos? (2)
Module#refine
método de Module#refine
toma una clase y un bloque y devuelve un módulo de refinamiento, así que pensé que podría definir:
class Class
def include_refined(klass)
_refinement = Module.new do
include refine(klass) {
yield if block_given?
}
end
self.send :include, _refinement
end
end
y la siguiente prueba pasa
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
Por lo tanto, utilizando mejoras, puedo convertir una clase en un módulo, refinar su comportamiento sobre la marcha e incluirlo en otras clases.
- ¿Hay una forma más sencilla de convertir una clase en un módulo en Ruby (por ejemplo, en ruby <2)?
En la implementación en C de rb_mod_refine vemos
refinement = rb_module_new(); RCLASS_SET_SUPER(refinement, klass);
¿Esto está configurando la superclase de refinamiento en
klass
que copia la implementación de la clase dentro del módulo de refinamiento?- Soy consciente de que la herencia múltiple se realiza a través de módulos, pero ¿qué pensaría la comunidad de la
Class#include_refined
mencionada anteriormente? ¿Sería razonable extraer este aspecto de los refinamientos? ¿Parcheando "localmente" dentro de una Clase en lugar de usar "usar" interruptores para activar refinamientos?
Andrea, gracias por la información en el comentario. Disculpe mi falta de conocimiento para entender que esto es realmente necesario, aunque parece factible según su investigación.
No creo que tengamos que ir a un nivel tan bajo para hacer algo en Rails.
Si voy a hacer algo similar en Engine, probaré las siguientes ideas, de fácil a difícil.
En route.rb, monte todo el motor en la ruta correcta.
Me temo que este uso más común no puede satisfacer su necesidad
En route.rb, personalice la ruta del motor para controladores específicos en la ruta de la aplicación.
La concepción, como motor, puede hacerse fácilmente. Pero sé que no todos los motores podrían hacer esto .
En route.rb, redirigir el conjunto de rutas específicas o completas a las rutas del motor.
En la acción de su aplicación, redirija a la acción específica del motor en la acción de la aplicación.
Esto debería ser lo suficientemente personalizado para una acción específica.
class FoosController < ApplicationController def foo redirect_to some_engine_path if params[:foo] == ''bar'' end
Herede el controlador del motor - para un conjunto de acciones, y si todo lo anterior no encaja
* Las clases del motor están disponibles en todas las aplicaciones, puede heredar un controlador de ellas, en lugar del ApplicationController normal.
# class FoosController < ApplicationController class FoosController < BarEngine::BarsController
* Dado que la mayoría de los controladores del motor heredan de ApplicationController, esta herencia aún le permite usar sus propias cosas de ApplicationController, sin ningún efecto negativo.
Si no se puede hacer todo lo anterior, puedo intentar servir un local personalizado o desde mi repositorio github.
En conclusión, lo anterior debería poder resolver la mayoría de los casos, y yo mismo prefiero el # 5 cuando sea posible y necesario.
Estoy realmente contento con el alcance "privado" de las mejoras de Ruby 2.1 (y más tarde) a nivel de clase. Mi ejemplo anterior se puede reformular como:
# 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
Y se adapta como solución al problema original.