ruport 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/AUTHORS +7 -3
  2. data/Rakefile +8 -9
  3. data/TODO +16 -0
  4. data/examples/RWEmerson.jpg +0 -0
  5. data/examples/centered_pdf_text_box.rb +66 -0
  6. data/examples/invoice.rb +35 -25
  7. data/examples/invoice_report.rb +1 -1
  8. data/examples/line_plotter.rb +1 -1
  9. data/examples/pdf_table_with_title.rb +42 -0
  10. data/lib/ruport.rb +5 -7
  11. data/lib/ruport.rb.rej +41 -0
  12. data/lib/ruport.rb~ +85 -0
  13. data/lib/ruport/attempt.rb +59 -59
  14. data/lib/ruport/config.rb +15 -4
  15. data/lib/ruport/data.rb +0 -2
  16. data/lib/ruport/data/groupable.rb +25 -16
  17. data/lib/ruport/data/record.rb +128 -102
  18. data/lib/ruport/data/table.rb +352 -199
  19. data/lib/ruport/data/taggable.rb +18 -7
  20. data/lib/ruport/format/html.rb +3 -1
  21. data/lib/ruport/format/latex.rb +1 -1
  22. data/lib/ruport/format/latex.rb.rej +26 -0
  23. data/lib/ruport/format/latex.rb~ +47 -0
  24. data/lib/ruport/format/pdf.rb +111 -28
  25. data/lib/ruport/format/pdf.rb.rej +168 -0
  26. data/lib/ruport/format/pdf.rb~ +189 -0
  27. data/lib/ruport/format/plugin.rb +0 -5
  28. data/lib/ruport/format/svg.rb +4 -4
  29. data/lib/ruport/format/xml.rb +3 -3
  30. data/lib/ruport/generator.rb +66 -27
  31. data/lib/ruport/mailer.rb +4 -1
  32. data/lib/ruport/query.rb +13 -1
  33. data/lib/ruport/renderer.rb +89 -17
  34. data/lib/ruport/renderer/graph.rb +5 -5
  35. data/lib/ruport/renderer/table.rb +8 -9
  36. data/lib/ruport/report.rb +2 -6
  37. data/test/test_config.rb +88 -76
  38. data/test/{test_text_table.rb → test_format_text.rb} +4 -2
  39. data/test/test_groupable.rb +15 -13
  40. data/test/test_query.rb +6 -3
  41. data/test/test_record.rb +57 -33
  42. data/test/test_renderer.rb +77 -0
  43. data/test/test_report.rb +188 -181
  44. data/test/test_ruport.rb +5 -6
  45. data/test/test_table.rb +290 -190
  46. data/test/test_table_renderer.rb +56 -8
  47. data/test/test_taggable.rb +7 -8
  48. data/test/unit.log +259 -7
  49. metadata +22 -19
  50. data/lib/ruport/data/collection.rb +0 -65
  51. data/lib/ruport/data/set.rb +0 -148
  52. data/test/test_collection.rb +0 -30
  53. data/test/test_set.rb +0 -118
@@ -5,7 +5,8 @@
5
5
  # Copyright 2006 by respective content owners, all rights reserved.
6
6
  module Ruport::Data
7
7
 
8
-
8
+ require 'set'
9
+ #
9
10
  # === Overview
10
11
  #
11
12
  # This module provides a simple mechanism for tagging arbitrary objects.
@@ -14,6 +15,7 @@ module Ruport::Data
14
15
  #
15
16
  module Taggable
16
17
 
18
+ #
17
19
  # Adds a tag to the object.
18
20
  #
19
21
  # Example:
@@ -21,9 +23,10 @@ module Ruport::Data
21
23
  # taggable_obj.tag :spiffy
22
24
  #
23
25
  def tag(tag_name)
24
- tags << tag_name unless has_tag? tag_name
26
+ tags << tag_name
25
27
  end
26
28
 
29
+ #
27
30
  # Removes a tag from the object.
28
31
  #
29
32
  # Example:
@@ -34,6 +37,7 @@ module Ruport::Data
34
37
  tags.delete tag_name
35
38
  end
36
39
 
40
+ #
37
41
  # Checks to see if a tag is present.
38
42
  #
39
43
  # Example:
@@ -44,24 +48,31 @@ module Ruport::Data
44
48
  tags.include? tag_name
45
49
  end
46
50
 
51
+ #
47
52
  # Returns an Array of the object's tags.
48
53
  #
49
54
  # Example:
50
55
  #
51
56
  # taggable_obj.tags #=> [:spiffy, :kind_of_spiffy]
52
57
  #
53
- def tags
54
- @ruport_tags ||= []
58
+ def tags
59
+ @ruport_tags ||= Set.new
55
60
  end
56
61
 
57
- # Sets the tags to some Array.
62
+ #
63
+ # Sets the tags.
58
64
  #
59
65
  # Example:
60
66
  #
61
67
  # taggable_obj.tags = [:really_dang_spiffy, :the_most_spiffy]
62
68
  #
63
- def tags=(tags_list)
64
- @ruport_tags = tags_list
69
+ def tags=(tags_list)
70
+ case tags_list
71
+ when Array
72
+ @ruport_tags = Set.new(tags_list)
73
+ else
74
+ @ruport_tags = tags_list
75
+ end
65
76
  end
66
77
 
67
78
  end
@@ -24,7 +24,9 @@ module Ruport::Format
24
24
  def build_table_body
25
25
  output << data.inject("") do |s,r|
26
26
  row = r.map { |e| e.to_s.empty? ? "&nbsp;" : e }
27
- classstr = r.tags.inject("") {|cs,c| cs + " class='#{c}'" }
27
+ #classstr = r.tags.inject("") {|cs,c| cs + " class='#{c}'" }
28
+ classstr =
29
+ r.tags.length > 0 ? " class='#{r.tags.to_a.join(' ')}'" : ""
28
30
  s + "\t\t<tr#{classstr}>\n\t\t\t<td#{classstr}>" +
29
31
  row.to_a.join("</td>\n\t\t\t<td#{classstr}>") +
30
32
  "</td>\n\t\t</tr>\n"
@@ -30,7 +30,7 @@ module Ruport::Format
30
30
 
31
31
  def build_table_body
32
32
  data.each do |r|
33
- output << r.data.join(" & ") + "\\\\\n"
33
+ output << r.to_a.join(" & ") + "\\\\\n"
34
34
  output << "\\hline\n"
35
35
  end
36
36
  if caption
@@ -0,0 +1,26 @@
1
+ ***************
2
+ *** 15,27 ****
3
+ output << " }\n"
4
+ output << "\\hline\n"
5
+
6
+ - #FIXME: this ain't ruby, jh ;)
7
+ - counter = 0
8
+ -
9
+ - data.column_names.each do |t|
10
+ - output << " & " unless counter == 0
11
+ output << "\\textsc{#{t}}"
12
+ - counter += 1
13
+ end
14
+
15
+ output << "\\\\\n"
16
+ --- 15,24 ----
17
+ output << " }\n"
18
+ output << "\\hline\n"
19
+
20
+ + output << "\\textsc{#{data.column_names[0]}}"
21
+ + data.column_names[1..-1].each do |t|
22
+ + output << " & "
23
+ output << "\\textsc{#{t}}"
24
+ end
25
+
26
+ output << "\\\\\n"
@@ -0,0 +1,47 @@
1
+ module Ruport::Format
2
+ class Latex < Plugin
3
+
4
+ attr_accessor :caption
5
+
6
+ def build_table_header
7
+ output << "\\documentclass[11pt]{article}\n" <<
8
+ "\\RequirePackage{lscape,longtable}\n" <<
9
+ "\\begin{document}\n" <<
10
+ "\\begin{longtable}[c]{ "
11
+
12
+ data.column_names.each do
13
+ output << " p{2cm} "
14
+ end
15
+ output << " }\n"
16
+ output << "\\hline\n"
17
+
18
+ output << "\\textsc{#{data.column_names[0]}}"
19
+ data.column_names[1..-1].each do |t|
20
+ output << " & "
21
+ output << "\\textsc{#{t}}"
22
+ end
23
+
24
+ output << "\\\\\n"
25
+ output << "\\hline\n"
26
+ output << "\\endhead\n"
27
+ output << "\\endfoot\n"
28
+ output << "\\hline\n"
29
+ end
30
+
31
+ def build_table_body
32
+ data.each do |r|
33
+ output << r.to_a.join(" & ") + "\\\\\n"
34
+ output << "\\hline\n"
35
+ end
36
+ if caption
37
+ output << "\\caption[#{caption}]{#{caption}}\n"
38
+ end
39
+ output << "\\end{longtable}\n"
40
+ end
41
+
42
+ def build_table_footer
43
+ output << "\\end{document}\n"
44
+ end
45
+
46
+ end
47
+ end
@@ -11,10 +11,6 @@ module Ruport::Format
11
11
  # * table_width
12
12
  # * max_table_width #=> 500
13
13
  #
14
- # This class makes extensive use of Austin Zeigler's PDF::Writer
15
- # Please refer to the API documentation for PDF::Writer if you need more
16
- # information.
17
- #
18
14
  class PDF < Plugin
19
15
  attr_writer :pdf_writer
20
16
  attr_accessor :table_header_proc
@@ -77,45 +73,132 @@ module Ruport::Format
77
73
  def add_text(*args)
78
74
  pdf_writer.text(*args)
79
75
  end
76
+
77
+ # - if the image is bigger than the box, it will be scaled down until it fits
78
+ # - if the image is smaller than the box it's won't be resized
79
+ #
80
+ # arguments:
81
+ # - x: left bound of box
82
+ # - y: bottom bound of box
83
+ # - width: width of box
84
+ # - height: height of box
85
+ def center_image_in_box(path, x, y, width, height)
86
+ info = ::PDF::Writer::Graphics::ImageInfo.new(File.read(path))
87
+
88
+ # if the image is larger than the requested box, prepare to
89
+ # scale it down
90
+ fits = !(info.width > width || info.height > height)
91
+
92
+ # setup initial sizes for the image. These will be reduced as necesary
93
+ img_width = info.width
94
+ img_height = info.height
95
+ img_ratio = info.height.to_f / info.width.to_f
96
+
97
+ # reduce the size of the image until it fits into the requested box
98
+ until fits
99
+ img_width -= 1
100
+ img_height = img_width * img_ratio
101
+ fits = true if img_width < width && img_height < height
102
+ end
103
+
104
+ # if the width of the image is less than the requested box, calculate
105
+ # the white space buffer
106
+ if img_width < width
107
+ white_space = width - img_width
108
+ x = x + (white_space / 2)
109
+ end
110
+
111
+ # if the height of the image is less than the requested box, calculate
112
+ # the white space buffer
113
+ if img_height < height
114
+ white_space = height - img_height
115
+ y = y + (white_space / 2)
116
+ end
117
+
118
+ pdf_writer.add_image_from_file(path, x, y, img_width, img_height)
119
+ end
120
+
121
+ # Draws some text on the canvas, surrounded by a box with rounded corners
122
+ #
123
+ def rounded_text_box(text)
124
+ opts = OpenStruct.new
125
+ yield(opts)
126
+
127
+ # resize the text automatically to ensure it isn't wider than the box
128
+ loop do
129
+ sz = pdf_writer.text_width( text, opts.font_size )
130
+ opts.x + sz > opts.x + opts.width or break
131
+ opts.font_size -= 1
132
+ end
133
+
134
+ # save the drawing state (colors, etc) so we can restore it later
135
+ pdf_writer.save_state
136
+
137
+ # draw our box
138
+ pdf_writer.fill_color(opts.fill_color || Color::RGB::White)
139
+ pdf_writer.stroke_color(opts.stroke_color || Color::RGB::Black)
140
+ pdf_writer.rounded_rectangle( opts.x, opts.y,
141
+ opts.width, opts.height,
142
+ opts.radius).fill_stroke
143
+
144
+ # if a heading for this box has been specified
145
+ if opts.heading
146
+ pdf_writer.line( opts.x, opts.y - 20,
147
+ opts.x + opts.width, opts.y - 20).stroke
148
+ pdf_writer.fill_color(Color::RGB::Black)
149
+ move_cursor_to(opts.y - 3)
150
+ add_text("<b>#{opts.heading}</b>",
151
+ :absolute_left => opts.x, :absolute_right => opts.x + opts.width,
152
+ :justification => :center, :font_size => opts.font_size)
153
+ end
154
+
155
+ # restore the original colors
156
+ pdf_writer.restore_state
157
+
158
+ # move our y cursor into position, write the text, then move the cursor
159
+ # to be just below the box
160
+ pdf_writer.y = opts.heading ? opts.y - 20 : opts.y
161
+
162
+ add_text( text, :absolute_left => opts.x,
163
+ :absolute_right => opts.x + opts.width,
164
+ :justification => opts.justification || :center,
165
+ :font_size => opts.font_size )
166
+
167
+ pdf_writer.y = opts.y - opts.height
168
+ end
169
+
170
+ # adds an image to every page. The color and size won't be modified,
171
+ # but it will be centered.
172
+ #
173
+ def watermark(imgpath)
174
+ x = pdf_writer.absolute_left_margin
175
+ y = pdf_writer.absolute_bottom_margin
176
+ width = pdf_writer.absolute_right_margin - x
177
+ height = pdf_writer.absolute_top_margin - y
178
+
179
+ pdf_writer.open_object do |wm|
180
+ pdf_writer.save_state
181
+ center_image_in_box(imgpath, x, y, width, height)
182
+ pdf_writer.restore_state
183
+ pdf_writer.close_object
184
+ pdf_writer.add_object(wm, :all_pages)
185
+ end
186
+ end
80
187
 
81
- # Adds n to PDF::Writer#y.
82
- #
83
- # Basically, this allows you to move the rendering cursor up and down the page.
84
- #
85
- # move_cursor(10) #=> move up 10
86
- # move_cursor(-10) #=> move down 10
87
188
  def move_cursor(n)
88
189
  pdf_writer.y += n
89
190
  end
90
191
 
91
- # Sets PDF::Writer#y to n.
92
- #
93
- # This lets you move to a given height on the page.
94
- #
95
192
  def move_cursor_to(n)
96
193
  pdf_writer.y = n
97
194
  end
98
195
 
99
- # creates a margin of <tt>y</tt> amount and allows a block to be called to
100
- # render items within that margin.
101
- #
102
- # Example:
103
- #
104
- # pad(10) { add_text 'hello' } #=> creates text hello with vertical margin
105
- # of 10.
106
196
  def pad(y,&block)
107
197
  move_cursor -y
108
198
  block.call
109
199
  move_cursor -y
110
200
  end
111
201
 
112
- # Builds a PDF::SimpleTable from a Data::Table
113
- #
114
- # Can be customized via the follow layout parameters:
115
- #
116
- # max_table_width #=> defaults to 500
117
- # table_width #=> optionally set a fixed width for the table
118
- # orientation #=> defaults to :center
119
202
  def draw_table
120
203
  m = "Sorry, cant build PDFs from array like things (yet)"
121
204
  raise m if data.column_names.empty?
@@ -0,0 +1,168 @@
1
+ ***************
2
+ *** 11,65 ****
3
+ # * table_width
4
+ # * max_table_width #=> 500
5
+ #
6
+ class PDF < Plugin
7
+ attr_writer :pdf_writer
8
+ attr_accessor :table_header_proc
9
+ attr_accessor :table_footer_proc
10
+
11
+ def initialize
12
+ require "pdf/writer"
13
+ require "pdf/simpletable"
14
+ end
15
+
16
+ def pdf_writer
17
+ @pdf_writer ||=
18
+ ::PDF::Writer.new( :paper => layout.paper_size || "LETTER" )
19
+ end
20
+
21
+ def build_table_header
22
+ table_header_proc[pdf_writer] if table_header_proc
23
+ end
24
+
25
+ def build_table_body
26
+ draw_table
27
+ end
28
+
29
+ def build_table_footer
30
+ table_footer_proc[pdf_writer] if table_footer_proc
31
+ end
32
+
33
+ def finalize_table
34
+ output << pdf_writer.render
35
+ end
36
+
37
+ def add_text(*args)
38
+ pdf_writer.text(*args)
39
+ end
40
+
41
+ def move_cursor(n)
42
+ pdf_writer.y += n
43
+ end
44
+
45
+ def move_cursor_to(n)
46
+ pdf_writer.y = n
47
+ end
48
+
49
+ def pad(y,&block)
50
+ move_cursor -y
51
+ block.call
52
+ move_cursor -y
53
+ end
54
+
55
+ def draw_table
56
+ m = "Sorry, cant build PDFs from array like things (yet)"
57
+ raise m if data.column_names.empty?
58
+ --- 11,120 ----
59
+ # * table_width
60
+ # * max_table_width #=> 500
61
+ #
62
+ + # This class makes extensive use of Austin Zeigler's PDF::Writer
63
+ + # Please refer to the API documentation for PDF::Writer if you need more
64
+ + # information.
65
+ + #
66
+ class PDF < Plugin
67
+ attr_writer :pdf_writer
68
+ attr_accessor :table_header_proc
69
+ attr_accessor :table_footer_proc
70
+
71
+ + # Does the necessary PDF::Writer requires
72
+ def initialize
73
+ require "pdf/writer"
74
+ require "pdf/simpletable"
75
+ end
76
+
77
+ + # Returns the current PDF::Writer object or creates a new one if it has not
78
+ + # been set yet.
79
+ + #
80
+ def pdf_writer
81
+ @pdf_writer ||=
82
+ ::PDF::Writer.new( :paper => layout.paper_size || "LETTER" )
83
+ end
84
+
85
+ + # If table_header_proc is defined, it will be executed and the PDF::Writer
86
+ + # object will be yielded.
87
+ + #
88
+ + # This should be overridden by subclasses, or used as a shortcut for your
89
+ + # own plugin implementations
90
+ + #
91
+ + # This method is automatically called by the table renderer
92
+ + #
93
+ def build_table_header
94
+ table_header_proc[pdf_writer] if table_header_proc
95
+ end
96
+
97
+ + # Calls the draw_table method
98
+ + #
99
+ + # This method is automatically called by the table renderer
100
+ + #
101
+ def build_table_body
102
+ draw_table
103
+ end
104
+
105
+ + # If table_footer_proc is defined, it will be executed and the PDF::Writer
106
+ + # object will be yielded.
107
+ + #
108
+ + # This should be overridden by subclasses, or used as a shortcut for your
109
+ + # own plugin implementations
110
+ + #
111
+ + # This method is automatically called by the table renderer
112
+ + #
113
+ def build_table_footer
114
+ table_footer_proc[pdf_writer] if table_footer_proc
115
+ end
116
+
117
+ + # Appends the results of PDF::Writer#render to output for your
118
+ + # <tt>pdf_writer</tt> object.
119
+ def finalize_table
120
+ output << pdf_writer.render
121
+ end
122
+
123
+ + # Call PDF::Writer#text with the given arguments
124
+ def add_text(*args)
125
+ pdf_writer.text(*args)
126
+ end
127
+
128
+ + # Adds n to PDF::Writer#y.
129
+ + #
130
+ + # Basically, this allows you to move the rendering cursor up and down the page.
131
+ + #
132
+ + # move_cursor(10) #=> move up 10
133
+ + # move_cursor(-10) #=> move down 10
134
+ def move_cursor(n)
135
+ pdf_writer.y += n
136
+ end
137
+
138
+ + # Sets PDF::Writer#y to n.
139
+ + #
140
+ + # This lets you move to a given height on the page.
141
+ + #
142
+ def move_cursor_to(n)
143
+ pdf_writer.y = n
144
+ end
145
+
146
+ + # creates a margin of <tt>y</tt> amount and allows a block to be called to
147
+ + # render items within that margin.
148
+ + #
149
+ + # Example:
150
+ + #
151
+ + # pad(10) { add_text 'hello' } #=> creates text hello with vertical margin
152
+ + # of 10.
153
+ def pad(y,&block)
154
+ move_cursor -y
155
+ block.call
156
+ move_cursor -y
157
+ end
158
+
159
+ + # Builds a PDF::SimpleTable from a Data::Table
160
+ + #
161
+ + # Can be customized via the follow layout parameters:
162
+ + #
163
+ + # max_table_width #=> defaults to 500
164
+ + # table_width #=> optionally set a fixed width for the table
165
+ + # orientation #=> defaults to :center
166
+ def draw_table
167
+ m = "Sorry, cant build PDFs from array like things (yet)"
168
+ raise m if data.column_names.empty?