ruport 1.2.3 → 1.4.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.
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