2011-10-28 11 views
7

Soy nuevo en Ruby y preguntándose por qué estoy recibiendo un error en esta situación utilizando la gema 'mail' en una sencilla aplicación Sinatra:¿Por qué el bloque de correo no puede ver mi variable?

post "/email/send" do 

    @recipient = params[:email] 

    Mail.deliver do 
    to @recipient # throws error as this is undefined 
    from '[email protected]' 
    subject 'testing sendmail' 
    body 'testing sendmail' 
    end 

    erb :email_sent 

end 

Sin embargo, esto funciona bien:

post "/email/send" do 

    Mail.deliver do 
    to '[email protected]' 
    from '[email protected]' 
    subject 'testing sendmail' 
    body 'testing sendmail' 
    end 

    erb :email_sent 

end 

Sospecho que esto tiene algo que ver con el alcance del bloque y mi malentendido de él.

+1

¿Está seguro de que su problema es con la instancia var, y no con 'params [: email]'? ¿Has intentado sacarlo? También una variable local debería ser suficiente aquí, ya que el bloque es un cierre de todos modos. –

Respuesta

14

Como dice Julik, Mail#delivery ejecuta su bloque usando #instance_exec, que simplemente cambia self mientras se ejecuta un bloque (que no sería capaz de llamar a los métodos #to y #from dentro del bloque de otra manera).

Lo que realmente puede hacer aquí es utilizar un hecho que los bloques son cierres. Lo que significa que "recuerda" todas las variables locales a su alrededor.

recipient = params[:email] 
Mail.deliver do 
    to recipient # 'recipient' is a local variable, not a method, not an instance variable 
... 
end 

Una vez más, brevemente:

  • instancia variables y llamadas a métodos dependen de self
  • #instance_exec cambia el self;
  • variables locales no dependen de self y son recordadas por bloques porque los bloques son cierres.
+0

Y una pequeña adición: este comportamiento no depende de una versión de Ruby. Así que eliminaría las palabras "Ruby 1.9" del título de la pregunta, puede ser confuso. –

+0

Entonces mi error fue usar una variable de instancia? Utilicé una variable de instancia porque quería que los datos estuvieran disponibles en la plantilla de ERB y, si no me equivoco, eso significa que debe ser una instancia de var. Puedo evitarlo, ya sea declarando una variable local, o usando uno de los enfoques que The Tin Man sugiere. Gracias, amando a Ruby hasta ahora, pero hay mucho que aprender :) –

3

Creo que es porque la gema Mail usa instance_exec debajo del capó. instance_exec usa variables de instancia del objeto al que se llama y no de la persona que llama. Lo que haría es buscar un método en la gema de Correo que no utilice trucos de instancia, pero pase un objeto de configuración explícito al bloque y proceda desde allí. Ahorra unos pelos grises.

8

Además, si lee los documentos para Mail, encontrará una buena solución alternativa que funcionará. En lugar de uso:

Mail.deliver do 
    to @recipient # throws error as this is undefined 
    from '[email protected]' 
    subject 'testing sendmail' 
    body 'testing sendmail' 
end 

puede utilizar el método de correo new(), pasando en los parámetros, y pasar por alto el bloque:

Mail.new(
    to:  @recipient, 
    from: '[email protected]', 
    subject: 'testing sendmail', 
    body: 'testing sendmail' 
).deliver! 

o las definiciones de elementos de hash alternativos:

Mail.new(
    :to  => @recipient, 
    :from => '[email protected]', 
    :subject => 'testing sendmail', 
    :body => 'testing sendmail' 
).deliver! 

En palanca , o irb, usted vería:

pry(main)> Mail.new(
pry(main)* to: '[email protected]', 
pry(main)* from: '[email protected]' << `hostname`.strip, 
pry(main)* subject: 'test mail gem', 
pry(main)* body: 'this is only a test' 
pry(main)*).deliver! 
=> #<Mail::Message:59273220, Multipart: false, Headers: <Date: Fri, 28 Oct 2011 09:01:14 -0700>, <From: [email protected]>, <To: [email protected]>, <Message-ID: <[email protected]>>, <Subject: test mail gem>, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: 7bit>> 

El método new tiene varias variaciones que puede usar. Esto también es de los documentos, y podría funcionar mejor:

Como nota al margen, también puede crear un nuevo correo electrónico mediante la creación de un objeto Mail :: Message directamente y luego pasar valores a través de cadena, símbolo o directo llamadas de método. Ver Mail :: Mensaje para más información.

mail = Mail.new 
mail.to = '[email protected]' 
mail[:from] = '[email protected]' 
mail['subject'] = 'This is an email' 
mail.body = 'This is the body' 

seguido por mail.deliver!.

También tenga en cuenta, en el ejemplo anterior, que hay múltiples formas de acceder a los diversos encabezados en el sobre del mensaje. Es una gema flexible que parece estar bien pensada y sigue muy bien el estilo Ruby.

+0

Gracias por hacer esa investigación por mí :) He estado mirando demasiado de cerca los documentos de GitHub, en lugar de ir a ver los de RubyGems. Lección aprendida allí. Gracias de nuevo. –

Cuestiones relacionadas