2012-04-27 30 views
6

Tengo una situación en mi aplicación Rails donde necesito incluir módulos arbitrarios dependiendo del estado actual del tiempo de ejecución. El módulo proporciona código de aplicación personalizado que solo es necesario cuando ciertas condiciones son verdaderas. Básicamente, estoy tirando el nombre de una empresa a partir del contexto actual y el uso que, como el nombre del archivo para el módulo y su definición:Ruby: incluya un nombre de módulo dinámico

p = self.user.company.subdomain + ".rb" 
if File.exists?(Rails.root + "lib/" + p) 
include self.class.const_get(self.user.company.subdomain.capitalize.to_sym) 
    self.custom_add_url 
end 

Mi módulo de prueba es el siguiente:

module Companyx 
    def custom_add_url 
    puts "Calling custom_add_url" 
end 
end 

Ahora en la consola, esto realmente funciona bien. Puedo tirar de un usuario e incluir el módulo de esta manera:

[1] pry(main)> c = Card.find_by_personal_url("username") 
[2] pry(main)> include c.class.const_get(c.user.company.subdomain.capitalize)=> Object 
[3] pry(main)> c.custom_add_url 

Calling custom_add_url

Si trato de ejecutar la línea de incluir de mi modelo, consigo

NoMethodError: undefined method `include' for #<Card:0x007f91f9094fb0> 

¿Puede alguien sugerir qué la instrucción include funcionaría en la consola, pero no en mi código modelo?

Respuesta

5

Incluir es un método en una clase.

Si desea llamarlo dentro de un modelo, debe ejecutar el código en el contexto de su clase singleton.

p = self.user.company.subdomain + ".rb" 
if File.exists?(Rails.root + "lib/" + p) 
myself = self 
class_eval do 
    include self.const_get(myself.user.company.subdomain.capitalize.to_sym) 
end 
self.custom_add_url 

EDIT:

clase < < auto no acepta un bloque; class_eval lo hace, por lo tanto, conserva el estado de las variables locales. He modificado mi solución para usarla.

+0

Cambiar el contexto parece agregar una complicación: el autoobjeto deja de existir (o más exactamente, su asociación con el objeto del usuario sí lo hace)? He intentado varios métodos para evitar esto y leer este artículo para entender lo que está sucediendo aquí (http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/) sin éxito. –

+0

Oh, mi mal. Básicamente, el yo en el nuevo contexto es la clase. Es similar a hacer una evaluación de clase. Hice algo así como un experimento en un proyecto personal. La parte superior de este archivo debería ayudar: https://github.com/Hitonagashi/UndergroundFootball/blob/master/app/models/player.rb –

+0

No estoy seguro de entender: creo que estás hablando sobre el método de inicialización en tu código, donde estás agregando todas las habilidades posibles como métodos de clase dinámicamente. No veo cómo eso se aplica a mi problema, ya que no puedo agregar un objeto asociado a la clase. ¿Hay alguna forma de acceder a las relaciones de la instancia para codificar dentro del bloque de clases? –

8

Estoy haciendo algo similar. He encontrado esta respuesta útil: How to convert a string to a constant in Ruby?

Resulta que yo estaba buscando el método constantize. Esta es la línea que estoy usando en mi código:

include "ModuleName::#{var.attr}".constantize 

Editar:

Así acuse de recibo, me encontré con varios problemas con el uso de esa línea realidad a mí mismo. Parcialmente porque estaba tratando de llamarlo dentro de un método en una clase. Pero ya que sólo estoy llamando a un método en la clase (que llama/corre todo lo demás) la versión final de trabajo que tengo ahora es

"ModuleName::#{var.attr}".constantize.new.methodname 

Obviamente methodname es un método de instancia, por lo que podría deshacerse de la nueva si el tuyo es un método de clase.

Cuestiones relacionadas