ruport 1.0.2 → 1.2.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.
@@ -66,7 +66,7 @@ module Ruport
66
66
  #
67
67
  module RenderingTools
68
68
  # Iterates through <tt>data</tt> and passes
69
- # each row to render_row with the given options
69
+ # each row to render_row with the given options.
70
70
  def render_data_by_row(options={},&block)
71
71
  data.each do |r|
72
72
  render_row(r,options,&block)
@@ -124,7 +124,9 @@ module Ruport
124
124
  def render_helper(rend_klass, source_data,options={},&block)
125
125
  options = {:data => source_data,
126
126
  :io => output,
127
- :layout => false }.merge(options)
127
+ :layout => false }.merge(options)
128
+
129
+ options[:io] = "" if self.class.kind_of?(Ruport::Formatter::PDF)
128
130
  rend_klass.render(format,options) do |rend|
129
131
  block[rend] if block
130
132
  end
@@ -140,11 +142,11 @@ module Ruport
140
142
  # Set automatically by Renderer#render(format) or Renderer#render_format
141
143
  attr_accessor :format
142
144
 
143
- # Set automatically by Renderer#render as a Renderer::Options option built
145
+ # Set automatically by Renderer#render as a Renderer::Options object built
144
146
  # by the hash provided.
145
147
  attr_writer :options
146
148
 
147
- # Registers the formatter with one or more Renderers
149
+ # Registers the formatter with one or more Renderers.
148
150
  #
149
151
  # renders :pdf, :for => MyRenderer
150
152
  # render :text, :for => [MyRenderer,YourRenderer]
@@ -172,6 +174,11 @@ module Ruport
172
174
  # Gives a list of formats registered for this formatter.
173
175
  def self.formats
174
176
  @formats ||= []
177
+ end
178
+
179
+ # Returns the template currently set for this formatter.
180
+ def template
181
+ Template[options.template]
175
182
  end
176
183
 
177
184
  # Stores a string used for outputting formatted data.
@@ -180,15 +187,20 @@ module Ruport
180
187
  @output ||= ""
181
188
  end
182
189
 
183
- # Provides a Renderer::Options object for storing formatting options
190
+ # Provides a Renderer::Options object for storing formatting options.
184
191
  def options
185
192
  @options ||= Renderer::Options.new
186
193
  end
187
194
 
188
- # Clears output.
195
+ # Clears the output.
189
196
  def clear_output
190
197
  @output.replace("")
191
- end
198
+ end
199
+
200
+ # Saves the output to a file.
201
+ def save_output(filename)
202
+ File.open(filename,"w") {|f| f << output }
203
+ end
192
204
 
193
205
  # Evaluates the string using ERB and returns the results.
194
206
  #
@@ -203,7 +215,7 @@ module Ruport
203
215
  end
204
216
  end
205
217
 
206
- # Provides a shortcut for per format handlers.
218
+ # Provides a shortcut for per-format handlers.
207
219
  #
208
220
  # Example:
209
221
  #
@@ -218,8 +230,9 @@ module Ruport
218
230
  end
219
231
  end
220
232
  end
221
- end
233
+ end
222
234
 
235
+ require "ruport/formatter/template"
223
236
  require "ruport/formatter/csv"
224
237
  require "ruport/formatter/html"
225
238
  require "ruport/formatter/text"
@@ -14,8 +14,8 @@
14
14
  module Ruport
15
15
 
16
16
  # This formatter implements the CSV format for Ruport's Row, Table, Group
17
- # and Grouping renderers. It is a light wrapper around James Edward Gray II's
18
- # FasterCSV.
17
+ # and Grouping renderers. It is a light wrapper around
18
+ # James Edward Gray II's FasterCSV.
19
19
  #
20
20
  # === Rendering Options
21
21
  #
@@ -37,6 +37,16 @@ module Ruport
37
37
  :show_group_headers,
38
38
  :style
39
39
 
40
+ # Hook for setting available options using a template. See the template
41
+ # documentation for the available options and their format.
42
+ def apply_template
43
+ apply_table_format_template(template.table_format)
44
+ apply_grouping_format_template(template.grouping_format)
45
+
46
+ options.format_options = template.format_options if
47
+ template.format_options
48
+ end
49
+
40
50
  # Generates table header by turning column_names into a CSV row.
41
51
  # Uses the row renderer to generate the actual formatted output
42
52
  #
@@ -82,7 +92,7 @@ module Ruport
82
92
  end
83
93
  end
84
94
 
85
- # determines the proper style to use and renders the Grouping.
95
+ # Determines the proper style to use and renders the Grouping.
86
96
  def build_grouping_body
87
97
  case style
88
98
  when :inline
@@ -110,5 +120,19 @@ module Ruport
110
120
  output << "\n"
111
121
  end
112
122
  end
123
+
124
+ def apply_table_format_template(t)
125
+ t = t || {}
126
+ options.show_table_headers = t[:show_headings] unless
127
+ t[:show_headings].nil?
128
+ end
129
+
130
+ def apply_grouping_format_template(t)
131
+ t = t || {}
132
+ options.style = t[:style] if t[:style]
133
+ options.show_group_headers = t[:show_headings] unless
134
+ t[:show_headings].nil?
135
+ end
136
+
113
137
  end
114
138
  end
@@ -11,9 +11,9 @@
11
11
  # See LICENSE and COPYING for details.
12
12
  #
13
13
  module Ruport
14
- # This class produces HTML output for Ruport's Row,Table,Group, and Grouping
15
- # renderers. It can be used as a subclass, as it has some helper methods
16
- # that might be useful for custom output
14
+ # This class produces HTML output for Ruport's Row, Table, Group, and
15
+ # Grouping renderers. It can be subclassed, as it has some helper methods
16
+ # that might be useful for custom output.
17
17
  #
18
18
  # === Rendering Options
19
19
  #
@@ -30,10 +30,17 @@ module Ruport
30
30
 
31
31
  opt_reader :show_table_headers, :show_group_headers, :style
32
32
 
33
+ # Hook for setting available options using a template. See the template
34
+ # documentation for the available options and their format.
35
+ def apply_template
36
+ apply_table_format_template(template.table_format)
37
+ apply_grouping_format_template(template.grouping_format)
38
+ end
39
+
33
40
  # Generates table headers based on the column names of your Data::Table.
34
41
  #
35
- # This method does not do anything if options.show_table_headers is false or
36
- # the Data::Table has no column names.
42
+ # This method does not do anything if options.show_table_headers is false
43
+ # or the Data::Table has no column names.
37
44
  def build_table_header
38
45
  output << "\t<table>\n"
39
46
  unless data.column_names.empty? || !show_table_headers
@@ -57,7 +64,7 @@ module Ruport
57
64
  output << "\t</table>"
58
65
  end
59
66
 
60
- # Renders individual rows for the table
67
+ # Renders individual rows for the table.
61
68
  def build_row
62
69
  output <<
63
70
  "\t\t<tr>\n\t\t\t<td>" +
@@ -139,6 +146,19 @@ module Ruport
139
146
  def grouping_columns
140
147
  data.data.to_a[0][1].column_names
141
148
  end
149
+
150
+ def apply_table_format_template(t)
151
+ t = t || {}
152
+ options.show_table_headers = t[:show_headings] unless
153
+ t[:show_headings].nil?
154
+ end
155
+
156
+ def apply_grouping_format_template(t)
157
+ t = t || {}
158
+ options.style = t[:style] if t[:style]
159
+ options.show_group_headers = t[:show_headings] unless
160
+ t[:show_headings].nil?
161
+ end
142
162
 
143
163
  end
144
164
  end
@@ -54,9 +54,9 @@ module Ruport
54
54
  #p "this actually gets called"
55
55
  max_width = PDF::Writer::OHash.new(-1)
56
56
 
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 do
59
- # some analysis first.
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
60
  @data.each do |row|
61
61
  @cols.each do |name, column|
62
62
  w = pdf.text_width(row[name].to_s, @font_size)
@@ -82,8 +82,7 @@ module Ruport
82
82
 
83
83
  attr_writer :pdf_writer
84
84
 
85
- opt_reader :show_table_headers,
86
- :style,
85
+ opt_reader :style,
87
86
  :table_format,
88
87
  :text_format,
89
88
  :paper_size,
@@ -95,6 +94,17 @@ module Ruport
95
94
  require "pdf/simpletable"
96
95
  end
97
96
  end
97
+
98
+ # Hook for setting available options using a template. See the template
99
+ # documentation for the available options and their format.
100
+ 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)
107
+ end
98
108
 
99
109
  # Returns the current PDF::Writer object or creates a new one if it has not
100
110
  # been set yet.
@@ -119,18 +129,18 @@ module Ruport
119
129
  render_pdf unless options.skip_finalize_table
120
130
  end
121
131
 
122
- # Generates a header with the group name for Renderer::Group
132
+ # Generates a header with the group name for Renderer::Group.
123
133
  def build_group_header
124
134
  pad(10) { add_text data.name.to_s, :justification => :center }
125
135
  end
126
136
 
127
- # Renders the group as a table for Renderer::Group
137
+ # Renders the group as a table for Renderer::Group.
128
138
  def build_group_body
129
139
  render_table data, options.to_hash.merge(:formatter => pdf_writer)
130
140
  end
131
141
 
132
142
  # Determines which style to use and renders the main body for
133
- # Renderer::Grouping
143
+ # Renderer::Grouping.
134
144
  def build_grouping_body
135
145
  case style
136
146
  when :inline
@@ -145,13 +155,13 @@ module Ruport
145
155
  end
146
156
  end
147
157
 
148
- # calls <tt>render_pdf</tt>
158
+ # Calls <tt>render_pdf</tt>.
149
159
  def finalize_grouping
150
160
  render_pdf
151
161
  end
152
162
 
153
- # Call PDF::Writer#text with the given arguments, using <tt>text_format</tt>
154
- # defaults, if they are defined.
163
+ # Call PDF::Writer#text with the given arguments, using
164
+ # <tt>text_format</tt> defaults, if they are defined.
155
165
  #
156
166
  # Example:
157
167
  #
@@ -164,7 +174,7 @@ module Ruport
164
174
  pdf_writer.text(text, format_opts)
165
175
  end
166
176
 
167
- # Calls PDF::Writer#render and appends to <tt>output</tt>
177
+ # Calls PDF::Writer#render and appends to <tt>output</tt>.
168
178
  def render_pdf
169
179
  output << pdf_writer.render
170
180
  end
@@ -196,7 +206,7 @@ module Ruport
196
206
  pdf_writer.add_image_from_file(path, x, y, img_width, img_height)
197
207
  end
198
208
 
199
- # Draws some text on the canvas, surrounded by a box with rounded corner
209
+ # Draws some text on the canvas, surrounded by a box with rounded corners.
200
210
  #
201
211
  # Yields an OpenStruct which options can be defined on.
202
212
  #
@@ -254,22 +264,22 @@ module Ruport
254
264
  end
255
265
  end
256
266
 
257
- # adds n to pdf_writer.y, moving the vertical drawing position in the
258
- # document
267
+ # Adds n to pdf_writer.y, moving the vertical drawing position in the
268
+ # document.
259
269
  def move_cursor(n)
260
270
  pdf_writer.y += n
261
271
  end
262
272
 
263
- # moves the cursor to a specific y coordinate in the document
273
+ # Moves the cursor to a specific y coordinate in the document.
264
274
  def move_cursor_to(n)
265
275
  pdf_writer.y = n
266
276
  end
267
277
 
268
- # puts a specified amount of whitespace above and below the code
278
+ # Adds a specified amount of whitespace above and below the code
269
279
  # in your block. For example, if you want to surround the top and
270
280
  # bottom of a line of text with 5 pixels of whitespace:
271
281
  #
272
- # pad(5) { add_text "This will be padded" }
282
+ # pad(5) { add_text "This will be padded top and bottom" }
273
283
  def pad(y,&block)
274
284
  move_cursor(-y)
275
285
  block.call
@@ -290,13 +300,13 @@ module Ruport
290
300
  # For example, if you want to add a 10 pixel buffer to the bottom of a
291
301
  # line of text:
292
302
  #
293
- # pad_bottom(10) { add_text "This will be padded on top" }
303
+ # pad_bottom(10) { add_text "This will be padded on bottom" }
294
304
  def pad_bottom(y,&block)
295
305
  block.call
296
306
  move_cursor(-y)
297
307
  end
298
308
 
299
- # Draws a PDF::SimpleTable using the given data (usually a Data::Table)
309
+ # Draws a PDF::SimpleTable using the given data (usually a Data::Table).
300
310
  # Takes all the options you can set on a PDF::SimpleTable object,
301
311
  # see the PDF::Writer API docs for details, or check our quick reference
302
312
  # at:
@@ -327,25 +337,30 @@ module Ruport
327
337
  pdf_writer.font_size = old
328
338
  end
329
339
 
330
- # This module provides tools to simplify some common drawing operations
331
- # it is included by default in the PDF formatter
340
+ # Save the output to a file.
341
+ def save_output(filename)
342
+ File.open(filename,"wb") {|f| f << output }
343
+ end
344
+
345
+ # This module provides tools to simplify some common drawing operations.
346
+ # It is included by default in the PDF formatter.
332
347
  #
333
348
  module DrawingHelpers
334
349
 
335
- # draws a horizontal line from x1 to x2
350
+ # Draws a horizontal line from x1 to x2
336
351
  def horizontal_line(x1,x2)
337
352
  pdf_writer.line(x1,cursor,x2,cursor)
338
353
  pdf_writer.stroke
339
354
  end
340
355
 
341
- # draws a horizontal line from left_boundary to right_boundary
356
+ # Draws a horizontal line from left_boundary to right_boundary
342
357
  def horizontal_rule
343
358
  horizontal_line(left_boundary,right_boundary)
344
359
  end
345
360
 
346
361
  alias_method :hr, :horizontal_rule
347
362
 
348
- # draws a vertical line at x from y1 to y2
363
+ # Draws a vertical line at x from y1 to y2
349
364
  def vertical_line_at(x,y1,y2)
350
365
  pdf_writer.line(x,y1,x,y2)
351
366
  pdf_writer.stroke
@@ -377,33 +392,82 @@ module Ruport
377
392
  end
378
393
 
379
394
  # Draws text at an absolute location, defined by
380
- # :y, :x1|:left, :x2|:right
395
+ # :y, :x1|:left, :x2|:right
381
396
  #
382
- # All usual options to add_text are also supported
397
+ # All options to add_text are also supported.
383
398
  def draw_text(text,text_opts)
399
+ ypos = cursor
384
400
  move_cursor_to(text_opts[:y]) if text_opts[:y]
385
401
  add_text(text,
386
402
  text_opts.merge(:absolute_left => text_opts[:x1] || text_opts[:left],
387
403
  :absolute_right => text_opts[:x2] || text_opts[:right]))
404
+ move_cursor_to(ypos)
388
405
  end
389
406
 
407
+ # Draws text at an absolute location, defined by
408
+ # :y, :x1|:left
409
+ #
410
+ # The x position defaults to the left margin and the
411
+ # y position defaults to the current cursor location.
412
+ #
413
+ # Uses PDF::Writer#add_text, so it will ignore any options not supported
414
+ # by that method.
415
+ def draw_text!(text,text_opts)
416
+ ypos = cursor
417
+ pdf_writer.add_text(text_opts[:x1] || text_opts[:left] || left_boundary,
418
+ text_opts[:y] || ypos,
419
+ text,
420
+ text_opts[:font_size],
421
+ text_opts[:angle] || 0)
422
+ move_cursor_to(ypos)
423
+ end
390
424
  end
391
425
 
392
426
  include DrawingHelpers
393
427
 
394
- private
428
+ private
395
429
 
396
430
  def apply_pdf_table_column_opts(table,table_data,format_opts)
397
- column_opts = format_opts.delete(:column_options)
398
- if column_opts
399
- columns = table_data.column_names.inject({}) { |s,c|
400
- s.merge( c => ::PDF::SimpleTable::Column.new(c) { |col|
401
- column_opts.each { |k,v| col.send("#{k}=",v) }
431
+ column_opts = format_opts.delete(:column_options)
432
+
433
+ if column_opts
434
+ heading_opts = column_opts.delete(:heading)
435
+ if column_opts[:justification]
436
+ heading_opts ||= {}
437
+ heading_opts = {
438
+ :justification => column_opts[:justification]
439
+ }.merge(heading_opts)
440
+ end
441
+ specific = get_specific_column_options(table_data.column_names,
442
+ column_opts)
443
+ columns = table_data.column_names.inject({}) { |s,c|
444
+ s.merge( c => ::PDF::SimpleTable::Column.new(c) { |col|
445
+ col.heading = create_heading(heading_opts)
446
+ column_opts.each { |k,v| col.send("#{k}=",v) }
447
+ # use the specific column names now
448
+ specific[c].each { |k,v| col.send("#{k}=",v) }
402
449
  })
403
- }
450
+ }
404
451
  table.columns = columns
405
- end
406
- end
452
+ end
453
+ end
454
+
455
+ def get_specific_column_options(column_names,column_opts)
456
+ column_names.inject({}) do |s,c|
457
+ opts = column_opts.delete(c) || {}
458
+ if opts[:heading]
459
+ opts = opts.merge(:heading => create_heading(opts[:heading]))
460
+ end
461
+ s.merge(c => opts)
462
+ end
463
+ end
464
+
465
+ def create_heading(heading_opts)
466
+ heading_opts ||= {}
467
+ ::PDF::SimpleTable::Column::Heading.new {|head|
468
+ heading_opts.each {|k,v| head.send("#{k}=",v) }
469
+ }
470
+ end
407
471
 
408
472
  def grouping_columns
409
473
  data.data.to_a[0][1].column_names.dup.unshift(data.grouped_by)
@@ -487,5 +551,74 @@ module Ruport
487
551
  :justification => :center, :font_size => font_size)
488
552
  end
489
553
 
554
+ def apply_page_format_template(t)
555
+ t = t || {}
556
+ options.paper_size = t[:size] if t[:size]
557
+ options.paper_orientation = t[:layout] if t[:layout]
558
+ end
559
+
560
+ def apply_text_format_template(t)
561
+ options.text_format = template.text_format if t
562
+ end
563
+
564
+ def apply_table_format_template(t)
565
+ options.table_format = template.table_format.dup if t
566
+ end
567
+
568
+ def apply_column_format_template(t)
569
+ t = t || {}
570
+ column_opts = {}
571
+ column_opts.merge!(:justification => t[:alignment]) if t[:alignment]
572
+ column_opts.merge!(:width => t[:width]) if t[:width]
573
+ unless column_opts.empty?
574
+ if options.table_format
575
+ if options.table_format[:column_options]
576
+ options.table_format[:column_options] =
577
+ column_opts.merge(options.table_format[:column_options])
578
+ else
579
+ options.table_format.merge!(:column_options => column_opts)
580
+ end
581
+ else
582
+ options.table_format = { :column_options => column_opts }
583
+ end
584
+ end
585
+ end
586
+
587
+ def apply_heading_format_template(t)
588
+ t = t || {}
589
+ heading_opts = {}
590
+ heading_opts.merge!(:justification => t[:alignment]) if t[:alignment]
591
+ heading_opts.merge!(:bold => t[:bold]) unless t[:bold].nil?
592
+ heading_opts.merge!(:title => t[:title]) if t[:title]
593
+ unless heading_opts.empty?
594
+ if options.table_format
595
+ if options.table_format[:column_options]
596
+ if options.table_format[:column_options][:heading]
597
+ options.table_format[:column_options][:heading] =
598
+ heading_opts.merge(
599
+ options.table_format[:column_options][:heading]
600
+ )
601
+ else
602
+ options.table_format[:column_options].merge!(
603
+ :heading => heading_opts
604
+ )
605
+ end
606
+ else
607
+ options.table_format.merge!(
608
+ :column_options => { :heading => heading_opts }
609
+ )
610
+ end
611
+ else
612
+ options.table_format = {
613
+ :column_options => { :heading => heading_opts }
614
+ }
615
+ end
616
+ end
617
+ end
618
+
619
+ def apply_grouping_format_template(t)
620
+ t = t || {}
621
+ options.style = t[:style] if t[:style]
622
+ end
490
623
  end
491
624
  end