ruport 0.4.23 → 0.4.99

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