Intentando sobrescribir métodos de un c ontrolador

Buenas a todos.

Llevo varios días intentando encontrar la manera de que a través de la
inclusión de un módulo pueda ser capaz de sobrescribir determinados
métodos de un controlador.

Hasta ahora he intentado atacar el problema por tres caminos distintos:

  1. include normal y corriente
    =============================

    module ExtendedTestController
    def test
    render :text => “TestController de instancia”
    end
    end

    class TestController < ApplicationController
    include ExtendedTestController

    def test
    render :text => “TestController original”
    end
    end

Si ejecutáis este fragmento de código os saldrá en el navegador
“TestController original”. Si elimináis el método test de
TestController os saldrá “TestController de instancia”.

  1. extender la clase
    ====================

    module ExtendedTestController
    def included(base)
    base.extend(ClassMethods)
    end

    module ClassMethods
    def test
    render :text => “TestController de clase”
    end
    end
    end

Con el TestController original saldría el texto de “TestController
original”, es decir, que “ignora” a los métodos de clase. Si
eliminásemos el método test de TestController directamente saltaría la
vista que Rails consiguiese encontrar, o el error avisándote de que no
existe la vista.

  1. alias_method_chain
    =====================

    module ExtendedTestController
    def included(base)
    base.class_eval do
    alias_method_chain :test, :changes
    end
    end

    def test_with_changes
    render :text => “TestController de instancia”
    end
    end

Igual que con 2. no funciona.

Ya no se me ocurre qué más probar :confused:

Por el momento lo más cercano a la solución es el punto 1, que es la
que estoy utilizando, pero eso me obliga a que en todos los módulos
tenga que definir los mismos métodos, y lo que ando buscando es
precisamente poder tener en la clase original los métodos “base” y que
si incluyo el módulo X solo sobrescriba el método foo y si incluyo el
módulo Y solo cambie el método bar. Y es que el incluir el módulo X o
el módulo Y vendrá dado en función de una constante definida, que para
cada dominio donde se ejecute la aplicación será distinta.

Grasias de antebrazo… digo… gracias de antemano :wink:

Salu2

2009/1/2 Luis M. [email protected]:

 render :text => "TestController original"

module ExtendedTestController

 base.class_eval do

Grasias de antebrazo… digo… gracias de antemano :wink:

Salu2

El problema que tienes es que un módulo “incluido” aparece después de
la “clase padre” en la lista de ancestros… esto… más claro:

TestController.ancestors
=> [TestController, ExtendedTestController, Object, Kernel]

(bueno, falta ahí el ApplicationController y todos sus ancestros, pero
se me entiende ¿no?)

Cuando Ruby intenta encontrar el método “test” busca en esa lista en
orden, por lo que el primero que encuentra es el de TestController.

Para solucionarlo puedes hacer uso de remove_method y quizá de
method_added:


module ExtendedTestController
def self.included(base)
base.class_eval do
remove_method :test
end
end

def test
puts “ExtendedTestController”
end
end

class TestController < ApplicationController
def test
puts “TestController”
end

include ExtendedTestController
end

Como ves el “include ExtendedTestController” va a final de la clase
para que pueda eliminar el método ya definido. Si quieres que pueda ir
en la parte superior, como te digo, tendrás que comprobar si el método
existe y eliminarlo o capturar las posibles definiciones de nuevos
métodos y evitar las de los métodos que te interesen.

Todos los métodos interesantes se encuentrar en la “clase” Module
http://apidock.com/ruby/Module.

Sin embargo, debo avisar que utilizar los módulos de esta manera puede
inducir a confusión: otros programadores Ruby no esperan que un módulo
sobreescriba los métodos de la clase, por lo que deberías dejar claro
lo que va a suceder si tu módulo es incluido.

De cualquier forma tu problema me parece que se resolvería mucho más
sencillamente con un patrón estrategia
http://en.wikipedia.org/wiki/Strategy_pattern, con una estrategia
por defecto y las estrategias para los métodos foo y bar (que bien
pensado incluso puedes anidar para sustituir los dos).

Suerte.

Y heredar de otro controlador con los metodos comunes no te sirve?

El 03/01/2009, a las 10:35, Francesc E. escribió:

Y heredar de otro controlador con los metodos comunes no te sirve?

Es que realmente no hay métodos comunes…

La intención final de todo esto es que a un CMS que desarrollo pueda
de forma fácil hacer personalización de acciones de controladores en
base al dominio o grupo de dominios desde el que se está ejecutando el
CMS. Todos los grupos de dominio utilizan la misma aplicación desde el
mismo repositorio, pero cada uno desde un directorio diferente (/var/
www/grupo1, /var/www/grupo2, etc).

De esta manera puedo tener en el repositorio cosas tipo:

app/customizations/grupo1/controllers/foo_controller.rb
app/customizations/grupo1/controllers/bar_controller.rb
app/customizations/grupo2/controllers/foo_controller.rb
app/customizations/grupo3/controllers/baz_controller.rb

Y luego una función que pongo en la primera línea de los controladores
tal que:

load_customizations_for :name => ‘Foo’, :type => :controller

Que ya se encarga de detectar si el fichero existe, y en caso de que
sí hacer el require e incluir el módulo que tiene como nombre
Grupo1FooController.

A lo mejor en foo_controller de grupo1 lo único que hago es cambiar
una cosa del def index y en grupo2 foo_controller cambia algo de otro
método completamente distinto.

Salu2


Ror-es mailing list
[email protected]

Hola Luis yo estoy leendo actualmente el Head First Design Patterns, es
una
joya como todos los libros de esa serie y aunque los ejemplos estan en
Java
lo que vale es comprender la intencion de los patrones y aprender los
buenos
principios de diseño, despues de terminar un capitulo examino su
implementacion en Ruby(que por cierto es muy sencilla) con la ayuda de
estos
documentos:

Saludos.

Hola Daniel, tienes mucha razón, varios de los ejemplos de los
documentos
que envie estan mal o de alguna manera incompletos lamentablemente, por
eso
es importante comprender bien la intencion del patrón, en cuanto al
libro
del Head First, si que es muy bueno pero es un poco extenso.

Saludos.

2009/1/3 Luis M. [email protected]:

Voy a ver si puedo aplicar algo parecido a
esto: http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
Si no, aunque no tan “bonito”, tu solución de poner el include al final
funciona perfectamente :slight_smile:

Si mal no entiendo ese artículo explica como evitar que se abra de
nuevo una clase y se redefinan los métodos. Desconozco si “include” se
realizará las llamadas a “method_added” necesarias para que funcione
“inmutable”. De ser así, creo que te serviría.

Con esto ya me has dado una idea para estos Reyes :stuck_out_tongue:
Por el momento he localizado este libro:
http://www.amazon.com/Design-Patterns-Ruby-Addison-Wesley-Professional/dp/0321490452
¿Conoces alguno mejor, o que complemente a ese?
Gracias de nuevo.

Desconozco ese libro. El outline parece bueno, pero algunas de las
cosas que he leido en el sitio web del libro me parecen no muy
correctas. Pero es que cada uno tiene su visión de los patrones de
diseño.

El Libro sobre los patrones de diseño es, obviamente, el escrito
original http://www.amazon.com/dp/0201633612/. Lo que pasa es que es
menos “práctico” y los ejemplos de código vienen normalmente en C++ o
Smalltalk. Siempre he oido cosas buenas sobre el “Head First”, pero a
mí me da cosa la portada.

Sobre las referencias que envia Ruben en otro correo me parecen que
son mucho código y les falta la teoría. Además muchos de los patrones
de diseño se “traducen” mal a Ruby (principalmente porque algunos
lenguajes no necesitan ciertos patrones) y los ejemplos están forzados
(o incluso mal, como el primero que he visto del documento gof
patterns in ruby).

Suerte.