IT TIP

Rails, 모델에서 뷰 / 부분을 렌더링하는 방법

itqueen 2020. 12. 9. 22:03
반응형

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>):

제안? 어떻게 든 다른 곳에 렌더링해야합니까? 또는 콘텐츠를 설정하기 위해 모델에서 어떻게 렌더링 할 수 있습니까? 감사


적절한 해결책

글쎄, "그들"이 옳다. 실제로 컨트롤러에서 렌더링을 수행해야하지만 모델에서 해당 컨트롤러를 호출하는 것은 공정한 게임입니다! 다행히 Rails 3의 AbstractController는 내가 생각했던 것보다 더 쉽게 만들었습니다. 저는 ActionMailer처럼 작동하는 간단한 ActionPusher 클래스를 만들었습니다. 아마도 나는 야심을 갖고 언젠가 이것을 적절한 보석으로 만들 것이지만 이것은 내 입장에서 다른 누구에게나 좋은 시작이 될 것입니다.

이 링크에서 가장 많은 도움을 받았습니다. 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

그런 다음 app / 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 () 'ed됩니다. 나는 그것을 정리하고 거의 확실하게 render_anywhere를 더 일반적인 어딘가로 옮기고 아마도 이와 같은 것을 시도 할 계획입니다.

푸시에는 일반적으로 사용 가능한 템플릿을 호출하는 자체 템플릿이 필요하다는 것을 알 수 있으며 모두 한곳에서 수집 할 수 있습니다. 한 가지 좋은 작은 문제는 메뉴 항목에 불을 붙이는 것과 같이 파트에서 controller_name을 가끔 사용한다는 것입니다.하지만 분명히 다른 전략을 취해야합니다. 더 많은 도우미를 사용하려면 뭔가를해야 할 것 같지만 아직 거기에 도달하지 못했습니다.

성공! 만세! 이것은 귀하의 질문에 답할 것입니다. 나중에 적절하다고 생각되면 자세한 내용을 추가하겠습니다. 행운을 빕니다!!!!

명확성을 위해 1 시간 전의 원래 무응답 남음

나는 대답이 없지만,이시기 적절한 질문은 더 명확 할 가치가 있으며, 질문을 도와서 대답에 더 가까워지기를 바랍니다. :)

나는 같은 문제에 직면하고 있습니다. 좀 더 명확하게 설명하기 위해 Pusher는 연결된 사용자 브라우저에 비동기 적으로 콘텐츠를 보냅니다. 일반적인 사용 사례는 사용자에게 다른 사용자로부터 새 메시지가 있음을 보여주는 것입니다. Pusher를 사용하면 수신자의 브라우저에 메시지를 푸시 할 수 있으므로 로그인하면 즉시 알림을받을 수 있습니다. Pusher가 수행 할 수있는 작업에 대한 멋진 데모는 http://wordsquared.com/을 확인하세요 .

원하는 방식으로 해석하기 위해 JSON 해시와 같이 원하는 데이터를 보낼 수 있지만 다른 ajax 호출 및 클라이언트 측에서 eval ()과 마찬가지로 RJS를 보내는 것이 매우 편리합니다. 이런 식으로 (예를 들어) 메뉴 모음의 템플릿을 렌더링하거나 전체를 업데이트하거나 사용자에게 표시되는 새 메시지 수만 표시 할 수 있습니다. 모든 동일한 부분을 사용하여 뼈를 건조하게 유지합니다. 원칙적으로 발신자의 컨트롤러 에서 부분을 렌더링 할 수는 있지만 그다지 의미가 없으며 요청이 없을 수도 있습니다. 예를 들어 크론 작업 또는 다음과 같은 다른 이벤트에 의해 트리거 될 수 있습니다. 주가 변동. 발신자 컨트롤러는 그것에 대해 알 필요가 없어야합니다. 컨트롤러를 굶주린 식단에 유지하고 싶습니다.)

MVC 위반처럼 들릴 수 있지만 실제로는 아닙니다. 실제로 ActionMailer와 같은 방법으로 해결해야하지만 나머지 앱과 도우미 및 부분을 공유해야합니다. 내 앱에서 ActionMailer 호출과 동시에 (또는 대신) Pusher 이벤트를 보내고 싶습니다. 사용자 A의 이벤트를 기반으로 사용자 B에 대한 임의의 부분을 렌더링하고 싶습니다.

이러한 링크는 솔루션을 가리킬 수 있습니다.

마지막 것은 가장 유망 해 보이며 다음과 같은 흥미 진진한 스 니펫을 제공합니다.

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' })

레일스 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'

도움이되기를 바랍니다.


You can use ActionView directly and render partials to string without having a controller. I find that pattern useful to create models that encapsulate some javascript generation, for instance.

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

Then, just put your _test.html.erb in you view folder and try it out!


I'm fairly sure the answers you seek lie within Crafting Rails Applications where Jose Valim goes into great detail about how and why you would want to render views straight from your db

Sorry I can't be of more help yet because I've just started reading it myself tonight.

You might find some help here - it's a blog post about doing this sort of thing, albeit using different methods than yours


the "proper" way to do this is to push an object in serialized form(json), and then have the view deal with it once the event is received. Perhaps you want to use Handlebars to render the object.

Edit: I originally wrote about how, despite my answer, I was going to follow your example. But I just realized there is a HUGE gotcha with your approach when it comes to push notifications.

In your problem, you are doing push notifications to one user. For me, I was broadcasting out to a set of users. So I was going to render html with a presumption of a "current_user" and all that comes with it(eg logic, permissions, etc). This is NO BUENO as each push notification will be received by a different "current user".

Therefore, really, you need to just send back the data, and let each individual view handle it.


You should call all render methods from a controller. So, in this case, you can notify the controller that the object has been created and the controller can then render the view. Also, since you can render only once, I think you can wait for all your server side operations to complete before invoking the render.


The render methods are defined on the ActiveController class and its progeny. Inherently you do not have access to it on the model, nor is it a class method so you can't use it without an instance of the controller.

I've never tried to instantiate a controller for the express purpose of simply stringifying a partial, but if you can get your hands on a controller, render_to_string seems to be the way to go.

I will chime in by saying that if you're going down this path you're taking RoR "off the Rails". This is a violation of MVC and fundamentally poor program design.This doesn't mean I think you're a bad person :P Sometimes life drives us off the rails, so to speak.

I can't speak to the details that have driven you to do this, but I'd strongly suggest you rethink your approach.


I have created a gist for this.
I needed something similar, where the models don't necessarily (or in my case, ever) get updated via a controller, so the logic can't sit there.

Created a server-push based controller:
https://gist.github.com/4707055


Rails 6.0.0 compatible answer, since I ended up on this page while searching for a solution:

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 }

참고URL : https://stackoverflow.com/questions/6318959/rails-how-to-render-a-view-partial-in-a-model

반응형