Ответ 1
правильное решение
Ну, "они" правы. Вам действительно нужно сделать рендеринг в контроллере - но это справедливая игра, чтобы назвать этого контроллера из модели! К счастью, AbstractController в Rails 3 делает это легче, чем я думал. Я закончил создание простого Класс ActionPusher, работающий так же, как ActionMailer. Возможно, я стану амбициозным и сделайте это надлежащим драгоценным камнем когда-нибудь, но это должно послужить хорошим началом для всех, кто на моей стороне.
Мне очень понравилась эта ссылка: http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-rails-3/
в 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
в приложении/толкателях/users_pusher.rb. Я предполагаю, что требование может пойти куда-то более глобальным?
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
Теперь в моей модели я могу просто сделать это:
after_commit :push_add_notice
private
def push_add_notice
UsersPusher.new(user).add_notice(self).push
end
и тогда вам понадобится частичный, например. app/views/users_pusher/add_notice.js.haml, который может быть таким же простым, как:
alert('#{@notice.body}')
Я думаю, вам действительно не нужно делать это с помощью Pushable внутреннего класса и .push позвоните в конце, но я хотел бы сделать его похожим на ActiveMailer. У меня также есть pusher_key в моей модели пользователя, чтобы сделать канал для каждого пользователя, но это мой первый день с чем-то вроде Пушера, поэтому я не могу точно сказать, правильно ли это стратегия. Там будет больше, чтобы быть конкретизированным, но этого достаточно для меня, чтобы начать.
Удачи!
(это был мой первый проект ответа, оставив его, потому что он может помочь кому-то)
У меня есть общая схема работы решения. Например, в вашей модели:
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
который определенно работает - шаблон является рендерингом и успешно получает eval() на стороне клиента. Я планирую очистить его, почти наверняка переместите render_anywhere где-то более общим и, возможно, попробуйте что-то вроде этого
Я вижу, что для подталкивания нужны свои собственные шаблоны, вызывающие общедоступные, и я могу попытаться собрать их всех в одном месте. Одна из приятных проблем заключается в том, что я иногда использую имя_контроллера в своих элементарных элементах, например, чтобы загореться пункт меню, но, очевидно, мне придется использовать другую тактику. Я предполагаю, что мне, возможно, придется что-то сделать, чтобы получить больше помощников, но я еще не получил их.
Успех! Ура! Это должно ответить на ваш вопрос, а мое - я добавлю более подробную информацию, если это будет уместно позже. Удачи!!!!
оригинальный не-ответ от часа назад для ясности слева
У меня нет ответа, но этот своевременный вопрос заслуживает большего разъяснения, и я надеюсь подобраться ближе к моему ответу, помогая спросить:)
У меня такая же проблема. Чтобы объяснить немного более четко, Pusher асинхронно отправляет контент в подключенный пользовательский браузер. Типичный пример использования - это показать пользователю, что у него есть новое сообщение от другого пользователя. С помощью Pusher вы можете нажать сообщение в браузере получателя, чтобы они получили немедленное уведомление, если они вошли в систему. Для действительно отличной демонстрации того, что может сделать Pusher, посмотрите http://wordsquared.com/
Вы можете отправлять любые данные, такие как хэш JSON, чтобы интерпретировать, как вам это нравится, но было бы очень удобно отправлять RJS, как и с любым другим вызовом ajax и eval() на стороне клиента. Таким образом, вы могли бы (например) визуализировать шаблон для панели меню, обновлять его целиком или просто новый счетчик сообщений, отображаемый пользователю, используя все те же частичные части, чтобы сохранить его сухим. В принципе, вы можете отобразить частичное из контроллера отправителя, но это тоже не имеет особого смысла, и может быть даже не запрос, он может быть вызван заданием cron, например, или каким-либо другим событием, например изменение цены акций. Контроллер отправителя просто не должен знать об этом - мне нравится держать мои контроллеры в голодной диете;)
Это может показаться нарушением MVC, но это действительно не так - и это действительно должно быть разрешено с помощью чего-то вроде ActionMailer, но совместно с помощниками и частицами с остальной частью приложения. Я знаю в своем приложении, я хотел бы отправить событие Pusher одновременно с вызовом ActionMailer (или вместо него). Я хочу сделать произвольное частичное для пользователя B на основе события от пользователя A.
Эти ссылки могут указывать путь к решению:
- http://blog.choonkeat.com/weblog/2006/08/rails-calling-r.html
- Как отобразить Partial из модели в Rails 2.3.5
- http://mattwindsurfs.wordpress.com/2008/06/19/rails-render-in-a-model/
- http://davetroy.blogspot.com/2008/02/actsasrenderer-brings-output-to-models.html
- https://github.com/asapnet/acts_as_renderer
- http://ethilien.net/archives/render-rails-templates-anywhere-even-in-a-model/
Последний выглядит наиболее перспективным, предлагая этот дразнящий фрагмент:
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
Как эта ссылка, предоставленная другим плакатом выше.
Я отчитаюсь, если у меня что-то получится
tl; dr: меня тоже!