Forum: Rails-ES Intentando sobrescribir métodos de un c ontrolador

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
990cde3e04aa0821816c03b0af28f76b?d=identicon&s=25 Luis Mayoral (Guest)
on 2009-01-02 21:26
(Received via mailing list)
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".

2. 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.

3. 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 :/

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 ;)

Salu2
49b6123803e4f327144e991daab62f77?d=identicon&s=25 Daniel Rodriguez Troitiño (Guest)
on 2009-01-02 23:54
(Received via mailing list)
2009/1/2 Luis Mayoral <luis@luismayoral.com>:
>
>      render :text => "TestController original"
>  module ExtendedTestController
>
>      base.class_eval do
>
>
> Grasias de antebrazo... digo... gracias de antemano ;)
>
> 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.
F625b891618be8ec32547a07b3192bb0?d=identicon&s=25 Francesc Esplugas (fesplugas)
on 2009-01-03 10:35
(Received via mailing list)
Y heredar de otro controlador con los metodos comunes no te sirve?
990cde3e04aa0821816c03b0af28f76b?d=identicon&s=25 Luis Mayoral (Guest)
on 2009-01-03 12:37
(Received via mailing list)
El 03/01/2009, a las 10:35, Francesc Esplugas 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
990cde3e04aa0821816c03b0af28f76b?d=identicon&s=25 Luis Mayoral (Guest)
on 2009-01-03 15:13
(Received via mailing list)
_______________________________________________
Ror-es mailing list
Ror-es@lists.simplelogica.net
http://lists.simplelogica.net/mailman/listinfo/ror-es
0e34c56054c414263e933a1b8b3d0d55?d=identicon&s=25 Ruben Davila (rdavila)
on 2009-01-03 16:32
(Received via mailing list)
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:

http://www.scribd.com/doc/2217773/Design-Patterns-in-Ruby
http://www.scribd.com/doc/396559/gof-patterns-in-ruby


Saludos.
49b6123803e4f327144e991daab62f77?d=identicon&s=25 Daniel Rodriguez Troitiño (Guest)
on 2009-01-03 23:45
(Received via mailing list)
2009/1/3 Luis Mayoral <luis@luismayoral.com>:
>
> Voy a ver si puedo aplicar algo parecido a
> esto: http://scie.nti.st/2008/9/17/making-methods-immuta...
> Si no, aunque no tan "bonito", tu solución de poner el include al final
> funciona perfectamente :)

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 :P
> Por el momento he localizado este libro:
> http://www.amazon.com/Design-Patterns-Ruby-Addison...
> ¿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.
0e34c56054c414263e933a1b8b3d0d55?d=identicon&s=25 Ruben Davila (rdavila)
on 2009-01-04 00:22
(Received via mailing list)
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.
This topic is locked and can not be replied to.