ruport 1.2.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,9 +1,8 @@
1
1
  require "rake/rdoctask"
2
2
  require "rake/testtask"
3
3
  require "rake/gempackagetask"
4
- #
5
4
 
6
- RUPORT_VERSION = "1.2.3"
5
+ RUPORT_VERSION = "1.4.0"
7
6
 
8
7
  begin
9
8
  require "rubygems"
@@ -33,9 +32,8 @@ spec = Gem::Specification.new do |spec|
33
32
  spec.extra_rdoc_files = %w{README LICENSE AUTHORS}
34
33
  spec.rdoc_options << '--title' << 'Ruport Documentation' <<
35
34
  '--main' << 'README' << '-q'
36
- spec.add_dependency('transaction-simple', "=1.4.0")
37
35
  spec.add_dependency('fastercsv', '>= 1.1.0')
38
- spec.add_dependency('pdf-writer', '>= 1.1.3')
36
+ spec.add_dependency('pdf-writer', '= 1.1.7')
39
37
  spec.author = "Gregory Brown"
40
38
  spec.email = " gregory.t.brown@gmail.com"
41
39
  spec.rubyforge_project = "ruport"
@@ -8,13 +8,9 @@ require "ruport"
8
8
  #
9
9
  class Document < Ruport::Renderer
10
10
 
11
- # will throw an error if these options are not set at rendering time
11
+ # Will throw an error if these options are not set at rendering time
12
12
  required_option :text, :author
13
13
 
14
- # allows this option to be set directly on a renderer instance,
15
- # and creates a reader for it if a header() method does not already exist
16
- option :heading
17
-
18
14
  # The renderer will look for a build_document_body() method on the formatter,
19
15
  # but silently skip this stage if it is missing
20
16
  stage :document_body
@@ -84,4 +80,4 @@ took flesh. To be great is to be misunderstood. . . .
84
80
  EOS
85
81
  end
86
82
 
87
- puts a
83
+ puts a
@@ -19,12 +19,9 @@ class ClientRenderer < Ruport::Renderer
19
19
  prepare :standard_report
20
20
  stage :company_header, :client_header, :client_body, :client_footer
21
21
  finalize :standard_report
22
- option :example
23
22
 
24
23
  def setup
25
24
  data.rename_columns { |c| c.to_s.titleize }
26
- # this lets us omit the options prefix in the formatter
27
- formatter.class.opt_reader(:example)
28
25
  end
29
26
  end
30
27
 
@@ -58,7 +55,7 @@ class ClientPDF < CompanyPDFBase
58
55
 
59
56
  def build_client_header
60
57
  pad(10) do
61
- add_text "Specific Report Header with example=#{example}",
58
+ add_text "Specific Report Header with example=#{options.example}",
62
59
  :justification => :center, :font_size => 12
63
60
  end
64
61
  end
@@ -72,4 +69,4 @@ table = Table([:a,:b,:c]) << [1,2,3] << [4,5,6]
72
69
 
73
70
  File.open("example.pdf","w") do |f|
74
71
  f << ClientRenderer.render_pdf(:data => table,:example => "apple")
75
- end
72
+ end
@@ -2,9 +2,6 @@ require "rubygems"
2
2
  require "ruport"
3
3
 
4
4
  class RoadmapRenderer < Ruport::Renderer
5
-
6
- option :image_file
7
-
8
5
  stage :roadmap_image, :roadmap_text_body
9
6
  finalize :roadmap
10
7
  end
@@ -12,7 +9,6 @@ end
12
9
  class HTMLRoadmap < Ruport::Formatter
13
10
 
14
11
  renders :html, :for => RoadmapRenderer
15
- opt_reader :image_file
16
12
 
17
13
  def layout
18
14
  output << "<html><body>\n"
@@ -21,7 +17,7 @@ class HTMLRoadmap < Ruport::Formatter
21
17
  end
22
18
 
23
19
  def build_roadmap_image
24
- output << "<img src='#{image_file}'/>"
20
+ output << "<img src='#{options.image_file}'/>"
25
21
  end
26
22
 
27
23
  def build_roadmap_text_body
@@ -33,10 +29,9 @@ end
33
29
  class PDFRoadmap < Ruport::Formatter::PDF
34
30
 
35
31
  renders :pdf, :for => RoadmapRenderer
36
- opt_reader :image_file
37
32
 
38
33
  def build_roadmap_image
39
- center_image_in_box image_file, :x => 0, :y => 200,
34
+ center_image_in_box options.image_file, :x => 0, :y => 200,
40
35
  :width => 624, :height => 432
41
36
  move_cursor_to 80
42
37
  end
@@ -56,4 +51,4 @@ formats = [:html, :pdf]
56
51
  formats.each do |format|
57
52
  RoadmapRenderer.render(format, :image_file => "roadmap.png",
58
53
  :file => "roadmap.#{format}")
59
- end
54
+ end
@@ -1,24 +1,24 @@
1
1
  require "ruport"
2
2
 
3
- Ruport::Formatter::Template.create(:simple) do |t|
4
- t.page_format = {
3
+ Ruport::Formatter::Template.create(:simple) do |format|
4
+ format.page = {
5
5
  :size => "LETTER",
6
6
  :layout => :landscape
7
7
  }
8
- t.text_format = {
8
+ format.text = {
9
9
  :font_size => 16
10
10
  }
11
- t.table_format = {
11
+ format.table = {
12
12
  :font_size => 16,
13
13
  :show_headings => false
14
14
  }
15
- t.column_format = {
15
+ format.column = {
16
16
  :alignment => :center,
17
17
  }
18
- t.heading_format = {
18
+ format.heading = {
19
19
  :alignment => :right
20
20
  }
21
- t.grouping_format = {
21
+ format.grouping = {
22
22
  :style => :separated
23
23
  }
24
24
  end
@@ -1,10 +1,8 @@
1
1
  # A dump of the database for this example can be found in ./data/tattle.dump
2
2
 
3
-
4
3
  require "active_record"
5
4
  require "ruport"
6
5
 
7
-
8
6
  # Update with your connection parameters
9
7
  ActiveRecord::Base.establish_connection(
10
8
  :adapter => 'mysql',
@@ -37,4 +35,3 @@ sorted_table = rubygems_versions.sort_rows_by("count", :order => :descending)
37
35
  sorted_table.reduce { |r| r["platform"] !~ /darwin/i }
38
36
  g = Grouping(sorted_table, :by => "platform", :order => "name")
39
37
  puts g.to_pdf
40
-
@@ -79,6 +79,12 @@ module Ruport::Data
79
79
 
80
80
  alias_method :==, :eql?
81
81
 
82
+ protected
83
+
84
+ attr_writer :name, :subgroups #:nodoc:
85
+
86
+ private
87
+
82
88
  # Creates subgroups for the group based on the supplied column name. Each
83
89
  # subgroup is a hash whose keys are the unique values in the column.
84
90
  #
@@ -93,23 +99,18 @@ module Ruport::Data
93
99
  if @subgroups.empty?
94
100
  @subgroups = grouped_data(group_column)
95
101
  else
96
- @subgroups.each {|name,group| group.create_subgroups(group_column) }
102
+ @subgroups.each {|name,group|
103
+ group.send(:create_subgroups, group_column)
104
+ }
97
105
  end
98
106
  end
99
107
 
100
- protected
101
-
102
- attr_writer :name, :subgroups #:nodoc:
103
-
104
- private
105
-
106
108
  def grouped_data(group_column) #:nodoc:
107
109
  data = {}
108
110
  group_names = column(group_column).uniq
109
111
  columns = column_names.dup
110
112
  columns.delete(group_column)
111
113
  group_names.each do |name|
112
- # FIXME - this doesn't seem to reduce the data set
113
114
  group_data = sub_table(columns) {|r|
114
115
  r.send(group_column) == name
115
116
  }
@@ -172,7 +173,7 @@ module Ruport::Data
172
173
  @data = data.to_group.send(:grouped_data, cols.shift)
173
174
  cols.each do |col|
174
175
  @data.each do |name,group|
175
- group.create_subgroups(col)
176
+ group.send(:create_subgroups, col)
176
177
  end
177
178
  end
178
179
  end
@@ -378,7 +379,7 @@ module Kernel
378
379
  #
379
380
  # Example:
380
381
  #
381
- # a = [[1,2,3],[4,5,6]].to_table(%w[a b c])
382
+ # a = Table(%w[a b c], :data => [[1,2,3],[4,5,6]])
382
383
  # b = Grouping(a, :by => "a") #=> creates a new grouping on column "a"
383
384
  #
384
385
  def Grouping(*args)
@@ -16,7 +16,9 @@ module Ruport::Data
16
16
  # as Array-like, Hash-like, or Struct-like objects. They are used as the
17
17
  # base element for Data::Table
18
18
  #
19
- class Record
19
+ class Record
20
+
21
+ private :id
20
22
 
21
23
  include Enumerable
22
24
 
@@ -824,17 +824,3 @@ module Kernel
824
824
  return table
825
825
  end
826
826
  end
827
-
828
- class Array
829
-
830
- # Converts an array to a Ruport::Data::Table object, ready to
831
- # use in your reports.
832
- #
833
- # Example:
834
- # [[1,2],[3,4]].to_table(%w[a b])
835
- #
836
- def to_table(column_names=nil,&b)
837
- Ruport::Data::Table.new({:data => self, :column_names => column_names},&b)
838
- end
839
-
840
- end
@@ -32,16 +32,11 @@ module Ruport
32
32
  renders :csv, :for => [ Renderer::Row, Renderer::Table,
33
33
  Renderer::Group, Renderer::Grouping ]
34
34
 
35
- opt_reader :show_table_headers,
36
- :format_options,
37
- :show_group_headers,
38
- :style
39
-
40
35
  # Hook for setting available options using a template. See the template
41
36
  # documentation for the available options and their format.
42
37
  def apply_template
43
- apply_table_format_template(template.table_format)
44
- apply_grouping_format_template(template.grouping_format)
38
+ apply_table_format_template(template.table)
39
+ apply_grouping_format_template(template.grouping)
45
40
 
46
41
  options.format_options ||= template.format_options
47
42
  end
@@ -52,22 +47,22 @@ module Ruport
52
47
  # This method does not do anything if options.show_table_headers is false
53
48
  # or the Data::Table has no column names.
54
49
  def build_table_header
55
- unless data.column_names.empty? || !show_table_headers
56
- render_row data.column_names, :format_options => format_options
50
+ unless data.column_names.empty? || !options.show_table_headers
51
+ render_row data.column_names, :format_options => options.format_options
57
52
  end
58
53
  end
59
54
 
60
55
  # Calls the row renderer for each row in the Data::Table
61
56
  def build_table_body
62
57
  render_data_by_row { |r|
63
- r.options.format_options = format_options
58
+ r.options.format_options = options.format_options
64
59
  }
65
60
  end
66
61
 
67
62
  # Produces CSV output for a data row.
68
63
  def build_row
69
64
  require "fastercsv"
70
- output << FCSV.generate_line(data,format_options || {})
65
+ output << FCSV.generate_line(data,options.format_options || {})
71
66
  end
72
67
 
73
68
  # Renders the header for a group using the group name.
@@ -86,14 +81,14 @@ module Ruport
86
81
  # column names.
87
82
  #
88
83
  def build_grouping_header
89
- unless style == :inline
84
+ unless options.style == :inline
90
85
  output << "#{data.grouped_by}," << grouping_columns
91
86
  end
92
87
  end
93
88
 
94
89
  # Determines the proper style to use and renders the Grouping.
95
90
  def build_grouping_body
96
- case style
91
+ case options.style
97
92
  when :inline
98
93
  render_inline_grouping(options)
99
94
  when :justified, :raw
@@ -112,9 +107,9 @@ module Ruport
112
107
 
113
108
  def render_justified_or_raw_grouping
114
109
  data.each do |_,group|
115
- output << "#{group.name}" if style == :justified
110
+ output << "#{group.name}" if options.style == :justified
116
111
  group.each do |row|
117
- output << "#{group.name if style == :raw}," << row.to_csv
112
+ output << "#{group.name if options.style == :raw}," << row.to_csv
118
113
  end
119
114
  output << "\n"
120
115
  end
@@ -28,13 +28,11 @@ module Ruport
28
28
  renders :html, :for => [ Renderer::Row, Renderer::Table,
29
29
  Renderer::Group, Renderer::Grouping ]
30
30
 
31
- opt_reader :show_table_headers, :show_group_headers, :style
32
-
33
31
  # Hook for setting available options using a template. See the template
34
32
  # documentation for the available options and their format.
35
33
  def apply_template
36
- apply_table_format_template(template.table_format)
37
- apply_grouping_format_template(template.grouping_format)
34
+ apply_table_format_template(template.table)
35
+ apply_grouping_format_template(template.grouping)
38
36
  end
39
37
 
40
38
  # Generates table headers based on the column names of your Data::Table.
@@ -43,7 +41,7 @@ module Ruport
43
41
  # or the Data::Table has no column names.
44
42
  def build_table_header
45
43
  output << "\t<table>\n"
46
- unless data.column_names.empty? || !show_table_headers
44
+ unless data.column_names.empty? || !options.show_table_headers
47
45
  output << "\t\t<tr>\n\t\t\t<th>" +
48
46
  data.column_names.join("</th>\n\t\t\t<th>") +
49
47
  "</th>\n\t\t</tr>\n"
@@ -89,7 +87,7 @@ module Ruport
89
87
  # renders them using the group renderer.
90
88
  #
91
89
  def build_grouping_body
92
- case style
90
+ case options.style
93
91
  when :inline
94
92
  render_inline_grouping(options)
95
93
  when :justified
@@ -39,41 +39,13 @@ module Ruport
39
39
  # Grouping:
40
40
  # * style (:inline,:justified,:separated,:offset)
41
41
  #
42
- class Formatter::PDF < Formatter
43
-
44
- module PDFWriterMemoryPatch #:nodoc:
45
- unless self.class.instance_methods.include?("_post_transaction_rewind")
46
- def _post_transaction_rewind
47
- @objects.each { |e| e.instance_variable_set(:@parent,self) }
48
- end
49
- end
50
- end
51
-
52
- module PDFSimpleTableOrderingPatch #:nodoc:
53
- def __find_table_max_width__(pdf)
54
- #p "this actually gets called"
55
- max_width = PDF::Writer::OHash.new(-1)
42
+ class Formatter::PDF < Formatter
56
43
 
57
- # Find the maximum cell widths based on the data and the headings.
58
- # Passing through the data multiple times is unavoidable as we must
59
- # do some analysis first.
60
- @data.each do |row|
61
- @cols.each do |name, column|
62
- w = pdf.text_width(row[name].to_s, @font_size)
63
- w *= PDF::SimpleTable::WIDTH_FACTOR
64
-
65
- max_width[name] = w if w > max_width[name]
66
- end
67
- end
68
-
69
- @cols.each do |name, column|
70
- title = column.heading.title if column.heading
71
- title ||= column.name
72
- w = pdf.text_width(title, @heading_font_size)
73
- w *= PDF::SimpleTable::WIDTH_FACTOR
74
- max_width[name] = w if w > max_width[name]
75
- end
76
- max_width
44
+ module PDFWriterProxy #:nodoc:
45
+ def method_missing(id,*args)
46
+ super(id,*args)
47
+ rescue
48
+ pdf_writer.send(id,*args)
77
49
  end
78
50
  end
79
51
 
@@ -82,11 +54,15 @@ module Ruport
82
54
 
83
55
  attr_writer :pdf_writer
84
56
 
85
- opt_reader :style,
86
- :table_format,
87
- :text_format,
88
- :paper_size,
89
- :paper_orientation
57
+
58
+ # If you use this macro in your formatter, Ruport will automatically forward
59
+ # calls to the underlying PDF::Writer, for any methods that are not wrapped
60
+ # or redefined.
61
+ def self.proxy_to_pdf_writer
62
+ include PDFWriterProxy
63
+ end
64
+
65
+ save_as_binary_file
90
66
 
91
67
  def initialize
92
68
  Ruport.quiet do
@@ -98,12 +74,12 @@ module Ruport
98
74
  # Hook for setting available options using a template. See the template
99
75
  # documentation for the available options and their format.
100
76
  def apply_template
101
- apply_page_format_template(template.page_format)
102
- apply_text_format_template(template.text_format)
103
- apply_table_format_template(template.table_format)
104
- apply_column_format_template(template.column_format)
105
- apply_heading_format_template(template.heading_format)
106
- apply_grouping_format_template(template.grouping_format)
77
+ apply_page_format_template(template.page)
78
+ apply_text_format_template(template.text)
79
+ apply_table_format_template(template.table)
80
+ apply_column_format_template(template.column)
81
+ apply_heading_format_template(template.heading)
82
+ apply_grouping_format_template(template.grouping)
107
83
  end
108
84
 
109
85
  # Returns the current PDF::Writer object or creates a new one if it has not
@@ -111,9 +87,8 @@ module Ruport
111
87
  #
112
88
  def pdf_writer
113
89
  @pdf_writer ||= options.formatter ||
114
- ::PDF::Writer.new( :paper => paper_size || "LETTER",
115
- :orientation => paper_orientation || :portrait)
116
- @pdf_writer.extend(PDFWriterMemoryPatch)
90
+ ::PDF::Writer.new( :paper => options.paper_size || "LETTER",
91
+ :orientation => options.paper_orientation || :portrait)
117
92
  end
118
93
 
119
94
  # Calls the draw_table method.
@@ -142,7 +117,7 @@ module Ruport
142
117
  # Determines which style to use and renders the main body for
143
118
  # Renderer::Grouping.
144
119
  def build_grouping_body
145
- case style
120
+ case options.style
146
121
  when :inline
147
122
  render_inline_grouping(options.to_hash.merge(:formatter => pdf_writer,
148
123
  :skip_finalize_table => true))
@@ -170,7 +145,7 @@ module Ruport
170
145
  # add_text("Hello Joe") #renders at 14pt
171
146
  # add_text("Hello Mike",:font_size => 16) # renders at 16pt
172
147
  def add_text(text, format_opts={})
173
- format_opts = text_format.merge(format_opts) if text_format
148
+ format_opts = options.text_format.merge(format_opts) if options.text_format
174
149
  pdf_writer.text(text, format_opts)
175
150
  end
176
151
 
@@ -245,25 +220,6 @@ module Ruport
245
220
  move_cursor_to(opts.y - opts.height)
246
221
  end
247
222
 
248
- # Adds an image to every page. The color and size won't be modified,
249
- # but it will be centered.
250
- #
251
- def watermark(imgpath)
252
- x = pdf_writer.absolute_left_margin
253
- y = pdf_writer.absolute_bottom_margin
254
- width = pdf_writer.absolute_right_margin - x
255
- height = pdf_writer.absolute_top_margin - y
256
-
257
- pdf_writer.open_object do |wm|
258
- pdf_writer.save_state
259
- center_image_in_box(imgpath, :x => x, :y => y,
260
- :width => width, :height => height)
261
- pdf_writer.restore_state
262
- pdf_writer.close_object
263
- pdf_writer.add_object(wm, :all_pages)
264
- end
265
- end
266
-
267
223
  # Adds n to pdf_writer.y, moving the vertical drawing position in the
268
224
  # document.
269
225
  def move_cursor(n)
@@ -274,6 +230,15 @@ module Ruport
274
230
  def move_cursor_to(n)
275
231
  pdf_writer.y = n
276
232
  end
233
+
234
+ # Moves the vertical drawing position in the document upwards by n.
235
+ def move_up(n)
236
+ pdf_writer.y += n
237
+ end
238
+
239
+ def move_down(n)
240
+ pdf_writer.y -= n
241
+ end
277
242
 
278
243
  # Adds a specified amount of whitespace above and below the code
279
244
  # in your block. For example, if you want to surround the top and
@@ -317,15 +282,15 @@ module Ruport
317
282
  raise FormatterError, m if table_data.column_names.empty?
318
283
 
319
284
  table_data.rename_columns { |c| c.to_s }
320
-
321
- if table_format
322
- format_opts = Marshal.load(Marshal.dump(table_format.merge(format_opts)))
323
- end
285
+
286
+ if options.table_format
287
+ format_opts =
288
+ Marshal.load(Marshal.dump(options.table_format.merge(format_opts)))
289
+ end
324
290
 
325
291
  old = pdf_writer.font_size
326
292
 
327
- ::PDF::SimpleTable.new do |table|
328
- table.extend(PDFSimpleTableOrderingPatch)
293
+ ::PDF::SimpleTable.new do |table|
329
294
  table.maximum_width = 500
330
295
  table.column_order = table_data.column_names
331
296
  table.data = table_data
@@ -338,12 +303,7 @@ module Ruport
338
303
 
339
304
  pdf_writer.font_size = old
340
305
  end
341
-
342
- # Save the output to a file.
343
- def save_output(filename)
344
- File.open(filename,"wb") {|f| f << output }
345
- end
346
-
306
+
347
307
  # This module provides tools to simplify some common drawing operations.
348
308
  # It is included by default in the PDF formatter.
349
309
  #
@@ -482,14 +442,14 @@ module Ruport
482
442
  def render_justified_or_separated_grouping
483
443
  table = table_with_grouped_by_column
484
444
  data.each do |name,group|
485
- group_column = { data.grouped_by => "<b>#{name}</b>\n" }
486
- group.each_with_index do |rec,i|
487
- # FIXME - Find the source of the bug requiring to_a
488
- i == 0 ?
489
- table << group_column.merge(rec.to_hash) :
490
- table << rec.to_a.unshift(nil)
445
+ group.each_with_index do |r,i|
446
+ if i == 0
447
+ table << { data.grouped_by => "<b>#{name}</b>" }.merge(r.to_hash)
448
+ else
449
+ table << r
450
+ end
491
451
  end
492
- table << Array.new(grouping_columns.length,' ') if style == :separated
452
+ table << [" "] if options.style == :separated
493
453
  end
494
454
  render_table table, options.to_hash.merge(:formatter => pdf_writer)
495
455
  end
@@ -497,9 +457,8 @@ module Ruport
497
457
  def render_offset_grouping
498
458
  table = table_with_grouped_by_column
499
459
  data.each do |name,group|
500
- table << ["<b>#{name}</b>\n",nil,nil]
501
- # FIXME - Find the source of the bug requiring to_a
502
- group.each {|r| table << r.to_a.unshift(nil) }
460
+ table << ["<b>#{name}</b>"]
461
+ group.each {|r| table << r }
503
462
  end
504
463
  render_table table, options.to_hash.merge(:formatter => pdf_writer)
505
464
  end