ruport 0.7.2 → 0.8.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.
Files changed (53) hide show
  1. data/AUTHORS +7 -3
  2. data/Rakefile +8 -9
  3. data/TODO +16 -0
  4. data/examples/RWEmerson.jpg +0 -0
  5. data/examples/centered_pdf_text_box.rb +66 -0
  6. data/examples/invoice.rb +35 -25
  7. data/examples/invoice_report.rb +1 -1
  8. data/examples/line_plotter.rb +1 -1
  9. data/examples/pdf_table_with_title.rb +42 -0
  10. data/lib/ruport.rb +5 -7
  11. data/lib/ruport.rb.rej +41 -0
  12. data/lib/ruport.rb~ +85 -0
  13. data/lib/ruport/attempt.rb +59 -59
  14. data/lib/ruport/config.rb +15 -4
  15. data/lib/ruport/data.rb +0 -2
  16. data/lib/ruport/data/groupable.rb +25 -16
  17. data/lib/ruport/data/record.rb +128 -102
  18. data/lib/ruport/data/table.rb +352 -199
  19. data/lib/ruport/data/taggable.rb +18 -7
  20. data/lib/ruport/format/html.rb +3 -1
  21. data/lib/ruport/format/latex.rb +1 -1
  22. data/lib/ruport/format/latex.rb.rej +26 -0
  23. data/lib/ruport/format/latex.rb~ +47 -0
  24. data/lib/ruport/format/pdf.rb +111 -28
  25. data/lib/ruport/format/pdf.rb.rej +168 -0
  26. data/lib/ruport/format/pdf.rb~ +189 -0
  27. data/lib/ruport/format/plugin.rb +0 -5
  28. data/lib/ruport/format/svg.rb +4 -4
  29. data/lib/ruport/format/xml.rb +3 -3
  30. data/lib/ruport/generator.rb +66 -27
  31. data/lib/ruport/mailer.rb +4 -1
  32. data/lib/ruport/query.rb +13 -1
  33. data/lib/ruport/renderer.rb +89 -17
  34. data/lib/ruport/renderer/graph.rb +5 -5
  35. data/lib/ruport/renderer/table.rb +8 -9
  36. data/lib/ruport/report.rb +2 -6
  37. data/test/test_config.rb +88 -76
  38. data/test/{test_text_table.rb → test_format_text.rb} +4 -2
  39. data/test/test_groupable.rb +15 -13
  40. data/test/test_query.rb +6 -3
  41. data/test/test_record.rb +57 -33
  42. data/test/test_renderer.rb +77 -0
  43. data/test/test_report.rb +188 -181
  44. data/test/test_ruport.rb +5 -6
  45. data/test/test_table.rb +290 -190
  46. data/test/test_table_renderer.rb +56 -8
  47. data/test/test_taggable.rb +7 -8
  48. data/test/unit.log +259 -7
  49. metadata +22 -19
  50. data/lib/ruport/data/collection.rb +0 -65
  51. data/lib/ruport/data/set.rb +0 -148
  52. data/test/test_collection.rb +0 -30
  53. data/test/test_set.rb +0 -118
data/lib/ruport/query.rb CHANGED
@@ -3,6 +3,7 @@ require "ruport/query/sql_split"
3
3
 
4
4
  module Ruport
5
5
 
6
+ #
6
7
  # === Overview
7
8
  #
8
9
  # Query offers a way to interact with databases via DBI. It supports
@@ -18,6 +19,7 @@ module Ruport
18
19
 
19
20
  include Enumerable
20
21
 
22
+ #
21
23
  # Queries are initialized with some SQL and a number of options that
22
24
  # affect their operation. They are NOT executed at initialization. This
23
25
  # is important to note as they will not query the database until either
@@ -96,6 +98,7 @@ module Ruport
96
98
  @cached_data = nil
97
99
  end
98
100
 
101
+ #
99
102
  # Set this to <tt>true</tt> to get DBI:Rows, <tt>false</tt> to get Ruport
100
103
  # constructs.
101
104
  #
@@ -107,6 +110,7 @@ module Ruport
107
110
  # The original SQL for the Query object
108
111
  attr_reader :sql
109
112
 
113
+ #
110
114
  # This will set the <tt>dsn</tt>, <tt>username</tt>, and <tt>password</tt>
111
115
  # to one specified by a source in Ruport::Config.
112
116
  #
@@ -116,18 +120,20 @@ module Ruport
116
120
  @password = Ruport::Config.sources[label].password
117
121
  end
118
122
 
123
+ #
119
124
  # Standard <tt>each</tt> iterator, iterates through the result set row by
120
125
  # row.
121
126
  #
122
127
  def each(&action)
123
128
  Ruport.log(
124
129
  "no block given!", :status => :fatal,
125
- :level => :log_only, :exception => LocalJumpError
130
+ :level => :log_only, :raises => LocalJumpError
126
131
  ) unless action
127
132
  fetch(&action)
128
133
  self
129
134
  end
130
135
 
136
+ #
131
137
  # Grabs the result set as a Data::Table or an Array of DBI::Row objects
132
138
  # if in <tt>raw_data</tt> mode.
133
139
  #
@@ -141,6 +147,7 @@ module Ruport
141
147
  @cached_data = nil
142
148
  end
143
149
 
150
+ #
144
151
  # Clears the contents of the cache, then runs the query, filling the
145
152
  # cache with the new result.
146
153
  #
@@ -149,6 +156,7 @@ module Ruport
149
156
  clear_cache; fetch
150
157
  end
151
158
 
159
+ #
152
160
  # Turns on caching. New data will not be loaded until the cache is clear
153
161
  # or caching is disabled.
154
162
  #
@@ -162,6 +170,7 @@ module Ruport
162
170
  @cache_enabled = false
163
171
  end
164
172
 
173
+ #
165
174
  # Returns a Data::Table, even if in <tt>raw_data</tt> mode.
166
175
  # This doesn't work with raw data if the cache is enabled and filled.
167
176
  #
@@ -183,9 +192,11 @@ module Ruport
183
192
  private
184
193
 
185
194
  def query_data(query_text, params=@params)
195
+
186
196
  require "dbi"
187
197
 
188
198
  data = @raw_data ? [] : Data::Table.new
199
+
189
200
  DBI.connect(@dsn, @user, @password) do |dbh|
190
201
  dbh.execute(query_text, *(params || [])) do |sth|
191
202
  # Work-around for inconsistent DBD behavior w/ resultless queries
@@ -197,6 +208,7 @@ module Ruport
197
208
  end
198
209
 
199
210
  data.column_names = names unless @raw_data
211
+
200
212
  sth.each do |row|
201
213
  row = row.to_a
202
214
  row = Data::Record.new(row, :attributes => names) unless @raw_data
@@ -14,6 +14,93 @@
14
14
  # you do not need that, may not be relevant to study for your use of Ruport.
15
15
  class Ruport::Renderer
16
16
  module Helpers #:nodoc:
17
+ module ClassMethods
18
+
19
+ # establish some class instance variables for storing require data
20
+ attr_accessor :first_stage,:final_stage,:required_options,:stages
21
+
22
+ # allow the report designer to specify what method will
23
+ # render the report e.g.
24
+ # finalize :document
25
+ #
26
+ def finalize(stage)
27
+ raise 'final stage already defined' if final_stage
28
+ self.final_stage = stage
29
+ end
30
+
31
+ # allow the report designer to specify a preparation stage for their
32
+ # report, e.g.
33
+ #
34
+ # prepare :document
35
+ #
36
+ def prepare(stage)
37
+ raise "prepare stage already defined" if first_stage
38
+ self.first_stage = stage
39
+ end
40
+
41
+ # allow the report designer to specify options that can be used to build
42
+ # the report. These are generally used for defining rendering options or
43
+ # data
44
+ # e.g.
45
+ # option :report_title
46
+ # option :table_width
47
+ def option(opt)
48
+ opt = "#{opt.to_s}="
49
+ define_method(opt) {|t| options.send(opt, t) }
50
+ end
51
+
52
+ # allow the report designer to specify a compulsory option
53
+ # e.g.
54
+ # required_option :freight
55
+ # required_option :tax
56
+ def required_option(opt)
57
+ self.required_options ||= []
58
+ self.required_options << opt
59
+ option opt
60
+ end
61
+
62
+ # allow the report designer to specify the stages that will be used to
63
+ # build the report
64
+ # e.g.
65
+ # stage :document_header
66
+ # stage :document_body
67
+ # stage :document_footer
68
+ def stage(stage)
69
+ self.stages ||= []
70
+ self.stages << stage.to_s
71
+ end
72
+ end
73
+
74
+ def self.included(base)
75
+ base.extend ClassMethods
76
+ end
77
+
78
+ # called automagically when the report is rendered. Uses the
79
+ # data collected from the earlier methods.
80
+ def run
81
+ # ensure all the required options have been set
82
+ unless self.class.required_options.nil?
83
+ self.class.required_options.each do |opt|
84
+ if options.__send__(opt).nil?
85
+ raise "Required option #{opt} not set"
86
+ end
87
+ end
88
+ end
89
+
90
+ prepare self.class.first_stage if self.class.first_stage
91
+
92
+ # call each stage to build the report
93
+ unless self.class.stages.nil?
94
+ self.class.stages.each do |stage|
95
+ self.build stage
96
+ end
97
+ end
98
+
99
+ finalize self.class.final_stage if self.class.final_stage
100
+
101
+ end
102
+
103
+
17
104
  def prepare(name)
18
105
  maybe "prepare_#{name}"
19
106
  end
@@ -33,8 +120,7 @@ class Ruport::Renderer
33
120
  plugin.send something if plugin.respond_to? something
34
121
  end
35
122
  end
36
-
37
- # Allows you to register a format with the renderer.
123
+ # allows you to register a format with the renderer.
38
124
  #
39
125
  # example:
40
126
  #
@@ -45,13 +131,11 @@ class Ruport::Renderer
45
131
  # SomeRenderer.add_format self, :my_plugin
46
132
  #
47
133
  # end
48
- #
49
134
  def self.add_format(format,name=nil)
50
135
  return formats[name] = format if name
51
136
 
52
- # NOTE: MEANT FOR INTERNAL USE
53
137
  add_core_format(format)
54
- end
138
+ end
55
139
 
56
140
 
57
141
  # reader for formats. Defaults to a hash
@@ -61,7 +145,6 @@ class Ruport::Renderer
61
145
 
62
146
  # same as Renderer#layout, but can be used to specify a default layout object
63
147
  # for the entire class.
64
- #
65
148
  def self.layout
66
149
  @layout ||= Ruport::Layout::Component.new
67
150
  yield(@layout) if block_given?
@@ -76,7 +159,6 @@ class Ruport::Renderer
76
159
  # The run() method is then called on the renderer method.
77
160
  #
78
161
  # Finally, the value of the plugin's output accessor is returned
79
- #
80
162
  def self.render(format,&block)
81
163
  rend = build format, &block
82
164
  rend.run
@@ -89,7 +171,6 @@ class Ruport::Renderer
89
171
  # yielded.
90
172
  #
91
173
  # Returns the renderer instance.
92
- #
93
174
  def self.build(format)
94
175
  rend = self.new
95
176
 
@@ -114,7 +195,6 @@ class Ruport::Renderer
114
195
  end
115
196
 
116
197
  # sets +data+ attribute on both the renderer and any active plugin
117
- #
118
198
  def data=(val)
119
199
  @data = val.dup
120
200
  plugin.data = @data if plugin
@@ -122,7 +202,6 @@ class Ruport::Renderer
122
202
 
123
203
  # General purpose openstruct which is shared with the current formatting
124
204
  # plugin.
125
- #
126
205
  def options
127
206
  yield(plugin.options) if block_given?
128
207
  plugin.options
@@ -135,7 +214,6 @@ class Ruport::Renderer
135
214
  #
136
215
  # when a block is given without block variables, instance_evals the block
137
216
  # within the context of the plugin
138
- #
139
217
  def plugin(&block)
140
218
  if block.nil?
141
219
  return @plugin
@@ -159,7 +237,6 @@ class Ruport::Renderer
159
237
 
160
238
  # tries to autoload and register a format which is part of the Ruport::Format
161
239
  # module. For internal use only.
162
- #
163
240
  def self.add_core_format(format)
164
241
  try_require(format)
165
242
 
@@ -170,14 +247,12 @@ class Ruport::Renderer
170
247
  end
171
248
 
172
249
  # internal shortcut for format registering
173
- #
174
250
  def self.add_formats(*formats)
175
251
  formats.each { |f| add_format f }
176
252
  end
177
253
 
178
254
  # Trys to autoload a given format,
179
255
  # silently fails
180
- #
181
256
  def self.try_require(format)
182
257
  begin
183
258
  require "ruport/format/#{format}"
@@ -187,20 +262,17 @@ class Ruport::Renderer
187
262
  end
188
263
 
189
264
  # selects a plugin for use by format name
190
- #
191
265
  def use_plugin(format)
192
266
  self.plugin = self.class.formats[format].new
193
267
  end
194
268
 
195
269
  # provides a shortcut to render() to allow
196
270
  # render(:csv) to become render_csv
197
- #
198
271
  def self.method_missing(id,*args,&block)
199
272
  id.to_s =~ /^render_(.*)/
200
273
  $1 ? render($1.to_sym,&block) : super
201
274
  end
202
275
 
203
-
204
276
  end
205
277
 
206
278
  require "ruport/renderer/table"
@@ -37,11 +37,11 @@ module Ruport
37
37
  l.style = :line
38
38
  end
39
39
 
40
- def run
41
- prepare :graph
42
- build :graph
43
- finalize :graph
44
- end
40
+ prepare :graph
41
+
42
+ stage :graph
43
+
44
+ finalize :graph
45
45
 
46
46
  end
47
47
 
@@ -20,7 +20,7 @@ module Ruport
20
20
  # table.as(:text){ |r| r.rewrite_column("col1") { |a| a[0] + 5 }
21
21
  # table.as(:csv) { |r| r.rewrite_column(2) { |a| a.capitalize }
22
22
  def rewrite_column(key,&block)
23
- data.each { |r| r[key] = block[r] }
23
+ data.to_a.each { |r| r[key] = block[r] }
24
24
  end
25
25
 
26
26
  # Gets the number of columns in a table. Useful in formatting plugins.
@@ -118,15 +118,14 @@ module Ruport
118
118
 
119
119
  add_formats :csv, :text, :html, :latex, :pdf
120
120
 
121
- layout do |lay|
122
- lay.show_table_headers = true
123
- end
121
+ layout { |lay| lay.show_table_headers = true }
122
+
123
+ prepare :table
124
124
 
125
- def run
126
- prepare :table
127
- build [:header,:body,:footer], :table
128
- finalize :table
129
- end
125
+ stage :table_header
126
+ stage :table_body
127
+ stage :table_footer
130
128
 
129
+ finalize :table
131
130
  end
132
131
  end
data/lib/ruport/report.rb CHANGED
@@ -160,8 +160,8 @@ module Ruport
160
160
  # Like Report#write, but will append to a file rather than overwrite it if
161
161
  # the file already exists.
162
162
  #
163
- def append(my_file=file)
164
- File.open(my_file,"a") { |f| f << results }
163
+ def append(my_file=file,my_results=results)
164
+ File.open(my_file,"a") { |f| f << my_results }
165
165
  end
166
166
 
167
167
  # This method passes <tt>self</tt> to Report.run.
@@ -308,10 +308,6 @@ module Ruport
308
308
  end
309
309
 
310
310
  end
311
-
312
311
  end
313
-
314
312
  end
315
313
  end
316
-
317
-
data/test/test_config.rb CHANGED
@@ -1,76 +1,88 @@
1
- require "test/unit"
2
- require "ruport"
3
-
4
- class TestConfiguration < Test::Unit::TestCase
5
-
6
- def setup
7
- Ruport::Config.log_file = "test/unit.log"
8
- end
9
-
10
- def test_dsn_defaults
11
- assert_equal(nil, Ruport::Config.default_source)
12
- end
13
-
14
- def test_mail_defaults
15
- assert_equal(nil, Ruport::Config.default_mailer)
16
- end
17
-
18
- def test_missing_dsn
19
- assert_raise(ArgumentError) {
20
- Ruport::Config.source :foo, :user => "root", :password => "fff"
21
- }
22
- assert_nothing_raised { Ruport::Config.source :bar, :dsn => "..." }
23
- end
24
-
25
- def test_mailer_errors
26
- assert_raise(ArgumentError) {
27
- Ruport::Config.mailer :bar, :user => :foo, :address => "foo@bar.com"
28
- }
29
- assert_nothing_raised { Ruport::Config.mailer :bar, :host => "localhost" }
30
- end
31
-
32
- def test_new_defaults
33
- Ruport::Config.source :default, :dsn => "dbi:mysql:test",
34
- :user => "root",
35
- :password => ""
36
- assert_equal("dbi:mysql:test", Ruport::Config.default_source.dsn)
37
- assert_equal("root", Ruport::Config.default_source.user)
38
- assert_equal("", Ruport::Config.default_source.password)
39
- end
40
-
41
- def test_multiple_sources
42
- Ruport::Config.source :foo, :dsn => "dbi:mysql:test"
43
- Ruport::Config.source :bar, :dsn => "dbi:mysql:test2"
44
- assert_equal("dbi:mysql:test", Ruport::Config.sources[:foo].dsn)
45
- assert_equal("dbi:mysql:test2", Ruport::Config.sources[:bar].dsn)
46
- end
47
-
48
- def test_simple_interface
49
- Ruport.configure do |c|
50
- c.source :foo, :dsn => "dbi:odbc:test"
51
- c.source :bar, :dsn => "dbi:odbc:test2"
52
- end
53
- assert_equal("dbi:odbc:test",Ruport::Config.sources[:foo].dsn)
54
- assert_equal("dbi:odbc:test2",Ruport::Config.sources[:bar].dsn)
55
- end
56
-
57
-
58
- def test_logger
59
- # We have a logger running now, dont we?
60
- assert(Ruport::Config.logger.kind_of?(Logger))
61
-
62
- # And then we change are mind again. Back logging?
63
- Ruport::Config.log_file = "test/unit.log"
64
- assert(Ruport::Config.logger.kind_of?(Logger))
65
-
66
- end
67
-
68
- def test_debug
69
- assert_equal(false, Ruport::Config.debug_mode?)
70
- Ruport::Config.debug_mode = true
71
- assert_equal(true, Ruport::Config.debug_mode?)
72
- Ruport::Config.debug_mode = false
73
- assert_equal(false, Ruport::Config.debug_mode?)
74
- end
75
-
76
- end
1
+ require "test/unit"
2
+ require "ruport"
3
+
4
+ class TestConfiguration < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Ruport::Config.log_file = "test/unit.log"
8
+ end
9
+
10
+ def test_dsn_defaults
11
+ assert_equal(nil, Ruport::Config.default_source)
12
+ end
13
+
14
+ def test_mail_defaults
15
+ assert_equal(nil, Ruport::Config.default_mailer)
16
+ end
17
+
18
+ def test_missing_dsn
19
+ assert_raise(ArgumentError) {
20
+ Ruport::Config.source :foo, :user => "root", :password => "fff"
21
+ }
22
+ assert_nothing_raised { Ruport::Config.source :bar, :dsn => "..." }
23
+ end
24
+
25
+ def test_mailer_errors
26
+ assert_raise(ArgumentError) {
27
+ Ruport::Config.mailer :bar, :user => :foo, :address => "foo@bar.com"
28
+ }
29
+ assert_nothing_raised { Ruport::Config.mailer :bar, :host => "localhost" }
30
+ end
31
+
32
+ def test_new_defaults
33
+ Ruport::Config.source :default, :dsn => "dbi:mysql:test",
34
+ :user => "root",
35
+ :password => ""
36
+ assert_equal("dbi:mysql:test", Ruport::Config.default_source.dsn)
37
+ assert_equal("root", Ruport::Config.default_source.user)
38
+ assert_equal("", Ruport::Config.default_source.password)
39
+ end
40
+
41
+ def test_multiple_sources
42
+ Ruport::Config.source :foo, :dsn => "dbi:mysql:test"
43
+ Ruport::Config.source :bar, :dsn => "dbi:mysql:test2"
44
+ assert_equal("dbi:mysql:test", Ruport::Config.sources[:foo].dsn)
45
+ assert_equal("dbi:mysql:test2", Ruport::Config.sources[:bar].dsn)
46
+ end
47
+
48
+ def test_simple_interface
49
+ Ruport.configure do |c|
50
+ c.source :foo, :dsn => "dbi:odbc:test"
51
+ c.source :bar, :dsn => "dbi:odbc:test2"
52
+ end
53
+ assert_equal("dbi:odbc:test",Ruport::Config.sources[:foo].dsn)
54
+ assert_equal("dbi:odbc:test2",Ruport::Config.sources[:bar].dsn)
55
+ end
56
+
57
+
58
+ def test_logger
59
+ # We have a logger running now, dont we?
60
+ assert(Ruport::Config.logger.kind_of?(Logger))
61
+
62
+ # And then we change are mind again. Back logging?
63
+ Ruport::Config.log_file = "test/unit.log"
64
+ assert(Ruport::Config.logger.kind_of?(Logger))
65
+
66
+ end
67
+
68
+ def test_debug
69
+ assert_equal(false, Ruport::Config.debug_mode?)
70
+ Ruport::Config.debug_mode = true
71
+ assert_equal(true, Ruport::Config.debug_mode?)
72
+ Ruport::Config.debug_mode = false
73
+ assert_equal(false, Ruport::Config.debug_mode?)
74
+ end
75
+
76
+ def test_adding_custom_accessors
77
+ Ruport::Config.my_new_setting = 'blinky'
78
+ assert_equal('blinky', Ruport::Config.my_new_setting)
79
+ Ruport::Config.my_new_setting = 'clyde'
80
+ assert_equal('clyde', Ruport::Config.my_new_setting)
81
+
82
+ Ruport::Config.my_other_new_setting 'inky'
83
+ assert_equal('inky', Ruport::Config.my_other_new_setting)
84
+ Ruport::Config.my_other_new_setting 'sue'
85
+ assert_equal('sue', Ruport::Config.my_other_new_setting)
86
+ end
87
+
88
+ end