ruport 0.5.4 → 0.6.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 (56) hide show
  1. data/AUTHORS +13 -4
  2. data/CHANGELOG +15 -1
  3. data/Rakefile +1 -1
  4. data/bin/rope +5 -1
  5. data/examples/basic_grouping.rb +13 -3
  6. data/examples/latex_table.rb +17 -0
  7. data/examples/line_graph_report.rb +23 -0
  8. data/examples/line_graph_report.rb.rej +15 -0
  9. data/examples/line_graph_report.rb~ +23 -0
  10. data/examples/line_plotter.rb +1 -0
  11. data/examples/sql_erb.rb +20 -0
  12. data/examples/template.rb +1 -1
  13. data/examples/text_processors.rb +3 -9
  14. data/lib/ruport.rb +2 -3
  15. data/lib/ruport.rb~ +69 -0
  16. data/lib/ruport/attempt.rb +63 -0
  17. data/lib/ruport/data.rb +1 -1
  18. data/lib/ruport/data.rb.rej +5 -0
  19. data/lib/ruport/data.rb~ +1 -0
  20. data/lib/ruport/data/groupable.rb +20 -0
  21. data/lib/ruport/data/record.rb +18 -13
  22. data/lib/ruport/data/table.rb +92 -26
  23. data/lib/ruport/data/table.rb~ +329 -0
  24. data/lib/ruport/format.rb +7 -1
  25. data/lib/ruport/format/engine/table.rb +2 -1
  26. data/lib/ruport/format/plugin.rb +8 -8
  27. data/lib/ruport/format/plugin/csv_plugin.rb +5 -1
  28. data/lib/ruport/format/plugin/html_plugin.rb +12 -8
  29. data/lib/ruport/format/plugin/latex_plugin.rb +50 -0
  30. data/lib/ruport/format/plugin/text_plugin.rb +38 -33
  31. data/lib/ruport/meta_tools.rb +3 -3
  32. data/lib/ruport/query.rb +21 -9
  33. data/lib/ruport/report.rb +35 -20
  34. data/lib/ruport/report/graph.rb +14 -0
  35. data/lib/ruport/system_extensions.rb +9 -8
  36. data/test/_test_groupable.rb +0 -0
  37. data/test/samples/data.tsv +3 -0
  38. data/test/samples/erb_test.sql +1 -0
  39. data/test/samples/query_test.sql +1 -0
  40. data/test/test_collection.rb +1 -1
  41. data/test/test_format.rb +1 -1
  42. data/test/test_format_engine.rb +17 -0
  43. data/test/test_groupable.rb +41 -0
  44. data/test/test_invoice.rb +1 -1
  45. data/test/test_latex.rb +20 -0
  46. data/test/test_plugin.rb +59 -29
  47. data/test/test_query.rb +12 -6
  48. data/test/test_record.rb +23 -4
  49. data/test/test_record.rb.rej +46 -0
  50. data/test/test_report.rb +32 -7
  51. data/test/test_table.rb +152 -4
  52. data/test/ts_all.rb +21 -0
  53. data/test/unit.log +61 -154
  54. metadata +32 -12
  55. data/lib/ruport/rails.rb +0 -2
  56. data/lib/ruport/rails/reportable.rb +0 -58
data/AUTHORS CHANGED
@@ -8,17 +8,26 @@
8
8
  = Contributors / People we've (legally) stolen from:
9
9
 
10
10
  Iain Broadfoot:
11
- - RuportDay 2006 Participant
11
+ - RuportDay 2006 Participant
12
12
 
13
13
  Eric Pugh:
14
- - RuportDay 2006 Participant
14
+ - RuportDay 2006 Participant
15
15
 
16
16
  James Edward Gray II:
17
17
  - Original inspiration via query.rb
18
18
  - system_extensions.rb
19
19
 
20
+ Mathijs Mohlmann:
21
+ - Performance Enhancement
22
+ TextPlugin r264-r265
23
+ HTMLPlugin r266
24
+
20
25
  Francis Hwang:
21
- - SQLSplit
26
+ - SQLSplit
22
27
 
23
28
  Simon Claret:
24
- - Initial PDF table support (now deprecated)
29
+ - Initial PDF table support (now deprecated)
30
+
31
+ Daniel Berger:
32
+ - we vendored and modified attempt.rb to support it directly in Report.
33
+ Original website: http://raa.ruby-lang.org/project/attempt/
data/CHANGELOG CHANGED
@@ -1,4 +1,18 @@
1
- Tnghe current version of Ruby Reports is 0.5.4
1
+ The current version of Ruby Reports is 0.6.0
2
+
3
+ changes since 0.5.4
4
+
5
+ From Ruport News (http://www.stonecode.org/blog/?cat=14)
6
+
7
+ * added ERb support inside SQL queries
8
+ * a new LaTeX plugin for the table formatting engine
9
+ * support for summation in data tables
10
+ * new grouping functionality based on the tagging system
11
+ * very large performance enhancements in HTML and text rendering
12
+ * better integration with FasterCSV
13
+ * some simple interfaces for graphs and data tables
14
+ * syntactic sugar (shortcuts) for common tasks
15
+ * support for rerunning reports on timeouts and errors
2
16
 
3
17
  changes since 0.5.3
4
18
 
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ end
23
23
 
24
24
  spec = Gem::Specification.new do |spec|
25
25
  spec.name = LEAN ? "lean-ruport" : "ruport"
26
- spec.version = "0.5.4"
26
+ spec.version = "0.6.0"
27
27
  spec.platform = Gem::Platform::RUBY
28
28
  spec.summary = "A generalized Ruby report generation and templating engine."
29
29
  spec.files = Dir.glob("{examples,lib,test,bin}/**/**/**/*") +
data/bin/rope CHANGED
@@ -68,7 +68,11 @@ class #{class_name} < Ruport::Report
68
68
 
69
69
  end
70
70
 
71
- ##{class_name}.run { |res| puts res.results }
71
+ # uncomment the line below to let the report be run directly
72
+ #
73
+ # if __FILE__ == $0
74
+ # #{class_name}.run { |res| puts res.results }
75
+ # end
72
76
  EOR
73
77
 
74
78
  TEST = <<EOR
@@ -1,9 +1,19 @@
1
1
  require "ruport"
2
2
  a = [ ['a',7],['b',5],['c',11],
3
- ['d',9],['a',3],['b',2] ].to_table(%w[letter num])
3
+ ['d',9],['a',3],['b',2], ['e',4] ].to_table(%w[letter num])
4
4
  puts "Initial Data:\n#{a}"
5
+
6
+
7
+ # group by column values
5
8
  b = a.split(:group => "letter")
6
9
  totals = [].to_table(%w[group sum])
7
- b.attributes.each { |x| totals << [x,b[x].inject(0) { |s,r| s + r['num'] }] }
8
- puts "After grouping:\n#{totals}"
10
+ b.each_group { |x| totals << [x,b[x].sum("num")] }
11
+ puts "After column grouping:\n#{totals}"
9
12
 
13
+ # group by tag name
14
+ a.create_tag_group(:num_even) { |r| (r.num % 2).zero? }
15
+ a.create_tag_group(:num_odd) { |r| (r.num % 2).nonzero? }
16
+ c = a.group_by_tag
17
+ totals.data.clear
18
+ c.each_group { |x| totals << [x,c[x].sum("num")] }
19
+ puts "After tag grouping:\n#{totals}"
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/../lib/")
2
+ require "ruport"
3
+
4
+ # build up a Table - this could be built in a range of ways
5
+ # Check the API documentation and recipe book for more information
6
+ data = [[1,4,7,9], [6,2,3,0]].to_table(%w[a b c d])
7
+
8
+ # Build the report object
9
+ report = Ruport::Format.table_object(:plugin => :latex, :data => data)
10
+
11
+ # By default, the latex plugin will return plain text latex source
12
+ # changing the format option asks Ruport to attempt to render
13
+ # the source into a PDF using pdflatex
14
+ report.options = { :format => :pdf }
15
+
16
+ # save the resulting report to a file on the filesystem
17
+ File.open( "table.tex","w") { |f| f.puts report.render }
@@ -0,0 +1,23 @@
1
+ require "ruport"
2
+ class GraphSample < Ruport::Report
3
+
4
+ include Graph
5
+
6
+ prepare do
7
+ @data = [[5,7,9,12,14,16,18]].to_table(%w[jan feb mar apr may jun jul])
8
+ end
9
+
10
+ generate do
11
+ render_graph do |g|
12
+ g.data = @data
13
+ g.width = 700
14
+ g.height = 500
15
+ g.title = "A Simple Line Graph"
16
+ g.style = :line
17
+ end
18
+ end
19
+ end
20
+
21
+ GraphSample.run { |r| puts r.results }
22
+
23
+
@@ -0,0 +1,15 @@
1
+ ***************
2
+ *** 19,24 ****
3
+ end
4
+ end
5
+
6
+ - GraphSample.run { |r| puts r.write }
7
+
8
+
9
+ --- 19,24 ----
10
+ end
11
+ end
12
+
13
+ + GraphSample.run(:tries => 3, :timeout => 1) { |r| sleep 2; puts r.results }
14
+
15
+
@@ -0,0 +1,23 @@
1
+ require "ruport"
2
+ class GraphSample < Ruport::Report
3
+
4
+ include Graph
5
+
6
+ prepare do
7
+ @data = [[5,7,9,12,14,16,18]].to_table(%w[jan feb mar apr may jun jul])
8
+ end
9
+
10
+ generate do
11
+ render_graph do |g|
12
+ g.data = @data
13
+ g.width = 700
14
+ g.height = 500
15
+ g.title = "A Simple Line Graph"
16
+ g.style = :line
17
+ end
18
+ end
19
+ end
20
+
21
+ GraphSample.run { |r| puts r.results }
22
+
23
+
@@ -32,6 +32,7 @@ class SVG < Format::Plugin
32
32
  } << "</g></svg>"
33
33
  end
34
34
 
35
+ plugin_name :svg
35
36
  register_on :line_plotting_engine
36
37
  end
37
38
 
@@ -0,0 +1,20 @@
1
+ require "ruport"
2
+ require "rubygems"
3
+
4
+ class Foo < Ruport::Report
5
+
6
+ SQL = "select * from <%= helper %>"
7
+
8
+ prepare do
9
+ source :default, :dsn => "dbi:mysql:foo", :user => "root"
10
+ end
11
+
12
+ generate do
13
+ query(SQL)
14
+ end
15
+
16
+ def helper; "bar" end
17
+
18
+ end
19
+
20
+ Foo.run { |r| puts r.results }
data/examples/template.rb CHANGED
@@ -12,4 +12,4 @@ class MyReport < Ruport::Report
12
12
  generate { eval_template TEMPLATE }
13
13
  end
14
14
 
15
- MyReport.run
15
+ MyReport.run { |r| puts r.results }
@@ -3,17 +3,11 @@ require "ruport"
3
3
  class MyReport < Ruport::Report
4
4
  prepare {
5
5
  self.results = "Foo Bar Baz"
6
- text_processor(:replace_foo) { results.gsub!(/Foo/,"Ruport") }
7
- text_processor(:replace_bar) { results.gsub!(/Bar/,"Is") }
8
- text_processor(:replace_baz) { results.gsub!(/Baz/, "Cool!") }
6
+ text_processor(:replace_foo) { |r| r.gsub(/Foo/,"Ruport") }
7
+ text_processor(:replace_bar) { |r| r.gsub(/Bar/,"Is") }
8
+ text_processor(:replace_baz) { |r| r.gsub(/Baz/, "Cool!") }
9
9
  }
10
10
  generate {
11
11
  process_text results, :filters => [:replace_foo,:replace_bar,:replace_baz]
12
12
  }
13
13
  end
14
-
15
- MyReport.run { |e| puts e.results }
16
-
17
-
18
-
19
-
data/lib/ruport.rb CHANGED
@@ -9,10 +9,9 @@
9
9
  #
10
10
  # See LICENSE and COPYING for details
11
11
  #
12
-
13
12
  module Ruport
14
13
 
15
- VERSION = "0.5.4"
14
+ VERSION = "0.6.0"
16
15
 
17
16
  # Ruports logging and error interface.
18
17
  # Can generate warnings or raise fatal errors
@@ -65,6 +64,6 @@ module Ruport
65
64
  end
66
65
 
67
66
 
68
- %w[config meta_tools report format query data mailer].each { |lib|
67
+ %w[attempt config data meta_tools report format query mailer].each { |lib|
69
68
  require "ruport/#{lib}"
70
69
  }
data/lib/ruport.rb~ ADDED
@@ -0,0 +1,69 @@
1
+ # ruport.rb : Ruby Reports toplevel module
2
+ #
3
+ # Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
4
+ #
5
+ # Copyright (c) 2006, All Rights Reserved.
6
+ #
7
+ # This is free software. You may modify and redistribute this freely under
8
+ # your choice of the GNU General Public License or the Ruby License.
9
+ #
10
+ # See LICENSE and COPYING for details
11
+ #
12
+ module Ruport
13
+
14
+ VERSION = "0.6.0"
15
+
16
+ # Ruports logging and error interface.
17
+ # Can generate warnings or raise fatal errors
18
+ #
19
+ # Takes a message to display and a set of options.
20
+ # Will log to the file defined by Config::log_file
21
+ #
22
+ # Options:
23
+ # <tt>:status</tt>:: sets the severity level. defaults to <tt>:warn</tt>
24
+ # <tt>:output</tt>:: optional 2nd output, defaults to <tt>$stderr</tt>
25
+ # <tt>:level</tt>:: set to <tt>:log_only</tt> to disable secondary output
26
+ # <tt>:exception</tt>:: exception to throw on fail. Defaults to RunTimeError
27
+ #
28
+ # The status <tt>:warn</tt> will invoke Logger#warn. A status of
29
+ # <tt>:fatal</tt> will invoke Logger#fatal and raise an exception
30
+ #
31
+ # By default, <tt>log()</tt> will also print warnings to $stderr
32
+ # You can redirect this to any I/O object via <tt>:output</tt>
33
+ #
34
+ # You can prevent messages from appearing on the secondary output by setting
35
+ # <tt>:level</tt> to <tt>:log_only</tt>
36
+ #
37
+ # If you want to recover these messages to secondary output for debugging, you
38
+ # can use Config::enable_paranoia
39
+ def self.log(message,options={})
40
+ options = {:status => :warn, :output => $stderr}.merge(options)
41
+ options[:output].puts "[!!] #{message}" unless
42
+ options[:level].eql?(:log_only) and not Ruport::Config.paranoid?
43
+ Ruport::Config::logger.send(options[:status],message) if Config.logger
44
+ if options[:status].eql? :fatal
45
+ raise(options[:exception] || RuntimeError, message)
46
+ end
47
+ end
48
+
49
+ #Alias for Ruport.log
50
+ def self.complain(*args); Ruport.log(*args) end
51
+
52
+ # yields a Ruport::Config object, allowing you to specify configuration
53
+ # options.
54
+ #
55
+ # Example:
56
+ #
57
+ # Ruport.configure do |c|
58
+ # c.source :default, :dsn => "dbi:mysql:foo",
59
+ # :user => "clyde", :password => "pman"
60
+ # end
61
+ def self.configure(&block)
62
+ block.call(Ruport::Config)
63
+ end
64
+ end
65
+
66
+
67
+ %w[attempt config meta_tools report format query data mailer].each { |lib|
68
+ require "ruport/#{lib}"
69
+ }
@@ -0,0 +1,63 @@
1
+ require 'timeout'
2
+
3
+ class Attempt
4
+ VERSION = '0.1.0'
5
+
6
+ # Number of attempts to make before failing. The default is 3.
7
+ attr_accessor :tries
8
+
9
+ # Number of seconds to wait between attempts. The default is 60.
10
+ attr_accessor :interval
11
+
12
+ # a level which ruport understands.
13
+ attr_accessor :log_level
14
+
15
+ # If set, this increments the interval with each failed attempt by that
16
+ # number of seconds.
17
+ attr_accessor :increment
18
+
19
+ # If set, the code block is further wrapped in a timeout block.
20
+ attr_accessor :timeout
21
+
22
+ # Determines which exception level to check when looking for errors to
23
+ # retry. The default is 'Exception' (i.e. all errors).
24
+ attr_accessor :level
25
+
26
+ # :call-seq:
27
+ # Attempt.new{ |a| ... }
28
+ #
29
+ # Creates and returns a new +Attempt+ object. Use a block to set the
30
+ # accessors.
31
+ #
32
+ def initialize
33
+ @tries = 3 # Reasonable default
34
+ @interval = 60 # Reasonable default
35
+ @increment = nil # Should be an int, if provided
36
+ @timeout = nil # Wrap the code in a timeout block if provided
37
+ @level = Exception # Level of exception to be caught
38
+
39
+ yield self if block_given?
40
+ end
41
+
42
+ def attempt
43
+ count = 1
44
+ begin
45
+ if @timeout
46
+ Timeout.timeout(@timeout){ yield }
47
+ else
48
+ yield
49
+ end
50
+ rescue @level => error
51
+ @tries -= 1
52
+ if @tries > 0
53
+ msg = "Error on attempt # #{count}: #{error}; retrying"
54
+ count += 1
55
+ Ruport.log(msg, :level => log_level)
56
+ @interval += @increment if @increment
57
+ sleep @interval
58
+ retry
59
+ end
60
+ raise
61
+ end
62
+ end
63
+ end
data/lib/ruport/data.rb CHANGED
@@ -1 +1 @@
1
- %w[taggable record collection table set].each { |l| require "ruport/data/#{l}" }
1
+ %w[groupable taggable record collection table set ].each { |l| require "ruport/data/#{l}" }
@@ -0,0 +1,5 @@
1
+ ***************
2
+ *** 1 ****
3
+ - %w[taggable record collection table set groupable ].each { |l| require "ruport/data/#{l}" }
4
+ --- 1 ----
5
+ + %w[groupable taggable record collection table set].each { |l| require "ruport/data/#{l}" }
@@ -0,0 +1 @@
1
+ %w[taggable record collection table set groupable].each { |l| require "ruport/data/#{l}" }
@@ -0,0 +1,20 @@
1
+ module Ruport::Data
2
+ module Groupable
3
+
4
+ def group_by_tag
5
+ r_tags = data.map {|row| row.tags}.flatten.uniq
6
+ d = r_tags.map do |t|
7
+ select {|row| row.tags.include? t }.to_table(column_names)
8
+ end
9
+ r = Record.new d, :attributes => r_tags
10
+ class << r
11
+ def each_group; attributes.each { |a| yield(a) }; end
12
+ end; r
13
+ end
14
+
15
+ def create_tag_group(label,&block)
16
+ select(&block).each { |r| r.tag label }
17
+ end
18
+
19
+ end
20
+ end
@@ -47,7 +47,11 @@ module Ruport::Data
47
47
  end
48
48
  else
49
49
  @data = data.dup
50
- @attributes = options[:attributes]
50
+ @attributes = if options[:attributes]
51
+ options[:attributes]
52
+ else
53
+ []
54
+ end
51
55
  end
52
56
  end
53
57
 
@@ -68,8 +72,8 @@ module Ruport::Data
68
72
  raise "Invalid index" unless index < @data.length
69
73
  @data[index]
70
74
  else
71
- raise "Invalid index" unless @attributes.index(index)
72
- @data[@attributes.index(index)]
75
+ raise "Invalid index" unless attributes.index(index.to_s)
76
+ @data[attributes.index(index.to_s)]
73
77
  end
74
78
  end
75
79
 
@@ -86,8 +90,8 @@ module Ruport::Data
86
90
  raise "Invalid index" unless index < @data.length
87
91
  @data[index] = value
88
92
  else
89
- raise "Invalid index" unless @attributes.index(index)
90
- @data[attributes.index(index)] = value
93
+ raise "Invalid index" unless attributes.index(index.to_s)
94
+ @data[attributes.index(index.to_s)] = value
91
95
  end
92
96
  end
93
97
 
@@ -95,9 +99,9 @@ module Ruport::Data
95
99
  # If attributes and data are equivalent, then == evaluates to true.
96
100
  # Otherwise, == returns false
97
101
  def ==(other)
98
- return false if @attributes && !other.attributes
99
- return false if other.attributes && !@attributes
100
- @attributes == other.attributes && @data == other.data
102
+ return false if attributes && !other.attributes
103
+ return false if other.attributes && !attributes
104
+ attributes == other.attributes && @data == other.data
101
105
  end
102
106
 
103
107
  alias_method :eql?, :==
@@ -113,19 +117,20 @@ module Ruport::Data
113
117
  #
114
118
  # a = Data::Record.new([1,2],:attributes => %w[a b])
115
119
  # a.to_h #=> {"a" => 1, "b" => 2}
116
- def to_h; Hash[*@attributes.zip(data).flatten] end
120
+ def to_h; Hash[*attributes.zip(data).flatten] end
117
121
 
118
122
  # Returns a copy of the list of attribute names associated with this Record.
119
123
  #
120
124
  # a = Data::Record.new([1,2],:attributes => %w[a b])
121
125
  # a.attributes #=> ["a","b"]
122
- def attributes; @attributes && @attributes.dup; end
126
+ def attributes; @attributes.map { |a| a.to_s }; end
123
127
 
124
128
  # Sets the attribute list for this Record
125
129
  #
126
130
  # my_record.attributes = %w[foo bar baz]
127
131
  attr_writer :attributes
128
132
 
133
+
129
134
  # Allows you to change the order of or reduce the number of columns in a
130
135
  # Record. Example:
131
136
  #
@@ -140,9 +145,9 @@ module Ruport::Data
140
145
  # Same as Record#reorder but is destructive
141
146
  def reorder!(*indices)
142
147
  indices = reorder_data!(*indices)
143
- if @attributes
148
+ if attributes
144
149
  if indices.all? { |e| e.kind_of? Integer }
145
- @attributes = indices.map { |i| @attributes[i] }
150
+ @attributes = indices.map { |i| attributes[i] }
146
151
  else
147
152
  @attributes = indices
148
153
  end
@@ -181,7 +186,7 @@ module Ruport::Data
181
186
  # my_record.foo #=> 2
182
187
  def method_missing(id,*args)
183
188
  id = id.to_s.gsub(/=$/,"")
184
- if @attributes.include?(id)
189
+ if attributes.include?(id)
185
190
  args.empty? ? self[id] : self[id] = args.first
186
191
  else
187
192
  super