Pregunta

Tengo una aplicación Rails estándar.

Cuando se crea un consejo, me gustaría crear un mensaje para cada usuario que esté interesado en esa punta.

Esto suena simple derecho? Debe ser ...

Por lo tanto, comenzamos con un Observador Consejo:

class TipObserver < ActiveRecord::Observer
  def after_save(tip)
    # after the tip is saved, we'll create some messages to inform the users
    users = User.interested_in(tip) # get the relevant users
    users.each do |u|
      m = Message.new
      m.recipient = u
      link_to_tip = tip_path(tip)
      m.body = "Hello #{u.name}, a new tip: #{link_to_tip}"
      m.save!
    end
  end
end

Variedades:

tip_observer.rb:13:in `after_save': undefined method `tip_path' for #<TipObserver:0xb75ca17c> (NoMethodError)

Ok, así TipObserver necesita tener acceso a los métodos UrlWriter. Esto debería ser bastante sencillo de arreglar, ¿verdad?

class TipObserver < ActiveRecord::Observer
  include ActionController::UrlWriter
(!)

Ahora que se ejecute y Salidas:

Hello dave18, a new tip: /tips/511

Grande que funciona !! Bueno, un poco, realmente queremos que para ser un enlace de clic-poder. Una vez más, esto debería ser fácil ¿verdad?

link_to_tip = link_to tip.name, tip_path(tip)

Variedades:

tip_observer.rb:13:in `after_save': undefined method `link_to' for #<TipObserver:0xb75f7708> (NoMethodError)

Ok, así que esta vez TipObserver necesita tener acceso a los UrlHelper métodos. Esto debería ser bastante sencillo de arreglar, ¿verdad?

class TipObserver < ActiveRecord::Observer
  include ActionController::UrlWriter
  include ActionView::Helpers::UrlHelper

Variedades:

whiny_nil.rb:52:in `method_missing': undefined method `url_for' for nil:NilClass (NoMethodError)

Ok, parece añadiendo que ha interferido con la declaración url_for. Vamos a tratar la incluye en un orden diferente:

class TipObserver < ActiveRecord::Observer
  include ActionView::Helpers::UrlHelper
  include ActionController::UrlWriter

Variedades:

url_rewriter.rb:127:in `merge': can't convert String into Hash (TypeError)

Hmm, no hay manera obvia de evitar esto. Pero después de leer algunos inteligentes-zuecos sugerencia de que Barredoras son los mismos que los observadores pero tienen acceso a los ayudantes de URL. Por lo que permite convertir el observador a un barrendero y retire la UrlHelper y UrlWriter.

class TipObserver < ActionController::Caching::Sweeper
  observe Tip
  #include ActionView::Helpers::UrlHelper
  #include ActionController::UrlWriter

Bueno, que permite que se ejecute, pero aquí está la salida:

Hello torey39, a new tip:

Por lo tanto, no hay ningún error, pero no se genera la url. La investigación adicional con la consola revela que:

tip_path => nil

y por lo tanto:

tip_path(tip) => nil

Ok, así que no tengo idea de cómo solucionar ese problema, por lo que tal vez podamos atacar esto desde una dirección diferente. Si movemos el contenido en una plantilla erb, y hacer que la Message.body como una vista - que le da dos ventajas - en primer lugar el contenido de "Vista" se pone en el lugar correcto, y que podría ayudar a evitar estos problemas * _PATH <. / p>

Así que vamos a cambiar el método de after_save:

def after_save(tip)
  ...
  template_instance = ActionView::Base.new(Rails::Configuration.new.view_path)
  m.body = template_instance.render(:partial => "messages/tip", :locals => { 
      :user=>user, 
      :tip=>tip
    })
  m.save!
end

Variedades:

undefined method `url_for' for nil:NilClass (ActionView::TemplateError)

Gran, pero ahora estamos de vuelta a este sangriento url_for nuevo. Así que esta vez sus ActionView la eso de las protestas. Vamos a tratar de solucionar este problema a continuación:

def after_save(tip)
  ...
  template_instance = ActionView::Base.new(Rails::Configuration.new.view_path)
  template_instance.extend ActionController::UrlWriter

Variedades:

undefined method `default_url_options' for ActionView::Base:Class

Gran así que cualquier cosa que hagamos nos encontramos con errores. He probado muchos muchos manera de asignar default_url_options dentro del template_instance sin éxito.

Hasta el momento esto no se siente muy "Railsy", de hecho, se siente francamente difícil.

Así que mi pregunta es:

  • ¿Estoy tratando de conseguir una clavija cuadrada en un agujero redondo? Si es así, cómo debería adaptar la arquitectura para proporcionar esta funcionalidad? No puedo creer no es algo que existe en otros sitios web.
  • ¿He de dejar de intentar utilizar un observador o barrendero?
  • ¿Debo estar tratando de crear nuevos mensajes a través del MessagesController, y si es así, ¿cómo puedo invocar el MessagesController directa e varias veces desde dentro del Observador / Sweeper?

Cualquier consejo consejos o sugerencias serán muy agradecido recibido, he estado golpeando mi cabeza contra la pared de ladrillo desde hace varios días y poco a poco pierde la voluntad de vivir.

tia

Keith

¿Fue útil?

Solución

Bueno, tienes razón de que su enfoque no es muy Carriles-y. Usted está tratando de mezclar modelo, controlador y ver métodos en una forma en que no están diseñados para y eso siempre es un poco inestable.

Si yo hubiera comenzado abajo de su trayectoria, probablemente habría renunciado en el problema y link_to (I admitir que no es "el camino rieles") codificado el código HTML para el enlace de forma manual. Así se convierte en link_to_tip = link_to tip.name, tip_path(tip) link_to_tip = '<a href="#{tip_path(tip)}">#{tip.name}</a> - una solución rápida y sucia si usted está buscando para uno; -)

Sin embargo, en mi experiencia, Rails es bastante limpio hasta que quiere hacer las cosas de un modo no estándar. Entonces se puede morder: -)

El problema se está escribiendo y el almacenamiento de texto en su modelo de mensaje que no debería estar allí. El modelo de mensaje debe belong_to Tips y una vista debe ser el encargado de presentar el texto del mensaje, incluyendo el enlace a la punta. Si un mensaje puede ser algo distinto de consejos, se puede hacer una asociación polimórfica en el modelo de mensaje como este:

belongs_to :source, :polymorphic => true

El modelo punta incluiría:

has_many :messages, :as => :source

Luego de hacer esto (usando el código de ejemplo):

m = Message.new
m.source = tip
m.save!

La vista que hace que el mensaje es entonces responsable de crear el enlace, así:

<%= "Hello #{u.name}, a new tip: #{link_to m.source.name, tip_path(m.source)}" %>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top