ruport 0.7.2 → 0.8.0

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.
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?