0

I would love to call a controller action from within a model. Yes, MVC. Thanks.

So, why would i like to call a controller action and fetch the response?

Because my controller knows how to render the file I would like to cache. Why should I duplicate code to collect all data needed by my view?

Setup:

  • InvoicesController responds_to :html, :pdf
  • Invoice uses state_machine (:new -> :open -> paid)

What I "need" within the state transition from :new to :open

  • generate /invoices/:id.pdf
  • cache the PDF as Invoice#file for later use in delayed_job or simliar

What interface i would love to use elsewhere?

  • @invoice.build_pdf

Any suggestions?

Update: I would like to cache the PDF as model attachement for later use (delayed_job mailing, etc)

Salz
  • 152
  • 1
  • 8
  • nice to read, but didn't really help: http://stackoverflow.com/questions/7770119/how-to-route-and-render-dispatch-from-a-model-in-rails-3 http://jguimont.com/post/5582583230/how-to-render-a-full-page-template-in-a-rake-task-with – Salz Feb 14 '12 at 04:37

1 Answers1

0

It may be better to take the code you're using in your controller to render the PDF data, and create a module or class in your lib directory let's say lib/pdf.rb then use that code in both your controller and your model.

Update: You can cache the PDF itself on the filesystem as a file with a timestamp as it's name or something like that that is uniquely identifiable. Then add a file or invoice attribute to the model to store the location in the database for later use.

Update: You can use the block form of respond_to like so:

respond_to do |format|
  format.html
  format.pdf  { InvoiceBuilder.create(@invoice) }
end

Update: It seems that ActionDispatch::Integration::Session is a class for creating and interface for integration testing. But I think you could exploit it for your purposes. You should be able to use it like this:

# app/models/invoice.rb
class Invoice < ActiveRecord::Base
  def get_body
    app = ActionDispatch::Integration::Session.new(Rails.application)
    app.get("invoices/#{self.id}.pdf")
    app.response.body # this returns the rendered content that the browser will see.
  end
end

Another way would be to grab the content by using curl i.e. curl http://localhost:3000/invoices/1.pdf and store the output.

  • Maybe consider using prawn (http://prawn.majesticseacreature.com), I've used it with great success for similar things, although it may not be as good at supporting free form layouts, it worked great for me when I needed to create classes for generating PDF code. –  Feb 14 '12 at 04:59
  • so i had two places to maintain my code? I would prefer to keep a single place for it. – Salz Feb 14 '12 at 05:02
  • No, it would be in one place, you could use prawn to create a class that generates invoices for you that you could use in your models and your controllers. And you could cache the results in memory or on the filesystem. –  Feb 14 '12 at 05:07
  • it would result in a different process to generate html and pdf views. Not a "simple" respond_with @invoice and the format param choses the right template – Salz Feb 14 '12 at 05:08
  • Yeah but you gain the control you want, and it's not much more just using the block forms of `respond_with` or `respond_to`. –  Feb 14 '12 at 05:13
  • Maybe for the next app, right now there is way too much latex that would needed to be converted into proper prawn statements. – Salz Feb 14 '12 at 05:15
  • Ok, that's understandable, have you tried using `LatexToPdf.escape_latex(text)` on the raw view? You'd have to render the ERB first with the correct binding of course. –  Feb 14 '12 at 05:19
  • i have a "dirty" way to accomplish the task, but i would prefer to use the Stack that's already there. – Salz Feb 14 '12 at 05:25
  • I just found something else, I don't know if will be clean enough, but it should work. I'll update the answer. –  Feb 14 '12 at 06:21
  • 1. app.get("/path") is the way it can be done within the console and tests. But building ActionDispatch::Integration::Session in an running application, fails with a deadlock. But would be my preferred way 2. curl has some drawbacks, like authentication/autorisation for the request. "cleanest" solution i know, but still not beautiful. – Salz Feb 14 '12 at 06:36
  • After seeing Yahuda Katz's answer here: http://stackoverflow.com/a/7780580/266647, I tried it again and I was able to dump the contents of a the response into a file using a thread. I'll update the answer. –  Feb 15 '12 at 02:12
  • i dunno why, but testing it today it dosn't end in a deadlock. Thanks for your answer. have a nice weekend. – Salz Feb 17 '12 at 18:38