Rails, Как сделать представление / частичное в модели - PullRequest
58 голосов
/ 12 июня 2011

В моей модели у меня есть:

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

Это раздражает, так как мне не нравится рендеринг вида в модели, но он мне нужен там.1009 *

NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):

Предложения?Должен ли я сделать это где-то еще как-нибудь?Или как я могу сделать рендеринг в модели, чтобы установить контент?Спасибо

Ответы [ 9 ]

65 голосов
/ 20 июля 2011

правильное решение

Ну, «они» правы.Вы действительно должны сделать рендеринг в контроллере, но это справедливая игра, чтобы вызвать этот контроллер из модели!К счастью, 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

в app / pushers / 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

и тогда вам понадобится частичное, например, приложение/views/users_pusher/add_notice.js.haml, который может быть простым:

alert('#{@notice.body}')

Я думаю, вам не нужно делать это с внутренним классом Pushable и вызовом .push в конце, но я хотел, чтобы это выглядело как ActiveMailer.У меня также есть метод pusher_key в моей пользовательской модели, чтобы создать канал для каждого пользователя - но это мой первый день с чем-то вроде Pusher, поэтому я не могу точно сказать, правильная ли это стратегия.Есть еще кое-что, что мне нужно, но этого достаточно для начала.

Удачи!

(это был мой первый черновой ответ, оставив его, потому что он может кому-то помочь)

У меня есть общий план работы решения.Например, в вашей модели:

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_any куда-нибудь куда более общее и, вероятно, попробовать что-то вроде этого

Я вижу, что для толчков потребуются свои собственные шаблоны, вызывающие общедоступныеи я могу попытаться собрать их всех в одном месте.Одна приятная небольшая проблема заключается в том, что я иногда использую controller_name в своих партиалах, например для подсветки пункта меню, но мне, очевидно, придется использовать другую тактику.Я предполагаю, что мне, возможно, придется что-то сделать, чтобы получить больше помощников, но я еще не добрался там.

Успех!Ура!Это должно ответить на ваш вопрос, и мой - я добавлю больше подробностей, если это покажется уместным позже.Удачи !!!!

оригинальный ответ от часа назад оставлен для ясности

У меня нет ответа, но этот своевременный вопрос заслуживает большего уточнения, и я надеюсьчтобы приблизиться к моему ответу, помогая спросить :)

Я столкнулся с той же проблемой.Чтобы объяснить это немного яснее, Pusher асинхронно отправляет контент в подключенный пользовательский браузер.Типичным вариантом использования может быть показ пользователю, у которого есть новое сообщение от другого пользователя.С помощью Pusher вы можете отправить сообщение в браузер получателя, чтобы они сразу получали уведомление, если вошли в систему. Для действительно отличной демонстрации того, что может сделать Pusher, посмотрите http://wordsquared.com/

Вы можете отправитьлюбые данные, которые вам нравятся, например, JSON-хеш для интерпретации того, как вам это нравится, но было бы очень удобно отправлять RJS, как и при любом другом вызове ajax, и eval () на стороне клиента.Таким образом, вы можете (например) визуализировать шаблон для вашей строки меню, обновить его полностью или просто счетчик новых сообщений, отображаемый для пользователя, используя все те же частичные значения, чтобы сохранить его в сухом виде.В принципе, вы можете визуализировать партиал из контроллера отправителя , но это также не имеет особого смысла, и может даже не быть запроса, он может быть запущен заданием cron, например,или другое событие, например, изменение цены акций.Контролеру отправителя просто не нужно знать об этом - мне нравится держать мои контроллеры на голодной диете;)

Это может звучать как нарушение MVC, но на самом деле это не так - и это действительно должно быть решено с помощью чего-то вроде ActionMailer, но с разделением помощников и частичных функций с остальной частью приложения.Я знаю, что в своем приложении я хотел бы отправить событие Pusher одновременно с вызовом ActionMailer (или вместо него).Я хочу отобразить произвольное частичное для пользователя B на основе события от пользователя A.

Эти ссылки могут указывать путь к решению:

Последний выглядит наиболее перспективным, предлагая этот дразнящий фрагмент:

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

Как и эта ссылка предоставленадругим постером выше.

Я сообщу, если что-нибудь заработает

д-р: я тоже!

58 голосов
/ 25 ноября 2012

Я просто делаю это:

ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
30 голосов
/ 21 марта 2017

Rails 5 way

В 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'

Надеюсь, что это поможет.

24 голосов
/ 03 июля 2014

Вы можете использовать ActionView напрямую и выводить партиалы в строку без контроллера.Я нахожу этот шаблон полезным для создания моделей, которые включают в себя, например, генерацию JavaScript.

html = ActionView::Base.new(Rails.configuration.paths['app/views']).render(
  partial: 'test', 
  formats: [:html],
  handlers: [:erb],
  locals: { variable: 'value' }
)

Затем просто поместите _test.html.erb в папку просмотра и попробуйте!

2 голосов
/ 02 декабря 2012

«правильный» способ сделать это состоит в том, чтобы выдвинуть объект в сериализованной форме (json), а затем получить представление об этом после получения события. Возможно, вы хотите использовать Handlebars для визуализации объекта.

Редактировать: Первоначально я писал о том, что, несмотря на мой ответ, я собирался последовать вашему примеру. Но я только что понял, что есть ОГРОМНАЯ ошибка с вашим подходом, когда дело доходит до push-уведомлений.

В вашей проблеме вы делаете push-уведомления одному пользователю. Для меня я вещал на группу пользователей. Поэтому я собирался визуализировать html с предположением «current_user» и всего, что с ним связано (например, логика, разрешения и т. Д.). Это не BUENO, так как каждое push-уведомление будет получено другим «текущим пользователем».

Следовательно, на самом деле вам нужно просто отправить данные обратно и позволить каждому отдельному представлению обрабатывать их.

2 голосов
/ 12 июня 2011

Я вполне уверен, что ответы, которые вы ищете, находятся в пределах Crafting Rails Applications , где Jose Valim подробно описывает как и почему вы бы хотели визуализировать взгляды прямо из вашей базы данных

Извините, я не могу вам больше помочь, потому что только сегодня вечером начал читать.

Вы могли бы найти справку здесь - это сообщение в блоге о таких вещах, хотя и с использованием методов, отличных от ваших

0 голосов
/ 04 февраля 2013

Я создал суть для этого.
Мне нужно что-то подобное, где модели не обязательно (или в моем случае, никогда) обновляются через контроллер, поэтому логика не может сидеть там.

Создан серверный контроллер на основе:
https://gist.github.com/4707055

0 голосов
/ 12 июня 2011

Методы рендеринга определены в классе ActiveController и его потомстве. По сути, у вас нет доступа к нему в модели, и при этом это не метод класса, поэтому вы не можете использовать его без экземпляра контроллера.

Я никогда не пытался создать экземпляр контроллера с явной целью просто привести в порядок частичную часть, но если вы можете взять в руки контроллер, то render_to_string, похоже, является подходящим вариантом.

Я вступлю в разговор, сказав, что если вы идете по этому пути, вы берете RoR «с рельсов». Это нарушение MVC и принципиально плохой дизайн программы. Это не значит, что я думаю, что вы плохой человек: P Иногда, так сказать, жизнь срывает нас с пути.

Я не могу говорить о деталях, которые побудили вас сделать это, но я настоятельно рекомендую вам пересмотреть свой подход.

0 голосов
/ 12 июня 2011

Вы должны вызывать все методы рендеринга из контроллера. Таким образом, в этом случае вы можете уведомить контроллер о том, что объект был создан, и контроллер может затем визуализировать представление. Кроме того, поскольку вы можете выполнить рендеринг только один раз, я думаю, что вы можете дождаться завершения всех операций на стороне сервера, прежде чем активировать рендеринг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...