Подтвердить что ты не робот

Rails, Как визуализировать представление/частичное в модели

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

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

Это раздражает рельсы, поскольку мне не нравится, что я представляю представление в модели, но мне это нужно.

Ошибка:

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

Предложения? Должен ли я каким-то образом сделать это где-то еще? Или как я могу отображать в модели задание контента? Благодаря

4b9b3361

Ответ 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.

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

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

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: меня тоже!

Ответ 2

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

ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })

Ответ 3

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'

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

Ответ 4

Вы можете напрямую использовать 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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

Ответ 9

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

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

Ответ 10

Ответ, совместимый с Rails 6.0.0, так как я оказался на этой странице во время поиска решения:

lookup_context = ActionView::LookupContext.new(Rails.configuration.paths["app/views"])
renderer = ActionView::Base.new(lookup_context)
renderer.extend(Rails.application.helpers)
renderer.render \
  template: "foo/bar",
  formats: [:html],
  handlers: [:erb],
  locals: { user: User.new }