ruport 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -0
- data/CHANGELOG +10 -1
- data/Rakefile +1 -1
- data/examples/simple_table_interface.rb +20 -0
- data/lib/ruport.rb +87 -69
- data/lib/ruport/attempt.rb +1 -1
- data/lib/ruport/config.rb +198 -166
- data/lib/ruport/data.rb +6 -1
- data/lib/ruport/data/collection.rb +15 -8
- data/lib/ruport/data/groupable.rb +68 -6
- data/lib/ruport/data/record.rb +73 -34
- data/lib/ruport/data/record.rb~ +236 -0
- data/lib/ruport/data/set.rb +48 -13
- data/lib/ruport/data/table.rb +164 -74
- data/lib/ruport/data/table.rb.rej +67 -0
- data/lib/ruport/data/table.rb~ +153 -68
- data/lib/ruport/data/taggable.rb +37 -9
- data/lib/ruport/format.rb +1 -1
- data/lib/ruport/mailer.rb +41 -27
- data/lib/ruport/meta_tools.rb +26 -11
- data/lib/ruport/query.rb +102 -68
- data/lib/ruport/query/sql_split.rb +1 -1
- data/lib/ruport/report.rb +84 -58
- data/lib/ruport/report/graph.rb +1 -1
- data/lib/ruport/report/invoice.rb +1 -1
- data/test/test_query.rb +305 -48
- data/test/test_query.rb.rej +161 -0
- data/test/test_query.rb~ +337 -0
- data/test/test_record.rb +6 -0
- data/test/test_table.rb +18 -0
- data/test/test_table.rb~ +336 -0
- data/test/unit.log +180 -6
- metadata +8 -2
data/lib/ruport/data.rb
CHANGED
@@ -1 +1,6 @@
|
|
1
|
-
|
1
|
+
require "ruport/data/groupable"
|
2
|
+
require "ruport/data/taggable"
|
3
|
+
require "ruport/data/record"
|
4
|
+
require "ruport/data/collection"
|
5
|
+
require "ruport/data/table"
|
6
|
+
require "ruport/data/set"
|
@@ -6,7 +6,13 @@
|
|
6
6
|
|
7
7
|
module Ruport::Data
|
8
8
|
|
9
|
-
#
|
9
|
+
#
|
10
|
+
# === Overview
|
11
|
+
#
|
12
|
+
# This is the base class for Ruport's Data structures. It mixes in the
|
13
|
+
# <tt>Taggable</tt> module and provides methods for converting between
|
14
|
+
# <tt>Data::Set</tt>s and <tt>Data::Table</tt>s.
|
15
|
+
#
|
10
16
|
class Collection
|
11
17
|
require "forwardable"
|
12
18
|
extend Forwardable
|
@@ -17,28 +23,29 @@ module Ruport::Data
|
|
17
23
|
@data = data.dup if data
|
18
24
|
end
|
19
25
|
|
20
|
-
#
|
21
|
-
# table from a Collection object
|
26
|
+
# A simple formatting tool which allows you to quickly generate a formatted
|
27
|
+
# table from a <tt>Collection</tt> object.
|
22
28
|
#
|
23
|
-
#
|
29
|
+
# Example:
|
30
|
+
# my_collection.as(:csv) #=> "1,2,3\n4,5,6"
|
24
31
|
def as(type)
|
25
32
|
eng = Ruport::Format.table_object :data => self, :plugin => type
|
26
33
|
yield(eng) if block_given?
|
27
34
|
eng.render
|
28
35
|
end
|
29
36
|
|
30
|
-
# Converts
|
37
|
+
# Converts a <tt>Collection</tt> object to a <tt>Data::Set</tt>.
|
31
38
|
def to_set
|
32
39
|
Set.new :data => data
|
33
40
|
end
|
34
41
|
|
35
|
-
# Converts
|
42
|
+
# Converts a <tt>Collection</tt> object to a <tt>Data::Table</tt>.
|
36
43
|
def to_table(options={})
|
37
44
|
Table.new({:data => data.map { |r| r.to_a }}.merge(options))
|
38
45
|
end
|
39
46
|
|
40
|
-
# Provides a shortcut for the as() method by converting
|
41
|
-
# into to_format_name
|
47
|
+
# Provides a shortcut for the <tt>as()</tt> method by converting a call to
|
48
|
+
# <tt>as(:format_name)</tt> into a call to <tt>to_format_name</tt>
|
42
49
|
def method_missing(id,*args)
|
43
50
|
return as($1.to_sym) if id.to_s =~ /^to_(.*)/
|
44
51
|
super
|
@@ -1,20 +1,82 @@
|
|
1
1
|
module Ruport::Data
|
2
|
+
|
3
|
+
#
|
4
|
+
# === Overview
|
5
|
+
#
|
6
|
+
# This module provides a simple mechanism for grouping objects based on
|
7
|
+
# tags.
|
8
|
+
#
|
2
9
|
module Groupable
|
3
10
|
|
11
|
+
#
|
12
|
+
# Creates a <tt>Record</tt> made up of <tt>Table</tt>s containing all the
|
13
|
+
# records in the original table with the same tag.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# table = [['inky', 1],
|
17
|
+
# ['blinky',2],
|
18
|
+
# ['pinky', 3],
|
19
|
+
# ['clyde', 4]].to_table(['name','score'])
|
20
|
+
#
|
21
|
+
# table[0].tag(:winners)
|
22
|
+
# table[1].tag(:losers)
|
23
|
+
# table[2].tag(:winners)
|
24
|
+
# table[3].tag(:losers)
|
25
|
+
#
|
26
|
+
# r = table.group_by_tag
|
27
|
+
# puts r[:winners]
|
28
|
+
# => +---------------+
|
29
|
+
# | name | score |
|
30
|
+
# +---------------+
|
31
|
+
# | inky | 1 |
|
32
|
+
# | pinky | 3 |
|
33
|
+
# +---------------+
|
34
|
+
#
|
35
|
+
# puts r[:losers]
|
36
|
+
# => +----------------+
|
37
|
+
# | name | score |
|
38
|
+
# +----------------+
|
39
|
+
# | blinky | 2 |
|
40
|
+
# | clyde | 4 |
|
41
|
+
# +----------------+
|
42
|
+
#
|
4
43
|
def group_by_tag
|
5
|
-
r_tags =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
44
|
+
r_tags = map { |r| r.tags }.flatten.uniq
|
45
|
+
tables_hash = Hash.new { |h,k| h[k] = [].to_table(column_names) }
|
46
|
+
each { |row|
|
47
|
+
row.tags.each { |t| tables_hash[t] << row }
|
48
|
+
}
|
49
|
+
r = Record.new tables_hash, :attributes => r_tags
|
10
50
|
class << r
|
11
51
|
def each_group; attributes.each { |a| yield(a) }; end
|
12
52
|
end; r
|
13
53
|
end
|
14
54
|
|
55
|
+
#
|
56
|
+
# Tags each row of the <tt>Table</tt> for which the <tt>block</tt> is not
|
57
|
+
# false with <tt>label</tt>.
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
# table = [['inky', 1],
|
61
|
+
# ['blinky',2],
|
62
|
+
# ['pinky', 3]].to_table(['name','score'])
|
63
|
+
#
|
64
|
+
# table.create_tag_group(:cool_kids) {|r| r.score > 1}
|
65
|
+
# groups = table.group_by_tag(:cool_kids)
|
66
|
+
#
|
67
|
+
# puts group[:cool_kids]
|
68
|
+
# => +----------------+
|
69
|
+
# | name | score |
|
70
|
+
# +----------------+
|
71
|
+
# | blinky | 2 |
|
72
|
+
# | pinky | 3 |
|
73
|
+
# +----------------+
|
74
|
+
#
|
15
75
|
def create_tag_group(label,&block)
|
16
|
-
|
76
|
+
each { |r| block[r] && r.tag(label) }
|
77
|
+
#select(&block).each { |r| r.tag label }
|
17
78
|
end
|
79
|
+
alias_method :tag_group, :create_tag_group
|
18
80
|
|
19
81
|
end
|
20
82
|
end
|
data/lib/ruport/data/record.rb
CHANGED
@@ -5,21 +5,28 @@
|
|
5
5
|
# Copyright 2006 by respective content owners, all rights reserved.
|
6
6
|
module Ruport::Data
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
8
|
+
#
|
9
|
+
# === Overview
|
10
|
+
#
|
11
|
+
# Data::Records are the work horse of Ruport's data model. These can behave
|
12
|
+
# as Array-like, Hash-like, or Struct-like objects. They are used as the
|
13
|
+
# base element in both Tables and Sets.
|
14
|
+
#
|
11
15
|
class Record
|
12
16
|
require "forwardable"
|
13
17
|
extend Forwardable
|
14
18
|
include Enumerable
|
15
19
|
include Taggable
|
16
20
|
|
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
|
#
|
21
|
-
#
|
22
|
+
# Creates a new Record object. If the <tt>:attributes</tt>
|
23
|
+
# keyword is specified, Hash-like and Struct-like
|
24
|
+
# access will be enabled. Otherwise, Record elements may be
|
25
|
+
# accessed ordinally, like an Array.
|
26
|
+
#
|
27
|
+
# A Record can accept either a Hash or an Array as its <tt>data</tt>.
|
22
28
|
#
|
29
|
+
# Examples:
|
23
30
|
# a = Record.new [1,2,3]
|
24
31
|
# a[1] #=> 2
|
25
32
|
#
|
@@ -37,6 +44,7 @@ module Ruport::Data
|
|
37
44
|
# b[1] #=> ? (without attributes, you cannot rely on order)
|
38
45
|
# b['a'] #=> 1
|
39
46
|
# b.c #=> 3
|
47
|
+
#
|
40
48
|
def initialize(data,options={})
|
41
49
|
if data.kind_of?(Hash)
|
42
50
|
if options[:attributes]
|
@@ -55,18 +63,19 @@ module Ruport::Data
|
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
58
|
-
# The underlying data which is being stored in the record
|
66
|
+
# The underlying <tt>data</tt> which is being stored in the record.
|
59
67
|
attr_reader :data
|
60
68
|
|
61
69
|
def_delegators :@data,:each, :length
|
62
70
|
|
63
|
-
#
|
71
|
+
#
|
72
|
+
# Allows either Array or Hash-like indexing.
|
73
|
+
#
|
74
|
+
# Examples:
|
64
75
|
#
|
65
76
|
# my_record[1]
|
66
77
|
# my_record["foo"]
|
67
78
|
#
|
68
|
-
# Also, this provides a feature via method_missing which allows
|
69
|
-
# my_record.foo
|
70
79
|
def [](index)
|
71
80
|
if index.kind_of? Integer
|
72
81
|
raise "Invalid index" unless index < @data.length
|
@@ -78,13 +87,14 @@ module Ruport::Data
|
|
78
87
|
end
|
79
88
|
|
80
89
|
|
81
|
-
#
|
82
|
-
#
|
90
|
+
#
|
91
|
+
# Allows setting a <tt>value</tt> at an <tt>index</tt>.
|
92
|
+
#
|
93
|
+
# Examples:
|
94
|
+
#
|
83
95
|
# my_record[1] = "foo"
|
84
96
|
# my_record["bar"] = "baz"
|
85
97
|
#
|
86
|
-
# And via method_missing
|
87
|
-
# my_record.ghost = "blinky"
|
88
98
|
def []=(index, value)
|
89
99
|
if index.kind_of? Integer
|
90
100
|
raise "Invalid index" unless index < @data.length
|
@@ -95,9 +105,10 @@ module Ruport::Data
|
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
98
|
-
|
99
|
-
# If attributes and data are equivalent, then
|
100
|
-
# Otherwise,
|
108
|
+
#
|
109
|
+
# If <tt>attributes</tt> and <tt>data</tt> are equivalent, then
|
110
|
+
# <tt>==</tt> evaluates to true. Otherwise, <tt>==</tt> returns false.
|
111
|
+
#
|
101
112
|
def ==(other)
|
102
113
|
return false if attributes && !other.attributes
|
103
114
|
return false if other.attributes && !attributes
|
@@ -106,43 +117,62 @@ module Ruport::Data
|
|
106
117
|
|
107
118
|
alias_method :eql?, :==
|
108
119
|
|
109
|
-
#
|
120
|
+
#
|
121
|
+
# Converts a Record into an Array.
|
122
|
+
#
|
123
|
+
# Example:
|
110
124
|
#
|
111
125
|
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
112
126
|
# a.to_a #=> [1,2]
|
127
|
+
#
|
113
128
|
def to_a; @data.dup; end
|
114
|
-
|
115
|
-
#
|
116
|
-
#
|
129
|
+
|
130
|
+
#
|
131
|
+
# Converts a Record into a Hash. This only works if <tt>attributes</tt>
|
132
|
+
# are specified in the Record.
|
133
|
+
#
|
134
|
+
# Example:
|
117
135
|
#
|
118
136
|
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
119
137
|
# a.to_h #=> {"a" => 1, "b" => 2}
|
138
|
+
#
|
120
139
|
def to_h; Hash[*attributes.zip(data).flatten] end
|
121
140
|
|
122
|
-
#
|
141
|
+
#
|
142
|
+
# Returns a copy of the <tt>attributes</tt> from this Record.
|
143
|
+
#
|
144
|
+
# Example:
|
123
145
|
#
|
124
146
|
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
125
147
|
# a.attributes #=> ["a","b"]
|
148
|
+
#
|
126
149
|
def attributes; @attributes.map { |a| a.to_s }; end
|
127
150
|
|
128
|
-
#
|
151
|
+
#
|
152
|
+
# Sets the <tt>attribute</tt> list for this Record.
|
153
|
+
#
|
154
|
+
# Example:
|
129
155
|
#
|
130
156
|
# my_record.attributes = %w[foo bar baz]
|
157
|
+
#
|
131
158
|
attr_writer :attributes
|
132
159
|
|
133
|
-
|
160
|
+
#
|
134
161
|
# Allows you to change the order of or reduce the number of columns in a
|
135
|
-
# Record.
|
162
|
+
# Record.
|
163
|
+
#
|
164
|
+
# Example:
|
136
165
|
#
|
137
166
|
# a = Data::Record.new([1,2,3,4],:attributes => %w[a b c d])
|
138
167
|
# b = a.reorder("a","d","b")
|
139
168
|
# b.attributes #=> ["a","d","b"]
|
140
169
|
# b.data #=> [1,4,2]
|
170
|
+
#
|
141
171
|
def reorder(*indices)
|
142
172
|
dup.reorder!(*indices)
|
143
173
|
end
|
144
174
|
|
145
|
-
# Same as Record#reorder but
|
175
|
+
# Same as Record#reorder but modifies its reciever in place.
|
146
176
|
def reorder!(*indices)
|
147
177
|
indices = reorder_data!(*indices)
|
148
178
|
if attributes
|
@@ -154,7 +184,7 @@ module Ruport::Data
|
|
154
184
|
end; self
|
155
185
|
end
|
156
186
|
|
157
|
-
def reorder_data!(*indices)
|
187
|
+
def reorder_data!(*indices) # :nodoc:
|
158
188
|
indices = indices[0] if indices[0].kind_of?(Array)
|
159
189
|
indices.each do |i|
|
160
190
|
self[i] rescue raise ArgumentError,
|
@@ -165,7 +195,7 @@ module Ruport::Data
|
|
165
195
|
end
|
166
196
|
|
167
197
|
|
168
|
-
# Makes a fresh copy of the Record
|
198
|
+
# Makes a fresh copy of the Record.
|
169
199
|
def dup
|
170
200
|
copy = self.class.new(@data,:attributes => attributes)
|
171
201
|
copy.tags = self.tags.dup
|
@@ -175,21 +205,30 @@ module Ruport::Data
|
|
175
205
|
#FIXME: This does not take into account frozen / tainted state
|
176
206
|
alias_method :clone, :dup
|
177
207
|
|
178
|
-
#
|
208
|
+
#
|
209
|
+
# Provides a unique hash value. If a Record contains the same data and
|
210
|
+
# attributes as another Record, they will hash to the same value, even if
|
211
|
+
# they are not the same object. This is similar to the way Array works,
|
212
|
+
# but different from Hash and other objects.
|
213
|
+
#
|
179
214
|
def hash
|
180
215
|
(attributes.to_a + data.to_a).hash
|
181
216
|
end
|
182
217
|
|
183
|
-
#
|
218
|
+
#
|
219
|
+
# Provides accessor style methods for attribute access.
|
220
|
+
#
|
221
|
+
# Example:
|
184
222
|
#
|
185
223
|
# my_record.foo = 2
|
186
224
|
# my_record.foo #=> 2
|
187
|
-
|
188
|
-
|
225
|
+
#
|
226
|
+
def method_missing(mname, *args)
|
227
|
+
id = mname.to_s.gsub(/=$/,"")
|
189
228
|
if attributes.include?(id)
|
190
229
|
args.empty? ? self[id] : self[id] = args.first
|
191
230
|
else
|
192
|
-
super
|
231
|
+
super(mname, *args)
|
193
232
|
end
|
194
233
|
end
|
195
234
|
|
@@ -0,0 +1,236 @@
|
|
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
|
+
module Ruport::Data
|
7
|
+
|
8
|
+
#
|
9
|
+
# === Overview
|
10
|
+
#
|
11
|
+
# Data::Records are the work horse of Ruport's data model. These can behave
|
12
|
+
# as Array-like, Hash-like, or Struct-like objects. They are used as the
|
13
|
+
# base element in both Tables and Sets.
|
14
|
+
#
|
15
|
+
class Record
|
16
|
+
require "forwardable"
|
17
|
+
extend Forwardable
|
18
|
+
include Enumerable
|
19
|
+
include Taggable
|
20
|
+
|
21
|
+
#
|
22
|
+
# Creates a new Record object. If the <tt>:attributes</tt>
|
23
|
+
# keyword is specified, Hash-like and Struct-like
|
24
|
+
# access will be enabled. Otherwise, Record elements may be
|
25
|
+
# accessed ordinally, like an Array.
|
26
|
+
#
|
27
|
+
# A Record can accept either a Hash or an Array as its <tt>data</tt>.
|
28
|
+
#
|
29
|
+
# Examples:
|
30
|
+
# a = Record.new [1,2,3]
|
31
|
+
# a[1] #=> 2
|
32
|
+
#
|
33
|
+
# b = Record.new [1,2,3], :attributes => %w[a b c]
|
34
|
+
# b[1] #=> 2
|
35
|
+
# b['a'] #=> 1
|
36
|
+
# b.c #=> 3
|
37
|
+
#
|
38
|
+
# c = Record.new {"a" => 1, "c" => 3, "b" => 2}, :attributes => %w[a b c]
|
39
|
+
# b[1] #=> 2
|
40
|
+
# b['a'] #=> 1
|
41
|
+
# b.c #=> 3
|
42
|
+
#
|
43
|
+
# c = Record.new { "a" => 1, "c" => 3, "b" => 2 }
|
44
|
+
# b[1] #=> ? (without attributes, you cannot rely on order)
|
45
|
+
# b['a'] #=> 1
|
46
|
+
# b.c #=> 3
|
47
|
+
#
|
48
|
+
def initialize(data,options={})
|
49
|
+
if data.kind_of?(Hash)
|
50
|
+
if options[:attributes]
|
51
|
+
@attributes = options[:attributes]
|
52
|
+
@data = options[:attributes].map { |k| data[k] }
|
53
|
+
else
|
54
|
+
@attributes, @data = data.to_a.transpose
|
55
|
+
end
|
56
|
+
else
|
57
|
+
@data = data.dup
|
58
|
+
@attributes = if options[:attributes]
|
59
|
+
options[:attributes]
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# The underlying <tt>data</tt> which is being stored in the record.
|
67
|
+
attr_reader :data
|
68
|
+
|
69
|
+
def_delegators :@data,:each, :length
|
70
|
+
|
71
|
+
#
|
72
|
+
# Allows either Array or Hash-like indexing.
|
73
|
+
#
|
74
|
+
# Examples:
|
75
|
+
#
|
76
|
+
# my_record[1]
|
77
|
+
# my_record["foo"]
|
78
|
+
#
|
79
|
+
def [](index)
|
80
|
+
if index.kind_of? Integer
|
81
|
+
raise "Invalid index" unless index < @data.length
|
82
|
+
@data[index]
|
83
|
+
else
|
84
|
+
raise "Invalid index" unless attributes.index(index.to_s)
|
85
|
+
@data[attributes.index(index.to_s)]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
#
|
91
|
+
# Allows setting a <tt>value</tt> at an <tt>index</tt>.
|
92
|
+
#
|
93
|
+
# Examples:
|
94
|
+
#
|
95
|
+
# my_record[1] = "foo"
|
96
|
+
# my_record["bar"] = "baz"
|
97
|
+
#
|
98
|
+
def []=(index, value)
|
99
|
+
if index.kind_of? Integer
|
100
|
+
raise "Invalid index" unless index < @data.length
|
101
|
+
@data[index] = value
|
102
|
+
else
|
103
|
+
raise "Invalid index" unless attributes.index(index.to_s)
|
104
|
+
@data[attributes.index(index.to_s)] = value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# If <tt>attributes</tt> and <tt>data</tt> are equivalent, then
|
110
|
+
# <tt>==</tt> evaluates to true. Otherwise, <tt>==</tt> returns false.
|
111
|
+
#
|
112
|
+
def ==(other)
|
113
|
+
return false if attributes && !other.attributes
|
114
|
+
return false if other.attributes && !attributes
|
115
|
+
attributes == other.attributes && @data == other.data
|
116
|
+
end
|
117
|
+
|
118
|
+
alias_method :eql?, :==
|
119
|
+
|
120
|
+
#
|
121
|
+
# Converts a Record into an Array.
|
122
|
+
#
|
123
|
+
# Example:
|
124
|
+
#
|
125
|
+
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
126
|
+
# a.to_a #=> [1,2]
|
127
|
+
#
|
128
|
+
def to_a; @data.dup; end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Converts a Record into a Hash. This only works if <tt>attributes</tt>
|
132
|
+
# are specified in the Record.
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
#
|
136
|
+
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
137
|
+
# a.to_h #=> {"a" => 1, "b" => 2}
|
138
|
+
#
|
139
|
+
def to_h; Hash[*attributes.zip(data).flatten] end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Returns a copy of the <tt>attributes</tt> from this Record.
|
143
|
+
#
|
144
|
+
# Example:
|
145
|
+
#
|
146
|
+
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
147
|
+
# a.attributes #=> ["a","b"]
|
148
|
+
#
|
149
|
+
def attributes; @attributes.map { |a| a.to_s }; end
|
150
|
+
|
151
|
+
#
|
152
|
+
# Sets the <tt>attribute</tt> list for this Record.
|
153
|
+
#
|
154
|
+
# Example:
|
155
|
+
#
|
156
|
+
# my_record.attributes = %w[foo bar baz]
|
157
|
+
#
|
158
|
+
attr_writer :attributes
|
159
|
+
|
160
|
+
#
|
161
|
+
# Allows you to change the order of or reduce the number of columns in a
|
162
|
+
# Record.
|
163
|
+
#
|
164
|
+
# Example:
|
165
|
+
#
|
166
|
+
# a = Data::Record.new([1,2,3,4],:attributes => %w[a b c d])
|
167
|
+
# b = a.reorder("a","d","b")
|
168
|
+
# b.attributes #=> ["a","d","b"]
|
169
|
+
# b.data #=> [1,4,2]
|
170
|
+
#
|
171
|
+
def reorder(*indices)
|
172
|
+
dup.reorder!(*indices)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Same as Record#reorder but modifies its reciever in place.
|
176
|
+
def reorder!(*indices)
|
177
|
+
indices = reorder_data!(*indices)
|
178
|
+
if attributes
|
179
|
+
if indices.all? { |e| e.kind_of? Integer }
|
180
|
+
@attributes = indices.map { |i| attributes[i] }
|
181
|
+
else
|
182
|
+
@attributes = indices
|
183
|
+
end
|
184
|
+
end; self
|
185
|
+
end
|
186
|
+
|
187
|
+
def reorder_data!(*indices) # :nodoc:
|
188
|
+
indices = indices[0] if indices[0].kind_of?(Array)
|
189
|
+
indices.each do |i|
|
190
|
+
self[i] rescue raise ArgumentError,
|
191
|
+
"you may have specified an invalid column"
|
192
|
+
end
|
193
|
+
@data = indices.map { |i| self[i] }
|
194
|
+
return indices;
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# Makes a fresh copy of the Record.
|
199
|
+
def dup
|
200
|
+
copy = self.class.new(@data,:attributes => attributes)
|
201
|
+
copy.tags = self.tags.dup
|
202
|
+
return copy
|
203
|
+
end
|
204
|
+
|
205
|
+
#FIXME: This does not take into account frozen / tainted state
|
206
|
+
alias_method :clone, :dup
|
207
|
+
|
208
|
+
#
|
209
|
+
# Provides a unique hash value. If a Record contains the same data and
|
210
|
+
# attributes as another Record, they will hash to the same value, even if
|
211
|
+
# they are not the same object. This is similar to the way Array works,
|
212
|
+
# but different from Hash and other objects.
|
213
|
+
#
|
214
|
+
def hash
|
215
|
+
(attributes.to_a + data.to_a).hash
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# Provides accessor style methods for attribute access.
|
220
|
+
#
|
221
|
+
# Example:
|
222
|
+
#
|
223
|
+
# my_record.foo = 2
|
224
|
+
# my_record.foo #=> 2
|
225
|
+
#
|
226
|
+
def method_missing(id,*args)
|
227
|
+
id = id.to_s.gsub(/=$/,"")
|
228
|
+
if attributes.include?(id)
|
229
|
+
args.empty? ? self[id] : self[id] = args.first
|
230
|
+
else
|
231
|
+
super
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|