Dans mon modèle, j'ai:
after_create :Push_create
I Push_create J'ai besoin de rendre une vue. J'essaie de faire ça comme ça:
def Push_event(event_type)
X["XXXXX-#{Rails.env}"].trigger(event_type,
{
:content => render( :partial =>"feeds/feed_item", :locals => { :feed_item => self })
}
)
end
Cela met en colère Rails car il n'aime pas que je rende une vue dans le modèle mais j'en ai besoin là-bas.
Erreur:
NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):
Suggestions? Dois-je le rendre ailleurs quelque part? Ou comment puis-je effectuer un rendu dans le modèle pour définir le contenu? Merci
Eh bien, "ils" ont raison. Vous devez vraiment faire le rendu dans un contrôleur - mais il est juste d'appeler ce contrôleur à partir d'un modèle! Heureusement, AbstractController dans Rails 3 rend les choses plus faciles que je ne le pensais. J'ai fini par créer une classe ActionPusher simple, fonctionnant comme ActionMailer. Peut-être que je deviendrai ambitieux et en ferai un vrai bijou un jour, mais cela devrait être un bon début pour quiconque à ma place.
J'ai obtenu le plus d'aide de ce lien: http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-Rails-3/
dans lib/action_pusher.rb
class ActionPusher < AbstractController::Base
include AbstractController::Rendering
include AbstractController::Helpers
include AbstractController::Translation
include AbstractController::AssetPaths
include Rails.application.routes.url_helpers
helper ApplicationHelper
self.view_paths = "app/views"
class Pushable
def initialize(channel, pushtext)
@channel = channel
@pushtext = pushtext
end
def Push
Pusher[@channel].trigger('rjs_Push', @pushtext )
end
end
end
dans app/pushers/users_pusher.rb. Je suppose que l'exigence pourrait aller quelque part plus global?
require 'action_pusher'
class UsersPusher < ActionPusher
def initialize(user)
@user = user
end
def channel
@user.pusher_key
end
def add_notice(notice = nil)
@notice = notice
Pushable.new channel, render(template: 'users_pusher/add_notice')
end
end
Maintenant, dans mon modèle, je peux simplement faire ceci:
after_commit :Push_add_notice
private
def Push_add_notice
UsersPusher.new(user).add_notice(self).Push
end
et puis vous voudrez un partiel, par exemple app/views/users_pusher/add_notice.js.haml, qui pourrait être aussi simple que:
alert('#{@notice.body}')
Je suppose que vous n'avez pas vraiment besoin de le faire avec la classe interne Pushable et l'appel .Push à la fin, mais je voulais qu'il ressemble à ActiveMailer. J'ai également une méthode pusher_key sur mon modèle d'utilisateur, pour créer un canal pour chaque utilisateur - mais c'est mon premier jour avec quelque chose comme Pusher, donc je ne peux pas dire avec certitude si c'est la bonne stratégie. Il y a plus à étoffer, mais cela me suffit pour commencer.
Bonne chance!
J'ai les grandes lignes d'une solution qui fonctionne. Comme ça, dans votre modèle:
after_create :Push_new_message
private
def render_anywhere(partial, assigns = {})
view = ActionView::Base.new(ActionController::Base.view_paths, assigns)
view.extend ApplicationHelper
view.render(:partial => partial)
end
def Push_new_message
pushstring = render_anywhere('notices/Push_new_message', :message_text => self.body)
Pusher[user.pusher_key].trigger!('new_message', pushstring)
end
cela fonctionne définitivement - le modèle est rendu, et obtient eval () du côté client avec succès. Je prévois de le nettoyer, de déplacer presque certainement render_anywhere dans un endroit plus général, et d'essayer probablement quelque chose comme ça
Je peux voir que les push auront besoin de leurs propres modèles, appelant ceux généralement disponibles, et je peux essayer de les rassembler tous en un seul endroit. Un petit problème sympa est que j'utilise parfois controller_name dans mes partiels, comme pour allumer un élément de menu, mais je devrai évidemment prendre une tactique différente. Je suppose que je devrais peut-être faire quelque chose pour obtenir plus d'aides, mais je n'y suis pas encore arrivé.
Succès! Hourra! Cela devrait répondre à votre question et à la mienne - j'ajouterai plus de détails si cela semble approprié plus tard. Bonne chance!!!!
Je n'ai pas de réponse, mais cette question opportune mérite plus de précisions, et j'espère me rapprocher de mon réponse en aidant à poser :)
Je fais face au même problème. Pour expliquer un peu plus clairement, Pusher envoie de manière asynchrone du contenu à un navigateur utilisateur connecté. Un cas d'utilisation typique serait de montrer à l'utilisateur qu'il a un nouveau message d'un autre utilisateur. Avec Pusher, vous pouvez envoyer un message au navigateur du destinataire, afin qu'il reçoive une notification immédiate s'il est connecté. Pour une démonstration vraiment intéressante de ce que Pusher peut faire, consultez http://wordsquared.com/
Vous pouvez envoyer toutes les données que vous aimez, comme un hachage JSON pour interpréter comment vous l'aimez, mais il serait très pratique d'envoyer du RJS, comme avec tout autre appel ajax et eval () du côté client. De cette façon, vous pouvez (par exemple) rendre le modèle de votre barre de menus, le mettre à jour dans son intégralité, ou simplement le nouveau nombre de messages affiché à l'utilisateur, en utilisant tous les mêmes partiels pour le garder sec. En principe, vous pouvez rendre le partiel à partir du contrôleur sender's, mais cela n'a pas beaucoup de sens non plus, et il pourrait même ne pas y avoir de demande, cela pourrait être déclenché par un travail cron, par exemple, ou un autre événement, comme un changement de cours boursier. Le contrôleur de l'expéditeur ne devrait tout simplement pas avoir à le savoir - j'aime garder mes contrôleurs au régime de famine;)
Cela peut sembler une violation de MVC, mais ce n'est vraiment pas le cas - et cela devrait vraiment être résolu avec quelque chose comme ActionMailer, mais en partageant des assistants et des partiels avec le reste de l'application. Je sais que dans mon application, j'aimerais envoyer un événement Pusher en même temps (ou à la place) qu'un appel ActionMailer. Je veux rendre un partiel arbitraire pour l'utilisateur B basé sur un événement de l'utilisateur A.
Ces liens peuvent indiquer la voie vers une solution:
Le dernier semble le plus prometteur, offrant cet extrait alléchant:
def render_anywhere(partial, assigns)
view = ActionView::Base.new(Rails::Configuration.new.view_path, assigns)
ActionView::Base.helper_modules.each { |helper| view.extend helper }
view.extend ApplicationHelper
view.render(:partial => partial)
end
De même que ce lien fourni par une autre affiche ci-dessus.
Je ferai rapport si je fais fonctionner quelque chose
tl; dr: moi aussi!
Je fais juste ceci:
ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
Dans Rails 5 le rendu en dehors d'un contrôleur est devenu assez simple en raison de implémentérender
méthode de classe de contrôleur:
# render template
ApplicationController.render 'templates/name'
# render action
FooController.render :index
# render file
ApplicationController.render file: 'path'
# render inline
ApplicationController.render inline: 'erb content'
Lors de l'appel de render
en dehors d'un contrôleur, on peut affecter des variables d'instance via l'option assigns
et utiliser toutes les autres options disponibles à partir d'un contrôleur:
ApplicationController.render(
assigns: { article: Article.take },
template: 'articles/show',
layout: false
)
Environnement de requête peut être personnalisé via les options par défaut
ApplicationController.render inline: '<%= users_url %>'
# => 'http://default_Host.com/users'
ApplicationController.renderer.defaults[:http_Host] = 'custom_Host.org'
# => "custom_Host.org"
ApplicationController.render inline: '<%= users_url %>'
# => 'http://custom_Host.org/users'
ou explicitement en initialisant un nouveau rendu
renderer = ApplicationController.renderer.new(
http_Host: 'custom_Host.org',
https: true
)
renderer.render inline: '<%= users_url %>'
# => 'https://custom_Host.org/users'
J'espère que ça t'as aidé.
Vous pouvez utiliser ActionView directement et rendre des partiels en chaîne sans avoir de contrôleur. Je trouve ce modèle utile pour créer des modèles qui encapsulent une génération javascript, par exemple.
html = ActionView::Base.new(Rails.configuration.paths['app/views']).render(
partial: 'test',
formats: [:html],
handlers: [:erb],
locals: { variable: 'value' }
)
Ensuite, mettez simplement votre _test.html.erb
dans votre dossier et essayez-le!
Je suis presque sûr que les réponses que vous cherchez se trouvent dans Crafting Rails Applications where Jose Valim explique en détail comment et pourquoi vous voudriez rendre les vues directement depuis votre base de données
Désolé, je ne peux pas vous être encore plus utile car je viens de commencer à le lire moi-même ce soir.
Vous peut-être trouver de l'aide ici - c'est un article de blog sur ce genre de chose, mais en utilisant des méthodes différentes de la vôtre
la manière "appropriée" de procéder consiste à envoyer un objet sous forme sérialisée (json), puis à faire en sorte que la vue le gère une fois l'événement reçu. Vous souhaitez peut-être utiliser le guidon pour rendre l'objet.
Edit: J'ai écrit à l'origine comment, malgré ma réponse, j'allais suivre votre exemple. Mais je viens de réaliser qu'il y a un énorme problème avec votre approche en ce qui concerne les notifications Push.
Dans votre problème, vous effectuez des notifications Push à un utilisateur. Pour moi, je diffusais à un ensemble d'utilisateurs. J'allais donc rendre le html avec une présomption de "current_user" et tout ce qui va avec (par exemple la logique, les permissions, etc.). Ce n'est PAS BUENO car chaque notification Push sera reçue par un "utilisateur actuel" différent.
Par conséquent, vous devez simplement renvoyer les données et laisser chaque vue individuelle les gérer.
Les méthodes de rendu sont définies sur la classe ActiveController et sa descendance. Intrinsèquement, vous n'y avez pas accès sur le modèle, ce n'est pas non plus une méthode de classe, vous ne pouvez donc pas l'utiliser sans une instance du contrôleur.
Je n'ai jamais essayé d'instancier un contrôleur dans le but exprès de simplement stringifier un partiel, mais si vous pouvez mettre la main sur un contrôleur, render_to_string semble être la voie à suivre.
Je répondrai en disant que si vous suivez cette voie, vous retirez RoR "des rails". C'est une violation de MVC et une conception de programme fondamentalement médiocre. Cela ne signifie pas que je pense que vous êtes une mauvaise personne: P Parfois, la vie nous pousse à quitter les Rails, pour ainsi dire.
Je ne peux pas parler des détails qui vous ont poussé à le faire, mais je vous suggère fortement de repenser votre approche.
J'ai créé un Gist pour cela.
J'avais besoin de quelque chose de similaire, où les modèles ne sont pas nécessairement (ou dans mon cas, jamais) mis à jour via un contrôleur, donc la logique ne peut pas rester là.
Création d'un contrôleur basé sur le serveur Push:
https://Gist.github.com/4707055
Vous devez appeler toutes les méthodes de rendu à partir d'un contrôleur. Ainsi, dans ce cas, vous pouvez informer le contrôleur que l'objet a été créé et le contrôleur peut alors rendre la vue. De plus, comme vous ne pouvez effectuer un rendu qu'une seule fois, je pense que vous pouvez attendre la fin de toutes vos opérations côté serveur avant d'appeler le rendu.