sequel 0.4.4.1 → 0.4.4.2
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.
- data/CHANGELOG +10 -0
- data/Rakefile +161 -159
- data/lib/sequel.rb +14 -10
- data/lib/sequel/adapters/adapter_skeleton.rb +2 -1
- data/lib/sequel/adapters/ado.rb +2 -1
- data/lib/sequel/adapters/db2.rb +5 -3
- data/lib/sequel/adapters/dbi.rb +2 -1
- data/lib/sequel/adapters/informix.rb +2 -1
- data/lib/sequel/adapters/jdbc.rb +3 -2
- data/lib/sequel/adapters/mysql.rb +268 -264
- data/lib/sequel/adapters/odbc.rb +7 -2
- data/lib/sequel/adapters/odbc_mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +2 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/postgres.rb +32 -16
- data/lib/sequel/adapters/sqlite.rb +7 -6
- data/lib/sequel/array_keys.rb +295 -295
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core_sql.rb +14 -5
- data/lib/sequel/database.rb +4 -4
- data/lib/sequel/dataset.rb +12 -10
- data/lib/sequel/dataset/convenience.rb +10 -8
- data/lib/sequel/dataset/sequelizer.rb +19 -16
- data/lib/sequel/dataset/sql.rb +43 -30
- data/lib/sequel/exceptions.rb +45 -0
- data/lib/sequel/migration.rb +7 -5
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/model/hooks.rb +0 -4
- data/lib/sequel/model/record.rb +9 -9
- data/lib/sequel/model/relations.rb +2 -2
- data/lib/sequel/pretty_table.rb +6 -3
- data/lib/sequel/schema/schema_sql.rb +11 -6
- data/lib/sequel/worker.rb +8 -7
- data/spec/adapters/sqlite_spec.rb +3 -3
- data/spec/array_keys_spec.rb +543 -543
- data/spec/connection_pool_spec.rb +6 -3
- data/spec/database_spec.rb +4 -4
- data/spec/dataset_spec.rb +25 -25
- data/spec/migration_spec.rb +1 -1
- data/spec/model_spec.rb +16 -16
- data/spec/sequelizer_spec.rb +7 -7
- data/spec/spec.opts +8 -0
- metadata +5 -5
- data/lib/sequel/error.rb +0 -22
@@ -1,265 +1,269 @@
|
|
1
|
-
require 'mysql'
|
2
|
-
|
3
|
-
# Monkey patch Mysql::Result to yield hashes with symbol keys
|
4
|
-
class Mysql::Result
|
5
|
-
MYSQL_TYPES = {
|
6
|
-
0 => :to_d, # MYSQL_TYPE_DECIMAL
|
7
|
-
1 => :to_i, # MYSQL_TYPE_TINY
|
8
|
-
2 => :to_i, # MYSQL_TYPE_SHORT
|
9
|
-
3 => :to_i, # MYSQL_TYPE_LONG
|
10
|
-
4 => :to_f, # MYSQL_TYPE_FLOAT
|
11
|
-
5 => :to_f, # MYSQL_TYPE_DOUBLE
|
12
|
-
# 6 => ??, # MYSQL_TYPE_NULL
|
13
|
-
7 => :to_time, # MYSQL_TYPE_TIMESTAMP
|
14
|
-
8 => :to_i, # MYSQL_TYPE_LONGLONG
|
15
|
-
9 => :to_i, # MYSQL_TYPE_INT24
|
16
|
-
10 => :to_time, # MYSQL_TYPE_DATE
|
17
|
-
11 => :to_time, # MYSQL_TYPE_TIME
|
18
|
-
12 => :to_time, # MYSQL_TYPE_DATETIME
|
19
|
-
13 => :to_i, # MYSQL_TYPE_YEAR
|
20
|
-
14 => :to_time, # MYSQL_TYPE_NEWDATE
|
21
|
-
# 15 => :to_s # MYSQL_TYPE_VARCHAR
|
22
|
-
# 16 => :to_s, # MYSQL_TYPE_BIT
|
23
|
-
246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
|
24
|
-
247 => :to_i, # MYSQL_TYPE_ENUM
|
25
|
-
248 => :to_i # MYSQL_TYPE_SET
|
26
|
-
# 249 => :to_s, # MYSQL_TYPE_TINY_BLOB
|
27
|
-
# 250 => :to_s, # MYSQL_TYPE_MEDIUM_BLOB
|
28
|
-
# 251 => :to_s, # MYSQL_TYPE_LONG_BLOB
|
29
|
-
# 252 => :to_s, # MYSQL_TYPE_BLOB
|
30
|
-
# 253 => :to_s, # MYSQL_TYPE_VAR_STRING
|
31
|
-
# 254 => :to_s, # MYSQL_TYPE_STRING
|
32
|
-
# 255 => :to_s # MYSQL_TYPE_GEOMETRY
|
33
|
-
}
|
34
|
-
|
35
|
-
def convert_type(v, type)
|
36
|
-
v ? ((t = MYSQL_TYPES[type]) ? v.send(t) : v) : nil
|
37
|
-
end
|
38
|
-
|
39
|
-
def columns(with_table = nil)
|
40
|
-
unless @columns
|
41
|
-
@column_types = []
|
42
|
-
@columns = fetch_fields.map do |f|
|
43
|
-
@column_types << f.type
|
44
|
-
(with_table ? (f.table + "." + f.name) : f.name).to_sym
|
45
|
-
end
|
46
|
-
end
|
47
|
-
@columns
|
48
|
-
end
|
49
|
-
|
50
|
-
def each_array(with_table = nil)
|
51
|
-
c = columns
|
52
|
-
while row = fetch_row
|
53
|
-
c.each_with_index do |f, i|
|
54
|
-
if (t = MYSQL_TYPES[@column_types[i]]) && (v = row[i])
|
55
|
-
row[i] = v.send(t)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
row.keys = c
|
59
|
-
yield row
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def each_hash(with_table = nil)
|
64
|
-
c = columns
|
65
|
-
while row = fetch_row
|
66
|
-
h = {}
|
67
|
-
c.each_with_index {|f, i| h[f] = convert_type(row[i], @column_types[i])}
|
68
|
-
yield h
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
module Sequel
|
74
|
-
module MySQL
|
75
|
-
class Database < Sequel::Database
|
76
|
-
set_adapter_scheme :mysql
|
77
|
-
|
78
|
-
def serial_primary_key_options
|
79
|
-
{:primary_key => true, :type => :integer, :auto_increment => true}
|
80
|
-
end
|
81
|
-
|
82
|
-
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
83
|
-
|
84
|
-
def auto_increment_sql
|
85
|
-
AUTO_INCREMENT
|
86
|
-
end
|
87
|
-
|
88
|
-
def connect
|
89
|
-
conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
|
90
|
-
|
91
|
-
conn.query_with_result = false
|
92
|
-
if encoding = @opts[:encoding] || @opts[:charset]
|
93
|
-
conn.query("set character_set_connection = '#{encoding}'")
|
94
|
-
conn.query("set character_set_client = '#{encoding}'")
|
95
|
-
conn.query("set character_set_results = '#{encoding}'")
|
96
|
-
end
|
97
|
-
conn.reconnect = true
|
98
|
-
conn
|
99
|
-
end
|
100
|
-
|
101
|
-
def disconnect
|
102
|
-
@pool.disconnect {|c| c.close}
|
103
|
-
end
|
104
|
-
|
105
|
-
def tables
|
106
|
-
@pool.hold do |conn|
|
107
|
-
conn.list_tables.map {|t| t.to_sym}
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def dataset(opts = nil)
|
112
|
-
MySQL::Dataset.new(self, opts)
|
113
|
-
end
|
114
|
-
|
115
|
-
def execute(sql)
|
116
|
-
@logger.info(sql) if @logger
|
117
|
-
@pool.hold do |conn|
|
118
|
-
conn.query(sql)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def execute_select(sql)
|
123
|
-
@logger.info(sql) if @logger
|
124
|
-
@pool.hold do |conn|
|
125
|
-
conn.query(sql)
|
126
|
-
conn.use_result
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def execute_insert(sql)
|
131
|
-
@logger.info(sql) if @logger
|
132
|
-
@pool.hold do |conn|
|
133
|
-
conn.query(sql)
|
134
|
-
conn.insert_id
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def execute_affected(sql)
|
139
|
-
@logger.info(sql) if @logger
|
140
|
-
@pool.hold do |conn|
|
141
|
-
conn.query(sql)
|
142
|
-
conn.affected_rows
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def alter_table_sql(table, op)
|
147
|
-
case op[:op]
|
148
|
-
when :rename_column
|
149
|
-
"ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:new_name])} #{op[:type]}"
|
150
|
-
when :set_column_type
|
151
|
-
"ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:name])} #{op[:type]}"
|
152
|
-
when :drop_index
|
153
|
-
"DROP INDEX #{default_index_name(table, op[:columns])} ON #{table}"
|
154
|
-
else
|
155
|
-
super(table, op)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def transaction
|
160
|
-
@pool.hold do |conn|
|
161
|
-
@transactions ||= []
|
162
|
-
if @transactions.include? Thread.current
|
163
|
-
return yield(conn)
|
164
|
-
end
|
165
|
-
conn.query(SQL_BEGIN)
|
166
|
-
begin
|
167
|
-
@transactions << Thread.current
|
168
|
-
result = yield(conn)
|
169
|
-
conn.query(SQL_COMMIT)
|
170
|
-
result
|
171
|
-
rescue => e
|
172
|
-
conn.query(SQL_ROLLBACK)
|
173
|
-
raise e unless
|
174
|
-
ensure
|
175
|
-
@transactions.delete(Thread.current)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
class Dataset < Sequel::Dataset
|
182
|
-
def quote_column_ref(c); "`#{c}`"; end
|
183
|
-
|
184
|
-
TRUE = '1'
|
185
|
-
FALSE = '0'
|
186
|
-
|
187
|
-
def literal(v)
|
188
|
-
case v
|
189
|
-
when LiteralString
|
190
|
-
|
191
|
-
when
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
if
|
220
|
-
sql << "
|
221
|
-
end
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
end
|
229
|
-
|
230
|
-
def
|
231
|
-
@db.
|
232
|
-
end
|
233
|
-
|
234
|
-
def
|
235
|
-
@db.execute_affected(
|
236
|
-
end
|
237
|
-
|
238
|
-
def
|
239
|
-
@db.
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
1
|
+
require 'mysql'
|
2
|
+
|
3
|
+
# Monkey patch Mysql::Result to yield hashes with symbol keys
|
4
|
+
class Mysql::Result
|
5
|
+
MYSQL_TYPES = {
|
6
|
+
0 => :to_d, # MYSQL_TYPE_DECIMAL
|
7
|
+
1 => :to_i, # MYSQL_TYPE_TINY
|
8
|
+
2 => :to_i, # MYSQL_TYPE_SHORT
|
9
|
+
3 => :to_i, # MYSQL_TYPE_LONG
|
10
|
+
4 => :to_f, # MYSQL_TYPE_FLOAT
|
11
|
+
5 => :to_f, # MYSQL_TYPE_DOUBLE
|
12
|
+
# 6 => ??, # MYSQL_TYPE_NULL
|
13
|
+
7 => :to_time, # MYSQL_TYPE_TIMESTAMP
|
14
|
+
8 => :to_i, # MYSQL_TYPE_LONGLONG
|
15
|
+
9 => :to_i, # MYSQL_TYPE_INT24
|
16
|
+
10 => :to_time, # MYSQL_TYPE_DATE
|
17
|
+
11 => :to_time, # MYSQL_TYPE_TIME
|
18
|
+
12 => :to_time, # MYSQL_TYPE_DATETIME
|
19
|
+
13 => :to_i, # MYSQL_TYPE_YEAR
|
20
|
+
14 => :to_time, # MYSQL_TYPE_NEWDATE
|
21
|
+
# 15 => :to_s # MYSQL_TYPE_VARCHAR
|
22
|
+
# 16 => :to_s, # MYSQL_TYPE_BIT
|
23
|
+
246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
|
24
|
+
247 => :to_i, # MYSQL_TYPE_ENUM
|
25
|
+
248 => :to_i # MYSQL_TYPE_SET
|
26
|
+
# 249 => :to_s, # MYSQL_TYPE_TINY_BLOB
|
27
|
+
# 250 => :to_s, # MYSQL_TYPE_MEDIUM_BLOB
|
28
|
+
# 251 => :to_s, # MYSQL_TYPE_LONG_BLOB
|
29
|
+
# 252 => :to_s, # MYSQL_TYPE_BLOB
|
30
|
+
# 253 => :to_s, # MYSQL_TYPE_VAR_STRING
|
31
|
+
# 254 => :to_s, # MYSQL_TYPE_STRING
|
32
|
+
# 255 => :to_s # MYSQL_TYPE_GEOMETRY
|
33
|
+
}
|
34
|
+
|
35
|
+
def convert_type(v, type)
|
36
|
+
v ? ((t = MYSQL_TYPES[type]) ? v.send(t) : v) : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def columns(with_table = nil)
|
40
|
+
unless @columns
|
41
|
+
@column_types = []
|
42
|
+
@columns = fetch_fields.map do |f|
|
43
|
+
@column_types << f.type
|
44
|
+
(with_table ? (f.table + "." + f.name) : f.name).to_sym
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@columns
|
48
|
+
end
|
49
|
+
|
50
|
+
def each_array(with_table = nil)
|
51
|
+
c = columns
|
52
|
+
while row = fetch_row
|
53
|
+
c.each_with_index do |f, i|
|
54
|
+
if (t = MYSQL_TYPES[@column_types[i]]) && (v = row[i])
|
55
|
+
row[i] = v.send(t)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
row.keys = c
|
59
|
+
yield row
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_hash(with_table = nil)
|
64
|
+
c = columns
|
65
|
+
while row = fetch_row
|
66
|
+
h = {}
|
67
|
+
c.each_with_index {|f, i| h[f] = convert_type(row[i], @column_types[i])}
|
68
|
+
yield h
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module Sequel
|
74
|
+
module MySQL
|
75
|
+
class Database < Sequel::Database
|
76
|
+
set_adapter_scheme :mysql
|
77
|
+
|
78
|
+
def serial_primary_key_options
|
79
|
+
{:primary_key => true, :type => :integer, :auto_increment => true}
|
80
|
+
end
|
81
|
+
|
82
|
+
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
83
|
+
|
84
|
+
def auto_increment_sql
|
85
|
+
AUTO_INCREMENT
|
86
|
+
end
|
87
|
+
|
88
|
+
def connect
|
89
|
+
conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
|
90
|
+
@opts[:database], @opts[:port], nil, Mysql::CLIENT_MULTI_RESULTS)
|
91
|
+
conn.query_with_result = false
|
92
|
+
if encoding = @opts[:encoding] || @opts[:charset]
|
93
|
+
conn.query("set character_set_connection = '#{encoding}'")
|
94
|
+
conn.query("set character_set_client = '#{encoding}'")
|
95
|
+
conn.query("set character_set_results = '#{encoding}'")
|
96
|
+
end
|
97
|
+
conn.reconnect = true
|
98
|
+
conn
|
99
|
+
end
|
100
|
+
|
101
|
+
def disconnect
|
102
|
+
@pool.disconnect {|c| c.close}
|
103
|
+
end
|
104
|
+
|
105
|
+
def tables
|
106
|
+
@pool.hold do |conn|
|
107
|
+
conn.list_tables.map {|t| t.to_sym}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def dataset(opts = nil)
|
112
|
+
MySQL::Dataset.new(self, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
def execute(sql)
|
116
|
+
@logger.info(sql) if @logger
|
117
|
+
@pool.hold do |conn|
|
118
|
+
conn.query(sql)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def execute_select(sql)
|
123
|
+
@logger.info(sql) if @logger
|
124
|
+
@pool.hold do |conn|
|
125
|
+
conn.query(sql)
|
126
|
+
conn.use_result
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def execute_insert(sql)
|
131
|
+
@logger.info(sql) if @logger
|
132
|
+
@pool.hold do |conn|
|
133
|
+
conn.query(sql)
|
134
|
+
conn.insert_id
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def execute_affected(sql)
|
139
|
+
@logger.info(sql) if @logger
|
140
|
+
@pool.hold do |conn|
|
141
|
+
conn.query(sql)
|
142
|
+
conn.affected_rows
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def alter_table_sql(table, op)
|
147
|
+
case op[:op]
|
148
|
+
when :rename_column
|
149
|
+
"ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:new_name])} #{op[:type]}"
|
150
|
+
when :set_column_type
|
151
|
+
"ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:name])} #{op[:type]}"
|
152
|
+
when :drop_index
|
153
|
+
"DROP INDEX #{default_index_name(table, op[:columns])} ON #{table}"
|
154
|
+
else
|
155
|
+
super(table, op)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def transaction
|
160
|
+
@pool.hold do |conn|
|
161
|
+
@transactions ||= []
|
162
|
+
if @transactions.include? Thread.current
|
163
|
+
return yield(conn)
|
164
|
+
end
|
165
|
+
conn.query(SQL_BEGIN)
|
166
|
+
begin
|
167
|
+
@transactions << Thread.current
|
168
|
+
result = yield(conn)
|
169
|
+
conn.query(SQL_COMMIT)
|
170
|
+
result
|
171
|
+
rescue => e
|
172
|
+
conn.query(SQL_ROLLBACK)
|
173
|
+
raise e unless Error::Rollback === e
|
174
|
+
ensure
|
175
|
+
@transactions.delete(Thread.current)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Dataset < Sequel::Dataset
|
182
|
+
def quote_column_ref(c); "`#{c}`"; end
|
183
|
+
|
184
|
+
TRUE = '1'
|
185
|
+
FALSE = '0'
|
186
|
+
|
187
|
+
def literal(v)
|
188
|
+
case v
|
189
|
+
when LiteralString
|
190
|
+
v
|
191
|
+
when String
|
192
|
+
"'#{v.gsub(/'|\\/, '\&\&')}'"
|
193
|
+
when true
|
194
|
+
TRUE
|
195
|
+
when false
|
196
|
+
FALSE
|
197
|
+
else
|
198
|
+
super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def match_expr(l, r)
|
203
|
+
case r
|
204
|
+
when Regexp
|
205
|
+
r.casefold? ? \
|
206
|
+
"(#{literal(l)} REGEXP #{literal(r.source)})" :
|
207
|
+
"(#{literal(l)} REGEXP BINARY #{literal(r.source)})"
|
208
|
+
else
|
209
|
+
super
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# MySQL supports ORDER and LIMIT clauses in UPDATE statements.
|
214
|
+
def update_sql(values, opts = nil)
|
215
|
+
sql = super
|
216
|
+
|
217
|
+
opts = opts ? @opts.merge(opts) : @opts
|
218
|
+
|
219
|
+
if order = opts[:order]
|
220
|
+
sql << " ORDER BY #{column_list(order)}"
|
221
|
+
end
|
222
|
+
|
223
|
+
if limit = opts[:limit]
|
224
|
+
sql << " LIMIT #{limit}"
|
225
|
+
end
|
226
|
+
|
227
|
+
sql
|
228
|
+
end
|
229
|
+
|
230
|
+
def insert(*values)
|
231
|
+
@db.execute_insert(insert_sql(*values))
|
232
|
+
end
|
233
|
+
|
234
|
+
def update(*args, &block)
|
235
|
+
@db.execute_affected(update_sql(*args, &block))
|
236
|
+
end
|
237
|
+
|
238
|
+
def delete(opts = nil)
|
239
|
+
@db.execute_affected(delete_sql(opts))
|
240
|
+
end
|
241
|
+
|
242
|
+
def fetch_rows(sql)
|
243
|
+
@db.synchronize do
|
244
|
+
r = @db.execute_select(sql)
|
245
|
+
begin
|
246
|
+
@columns = r.columns
|
247
|
+
r.each_hash {|row| yield row}
|
248
|
+
ensure
|
249
|
+
r.free
|
250
|
+
end
|
251
|
+
end
|
252
|
+
self
|
253
|
+
end
|
254
|
+
|
255
|
+
def array_tuples_fetch_rows(sql, &block)
|
256
|
+
@db.synchronize do
|
257
|
+
r = @db.execute_select(sql)
|
258
|
+
begin
|
259
|
+
@columns = r.columns
|
260
|
+
r.each_array(&block)
|
261
|
+
ensure
|
262
|
+
r.free
|
263
|
+
end
|
264
|
+
end
|
265
|
+
self
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
265
269
|
end
|