Skip to content

Commit

Permalink
overhaul and some testing
Browse files Browse the repository at this point in the history
- broke out prawn and prawnto options
- removed @prawn_document_options and instead using functions to dictate
options
- allowed option of dsl templates with locals
- some testing as well
  • Loading branch information
thorny_sun committed Aug 22, 2008
1 parent 45b01cf commit 521ceb0
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 103 deletions.
6 changes: 3 additions & 3 deletions MIT-LICENSE
@@ -1,16 +1,16 @@
Copyright (c) 2008 cracklabs.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Expand Down
9 changes: 7 additions & 2 deletions README
@@ -1,7 +1,12 @@
prawnto
----------------
Prawnto
=======

a rails (2.1) plugin, providing templating abilities
for generating pdf files leveraging the new kick-ass prawn library

full documentation/demos at: http://cracklabs.com/prawnto




Copyright (c) 2008 cracklabs.com, released under the MIT license
22 changes: 22 additions & 0 deletions Rakefile
@@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the prawnto plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

desc 'Generate documentation for the prawnto plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'Prawnto'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end
2 changes: 1 addition & 1 deletion init.rb
@@ -1,5 +1,5 @@
require 'prawnto'

Mime::Type.register "application/pdf", :pdf
ActionView::Template.register_template_handler :prawn, Prawnto::Prawn
ActionView::Template.register_template_handler :prawn, Prawnto::TemplateHandler

76 changes: 8 additions & 68 deletions lib/prawnto.rb
@@ -1,72 +1,12 @@
require 'prawn'
require 'prawnto/action_controller'
require 'prawnto/template_handler'


module Prawnto

class Prawn < ActionView::TemplateHandler
include ActionView::TemplateHandlers::Compilable

# snips out source code from dummy method below (used by compile method)
def self.template_setup_source
@@template_setup_source ||=
begin
method = 'template_setup_source_container'
regex_s = '\s*def\s' + method + '\s*\n(.*\n)\s*end\s*\#+\s*' + method
source_regex = Regexp.new regex_s, Regexp::MULTILINE
source_regex.match(File.read(__FILE__))[1]
end
end

def self.line_offset
@@line_offset ||= (self.template_setup_source.count("\n") + 1)
end

# incorporates template into a string to be compiled (using a proc)
def compile(template)
setup_source = Prawn::template_setup_source

# what does following line do exactly? not sure but copied it from builder implementation -- no longer needed since only line with "controller" is flaky anyhow
# setup_source.gsub!("controller","controller.response") if @view.send!(:controller).respond_to?(:response)

setup_source +
"pdf = Prawn::Document.new(@prawn_document_options)\n" +
template.source +
"\npdf.render\n"
end


# this is a dummy method to let me see code with editor's syntax goodness.
# it is erased immediately afterwards
# this method is never called, but instead the code is stripped out and pasted
# as code to be run by the caller of compile method
def template_setup_source_container
@prawn_document_options ||= {}

#TODO: check if this really makes sense-- kept around from railspdf, but maybe not needed?
___pragma = 'no-cache' # underscores are an attempt to avoid name clashes with user's local view variables
___cache_control = 'no-cache, must-revalidate'
___pragma = ___cache_control = '' if request.env['HTTP_USER_AGENT'] =~ /msie/i #keep ie happy (from railspdf-- no personal knowledge of these issues)
headers['Pragma'] ||= ___pragma
headers['Cache-Control'] ||= ___cache_control

# controller.content_type ||= Mime::PDF
# for some reason above line is not working on subsequent requests for different pdfs, but the following does-- i have no clue?
headers['Content-Type'] ||= 'application/pdf'

attachment = @prawn_document_options.delete(:attachment)
filename = @prawn_document_options.delete(:filename)
attachment = attachment ? 'attachment' : 'inline'
filename = filename ? "filename=#{filename}" : nil
disposition = [attachment,filename].compact.join(';')

headers["Content-Disposition"] ||= disposition if disposition.length > 0

#specify filename in controller otherwise will be inline
#TODO: verify attachment/inline behavior
#TODO: come up with solution for default naming (probably should just use railspdf way)

end ### template_setup_source_container <--- keep comment there so i can use it to snip out this code easier
remove_method :template_setup_source_container

end
# for now applying to all Controllers
# however, could reduce footprint by letting user mixin (i.e. include) only into controllers that need it
# but would does it really matter performance wise to include in a controller that doesn't need it? doubtful.
class ActionController::Base
include Prawnto::ActionController
end

48 changes: 48 additions & 0 deletions lib/prawnto/action_controller.rb
@@ -0,0 +1,48 @@
module Prawnto
module ActionController

def self.included(base)
base.extend ClassMethods
base.before_filter :reset_prawnto_options
end

module ClassMethods
def prawnto(options)
prawn_options, prawnto_options = breakdown_prawnto_options options
write_inheritable_hash(:prawn, prawn_options)
write_inheritable_hash(:prawnto, prawnto_options)
end

private

def breakdown_prawnto_options(options)
prawnto_options = options.dup
prawn_options = (prawnto_options.delete(:prawn) || {}).dup
[prawn_options, prawnto_options]
end
end

def prawnto(options)
@prawnto_options = options
end


private

def reset_prawnto_options
@prawnto_options = nil
end

def compute_prawnto_options

@prawnto_options ||= {}
@prawnto_options[:prawn] ||= {}
@prawnto_options[:prawn].merge!(self.class.read_inheritable_attribute(:prawn) || {}) {|k,o,n| o}
@prawnto_options.merge!(self.class.read_inheritable_attribute(:prawnto) || {}) {|k,o,n| o}
@prawnto_options
end

end
end


80 changes: 80 additions & 0 deletions lib/prawnto/template_handler.rb
@@ -0,0 +1,80 @@
module Prawnto
class TemplateHandler < ActionView::TemplateHandler
include ActionView::TemplateHandlers::Compilable

def self.line_offset
# looks for spot where binding is called-- since that is where the template is ultimately evaluated -- and that is what will give us accurate line # reporting
@@line_offset ||= template_wrapper_source.split(/^.*=\s*binding/)[0].count("\n")
end

def compile(template)
self.class.template_wrapper_source.sub(/^\s*yield/, template.source)
end

private

# this is a dummy method to let me see code with editor's syntax goodness.
# it is erased immediately afterwards
# this method is never called, but instead the code is stripped out and pasted
# as code to be run by the caller of compile method
def template_wrapper_source_container
@prawnto_options = controller.send :compute_prawnto_options
# underscores are an attempt to avoid name clashes with user's local view variables

#TODO: check if this really makes sense-- kept around from railspdf, but maybe not needed?
_pragma = 'no-cache'
_cache_control = 'no-cache, must-revalidate'
_pragma = _cache_control = '' if request.env['HTTP_USER_AGENT'] =~ /msie/i #keep ie happy (from railspdf-- no personal knowledge of these issues)
response.headers['Pragma'] ||= _pragma
response.headers['Cache-Control'] ||= _cache_control

response.content_type = Mime::PDF

_inline = @prawnto_options[:inline] ? 'inline' : 'attachment'
_filename = @prawnto_options[:filename] ? "filename=#{_filename}" : nil
_disposition = [_inline,_filename].compact.join(';')

response.headers["Content-Disposition"] = _disposition if _disposition.length > 0

_binding = nil
pdf = Prawn::Document.new(@prawnto_options[:prawn])
if _dsl = @prawnto_options[:dsl]
_variable_transfer = if _dsl.kind_of?(Array)
_dsl.map {|v| "#{v}=@#{v};"}.join('')
elsif _dsl.kind_of?(Hash)
_dsl.map {|k,v| "#{v}=@#{k};"}.join('')
else
""
end
eval _variable_transfer
pdf.instance_eval do
_binding = binding; end; else; _binding = binding; #stuck together to keep binding captures on same line so line_offset is valid in both cases
end

_view = <<EOS
yield
EOS


eval(_view,_binding)

#TODO: verify attachment/inline behavior

pdf.render
end
remove_method :template_wrapper_source_container


# snips out source code from dummy method below (used by compile method)
def self.template_wrapper_source
@@template_wrapper_source ||=
begin
method = 'template_wrapper_source_container'
regex_s = '(\s*)def\s' + method + '\s*\n(.*?\n)\1end'
source_regex = Regexp.new regex_s, Regexp::MULTILINE
source_regex.match(File.read(__FILE__))[2]
end
end

end
end
4 changes: 4 additions & 0 deletions tasks/prawnto_tasks.rake
@@ -0,0 +1,4 @@
# desc "Explaining what the task does"
# task :prawnto do
# # Task goes here
# end
51 changes: 51 additions & 0 deletions test/action_controller_test.rb
@@ -0,0 +1,51 @@
require 'rubygems'
require 'action_controller'
require 'action_view'

require 'test/unit'
require File.dirname(__FILE__) + '/../lib/prawnto'


module Prawnto
class ActionControllerTest < Test::Unit::TestCase

def setup
@controller_class = Class.new(::ActionController::Base)

# for some reason using the following as a block in the preceding statement is somehow different?
@controller_class.module_eval do
prawnto :inline=>true, :prawn=>{:page_orientation=>:landscape}

def test
prawnto :inline=>false, :prawn=>{:page_size=>'A4'}
end
end
end

def test_inheritable_options
assert_equal({:page_orientation=>:landscape}, @controller_class.read_inheritable_attribute(:prawn))
assert_equal({:inline=>true}, @controller_class.read_inheritable_attribute(:prawnto))
end

def test_computed_options
controller = @controller_class.new
controller.test
assert_equal({:inline=>false, :prawn=>{:page_orientation=>:landscape, :page_size=>'A4'}}, controller.send(:compute_prawnto_options))
end

end

class TemplateHandlerTest < Test::Unit::TestCase

class ActionView; end
class Template; def source; return 'SOURCE'; end; end

def test_valid_source
@template_handler = TemplateHandler.new(ActionView.new)
source = @template_handler.compile(Template.new).split('SOURCE')[0]
assert_equal TemplateHandler.line_offset, source.count("\n")
assert TemplateHandler.line_offset > 10
end
end
end

18 changes: 0 additions & 18 deletions test/prawnto_test.rb

This file was deleted.

11 changes: 0 additions & 11 deletions test/test_helper.rb

This file was deleted.

0 comments on commit 521ceb0

Please sign in to comment.