ruport 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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