tn_pdf 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.swp
2
+ ~*
3
+ bin/
4
+ .bundle
5
+ .rspec
6
+ coverage
7
+ *.gem
8
+ Gemfile.lock
9
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in tn_pdf.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ Ascii85 (1.0.1)
5
+ ZenTest (4.5.0)
6
+ activesupport (2.3.5)
7
+ autotest (4.4.6)
8
+ ZenTest (>= 4.4.1)
9
+ coderay (0.9.8)
10
+ diff-lcs (1.1.2)
11
+ method_source (0.4.1)
12
+ ruby_parser (>= 2.0.5)
13
+ pdf-reader (0.9.2)
14
+ Ascii85 (>= 0.9)
15
+ prawn (0.11.1)
16
+ pdf-reader (>= 0.9.0)
17
+ ttfunk (~> 1.0.0)
18
+ pry (0.8.3)
19
+ coderay (>= 0.9.7)
20
+ method_source (>= 0.4.0)
21
+ ruby_parser (>= 2.0.5)
22
+ slop (>= 1.5.3)
23
+ rcov (0.9.9)
24
+ rspec (2.6.0)
25
+ rspec-core (~> 2.6.0)
26
+ rspec-expectations (~> 2.6.0)
27
+ rspec-mocks (~> 2.6.0)
28
+ rspec-core (2.6.1)
29
+ rspec-expectations (2.6.0)
30
+ diff-lcs (~> 1.1.2)
31
+ rspec-mocks (2.6.0)
32
+ ruby_parser (2.0.6)
33
+ sexp_processor (~> 3.0)
34
+ sexp_processor (3.0.5)
35
+ slop (1.6.0)
36
+ ttfunk (1.0.1)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ activesupport
43
+ autotest
44
+ prawn (~> 0.11.1)
45
+ pry
46
+ rcov
47
+ rspec (~> 2.6.0)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'spec/rake/spectask'
6
+
7
+ desc "Run all examples with RCov"
8
+ Spec::Rake::SpecTask.new('examples_with_rcov') do |t|
9
+ t.spec_files = FileList['spec/**/*_spec.rb']
10
+ t.rcov = true
11
+ t.rcov_opts = ['--exclude', 'spec,.bundle']
12
+ end
data/lib/tn_pdf/box.rb ADDED
@@ -0,0 +1,72 @@
1
+ module TnPDF
2
+ class PageSection
3
+
4
+ class Box
5
+ attr_reader :image_path, :image_options
6
+ attr_reader :text, :text_options
7
+
8
+ def initialize(options)
9
+ parse_options(options)
10
+ end
11
+
12
+ def render(document, pos, width, height=nil)
13
+ options_hash = { :width => width }
14
+ options_hash[:height] = height unless height.nil?
15
+
16
+ document.bounding_box(pos, options_hash) do
17
+ if has_image?
18
+ image_args = [image_path]
19
+ image_args << image_options unless image_options.empty?
20
+ document.image *image_args
21
+ end
22
+
23
+ if has_text?
24
+ text_args = [text]
25
+ text_args << text_options unless text_options.empty?
26
+ document.text *text_args
27
+ end
28
+ end
29
+ end
30
+
31
+ def has_image?
32
+ !image_path.nil?
33
+ end
34
+
35
+ def has_text?
36
+ !text.nil?
37
+ end
38
+
39
+ private
40
+
41
+ def parse_options(options)
42
+ options.to_options!
43
+
44
+ options[:align] ||= :justify
45
+ options[:valign] ||= :center
46
+
47
+ if options[:text]
48
+ unless options[:text].kind_of? Hash
49
+ options[:text] = { :text => options[:text] }
50
+ end
51
+ options[:text][:align] = options[:align]
52
+ options[:text][:valign] = options[:valign]
53
+
54
+ @text = options[:text][:text]
55
+ @text_options = options[:text].reject { |k,_| k == :text }
56
+ end
57
+
58
+ if options[:image]
59
+ unless options[:image].kind_of? Hash
60
+ options[:image] = { :path => options[:image] }
61
+ end
62
+ options[:image][:position] = options[:align]
63
+ options[:image][:vposition] = options[:valign]
64
+
65
+ @image_path = options[:image][:path]
66
+ @image_options = options[:image].reject { |k,_| k == :path }
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,124 @@
1
+ require 'yaml'
2
+ module TnPDF
3
+
4
+ class Configuration
5
+ class << self
6
+
7
+
8
+ def [](property)
9
+ property = property.to_s
10
+ case property
11
+ when /^page_header_/
12
+ property_key = property.sub('page_header_','').to_sym
13
+ header[property_key]
14
+ when /^page_footer_/
15
+ property_key = property.sub('page_footer_','').to_sym
16
+ footer[property_key]
17
+ when /^table_/
18
+ property_key = property.sub('table_','').to_sym
19
+ table[property_key]
20
+ when /^column_/
21
+ property_key = property.sub('column_','').to_sym
22
+ column[property_key]
23
+ else
24
+ report[property.to_sym]
25
+ end
26
+ end
27
+
28
+ def report_properties_names
29
+ report.keys
30
+ end
31
+
32
+ def header_properties_names
33
+ header.keys
34
+ end
35
+
36
+ def footer_properties_names
37
+ footer.keys
38
+ end
39
+
40
+ def table_properties_names
41
+ table.keys
42
+ end
43
+
44
+ def properties_names
45
+ report_properties_names.map { |p| "report_#{p}" } +
46
+ header_properties_names.map { |p| "page_header_#{p}" } +
47
+ footer_properties_names.map { |p| "page_footer_#{p}" } +
48
+ table_properties_names.map { |p| "table_#{p}" }
49
+ end
50
+
51
+ def load_from(yaml_file)
52
+ configurations = YAML.load_file(yaml_file)
53
+ configurations.to_options!
54
+ configurations.each_key do |item|
55
+ self.send(item).merge! configurations[item].to_options!
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def report
62
+ @report ||= {
63
+ :page_size => "A4",
64
+ :page_layout => :landscape,
65
+ :left_margin => 1.cm,
66
+ :right_margin => 1.cm,
67
+ :top_margin => 0.cm,
68
+ :bottom_margin => 0.cm,
69
+ :font => "Courier",
70
+ :font_size => 10,
71
+ }
72
+ end
73
+
74
+ def header
75
+ @header ||= {
76
+ :height => 1.cm,
77
+ :top => 0.5.cm,
78
+ :bottom => 0.2.cm,
79
+ }
80
+ end
81
+
82
+
83
+ def footer
84
+ @footer ||= {
85
+ :height => 1.cm,
86
+ :top => 0.2.cm,
87
+ :bottom => 0.1.cm,
88
+ }
89
+ end
90
+
91
+ def table
92
+ @table ||= {
93
+ :align => :center,
94
+ :multipage_headers => true,
95
+ :borders => false,
96
+ :header_color => "FF0000",
97
+ :odd_row_color => "00FF00",
98
+ :even_row_color => "0000FF",
99
+ }
100
+ end
101
+
102
+ def column
103
+ @column ||= {
104
+ :currency => { :format => "%0.2f",
105
+ :align => :right,
106
+ :decimal => "," },
107
+
108
+ :number => { :format => "%d",
109
+ :align => :right},
110
+
111
+ :date => { :format => "%d/%m/%Y",
112
+ :align => :center },
113
+
114
+ :float => { :format => "%0.3f",
115
+ :align => :right,
116
+ :decimal => "," },
117
+
118
+ :text => { :format => "%s",
119
+ :align => :left }
120
+ }
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,56 @@
1
+ module TnPDF
2
+ class PageSection
3
+ attr_reader :left, :right, :center
4
+ attr_reader :left_box, :center_box, :right_box
5
+ attr_writer :top, :bottom
6
+ attr_accessor :width, :height
7
+
8
+ def left=(options)
9
+ @left = options
10
+ options[:align] = :left
11
+ @left_box = Box.new(options)
12
+ end
13
+
14
+ def center=(options)
15
+ @center = options
16
+ options[:align] = :center
17
+ @center_box = Box.new(options)
18
+ end
19
+
20
+ def right=(options)
21
+ @right = options
22
+ options[:align] = :right
23
+ @right_box = Box.new(options)
24
+ end
25
+
26
+ def total_height
27
+ height + top + bottom
28
+ end
29
+
30
+ def boxes
31
+ [left_box, center_box, right_box].compact
32
+ end
33
+
34
+ def top
35
+ @top ||= 0
36
+ end
37
+
38
+ def bottom
39
+ @bottom ||= 0
40
+ end
41
+
42
+ def render(document, position)
43
+ width ||= document.bounds.width
44
+
45
+ box_width = width/(boxes.count)
46
+
47
+ boxes.inject(0) do |offset, box|
48
+ x_pos = position[0] + offset
49
+ y_pos = position[1] - top
50
+ box.render(document, [x_pos, y_pos], box_width, height )
51
+ offset += box_width
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,131 @@
1
+ module TnPDF
2
+ class Report
3
+ attr_reader :page_header, :page_footer, :table, :record_collection
4
+ attr_accessor *Configuration.report_properties_names
5
+
6
+ def initialize(properties = {})
7
+ @page_header = PageSection.new
8
+ @page_footer = PageSection.new
9
+ @record_collection = Array.new
10
+ initialize_properties(properties)
11
+ end
12
+
13
+ class << self
14
+ private
15
+
16
+ def forward_property(object_name, property)
17
+ class_eval <<-STRING
18
+ def #{object_name}_#{property}
19
+ #{object_name}.#{property}
20
+ end
21
+
22
+ def #{object_name}_#{property}=(value)
23
+ #{object_name}.#{property} = value
24
+ end
25
+ STRING
26
+ end
27
+
28
+ end
29
+
30
+ Configuration.header_properties_names.each do |property|
31
+ forward_property("page_header", property)
32
+ end
33
+
34
+ Configuration.footer_properties_names.each do |property|
35
+ forward_property("page_footer", property)
36
+ end
37
+
38
+ Configuration.table_properties_names.each do |property|
39
+ forward_property("table", property)
40
+ end
41
+
42
+ def record_collection=(collection)
43
+ unless collection.kind_of? Array
44
+ raise ArgumentError, "collection should be an Array!"
45
+ end
46
+ @record_collection = table.collection = collection
47
+ end
48
+
49
+ def table_columns
50
+ table.columns || Array.new
51
+ end
52
+
53
+ def table_columns=(columns)
54
+ raise ArgumentError unless columns.kind_of? Array
55
+ columns.each do |column|
56
+ table.add_column column
57
+ end
58
+ end
59
+
60
+ def render(filename)
61
+ document_width = document.bounds.width
62
+ page_header_position = [0, document.cursor]
63
+ page_footer_position = [0, Configuration[:page_footer_height]]
64
+
65
+ document.font(font)
66
+ document.font_size(font_size)
67
+
68
+ document.repeat :all do
69
+ page_header.render(document, page_header_position)
70
+ document.stroke_horizontal_rule
71
+ end
72
+
73
+ table_height = page_body_height
74
+ document.bounding_box([0, page_body_height+page_footer.total_height],
75
+ :width => document.bounds.width) do
76
+ table.render(document, table_height)
77
+ end
78
+
79
+ document.repeat :all do
80
+ page_footer.render(document, page_footer_position)
81
+ end
82
+
83
+ document.render_file filename
84
+ end
85
+
86
+ def page_body_height
87
+ height = document.bounds.height
88
+ height -= page_header.total_height
89
+ height -= page_footer.total_height
90
+ end
91
+
92
+ def document
93
+ @document ||= Prawn::Document.new(properties)
94
+ end
95
+
96
+ def table
97
+ @table ||= Table.new
98
+ end
99
+
100
+ # Configurable properties
101
+
102
+ def properties
103
+ Configuration.report_properties_names.inject({}) do |properties_hash, property|
104
+ properties_hash[property] = send(property)
105
+ properties_hash
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def initialize_properties(properties)
112
+ owned_properties = Configuration.report_properties_names
113
+ owned_properties += Configuration.footer_properties_names.map do |p|
114
+ "page_footer_#{p}"
115
+ end
116
+ owned_properties += Configuration.header_properties_names.map do |p|
117
+ "page_header_#{p}"
118
+ end
119
+ owned_properties += Configuration.table_properties_names.map do |p|
120
+ "table_#{p}"
121
+ end
122
+
123
+ owned_properties.each do |property|
124
+ properties[property] ||= Configuration[property]
125
+ send(:"#{property}=", properties[property])
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,91 @@
1
+ require 'tn_pdf/table_column'
2
+ require 'prawn'
3
+
4
+ module TnPDF
5
+ class Table
6
+ attr_accessor *Configuration.table_properties_names
7
+
8
+ def initialize
9
+ Configuration.table_properties_names.each do |property|
10
+ send("#{property}=", Configuration["table_#{property}"])
11
+ end
12
+ end
13
+
14
+ def columns_hash
15
+ columns_hash = ActiveSupport::OrderedHash.new
16
+ columns.inject(columns_hash) do |hash, column|
17
+ hash[column.header] = column.to_proc
18
+ hash
19
+ end
20
+ end
21
+
22
+ def columns_headers
23
+ columns.map(&:header)
24
+ end
25
+
26
+ def collection
27
+ @collection ||= Array.new
28
+ end
29
+
30
+ def collection=(collection)
31
+ raise ArgumentError unless collection.kind_of? Array
32
+ @collection = collection
33
+ end
34
+
35
+ def add_column(column)
36
+ unless column.kind_of? Column
37
+ column = Column.new(column)
38
+ end
39
+ columns << column
40
+ end
41
+
42
+ def rows
43
+ collection.map do |object|
44
+ columns.map do |column|
45
+ column.value_for(object)
46
+ end
47
+ end
48
+ end
49
+
50
+ def render(document, max_height)
51
+ table = document.make_table([columns_headers]+rows) do |table|
52
+ table.header = self.multipage_headers
53
+ table.cells.borders = [] unless self.borders
54
+ table.row_colors = [self.odd_row_color, self.even_row_color]
55
+ table.row(0).background_color = self.header_color
56
+
57
+ columns.each_with_index do |column, index|
58
+ style = column.style.reject { |k, v| [:format, :decimal].include? k }
59
+ table.columns(index).style(style)
60
+ end
61
+ end
62
+ x_pos = x_pos_on(document, table.width)
63
+ document.bounding_box([x_pos, document.cursor],
64
+ :width => table.width,
65
+ :height => max_height) do
66
+ table.draw
67
+ end
68
+ end
69
+
70
+ def columns
71
+ @columns ||= []
72
+ end
73
+
74
+ private
75
+
76
+ def x_pos_on(document, table_width)
77
+ case align
78
+ when :left
79
+ 0
80
+ when :center
81
+ (document.bounds.right - table_width)/2.0
82
+ when :right
83
+ document.bounds.right - table_width
84
+ else
85
+ 0
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,67 @@
1
+ module TnPDF
2
+ class Table
3
+
4
+ class Column
5
+ attr_reader :header, :proc, :collection, :style
6
+
7
+ alias_method :to_proc, :proc
8
+ def initialize(arguments)
9
+ raise ArgumentError unless valid_column_args?(arguments)
10
+ @header = arguments[0].to_s
11
+ @proc = arguments[1].to_proc
12
+ @style = style_for(arguments[2])
13
+ end
14
+
15
+ def values_for(collection)
16
+ collection.map do |object|
17
+ value_for(object)
18
+ end
19
+ end
20
+
21
+ def value_for(object)
22
+ value = @proc.call(object)
23
+ method = if value.respond_to?(:strftime)
24
+ value.method(:strftime)
25
+ elsif value.respond_to?(:sprintf)
26
+ value.method(:sprintf)
27
+ else
28
+ method(:sprintf)
29
+ end
30
+ string = method.arity == 1 ?
31
+ method.call(style[:format]) :
32
+ method.call(style[:format], value)
33
+
34
+ string.gsub!(".", style[:decimal]) if style[:decimal]
35
+ return string
36
+ rescue TypeError
37
+ puts "WARNING: Bad format '#{style[:format]}' for value '#{value}'"
38
+ return value.to_s
39
+ end
40
+
41
+ private
42
+
43
+ def valid_column_args?(column_args)
44
+ validity = true
45
+ validity &= column_args.kind_of? Array
46
+ validity &= column_args.count == 2 || column_args.count == 3
47
+ validity &= column_args[0].respond_to?(:to_s)
48
+ validity &= column_args[1].respond_to?(:to_proc)
49
+ rescue NoMethodError
50
+ valid = false
51
+ ensure
52
+ return validity
53
+ end
54
+
55
+ def style_for(type)
56
+ if type.nil?
57
+ {:format => "%s"}
58
+ elsif type.kind_of? Symbol
59
+ Configuration["column_#{type}"]
60
+ else
61
+ type
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module TnPDF
2
+ VERSION = "0.0.2"
3
+ end
data/lib/tn_pdf.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'active_support' # Because of to_options!
3
+ require 'prawn'
4
+ require 'prawn/measurement_extensions'
5
+
6
+ require 'tn_pdf/configuration'
7
+ require 'tn_pdf/report'
8
+ require 'tn_pdf/page_section'
9
+ require 'tn_pdf/box'
10
+ require 'tn_pdf/table'
11
+ require 'tn_pdf/table_column'
@@ -0,0 +1,18 @@
1
+ # Adds the "lib" dir to the load path
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
3
+ require 'tn_pdf'
4
+
5
+ # Used to 'stub' needed classes that doesn't exist (yet)
6
+ class EmptyClass
7
+ def method_missing(method,*args)
8
+ nil
9
+ end
10
+ end
11
+
12
+ require 'prawn'
13
+
14
+ # Utility methods:
15
+
16
+ def random_string
17
+ (Time.now.to_f+rand(1000)).to_s
18
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ module TnPDF
4
+ class PageSection
5
+
6
+ describe Box do
7
+
8
+ subject { Box.new( {} ) }
9
+
10
+ describe "creation options" do
11
+ it "accept texts and/or images" do
12
+ assigning_nice_values = lambda do
13
+ Box.new( { :text => "text" } )
14
+ Box.new( { :image => "image.jpg" } )
15
+ Box.new( { :text => "text", :image => "image.jpg" } )
16
+ end
17
+ assigning_nice_values.should_not raise_error
18
+ end
19
+
20
+ it "accepts extra text options to prawn by passing hashes" do
21
+ document = Prawn::Document.new
22
+ box = Box.new( :text => {:text => "text", :font => "DejaVu Sans"} )
23
+
24
+ document.should_receive(:text).with("text", hash_including(:font => "DejaVu Sans") )
25
+ box.render(document, [0,0], 100)
26
+ end
27
+
28
+ it "accepts extra image options to prawn by passing hashes" do
29
+ document = Prawn::Document.new
30
+ box = Box.new( :image => {:path => "image.jpg", :width => 1.cm } )
31
+
32
+ document.should_receive(:image).with("image.jpg", hash_including(:width => 1.cm) )
33
+ box.render(document, [0,0], 100)
34
+ end
35
+ end
36
+
37
+ describe "#render" do
38
+ let(:document) { Prawn::Document.new }
39
+
40
+ it "creates a bounding box on the provided document" do
41
+ document.should_receive(:bounding_box).with([0,0], hash_including(:width => 20.cm) )
42
+ subject.render(document, [0,0], 20.cm)
43
+ end
44
+
45
+ specify "when the box contains text, the text is rendered" do
46
+ subject = Box.new({ :text => "some text" })
47
+
48
+ document.should_receive(:text).with("some text", anything())
49
+ subject.render(document, [0, 0], 20.cm)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ module TnPDF
4
+
5
+ describe PageSection do
6
+
7
+ describe "its left, center and right parts" do
8
+ let(:parts) { [:left, :center, :right] }
9
+
10
+ specify "exist and can be set" do
11
+ parts.each do |part|
12
+ subject.should respond_to(part)
13
+ subject.should respond_to(:"#{part}=")
14
+ end
15
+ end
16
+
17
+ specify "are accessible as hashes, and also as boxes" do
18
+ parts.each do |part|
19
+ subject.send("#{part}=", {} )
20
+ subject.send("#{part}").should be_kind_of Hash
21
+ subject.send("#{part}_box").should be_kind_of PageSection::Box
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "#render" do
27
+ let(:document) { Prawn::Document.new }
28
+
29
+ it "renders each of the defined boxes on the provided document" do
30
+ subject.left = {:text => "Blah"}
31
+ subject.center = {:text => "Blah"}
32
+ subject.right = {:text => "Blah"}
33
+
34
+ subject.left_box.should_receive(:render)
35
+ subject.center_box.should_receive(:render)
36
+ subject.right_box.should_receive(:render)
37
+
38
+ subject.render(document, [0, 0])
39
+ end
40
+
41
+ it "divides the available width between the defined boxes" do
42
+ subject.left = {:text => "Blah"}
43
+ subject.right = {:text => "Blah"}
44
+
45
+ subject.left_box.should_receive(:render).with(
46
+ document, [0, 0], document.bounds.width/2, nil)
47
+
48
+ subject.render(document, [0, 0])
49
+
50
+
51
+ subject = PageSection.new
52
+ subject.left = {:text => "Blah"}
53
+
54
+ subject.left_box.should_receive(:render).with(
55
+ document, [0, 0], document.bounds.width, nil)
56
+
57
+ subject.render(document, [0, 0])
58
+ end
59
+
60
+ it "translates the second and third boxes to the right" do
61
+ subject.left = {:text => "Blah"}
62
+ subject.center = {:text => "Blah"}
63
+ subject.right = {:text => "Blah"}
64
+
65
+ box_width = document.bounds.width/3
66
+ subject.left_box.should_receive(:render).with(
67
+ document, [0, 0], box_width, nil)
68
+
69
+ subject.center_box.should_receive(:render).with(
70
+ document, [box_width, 0], box_width, nil)
71
+
72
+ subject.right_box.should_receive(:render).with(
73
+ document, [box_width*2, 0], box_width, nil)
74
+
75
+ subject.render(document, [0, 0])
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module TnPDF
4
+ const_set("PageSection", EmptyClass) unless const_defined?("PageSection")
5
+ const_set("Table", EmptyClass) unless const_defined?("Table")
6
+
7
+ describe Report do
8
+
9
+ describe "#page_header and #page_footer" do
10
+ specify "are page sections" do
11
+ subject.page_header.should be_kind_of TnPDF::PageSection
12
+ subject.page_footer.should be_kind_of TnPDF::PageSection
13
+ end
14
+
15
+ specify "aren't publicly modifiable" do
16
+ direct_assignment = lambda do
17
+ subject.page_header = :some_section
18
+ subject.page_footer = :some_section
19
+ end
20
+ direct_assignment.should raise_error
21
+ end
22
+
23
+ specify "are modifiable by page_header_left and page_footer_left" do
24
+ subject.page_header.should_receive(:left=)
25
+ subject.page_footer.should_receive(:left=)
26
+
27
+ subject.page_header_left = { :text => random_string }
28
+ subject.page_footer_left = { :text => random_string }
29
+ end
30
+
31
+ specify "are modifiable by page_header_right and page_footer_right" do
32
+ subject.page_header.should_receive(:right=)
33
+ subject.page_footer.should_receive(:right=)
34
+
35
+ subject.page_header_right = { :text => random_string }
36
+ subject.page_footer_right = { :text => random_string }
37
+ end
38
+
39
+ specify "are modifiable by page_header_center and page_footer_center" do
40
+ subject.page_header.should_receive(:center=)
41
+ subject.page_footer.should_receive(:center=)
42
+
43
+ subject.page_header_center = { :text => random_string }
44
+ subject.page_footer_center = { :text => random_string }
45
+ end
46
+
47
+ end
48
+
49
+ describe "#record_collection" do
50
+ it "is a kind of array" do
51
+ subject.record_collection.should be_kind_of Array
52
+ end
53
+
54
+ it "is settable by the record_collection= method" do
55
+ subject.record_collection = [:a, :b, :c]
56
+ subject.record_collection.should == [:a, :b, :c]
57
+ end
58
+
59
+ it "doesn't accept non-array arguments" do
60
+ invalid_values = [ Hash.new, :collection, "collection", 123]
61
+ invalid_values.each do |invalid_value|
62
+ setting_invalid_value = lambda do
63
+ subject.record_collection = invalid_value
64
+ end
65
+ setting_invalid_value.should raise_error
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#table_columns" do
71
+ it "is a kind of array" do
72
+ subject.table_columns.should be_kind_of Array
73
+ end
74
+ end
75
+
76
+ describe "#table_columns=" do
77
+ it "is a proxy to Table#add_column" do
78
+ subject.table.should_receive(:add_column)
79
+
80
+ column = [ [ 'String' , :to_s ] ]
81
+ subject.table_columns = column
82
+ end
83
+
84
+ it "works with multiple columns" do
85
+ subject.table.should_receive(:add_column).exactly(3).times
86
+
87
+ columns = [ [ 'String' => :to_s] ,
88
+ [ 'Object id' => :object_id] ,
89
+ [ 'Name' => :name] ]
90
+ subject.table_columns = columns
91
+ end
92
+ end
93
+
94
+ describe "#document" do
95
+ it "returns the Prawn::Document associated with the report" do
96
+ subject.document.should be_kind_of Prawn::Document
97
+ end
98
+ end
99
+
100
+ it "has all the properties defined on Report" do
101
+ Configuration.properties_names do |property|
102
+ subject.should respond_to(property)
103
+ subject.should respond_to(:"#{property}=")
104
+ end
105
+ end
106
+
107
+ specify "passing a hash of options on initialization works" do
108
+ properties = { :page_layout => :portrait,
109
+ :left_margin => 5.cm }
110
+ subject = Report.new(properties)
111
+ subject.page_layout.should == :portrait
112
+ subject.left_margin.should == 5.cm
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ module TnPDF
4
+ class Table
5
+ describe Column do
6
+
7
+ context "to be valid, needs a parameter that is" do
8
+
9
+ specify "an Array" do
10
+ creating_bad_column = Proc.new { Table::Column.new :not_an_array }
11
+ creating_bad_column.should raise_error ArgumentError
12
+ end
13
+
14
+ specify "an Array whose 1st member can be coerced into a String" do
15
+ bad_string = /not_a_string/
16
+ class << bad_string
17
+ undef_method(:to_s)
18
+ end
19
+ creating_bad_column = Proc.new { Table::Column.new [bad_string, :to_s] }
20
+ creating_bad_column.should raise_error ArgumentError
21
+ end
22
+
23
+ specify "an Array whose 2nd member can be coerced into a Proc" do
24
+ bad_columns = [["String", "to_s"], ["String", 3], ["String", /to_s/]]
25
+ bad_columns.each do |bad_column|
26
+ creating_bad_column = Proc.new { Table::Column.new bad_column }
27
+ creating_bad_column.should raise_error ArgumentError
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#header" do
33
+ it "returns the string value that was passed as parameter" do
34
+ column_args = ["String", :to_s]
35
+ column = Table::Column.new(column_args)
36
+ column.header.should == "String"
37
+ end
38
+ end
39
+
40
+ describe "#to_proc" do
41
+ it "returns the proc that was passed as parameter" do
42
+ my_proc = Proc.new { |object| object.to_s }
43
+ column = Table::Column.new([123, my_proc])
44
+ column.to_proc.should == my_proc
45
+ end
46
+ end
47
+
48
+ describe "#values_for" do
49
+ it "maps received values to the underlying proc" do
50
+ column = Table::Column.new(["String", :to_s])
51
+ numbers = (1..3).to_a
52
+ column.values_for(numbers).should == %w[1 2 3]
53
+ end
54
+ end
55
+
56
+ describe "#value_for" do
57
+ it "maps received object to the underlying proc" do
58
+ column = Table::Column.new(["String", :to_s])
59
+ column.value_for(25).should == "25"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ module TnPDF
4
+ class Table
5
+ const_set("Column",EmptyClass) unless const_defined?("Column")
6
+ end
7
+
8
+ describe Table do
9
+ describe "#columns_hash" do
10
+ it "is a kind of Hash" do
11
+ subject.columns_hash.should be_kind_of(Hash)
12
+ end
13
+
14
+ it "is ordered" do
15
+ subject.add_column ["A", :A]
16
+ subject.add_column ["B", :B]
17
+ subject.add_column ["C", :C]
18
+
19
+ subject.columns_hash.keys.should == %w[A B C]
20
+ end
21
+ end
22
+
23
+ describe "#collection" do
24
+ it "is a kind of Array" do
25
+ subject.collection.should be_kind_of(Array)
26
+ end
27
+
28
+ it "is settable, as long as the parameter is also an Array" do
29
+ setting_good_parameter = Proc.new do
30
+ subject.collection = [:a, :b, :c]
31
+ end
32
+ setting_good_parameter.should_not raise_error
33
+ subject.collection.should == [:a, :b, :c]
34
+ end
35
+
36
+ it "raises an error if someone tries to set it as a non-array" do
37
+ setting_bad_parameter = Proc.new do
38
+ subject.collection = :not_an_array
39
+ end
40
+ setting_bad_parameter.should raise_error
41
+ subject.collection.should_not == :not_an_array
42
+ end
43
+ end
44
+
45
+ describe "#add_column" do
46
+ let(:valid_column) { column = ["String", :to_s] }
47
+
48
+ it "adds a supplied column to the columns hash" do
49
+ adding_valid_column = Proc.new do
50
+ subject.add_column(valid_column)
51
+ end
52
+ adding_valid_column.should change(subject.columns, :count).by(1)
53
+ end
54
+
55
+ it "should the argument be a column, it adds directly" do
56
+ column = Table::Column.new [ "String", :to_s ]
57
+ subject.add_column(column)
58
+ subject.columns.should include(column)
59
+ end
60
+ end
61
+
62
+ describe "#columns_headers" do
63
+ it "returns the columns headers" do
64
+ subject.add_column ["String", :to_s]
65
+ subject.add_column ["Integer", :to_i]
66
+ subject.add_column ["Mean", :mean]
67
+
68
+ subject.columns_headers.should == %w[String Integer Mean]
69
+ end
70
+ end
71
+
72
+ describe "#rows" do
73
+ it "returns the values of the objects for each column" do
74
+ subject = Table.new # Just to be explicit
75
+ subject.add_column( ["String", :to_s] )
76
+ subject.add_column( ["Integer", :to_i] )
77
+ subject.add_column( ["Doubled", Proc.new { |x| x*2 } ] )
78
+
79
+ subject.collection = [1, 2, 3]
80
+ subject.rows.should == [ ["1", 1, 2],
81
+ ["2", 2, 4],
82
+ ["3", 3, 6] ]
83
+ end
84
+ end
85
+
86
+ describe "#render" do
87
+ let(:document) do
88
+ mock("Prawn::Document").as_null_object
89
+ end
90
+
91
+ let(:table) do
92
+ mock("Prawn::Table").as_null_object
93
+ end
94
+
95
+ it "instantiates a Prawn::Table instance" do
96
+ document.should_receive(:make_table).and_return(table)
97
+ subject.render(document, 0)
98
+ end
99
+ end
100
+ end
101
+ end
data/tn_pdf.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "tn_pdf/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "tn_pdf"
7
+ s.version = TnPDF::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Renato Riccieri Santos Zannon"]
10
+ s.email = ["zannon@tecnologiaenegocios.com.br"]
11
+ s.summary = %q{A simple wrapper around prawn, devised to the generation of table-centric reports}
12
+
13
+ s.rubyforge_project = "tn_pdf"
14
+
15
+ s.add_dependency('prawn', '~> 0.11.1')
16
+ s.add_dependency('activesupport')
17
+
18
+ s.add_development_dependency('rspec', '~> 2.6.0')
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.require_paths = ["lib"]
23
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tn_pdf
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Renato Riccieri Santos Zannon
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-31 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: prawn
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 49
30
+ segments:
31
+ - 0
32
+ - 11
33
+ - 1
34
+ version: 0.11.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: activesupport
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: rspec
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 23
60
+ segments:
61
+ - 2
62
+ - 6
63
+ - 0
64
+ version: 2.6.0
65
+ type: :development
66
+ version_requirements: *id003
67
+ description:
68
+ email:
69
+ - zannon@tecnologiaenegocios.com.br
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - Rakefile
81
+ - lib/tn_pdf.rb
82
+ - lib/tn_pdf/box.rb
83
+ - lib/tn_pdf/configuration.rb
84
+ - lib/tn_pdf/page_section.rb
85
+ - lib/tn_pdf/report.rb
86
+ - lib/tn_pdf/table.rb
87
+ - lib/tn_pdf/table_column.rb
88
+ - lib/tn_pdf/version.rb
89
+ - spec/spec_helper.rb
90
+ - spec/tn_pdf/box_spec.rb
91
+ - spec/tn_pdf/page_section_spec.rb
92
+ - spec/tn_pdf/report_spec.rb
93
+ - spec/tn_pdf/table_column_spec.rb
94
+ - spec/tn_pdf/table_spec.rb
95
+ - tn_pdf.gemspec
96
+ has_rdoc: true
97
+ homepage:
98
+ licenses: []
99
+
100
+ post_install_message:
101
+ rdoc_options: []
102
+
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ requirements: []
124
+
125
+ rubyforge_project: tn_pdf
126
+ rubygems_version: 1.5.2
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: A simple wrapper around prawn, devised to the generation of table-centric reports
130
+ test_files: []
131
+