2

I'm new to Rails and i've been trying out Active Job and Action Cable and also Presenter and I've searched all over the place but not being able to figure it out.

so my question is that is there any way to use presenter in a partial and render it outside a controller(in ActiveJob for example)?

below is what i am wanting to achieve:

I have a message system(more like an email), with conversations controller and messages controller. What I want to achieve is when somebody starts a new conversation, if the receiver is on the conversation index page, the new conversation automatically appends to their index view.(like in emails) by using ActionCable

So, my conversation.rb

    class Conversation < ApplicationRecord
    has_many :messages, dependent: :destroy
    after_create_commit { ConversationBroadcastJob.perform_later(self) }

conversation_broadcast_job.rb

    class ConversationBroadcastJob < ApplicationJob
     queue_as :default

       def perform(conversation)
        ActionCable.server.broadcast "conversations:#{conversation.receiver_id}",
                              {convo: render_conversation(conversation)}
      end

      private

      def render_conversation(conversation)
        ConversationsController.render(partial: 'conversations/conversation',
                     locals: {conversation: conversation, var: 'not'})
      end

    end

and my _conversation.html.erb, I used a Presenter by following this episode of RailCast

    <% present conversation do |conversation_presenter| %>

    <div class="conversation <%=conversation_presenter.css(var)%>"
         id="conversation_<%= conversation.id %>"
         data-sender-id='<%= conversation_presenter.sender_id%>'>

      <%=conversation_presenter.avatar%>

      <div class="conversation-counterpart-name conversation-info truncate">
        <%=conversation_presenter.name(var) %>
      </div>


      <div class='conversation-subject conversation-info truncate'>
        <%=link_to conversation_messages_path(conversation),
                                      class:'link-gray truncate' do |n|%>
          <%=conversation_presenter.subject %>
           <div class='last-message truncate'>
            <%=conversation_presenter.last_message%>
          </div>
        <% end %>
      </div>

      <div class="conversation-date conversation-info">
        <%=conversation_presenter.last_date%>
      </div>

    </div>

    <% end %>

The present method is defined in ApplicationHelper

      def present(object, klass = nil)
        klass ||= "#{object.class}Presenter".constantize
        presenter = klass.new(object, self)
        yield presenter if block_given?
        presenter
      end

conversation_presenter.rb

    class ConversationPresenter < BasePresenter

      presents :conversation
      delegate :subject, to: :conversation


      def name(var)
        if conversation.sender_id == var.id
          conversation.receiver.name
        else
          conversation.sender.name
        end
      end

      def last_date
        if conversation.last_msg.nil?
          conversation.render_date
        else
          conversation.last_msg.render_date
        end
      end

      def last_message
        conversation.last_msg.content unless conversation.last_msg.nil?
      end

      def avatar
        h.image_tag('avatar.svg', size: 30, class:'conversation-        counterpart-avatar')
      end

      def css(var)
        "unread" unless
        conversation.read || (!conversation.last_msg.nil? &&     conversation.last_msg.wrote_by(var))
      end

      def sender_id
        if conversation.last_msg.nil?
          conversation.sender_id
        else
          conversation.last_msg.author_id
        end
      end

    end

The problem starts from here, the partial won't get rendered in the ActiveJob, because of the conversation presenter I assume. So my question is that is there anyway to use presenter with rendering outside a controller?

Please let me know if you need any information about other part of my codes

Thanks!

Rosalie
  • 27
  • 5

3 Answers3

2

I used the following code enough to run it from ActiveJob:

ApplicationController.new.render_to_string(
  :template => 'users/index',
  :layout => 'my_layout',
  :locals => { :@users => @users }
)

Extracted from https://makandracards.com/makandra/17751-render-a-view-from-a-model-in-rails

elmerfreddy
  • 141
  • 1
  • 5
0

try use

ConversationsController.render -> ApplicationController.renderer.render

this is helping me once.

  def render_conversation(conversation)
       renderer=ApplicationController.renderer.new
       renderer.render(partial: 'conversations/conversation',
                     locals: {conversation: conversation, var: 'not'})

  end
  • Could you give more information? I tried but it didn't work ConversationsController.render -> ApplicationController.renderer.render {(partial: 'conversations/conversation', locals: {conversation: conversation, var: 'not'})} – Rosalie Mar 20 '17 at 11:07
  • @Rosalie i have same problem when try do render from jobs. Usually the job inherits from ApplicationJob(ActiveJob::Base). But render is ApplicationController method. so to render from job you need change your Conversationscontroller to aplicationcontroller.renderer. i put an example in answer. – Валерий Васильев Mar 20 '17 at 17:13
  • @Rosalie try put some log. to see were is problem – Валерий Васильев Mar 20 '17 at 17:21
0

Below works in Rails 5:

ApplicationController.render(
  :template => 'users/index',
  :layout => 'my_layout',
  :assigns => { users: @users }
)

Example: this can be in jobs folder or any other place u wish.

i used in jobs (jobs/invoice_job.rb)

invoice_obj = Invoice.first
# Process the pdf_attachement in any way you want
pdf_attachement = WickedPdf.new.pdf_from_string(
      ApplicationController.render(template: 'invoices/invoice_pdf.html.erb', layout: 'layouts/pdf.html.haml', assigns: { invoice: invoice })
    )

use it in a view like below: (views/invoices/invoice_pdf.html.erb)

<%
@invoice = assigns[:invoice]
%>
<%= render 'invoices/invoice_pdf_template' %> <!-- generate your pdf html -->

pdf layout: (layouts/pdf.html.haml)

!!!
%html
  %head
    %title RailsWickedPdf
    = wicked_pdf_stylesheet_link_tag "application", :media => "all"
    = wicked_pdf_stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css", media: "all"
    = wicked_pdf_javascript_include_tag "application"
    = csrf_meta_tags
  %body
    = yield

Refer here for more infomation.

Ravistm
  • 2,163
  • 25
  • 25