2010-08-25 12 views
10

Tengo un método privado en mi aplicación Rails para conectarme a Amazon S3, ejecutar un bloque de código pasado y luego cerrar la conexión a S3. Parece que sí;¿Cómo puedo hacer que un método esté disponible tanto para mi controlador como para mi modelo en Rails?

def S3 
    AWS::S3::Base.establish_connection!(
    :access_key_id  => 'Not telling', 
    :secret_access_key => 'Really not telling' 
) 
    data = yield 
    AWS::S3::Base.disconnect 
    data 
end 

Se llama así (como ejemplo);

send_data(S3 {AWS::S3::S3Object.value("#{@upload_file.name}",'bucket')}, :filename => @upload_file.name) 

que llamar a este método en un número de maneras en mi controlador y el modelo de modo que se incluya en ambas clases como un método privado. Esto funciona bien y estoy contento con eso, pero no es muy seco.

¿Cómo puedo hacer que este método sea accesible tanto para mi modelo como para el controlador, pero solo el código aparece una vez? Esta es más una pregunta de Ruby que una pregunta de Rails y refleja mi novedad en OOP. Supongo que la respuesta es un módulo o una mezcla, pero en realidad no he usado ninguno de estos hasta ahora y necesito un poco de apoyo.

Gracias.

Respuesta

8

Los módulos se utilizan para 3 cosas diferentes en ruby. Primero es el espacio de nombres. Tener definiciones de clase o constantes dentro de un módulo no colisionará con clases o constantes fuera de ese módulo. Algo como esto

class Product 
    def foo 
    puts 'first' 
    end 
end 

module Affiliate 
    class Product 
    puts 'second' 
    end 
end 

p = Product.new 
p.foo # => 'first' 

p = Affiliate::Product.new 
p.foo # => 'second' 

El segundo uso para los módulos es como un lugar para aplicar métodos que realmente no tienen cabida en ningún otro lado. También puede hacer esto dentro de una clase, pero usar un módulo le dice a las personas que leen el código que no debe ser instanciado. Algo como esto

module Foo 
    def self.bar 
    puts 'hi' 
    end 
end 

Foo.bar #=> 'hi' 

Finalmente (y lo más confuso) es que los módulos se pueden incluir en otras clases. Usarlos de esta manera también se conoce como mixin, porque estás "mezclando" todos los métodos en lo que sea que incluyas.

module Foo 
    def bar 
    puts 'hi' 
    end 
end 

class Baz 
    include Foo 
end 

b = Baz.new 
b.bar #=> 'hi' 

Los mixins son en realidad un tema mucho más complejo que el que estoy cubriendo aquí, pero profundizar sería probablemente confuso.

Ahora, para mí, S3 parece ser algo que realmente pertenece al controlador, ya que los controladores suelen ser los que se ocupan de las conexiones entrantes y salientes. Si ese es el caso, simplemente tendría un método protegido en el controlador de la aplicación, ya que será accesible para todos los demás controladores, pero aún así será privado.

Si tiene una buena razón para que también esté en el modelo, me gustaría un mixin. Algo así como

module AwsUtils 
private 
    def S3 
    AWS::S3::Base.establish_connection!\ 
     :access_key_id  => 'Not telling', 
     :secret_access_key => 'Really not telling' 

    data = yield 
    AWS::S3::Base.disconnect 
    data 
    end 
end 

si se pone en lib/aws_utils.rb que, usted debe ser capaz de utilizarlo mediante la adición de include AwsUtils tanto en el controlador y el modelo. Rails sabe buscar clases y módulos en lib, pero solo si el nombre coincide (en caso amplio).Lo llamé AwsUtils porque sé qué raíles buscará cuando vea eso (aws_utils.rb), y para ser honesto, no tengo idea de lo que necesitará para S3Utils ;-)

No dude en solicitar más información si no tenía claro algo. Los módulos tienden a ser una de esas cosas en rubí que, aunque asombrosas, son francamente desconcertantes para los recién llegados.

+0

Hermosa gracias. Estoy de acuerdo en que esto pertenece al controlador, pero escribí el código original hace algún tiempo antes de saber realmente lo que estaba haciendo. Hay muchas cosas en el modelo que no deberían estar ahí, pero preferiría no reescribirlo ahora mismo. Tu técnica ha funcionado bien y he podido usarla para incluir el método S3 en otro controlador, así que valió la pena. Mi comprensión de los módulos y mixins sigue siendo imperfecta (son confusos, estoy de acuerdo) pero esto me ha ayudado muy bien. – brad

3

Su corazonada es correcta: puede colocar un módulo en el directorio lib. En fin de que estos métodos a disposición de sus modelos, simplemente incluirlo con:

class Model < ActiveRecord::Base 
    include MyModule 
end 

métodos de instancia del módulo incluido se convertirán en los métodos de instancia en su clase. (Esto se conoce como mixin)

module MyModule 
    def S3 
    #... 
    end 
end 
+0

1) que _can't_ módulos instanciate . 2) si incluye un módulo, todos sus métodos de instancia se convierten en métodos de instancia en su clase, por lo tanto, colóquelos en la clase singleton o inclúyalos, no haga ambos –

+0

Tiene la razón ... corregida. –

1

Puede escribir un módulo como:

module MyModule 
    def self.S3(args*) 
    AWS::S3::Base.establish_connection!(
     :access_key_id  => 'Not telling', 
     :secret_access_key => 'Really not telling' 
    ) 
    data = yield 
    AWS::S3::Base.disconnect 
    data 
    end 
end 

y luego llamarlo en su controlador o modelo como

MyModule.S3 (params *)

Cuestiones relacionadas