ruport 0.4.23 → 0.4.99

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 (71) hide show
  1. data/AUTHORS +16 -8
  2. data/CHANGELOG +30 -1
  3. data/README +144 -114
  4. data/Rakefile +12 -4
  5. data/TODO +4 -7
  6. data/bin/rope +21 -28
  7. data/examples/line_graph.rb +36 -0
  8. data/examples/sample_invoice_report.rb +1 -1
  9. data/examples/simple_graph.rb +8 -0
  10. data/lib/SVG/Graph/Bar.rb +137 -0
  11. data/lib/SVG/Graph/BarBase.rb +140 -0
  12. data/lib/SVG/Graph/BarHorizontal.rb +136 -0
  13. data/lib/SVG/Graph/Graph.rb +977 -0
  14. data/lib/SVG/Graph/Line.rb +444 -0
  15. data/lib/SVG/Graph/Pie.rb +394 -0
  16. data/lib/SVG/Graph/Plot.rb +494 -0
  17. data/lib/SVG/Graph/Schedule.rb +373 -0
  18. data/lib/SVG/Graph/TimeSeries.rb +241 -0
  19. data/lib/ruport.rb +2 -2
  20. data/lib/ruport/config.rb +47 -3
  21. data/lib/ruport/data/collection.rb +17 -1
  22. data/lib/ruport/data/record.rb +101 -8
  23. data/lib/ruport/data/set.rb +81 -2
  24. data/lib/ruport/data/set.rb.rej +147 -0
  25. data/lib/ruport/data/set.rb~ +73 -0
  26. data/lib/ruport/data/table.rb +127 -2
  27. data/lib/ruport/data/taggable.rb +21 -2
  28. data/lib/ruport/format.rb +36 -44
  29. data/lib/ruport/format/engine.rb +21 -1
  30. data/lib/ruport/format/plugin.rb +64 -1
  31. data/lib/ruport/mailer.rb +70 -36
  32. data/lib/ruport/meta_tools.rb +15 -6
  33. data/lib/ruport/query.rb +1 -1
  34. data/lib/ruport/rails/reportable.rb +23 -1
  35. data/lib/ruport/report.rb +11 -11
  36. data/lib/ruport/report/invoice.rb +16 -0
  37. data/lib/ruport/system_extensions.rb +3 -55
  38. data/test/{tc_database.rb → _test_database.rb} +0 -0
  39. data/test/{tc_config.rb → test_config.rb} +0 -0
  40. data/test/{tc_format.rb → test_format.rb} +1 -0
  41. data/test/{tc_format_engine.rb → test_format_engine.rb} +14 -2
  42. data/test/test_graph.rb +101 -0
  43. data/test/{tc_invoice.rb → test_invoice.rb} +7 -1
  44. data/test/test_mailer.rb +108 -0
  45. data/test/test_meta_tools.rb +14 -0
  46. data/test/{tc_plugin.rb → test_plugin.rb} +12 -1
  47. data/test/{tc_query.rb → test_query.rb} +0 -0
  48. data/test/{tc_record.rb → test_record.rb} +9 -0
  49. data/test/{tc_report.rb → test_report.rb} +2 -1
  50. data/test/{tc_ruport.rb → test_ruport.rb} +0 -0
  51. data/test/test_set.rb +118 -0
  52. data/test/test_set.rb.rej +16 -0
  53. data/test/{tc_set.rb → test_set.rb~} +17 -0
  54. data/test/{tc_sql_split.rb → test_sql_split.rb} +0 -0
  55. data/test/{tc_table.rb → test_table.rb} +15 -0
  56. data/test/{tc_taggable.rb → test_taggable.rb} +0 -0
  57. data/test/unit.log +361 -0
  58. metadata +52 -30
  59. data/examples/bar.pdf +0 -193
  60. data/examples/f.log +0 -5
  61. data/examples/foo.pdf +0 -193
  62. data/lib/ruport/format/document.rb +0 -78
  63. data/lib/ruport/format/open_node.rb +0 -38
  64. data/test/tc_data_row.rb +0 -132
  65. data/test/tc_data_set.rb +0 -386
  66. data/test/tc_document.rb +0 -42
  67. data/test/tc_element.rb +0 -18
  68. data/test/tc_page.rb +0 -42
  69. data/test/tc_section.rb +0 -45
  70. data/test/ts_all.rb +0 -12
  71. data/test/ts_format.rb +0 -7
@@ -14,7 +14,7 @@ module Ruport
14
14
 
15
15
  #begin; require 'rubygems'; rescue LoadError; nil end
16
16
 
17
- VERSION = "0.4.23"
17
+ VERSION = "0.4.99"
18
18
 
19
19
  # Ruports logging and error interface.
20
20
  # Can generate warnings or raise fatal errors
@@ -67,6 +67,6 @@ module Ruport
67
67
  end
68
68
 
69
69
 
70
- %w[config meta_tools report format query data].each { |lib|
70
+ %w[config meta_tools report format query data mailer].each { |lib|
71
71
  require "ruport/#{lib}"
72
72
  }
@@ -58,46 +58,89 @@ module Ruport
58
58
  #
59
59
  module Config
60
60
  module_function
61
-
62
- def source(*args)
61
+
62
+
63
+ # create or retrieve a database source configuration.
64
+ #
65
+ # setting a source
66
+ #
67
+ # source :default, :user => "root", :password => "clyde",
68
+ # :dsn => "dbi:mysql:blinkybase"
69
+ #
70
+ # retrieving a source
71
+ #
72
+ # db = source(:default) #=> <OpenStruct ..>
73
+ # db.dsn #=> "dbi:mysql:blinkybase"
74
+ def source(*args)
63
75
  return sources[args.first] if args.length == 1
64
76
  sources[args.first] = OpenStruct.new(*args[1..-1])
65
77
  check_source(sources[args.first],args.first)
66
78
  end
67
79
 
80
+ # create or retrieve a mailer configuration
81
+ #
82
+ # creating a mailer config
83
+ #
84
+ # mailer :alternate, :host => "mail.test.com",
85
+ # :address => "test@test.com",
86
+ # :user => "test", :password => "blinky"
87
+ # :auth_type => :cram_md5
88
+ #
89
+ # retreiving a mailer config
90
+ #
91
+ # mail_conf = mailer(:alternate) #=> <OpenStruct ..>
92
+ # mail_conf.address #=> test@test.com
68
93
  def mailer(*args)
94
+ return mailers[args.first] if args.length == 1
69
95
  mailers[args.first] = OpenStruct.new(*args[1..-1])
70
96
  check_mailer(mailers[args.first],args.first)
71
97
  end
72
98
 
99
+
100
+ # Sets the logger to use the specified file.
101
+ #
102
+ # log_file "foo.log"
73
103
  def log_file(file)
74
104
  @logger = Logger.new(file)
75
105
  end
76
106
 
77
- #alias_method :log_file=, :log_file
107
+ # Same as Config.log_file, but accessor style
78
108
  def log_file=(file)
79
109
  log_file(file)
80
110
  end
81
111
 
112
+ # Returns the source which is labeled :default
82
113
  def default_source
83
114
  sources[:default]
84
115
  end
85
116
 
117
+ # Returns the mailer which is labeled :default
86
118
  def default_mailer
87
119
  mailers[:default]
88
120
  end
89
121
 
122
+ # Returns an array of database source configs
90
123
  def sources; @sources ||= {}; end
91
124
 
125
+ # Returns an array of mailer configs
92
126
  def mailers; @mailers ||= {}; end
93
127
 
128
+ # Returns the currently active logger
94
129
  def logger; @logger; end
95
130
 
131
+ # Forces all messages marked :log_only to surface
96
132
  def enable_paranoia; @paranoid = true; end
133
+
134
+ # Disables the printing of :log_only messages to STDERR
97
135
  def disable_paranoia; @paranoid = false; end
136
+
137
+ # Sets paranoid status
98
138
  def paranoid=(val); @paranoid = val; end
139
+
140
+ # Checks to see if paranoia is enabled
99
141
  def paranoid?; !!@paranoid; end
100
142
 
143
+ # Verifies that you have provided a DSN for your source
101
144
  def check_source(settings,label)
102
145
  unless settings.dsn
103
146
  Ruport.complain(
@@ -108,6 +151,7 @@ module Ruport
108
151
  end
109
152
  end
110
153
 
154
+ # Verifies that you have provided a host for your mailer
111
155
  def check_mailer(settings, label)
112
156
  unless settings.host
113
157
  Ruport.complain(
@@ -1,4 +1,12 @@
1
+ # The Ruport Data Collections.
2
+ # Authors: Gregory Brown / Dudley Flanders
3
+ #
4
+ # This is Free Software. For details, see LICENSE and COPYING
5
+ # Copyright 2006 by respective content owners, all rights reserved.
6
+
1
7
  module Ruport::Data
8
+
9
+ # This is the base class for Ruport's Data structures.
2
10
  class Collection
3
11
  require "forwardable"
4
12
  extend Forwardable
@@ -9,18 +17,26 @@ module Ruport::Data
9
17
  @data = data.dup if data
10
18
  end
11
19
 
20
+ # Simple formatting tool which allows you to quickly generate a formatted
21
+ # table from a Collection object, eg
22
+ #
23
+ # my_collection.as(:csv) #=> "1,2,3\n4,5,6"
12
24
  def as(type)
13
25
  Ruport::Format.table :data => self, :plugin => type
14
26
  end
15
27
 
28
+ # Converts any Collection object to a Data::Set
16
29
  def to_set
17
30
  Set.new :data => data
18
31
  end
19
-
32
+
33
+ # Converts any Collection object to a Data::Table
20
34
  def to_table(options={})
21
35
  Table.new({:data => data.map { |r| r.to_a }}.merge(options))
22
36
  end
23
37
 
38
+ # Provides a shortcut for the as() method by converting as(:format_name)
39
+ # into to_format_name
24
40
  def method_missing(id,*args)
25
41
  return as($1.to_sym) if id.to_s =~ /^to_(.*)/
26
42
  super
@@ -1,10 +1,42 @@
1
+ # The Ruport Data Collections.
2
+ # Authors: Gregory Brown / Dudley Flanders
3
+ #
4
+ # This is Free Software. For details, see LICENSE and COPYING
5
+ # Copyright 2006 by respective content owners, all rights reserved.
1
6
  module Ruport::Data
7
+
8
+ # Data::Records are the work horse of Ruport's Data model. These can behave
9
+ # as array like, hash like, or struct like objects. They are used as the base
10
+ # record for both Tables and Sets in Ruport.
2
11
  class Record
3
12
  require "forwardable"
4
13
  extend Forwardable
5
14
  include Enumerable
6
15
  include Taggable
7
16
 
17
+ # Creates a new Record object. If the <tt>:attributes</tt> keyword is
18
+ # specified, Hash-like and Struct-like access will be enabled. Otherwise,
19
+ # Record elements may be accessed ordinally, like an Array.
20
+ #
21
+ # Records accept either Hashes or Arrays as their data.
22
+ #
23
+ # a = Record.new [1,2,3]
24
+ # a[1] #=> 2
25
+ #
26
+ # b = Record.new [1,2,3], :attributes => %w[a b c]
27
+ # b[1] #=> 2
28
+ # b['a'] #=> 1
29
+ # b.c #=> 3
30
+ #
31
+ # c = Record.new {"a" => 1, "c" => 3, "b" => 2}, :attributes => %w[a b c]
32
+ # b[1] #=> 2
33
+ # b['a'] #=> 1
34
+ # b.c #=> 3
35
+ #
36
+ # c = Record.new { "a" => 1, "c" => 3, "b" => 2 }
37
+ # b[1] #=> ? (without attributes, you cannot rely on order)
38
+ # b['a'] #=> 1
39
+ # b.c #=> 3
8
40
  def initialize(data,options={})
9
41
  if data.kind_of?(Hash)
10
42
  if options[:attributes]
@@ -18,26 +50,50 @@ module Ruport::Data
18
50
  @attributes = options[:attributes]
19
51
  end
20
52
  end
21
-
53
+
54
+ # The underlying data which is being stored in the record
22
55
  attr_reader :data
56
+
23
57
  def_delegators :@data,:each, :length
24
58
 
59
+ # Allows either array or hash_like indexing
60
+ #
61
+ # my_record[1]
62
+ # my_record["foo"]
63
+ #
64
+ # Also, this provides a feature via method_missing which allows
65
+ # my_record.foo
25
66
  def [](index)
26
67
  if index.kind_of? Integer
68
+ raise "Invalid index" unless index < @data.length
27
69
  @data[index]
28
70
  else
71
+ raise "Invalid index" unless @attributes.index(index)
29
72
  @data[@attributes.index(index)]
30
73
  end
31
74
  end
32
75
 
76
+
77
+ # Allows setting a value at an index
78
+ #
79
+ # my_record[1] = "foo"
80
+ # my_record["bar"] = "baz"
81
+ #
82
+ # And via method_missing
83
+ # my_record.ghost = "blinky"
33
84
  def []=(index, value)
34
85
  if index.kind_of? Integer
86
+ raise "Invalid index" unless index < @data.length
35
87
  @data[index] = value
36
88
  else
89
+ raise "Invalid index" unless @attributes.index(index)
37
90
  @data[attributes.index(index)] = value
38
91
  end
39
92
  end
40
93
 
94
+
95
+ # If attributes and data are equivalent, then == evaluates to true.
96
+ # Otherwise, == returns false
41
97
  def ==(other)
42
98
  return false if @attributes && !other.attributes
43
99
  return false if other.attributes && !@attributes
@@ -45,20 +101,49 @@ module Ruport::Data
45
101
  end
46
102
 
47
103
  alias_method :eql?, :==
48
-
104
+
105
+ # Makes an array out of the data wrapped by Record
106
+ #
107
+ # a = Data::Record.new([1,2],:attributes => %w[a b])
108
+ # a.to_a #=> [1,2]
49
109
  def to_a; @data.dup; end
50
-
110
+
111
+ # Makes a hash out of the data wrapped by Record
112
+ # Only works if attributes are specified
113
+ #
114
+ # a = Data::Record.new([1,2],:attributes => %w[a b])
115
+ # a.to_h #=> {"a" => 1, "b" => 2}
51
116
  def to_h; Hash[*@attributes.zip(data).flatten] end
52
-
117
+
118
+ # Returns a copy of the list of attribute names associated with this Record.
119
+ #
120
+ # a = Data::Record.new([1,2],:attributes => %w[a b])
121
+ # a.attributes #=> ["a","b"]
53
122
  def attributes; @attributes && @attributes.dup; end
54
123
 
124
+ # Sets the attribute list for this Record
125
+ #
126
+ # my_record.attributes = %w[foo bar baz]
55
127
  def attributes=(a); @attributes=a; end
56
128
 
129
+ # Allows you to change the order of or reduce the number of columns in a
130
+ # Record. Example:
131
+ #
132
+ # a = Data::Record.new([1,2,3,4],:attributes => %w[a b c d])
133
+ # b = a.reorder("a","d","b")
134
+ # b.attributes #=> ["a","d","b"]
135
+ # b.data #=> [1,4,2]
57
136
  def reorder(*indices)
58
137
  dup.reorder! *indices
59
138
  end
60
-
139
+
140
+ # Same as Record#reorder but is destructive
61
141
  def reorder!(*indices)
142
+ indices = indices[0] if indices[0].kind_of?(Array)
143
+ indices.each do |i|
144
+ self[i] rescue raise ArgumentError,
145
+ "you may have specified an invalid column"
146
+ end
62
147
  @data = indices.map { |i| self[i] }
63
148
  if @attributes
64
149
  if indices.all? { |e| e.kind_of? Integer }
@@ -68,18 +153,26 @@ module Ruport::Data
68
153
  end
69
154
  end; self
70
155
  end
71
-
156
+
157
+ # Makes a fresh copy of the Record
72
158
  def dup
73
- self.class.new(@data,:attributes => attributes)
159
+ copy = self.class.new(@data,:attributes => attributes)
160
+ copy.tags = self.tags.dup
161
+ return copy
74
162
  end
75
163
 
76
164
  #FIXME: This does not take into account frozen / tainted state
77
165
  alias_method :clone, :dup
78
-
166
+
167
+ # provides a unique hash value
79
168
  def hash
80
169
  (attributes.to_a + data.to_a).hash
81
170
  end
82
171
 
172
+ # provides accessor style methods for attribute access, Example
173
+ #
174
+ # my_record.foo = 2
175
+ # my_record.foo #=> 2
83
176
  def method_missing(id,*args)
84
177
  id = id.to_s.gsub(/=$/,"")
85
178
  if @attributes.include?(id)
@@ -1,41 +1,120 @@
1
+ # The Ruport Data Collections.
2
+ # Authors: Gregory Brown / Dudley Flanders
3
+ #
4
+ # This is Free Software. For details, see LICENSE and COPYING
5
+ # Copyright 2006 by respective content owners, all rights reserved.
1
6
  require 'set'
2
7
 
3
8
  module Ruport::Data
9
+
10
+ # This class is one of the core classes for building and working with data
11
+ # in Ruport. The idea is to get your data into a standard form, regardless
12
+ # of its source (a database, manual arrays, ActiveRecord, CSVs, etc.).
13
+ #
14
+ # Set is intended to be used as the data store for unstructured data -
15
+ # Ruport::Data::Table is an alternate intermediary data store intended
16
+ # for structured, tabular data.
17
+ #
18
+ # Once your data is in a Ruport::Data::Set object, it can be manipulated
19
+ # to suit your needs, then used to build a report.
4
20
  class Set < Collection
5
21
 
22
+ # Creates a new set containing the elements of options[:data].
23
+ #
24
+ # Set.new :data => [%w[one two three] %w[1 2 3] %w[I II III]]
6
25
  def initialize(options={})
7
26
  @data = ::Set.new
8
27
  options[:data].each {|e| self << e} if options[:data]
9
28
  end
10
29
 
11
- def <<(other)
30
+ # Adds the given object to the set and returns self.
31
+ # set = Set.new :data => [%w[one two three]]
32
+ # set << [5,6,7]
33
+ def add(other)
12
34
  case other
13
35
  when Record
14
36
  @data << other
15
37
  when Array
16
38
  @data << Record.new(other)
17
39
  end
40
+ self
18
41
  end
42
+ alias_method :<<, :add
19
43
 
44
+ # Produces a shallow copy of the set: the same data elements are
45
+ # referenced by both the old and new sets.
46
+ #
47
+ # set = Set.new :data => [%w[one two three]]
48
+ # set2 = set.dup
49
+ # set == set2 #=> true
50
+ # set << [8,9,10]
51
+ # set == set2 #=> false
52
+ def dup
53
+ a = self.class.new(:data=>@data)
54
+ a.tags = tags.dup
55
+ return a
56
+ end
57
+ alias_method :clone, :dup
58
+
59
+ # Equality. Two sets are equal if they contain the same set of objects.
60
+ # s1 = Set.new :data => [[1,2,3]]
61
+ # s2 = Set.new :data => [[1,2,3]]
62
+ # s1 == s2 #=> true
20
63
  def ==(other)
21
64
  @data == other.data
22
65
  end
23
66
 
67
+ # Union. Returns a new set containing the union of the objects contained in
68
+ # the two sets.
69
+ #
70
+ # s1 = Set.new :data => [[1,2,3]]
71
+ # s2 = Set.new :data => [[4,5,6]]
72
+ # s3 = s1 | s2
73
+ # s4 = Set.new :data => [[1,2,3], [4,5,6]]
74
+ # s3 == s4 #=> true
24
75
  def |(other)
25
76
  Set.new :data => (@data | other.data)
26
77
  end
27
78
  alias_method :union, :|
79
+ alias_method :+, :|
28
80
 
81
+ # Intersection. Returns a new set containing the objects common to the two
82
+ # sets.
83
+ #
84
+ # s1 = Set.new :data => [%w[a b c],[1,2,3]]
85
+ # s2 = Set.new :data => [%w[a b c],[4,5,6]]
86
+ # s3 = s1 & s2
87
+ # s4 = Set.new :data => [%w[a b c]]
88
+ # s3 == s4 #=> true
29
89
  def &(other)
30
90
  Set.new :data => (@data & other.data)
31
91
  end
32
92
  alias_method :intersection, :&
33
93
 
34
- # Set difference
94
+ # Difference. Returns a new set containing those objects present in this
95
+ # set but not the other.
96
+ #
97
+ # s1 = Set.new :data => [%w[a b c],[1,2,3]]
98
+ # s2 = Set.new :data => [%w[a b c],[4,5,6]]
99
+ # s3 = s1 - s2
100
+ # s4 = Set.new :data => [[1, 2, 3]]
101
+ # s3 == s4 #=> true
35
102
  def -(other)
36
103
  Set.new :data => (@data - other.data)
37
104
  end
38
105
  alias_method :difference, :-
106
+
107
+ # Exclusion. Returns a new set containing those objects in this set or the
108
+ # other set but not in both.
109
+ #
110
+ # s1 = Set.new :data => [%w[a b c],[1,2,3]]
111
+ # s2 = Set.new :data => [%w[a b c],[4,5,6]]
112
+ # s3 = s1 ^ s2
113
+ # s4 = Set.new :data => [[1, 2, 3],[4,5,6]]
114
+ # s3 == s4 #=> true
115
+ def ^(other)
116
+ Set.new :data => (@data ^ other.data)
117
+ end
39
118
 
40
119
  def_delegators :@data, :each
41
120
  end