Skip to content

Instantly share code, notes, and snippets.

@henrik
Created October 18, 2011 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henrik/1296300 to your computer and use it in GitHub Desktop.
Save henrik/1296300 to your computer and use it in GitHub Desktop.
Draft of view+template+helper PDFs with Prawn, including simple spec and manual verification specs triggered by Guard.
require "prawn/measurement_extensions"
class BaseDocument
delegate :render, :render_file,
to: :renderer
def initialize(data)
@data = data
end
private
def renderer
@renderer ||= PrawnTemplateRenderer.new(template, @data, self.class.helper_procs)
end
def template
self.class.name.underscore
end
# Any time we do "helpers do; stuff; end" in this class or subclasses,
# store that proc to be evaluated in the template context.
class_attribute :helper_procs
self.helper_procs ||= []
def self.helpers(&block)
self.helper_procs << block
end
end
class BaseDocument::PrawnTemplateRenderer
delegate :render, :render_file,
to: :doc
module PageSizes
A4 = "A4"
MY_SPECIAL_PAGE_SIZE = [ 123.mm, 456.mm ]
end
def initialize(template, data={}, helper_procs=[])
@template = template
@data = data
@helper_procs = helper_procs
# Evaluate the template within this instance, i.e. so that
# it calls our `meta` and `content` methods.
instance_eval(read_template, template_path)
end
def meta
extend PageSizes
@prawn_document_options = yield
end
def content(&block)
# Define any helpers on the Prawn document by evaluating them
# in its instance.
@helper_procs.each do |proc|
doc.instance_eval(&proc)
end
# Evaluate the contents of the data block in the Prawn document
# instance, yielding the data hash to that block.
doc.instance_exec(data_object, &block)
end
private
def doc
@doc ||= Prawn::Document.new(@prawn_document_options || {})
end
def data_object
OpenStruct.new(@data)
end
def read_template
File.read(File.join(app_root, template_path))
end
def template_path
"app/views/documents/#{@template}.pdf.rb"
end
def app_root
Rails.root
end
end
class MyDocument < BaseDocument
def initialize(user)
super(
name: user.name,
copies: 1
)
end
helpers do
def copies(count)
count.times do |i|
yield
start_new_page unless i == count-1
end
end
end
end
meta do
{
page_size: MY_SPECIAL_PAGE_SIZE,
margin: 12.mm
}
end
content do |data|
copies(data.copies) do
text "Hello #{data.name}!"
end
end
source 'http://rubygems.org'
# …
gem 'prawn'
group :test do
gem 'pdf-inspector', :require => "pdf/inspector"
end
# …
watch('app/documents/base_document.rb') { |m| "spec/documents" }
watch(%r{app/views/documents/(.+)\.pdf\.rb}) { |m| "spec/documents/#{m[1]}_spec.rb" }
# …
# encoding: utf-8
require "spec_helper"
describe MyDocument do
let(:document) { MyDocument.new(user) }
let(:pdf) { document.render }
let(:text) { PDF::Inspector::Text.analyze(pdf).strings.join(" ") }
context "with name" do
let(:user) { User.new(name: "Svelle") }
it "generates" do
text.should include("Hello Svelle!")
end
# To trigger these during dev:
# VIEW_PDF=true guard
it "previews", :manual_verification do
preview :name
end
end
context "with long name" do
let(:user) { User.new(name: "Svelle Forever") }
it "previews", :manual_verification do
preview :long_name
end
end
def preview(name)
file_name = "/tmp/my_document_#{name}.pdf"
document.render_file(file_name)
system("open", file_name) # OS X
end
end
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
# These are only for manual verification in dev and should never trigger from CI, specjour, rake spec etc.
# Do: VIEW_PDF=true guard
if ENV['VIEW_PDF']
puts "PDFs may open at any time!!1"
else
config.filter_run_excluding :manual_verification
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment