sequel 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG +68 -0
  2. data/COPYING +1 -1
  3. data/README.rdoc +87 -27
  4. data/bin/sequel +2 -4
  5. data/doc/association_basics.rdoc +1383 -0
  6. data/doc/dataset_basics.rdoc +106 -0
  7. data/doc/opening_databases.rdoc +45 -16
  8. data/doc/querying.rdoc +210 -0
  9. data/doc/release_notes/3.11.0.txt +254 -0
  10. data/doc/virtual_rows.rdoc +217 -31
  11. data/lib/sequel/adapters/ado.rb +28 -12
  12. data/lib/sequel/adapters/ado/mssql.rb +33 -1
  13. data/lib/sequel/adapters/amalgalite.rb +13 -8
  14. data/lib/sequel/adapters/db2.rb +1 -2
  15. data/lib/sequel/adapters/dbi.rb +7 -4
  16. data/lib/sequel/adapters/do.rb +14 -15
  17. data/lib/sequel/adapters/do/postgres.rb +4 -5
  18. data/lib/sequel/adapters/do/sqlite.rb +9 -0
  19. data/lib/sequel/adapters/firebird.rb +5 -10
  20. data/lib/sequel/adapters/informix.rb +2 -4
  21. data/lib/sequel/adapters/jdbc.rb +111 -49
  22. data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
  24. data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
  27. data/lib/sequel/adapters/mysql.rb +14 -5
  28. data/lib/sequel/adapters/odbc.rb +2 -4
  29. data/lib/sequel/adapters/odbc/mssql.rb +2 -4
  30. data/lib/sequel/adapters/openbase.rb +1 -2
  31. data/lib/sequel/adapters/oracle.rb +4 -8
  32. data/lib/sequel/adapters/postgres.rb +4 -11
  33. data/lib/sequel/adapters/shared/mssql.rb +22 -9
  34. data/lib/sequel/adapters/shared/mysql.rb +33 -30
  35. data/lib/sequel/adapters/shared/oracle.rb +0 -5
  36. data/lib/sequel/adapters/shared/postgres.rb +13 -11
  37. data/lib/sequel/adapters/shared/sqlite.rb +56 -10
  38. data/lib/sequel/adapters/sqlite.rb +16 -9
  39. data/lib/sequel/connection_pool.rb +6 -1
  40. data/lib/sequel/connection_pool/single.rb +1 -0
  41. data/lib/sequel/core.rb +6 -1
  42. data/lib/sequel/database.rb +52 -23
  43. data/lib/sequel/database/schema_generator.rb +6 -0
  44. data/lib/sequel/database/schema_methods.rb +5 -5
  45. data/lib/sequel/database/schema_sql.rb +1 -1
  46. data/lib/sequel/dataset.rb +4 -190
  47. data/lib/sequel/dataset/actions.rb +323 -1
  48. data/lib/sequel/dataset/features.rb +18 -2
  49. data/lib/sequel/dataset/graph.rb +7 -0
  50. data/lib/sequel/dataset/misc.rb +119 -0
  51. data/lib/sequel/dataset/mutation.rb +64 -0
  52. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  53. data/lib/sequel/dataset/query.rb +272 -6
  54. data/lib/sequel/dataset/sql.rb +186 -394
  55. data/lib/sequel/model.rb +4 -2
  56. data/lib/sequel/model/associations.rb +31 -14
  57. data/lib/sequel/model/base.rb +32 -13
  58. data/lib/sequel/model/exceptions.rb +8 -4
  59. data/lib/sequel/model/plugins.rb +3 -13
  60. data/lib/sequel/plugins/active_model.rb +26 -7
  61. data/lib/sequel/plugins/instance_filters.rb +98 -0
  62. data/lib/sequel/plugins/many_through_many.rb +1 -1
  63. data/lib/sequel/plugins/optimistic_locking.rb +25 -9
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +26 -0
  66. data/spec/adapters/mysql_spec.rb +33 -4
  67. data/spec/adapters/postgres_spec.rb +24 -1
  68. data/spec/adapters/spec_helper.rb +6 -0
  69. data/spec/adapters/sqlite_spec.rb +28 -0
  70. data/spec/core/connection_pool_spec.rb +17 -5
  71. data/spec/core/database_spec.rb +101 -1
  72. data/spec/core/dataset_spec.rb +42 -4
  73. data/spec/core/schema_spec.rb +13 -0
  74. data/spec/extensions/active_model_spec.rb +34 -11
  75. data/spec/extensions/caching_spec.rb +2 -0
  76. data/spec/extensions/instance_filters_spec.rb +55 -0
  77. data/spec/extensions/spec_helper.rb +2 -0
  78. data/spec/integration/dataset_test.rb +12 -1
  79. data/spec/integration/model_test.rb +12 -0
  80. data/spec/integration/plugin_test.rb +61 -1
  81. data/spec/integration/schema_test.rb +14 -3
  82. data/spec/model/base_spec.rb +27 -0
  83. data/spec/model/plugins_spec.rb +0 -22
  84. data/spec/model/record_spec.rb +32 -1
  85. data/spec/model/spec_helper.rb +2 -0
  86. metadata +14 -3
  87. data/lib/sequel/dataset/convenience.rb +0 -326
@@ -15,10 +15,12 @@ class MockDataset < Sequel::Dataset
15
15
 
16
16
  def update(*args)
17
17
  @db.execute update_sql(*args)
18
+ 1
18
19
  end
19
20
 
20
21
  def delete(*args)
21
22
  @db.execute delete_sql(*args)
23
+ 1
22
24
  end
23
25
 
24
26
  def fetch_rows(sql)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.10.0
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-04-02 00:00:00 -07:00
12
+ date: 2010-05-03 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -32,6 +32,9 @@ extra_rdoc_files:
32
32
  - doc/schema.rdoc
33
33
  - doc/sharding.rdoc
34
34
  - doc/virtual_rows.rdoc
35
+ - doc/dataset_basics.rdoc
36
+ - doc/association_basics.rdoc
37
+ - doc/querying.rdoc
35
38
  - doc/release_notes/1.0.txt
36
39
  - doc/release_notes/1.1.txt
37
40
  - doc/release_notes/1.3.txt
@@ -61,6 +64,7 @@ extra_rdoc_files:
61
64
  - doc/release_notes/3.8.0.txt
62
65
  - doc/release_notes/3.9.0.txt
63
66
  - doc/release_notes/3.10.0.txt
67
+ - doc/release_notes/3.11.0.txt
64
68
  files:
65
69
  - COPYING
66
70
  - CHANGELOG
@@ -102,9 +106,13 @@ files:
102
106
  - doc/release_notes/3.8.0.txt
103
107
  - doc/release_notes/3.9.0.txt
104
108
  - doc/release_notes/3.10.0.txt
109
+ - doc/release_notes/3.11.0.txt
105
110
  - doc/schema.rdoc
106
111
  - doc/sharding.rdoc
107
112
  - doc/virtual_rows.rdoc
113
+ - doc/dataset_basics.rdoc
114
+ - doc/association_basics.rdoc
115
+ - doc/querying.rdoc
108
116
  - spec/adapters/firebird_spec.rb
109
117
  - spec/adapters/informix_spec.rb
110
118
  - spec/adapters/mssql_spec.rb
@@ -162,6 +170,7 @@ files:
162
170
  - spec/extensions/validation_helpers_spec.rb
163
171
  - spec/extensions/composition_spec.rb
164
172
  - spec/extensions/rcte_tree_spec.rb
173
+ - spec/extensions/instance_filters_spec.rb
165
174
  - spec/integration/associations_test.rb
166
175
  - spec/integration/database_test.rb
167
176
  - spec/integration/dataset_test.rb
@@ -236,12 +245,13 @@ files:
236
245
  - lib/sequel/database/schema_sql.rb
237
246
  - lib/sequel/dataset.rb
238
247
  - lib/sequel/dataset/actions.rb
239
- - lib/sequel/dataset/convenience.rb
240
248
  - lib/sequel/dataset/features.rb
241
249
  - lib/sequel/dataset/graph.rb
250
+ - lib/sequel/dataset/misc.rb
242
251
  - lib/sequel/dataset/prepared_statements.rb
243
252
  - lib/sequel/dataset/query.rb
244
253
  - lib/sequel/dataset/sql.rb
254
+ - lib/sequel/dataset/mutation.rb
245
255
  - lib/sequel/exceptions.rb
246
256
  - lib/sequel/extensions/blank.rb
247
257
  - lib/sequel/extensions/inflector.rb
@@ -290,6 +300,7 @@ files:
290
300
  - lib/sequel/plugins/validation_helpers.rb
291
301
  - lib/sequel/plugins/composition.rb
292
302
  - lib/sequel/plugins/rcte_tree.rb
303
+ - lib/sequel/plugins/instance_filters.rb
293
304
  - lib/sequel/sql.rb
294
305
  - lib/sequel/timezones.rb
295
306
  - lib/sequel/version.rb
@@ -1,326 +0,0 @@
1
- module Sequel
2
- class Dataset
3
- COMMA_SEPARATOR = ', '.freeze
4
- COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
5
- ARRAY_ACCESS_ERROR_MSG = 'You cannot call Dataset#[] with an integer or with no arguments.'.freeze
6
- ARG_BLOCK_ERROR_MSG = 'Must use either an argument or a block, not both'.freeze
7
- IMPORT_ERROR_MSG = 'Using Sequel::Dataset#import an empty column array is not allowed'.freeze
8
-
9
- # Returns the first record matching the conditions. Examples:
10
- #
11
- # ds[:id=>1] => {:id=1}
12
- def [](*conditions)
13
- raise(Error, ARRAY_ACCESS_ERROR_MSG) if (conditions.length == 1 and conditions.first.is_a?(Integer)) or conditions.length == 0
14
- first(*conditions)
15
- end
16
-
17
- # Update all records matching the conditions
18
- # with the values specified. Examples:
19
- #
20
- # ds[:id=>1] = {:id=>2} # SQL: UPDATE ... SET id = 2 WHERE id = 1
21
- def []=(conditions, values)
22
- filter(conditions).update(values)
23
- end
24
-
25
- # Returns the average value for the given column.
26
- def avg(column)
27
- aggregate_dataset.get{avg(column)}
28
- end
29
-
30
- # Returns true if no records exist in the dataset, false otherwise
31
- def empty?
32
- get(1).nil?
33
- end
34
-
35
- # If a integer argument is
36
- # given, it is interpreted as a limit, and then returns all
37
- # matching records up to that limit. If no argument is passed,
38
- # it returns the first matching record. If any other type of
39
- # argument(s) is passed, it is given to filter and the
40
- # first matching record is returned. If a block is given, it is used
41
- # to filter the dataset before returning anything. Examples:
42
- #
43
- # ds.first => {:id=>7}
44
- # ds.first(2) => [{:id=>6}, {:id=>4}]
45
- # ds.order(:id).first(2) => [{:id=>1}, {:id=>2}]
46
- # ds.first(:id=>2) => {:id=>2}
47
- # ds.first("id = 3") => {:id=>3}
48
- # ds.first("id = ?", 4) => {:id=>4}
49
- # ds.first{|o| o.id > 2} => {:id=>5}
50
- # ds.order(:id).first{|o| o.id > 2} => {:id=>3}
51
- # ds.first{|o| o.id > 2} => {:id=>5}
52
- # ds.first("id > ?", 4){|o| o.id < 6} => {:id=>5}
53
- # ds.order(:id).first(2){|o| o.id < 2} => [{:id=>1}]
54
- def first(*args, &block)
55
- ds = block ? filter(&block) : self
56
-
57
- if args.empty?
58
- ds.single_record
59
- else
60
- args = (args.size == 1) ? args.first : args
61
- if Integer === args
62
- ds.limit(args).all
63
- else
64
- ds.filter(args).single_record
65
- end
66
- end
67
- end
68
-
69
- # Return the column value for the first matching record in the dataset.
70
- # Raises an error if both an argument and block is given.
71
- #
72
- # ds.get(:id)
73
- # ds.get{|o| o.sum(:id)}
74
- def get(column=nil, &block)
75
- if column
76
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
77
- select(column).single_value
78
- else
79
- select(&block).single_value
80
- end
81
- end
82
-
83
- # Returns a dataset grouped by the given column with count by group,
84
- # order by the count of records. Column aliases may be supplied, and will
85
- # be included in the select clause.
86
- #
87
- # Examples:
88
- #
89
- # ds.group_and_count(:name).all => [{:name=>'a', :count=>1}, ...]
90
- # ds.group_and_count(:first_name, :last_name).all => [{:first_name=>'a', :last_name=>'b', :count=>1}, ...]
91
- # ds.group_and_count(:first_name___name).all => [{:name=>'a', :count=>1}, ...]
92
- def group_and_count(*columns)
93
- group(*columns.map{|c| unaliased_identifier(c)}).select(*(columns + [COUNT_OF_ALL_AS_COUNT]))
94
- end
95
-
96
- # Inserts multiple records into the associated table. This method can be
97
- # to efficiently insert a large amounts of records into a table. Inserts
98
- # are automatically wrapped in a transaction.
99
- #
100
- # This method is called with a columns array and an array of value arrays:
101
- #
102
- # dataset.import([:x, :y], [[1, 2], [3, 4]])
103
- #
104
- # This method also accepts a dataset instead of an array of value arrays:
105
- #
106
- # dataset.import([:x, :y], other_dataset.select(:a___x, :b___y))
107
- #
108
- # The method also accepts a :slice or :commit_every option that specifies
109
- # the number of records to insert per transaction. This is useful especially
110
- # when inserting a large number of records, e.g.:
111
- #
112
- # # this will commit every 50 records
113
- # dataset.import([:x, :y], [[1, 2], [3, 4], ...], :slice => 50)
114
- def import(columns, values, opts={})
115
- return @db.transaction{insert(columns, values)} if values.is_a?(Dataset)
116
-
117
- return if values.empty?
118
- raise(Error, IMPORT_ERROR_MSG) if columns.empty?
119
-
120
- if slice_size = opts[:commit_every] || opts[:slice]
121
- offset = 0
122
- loop do
123
- @db.transaction(opts){multi_insert_sql(columns, values[offset, slice_size]).each{|st| execute_dui(st)}}
124
- offset += slice_size
125
- break if offset >= values.length
126
- end
127
- else
128
- statements = multi_insert_sql(columns, values)
129
- @db.transaction{statements.each{|st| execute_dui(st)}}
130
- end
131
- end
132
-
133
- # Returns the interval between minimum and maximum values for the given
134
- # column.
135
- def interval(column)
136
- aggregate_dataset.get{max(column) - min(column)}
137
- end
138
-
139
- # Reverses the order and then runs first. Note that this
140
- # will not necessarily give you the last record in the dataset,
141
- # unless you have an unambiguous order. If there is not
142
- # currently an order for this dataset, raises an Error.
143
- def last(*args, &block)
144
- raise(Error, 'No order specified') unless @opts[:order]
145
- reverse.first(*args, &block)
146
- end
147
-
148
- # Maps column values for each record in the dataset (if a column name is
149
- # given), or performs the stock mapping functionality of Enumerable.
150
- # Raises an error if both an argument and block are given. Examples:
151
- #
152
- # ds.map(:id) => [1, 2, 3, ...]
153
- # ds.map{|r| r[:id] * 2} => [2, 4, 6, ...]
154
- def map(column=nil, &block)
155
- if column
156
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
157
- super(){|r| r[column]}
158
- else
159
- super(&block)
160
- end
161
- end
162
-
163
- # Returns the maximum value for the given column.
164
- def max(column)
165
- aggregate_dataset.get{max(column)}
166
- end
167
-
168
- # Returns the minimum value for the given column.
169
- def min(column)
170
- aggregate_dataset.get{min(column)}
171
- end
172
-
173
- # This is a front end for import that allows you to submit an array of
174
- # hashes instead of arrays of columns and values:
175
- #
176
- # dataset.multi_insert([{:x => 1}, {:x => 2}])
177
- #
178
- # Be aware that all hashes should have the same keys if you use this calling method,
179
- # otherwise some columns could be missed or set to null instead of to default
180
- # values.
181
- #
182
- # You can also use the :slice or :commit_every option that import accepts.
183
- def multi_insert(hashes, opts={})
184
- return if hashes.empty?
185
- columns = hashes.first.keys
186
- import(columns, hashes.map{|h| columns.map{|c| h[c]}}, opts)
187
- end
188
-
189
- # Returns a Range object made from the minimum and maximum values for the
190
- # given column.
191
- def range(column)
192
- if r = aggregate_dataset.select{[min(column).as(v1), max(column).as(v2)]}.first
193
- (r[:v1]..r[:v2])
194
- end
195
- end
196
-
197
- # Returns a hash with key_column values as keys and value_column values as
198
- # values. Similar to to_hash, but only selects the two columns.
199
- def select_hash(key_column, value_column)
200
- select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
201
- end
202
-
203
- # Selects the column given (either as an argument or as a block), and
204
- # returns an array of all values of that column in the dataset. If you
205
- # give a block argument that returns an array with multiple entries,
206
- # the contents of the resulting array are undefined.
207
- def select_map(column=nil, &block)
208
- ds = naked.ungraphed
209
- ds = if column
210
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
211
- ds.select(column)
212
- else
213
- ds.select(&block)
214
- end
215
- ds.map{|r| r.values.first}
216
- end
217
-
218
- # The same as select_map, but in addition orders the array by the column.
219
- def select_order_map(column=nil, &block)
220
- ds = naked.ungraphed
221
- ds = if column
222
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
223
- ds.select(column).order(unaliased_identifier(column))
224
- else
225
- ds.select(&block).order(&block)
226
- end
227
- ds.map{|r| r.values.first}
228
- end
229
-
230
- # Returns the first record in the dataset.
231
- def single_record
232
- clone(:limit=>1).each{|r| return r}
233
- nil
234
- end
235
-
236
- # Returns the first value of the first record in the dataset.
237
- # Returns nil if dataset is empty.
238
- def single_value
239
- if r = naked.ungraphed.single_record
240
- r.values.first
241
- end
242
- end
243
-
244
- # Returns the sum for the given column.
245
- def sum(column)
246
- aggregate_dataset.get{sum(column)}
247
- end
248
-
249
- # Returns a string in CSV format containing the dataset records. By
250
- # default the CSV representation includes the column titles in the
251
- # first line. You can turn that off by passing false as the
252
- # include_column_titles argument.
253
- #
254
- # This does not use a CSV library or handle quoting of values in
255
- # any way. If any values in any of the rows could include commas or line
256
- # endings, you shouldn't use this.
257
- def to_csv(include_column_titles = true)
258
- n = naked
259
- cols = n.columns
260
- csv = ''
261
- csv << "#{cols.join(COMMA_SEPARATOR)}\r\n" if include_column_titles
262
- n.each{|r| csv << "#{cols.collect{|c| r[c]}.join(COMMA_SEPARATOR)}\r\n"}
263
- csv
264
- end
265
-
266
- # Returns a hash with one column used as key and another used as value.
267
- # If rows have duplicate values for the key column, the latter row(s)
268
- # will overwrite the value of the previous row(s). If the value_column
269
- # is not given or nil, uses the entire hash as the value.
270
- def to_hash(key_column, value_column = nil)
271
- inject({}) do |m, r|
272
- m[r[key_column]] = value_column ? r[value_column] : r
273
- m
274
- end
275
- end
276
-
277
- # Creates a unique table alias that hasn't already been used in the dataset.
278
- # table_alias can be any type of object accepted by alias_symbol.
279
- # The symbol returned will be the implicit alias in the argument,
280
- # possibly appended with "_N" if the implicit alias has already been
281
- # used, where N is an integer starting at 0 and increasing until an
282
- # unused one is found.
283
- def unused_table_alias(table_alias)
284
- table_alias = alias_symbol(table_alias)
285
- used_aliases = []
286
- used_aliases += opts[:from].map{|t| alias_symbol(t)} if opts[:from]
287
- used_aliases += opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if opts[:join]
288
- if used_aliases.include?(table_alias)
289
- i = 0
290
- loop do
291
- ta = :"#{table_alias}_#{i}"
292
- return ta unless used_aliases.include?(ta)
293
- i += 1
294
- end
295
- else
296
- table_alias
297
- end
298
- end
299
-
300
- private
301
-
302
- # Return a plain symbol given a potentially qualified or aliased symbol,
303
- # specifying the symbol that is likely to be used as the hash key
304
- # for the column when records are returned.
305
- def hash_key_symbol(s)
306
- raise(Error, "#{s.inspect} is not a symbol") unless s.is_a?(Symbol)
307
- _, c, a = split_symbol(s)
308
- (a || c).to_sym
309
- end
310
-
311
- # Return the unaliased part of the identifier. Handles both
312
- # implicit aliases in symbols, as well as SQL::AliasedExpression
313
- # objects. Other objects are returned as is.
314
- def unaliased_identifier(c)
315
- case c
316
- when Symbol
317
- c_table, column, _ = split_symbol(c)
318
- c_table ? column.to_sym.qualify(c_table) : column.to_sym
319
- when SQL::AliasedExpression
320
- c.expression
321
- else
322
- c
323
- end
324
- end
325
- end
326
- end