sequel_core 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)