Рельсы, как сделать вид / частичный в модели


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

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 56

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.

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

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

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