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
@@ -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
+