ruport 0.5.4 → 0.6.0

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