Permalink
Browse files

overhaul and some testing

- 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...
1 parent 45b01cf commit 521ceb0987cdd95a60a50535a1bb775226c381a5 thorny_sun committed Aug 22, 2008
Showing with 224 additions and 103 deletions.
  1. +3 −3 MIT-LICENSE
  2. +7 −2 README
  3. +22 −0 Rakefile
  4. +1 −1 init.rb
  5. +8 −68 lib/prawnto.rb
  6. +48 −0 lib/prawnto/action_controller.rb
  7. +80 −0 lib/prawnto/template_handler.rb
  8. +4 −0 tasks/prawnto_tasks.rake
  9. +51 −0 test/action_controller_test.rb
  10. +0 −18 test/prawnto_test.rb
  11. +0 −11 test/test_helper.rb
View
@@ -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
View
9 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
View
@@ -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
View
@@ -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
View
@@ -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
+
@@ -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
+
+
@@ -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
View
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :prawnto do
+# # Task goes here
+# end
@@ -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
+
View
@@ -1,18 +0,0 @@
-require 'test/unit'
-require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment'))
-
-
-class PrawntoTest < Test::Unit::TestCase
- def test_template_setup_source
- # maybe test that you can eval source without errors (after mocha-ing up some objects?
- end
-
- def test_template_line_offset
- assert Prawnto::Prawn.line_offset > 1
- end
-
- # TODO: perhaps also test that @prawn_document_options affects Prawn::Document's initial settings-- though seems like might be kinda pointless/redundant
-
- #TODO: partial possibilities?
-end
-
View
@@ -1,11 +0,0 @@
-require 'rubygems'
-require 'action_controller'
-require 'test/unit'
-
-module ApplicationHelper; end
-
-$LOAD_PATH << File.dirname(__FILE__) + '/../lib'
-require 'prawn'
-
-#TODO: figure out if mocha is really required-- what is the recommended way to test controller/view stuff in Rails 2.1?
-# require 'mocha'

0 comments on commit 521ceb0

Please sign in to comment.