Рельсы, как сделать вид / частичный в модели
В моей модели у меня есть:
after_create :push_create
Я push_create мне нужно, чтобы отобразить представление. Я пытаюсь сделать это так:
def push_event(event_type)
X["XXXXX-#{Rails.env}"].trigger(event_type,
{
:content => render( :partial =>"feeds/feed_item", :locals => { :feed_item => self })
}
)
end
это злит rails, так как мне не нравится рендеринг вида в модели, но мне это нужно.
ошибка:
NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):
предложения? Я должен сделать это где-то еще как-то? Или как я могу отобразить в модели, чтобы установить содержимое? Спасибо
9 ответов:
правильное решение
Ну, "они" правы. Вы действительно должны сделать рендеринг в контроллере - но это честная игра, чтобы назвать этот контроллер от модели! К Счастью, 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.РБ
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
в приложении / pushers / users_pusher.рубидий. Я думаю, что требование может пойти куда-то более глобально?
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}')
Я думаю, вам действительно не нужно делать это с Толкаемым внутренним классом и т. д.толкать позвоните в конце, но я хотел, чтобы это выглядело как 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()'ed на стороне клиента успешно. Я планирую очистить его, почти наверняка переместить render_anywhere куда-то более общее, и, вероятно, попробовать что-то вроде этого
Я вижу, что толкает их нужно собственные шаблоны, вызывающие общедоступные, и я могу попытаться собрать их все в одном месте. Одна приятная маленькая проблема заключается в том, что я иногда использую controller_name в своих частичных файлах, например, чтобы осветить пункт меню, но мне, очевидно, придется использовать другую тактику. Я предполагаю, что мне, возможно, придется что-то сделать, чтобы получить больше помощников, но я еще не добрался туда.
успехов! Ура! Это должно ответить на ваш вопрос, а мой-я добавлю больше деталей, если это кажется уместным позже. Удачи вам!!!!
оригинальный не-ответ от час назад оставил для ясности
у меня нет ответа, но этот своевременный вопрос заслуживает большего разъяснения, и я надеюсь приблизиться к мой ответ, помогая спросить :)
я столкнулся с той же проблемой. Чтобы объяснить немного более четко, Pusher асинхронно отправляет контент в подключенный браузер пользователя. Типичным вариантом использования было бы показать пользователю, что у них есть новое сообщение другой пользователь. С помощью Pusher вы можете отправить сообщение в браузер получателя, чтобы они получили немедленное уведомление, если они вошли в систему. Для действительно большой демонстрации того, что Толкач может сделать, проверьтеhttp://wordsquared.com/
вы можете отправлять любые данные, которые вам нравятся, например, хэш JSON, чтобы интерпретировать, как вам это нравится, но было бы очень удобно отправлять RJS, как и с любым другим вызовом ajax и eval() на стороне клиента. Таким образом, вы можете (например) визуализировать шаблон для строки меню, обновляя его в полном объеме, или просто новый счетчик сообщений, отображаемый пользователю, используя все те же частичные значения, чтобы сохранить его сухим. В принципе, вы можете сделать частичное из отправителя!--42--> контроллер, но это тоже не имеет большого смысла, и может даже не быть запроса, он может быть вызван заданием cron, например, или каким-то другим событием, например изменением цены акций. Контроллер отправителя просто не должен знать об этом - мне нравится чтобы держать моих контролеров на голодной диете;)
это может звучать как нарушение MVC, но это действительно не так - и это действительно должно быть решено с помощью чего-то вроде ActionMailer, но совместное использование помощников и частичных файлов с остальной частью приложения. Я знаю, что в моем приложении я хотел бы отправить событие Pusher одновременно с вызовом ActionMailer (или вместо него). Я хочу сделать произвольную частичную для пользователя B на основе события от пользователя A.
эти ссылки могут указывать путь к решение:
- http://blog.choonkeat.com/weblog/2006/08/rails-calling-r.html
- как сделать частичный из модели в 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: я тоже!
Я просто делаю это:
ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
Вы можете использовать ActionView напрямую и отображать частичные значения в строку без контроллера. Я считаю, что этот шаблон полезен для создания моделей, которые инкапсулируют некоторые поколения javascript, например.
html = ActionView::Base.new(Rails.configuration.paths['app/views']).render( partial: 'test', formats: [:html], handlers: [:erb], locals: { variable: 'value' } )
тогда, просто положите ваш
_test.html.erb
в папке просмотра и попробовать его!
рельсы 5 путь
В Rails 5 рендеринг вне контроллера стало довольно просто благодаря реализовала
render
метод класса контроллера:# render template ApplicationController.render 'templates/name' # render action FooController.render :index # render file ApplicationController.render file: 'path' # render inline ApplicationController.render inline: 'erb content'
при вызове
render
вне контроллера, можно назначить переменные экземпляра черезassigns
опция и использовать любые другие опции, доступные из контроллера:ApplicationController.render( assigns: { article: Article.take }, template: 'articles/show', layout: false )
запрос среды может быть адаптирован либо через дефолт варианты
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'
или явно инициализируя новый рендерер
renderer = ApplicationController.renderer.new( http_host: 'custom_host.org', https: true ) renderer.render inline: '<%= users_url %>' # => 'https://custom_host.org/users'
надеюсь, что это поможет.
Я совершенно уверен, что ответы, которые вы ищете, лежат внутри Крафт Рельсы Приложений здесь Хосе Валим подробно рассказывает о как и почему вы хотите сделать вид прямо из вашей БД
Извините, я пока не могу быть более полезным, потому что я только начал читать его сегодня вечером.
вы может найти помощь здесь - это сообщение в блоге о том, чтобы делать такие вещи, хотя и используя другие методы, чем ваш
"правильный" способ сделать это-нажать объект в сериализованной форме(json), а затем иметь дело с ним после получения события. Возможно, вы хотите использовать руль для визуализации объекта.
Edit: я изначально писал о том, как, несмотря на мой ответ, я собирался последовать вашему примеру. Но я только что понял, что есть огромный gotcha с вашим подходом, когда дело доходит до push-уведомлений.
в вашей проблеме вы делаете push-уведомления для одного пользователя. Для меня я транслировал набор пользователей. Поэтому я собирался сделать html с презумпцией "current_user" и все, что с ним связано(например, логика, разрешения и т. д.). Это не BUENO, так как каждое push-уведомление будет получено другим "текущим пользователем".
поэтому, действительно, вам нужно просто отправить обратно данные, и пусть каждый отдельный вид справиться с этим.
вы должны вызвать все методы визуализации из контроллера. Таким образом, в этом случае вы можете уведомить контроллер о том, что объект был создан, и контроллер может затем отобразить представление. Кроме того, поскольку вы можете выполнить рендеринг только один раз, я думаю, что вы можете дождаться завершения всех операций на стороне сервера перед вызовом рендеринга.
Методы визуализации определяются в классе ActiveController и его потомстве. По своей сути у вас нет доступа к нему на модели, и это не метод класса, поэтому вы не можете использовать его без экземпляра контроллера.
Я никогда не пытался создать экземпляр контроллера с явной целью просто stringifying частичное, но если вы можете получить ваши руки на контроллере, render_to_string, кажется, путь.
Я буду перезванивать, говоря, что если вы идя по этому пути вы берете Рор "с рельсов". Это нарушение MVC и принципиально плохой дизайн программы.Это не значит, что я считаю тебя плохим человеком :иногда жизнь сбивает нас с толку, так сказать.
Я не могу говорить о деталях, которые заставили вас сделать это, но я настоятельно рекомендую вам пересмотреть свой подход.
Я создал суть для этого.
Мне нужно нечто подобное, где модели не обязательно (или в моем случае никогда) обновляются через контроллер, поэтому логика не может сидеть.создал сервер-push-контроллер:
https://gist.github.com/4707055