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.
@@ -46,26 +46,39 @@ class Ruport::Formatter::TemplateNotDefined < StandardError; end
46
46
  #
47
47
  # Example:
48
48
  #
49
- # Ruport::Formatter::Template.create(:simple) do |t|
50
- # t.page_format = {
49
+ # Ruport::Formatter::Template.create(:simple) do |format|
50
+ # format.page = {
51
51
  # :size => "LETTER",
52
52
  # :layout => :landscape
53
53
  # }
54
54
  # end
55
55
  #
56
+ # If you define a template with the name :default, then it will be used by
57
+ # all formatters unless they either specify a template or explicitly turn off
58
+ # the templating functionality by using :template => false.
59
+ #
60
+ # Example:
61
+ #
62
+ # Ruport::Formatter::Template.create(:simple)
63
+ # Ruport::Formatter::Template.create(:default)
64
+ #
65
+ # puts g.to_pdf #=> uses the :default template
66
+ # puts g.to_pdf(:template => :simple) #=> uses the :simple template
67
+ # puts g.to_pdf(:template => false) #=> doesn't use a template
68
+ #
56
69
  # ==== PDF Formatter Options
57
70
  #
58
71
  # Option Key Value
59
72
  #
60
- # page_format :size Any size supported by the :paper
73
+ # page :size Any size supported by the :paper
61
74
  # option to PDF::Writer.new
62
75
  #
63
76
  # :layout :portrait, :landscape
64
77
  #
65
- # text_format Any available to Corresponding values
78
+ # text Any available to Corresponding values
66
79
  # PDF::Writer#text
67
80
  #
68
- # table_format All attributes of Corresponding values
81
+ # table All attributes of Corresponding values
69
82
  # PDF::SimpleTable
70
83
  #
71
84
  # :column_options - All attributes of
@@ -78,41 +91,41 @@ class Ruport::Formatter::TemplateNotDefined < StandardError; end
78
91
  # - :heading => { All attributes of
79
92
  # PDF::SimpleTable::Column::Heading }
80
93
  #
81
- # column_format :alignment :left, :right, :center, :full
94
+ # column :alignment :left, :right, :center, :full
82
95
  #
83
96
  # :width column width
84
97
  #
85
- # heading_format :alignment :left, :right, :center, :full
98
+ # heading :alignment :left, :right, :center, :full
86
99
  #
87
100
  # :bold true or false
88
101
  #
89
102
  # :title heading title (if not set,
90
103
  # defaults to column name)
91
104
  #
92
- # grouping_format :style :inline, :justified, :separated, :offset
105
+ # grouping :style :inline, :justified, :separated, :offset
93
106
  #
94
107
  #
95
108
  # ==== Text Formatter Options
96
109
  #
97
110
  # Option Key Value
98
111
  #
99
- # table_format :show_headings true or false
112
+ # table :show_headings true or false
100
113
  # :width Table width
101
114
  # :ignore_width true or false
102
115
  #
103
- # column_format :alignment :center
116
+ # column :alignment :center
104
117
  # :maximum_width Max column width
105
118
  #
106
- # grouping_format :show_headings true or false
119
+ # grouping :show_headings true or false
107
120
  #
108
121
  #
109
122
  # ==== HTML Formatter Options
110
123
  #
111
124
  # Option Key Value
112
125
  #
113
- # table_format :show_headings true or false
126
+ # table :show_headings true or false
114
127
  #
115
- # grouping_format :style :inline, :justified
128
+ # grouping :style :inline, :justified
116
129
  # :show_headings true or false
117
130
  #
118
131
  #
@@ -120,9 +133,9 @@ class Ruport::Formatter::TemplateNotDefined < StandardError; end
120
133
  #
121
134
  # Option Key Value
122
135
  #
123
- # table_format :show_headings true or false
136
+ # table :show_headings true or false
124
137
  #
125
- # grouping_format :style :inline, :justified, :raw
138
+ # grouping :style :inline, :justified, :raw
126
139
  # :show_headings true or false
127
140
  #
128
141
  # format_options All options Corresponding values
@@ -166,4 +179,9 @@ class Ruport::Formatter::Template < Ruport::Renderer::Options
166
179
  def self.[](label)
167
180
  templates[label] or raise Ruport::Formatter::TemplateNotDefined
168
181
  end
182
+
183
+ # Returns the default template.
184
+ def self.default
185
+ templates[:default]
186
+ end
169
187
  end
@@ -45,19 +45,15 @@ module Ruport
45
45
  # truncating it. Useful for file output.
46
46
  class Formatter::Text < Formatter
47
47
 
48
- renders :text, :for => [ Renderer::Row, Renderer::Table,
49
- Renderer::Group, Renderer::Grouping ]
48
+ renders [:txt, :text], :for => [ Renderer::Row, Renderer::Table,
49
+ Renderer::Group, Renderer::Grouping ]
50
50
 
51
- opt_reader :max_col_width, :alignment, :table_width,
52
- :show_table_headers, :show_group_headers,
53
- :ignore_table_width
54
-
55
51
  # Hook for setting available options using a template. See the template
56
52
  # documentation for the available options and their format.
57
53
  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)
54
+ apply_table_format_template(template.table)
55
+ apply_column_format_template(template.column)
56
+ apply_grouping_format_template(template.grouping)
61
57
  end
62
58
 
63
59
  # Checks to ensure the table is not empty and then calls
@@ -78,7 +74,7 @@ module Ruport
78
74
  return unless should_render_column_names?
79
75
 
80
76
  c = data.column_names.enum_for(:each_with_index).map { |f,i|
81
- f.to_s.center(max_col_width[i])
77
+ f.to_s.center(options.max_col_width[i])
82
78
  }
83
79
 
84
80
  output << fit_to_width("#{hr}| #{c.join(' | ')} |\n")
@@ -96,14 +92,14 @@ module Ruport
96
92
  output << fit_to_width(hr)
97
93
  return if data.empty?
98
94
 
99
- calculate_max_col_widths unless max_col_width
95
+ calculate_max_col_widths unless options.max_col_width
100
96
 
101
97
  render_data_by_row do |rend|
102
98
  rend.options do |o|
103
- o.max_col_width = max_col_width
104
- o.alignment = alignment
105
- o.table_width = table_width
106
- o.ignore_table_width = ignore_table_width
99
+ o.max_col_width = options.max_col_width
100
+ o.alignment = options.alignment
101
+ o.table_width = options.table_width
102
+ o.ignore_table_width = options.ignore_table_width
107
103
  end
108
104
  end
109
105
 
@@ -119,15 +115,15 @@ module Ruport
119
115
  # Uses fit_to_width to truncate the row if necessary.
120
116
  #
121
117
  def build_row
122
- max_col_widths_for_row(data) unless max_col_width
118
+ max_col_widths_for_row(data) unless options.max_col_width
123
119
 
124
120
  data.enum_for(:each_with_index).inject(line=[]) { |s,e|
125
121
  field,index = e
126
- if alignment.eql? :center
127
- line << field.to_s.center(max_col_width[index])
122
+ if options.alignment.eql? :center
123
+ line << field.to_s.center(options.max_col_width[index])
128
124
  else
129
125
  align = field.is_a?(Numeric) ? :rjust : :ljust
130
- line << field.to_s.send(align, max_col_width[index])
126
+ line << field.to_s.send(align, options.max_col_width[index])
131
127
  end
132
128
  }
133
129
  output << fit_to_width("| #{line.join(' | ')} |\n")
@@ -157,7 +153,7 @@ module Ruport
157
153
  # is false/nil. Returns true otherwise.
158
154
  #
159
155
  def should_render_column_names?
160
- not data.column_names.empty? || !show_table_headers
156
+ not data.column_names.empty? || !options.show_table_headers
161
157
  end
162
158
 
163
159
  # Generates the horizontal rule by calculating the total table width and
@@ -166,7 +162,7 @@ module Ruport
166
162
  # "+------------------+"
167
163
  def hr
168
164
  ref = data.column_names.empty? ? data[0].to_a : data.column_names
169
- len = max_col_width.inject(ref.length * 3) {|s,e|s+e}
165
+ len = options.max_col_width.inject(ref.length * 3) {|s,e|s+e}
170
166
  "+" + "-"*(len-1) + "+\n"
171
167
  end
172
168
 
@@ -174,7 +170,7 @@ module Ruport
174
170
  #
175
171
  # Otherwise, uses SystemExtensions to determine terminal width.
176
172
  def width
177
- table_width || SystemExtensions.terminal_width
173
+ options.table_width || SystemExtensions.terminal_width
178
174
  end
179
175
 
180
176
  # Truncates a string so that it does not exceed Text#width
@@ -191,13 +187,13 @@ module Ruport
191
187
  # Determines the text widths for each column.
192
188
  def calculate_max_col_widths
193
189
  # allow override
194
- return if max_col_width
190
+ return if options.max_col_width
195
191
 
196
192
  options.max_col_width = []
197
193
 
198
194
  unless data.column_names.empty?
199
195
  data.column_names.each_index do |i|
200
- max_col_width[i] = data.column_names[i].to_s.length
196
+ options.max_col_width[i] = data.column_names[i].to_s.length
201
197
  end
202
198
  end
203
199
 
@@ -209,8 +205,8 @@ module Ruport
209
205
  def max_col_widths_for_row(row)
210
206
  options.max_col_width ||= []
211
207
  row.each_with_index do |f,i|
212
- if !max_col_width[i] || f.to_s.length > max_col_width[i]
213
- max_col_width[i] = f.to_s.length
208
+ if !options.max_col_width[i] || f.to_s.length > options.max_col_width[i]
209
+ options.max_col_width[i] = f.to_s.length
214
210
  end
215
211
  end
216
212
  end
@@ -26,7 +26,6 @@ module Ruport
26
26
  #
27
27
  # class ReverseRenderer < Ruport::Renderer
28
28
  # stage :reversed_header, :reversed_body
29
- # option :header_text
30
29
  # end
31
30
  #
32
31
  # class ReversedText < Ruport::Formatter
@@ -159,16 +158,26 @@ module Ruport
159
158
  formats << format unless formats.include?(format)
160
159
  end
161
160
  end
162
- end
161
+ end
163
162
 
164
- # Allows the options specified to be accessed directly.
163
+ # Allows you to implement stages in your formatter using the
164
+ # following syntax:
165
+ #
166
+ # class ReversedText < Ruport::Formatter
167
+ # renders :txt, :for => ReverseRenderer
168
+ #
169
+ # build :reversed_header do
170
+ # output << "#{options.header_text}\n"
171
+ # output << "The reversed text will follow\n"
172
+ # end
165
173
  #
166
- # opt_reader :something
167
- # something == options.something #=> true
168
- def self.opt_reader(*opts)
169
- require "forwardable"
170
- extend Forwardable
171
- opts.each { |o| def_delegator :@options, o }
174
+ # build :reversed_body do
175
+ # output << data.reverse << "\n"
176
+ # end
177
+ # end
178
+ #
179
+ def self.build(stage,&block)
180
+ define_method "build_#{stage}", &block
172
181
  end
173
182
 
174
183
  # Gives a list of formats registered for this formatter.
@@ -178,7 +187,7 @@ module Ruport
178
187
 
179
188
  # Returns the template currently set for this formatter.
180
189
  def template
181
- Template[options.template]
190
+ Template[options.template] rescue nil || Template[:default]
182
191
  end
183
192
 
184
193
  # Stores a string used for outputting formatted data.
@@ -202,13 +211,20 @@ module Ruport
202
211
  File.open(filename,"w") {|f| f << output }
203
212
  end
204
213
 
214
+ # Use to define that your formatter should save in binary format
215
+ def self.save_as_binary_file
216
+ define_method :save_output do |filename|
217
+ File.open(filename,"wb") {|f| f << output }
218
+ end
219
+ end
220
+
205
221
  # Evaluates the string using ERB and returns the results.
206
222
  #
207
223
  # If <tt>:binding</tt> is specified, it will evaluate the template
208
224
  # in that context.
209
225
  def erb(string,options={})
210
226
  require "erb"
211
- if string =~ /\.r\w+$/
227
+ if string =~ /(\.r\w+)|(\.erb)$/
212
228
  ERB.new(File.read(string)).result(options[:binding]||binding)
213
229
  else
214
230
  ERB.new(string).result(options[:binding]||binding)
@@ -30,9 +30,6 @@ module Ruport
30
30
  # * build_group_footer
31
31
  #
32
32
  class Renderer::Group < Renderer
33
-
34
- option :show_table_headers
35
-
36
33
  options { |o| o.show_table_headers = true }
37
34
 
38
35
  stage :group_header, :group_body, :group_footer
@@ -61,9 +58,6 @@ module Ruport
61
58
  # * finalize_grouping
62
59
  #
63
60
  class Renderer::Grouping < Renderer
64
-
65
- option :show_group_headers, :style
66
-
67
61
  options do |o|
68
62
  o.show_group_headers = true
69
63
  o.style = :inline
@@ -18,7 +18,6 @@ module Ruport
18
18
  # * build_row
19
19
  #
20
20
  class Renderer::Row < Renderer
21
- option :format_options
22
21
  stage :row
23
22
  end
24
23
 
@@ -44,9 +43,6 @@ module Ruport
44
43
  # * finalize_table
45
44
  #
46
45
  class Renderer::Table < Renderer
47
-
48
- option :show_table_headers, :format_options
49
-
50
46
  options { |o| o.show_table_headers = true }
51
47
 
52
48
  prepare :table
@@ -25,7 +25,10 @@ class Ruport::Renderer
25
25
 
26
26
  # Structure for holding renderer options.
27
27
  # Simplified version of HashWithIndifferentAccess
28
- class Options < OpenStruct
28
+ class Options < OpenStruct
29
+
30
+ private :id
31
+
29
32
  # Returns a Hash object. Use this if you need methods other than []
30
33
  def to_hash
31
34
  @table
@@ -172,49 +175,16 @@ class Ruport::Renderer
172
175
  respond_to?(:renderable_data) ? renderable_data(format) : self
173
176
  yield(rend) if block_given?
174
177
  end
175
- end
176
- end
177
-
178
-
179
- module AutoRunner #:nodoc:
180
- # Called automatically when the report is rendered. Uses the
181
- # data collected from the earlier methods.
182
- def _run_
183
-
184
- # ensure all the required options have been set
185
- unless self.class.required_options.nil?
186
- self.class.required_options.each do |opt|
187
- if options.__send__(opt).nil?
188
- raise RequiredOptionNotSet, "Required option #{opt} not set"
189
- end
190
- end
191
- end
192
-
193
- if formatter.respond_to?(:apply_template) && options.template
194
- formatter.apply_template
195
- end
196
-
197
- prepare self.class.first_stage if self.class.first_stage
198
-
199
- if formatter.respond_to?(:layout) && options.layout != false
200
- formatter.layout do execute_stages end
201
- else
202
- execute_stages
203
- end
204
-
205
- finalize self.class.final_stage if self.class.final_stage
206
- end
178
+ end
207
179
 
208
- def execute_stages
209
- # call each stage to build the report
210
- unless self.class.stages.nil?
211
- self.class.stages.each do |stage|
212
- maybe("build_#{stage}")
213
- end
214
- end
180
+ def save_as(file,options={})
181
+ file =~ /.*\.(.*)/
182
+ format = $1
183
+ as(format.to_sym, options.merge(:file => file))
215
184
  end
216
185
  end
217
186
 
187
+
218
188
  class << self
219
189
 
220
190
  attr_accessor :first_stage,:final_stage,:required_options,:stages #:nodoc:
@@ -311,28 +281,6 @@ class Ruport::Renderer
311
281
  }
312
282
  end
313
283
 
314
- # Defines attribute writers for the Renderer::Options object shared
315
- # between Renderer and Formatter.
316
- #
317
- # usage:
318
- #
319
- # class MyRenderer < Ruport::Renderer
320
- # option :font_size, :font_style
321
- # # other details omitted
322
- # end
323
- def option(*opts)
324
- opts.each do |opt|
325
- o = opt
326
- unless instance_methods(false).include?(o.to_s)
327
- define_method(o) {
328
- options.send(o.to_s)
329
- }
330
- end
331
- opt = "#{opt}="
332
- define_method(opt) {|t| options.send(opt, t) }
333
- end
334
- end
335
-
336
284
  # Defines attribute writers for the Renderer::Options object shared
337
285
  # between Renderer and Formatter. Will throw an error if the user does
338
286
  # not provide values for these options upon rendering.
@@ -347,10 +295,15 @@ class Ruport::Renderer
347
295
  self.required_options ||= []
348
296
  opts.each do |opt|
349
297
  self.required_options << opt
350
- option opt
298
+
299
+ o = opt
300
+ unless instance_methods(false).include?(o.to_s)
301
+ define_method(o) { options.send(o.to_s) }
302
+ end
303
+ opt = "#{opt}="
304
+ define_method(opt) {|t| options.send(opt, t) }
351
305
  end
352
306
  end
353
-
354
307
 
355
308
  # Lists the formatters that are currently registered on a renderer,
356
309
  # as a hash keyed by format name.
@@ -372,15 +325,10 @@ class Ruport::Renderer
372
325
  # * If the renderer contains a module Helpers, mix it in to the instance.
373
326
  # * If a block is given, yield the Renderer instance.
374
327
  # * 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).
328
+ # * Call the run() method.
378
329
  # * If the :file option is set to a file name, appends output to the file.
379
330
  # * Return the results of formatter.output
380
331
  #
381
- # Note that the only time you will need a run() method is if you can't
382
- # do what you need to via a helpers module or via setup()
383
- #
384
332
  # Please see the examples/ directory for custom renderer examples, because
385
333
  # this is not nearly as complicated as it sounds in most cases.
386
334
  def render(*args)
@@ -388,12 +336,7 @@ class Ruport::Renderer
388
336
  yield(r) if block_given?
389
337
  r.setup if r.respond_to? :setup
390
338
  }
391
- if rend.respond_to? :run
392
- rend.run
393
- else
394
- include AutoRunner
395
- end
396
- rend._run_ if rend.respond_to? :_run_
339
+ rend.run
397
340
  rend.formatter.save_output(rend.options.file) if rend.options.file
398
341
  return rend.formatter.output
399
342
  end
@@ -411,6 +354,8 @@ class Ruport::Renderer
411
354
  return @options
412
355
  end
413
356
 
357
+ private
358
+
414
359
  # Creates a new instance of the renderer and sets it to use the specified
415
360
  # formatter (by name). If a block is given, the renderer instance is
416
361
  # yielded.
@@ -435,8 +380,6 @@ class Ruport::Renderer
435
380
  return rend
436
381
  end
437
382
 
438
- private
439
-
440
383
  # Allows you to register a format with the renderer.
441
384
  #
442
385
  # Example:
@@ -474,6 +417,12 @@ class Ruport::Renderer
474
417
  formatter.options
475
418
  end
476
419
 
420
+ # Call the _run_ method. You can override this method in your custom
421
+ # renderer if you need to define other actions.
422
+ def run
423
+ _run_
424
+ end
425
+
477
426
  # If an IO object is given, Formatter#output will use it instead of
478
427
  # the default String. For Ruport's core renderers, we technically
479
428
  # can use any object that supports the << method, but it's meant
@@ -504,6 +453,43 @@ class Ruport::Renderer
504
453
 
505
454
  private
506
455
 
456
+ # Called automatically when the report is rendered. Uses the
457
+ # data collected from the earlier methods.
458
+ def _run_
459
+ # ensure all the required options have been set
460
+ unless self.class.required_options.nil?
461
+ self.class.required_options.each do |opt|
462
+ if options.__send__(opt).nil?
463
+ raise RequiredOptionNotSet, "Required option #{opt} not set"
464
+ end
465
+ end
466
+ end
467
+
468
+ if formatter.respond_to?(:apply_template) && options.template != false
469
+ formatter.apply_template if options.template ||
470
+ Ruport::Formatter::Template.default
471
+ end
472
+
473
+ prepare self.class.first_stage if self.class.first_stage
474
+
475
+ if formatter.respond_to?(:layout) && options.layout != false
476
+ formatter.layout do execute_stages end
477
+ else
478
+ execute_stages
479
+ end
480
+
481
+ finalize self.class.final_stage if self.class.final_stage
482
+ end
483
+
484
+ def execute_stages
485
+ # call each stage to build the report
486
+ unless self.class.stages.nil?
487
+ self.class.stages.each do |stage|
488
+ maybe("build_#{stage}")
489
+ end
490
+ end
491
+ end
492
+
507
493
  def prepare(name)
508
494
  maybe "prepare_#{name}"
509
495
  end
data/lib/ruport.rb CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  module Ruport #:nodoc:#
14
14
 
15
- VERSION = "1.2.3"
15
+ VERSION = "1.4.0"
16
16
 
17
17
  class FormatterError < RuntimeError #:nodoc:
18
18
  end
@@ -105,8 +105,11 @@ require "enumerator"
105
105
  require "ruport/renderer"
106
106
  require "ruport/data"
107
107
  require "ruport/formatter"
108
- require "ruport/query"
109
108
 
110
- if Object.const_defined? :ActiveRecord
111
- require "ruport/acts_as_reportable"
109
+ begin
110
+ if Object.const_defined? :ActiveRecord
111
+ require "ruport/acts_as_reportable"
112
+ end
113
+ rescue LoadError
114
+ nil
112
115
  end
@@ -11,32 +11,32 @@ end
11
11
  class TestRenderCSVTable < Test::Unit::TestCase
12
12
 
13
13
  def setup
14
- Ruport::Formatter::Template.create(:simple) do |t|
15
- t.table_format = {
14
+ Ruport::Formatter::Template.create(:simple) do |format|
15
+ format.table = {
16
16
  :show_headings => false
17
17
  }
18
- t.grouping_format = {
18
+ format.grouping = {
19
19
  :style => :justified,
20
20
  :show_headings => false
21
21
  }
22
- t.format_options = { :col_sep => ":" }
22
+ format.format_options = { :col_sep => ":" }
23
23
  end
24
24
  end
25
25
 
26
26
  def test_render_csv_table
27
27
  actual = Ruport::Renderer::Table.render_csv do |r|
28
- r.data = [[1,2,3],[4,5,6]].to_table
28
+ r.data = Table([], :data => [[1,2,3],[4,5,6]])
29
29
  end
30
30
  assert_equal("1,2,3\n4,5,6\n",actual)
31
31
 
32
32
  actual = Ruport::Renderer::Table.render_csv do |r|
33
- r.data = [[1,2,3],[4,5,6]].to_table(%w[a b c])
33
+ r.data = Table(%w[a b c], :data => [[1,2,3],[4,5,6]])
34
34
  end
35
35
  assert_equal("a,b,c\n1,2,3\n4,5,6\n",actual)
36
36
  end
37
37
 
38
38
  def test_format_options
39
- a = [[1,2,3],[4,5,6]].to_table(%w[a b c])
39
+ a = Table(%w[a b c], :data => [[1,2,3],[4,5,6]])
40
40
  assert_equal "a\tb\tc\n1\t2\t3\n4\t5\t6\n",
41
41
  a.as(:csv,:format_options => { :col_sep => "\t" })
42
42
  end
@@ -44,7 +44,7 @@ class TestRenderCSVTable < Test::Unit::TestCase
44
44
  def test_table_headers
45
45
  actual = Ruport::Renderer::Table.
46
46
  render_csv(:show_table_headers => false,
47
- :data => [[1,2,3],[4,5,6]].to_table(%w[a b c]))
47
+ :data => Table(%w[a b c], :data => [[1,2,3],[4,5,6]]))
48
48
  assert_equal("1,2,3\n4,5,6\n",actual)
49
49
  end
50
50