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
@@ -0,0 +1,147 @@
1
+ ***************
2
+ *** 8,27 ****
3
+ module Ruport::Data
4
+ class Set < Collection
5
+
6
+ def initialize(options={})
7
+ @data = ::Set.new
8
+ options[:data].each {|e| self << e} if options[:data]
9
+ end
10
+
11
+ - def <<(other)
12
+ case other
13
+ when Record
14
+ @data << other
15
+ when Array
16
+ @data << Record.new(other)
17
+ end
18
+ end
19
+
20
+ def dup
21
+ a = self.class.new(:data=>@data)
22
+ a.tags = tags.dup
23
+ --- 8,42 ----
24
+ module Ruport::Data
25
+ class Set < Collection
26
+
27
+ + # Creates a new set containing the elements of options[:data].
28
+ + #
29
+ + # Set.new :data => [%w[one two three] %w[1 2 3] %w[I II III]]
30
+ def initialize(options={})
31
+ @data = ::Set.new
32
+ options[:data].each {|e| self << e} if options[:data]
33
+ end
34
+
35
+ + # Adds the given object to the set and returns self.
36
+ + # set = Set.new :data => [%w[one two three]]
37
+ + # set << [5,6,7]
38
+ + def add(other)
39
+ case other
40
+ when Record
41
+ @data << other
42
+ when Array
43
+ @data << Record.new(other)
44
+ end
45
+ + self
46
+ end
47
+ + alias_method :<<, :add
48
+
49
+ + # Produces a shallow copy of the set: the same data is referenced by both
50
+ + # the old and new sets.
51
+ + # set = Set.new :data => [%w[one two three]]
52
+ + # set2 = set.dup
53
+ + # set == set2 => true
54
+ + # set << [8,9,10]
55
+ + # set == set2 => false
56
+ def dup
57
+ a = self.class.new(:data=>@data)
58
+ a.tags = tags.dup
59
+ ***************
60
+ *** 29,53 ****
61
+ end
62
+ alias_method :clone, :dup
63
+
64
+ def ==(other)
65
+ @data == other.data
66
+ end
67
+
68
+ def |(other)
69
+ Set.new :data => (@data | other.data)
70
+ end
71
+ alias_method :union, :|
72
+
73
+ def &(other)
74
+ Set.new :data => (@data & other.data)
75
+ end
76
+ alias_method :intersection, :&
77
+
78
+ - # Set difference
79
+ def -(other)
80
+ Set.new :data => (@data - other.data)
81
+ end
82
+ alias_method :difference, :-
83
+
84
+ def_delegators :@data, :each
85
+ end
86
+ --- 44,104 ----
87
+ end
88
+ alias_method :clone, :dup
89
+
90
+ + # Equality. Two sets are equal if they contain the same set of objects.
91
+ + # s1 = Set.new :data => [[1,2,3]]
92
+ + # s2 = Set.new :data => [[1,2,3]]
93
+ + # s1 == s2 => true
94
+ def ==(other)
95
+ @data == other.data
96
+ end
97
+
98
+ + # Union. Returns a new set containing the union of the objects contained in
99
+ + # the two sets.
100
+ + # s1 = Set.new :data => [[1,2,3]]
101
+ + # s2 = Set.new :data => [[4,5,6]]
102
+ + # s3 = s1 | s2
103
+ + # s4 = Set.new :data => [[1,2,3], [4,5,6]]
104
+ + # s3 == s4 => true
105
+ def |(other)
106
+ Set.new :data => (@data | other.data)
107
+ end
108
+ alias_method :union, :|
109
+ + alias_method :+, :|
110
+
111
+ + # Intersection. Returns a new set containing the objects common to the two
112
+ + # sets.
113
+ + # s1 = Set.new :data => [%w[a b c],[1,2,3]]
114
+ + # s2 = Set.new :data => [%w[a b c],[4,5,6]]
115
+ + # s3 = s1 | s2
116
+ + # s4 = Set.new :data => [%w[a b c]]
117
+ + # s3 == s4 => true
118
+ def &(other)
119
+ Set.new :data => (@data & other.data)
120
+ end
121
+ alias_method :intersection, :&
122
+
123
+ + # Difference. Returns a new set containing those objects present in this
124
+ + # set but not the other.
125
+ + # s1 = Set.new :data => [%w[a b c],[1,2,3]]
126
+ + # s2 = Set.new :data => [%w[a b c],[4,5,6]]
127
+ + # s3 = s1 | s2
128
+ + # s4 = Set.new :data => [[1, 2, 3]]
129
+ + # s3 == s4 => true
130
+ def -(other)
131
+ Set.new :data => (@data - other.data)
132
+ end
133
+ alias_method :difference, :-
134
+ +
135
+ + # Exclusion. Returns a new set containing those objects in this set or the
136
+ + # other set but not in both.
137
+ + # s1 = Set.new :data => [%w[a b c],[1,2,3]]
138
+ + # s2 = Set.new :data => [%w[a b c],[4,5,6]]
139
+ + # s3 = s1 | s2
140
+ + # s4 = Set.new :data => [[1, 2, 3],[4,5,6]]
141
+ + # s3 == s4 => true
142
+ + def ^(other)
143
+ + Set.new :data => (@data ^ other.data)
144
+ + end
145
+
146
+ def_delegators :@data, :each
147
+ end
@@ -0,0 +1,73 @@
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
+ require 'set'
7
+
8
+ module Ruport::Data
9
+ class Set < Collection
10
+
11
+ # Creates a new set containing the elements of options[:data].
12
+ def initialize(options={})
13
+ @data = ::Set.new
14
+ options[:data].each {|e| self << e} if options[:data]
15
+ end
16
+
17
+ # Adds the given object to the set and returns self.
18
+ def add(other)
19
+ case other
20
+ when Record
21
+ @data << other
22
+ when Array
23
+ @data << Record.new(other)
24
+ end
25
+ self
26
+ end
27
+ alias_method :<<, :add
28
+
29
+ # Produces a shallow copy of the set: the same data is referenced by both
30
+ # the old and new sets.
31
+ def dup
32
+ a = self.class.new(:data=>@data)
33
+ a.tags = tags.dup
34
+ return a
35
+ end
36
+ alias_method :clone, :dup
37
+
38
+ # Equality. Two sets are equal if they contain the same set of objects.
39
+ def ==(other)
40
+ @data == other.data
41
+ end
42
+
43
+ # Union. Returns a new set containing the union of the objects contained in
44
+ # the two sets.
45
+ def |(other)
46
+ Set.new :data => (@data | other.data)
47
+ end
48
+ alias_method :union, :|
49
+ alias_method :+, :|
50
+
51
+ # Intersection. Returns a new set containing the objects common to the two
52
+ # sets.
53
+ def &(other)
54
+ Set.new :data => (@data & other.data)
55
+ end
56
+ alias_method :intersection, :&
57
+
58
+ # Difference. Returns a new set containing those objects present in this
59
+ # set but not the other.
60
+ def -(other)
61
+ Set.new :data => (@data - other.data)
62
+ end
63
+ alias_method :difference, :-
64
+
65
+ # Exclusion. Returns a new set containing those objects in this set or the
66
+ # other set but not in both.
67
+ def ^(other)
68
+ Set.new :data => (@data ^ other.data)
69
+ end
70
+
71
+ def_delegators :@data, :each
72
+ end
73
+ end
@@ -1,4 +1,14 @@
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
  class Array
8
+ # Converts an array to a Ruport::Data::Table object, ready to
9
+ # use in your reports.
10
+ #
11
+ # [[1,2],[3,4]].to_table(%w[a b])
2
12
  def to_table(options={})
3
13
  options = { :column_names => options } if options.kind_of? Array
4
14
  Ruport::Data::Table.new({:data => self}.merge(options))
@@ -6,7 +16,29 @@ class Array
6
16
  end
7
17
 
8
18
  module Ruport::Data
19
+
20
+ # This class is one of the core classes for building and working with data
21
+ # in Ruport. The idea is to get your data into a standard form, regardless
22
+ # of its source (a database, manual arrays, ActiveRecord, CSVs, etc.).
23
+ #
24
+ # Table is intended to be used as the data store for structured, tabular
25
+ # data - Ruport::Data::Set is an alternate intermediary data store intended
26
+ # for less structured data.
27
+ #
28
+ # Once your data is in a Ruport::Data::Table object, it can be manipulated
29
+ # to suit your needs, then used to build a report.
30
+ #
31
+ # Included in this class are methods to create Tables manually and from CSV
32
+ # files.
33
+ #
34
+ # For building a table using ActiveRecord, have a look at Ruport::Reportable.
9
35
  class Table < Collection
36
+
37
+ # Creates a new table based on the supplied options.
38
+ # Valid options are :data and :column_names.
39
+ #
40
+ # table = Table.new({:data => [1,2,3], [3,4,5],
41
+ # :column_names => %w[a b c]})
10
42
  def initialize(options={})
11
43
  @column_names = options[:column_names].dup if options[:column_names]
12
44
  @data = []
@@ -15,20 +47,42 @@ module Ruport::Data
15
47
 
16
48
  attr_reader :column_names
17
49
 
50
+ # Sets the column names for this table. The single parameter should be
51
+ # an array listing the names of the columns.
52
+ #
53
+ # tbl = Table.new({:data => [1,2,3], [3,4,5], :column_names => %w[a b c]})
54
+ # tbl.column_names = %w[e f g]
18
55
  def column_names=(other)
19
56
  @column_names = other.dup
20
57
  map { |r| r.attributes = @column_names }
21
58
  end
22
59
 
60
+ # Compares this table to another table and returns true if
61
+ # both the data and column names are equal
62
+ #
63
+ # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
64
+ # two = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
65
+ # one.eql?(two) #=> true
23
66
  def eql?(other)
24
67
  data.eql?(other.data) && column_names.eql?(other.column_names)
25
68
  end
26
69
  alias_method :==, :eql?
27
70
 
71
+ # Uses Ruport's built-in text plugin to render this table into a string
72
+ #
73
+ # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
74
+ # puts data.to_s
28
75
  def to_s
29
76
  as(:text)
30
77
  end
31
78
 
79
+ # Used to add extra data to the table. The single parameter can be an
80
+ # Array, Hash or Ruport::Data::Record.
81
+ #
82
+ # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
83
+ # data << [8,9]
84
+ # data << { :a => 4, :b => 5}
85
+ # data << Ruport::Data::Record.new [5,6], :attributes => %w[a b]
32
86
  def <<(other)
33
87
  case other
34
88
  when Array
@@ -39,12 +93,19 @@ module Ruport::Data
39
93
  @data << Record.new(arr, :attributes => @column_names)
40
94
  when Record
41
95
  raise ArgumentError unless column_names.eql? other.attributes
42
- @data << Record.new(other.to_a, :attributes => @column_names)
96
+ @data << Record.new(other.data, :attributes => @column_names)
97
+ @data.last.tags = other.tags.dup
43
98
  end
44
99
  self
45
100
  end
46
-
101
+
102
+ # Reorders the columns that exist in the table. Operates directly
103
+ # on this table.
104
+ #
105
+ # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
106
+ # data.reorder!([1,0])
47
107
  def reorder!(*indices)
108
+ indices = indices[0] if indices[0].kind_of? Array
48
109
  @column_names = if indices.all? { |i| i.kind_of? Integer }
49
110
  indices.map { |i| @column_names[i] }
50
111
  else
@@ -53,10 +114,21 @@ module Ruport::Data
53
114
  @data.each { |r| r.reorder! *indices }; self
54
115
  end
55
116
 
117
+ # Returns a copy of the table with its columns in the requested order.
118
+ #
119
+ # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
120
+ # two = one.reorder!([1,0])
56
121
  def reorder(*indices)
57
122
  dup.reorder! *indices
58
123
  end
59
124
 
125
+ # Adds an extra column to the table. Accepts an options Hash as its
126
+ # only parameter which should contain 2 keys - :name and :fill.
127
+ # :name specifies the new columns name, and :fill the default value to
128
+ # use for the column in existing rows.
129
+ #
130
+ # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
131
+ # data.append_coulmn({:name => 'new_column', :fill => 1)
60
132
  def append_column(options={})
61
133
  self.column_names += [options[:name]] if options[:name]
62
134
  if block_given?
@@ -66,10 +138,32 @@ module Ruport::Data
66
138
  end
67
139
  end
68
140
 
141
+ # Removes a column from the table. Any values in the specified column are
142
+ # lost.
143
+ # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
144
+ # data.append_column({:name => 'new_column', :fill => 1)
145
+ # data.remove_column({:name => 'new_column')
146
+ # data == Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
147
+ # => true
148
+ def remove_column(options={})
149
+ raise ArgumentError unless column_names.include? options[:name]
150
+ reorder! column_names - [options[:name]]
151
+ end
152
+
153
+ # Create a shallow copy of the table: the same data elements are referenced
154
+ # by both the old and new table.
155
+ #
156
+ # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
157
+ # two = one.dup
69
158
  def dup
70
159
  a = self.class.new(:data => @data, :column_names => @column_names)
160
+ a.tags = tags.dup
161
+ return a
71
162
  end
72
163
 
164
+ # Loads a CSV file directly into a table using the fasterCSV library.
165
+ #
166
+ # data = Table.load('mydata.csv')
73
167
  def self.load(csv_file, options = {})
74
168
  options = {:has_names => true}.merge(options)
75
169
  require "fastercsv"
@@ -88,6 +182,37 @@ module Ruport::Data
88
182
  end ; loaded_data
89
183
  end
90
184
 
185
+
186
+
187
+ # Allows you to split tables into multiple tables for grouping.
188
+ #
189
+ # Example:
190
+ #
191
+ # a = Table.new(:column_name => %w[name a b c])
192
+ # a << ["greg",1,2,3]
193
+ # a << ["joe", 2,3,4]
194
+ # a << ["greg",7,8,9]
195
+ # a << ["joe", 1,2,3]
196
+ #
197
+ # b = a.split :group => "name"
198
+ #
199
+ # b.greg.eql? [[1,2,3],[7,8,9]].to_table(%w[a b c]) #=> true
200
+ # b["joe"].eql? [[2,3,4],[1,2,3]].to_table(%w[a b c]) #=> true
201
+ #
202
+ # You can also pass an array to :group, and the resulting attributes in
203
+ # the group will be joined by an underscore.
204
+ #
205
+ # Example:
206
+ #
207
+ # a = Table.new(:column_names => %w[first_name last_name x]
208
+ # a << %w[greg brown foo]
209
+ # a << %w[greg gibson bar]
210
+ # a << %w[greg brown baz]
211
+ #
212
+ # b = a.split :group => %w[first_name last_name]
213
+ # a.greg_brown.length #=> 2
214
+ # a["greg_gibson"].length #=> 1
215
+ # a.greg_brown[0].x #=> "foo"
91
216
  def split(options={})
92
217
  if options[:group].kind_of? Array
93
218
  group = map { |r| options[:group].map { |e| r[e] } }.uniq
@@ -1,23 +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
2
7
 
8
+ # This module provides a simple mechanism for tagging arbitrary objects. This
9
+ # provides the necessary methods to set and retrieve tags which can consist of
10
+ # any Ruby object. This is used by Data::Record and the Ruport Data
11
+ # Collections.
3
12
  module Taggable
4
13
 
14
+ # Adds a tag to the object
15
+ # taggable_obj.tag :spiffy
5
16
  def tag(tag_name)
6
17
  tags << tag_name unless has_tag? tag_name
7
18
  end
8
-
19
+
20
+ # Removes a tag
21
+ # taggable_obj.delete_tag :not_so_spiffy
9
22
  def delete_tag(tag_name)
10
23
  tags.delete tag_name
11
24
  end
12
25
 
26
+ # Checks to see if a tag is present
27
+ # taggable_obj.has_tag? :spiffy #=> true
13
28
  def has_tag?(tag_name)
14
29
  tags.include? tag_name
15
30
  end
16
31
 
32
+ # Returns an array of tags.
33
+ # taggable_obj.tags #=> [:spiffy, :kind_of_spiffy]
17
34
  def tags
18
35
  @ruport_tags ||= []
19
36
  end
20
-
37
+
38
+ # Sets the tags to some array
39
+ # taggable_obj.tags = [:really_dang_spiffy, :the_most_spiffy]
21
40
  def tags=(tags_list)
22
41
  @ruport_tags = tags_list
23
42
  end