ruport 1.0.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # template.rb provides templating support for Ruby Reports.
4
+ #
5
+ # Copyright August 2007, Gregory Brown / Michael Milner. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+
10
+ # This class provides templating functionality for Ruport.
11
+ # New templates are created using the Template.create method.
12
+ #
13
+ # Example:
14
+ #
15
+ # Ruport::Formatter::Template.create(:simple) do |t|
16
+ # t.page_layout = :landscape
17
+ # t.grouping_style = :offset
18
+ # end
19
+ #
20
+ # You can then determine how the template should be used by defining
21
+ # an <tt>apply_template</tt> method in your formatter.
22
+ #
23
+ # Example:
24
+ #
25
+ # class Ruport::Formatter::PDF
26
+ # def apply_template
27
+ # options.paper_orientation = template.page_layout
28
+ # options.style = template.grouping_style
29
+ # end
30
+ # end
31
+ #
32
+ # When you're ready to render the output, you can set the :template as an
33
+ # option for the formatter. Using the template remains optional and you can
34
+ # still render the report without it.
35
+ #
36
+ # Example:
37
+ #
38
+ # puts g.to_pdf(:template => :simple) #=> uses the template
39
+ # puts g.to_pdf #=> doesn't use the template
40
+ #
41
+ # The built-in formatters all have <tt>apply_template</tt> methods defined that
42
+ # accept a standard set of options. Each option can be set by supplying a hash
43
+ # with the keys/values listed in the tables below.
44
+ #
45
+ # Example:
46
+ #
47
+ # Ruport::Formatter::Template.create(:simple) do |t|
48
+ # t.page_format = {
49
+ # :size => "LETTER",
50
+ # :layout => :landscape
51
+ # }
52
+ # end
53
+ #
54
+ # ==== PDF Formatter Options
55
+ #
56
+ # Option Key Value
57
+ #
58
+ # page_format :size Any size supported by the :paper
59
+ # option to PDF::Writer.new
60
+ #
61
+ # :layout :portrait, :landscape
62
+ #
63
+ # text_format Any available to Corresponding values
64
+ # PDF::Writer#text
65
+ #
66
+ # table_format All attributes of Corresponding values
67
+ # PDF::SimpleTable
68
+ #
69
+ # :column_options - All attributes of
70
+ # PDF::SimpleTable::Column
71
+ # except :heading
72
+ # - Hash keyed by a column name, whose
73
+ # value is a hash containing any of
74
+ # the other:column_options (sets values
75
+ # for specific columns)
76
+ # - :heading => { All attributes of
77
+ # PDF::SimpleTable::Column::Heading }
78
+ #
79
+ # column_format :alignment :left, :right, :center, :full
80
+ #
81
+ # :width column width
82
+ #
83
+ # heading_format :alignment :left, :right, :center, :full
84
+ #
85
+ # :bold true or false
86
+ #
87
+ # :title heading title (if not set,
88
+ # defaults to column name)
89
+ #
90
+ # grouping_format :style :inline, :justified, :separated, :offset
91
+ #
92
+ #
93
+ # ==== Text Formatter Options
94
+ #
95
+ # Option Key Value
96
+ #
97
+ # table_format :show_headings true or false
98
+ # :width Table width
99
+ # :ignore_width true or false
100
+ #
101
+ # column_format :alignment :center
102
+ # :maximum_width Max column width
103
+ #
104
+ # grouping_format :show_headings true or false
105
+ #
106
+ #
107
+ # ==== HTML Formatter Options
108
+ #
109
+ # Option Key Value
110
+ #
111
+ # table_format :show_headings true or false
112
+ #
113
+ # grouping_format :style :inline, :justified
114
+ # :show_headings true or false
115
+ #
116
+ #
117
+ # ==== CSV Formatter Options
118
+ #
119
+ # Option Key Value
120
+ #
121
+ # table_format :show_headings true or false
122
+ #
123
+ # grouping_format :style :inline, :justified, :raw
124
+ # :show_headings true or false
125
+ #
126
+ # format_options All options Corresponding values
127
+ # available to
128
+ # FasterCSV.new
129
+ #
130
+ class Ruport::Formatter::Template < Ruport::Renderer::Options
131
+
132
+ # Returns all existing templates in a hash keyed by the template names.
133
+ def self.templates
134
+ @templates ||= Hash.new
135
+ end
136
+
137
+ # Creates a new template with a name given by <tt>label</tt>.
138
+ #
139
+ # Example:
140
+ #
141
+ # Ruport::Formatter::Template.create(:simple) do |t|
142
+ # t.page_layout = :landscape
143
+ # t.grouping_style = :offset
144
+ # end
145
+ #
146
+ # You can inherit all the options set in a template by using the :base option
147
+ # and providing an existing template name to use as the base.
148
+ #
149
+ # Example:
150
+ #
151
+ # Ruport::Formatter::Template.create(:derived, :base => :simple)
152
+ #
153
+ def self.create(label,opts={})
154
+ if opts[:base]
155
+ obj = Marshal.load(Marshal.dump(self[opts[:base]]))
156
+ else
157
+ obj = new
158
+ end
159
+ yield(obj) if block_given?
160
+ templates[label] = obj
161
+ end
162
+
163
+ # Returns an existing template with the provided name (label).
164
+ def self.[](label)
165
+ templates[label]
166
+ end
167
+ end
@@ -10,9 +10,10 @@
10
10
  #
11
11
  # This is free software distributed under the same terms as Ruby 1.8
12
12
  # See LICENSE and COPYING for details.
13
- module Ruport
14
- # This class provides text output for Ruport's Row,Table,Group, and Grouping
15
- # renderers
13
+ module Ruport
14
+
15
+ # This class provides text output for Ruport's Row, Table, Group, and
16
+ # Grouping renderers
16
17
  #
17
18
  # It handles things like automatically truncating tables that go off the
18
19
  # edge of the screen in the console, proper column alignment, and pretty
@@ -24,24 +25,24 @@ module Ruport
24
25
  # | yes | no | yes |
25
26
  # | yes | yes | red snapper |
26
27
  # | what | the | red snapper |
27
- # +------------------------------+
28
+ # +------------------------------+
28
29
  #
29
30
  # === Supported Options
30
31
  #
31
32
  # <tt>:max_col_width:</tt> Ordinal array of column widths. Set automatically
32
- # but can be overridden
33
+ # but can be overridden.
33
34
  #
34
- # <tt>:alignment:</tt> Defaults to left justify text and right justify numbers.
35
- # centers all fields when set to :center
35
+ # <tt>:alignment:</tt> Defaults to left justify text and right justify
36
+ # numbers. Centers all fields when set to :center.
36
37
  #
37
- # <tt>:table_width:</tt> Will truncate rows at this limit.
38
+ # <tt>:table_width:</tt> Will truncate rows at this limit.
38
39
  #
39
40
  # <tt>:show_table_headers:</tt> Defaults to true
40
41
  #
41
42
  # <tt>:show_group_headers:</tt> Defaults to true
42
43
  #
43
44
  # <tt>:ignore_table_width:</tt> When set to true, outputs full table without
44
- # truncating it. Useful for file output
45
+ # truncating it. Useful for file output.
45
46
  class Formatter::Text < Formatter
46
47
 
47
48
  renders :text, :for => [ Renderer::Row, Renderer::Table,
@@ -51,8 +52,16 @@ module Ruport
51
52
  :show_table_headers, :show_group_headers,
52
53
  :ignore_table_width
53
54
 
55
+ # Hook for setting available options using a template. See the template
56
+ # documentation for the available options and their format.
57
+ def apply_template
58
+ apply_table_format_template(template.table_format)
59
+ apply_column_format_template(template.column_format)
60
+ apply_grouping_format_template(template.grouping_format)
61
+ end
62
+
54
63
  # Checks to ensure the table is not empty and then calls
55
- # calculate_max_col_widths
64
+ # calculate_max_col_widths.
56
65
  #
57
66
  def prepare_table
58
67
  raise Ruport::FormatterError, "Can't output table without " +
@@ -63,7 +72,7 @@ module Ruport
63
72
  # Uses the column names from the given Data::Table to generate a table
64
73
  # header.
65
74
  #
66
- # Calls fit_to_width to truncate table heading if necessary.
75
+ # Calls fit_to_width to truncate the table heading if necessary.
67
76
  #
68
77
  def build_table_header
69
78
  return unless should_render_column_names?
@@ -81,7 +90,7 @@ module Ruport
81
90
  # left justified. Can be changed to support centering of output by
82
91
  # setting options.alignment to :center
83
92
  #
84
- # Uses fit_to_width to truncate table if necessary.
93
+ # Uses fit_to_width to truncate the table if necessary.
85
94
  #
86
95
  def build_table_body
87
96
  output << fit_to_width(hr)
@@ -107,7 +116,7 @@ module Ruport
107
116
  # left justified. Can be changed to support centering of output by
108
117
  # setting options.alignment to :center
109
118
  #
110
- # Uses fit_to_width to truncate table if necessary.
119
+ # Uses fit_to_width to truncate the row if necessary.
111
120
  #
112
121
  def build_row
113
122
  max_col_widths_for_row(data) unless max_col_width
@@ -179,7 +188,7 @@ module Ruport
179
188
  }.join("\n") + "\n"
180
189
  end
181
190
 
182
- # determines the text widths for each column.
191
+ # Determines the text widths for each column.
183
192
  def calculate_max_col_widths
184
193
  # allow override
185
194
  return if max_col_width
@@ -195,7 +204,7 @@ module Ruport
195
204
  data.each { |r| max_col_widths_for_row(r) }
196
205
  end
197
206
 
198
- # used to calculate the <tt>max_col_widths</tt> array.
207
+ # Used to calculate the <tt>max_col_widths</tt> array.
199
208
  # Override this to tweak the automatic column size adjustments.
200
209
  def max_col_widths_for_row(row)
201
210
  options.max_col_width ||= []
@@ -205,6 +214,29 @@ module Ruport
205
214
  end
206
215
  end
207
216
  end
217
+
218
+ private
219
+
220
+ def apply_table_format_template(t)
221
+ t = t || {}
222
+ options.show_table_headers = t[:show_headings] unless
223
+ t[:show_headings].nil?
224
+ options.table_width = t[:width] if t[:width]
225
+ options.ignore_table_width = t[:ignore_width] unless
226
+ t[:ignore_width].nil?
227
+ end
228
+
229
+ def apply_column_format_template(t)
230
+ t = t || {}
231
+ options.max_col_width = t[:maximum_width] if t[:maximum_width]
232
+ options.alignment = t[:alignment] if t[:alignment]
233
+ end
234
+
235
+ def apply_grouping_format_template(t)
236
+ t = t || {}
237
+ options.show_group_headers = t[:show_headings] unless
238
+ t[:show_headings].nil?
239
+ end
208
240
 
209
241
  end
210
242
  end
data/lib/ruport/query.rb CHANGED
@@ -196,7 +196,7 @@ module Ruport
196
196
  row = row.to_a
197
197
  row = Data::Record.new(row, :attributes => names) unless @raw_data
198
198
  yield row if block_given?
199
- data << row if !block_given? || @cache_enabled
199
+ data << row if !block_given?
200
200
  end
201
201
  end
202
202
  end
@@ -26,11 +26,11 @@ class Ruport::Renderer
26
26
  # Structure for holding renderer options.
27
27
  # Simplified version of HashWithIndifferentAccess
28
28
  class Options < OpenStruct
29
- # returns a Hash object. Use this if you need methods other than []
29
+ # Returns a Hash object. Use this if you need methods other than []
30
30
  def to_hash
31
31
  @table
32
32
  end
33
- # indifferent lookup of an attribute, e.g.
33
+ # Indifferent lookup of an attribute, e.g.
34
34
  #
35
35
  # options[:foo] == options["foo"]
36
36
  def [](key)
@@ -50,12 +50,12 @@ class Ruport::Renderer
50
50
  end
51
51
 
52
52
  # This module provides hooks into Ruport's formatting system.
53
- # It is used to implement the as() method for all of Ruport's datastructures,
54
- # as well as the renders_with and renders_as_* helpers.
53
+ # It is used to implement the as() method for all of Ruport's data
54
+ # structures, as well as the renders_with and renders_as_* helpers.
55
55
  #
56
56
  # You can actually use this with any data structure, it will look for a
57
- # renderable_data method to pass to the <tt>renderer</tt> you specify,
58
- # but if that is not defined, it will pass <tt>self</tt>
57
+ # renderable_data(format) method to pass to the <tt>renderer</tt> you
58
+ # specify, but if that is not defined, it will pass <tt>self</tt>.
59
59
  #
60
60
  # Examples:
61
61
  #
@@ -73,10 +73,10 @@ class Ruport::Renderer
73
73
  # include Ruport::Renderer::Hooks
74
74
  # renders_as_row
75
75
  # attr_accessor :column_order
76
- # def renderable_data
77
- # column_order.map { |c| self[c] }
76
+ # def renderable_data(format)
77
+ # column_order.map { |c| self[c] }
78
78
  # end
79
- # end
79
+ # end
80
80
  #
81
81
  # # >> a = { :a => 1, :b => 2, :c => 3 }
82
82
  # # >> a.column_order = [:b,:a,:c]
@@ -87,7 +87,7 @@ class Ruport::Renderer
87
87
 
88
88
  # Tells the class which renderer as() will forward to.
89
89
  #
90
- # usage:
90
+ # Usage:
91
91
  #
92
92
  # class MyStructure
93
93
  # include Renderer::Hooks
@@ -95,11 +95,11 @@ class Ruport::Renderer
95
95
  # end
96
96
  #
97
97
  # You can also specify default rendering options, which will be used
98
- # if they are not overriden by the options passed to as()
98
+ # if they are not overriden by the options passed to as().
99
99
  #
100
100
  # class MyStructure
101
- # include Renderer::Hooks
102
- # renders_with CustomRenderer, :font_size => 14
101
+ # include Renderer::Hooks
102
+ # renders_with CustomRenderer, :font_size => 14
103
103
  # end
104
104
  def renders_with(renderer,opts={})
105
105
  @renderer = renderer.name
@@ -137,7 +137,7 @@ class Ruport::Renderer
137
137
 
138
138
  # The class of the renderer object for the base class.
139
139
  #
140
- # Example
140
+ # Example:
141
141
  #
142
142
  # >> Ruport::Data::Table.renderer
143
143
  # => Ruport::Renderer::Table
@@ -152,11 +152,11 @@ class Ruport::Renderer
152
152
  end
153
153
 
154
154
  # Uses the Renderer specified by renders_with to generate formatted
155
- # output. Passes the return value of the <tt>renderable_data</tt> method if
156
- # the method is defined, otherwise passes <tt>self</tt> as :data
155
+ # output. Passes the return value of the <tt>renderable_data(format)</tt>
156
+ # method if the method is defined, otherwise passes <tt>self</tt> as :data
157
157
  #
158
158
  # The remaining options are converted to a Renderer::Options object and
159
- # accessible in both the renderer and formatter.
159
+ # are accessible in both the renderer and formatter.
160
160
  #
161
161
  # Example:
162
162
  #
@@ -167,16 +167,17 @@ class Ruport::Renderer
167
167
  raise UnknownFormatError
168
168
  end
169
169
  self.class.renderer.render(format,
170
- self.class.rendering_options.merge(options)) do |rend|
171
- rend.data = respond_to?(:renderable_data) ? renderable_data : self
172
- yield(rend) if block_given?
170
+ self.class.rendering_options.merge(options)) do |rend|
171
+ rend.data =
172
+ respond_to?(:renderable_data) ? renderable_data(format) : self
173
+ yield(rend) if block_given?
173
174
  end
174
175
  end
175
176
  end
176
-
177
-
177
+
178
+
178
179
  module AutoRunner #:nodoc:
179
- # called automagically when the report is rendered. Uses the
180
+ # Called automatically when the report is rendered. Uses the
180
181
  # data collected from the earlier methods.
181
182
  def _run_
182
183
 
@@ -189,6 +190,10 @@ class Ruport::Renderer
189
190
  end
190
191
  end
191
192
 
193
+ if formatter.respond_to?(:apply_template) && options.template
194
+ formatter.apply_template
195
+ end
196
+
192
197
  prepare self.class.first_stage if self.class.first_stage
193
198
 
194
199
  if formatter.respond_to?(:layout) && options.layout != false
@@ -198,14 +203,13 @@ class Ruport::Renderer
198
203
  end
199
204
 
200
205
  finalize self.class.final_stage if self.class.final_stage
201
-
202
206
  end
203
207
 
204
208
  def execute_stages
205
209
  # call each stage to build the report
206
210
  unless self.class.stages.nil?
207
211
  self.class.stages.each do |stage|
208
- self.send(:build,stage)
212
+ maybe("build_#{stage}")
209
213
  end
210
214
  end
211
215
  end
@@ -308,7 +312,7 @@ class Ruport::Renderer
308
312
  end
309
313
 
310
314
  # Defines attribute writers for the Renderer::Options object shared
311
- # between Renderer and Formatter
315
+ # between Renderer and Formatter.
312
316
  #
313
317
  # usage:
314
318
  #
@@ -366,12 +370,13 @@ class Ruport::Renderer
366
370
  # sets the data and options, and then does the following process:
367
371
  #
368
372
  # * If the renderer contains a module Helpers, mix it in to the instance.
369
- # * If a block is given, yield the Renderer instance
370
- # * If a setup() method is defined on the Renderer, call it
371
- # * If the renderer has defined a run() method, call it, otherwise,
372
- # include Renderer::AutoRunner. (you usually won't need a run() method )
373
- # * call _run_ if it exists (This is provided by default, by AutoRunner)
374
- # * return the results of formatter.output
373
+ # * If a block is given, yield the Renderer instance.
374
+ # * If a setup() method is defined on the Renderer, call it.
375
+ # * If the renderer has defined a run() method, call it. Otherwise,
376
+ # include Renderer::AutoRunner (you usually won't need a run() method).
377
+ # * Call _run_ if it exists (this is provided by default, by AutoRunner).
378
+ # * If the :file option is set to a file name, appends output to the file.
379
+ # * Return the results of formatter.output
375
380
  #
376
381
  # Note that the only time you will need a run() method is if you can't
377
382
  # do what you need to via a helpers module or via setup()
@@ -389,10 +394,11 @@ class Ruport::Renderer
389
394
  include AutoRunner
390
395
  end
391
396
  rend._run_ if rend.respond_to? :_run_
397
+ rend.formatter.save_output(rend.options.file) if rend.options.file
392
398
  return rend.formatter.output
393
399
  end
394
400
 
395
- # Allows you to set class_wide default options
401
+ # Allows you to set class-wide default options.
396
402
  #
397
403
  # Example:
398
404
  #
@@ -433,7 +439,7 @@ class Ruport::Renderer
433
439
 
434
440
  # Allows you to register a format with the renderer.
435
441
  #
436
- # example:
442
+ # Example:
437
443
  #
438
444
  # class MyFormatter < Ruport::Formatter
439
445
  # # formatter code ...
@@ -446,10 +452,10 @@ class Ruport::Renderer
446
452
 
447
453
  end
448
454
 
449
- # The name of format being used
455
+ # The name of format being used.
450
456
  attr_accessor :format
451
457
 
452
- # The formatter object being used
458
+ # The formatter object being used.
453
459
  attr_writer :formatter
454
460
 
455
461
  # The +data+ that has been passed to the active formatter.
@@ -477,22 +483,11 @@ class Ruport::Renderer
477
483
  options.io=obj
478
484
  end
479
485
 
480
- # When no block is given, returns active formatter.
481
- #
482
- # When a block is given with a block variable, sets the block variable to the
483
- # formatter.
484
- #
485
- # When a block is given without block variables, instance_evals the block
486
- # within the context of the formatter.
486
+ # Returns the active formatter.
487
487
  #
488
+ # If a block is given, it is evaluated in the context of the formatter.
488
489
  def formatter(&block)
489
- if block.nil?
490
- return @formatter
491
- elsif block.arity > 0
492
- yield(@formatter)
493
- else
494
- @formatter.instance_eval(&block)
495
- end
490
+ @formatter.instance_eval(&block) if block
496
491
  return @formatter
497
492
  end
498
493
 
@@ -513,11 +508,6 @@ class Ruport::Renderer
513
508
  maybe "prepare_#{name}"
514
509
  end
515
510
 
516
- def build(names,prefix=nil)
517
- return maybe("build_#{names}") if prefix.nil?
518
- names.each { |n| maybe "build_#{prefix}_#{n}" }
519
- end
520
-
521
511
  def finalize(name)
522
512
  maybe "finalize_#{name}"
523
513
  end
@@ -530,7 +520,7 @@ class Ruport::Renderer
530
520
  formatter.options = o
531
521
  end
532
522
 
533
- # selects a formatter for use by format name
523
+ # Selects a formatter for use by format name
534
524
  def use_formatter(format)
535
525
  self.formatter = self.class.formats[format].new
536
526
  self.formatter.format = format