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
@@ -1,37 +1,42 @@
1
1
  module Sequel
2
2
  # Represents an error raised in Sequel code.
3
- class Error < StandardError
3
+ class Error < ::StandardError
4
4
 
5
- # Error raised when an invalid statement is executed.
6
- class InvalidStatement < Error; end
7
-
8
- # Rollback is a special error used to rollback a transactions.
9
- # A transaction block will catch this error and wont pass further up the stack.
10
- class Rollback < Error ; end
5
+ # Raised when Sequel is unable to load a specified adapter.
6
+ class AdapterNotFound < Error ; end
11
7
 
12
- # Represents an invalid value stored in the database.
13
- class InvalidValue < Error ; end
8
+ # Raise when an invalid expression is encountered inside a block filter.
9
+ class InvalidExpression < Error; end
14
10
 
15
- # Represents an Invalid transform.
16
- class InvalidTransform < Error ; end
11
+ # Represents an Invalid filter.
12
+ class InvalidFilter < Error ; end
17
13
 
14
+ # Represents an invalid join type.
15
+ class InvalidJoinType < Error ; end
16
+
18
17
  # Raised on an invalid operation.
19
18
  class InvalidOperation < Error; end
20
19
 
21
- # Represents an Invalid filter.
22
- class InvalidFilter < Error ; end
20
+ # Error raised when an invalid statement is executed.
21
+ class InvalidStatement < Error; end
22
+
23
+ # Represents an Invalid transform.
24
+ class InvalidTransform < Error ; end
23
25
 
24
- class InvalidExpression < Error; end
26
+ # Represents an invalid value stored in the database.
27
+ class InvalidValue < Error ; end
25
28
 
26
29
  # Represents an attempt to performing filter operations when no filter has been specified yet.
27
30
  class NoExistingFilter < Error ; end
28
31
 
29
- # Represents an invalid join type.
30
- class InvalidJoinType < Error ; end
32
+ # There was an error while waiting on a connection from the connection pool
33
+ class PoolTimeoutError < Error ; end
31
34
 
32
- class WorkerStop < RuntimeError ; end
35
+ # Rollback is a special error used to rollback a transactions.
36
+ # A transaction block will catch this error and won't pass further up the stack.
37
+ class Rollback < Error ; end
33
38
 
34
- # Raised when Sequel is unable to load a specified adapter.
35
- class AdapterNotFound < Error ; end
39
+ # Should be raised inside a worker loop to tell it to stop working.
40
+ class WorkerStop < RuntimeError ; end
36
41
  end
37
42
  end
@@ -1,6 +1,3 @@
1
- # The migration code is based on work by Florian Aßmann:
2
- # http://code.google.com/p/ruby-sequel/issues/detail?id=23
3
-
4
1
  module Sequel
5
2
  # The Migration class describes a database migration that can be reversed.
6
3
  # The migration looks very similar to ActiveRecord (Rails) migrations, e.g.:
@@ -23,28 +20,14 @@ module Sequel
23
20
  # To apply a migration to a database, you can invoke the #apply with
24
21
  # the target database instance and the direction :up or :down, e.g.:
25
22
  #
26
- # DB = Sequel.open ('sqlite:///mydb')
23
+ # DB = Sequel.open ('sqlite://mydb')
27
24
  # CreateSessions.apply(DB, :up)
28
- #
29
25
  class Migration
30
26
  # Creates a new instance of the migration and sets the @db attribute.
31
27
  def initialize(db)
32
28
  @db = db
33
29
  end
34
30
 
35
- # Adds the new migration class to the list of Migration descendants.
36
- def self.inherited(base)
37
- descendants << base
38
- end
39
-
40
- # Returns the list of Migration descendants.
41
- def self.descendants
42
- @descendants ||= []
43
- end
44
-
45
- def up; end #:nodoc:
46
- def down; end #:nodoc:
47
-
48
31
  # Applies the migration to the supplied database in the specified
49
32
  # direction.
50
33
  def self.apply(db, direction)
@@ -59,9 +42,27 @@ module Sequel
59
42
  end
60
43
  end
61
44
 
45
+ # Returns the list of Migration descendants.
46
+ def self.descendants
47
+ @descendants ||= []
48
+ end
49
+
50
+ # Adds the new migration class to the list of Migration descendants.
51
+ def self.inherited(base)
52
+ descendants << base
53
+ end
54
+
55
+ # The default down action does nothing
56
+ def down
57
+ end
58
+
62
59
  # Intercepts method calls intended for the database and sends them along.
63
60
  def method_missing(method_sym, *args, &block)
64
- @db.send method_sym, *args, &block
61
+ @db.send(method_sym, *args, &block)
62
+ end
63
+
64
+ # The default up action does nothing
65
+ def up
65
66
  end
66
67
  end
67
68
 
@@ -96,8 +97,9 @@ module Sequel
96
97
  # To migrate the database from version 1 to version 5:
97
98
  #
98
99
  # Sequel::Migrator.apply(DB, '.', 5, 1)
99
- #
100
100
  module Migrator
101
+ MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/.freeze
102
+
101
103
  # Migrates the supplied database in the specified directory from the
102
104
  # current version to the target version. If no current version is
103
105
  # supplied, it is extracted from a schema_info table. The schema_info
@@ -121,6 +123,19 @@ module Sequel
121
123
  target
122
124
  end
123
125
 
126
+ # Gets the current migration version stored in the database. If no version
127
+ # number is stored, 0 is returned.
128
+ def self.get_current_migration_version(db)
129
+ r = schema_info_dataset(db).first
130
+ r ? r[:version] : 0
131
+ end
132
+
133
+ # Returns the latest version available in the specified directory.
134
+ def self.latest_migration_version(directory)
135
+ l = migration_files(directory).last
136
+ l ? File.basename(l).to_i : nil
137
+ end
138
+
124
139
  # Returns a list of migration classes filtered for the migration range and
125
140
  # ordered according to the migration direction.
126
141
  def self.migration_classes(directory, target, current, direction)
@@ -142,50 +157,27 @@ module Sequel
142
157
  classes
143
158
  end
144
159
 
145
- MIGRATION_FILE_PATTERN = '[0-9][0-9][0-9]_*.rb'.freeze
146
-
147
160
  # Returns any found migration files in the supplied directory.
148
161
  def self.migration_files(directory, range = nil)
149
- pattern = File.join(directory, MIGRATION_FILE_PATTERN)
150
- files = Dir[pattern].inject([]) do |m, path|
151
- m[File.basename(path).to_i] = path
152
- m
162
+ files = []
163
+ Dir.new(directory).each do |file|
164
+ files[file.to_i] = File.join(directory, file) if MIGRATION_FILE_PATTERN.match(file)
153
165
  end
154
166
  filtered = range ? files[range] : files
155
167
  filtered ? filtered.compact : []
156
168
  end
157
169
 
158
- # Returns the latest version available in the specified directory.
159
- def self.latest_migration_version(directory)
160
- l = migration_files(directory).last
161
- l ? File.basename(l).to_i : nil
162
- end
163
-
164
- # Gets the current migration version stored in the database. If no version
165
- # number is stored, 0 is returned.
166
- def self.get_current_migration_version(db)
167
- r = schema_info_dataset(db).first
168
- r ? r[:version] : 0
170
+ # Returns the dataset for the schema_info table. If no such table
171
+ # exists, it is automatically created.
172
+ def self.schema_info_dataset(db)
173
+ db.create_table(:schema_info) {integer :version} unless db.table_exists?(:schema_info)
174
+ db[:schema_info]
169
175
  end
170
176
 
171
177
  # Sets the current migration version stored in the database.
172
178
  def self.set_current_migration_version(db, version)
173
179
  dataset = schema_info_dataset(db)
174
- if dataset.first
175
- dataset.update(:version => version)
176
- else
177
- dataset << {:version => version}
178
- end
179
- end
180
-
181
- # Returns the dataset for the schema_info table. If no such table
182
- # exists, it is automatically created.
183
- def self.schema_info_dataset(db)
184
- unless db.table_exists?(:schema_info)
185
- db.create_table(:schema_info) {integer :version}
186
- end
187
-
188
- db[:schema_info]
180
+ dataset.send(dataset.first ? :update : :<<, :version => version)
189
181
  end
190
182
  end
191
183
  end
@@ -68,7 +68,7 @@ module Sequel
68
68
  raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
69
69
 
70
70
  # Join the table early in order to avoid cloning the dataset twice
71
- ds = join_table(options[:join_type] || :left_outer, table == table_alias ? table : "#{table} #{table_alias}", join_conditions)
71
+ ds = join_table(options[:join_type] || :left_outer, table, join_conditions, table_alias)
72
72
  opts = ds.opts
73
73
 
74
74
  # Whether to include the table in the result set
@@ -162,50 +162,51 @@ module Sequel
162
162
  end
163
163
 
164
164
  private
165
- # Fetch the rows, split them into component table parts,
166
- # tranform and run the row_proc on each part (if applicable),
167
- # and yield a hash of the parts.
168
- def graph_each(opts, &block)
169
- # Reject tables with nil datasets, as they are excluded from
170
- # the result set
171
- datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
172
- # Get just the list of table aliases into a local variable, for speed
173
- table_aliases = datasets.collect{|ta,ds| ta}
174
- # Get an array of arrays, one for each dataset, with
175
- # the necessary information about each dataset, for speed
176
- datasets = datasets.collect do |ta, ds|
177
- [ta, ds, ds.instance_variable_get(:@transform), ds.row_proc]
165
+
166
+ # Fetch the rows, split them into component table parts,
167
+ # tranform and run the row_proc on each part (if applicable),
168
+ # and yield a hash of the parts.
169
+ def graph_each(opts, &block)
170
+ # Reject tables with nil datasets, as they are excluded from
171
+ # the result set
172
+ datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
173
+ # Get just the list of table aliases into a local variable, for speed
174
+ table_aliases = datasets.collect{|ta,ds| ta}
175
+ # Get an array of arrays, one for each dataset, with
176
+ # the necessary information about each dataset, for speed
177
+ datasets = datasets.collect do |ta, ds|
178
+ [ta, ds, ds.instance_variable_get(:@transform), ds.row_proc]
179
+ end
180
+ # Use the manually set graph aliases, if any, otherwise
181
+ # use the ones automatically created by .graph
182
+ column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
183
+ fetch_rows(select_sql(opts)) do |r|
184
+ graph = {}
185
+ # Create the sub hashes, one per table
186
+ table_aliases.each{|ta| graph[ta]={}}
187
+ # Split the result set based on the column aliases
188
+ # If there are columns in the result set that are
189
+ # not in column_aliases, they are ignored
190
+ column_aliases.each do |col_alias, tc|
191
+ ta, column = tc
192
+ graph[ta][column] = r[col_alias]
178
193
  end
179
- # Use the manually set graph aliases, if any, otherwise
180
- # use the ones automatically created by .graph
181
- column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
182
- fetch_rows(select_sql(opts)) do |r|
183
- graph = {}
184
- # Create the sub hashes, one per table
185
- table_aliases.each{|ta| graph[ta]={}}
186
- # Split the result set based on the column aliases
187
- # If there are columns in the result set that are
188
- # not in column_aliases, they are ignored
189
- column_aliases.each do |col_alias, tc|
190
- ta, column = tc
191
- graph[ta][column] = r[col_alias]
192
- end
193
- # For each dataset, transform and run the row
194
- # row_proc if applicable
195
- datasets.each do |ta,ds,tr,rp|
196
- g = graph[ta]
197
- graph[ta] = if g.values.any?
198
- g = ds.transform_load(g) if tr
199
- g = rp[g] if rp
200
- g
201
- else
202
- nil
203
- end
194
+ # For each dataset, transform and run the row
195
+ # row_proc if applicable
196
+ datasets.each do |ta,ds,tr,rp|
197
+ g = graph[ta]
198
+ graph[ta] = if g.values.any?
199
+ g = ds.transform_load(g) if tr
200
+ g = rp[g] if rp
201
+ g
202
+ else
203
+ nil
204
204
  end
205
-
206
- yield graph
207
205
  end
208
- self
206
+
207
+ yield graph
209
208
  end
209
+ self
210
+ end
210
211
  end
211
212
  end
@@ -1,76 +1,71 @@
1
- module Sequel
2
- module PrettyTable
3
- # Prints nice-looking plain-text tables
4
- #
5
- # +--+-------+
6
- # |id|name |
7
- # |--+-------|
8
- # |1 |fasdfas|
9
- # |2 |test |
10
- # +--+-------+
11
- def self.print(records, columns = nil) # records is an array of hashes
12
- columns ||= records_columns(records)
13
- sizes = column_sizes(records, columns)
14
-
15
- puts separator_line(columns, sizes)
16
- puts header_line(columns, sizes)
17
- puts separator_line(columns, sizes)
18
- records.each {|r| puts data_line(columns, sizes, r)}
19
- puts separator_line(columns, sizes)
20
- end
21
- end
22
- class << PrettyTable
23
- private
24
- def records_columns(records)
25
- columns = []
26
- records.each do |r|
27
- if Array === r && (k = r.keys)
28
- return k
29
- elsif Hash === r
30
- r.keys.each {|k| columns << k unless columns.include?(k)}
31
- end
32
- end
33
- columns
34
- end
35
-
36
- def column_sizes(records, columns)
37
- sizes = Hash.new {0}
38
- columns.each do |c|
39
- s = c.to_s.size
40
- sizes[c.to_sym] = s if s > sizes[c.to_sym]
41
- end
42
- records.each do |r|
43
- columns.each do |c|
44
- s = r[c].to_s.size
45
- sizes[c.to_sym] = s if s > sizes[c.to_sym]
46
- end
47
- end
48
- sizes
49
- end
50
-
51
- def separator_line(columns, sizes)
52
- l = ''
53
- '+' + columns.map {|c| '-' * sizes[c]}.join('+') + '+'
54
- end
55
-
56
- def format_cell(size, v)
57
- case v
58
- when Bignum, Fixnum
59
- "%#{size}d" % v
60
- when Float
61
- "%#{size}g" % v
62
- else
63
- "%-#{size}s" % v.to_s
64
- end
65
- end
66
-
67
- def data_line(columns, sizes, record)
68
- '|' + columns.map {|c| format_cell(sizes[c], record[c])}.join('|') + '|'
69
- end
70
-
71
- def header_line(columns, sizes)
72
- '|' + columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') + '|'
73
- end
74
- end
75
- end
76
-
1
+ module Sequel
2
+ module PrettyTable
3
+ # Prints nice-looking plain-text tables via puts
4
+ #
5
+ # +--+-------+
6
+ # |id|name |
7
+ # |--+-------|
8
+ # |1 |fasdfas|
9
+ # |2 |test |
10
+ # +--+-------+
11
+ def self.print(records, columns = nil) # records is an array of hashes
12
+ columns ||= records.first.keys.sort_by{|x|x.to_s}
13
+ sizes = column_sizes(records, columns)
14
+ sep_line = separator_line(columns, sizes)
15
+
16
+ puts sep_line
17
+ puts header_line(columns, sizes)
18
+ puts sep_line
19
+ records.each {|r| puts data_line(columns, sizes, r)}
20
+ puts sep_line
21
+ end
22
+
23
+ ### Private Module Methods ###
24
+
25
+ # Hash of the maximum size of the value for each column
26
+ def self.column_sizes(records, columns) # :nodoc:
27
+ sizes = Hash.new {0}
28
+ columns.each do |c|
29
+ s = c.to_s.size
30
+ sizes[c.to_sym] = s if s > sizes[c.to_sym]
31
+ end
32
+ records.each do |r|
33
+ columns.each do |c|
34
+ s = r[c].to_s.size
35
+ sizes[c.to_sym] = s if s > sizes[c.to_sym]
36
+ end
37
+ end
38
+ sizes
39
+ end
40
+
41
+ # String for each data line
42
+ def self.data_line(columns, sizes, record) # :nodoc:
43
+ '|' << columns.map {|c| format_cell(sizes[c], record[c])}.join('|') << '|'
44
+ end
45
+
46
+ # Format the value so it takes up exactly size characters
47
+ def self.format_cell(size, v) # :nodoc:
48
+ case v
49
+ when Bignum, Fixnum
50
+ "%#{size}d" % v
51
+ when Float
52
+ "%#{size}g" % v
53
+ else
54
+ "%-#{size}s" % v.to_s
55
+ end
56
+ end
57
+
58
+ # String for header line
59
+ def self.header_line(columns, sizes) # :nodoc:
60
+ '|' << columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') << '|'
61
+ end
62
+
63
+ # String for separtor line
64
+ def self.separator_line(columns, sizes) # :nodoc:
65
+ '+' << columns.map {|c| '-' * sizes[c]}.join('+') << '+'
66
+ end
67
+
68
+ metaprivate :column_sizes, :data_line, :format_cell, :header_line, :separator_line
69
+ end
70
+ end
71
+