sequel_core 1.5.1 → 2.0.0

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 (68) hide show
  1. data/CHANGELOG +116 -0
  2. data/COPYING +19 -19
  3. data/README +83 -32
  4. data/Rakefile +9 -20
  5. data/bin/sequel +43 -112
  6. data/doc/cheat_sheet.rdoc +225 -0
  7. data/doc/dataset_filtering.rdoc +257 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
  9. data/lib/sequel_core/adapters/ado.rb +3 -1
  10. data/lib/sequel_core/adapters/db2.rb +4 -2
  11. data/lib/sequel_core/adapters/dbi.rb +127 -113
  12. data/lib/sequel_core/adapters/informix.rb +4 -2
  13. data/lib/sequel_core/adapters/jdbc.rb +5 -3
  14. data/lib/sequel_core/adapters/mysql.rb +112 -46
  15. data/lib/sequel_core/adapters/odbc.rb +5 -7
  16. data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
  17. data/lib/sequel_core/adapters/openbase.rb +3 -1
  18. data/lib/sequel_core/adapters/oracle.rb +11 -9
  19. data/lib/sequel_core/adapters/postgres.rb +261 -262
  20. data/lib/sequel_core/adapters/sqlite.rb +72 -22
  21. data/lib/sequel_core/connection_pool.rb +140 -73
  22. data/lib/sequel_core/core_ext.rb +201 -66
  23. data/lib/sequel_core/core_sql.rb +123 -153
  24. data/lib/sequel_core/database/schema.rb +156 -0
  25. data/lib/sequel_core/database.rb +321 -338
  26. data/lib/sequel_core/dataset/callback.rb +11 -12
  27. data/lib/sequel_core/dataset/convenience.rb +213 -240
  28. data/lib/sequel_core/dataset/pagination.rb +58 -43
  29. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
  30. data/lib/sequel_core/dataset/query.rb +41 -0
  31. data/lib/sequel_core/dataset/schema.rb +15 -0
  32. data/lib/sequel_core/dataset/sequelizer.rb +41 -373
  33. data/lib/sequel_core/dataset/sql.rb +741 -632
  34. data/lib/sequel_core/dataset.rb +183 -168
  35. data/lib/sequel_core/deprecated.rb +1 -169
  36. data/lib/sequel_core/exceptions.rb +24 -19
  37. data/lib/sequel_core/migration.rb +44 -52
  38. data/lib/sequel_core/object_graph.rb +43 -42
  39. data/lib/sequel_core/pretty_table.rb +71 -76
  40. data/lib/sequel_core/schema/generator.rb +163 -105
  41. data/lib/sequel_core/schema/sql.rb +250 -93
  42. data/lib/sequel_core/schema.rb +2 -8
  43. data/lib/sequel_core/sql.rb +394 -0
  44. data/lib/sequel_core/worker.rb +37 -27
  45. data/lib/sequel_core.rb +99 -45
  46. data/spec/adapters/informix_spec.rb +0 -1
  47. data/spec/adapters/mysql_spec.rb +177 -124
  48. data/spec/adapters/oracle_spec.rb +0 -1
  49. data/spec/adapters/postgres_spec.rb +98 -58
  50. data/spec/adapters/sqlite_spec.rb +45 -4
  51. data/spec/blockless_filters_spec.rb +269 -0
  52. data/spec/connection_pool_spec.rb +21 -18
  53. data/spec/core_ext_spec.rb +169 -19
  54. data/spec/core_sql_spec.rb +56 -49
  55. data/spec/database_spec.rb +78 -17
  56. data/spec/dataset_spec.rb +300 -428
  57. data/spec/migration_spec.rb +1 -1
  58. data/spec/object_graph_spec.rb +5 -11
  59. data/spec/rcov.opts +1 -1
  60. data/spec/schema_generator_spec.rb +16 -4
  61. data/spec/schema_spec.rb +89 -10
  62. data/spec/sequelizer_spec.rb +56 -56
  63. data/spec/spec.opts +0 -5
  64. data/spec/spec_config.rb +7 -0
  65. data/spec/spec_config.rb.example +5 -5
  66. data/spec/spec_helper.rb +6 -0
  67. data/spec/worker_spec.rb +1 -1
  68. metadata +78 -63
@@ -0,0 +1,394 @@
1
+ # This file holds classes and modules under Sequel that are related to SQL
2
+ # creation.
3
+
4
+ module Sequel
5
+ # The SQL module holds classes whose instances represent SQL fragments.
6
+ # It also holds modules that are included in core ruby classes that
7
+ # make Sequel a friendly DSL.
8
+ module SQL
9
+
10
+ ### Classes ###
11
+
12
+ # Base class for all SQL fragments
13
+ class Expression
14
+ # Returns self, because SQL::Expression already acts like
15
+ # LiteralString.
16
+ def lit
17
+ self
18
+ end
19
+ end
20
+
21
+ # Represents all columns in a given table, table.* in SQL
22
+ class ColumnAll < Expression
23
+ # The table containing the columns being selected
24
+ attr_reader :table
25
+
26
+ # Create an object with the given table
27
+ def initialize(table)
28
+ @table = table
29
+ end
30
+
31
+ # ColumnAll expressions are considered equivalent if they
32
+ # have the same class and string representation
33
+ def ==(x)
34
+ x.class == self.class && @table == x.table
35
+ end
36
+
37
+ # Delegate the creation of the resulting SQL to the given dataset,
38
+ # since it may be database dependent.
39
+ def to_s(ds)
40
+ ds.column_all_sql(self)
41
+ end
42
+ end
43
+
44
+ # Represents a generic column expression, used for specifying order
45
+ # and aliasing of columns.
46
+ class ColumnExpr < Expression
47
+ # Created readers for the left, operator, and right expression.
48
+ attr_reader :l, :op, :r
49
+
50
+ # Sets the attributes for the object to those given.
51
+ # The right (r) is not required, it is used when aliasing.
52
+ # The left (l) usually specifies the column name, and the
53
+ # operator (op) is usually 'ASC', 'DESC', or 'AS'.
54
+ def initialize(l, op, r = nil)
55
+ @l, @op, @r = l, op, r
56
+ end
57
+
58
+ # Delegate the creation of the resulting SQL to the given dataset,
59
+ # since it may be database dependent.
60
+ def to_s(ds)
61
+ ds.column_expr_sql(self)
62
+ end
63
+ end
64
+
65
+ # Represents a complex SQL expression, with a given operator and one
66
+ # or more attributes (which may also be ComplexExpressions, forming
67
+ # a tree). This class is the backbone of the blockless filter support in
68
+ # Sequel.
69
+ #
70
+ # Most ruby operators methods are defined via metaprogramming: +, -, /, *, <, >, <=,
71
+ # >=, & (AND), | (OR). This allows for a simple DSL after some core
72
+ # classes have been overloaded with ComplexExpressionMethods.
73
+ class ComplexExpression < Expression
74
+ # A hash of the opposite for each operator symbol, used for inverting
75
+ # objects.
76
+ OPERTATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
77
+ :<= => :>, :>= => :<, :'=' => :'!=' , :'!=' => :'=', :LIKE => :'NOT LIKE',
78
+ :'NOT LIKE' => :LIKE, :~ => :'!~', :'!~' => :~, :IN => :'NOT IN',
79
+ :'NOT IN' => :IN, :IS => :'IS NOT', :'IS NOT' => :IS, :'~*' => :'!~*',
80
+ :'!~*' => :'~*'}
81
+
82
+ MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
83
+ INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
84
+ STRING_OPERATORS = [:'||']
85
+ SEARCH_OPERATORS = [:LIKE, :'NOT LIKE', :~, :'!~', :'~*', :'!~*']
86
+ INCLUSION_OPERATORS = [:IN, :'NOT IN']
87
+ BOOLEAN_OPERATORS = [:AND, :OR]
88
+
89
+ # Collection of all equality/inequality operator symbols
90
+ EQUALITY_OPERATORS = [:'=', :'!=', :IS, :'IS NOT', *INEQUALITY_OPERATORS]
91
+
92
+ # Operator symbols that do not work on boolean SQL input
93
+ NO_BOOLEAN_INPUT_OPERATORS = MATHEMATICAL_OPERATORS + INEQUALITY_OPERATORS + STRING_OPERATORS
94
+
95
+ # Operator symbols that result in boolean SQL output
96
+ BOOLEAN_RESULT_OPERATORS = BOOLEAN_OPERATORS + EQUALITY_OPERATORS + SEARCH_OPERATORS + INCLUSION_OPERATORS + [:NOT]
97
+
98
+ # Literal SQL booleans that are not allowed
99
+ BOOLEAN_LITERALS = [true, false, nil]
100
+
101
+ # Hash of ruby operator symbols to SQL operators, used for method creation
102
+ BOOLEAN_OPERATOR_METHODS = {:& => :AND, :| =>:OR}
103
+
104
+ # Operator symbols that take exactly two arguments
105
+ TWO_ARITY_OPERATORS = EQUALITY_OPERATORS + SEARCH_OPERATORS + INCLUSION_OPERATORS
106
+
107
+ # Operator symbols that take one or more arguments
108
+ N_ARITY_OPERATORS = MATHEMATICAL_OPERATORS + BOOLEAN_OPERATORS + STRING_OPERATORS
109
+
110
+ # An array of args for this object
111
+ attr_reader :args
112
+
113
+ # The operator symbol for this object
114
+ attr_reader :op
115
+
116
+ # Set the operator symbol and arguments for this object to the ones given.
117
+ # Convert all args that are hashes or arrays with all two pairs to ComplexExpressions.
118
+ # Raise an error if the operator doesn't allow boolean input and a boolean argument is given.
119
+ # Raise an error if the wrong number of arguments for a given operator is used.
120
+ def initialize(op, *args)
121
+ args.collect! do |a|
122
+ case a
123
+ when Hash
124
+ a.sql_expr
125
+ when Array
126
+ a.all_two_pairs? ? a.sql_expr : a
127
+ else
128
+ a
129
+ end
130
+ end
131
+ if NO_BOOLEAN_INPUT_OPERATORS.include?(op)
132
+ args.each do |a|
133
+ if BOOLEAN_LITERALS.include?(a) || ((ComplexExpression === a) && BOOLEAN_RESULT_OPERATORS.include?(a.op))
134
+ raise(Sequel::Error, "cannot apply #{op} to a boolean expression")
135
+ end
136
+ end
137
+ end
138
+ case op
139
+ when *N_ARITY_OPERATORS
140
+ raise(Sequel::Error, 'mathematical and boolean operators require at least 1 argument') unless args.length >= 1
141
+ when *TWO_ARITY_OPERATORS
142
+ raise(Sequel::Error, '(in)equality operators require precisely 2 arguments') unless args.length == 2
143
+ when :NOT
144
+ raise(Sequel::Error, 'the NOT operator requires a single argument') unless args.length == 1
145
+ else
146
+ raise(Sequel::Error, "invalid operator #{op}")
147
+ end
148
+ @op = op
149
+ @args = args
150
+ end
151
+
152
+ # Take pairs of values (e.g. a hash or array of arrays of two pairs)
153
+ # and converts it to a ComplexExpression. The operator and args
154
+ # used depends on the case of the right (2nd) argument:
155
+ #
156
+ # * 0..10 - left >= 0 AND left <= 10
157
+ # * [1,2] - left IN (1,2)
158
+ # * nil - left IS NULL
159
+ # * /as/ - left ~ 'as'
160
+ # * :blah - left = blah
161
+ # * 'blah' - left = 'blah'
162
+ #
163
+ # If multiple arguments are given, they are joined with the op given (AND
164
+ # by default, OR possible). If negate is set to true,
165
+ # all subexpressions are inverted before used. Therefore, the following
166
+ # expressions are equivalent:
167
+ #
168
+ # ~from_value_pairs(hash)
169
+ # from_value_pairs(hash, :OR, true)
170
+ def self.from_value_pairs(pairs, op=:AND, negate=false)
171
+ pairs = pairs.collect do |l,r|
172
+ ce = case r
173
+ when Range
174
+ new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
175
+ when Array, ::Sequel::Dataset
176
+ new(:IN, l, r)
177
+ when NilClass
178
+ new(:IS, l, r)
179
+ when Regexp
180
+ like(l, r)
181
+ else
182
+ new(:'=', l, r)
183
+ end
184
+ negate ? ~ce : ce
185
+ end
186
+ pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
187
+ end
188
+
189
+ # Creates a SQL pattern match exprssion. left (l) is the SQL string we
190
+ # are matching against, and ces are the patterns we are matching.
191
+ # The match succeeds if any of the patterns match (SQL OR). Patterns
192
+ # can be given as strings or regular expressions. Strings will cause
193
+ # the SQL LIKE operator to be used, and should be supported by most
194
+ # databases. Regular expressions will probably only work on MySQL
195
+ # and PostgreSQL, and SQL regular expression syntax is not fully compatible
196
+ # with ruby regular expression syntax, so be careful if using regular
197
+ # expressions.
198
+ def self.like(l, *ces)
199
+ ces.collect! do |ce|
200
+ op, expr = Regexp === ce ? [ce.casefold? ? :'~*' : :~, ce.source] : [:LIKE, ce.to_s]
201
+ new(op, l, expr)
202
+ end
203
+ ces.length == 1 ? ces.at(0) : new(:OR, *ces)
204
+ end
205
+
206
+ # Invert the regular expression, if possible. If the expression cannot
207
+ # be inverted, raise an error. An inverted expression should match everything that the
208
+ # uninverted expression did not match, and vice-versa.
209
+ def ~
210
+ case @op
211
+ when *BOOLEAN_OPERATORS
212
+ self.class.new(OPERTATOR_INVERSIONS[@op], *@args.collect{|a| ComplexExpression === a ? ~a : ComplexExpression.new(:NOT, a)})
213
+ when *TWO_ARITY_OPERATORS
214
+ self.class.new(OPERTATOR_INVERSIONS[@op], *@args.dup)
215
+ when :NOT
216
+ @args.first
217
+ else
218
+ raise(Sequel::Error, "operator #{@op} cannot be inverted")
219
+ end
220
+ end
221
+
222
+ # Delegate the creation of the resulting SQL to the given dataset,
223
+ # since it may be database dependent.
224
+ def to_s(ds)
225
+ ds.complex_expression_sql(@op, @args)
226
+ end
227
+
228
+ BOOLEAN_OPERATOR_METHODS.each do |m, o|
229
+ define_method(m) do |ce|
230
+ raise(Sequel::Error, "cannot apply #{o} to a non-boolean expression") unless BOOLEAN_RESULT_OPERATORS.include?(@op)
231
+ super
232
+ end
233
+ end
234
+
235
+ (MATHEMATICAL_OPERATORS + INEQUALITY_OPERATORS).each do |o|
236
+ define_method(o) do |ce|
237
+ raise(Sequel::Error, "cannot apply #{o} to a boolean expression") unless NO_BOOLEAN_INPUT_OPERATORS.include?(@op)
238
+ super
239
+ end
240
+ end
241
+ end
242
+
243
+ # Represents an SQL function call.
244
+ class Function < Expression
245
+ # The array of arguments to pass to the function (may be blank)
246
+ attr_reader :args
247
+
248
+ # The SQL function to call
249
+ attr_reader :f
250
+
251
+ # Set the attributes to the given arguments
252
+ def initialize(f, *args)
253
+ @f, @args = f, args
254
+ end
255
+
256
+ # Functions are considered equivalent if they
257
+ # have the same class, function, and arguments.
258
+ def ==(x)
259
+ x.class == self.class && @f == x.f && @args == x.args
260
+ end
261
+
262
+ # Delegate the creation of the resulting SQL to the given dataset,
263
+ # since it may be database dependent.
264
+ def to_s(ds)
265
+ ds.function_sql(self)
266
+ end
267
+ end
268
+
269
+ # Represents a qualified (column with table) reference. Used when
270
+ # joining tables to disambiguate columns.
271
+ class QualifiedColumnRef < Expression
272
+ # The table and column to reference
273
+ attr_reader :table, :column
274
+
275
+ # Set the attributes to the given arguments
276
+ def initialize(table, column)
277
+ @table, @column = table, column
278
+ end
279
+
280
+ # Delegate the creation of the resulting SQL to the given dataset,
281
+ # since it may be database dependent.
282
+ def to_s(ds)
283
+ ds.qualified_column_ref_sql(self)
284
+ end
285
+ end
286
+
287
+ # Represents an SQL array access, with multiple possible arguments.
288
+ class Subscript < Expression
289
+ # The SQL array column
290
+ attr_reader :f
291
+
292
+ # The array of subscripts to use (should be an array of numbers)
293
+ attr_reader :sub
294
+
295
+ # Set the attributes to the given arguments
296
+ def initialize(f, sub)
297
+ @f, @sub = f, sub
298
+ end
299
+
300
+ # Create a new subscript appending the given subscript(s)
301
+ # the the current array of subscripts.
302
+ def |(sub)
303
+ Subscript.new(@f, @sub + Array(sub))
304
+ end
305
+
306
+ # Delegate the creation of the resulting SQL to the given dataset,
307
+ # since it may be database dependent.
308
+ def to_s(ds)
309
+ ds.subscript_sql(self)
310
+ end
311
+ end
312
+
313
+ ### Modules ###
314
+
315
+ # Module included in core classes giving them a simple and easy DSL
316
+ # for creation of ComplexExpressions.
317
+ #
318
+ # Most ruby operators methods are defined via metaprogramming: +, -, /, *, <, >, <=,
319
+ # >=, & (AND), | (OR).
320
+ module ComplexExpressionMethods
321
+ NO_BOOLEAN_INPUT_OPERATORS = ComplexExpression::NO_BOOLEAN_INPUT_OPERATORS
322
+ BOOLEAN_RESULT_OPERATORS = ComplexExpression::BOOLEAN_RESULT_OPERATORS
323
+ BOOLEAN_OPERATOR_METHODS = ComplexExpression::BOOLEAN_OPERATOR_METHODS
324
+
325
+ BOOLEAN_OPERATOR_METHODS.each do |m, o|
326
+ define_method(m) do |ce|
327
+ raise(Sequel::Error, "cannot apply #{o} to a non-boolean expression") if (ComplexExpression === ce) && !BOOLEAN_RESULT_OPERATORS.include?(ce.op)
328
+ ComplexExpression.new(o, self, ce)
329
+ end
330
+ end
331
+
332
+ (ComplexExpression::MATHEMATICAL_OPERATORS + ComplexExpression::INEQUALITY_OPERATORS).each do |o|
333
+ define_method(o) do |ce|
334
+ raise(Sequel::Error, "cannot apply #{o} to a boolean expression") if (ComplexExpression === ce) && !NO_BOOLEAN_INPUT_OPERATORS.include?(ce.op)
335
+ ComplexExpression.new(o, self, ce)
336
+ end
337
+ end
338
+
339
+ # Create a new ComplexExpression with NOT, representing the inversion of whatever self represents.
340
+ def ~
341
+ ComplexExpression.new(:NOT, self)
342
+ end
343
+
344
+ # Create a ComplexExpression pattern match of self with the given patterns.
345
+ def like(*ces)
346
+ ComplexExpression.like(self, *ces)
347
+ end
348
+ end
349
+
350
+ # Holds methods that should be called on columns only.
351
+ module ColumnMethods
352
+ AS = 'AS'.freeze
353
+ DESC = 'DESC'.freeze
354
+ ASC = 'ASC'.freeze
355
+
356
+ # Create an SQL column alias of the receiving column to the given alias.
357
+ def as(a)
358
+ ColumnExpr.new(self, AS, a)
359
+ end
360
+
361
+ # Mark the receiving SQL column as sorting in a descending fashion.
362
+ def desc
363
+ ColumnExpr.new(self, DESC)
364
+ end
365
+
366
+ # Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
367
+ def asc
368
+ ColumnExpr.new(self, ASC)
369
+ end
370
+
371
+ # Cast the reciever to the given SQL type
372
+ def cast_as(t)
373
+ t = t.to_s.lit if t.is_a?(Symbol)
374
+ Sequel::SQL::Function.new(:cast, self.as(t))
375
+ end
376
+ end
377
+
378
+ class Expression
379
+ # Include the modules in Expression, couldn't be done
380
+ # earlier due to cyclic dependencies.
381
+ include ColumnMethods
382
+ include ComplexExpressionMethods
383
+ end
384
+ end
385
+
386
+ # LiteralString is used to represent literal SQL expressions. An
387
+ # LiteralString is copied verbatim into an SQL statement. Instances of
388
+ # LiteralString can be created by calling String#lit.
389
+ # LiteralStrings can use all of the SQL::ColumnMethods and the
390
+ # SQL::ComplexExpressionMethods.
391
+ class LiteralString < ::String
392
+ include SQL::ComplexExpressionMethods
393
+ end
394
+ end
@@ -1,12 +1,14 @@
1
- require "thread"
2
-
3
1
  module Sequel
4
-
2
+ # A Worker is a thread that accepts jobs off a work queue and
3
+ # processes them in the background. It accepts an optional
4
+ # database where it wruns all of its work inside a transaction.
5
5
  class Worker < Thread
6
-
7
6
  attr_reader :queue
8
7
  attr_reader :errors
9
8
 
9
+ # Setup the interal variables. If a database is given,
10
+ # run the thread inside a database transaction. Continue
11
+ # to work until #join is called.
10
12
  def initialize(db = nil)
11
13
  @queue = Queue.new
12
14
  @errors = []
@@ -16,17 +18,8 @@ module Sequel
16
18
  db ? super {db.transaction {t.work}} : super {t.work}
17
19
  end
18
20
 
19
- def work
20
- loop {next_job}
21
- rescue Sequel::Error::WorkerStop # signals the worker thread to stop
22
- ensure
23
- raise Sequel::Error::Rollback if @transaction && !@errors.empty?
24
- end
25
-
26
- def busy?
27
- @cur || !@queue.empty?
28
- end
29
-
21
+ # Add a job to the queue, specified either as a proc argument
22
+ # or as a block.
30
23
  def async(proc = nil, &block)
31
24
  @queue << (proc || block)
32
25
  self
@@ -34,25 +27,42 @@ module Sequel
34
27
  alias_method :add, :async
35
28
  alias_method :<<, :async
36
29
 
30
+ # Whether the worker is actively working and/or has work scheduled
31
+ def busy?
32
+ @cur || !@queue.empty?
33
+ end
34
+
35
+ # Wait until the worker is no longer busy and then stop working.
37
36
  def join
38
- while busy?
39
- sleep 0.1
40
- end
37
+ sleep(0.1) while busy?
41
38
  self.raise Error::WorkerStop
42
39
  super
43
40
  end
44
41
 
42
+ # Continually get jobs from the work queue and process them.
43
+ def work
44
+ begin
45
+ loop {next_job}
46
+ rescue Sequel::Error::WorkerStop # signals the worker thread to stop
47
+ ensure
48
+ raise Sequel::Error::Rollback if @transaction && !@errors.empty?
49
+ end
50
+ end
51
+
45
52
  private
53
+
54
+ # Get the next job from the work queue and process it.
46
55
  def next_job
47
- @cur = @queue.pop
48
- @cur.call
49
- rescue Error::WorkerStop => e
50
- raise e
51
- rescue Exception => e
52
- @errors << e
53
- ensure
54
- @cur = nil
56
+ begin
57
+ @cur = @queue.pop
58
+ @cur.call
59
+ rescue Error::WorkerStop => e
60
+ raise e
61
+ rescue Exception => e
62
+ @errors << e
63
+ ensure
64
+ @cur = nil
65
+ end
55
66
  end
56
67
  end
57
-
58
68
  end
data/lib/sequel_core.rb CHANGED
@@ -1,52 +1,106 @@
1
- require "metaid"
2
- require "bigdecimal"
3
- require "bigdecimal/util"
1
+ %w'bigdecimal bigdecimal/util date enumerator thread time uri yaml'.each do |f|
2
+ require f
3
+ end
4
+ %w"core_ext sql core_sql connection_pool exceptions pretty_table
5
+ dataset migration schema database worker object_graph".each do |f|
6
+ require "sequel_core/#{f}"
7
+ end
4
8
 
5
- files = %w[
6
- deprecated core_ext core_sql connection_pool exceptions pretty_table
7
- dataset migration schema database worker object_graph
8
- ]
9
- dir = File.join(File.dirname(__FILE__), "sequel_core")
10
- files.each {|f| require(File.join(dir, f))}
9
+ # Top level module for Sequel
10
+ #
11
+ # There are some class methods that are added via metaprogramming, one for
12
+ # each supported adapter. For example:
13
+ #
14
+ # DB = Sequel.sqlite # Memory database
15
+ # DB = Sequel.sqlite('blog.db')
16
+ # DB = Sequel.postgres('database_name', :user=>'user', \
17
+ # :password=>'password', :host=>'host', :port=>5432, \
18
+ # :max_connections=>10)
19
+ #
20
+ # If a block is given to these meethods, it is passed the opened Database
21
+ # object, which is closed when the block exits. For example:
22
+ #
23
+ # Sequel.sqlite('blog.db'){|db| puts db.users.count}
24
+ module Sequel
25
+ # Creates a new database object based on the supplied connection string
26
+ # and optional arguments. The specified scheme determines the database
27
+ # class used, and the rest of the string specifies the connection options.
28
+ # For example:
29
+ #
30
+ # DB = Sequel.connect('sqlite:/') # Memory database
31
+ # DB = Sequel.connect('sqlite://blog.db') # ./blog.db
32
+ # DB = Sequel.connect('sqlite:///blog.db') # /blog.db
33
+ # DB = Sequel.connect('postgres://user:password@host:port/database_name')
34
+ # DB = Sequel.connect('sqlite:///blog.db', :max_connections=>10)
35
+ #
36
+ # If a block is given, it is passed the opened Database object, which is
37
+ # closed when the block exits. For example:
38
+ #
39
+ # Sequel.connect('sqlite://blog.db'){|db| puts db.users.count}
40
+ def self.connect(*args, &block)
41
+ Database.connect(*args, &block)
42
+ end
43
+ metaalias :open, :connect
44
+
45
+ # Set whether to quote identifiers for all databases by default. By default,
46
+ # Sequel quotes identifiers in all SQL strings, so to turn that off:
47
+ #
48
+ # Sequel.quote_identifiers = false
49
+ def self.quote_identifiers=(value)
50
+ Database.quote_identifiers = value
51
+ end
52
+
53
+ # Set whether to set the single threaded mode for all databases by default. By default,
54
+ # Sequel uses a threadsafe connection pool, which isn't as fast as the
55
+ # single threaded connection pool. If your program will only have one thread,
56
+ # and speed is a priority, you may want to set this to true:
57
+ #
58
+ # Sequel.single_threaded = true
59
+ #
60
+ # Note that some database adapters (e.g. MySQL) have issues with single threaded mode if
61
+ # you try to perform more than one query simultaneously. For example, the
62
+ # following code will not work well in single threaded mode on MySQL:
63
+ #
64
+ # DB[:items].each{|i| DB[:nodes].filter(:item_id=>i[:id]).each{|n| puts "#{i} #{n}"}}
65
+ #
66
+ # Basically, you can't issue another query inside a call to Dataset#each in single
67
+ # threaded mode. There is a fairly easy fix, just use Dataset#all inside
68
+ # Dataset#each for the outer query:
69
+ #
70
+ # DB[:items].all{|i| DB[:nodes].filter(:item_id=>i[:id]).each{|n| puts "#{i} #{n}"}}
71
+ #
72
+ # Dataset#all gets all of the returned objects before calling the block, so the query
73
+ # isn't left open. Some of the adapters do this internally, and thus don't have a
74
+ # problem issuing queries inside of Dataset#each.
75
+ def self.single_threaded=(value)
76
+ Database.single_threaded = value
77
+ end
78
+
79
+ ### Private Class Methods ###
11
80
 
12
- module Sequel #:nodoc:
13
- Deprecation.deprecation_message_stream = STDERR
14
- #Deprecation.print_tracebacks = true
15
- class << self
16
- # call-seq:
17
- # Sequel::Database.connect(conn_string)
18
- # Sequel.connect(conn_string)
19
- # Sequel.open(conn_string)
20
- #
21
- # Creates a new database object based on the supplied connection string.
22
- # The specified scheme determines the database class used, and the rest
23
- # of the string specifies the connection options. For example:
24
- # DB = Sequel.open 'sqlite:///blog.db'
25
- def connect(*args)
26
- Database.connect(*args)
27
- end
28
- alias_method :open, :connect
29
-
30
- def single_threaded=(value)
31
- Database.single_threaded = value
81
+ # Helper method that the database adapter class methods that are added to Sequel via
82
+ # metaprogramming use to parse arguments.
83
+ def self.adapter_method(adapter, *args, &block) # :nodoc:
84
+ raise(::Sequel::Error, "Wrong number of arguments, 0-2 arguments valid") if args.length > 2
85
+ opts = {:adapter=>adapter.to_sym}
86
+ opts[:database] = args.shift if args.length >= 1 && !(args[0].is_a?(Hash))
87
+ if Hash === (arg = args[0])
88
+ opts.merge!(arg)
89
+ elsif !arg.nil?
90
+ raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
32
91
  end
92
+ connect(opts, &block)
93
+ end
33
94
 
34
- def self.def_adapter_method(*adapters)
35
- adapters.each do |adapter|
36
- define_method(adapter) do |*args|
37
- raise(::Sequel::Error, "Wrong number of arguments, 0-2 arguments valid") if args.length > 2
38
- opts = {:adapter=>adapter.to_sym}
39
- opts[:database] = args.shift if args.length >= 1 && !(args[0].is_a?(Hash))
40
- if Hash === (arg = args[0])
41
- opts.merge!(arg)
42
- elsif !arg.nil?
43
- raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
44
- end
45
- ::Sequel::Database.connect(opts)
46
- end
47
- end
95
+ # Method that adds a database adapter class method to Sequel that calls
96
+ # Sequel.adapter_method.
97
+ def self.def_adapter_method(*adapters) # :nodoc:
98
+ adapters.each do |adapter|
99
+ instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end")
48
100
  end
49
-
50
- def_adapter_method(*Database::ADAPTERS)
51
101
  end
102
+ metaprivate :adapter_method, :def_adapter_method
103
+
104
+ # Add the database adapter class methods to Sequel via metaprogramming
105
+ def_adapter_method(*Database::ADAPTERS)
52
106
  end
@@ -1,4 +1,3 @@
1
- require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
1
  require File.join(File.dirname(__FILE__), '../spec_helper.rb')
3
2
 
4
3
  unless defined?(INFORMIX_DB)