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.
Files changed (45) hide show
  1. data/CHANGELOG +10 -0
  2. data/Rakefile +161 -159
  3. data/lib/sequel.rb +14 -10
  4. data/lib/sequel/adapters/adapter_skeleton.rb +2 -1
  5. data/lib/sequel/adapters/ado.rb +2 -1
  6. data/lib/sequel/adapters/db2.rb +5 -3
  7. data/lib/sequel/adapters/dbi.rb +2 -1
  8. data/lib/sequel/adapters/informix.rb +2 -1
  9. data/lib/sequel/adapters/jdbc.rb +3 -2
  10. data/lib/sequel/adapters/mysql.rb +268 -264
  11. data/lib/sequel/adapters/odbc.rb +7 -2
  12. data/lib/sequel/adapters/odbc_mssql.rb +1 -1
  13. data/lib/sequel/adapters/openbase.rb +2 -1
  14. data/lib/sequel/adapters/oracle.rb +2 -1
  15. data/lib/sequel/adapters/postgres.rb +32 -16
  16. data/lib/sequel/adapters/sqlite.rb +7 -6
  17. data/lib/sequel/array_keys.rb +295 -295
  18. data/lib/sequel/connection_pool.rb +1 -1
  19. data/lib/sequel/core_sql.rb +14 -5
  20. data/lib/sequel/database.rb +4 -4
  21. data/lib/sequel/dataset.rb +12 -10
  22. data/lib/sequel/dataset/convenience.rb +10 -8
  23. data/lib/sequel/dataset/sequelizer.rb +19 -16
  24. data/lib/sequel/dataset/sql.rb +43 -30
  25. data/lib/sequel/exceptions.rb +45 -0
  26. data/lib/sequel/migration.rb +7 -5
  27. data/lib/sequel/model.rb +1 -1
  28. data/lib/sequel/model/base.rb +3 -3
  29. data/lib/sequel/model/hooks.rb +0 -4
  30. data/lib/sequel/model/record.rb +9 -9
  31. data/lib/sequel/model/relations.rb +2 -2
  32. data/lib/sequel/pretty_table.rb +6 -3
  33. data/lib/sequel/schema/schema_sql.rb +11 -6
  34. data/lib/sequel/worker.rb +8 -7
  35. data/spec/adapters/sqlite_spec.rb +3 -3
  36. data/spec/array_keys_spec.rb +543 -543
  37. data/spec/connection_pool_spec.rb +6 -3
  38. data/spec/database_spec.rb +4 -4
  39. data/spec/dataset_spec.rb +25 -25
  40. data/spec/migration_spec.rb +1 -1
  41. data/spec/model_spec.rb +16 -16
  42. data/spec/sequelizer_spec.rb +7 -7
  43. data/spec/spec.opts +8 -0
  44. metadata +5 -5
  45. data/lib/sequel/error.rb +0 -22
@@ -35,10 +35,15 @@ module Sequel
35
35
  end
36
36
 
37
37
  class Dataset < Sequel::Dataset
38
+ BOOL_TRUE = '1'.freeze
39
+ BOOL_FALSE = '0'.freeze
40
+
38
41
  def literal(v)
39
42
  case v
40
- when true: '1'
41
- when false: '0'
43
+ when true
44
+ BOOL_TRUE
45
+ when false
46
+ BOOL_FALSE
42
47
  else
43
48
  super
44
49
  end
@@ -31,7 +31,7 @@ module Sequel
31
31
  # ADD TOP to SELECT string for LIMITS
32
32
  if limit = opts[:limit]
33
33
  top = "TOP #{limit} "
34
- raise SequelError, "Offset not supported" if opts[:offset]
34
+ raise Error, "Offset not supported" if opts[:offset]
35
35
  end
36
36
 
37
37
  columns = opts[:select]
@@ -34,7 +34,8 @@ module Sequel
34
34
  class Dataset < Sequel::Dataset
35
35
  def literal(v)
36
36
  case v
37
- when Time: literal(v.iso8601)
37
+ when Time
38
+ literal(v.iso8601)
38
39
  else
39
40
  super
40
41
  end
@@ -43,7 +43,8 @@ module Sequel
43
43
  class Dataset < Sequel::Dataset
44
44
  def literal(v)
45
45
  case v
46
- when Time: literal(v.iso8601)
46
+ when Time
47
+ literal(v.iso8601)
47
48
  else
48
49
  super
49
50
  end
@@ -2,14 +2,23 @@ require 'postgres'
2
2
 
3
3
  class PGconn
4
4
  # the pure-ruby postgres adapter does not have a quote method.
5
+ TRUE = 't'.freeze
6
+ FALSE = 'f'.freeze
7
+ NULL = 'NULL'.freeze
8
+
5
9
  unless methods.include?('quote')
6
10
  def self.quote(obj)
7
11
  case obj
8
- when true: 't'
9
- when false: 'f'
10
- when nil: 'NULL'
11
- when String: "'#{obj}'"
12
- else obj.to_s
12
+ when true
13
+ TRUE
14
+ when false
15
+ FALSE
16
+ when nil
17
+ NULL
18
+ when String
19
+ "'#{obj}'"
20
+ else
21
+ obj.to_s
13
22
  end
14
23
  end
15
24
  end
@@ -125,10 +134,13 @@ class PGconn
125
134
  end
126
135
 
127
136
  class String
137
+ POSTGRES_BOOL_TRUE = 't'.freeze
138
+ POSTGRES_BOOL_FALSE = 'f'.freeze
139
+
128
140
  def postgres_to_bool
129
- if self == 't'
141
+ if self == POSTGRES_BOOL_TRUE
130
142
  true
131
- elsif self == 'f'
143
+ elsif self == POSTGRES_BOOL_FALSE
132
144
  false
133
145
  else
134
146
  nil
@@ -227,16 +239,16 @@ module Sequel
227
239
  # An error could occur if the inserted values include a primary key
228
240
  # value, while the primary key is serial.
229
241
  if e.message =~ RE_CURRVAL_ERROR
230
- raise SequelError, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
242
+ raise Error, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
231
243
  else
232
244
  raise e
233
245
  end
234
246
  end
235
247
 
236
248
  case values
237
- when Hash:
249
+ when Hash
238
250
  values[primary_key_for_table(conn, table)]
239
- when Array:
251
+ when Array
240
252
  values.first
241
253
  else
242
254
  nil
@@ -283,7 +295,7 @@ module Sequel
283
295
  rescue => e
284
296
  @logger.info(SQL_ROLLBACK) if @logger
285
297
  conn.async_exec(SQL_ROLLBACK) rescue nil
286
- raise e unless SequelRollbackError === e
298
+ raise e unless Error::Rollback === e
287
299
  ensure
288
300
  conn.transaction_in_progress = nil
289
301
  end
@@ -303,8 +315,10 @@ module Sequel
303
315
  class Dataset < Sequel::Dataset
304
316
  def literal(v)
305
317
  case v
306
- when LiteralString: v
307
- when String, Fixnum, Float, TrueClass, FalseClass: PGconn.quote(v)
318
+ when LiteralString
319
+ v
320
+ when String, Fixnum, Float, TrueClass, FalseClass
321
+ PGconn.quote(v)
308
322
  else
309
323
  super
310
324
  end
@@ -312,7 +326,7 @@ module Sequel
312
326
 
313
327
  def match_expr(l, r)
314
328
  case r
315
- when Regexp:
329
+ when Regexp
316
330
  r.casefold? ? \
317
331
  "(#{literal(l)} ~* #{literal(r.source)})" :
318
332
  "(#{literal(l)} ~ #{literal(r.source)})"
@@ -328,8 +342,10 @@ module Sequel
328
342
  row_lock_mode = opts ? opts[:lock] : @opts[:lock]
329
343
  sql = super
330
344
  case row_lock_mode
331
- when :update : sql << FOR_UPDATE
332
- when :share : sql << FOR_SHARE
345
+ when :update
346
+ sql << FOR_UPDATE
347
+ when :share
348
+ sql << FOR_SHARE
333
349
  end
334
350
  sql
335
351
  end
@@ -71,7 +71,7 @@ module Sequel
71
71
  end
72
72
 
73
73
  def auto_vacuum=(value)
74
- value = AUTO_VACUUM.index(value) || (raise SequelError, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
74
+ value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
75
75
  pragma_set(:auto_vacuum, value)
76
76
  end
77
77
 
@@ -82,7 +82,7 @@ module Sequel
82
82
  end
83
83
 
84
84
  def synchronous=(value)
85
- value = SYNCHRONOUS.index(value) || (raise SequelError, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
85
+ value = SYNCHRONOUS.index(value) || (raise Error, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
86
86
  pragma_set(:synchronous, value)
87
87
  end
88
88
 
@@ -93,7 +93,7 @@ module Sequel
93
93
  end
94
94
 
95
95
  def temp_store=(value)
96
- value = TEMP_STORE.index(value) || (raise SequelError, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
96
+ value = TEMP_STORE.index(value) || (raise Error, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
97
97
  pragma_set(:temp_store, value)
98
98
  end
99
99
 
@@ -102,7 +102,7 @@ module Sequel
102
102
  when :add_column
103
103
  "ALTER TABLE #{table} ADD #{column_definition_sql(op)}"
104
104
  else
105
- raise SequelError, "Unsupported ALTER TABLE operation"
105
+ raise Error, "Unsupported ALTER TABLE operation"
106
106
  end
107
107
  end
108
108
 
@@ -116,7 +116,7 @@ module Sequel
116
116
  conn.transaction {result = yield(conn)}
117
117
  result
118
118
  rescue => e
119
- raise e unless SequelRollbackError === e
119
+ raise e unless Error::Rollback === e
120
120
  end
121
121
  end
122
122
  end
@@ -125,7 +125,8 @@ module Sequel
125
125
  class Dataset < Sequel::Dataset
126
126
  def literal(v)
127
127
  case v
128
- when Time: literal(v.iso8601)
128
+ when Time
129
+ literal(v.iso8601)
129
130
  else
130
131
  super
131
132
  end
@@ -1,296 +1,296 @@
1
- # ArrayKeys provide support for accessing array elements by keys. ArrayKeys are
2
- # based on the arrayfields gem by Ara Howard, and can be used as substitutes
3
- # for fetching records tuples as Ruby hashes.
4
- #
5
- # The main advantage offered by ArrayKeys over hashes is that the values are
6
- # always ordered according to the column order in the query. Another purported
7
- # advantage is that they reduce the memory footprint, but this has turned out
8
- # to be a false claim.
9
- module ArrayKeys
10
- # The KeySet module contains methods that extend an array of keys to return
11
- # a key's position in the key set.
12
- module KeySet
13
- # Returns the key's position in the key set. Provides indifferent access
14
- # for symbols and strings.
15
- def key_pos(key)
16
- @key_indexes ||= inject({}) {|h, k| h[k.to_sym] = h.size; h}
17
- @key_indexes[key] || @key_indexes[key.to_sym] || @key_indexes[key.to_s]
18
- end
19
-
20
- # Adds a key to the key set.
21
- def add_key(key)
22
- self << key
23
- @key_indexes[key] = @key_indexes.size
24
- end
25
-
26
- # Removes a key from the key set by its index.
27
- def del_key(idx)
28
- delete_at(idx)
29
- @key_indexes = nil # reset key indexes
30
- end
31
- end
32
-
33
- # The KeyAccess provides a large part of the Hash API for arrays with keys.
34
- module KeyAccess
35
- # Returns a value referenced by an array index or a key.
36
- def [](idx, *args)
37
- if String === idx or Symbol === idx
38
- (idx = @keys.key_pos(idx)) ? super(idx, *args) : nil
39
- else
40
- super
41
- end
42
- end
43
-
44
- # Sets the value referenced by an array index or a key.
45
- def []=(idx,*args)
46
- if String === idx or Symbol === idx
47
- idx = @keys.key_pos(idx) || @keys.add_key(idx.to_sym)
48
- end
49
- super(idx, *args)
50
- end
51
-
52
- # Stores a value by index or key.
53
- def store(k, v); self[k] = v; end
54
-
55
- # Slices the array, and returns an array with its keys sliced accordingly.
56
- def slice(*args)
57
- s = super(*args)
58
- s.keys = @keys.slice(*args)
59
- s
60
- end
61
-
62
- # Converts the array into a hash.
63
- def to_hash
64
- h = {}
65
- each_with_index {|v, i| h[@keys[i].to_sym] = v}
66
- h
67
- end
68
- alias_method :to_h, :to_hash
69
-
70
- # Iterates over each key-value pair in the array.
71
- def each_pair
72
- each_with_index {|v, i| yield @keys[i], v}
73
- end
74
-
75
- # Iterates over the array's associated keys.
76
- def each_key(&block)
77
- @keys.each(&block)
78
- end
79
-
80
- # Iterates over the array's values.
81
- def each_value(&block)
82
- each(&block)
83
- end
84
-
85
- # Deletes a value by its key.
86
- def delete(key, *args)
87
- if (idx = @keys.key_pos(key))
88
- delete_at(idx)
89
- end
90
- end
91
-
92
- # Deletes a value by its index.
93
- def delete_at(idx)
94
- super(idx)
95
- @keys = @keys.clone
96
- @keys.del_key(idx)
97
- end
98
-
99
- # Returns true if the array's key set contains the given key.
100
- def include?(k)
101
- @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
102
- end
103
-
104
- # Returns true if the array's key set contains the given key.
105
- def has_key?(k)
106
- @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
107
- end
108
- alias_method :member?, :has_key?
109
- alias_method :key?, :has_key?
110
-
111
- # Returns true if the array contains the given value.
112
- def has_value?(k); orig_include?(k); end
113
- alias_method :value?, :has_value?
114
-
115
- # Fetches a value by its key and optionally passes it through the given
116
- # block:
117
- #
118
- # row.fetch(:name) {|v| v.to_sym}
119
- #
120
- # You can also give a default value
121
- #
122
- # row.fetch(:name, 'untitled')
123
- #
124
- def fetch(k, *args, &block)
125
- if idx = @keys.key_pos(k)
126
- v = at idx
127
- else
128
- !args.empty? ? (v = args.first) : (raise IndexError, "key not found")
129
- end
130
- block ? block[v] : v
131
- end
132
-
133
- # Returns self.
134
- def values
135
- self
136
- end
137
-
138
- # Creates a copy of self with the same key set.
139
- def dup
140
- copy = super
141
- copy.keys = @keys
142
- copy
143
- end
144
-
145
- # Creates a copy of self with a copy of the key set.
146
- def clone
147
- copy = super
148
- copy.keys = @keys.clone
149
- copy
150
- end
151
-
152
- # Returns an array merged from self and the given array.
153
- def merge(values, &block)
154
- clone.merge!(values, &block)
155
- end
156
-
157
- # Merges the given array with self, optionally passing the values from self
158
- # through the given block:
159
- #
160
- # row.merge!(new_values) {|k, old, new| (k == :name) ? old : new}
161
- #
162
- def merge!(values, &block)
163
- values.each_pair do |k, v|
164
- self[k] = (has_key?(k) && block) ? block[k, self[k], v] : v
165
- end
166
- self
167
- end
168
- alias_method :update, :merge!
169
- alias_method :update!, :merge!
170
- end
171
-
172
- # The ArrayExtensions module provides extensions for the Array class.
173
- module ArrayExtensions
174
- attr_reader :keys
175
-
176
- # Sets the key set for the array. Once a key set has been set for an array,
177
- # it is extended with the KeyAccess API
178
- def keys=(keys)
179
- extend ArrayKeys::KeyAccess if keys
180
- @keys = keys.frozen? ? keys.dup : keys
181
- unless @keys.respond_to?(:key_pos)
182
- @keys.extend(ArrayKeys::KeySet)
183
- end
184
- end
185
-
186
- alias_method :columns, :keys
187
- alias_method :columns=, :keys=
188
- end
189
-
190
- # The DatasetExtensions module provides extensions that modify
191
- # a dataset to return Array tuples instead of Hash tuples.
192
- module DatasetExtensions
193
- # Fetches a dataset's records, converting each tuple into an array with
194
- # keys.
195
- def array_tuples_each(opts = nil, &block)
196
- fetch_rows(select_sql(opts)) {|h| block[Array.from_hash(h)]}
197
- end
198
-
199
- # Provides the corresponding behavior to Sequel::Dataset#update_each_method,
200
- # using array tuples.
201
- def array_tuples_update_each_method
202
- # warning: ugly code generation ahead
203
- if @row_proc && @transform
204
- class << self
205
- def each(opts = nil, &block)
206
- if opts && opts[:naked]
207
- fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
208
- else
209
- fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(Array.from_hash(r))]]}
210
- end
211
- self
212
- end
213
- end
214
- elsif @row_proc
215
- class << self
216
- def each(opts = nil, &block)
217
- if opts && opts[:naked]
218
- fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
219
- else
220
- fetch_rows(select_sql(opts)) {|r| block[@row_proc[Array.from_hash(r)]]}
221
- end
222
- self
223
- end
224
- end
225
- elsif @transform
226
- class << self
227
- def each(opts = nil, &block)
228
- fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
229
- self
230
- end
231
- end
232
- else
233
- class << self
234
- def each(opts = nil, &block)
235
- fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
236
- self
237
- end
238
- end
239
- end
240
- end
241
- end
242
- end
243
-
244
- # Array extensions.
245
- class Array
246
- alias_method :orig_include?, :include?
247
-
248
- include ArrayKeys::ArrayExtensions
249
-
250
- # Converts a hash into an array with keys.
251
- def self.from_hash(h)
252
- a = []; a.keys = []
253
- a.merge!(h)
254
- end
255
- end
256
-
257
- module Sequel
258
- # Modifies all dataset classes to fetch records as arrays with keys. By
259
- # default records are fetched as hashes.
260
- def self.use_array_tuples
261
- Dataset.dataset_classes.each do |c|
262
- c.class_eval do
263
- if method_defined?(:array_tuples_fetch_rows)
264
- alias_method :hash_tuples_fetch_rows, :fetch_rows
265
- alias_method :fetch_rows, :array_tuples_fetch_rows
266
- else
267
- alias_method :orig_each, :each
268
- alias_method :orig_update_each_method, :update_each_method
269
- include ArrayKeys::DatasetExtensions
270
- alias_method :each, :array_tuples_each
271
- alias_method :update_each_method, :array_tuples_update_each_method
272
- end
273
- end
274
- end
275
- end
276
-
277
- # Modifies all dataset classes to fetch records as hashes.
278
- def self.use_hash_tuples
279
- Dataset.dataset_classes.each do |c|
280
- c.class_eval do
281
- if method_defined?(:hash_tuples_fetch_rows)
282
- alias_method :fetch_rows, :hash_tuples_fetch_rows
283
- else
284
- if method_defined?(:orig_each)
285
- alias_method :each, :orig_each
286
- undef_method :orig_each
287
- end
288
- if method_defined?(:orig_update_each_method)
289
- alias_method :update_each_method, :orig_update_each_method
290
- undef_method :orig_update_each_method
291
- end
292
- end
293
- end
294
- end
295
- end
1
+ # ArrayKeys provide support for accessing array elements by keys. ArrayKeys are
2
+ # based on the arrayfields gem by Ara Howard, and can be used as substitutes
3
+ # for fetching records tuples as Ruby hashes.
4
+ #
5
+ # The main advantage offered by ArrayKeys over hashes is that the values are
6
+ # always ordered according to the column order in the query. Another purported
7
+ # advantage is that they reduce the memory footprint, but this has turned out
8
+ # to be a false claim.
9
+ module ArrayKeys
10
+ # The KeySet module contains methods that extend an array of keys to return
11
+ # a key's position in the key set.
12
+ module KeySet
13
+ # Returns the key's position in the key set. Provides indifferent access
14
+ # for symbols and strings.
15
+ def key_pos(key)
16
+ @key_indexes ||= inject({}) {|h, k| h[k.to_sym] = h.size; h}
17
+ @key_indexes[key] || @key_indexes[key.to_sym] || @key_indexes[key.to_s]
18
+ end
19
+
20
+ # Adds a key to the key set.
21
+ def add_key(key)
22
+ self << key
23
+ @key_indexes[key] = @key_indexes.size
24
+ end
25
+
26
+ # Removes a key from the key set by its index.
27
+ def del_key(idx)
28
+ delete_at(idx)
29
+ @key_indexes = nil # reset key indexes
30
+ end
31
+ end
32
+
33
+ # The KeyAccess provides a large part of the Hash API for arrays with keys.
34
+ module KeyAccess
35
+ # Returns a value referenced by an array index or a key.
36
+ def [](idx, *args)
37
+ if String === idx or Symbol === idx
38
+ (idx = @keys.key_pos(idx)) ? super(idx, *args) : nil
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+ # Sets the value referenced by an array index or a key.
45
+ def []=(idx,*args)
46
+ if String === idx or Symbol === idx
47
+ idx = @keys.key_pos(idx) || @keys.add_key(idx.to_sym)
48
+ end
49
+ super(idx, *args)
50
+ end
51
+
52
+ # Stores a value by index or key.
53
+ def store(k, v); self[k] = v; end
54
+
55
+ # Slices the array, and returns an array with its keys sliced accordingly.
56
+ def slice(*args)
57
+ s = super(*args)
58
+ s.keys = @keys.slice(*args)
59
+ s
60
+ end
61
+
62
+ # Converts the array into a hash.
63
+ def to_hash
64
+ h = {}
65
+ each_with_index {|v, i| h[@keys[i].to_sym] = v}
66
+ h
67
+ end
68
+ alias_method :to_h, :to_hash
69
+
70
+ # Iterates over each key-value pair in the array.
71
+ def each_pair
72
+ each_with_index {|v, i| yield @keys[i], v}
73
+ end
74
+
75
+ # Iterates over the array's associated keys.
76
+ def each_key(&block)
77
+ @keys.each(&block)
78
+ end
79
+
80
+ # Iterates over the array's values.
81
+ def each_value(&block)
82
+ each(&block)
83
+ end
84
+
85
+ # Deletes a value by its key.
86
+ def delete(key, *args)
87
+ if (idx = @keys.key_pos(key))
88
+ delete_at(idx)
89
+ end
90
+ end
91
+
92
+ # Deletes a value by its index.
93
+ def delete_at(idx)
94
+ super(idx)
95
+ @keys = @keys.clone
96
+ @keys.del_key(idx)
97
+ end
98
+
99
+ # Returns true if the array's key set contains the given key.
100
+ def include?(k)
101
+ @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
102
+ end
103
+
104
+ # Returns true if the array's key set contains the given key.
105
+ def has_key?(k)
106
+ @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
107
+ end
108
+ alias_method :member?, :has_key?
109
+ alias_method :key?, :has_key?
110
+
111
+ # Returns true if the array contains the given value.
112
+ def has_value?(k); orig_include?(k); end
113
+ alias_method :value?, :has_value?
114
+
115
+ # Fetches a value by its key and optionally passes it through the given
116
+ # block:
117
+ #
118
+ # row.fetch(:name) {|v| v.to_sym}
119
+ #
120
+ # You can also give a default value
121
+ #
122
+ # row.fetch(:name, 'untitled')
123
+ #
124
+ def fetch(k, *args, &block)
125
+ if idx = @keys.key_pos(k)
126
+ v = at idx
127
+ else
128
+ !args.empty? ? (v = args.first) : (raise IndexError, "key not found")
129
+ end
130
+ block ? block[v] : v
131
+ end
132
+
133
+ # Returns self.
134
+ def values
135
+ self
136
+ end
137
+
138
+ # Creates a copy of self with the same key set.
139
+ def dup
140
+ copy = super
141
+ copy.keys = @keys
142
+ copy
143
+ end
144
+
145
+ # Creates a copy of self with a copy of the key set.
146
+ def clone
147
+ copy = super
148
+ copy.keys = @keys.clone
149
+ copy
150
+ end
151
+
152
+ # Returns an array merged from self and the given array.
153
+ def merge(values, &block)
154
+ clone.merge!(values, &block)
155
+ end
156
+
157
+ # Merges the given array with self, optionally passing the values from self
158
+ # through the given block:
159
+ #
160
+ # row.merge!(new_values) {|k, old, new| (k == :name) ? old : new}
161
+ #
162
+ def merge!(values, &block)
163
+ values.each_pair do |k, v|
164
+ self[k] = (has_key?(k) && block) ? block[k, self[k], v] : v
165
+ end
166
+ self
167
+ end
168
+ alias_method :update, :merge!
169
+ alias_method :update!, :merge!
170
+ end
171
+
172
+ # The ArrayExtensions module provides extensions for the Array class.
173
+ module ArrayExtensions
174
+ attr_reader :keys
175
+
176
+ # Sets the key set for the array. Once a key set has been set for an array,
177
+ # it is extended with the KeyAccess API
178
+ def keys=(keys)
179
+ extend ArrayKeys::KeyAccess if keys
180
+ @keys = keys.frozen? ? keys.dup : keys
181
+ unless @keys.respond_to?(:key_pos)
182
+ @keys.extend(ArrayKeys::KeySet)
183
+ end
184
+ end
185
+
186
+ alias_method :columns, :keys
187
+ alias_method :columns=, :keys=
188
+ end
189
+
190
+ # The DatasetExtensions module provides extensions that modify
191
+ # a dataset to return Array tuples instead of Hash tuples.
192
+ module DatasetExtensions
193
+ # Fetches a dataset's records, converting each tuple into an array with
194
+ # keys.
195
+ def array_tuples_each(opts = nil, &block)
196
+ fetch_rows(select_sql(opts)) {|h| block[Array.from_hash(h)]}
197
+ end
198
+
199
+ # Provides the corresponding behavior to Sequel::Dataset#update_each_method,
200
+ # using array tuples.
201
+ def array_tuples_update_each_method
202
+ # warning: ugly code generation ahead
203
+ if @row_proc && @transform
204
+ class << self
205
+ def each(opts = nil, &block)
206
+ if opts && opts[:naked]
207
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
208
+ else
209
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(Array.from_hash(r))]]}
210
+ end
211
+ self
212
+ end
213
+ end
214
+ elsif @row_proc
215
+ class << self
216
+ def each(opts = nil, &block)
217
+ if opts && opts[:naked]
218
+ fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
219
+ else
220
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[Array.from_hash(r)]]}
221
+ end
222
+ self
223
+ end
224
+ end
225
+ elsif @transform
226
+ class << self
227
+ def each(opts = nil, &block)
228
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
229
+ self
230
+ end
231
+ end
232
+ else
233
+ class << self
234
+ def each(opts = nil, &block)
235
+ fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
236
+ self
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ # Array extensions.
245
+ class Array
246
+ alias_method :orig_include?, :include?
247
+
248
+ include ArrayKeys::ArrayExtensions
249
+
250
+ # Converts a hash into an array with keys.
251
+ def self.from_hash(h)
252
+ a = []; a.keys = []
253
+ a.merge!(h)
254
+ end
255
+ end
256
+
257
+ module Sequel
258
+ # Modifies all dataset classes to fetch records as arrays with keys. By
259
+ # default records are fetched as hashes.
260
+ def self.use_array_tuples
261
+ Dataset.dataset_classes.each do |c|
262
+ c.class_eval do
263
+ if method_defined?(:array_tuples_fetch_rows)
264
+ alias_method :hash_tuples_fetch_rows, :fetch_rows
265
+ alias_method :fetch_rows, :array_tuples_fetch_rows
266
+ else
267
+ alias_method :orig_each, :each
268
+ alias_method :orig_update_each_method, :update_each_method
269
+ include ArrayKeys::DatasetExtensions
270
+ alias_method :each, :array_tuples_each
271
+ alias_method :update_each_method, :array_tuples_update_each_method
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ # Modifies all dataset classes to fetch records as hashes.
278
+ def self.use_hash_tuples
279
+ Dataset.dataset_classes.each do |c|
280
+ c.class_eval do
281
+ if method_defined?(:hash_tuples_fetch_rows)
282
+ alias_method :fetch_rows, :hash_tuples_fetch_rows
283
+ else
284
+ if method_defined?(:orig_each)
285
+ alias_method :each, :orig_each
286
+ undef_method :orig_each
287
+ end
288
+ if method_defined?(:orig_update_each_method)
289
+ alias_method :update_each_method, :orig_update_each_method
290
+ undef_method :orig_update_each_method
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
296
  end