Skip to content

Instantly share code, notes, and snippets.

@juggy
Created May 17, 2011 19:25
  • Star 37 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save juggy/977181 to your computer and use it in GitHub Desktop.
Render a complete page in rails 3 without controller
# create the template
template = PageOfflineTemplate.new
template.quote = quote
template.pages = quote.build_pages
# Here I render a template with layout to a string then a PDF
pdf = PDFKit.new template.render_to_string(:template=>"quotes/review.html.haml")
class OfflineTemplate < AbstractController::Base
# Include all the concerns we need to make this work
include AbstractController::Logger
include AbstractController::Rendering
include AbstractController::Layouts
include AbstractController::Helpers
include AbstractController::Translation
include AbstractController::AssetPaths
include ActionController::UrlFor
include Rails.application.routes.url_helpers
# this is you normal rails application helper
helper ApplicationHelper
# Define additional helpers, this one is for csrf_meta_tag
helper_method :protect_against_forgery?
# override the layout in your subclass if needed.
layout 'application'
# configure the different paths correctly
def initialize(*args)
super()
lookup_context.view_paths = Rails.root.join('app', 'views')
config.javascripts_dir = Rails.root.join('public', 'javascripts')
config.stylesheets_dir = Rails.root.join('public', 'stylesheets')
config.assets_dir = Rails.root.join('public')
end
# we are not in a browser, no need for this
def protect_against_forgery?
false
end
# so that your flash calls still work
def flash
{}
end
def params
{}
end
# same asset host as the controllers
self.asset_host = ActionController::Base.asset_host
# and nil request to differentiate between live and offline
def request
nil
end
end
# subclass to define the @ attributes your tempalte will use
class PageOfflineTemplate < OfflineTemplate
attr_accessor :pages, :quote, :invoice, :quotes, :mat_pages, :wo_pages, :exp_pages
end
@paulleader
Copy link

Thanks so much for this. I've been going a bit loopy trying to geneterate PDFs with PDFKit that I could then attack to emails, this works a treat.

@rafaelcgo
Copy link

Almost done. Can't make urls or assets work...
undefined method 'host' for nil:NilClass

@mcspring, Did you find a way to fix it?

@rafaelcgo
Copy link

I have a fix to make url_for helper work again without a valid request (offline on a rake task or resque task)

Make these changes to @juggy gist:

  • remove request method
  • remove include ActionController::UrlFor (needs a request object)
  • add include ActionDispatch::Routing::UrlFor (doesnt, and can make urls with url_for properly.)
  • add Rails.application.routes.default_url_options = { :host => 'www.yoursite.com' }

@rafaelcgo
Copy link

class OfflineTemplate < AbstractController::Base
  include AbstractController::Logger
  include AbstractController::Rendering
  include AbstractController::Layouts
  include AbstractController::Helpers
  include AbstractController::Translation
  include AbstractController::AssetPaths
  include ActionDispatch::Routing::UrlFor
  include Rails.application.routes.url_helpers
  Rails.application.routes.default_url_options = { :host => 'www.yoursite.com' }

  helper ApplicationHelper

  helper_method :protect_against_forgery?

  # configure the different paths correctly
  def initialize(*args)
    super()
    lookup_context.view_paths = Rails.root.join('app', 'views')
    config.javascripts_dir = Rails.root.join('public', 'javascripts')
    config.stylesheets_dir = Rails.root.join('public', 'stylesheets')
    config.assets_dir = Rails.root.join('public')
  end

  # we are not in a browser, no need for this
  def protect_against_forgery?
    false
  end

  # so that your flash calls still work
  def flash
    {}
  end

  def params
    {}
  end

  # same asset host as the controllers
  self.asset_host = ActionController::Base.asset_host
end

@marckohlbrugge
Copy link

FWIW, if you've already set config.action_mailer.default_url_options you can use the following in OfflineTemplate to keep it DRY:

Rails.application.routes.default_url_options = Rails.application.config.action_mailer.default_url_options

@anthonylebrun
Copy link

Blimey. I think Joe Armstrong nailed it when he said:

I think the lack of reusability comes in object-oriented languages, not functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

If you have referentially transparent code, if you have pure functions — all the data comes in its input arguments and everything goes out and leave no state behind — it’s incredibly reusable.

@chrise86
Copy link

chrise86 commented Mar 2, 2018

Had to do this for Rails 5.0.6:

class OfflineTemplate < AbstractController::Base
  include AbstractController::Logger
  include AbstractController::Rendering
  include ActionView::Layouts
  include AbstractController::Helpers
  include AbstractController::Translation
  include AbstractController::AssetPaths
  include ActionDispatch::Routing::UrlFor
  include Rails.application.routes.url_helpers
  Rails.application.routes.default_url_options = Rails.application.config.action_mailer.default_url_options

  helper ApplicationHelper

  helper_method :protect_against_forgery?

  # configure the different paths correctly
  def initialize(*_args)
    super
    lookup_context.view_paths = Rails.root.join('app', 'views')
    config.javascripts_dir = Rails.root.join('public', 'javascripts')
    config.stylesheets_dir = Rails.root.join('public', 'stylesheets')
    config.assets_dir = Rails.root.join('public')
  end

  # we are not in a browser, no need for this
  def protect_against_forgery?
    false
  end

  # so that your flash calls still work
  def flash
    {}
  end

  def params
    {}
  end

  def controller_name
    'offline'
  end

  # same asset host as the controllers
  self.asset_host = ActionController::Base.asset_host
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment