sequel 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
data/CHANGELOG
CHANGED
@@ -1,3 +1,103 @@
|
|
1
|
+
=== 3.1.0 (2009-06-04)
|
2
|
+
|
3
|
+
* Require the classes match to consider an association a reciprocal (jeremyevans) (#270)
|
4
|
+
|
5
|
+
* Make Migrator work correctly with file names like 001_873465873465873465_some_name.rb (jeremyevans) (#267)
|
6
|
+
|
7
|
+
* Add Dataset#qualify_to and #qualify_to_first_source, for qualifying unqualified identifiers in the dataset (jeremyevans)
|
8
|
+
|
9
|
+
* All the use of #sql_subscript on most SQL::* objects, and support non-integer subscript values (jeremyevans)
|
10
|
+
|
11
|
+
* Add reflection.rdoc file which explains and gives examples of many of Sequel's reflection methods (jeremyevans)
|
12
|
+
|
13
|
+
* Add many_through_many plugin, allowing you to construct an association to multiple objects through multiple join tables (jeremyevans)
|
14
|
+
|
15
|
+
* Add the :cartesian_product_number option to associations, for specifying if they can cause a cartesian product (jeremyevans)
|
16
|
+
|
17
|
+
* Make :eager_graph association option work correctly when lazily loading many_to_many associations (jeremyevans)
|
18
|
+
|
19
|
+
* Make eager_unique_table_alias consider joined tables as well as tables in the FROM clause (jeremyevans)
|
20
|
+
|
21
|
+
* Make add_graph_aliases work correctly even if set_graph_aliases hasn't been used (jeremyevans)
|
22
|
+
|
23
|
+
* Fix using :conditions that are a placeholder string in an association (e.g. :conditions=>['a = ?', 42]) (jeremyevans)
|
24
|
+
|
25
|
+
* On MySQL, make Dataset#insert_ignore affect #insert as well as #multi_insert and #import (jeremyevans, tmm1)
|
26
|
+
|
27
|
+
* Add -t option to bin/sequel to output the full backtrace if an exception is raised (jeremyevans)
|
28
|
+
|
29
|
+
* Make schema_dumper extension ignore errors with indexes unless it is dumping in the database-specific type format (jeremyevans)
|
30
|
+
|
31
|
+
* Don't dump partial indexes in the MySQL adapter (jeremyevans)
|
32
|
+
|
33
|
+
* Add :ignore_index_errors option to Database#create_table and :ignore_errors option to Database#add_index (jeremyevans)
|
34
|
+
|
35
|
+
* Make graphing a complex dataset work correctly (jeremyevans)
|
36
|
+
|
37
|
+
* Fix MySQL command out of sync errors, disconnect from database if they occur (jeremyevans)
|
38
|
+
|
39
|
+
* In the schema_dumper extension, do a much better job of parsing defaults from the database (jeremyevans)
|
40
|
+
|
41
|
+
* On PostgreSQL, assume the public schema if one is not given and there is no default in Database#tables (jeremyevans)
|
42
|
+
|
43
|
+
* Ignore a :default value if creating a String :text=>true or File column on MySQL, since it doesn't support defaults on text/blob columns (jeremyevans)
|
44
|
+
|
45
|
+
* On PostgreSQL, do not raise an error when attempting to reset the primary key sequence for a table without a primary key (jeremyevans)
|
46
|
+
|
47
|
+
* Allow plugins to have a configure method that is called on every attempt to load them (jeremyevans)
|
48
|
+
|
49
|
+
* Attempting to load an already loaded plugin no longer calls the plugin's apply method (jeremyevans)
|
50
|
+
|
51
|
+
* Make plugin's plugin_opts methods return an array of arguments if multiple arguments were given, instead of just the first argument (jeremyevans)
|
52
|
+
|
53
|
+
* Keep track of loaded plugins at Model.plugins, allows plugins to depend on other plugins (jeremyevans)
|
54
|
+
|
55
|
+
* Make Dataset#insert on PostgreSQL work with static SQL (jeremyevans)
|
56
|
+
|
57
|
+
* Add lazy_attributes plugin, for creating attributes that can be lazily loaded from the database (jeremyevans)
|
58
|
+
|
59
|
+
* Add tactical_eager_loading plugin, similar to DataMapper's strategic eager loading (jeremyevans)
|
60
|
+
|
61
|
+
* Don't raise an error when loading a plugin with DatasetMethods where none of the methods are public (jeremyevans)
|
62
|
+
|
63
|
+
* Add identity_map plugin, for creating temporary thread-local identity maps with some caching (jeremyevans)
|
64
|
+
|
65
|
+
* Support savepoints when using MySQL and SQLite (jeremyevans)
|
66
|
+
|
67
|
+
* Add -C option to bin/sequel that copies one database to another (jeremyevans)
|
68
|
+
|
69
|
+
* In the schema_dumper extension, don't include defaults that contain literal strings unless the DBs are the same (jeremyevans)
|
70
|
+
|
71
|
+
* Only include valid non-partial indexes of simple column references in the PostgreSQL adapter (jeremyevans)
|
72
|
+
|
73
|
+
* Add -h option to bin/sequel for outputting the usage, alias for -? (jeremyevans)
|
74
|
+
|
75
|
+
* Add -d and -D options to bin/sequel for dumping schema migrations (jeremyevans)
|
76
|
+
|
77
|
+
* Support eager graphing for model tables that lack primary keys (jeremyevans)
|
78
|
+
|
79
|
+
* Add Model.create_table? to the schema plugin, similar to Database#create_table? (jeremyevans)
|
80
|
+
|
81
|
+
* Add Database#create_table?, which creates the table if it doesn't already exist (jeremyevans)
|
82
|
+
|
83
|
+
* Handle ordered and limited datasets correctly when using UNION, INTERSECT, or EXCEPT (jeremyevans)
|
84
|
+
|
85
|
+
* Fix unlikely threading bug with class level validations (jeremyevans)
|
86
|
+
|
87
|
+
* Make the schema_dumper extension dump tables in alphabetical order in migrations (jeremyevans)
|
88
|
+
|
89
|
+
* Add Sequel.extension method for loading extensions, so you don't have to use require (jeremyevans)
|
90
|
+
|
91
|
+
* Allow bin/sequel to respect multiple -L options instead of ignoring all but the last one (jeremyevans)
|
92
|
+
|
93
|
+
* Add :command_timeout and :provider options to ADO adapter (hgimenez)
|
94
|
+
|
95
|
+
* Fix exception messages when Sequel.string_to_* fail (jeremyevans)
|
96
|
+
|
97
|
+
* Fix String :type=>:text generic type in the Firebird adapter (wishdev)
|
98
|
+
|
99
|
+
* Add Sequel.amalgalite adapter method (jeremyevans)
|
100
|
+
|
1
101
|
=== 3.0.0 (2009-05-04)
|
2
102
|
|
3
103
|
* Remove dead threads from connection pool if the pool is full and a connection is requested (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -44,15 +44,15 @@ If you have any comments or suggestions please post to the Google group.
|
|
44
44
|
|
45
45
|
DB.create_table :items do
|
46
46
|
primary_key :id
|
47
|
-
String name
|
48
|
-
Float price
|
47
|
+
String :name
|
48
|
+
Float :price
|
49
49
|
end
|
50
50
|
|
51
51
|
items = DB[:items] # Create a dataset
|
52
52
|
|
53
53
|
# Populate the table
|
54
54
|
items.insert(:name => 'abc', :price => rand * 100)
|
55
|
-
items.insert(name => 'def', :price => rand * 100)
|
55
|
+
items.insert(:name => 'def', :price => rand * 100)
|
56
56
|
items.insert(:name => 'ghi', :price => rand * 100)
|
57
57
|
|
58
58
|
# Print out the number of records
|
data/bin/sequel
CHANGED
@@ -5,12 +5,15 @@ require 'optparse'
|
|
5
5
|
require 'sequel'
|
6
6
|
|
7
7
|
db_opts = {}
|
8
|
+
copy_databases = nil
|
9
|
+
dump_migration = nil
|
8
10
|
echo = nil
|
9
11
|
env = nil
|
10
12
|
logfile = nil
|
11
13
|
migrate_dir = nil
|
12
14
|
migrate_ver = nil
|
13
|
-
|
15
|
+
backtrace = nil
|
16
|
+
load_dirs = []
|
14
17
|
|
15
18
|
opts = OptionParser.new do |opts|
|
16
19
|
opts.banner = "Sequel: The Database Toolkit for Ruby"
|
@@ -25,11 +28,23 @@ opts = OptionParser.new do |opts|
|
|
25
28
|
opts.separator ""
|
26
29
|
opts.separator "Options:"
|
27
30
|
|
28
|
-
opts.on_tail("-?", "--help", "Show this message") do
|
31
|
+
opts.on_tail("-h", "-?", "--help", "Show this message") do
|
29
32
|
puts opts
|
30
33
|
exit
|
31
34
|
end
|
32
35
|
|
36
|
+
opts.on("-C", "--copy-databases", "copy one database to another") do
|
37
|
+
copy_databases = true
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-d", "--dump-migration", "print database migration to STDOUT") do
|
41
|
+
dump_migration = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("-D", "--dump-migration-same-db", "print database migration to STDOUT without type translation") do
|
45
|
+
dump_migration = :same_db
|
46
|
+
end
|
47
|
+
|
33
48
|
opts.on("-e", "--env ENV", "use environment config for database") do |v|
|
34
49
|
env = v
|
35
50
|
end
|
@@ -42,8 +57,8 @@ opts = OptionParser.new do |opts|
|
|
42
57
|
logfile = v
|
43
58
|
end
|
44
59
|
|
45
|
-
opts.on("-L", "--load-dir DIR", "loads all *.rb
|
46
|
-
|
60
|
+
opts.on("-L", "--load-dir DIR", "loads all *.rb under specifed directory") do |v|
|
61
|
+
load_dirs << v
|
47
62
|
end
|
48
63
|
|
49
64
|
opts.on("-m", "--migrate-directory DIR", "run the migrations in directory") do |v|
|
@@ -54,6 +69,10 @@ opts = OptionParser.new do |opts|
|
|
54
69
|
migrate_ver = Integer(v)
|
55
70
|
end
|
56
71
|
|
72
|
+
opts.on("-t", "--trace", "Output the full backtrace if an exception is raised") do
|
73
|
+
backtrace = true
|
74
|
+
end
|
75
|
+
|
57
76
|
opts.on_tail("-v", "--version", "Show version") do
|
58
77
|
puts "sequel #{Sequel.version}"
|
59
78
|
exit
|
@@ -63,11 +82,16 @@ opts.parse!
|
|
63
82
|
|
64
83
|
db = ARGV.shift
|
65
84
|
|
66
|
-
|
67
|
-
puts
|
85
|
+
error_proc = lambda do |msg|
|
86
|
+
$stderr.puts(msg)
|
68
87
|
exit 1
|
69
88
|
end
|
70
89
|
|
90
|
+
error_proc["Error: Must specify -m if using -M"] if migrate_ver && !migrate_dir
|
91
|
+
error_proc["Error: Cannot specify -D or -d with -m"] if dump_migration && migrate_dir
|
92
|
+
error_proc["Error: Cannot specify -C with -d, -D, or -m"] if copy_databases && (dump_migration || migrate_dir)
|
93
|
+
error_proc["Error: Must specify database connection string or path to yaml file as argument"] if db.nil? || db.empty?
|
94
|
+
|
71
95
|
if logfile || echo
|
72
96
|
require 'logger'
|
73
97
|
db_opts[:loggers] = []
|
@@ -75,30 +99,89 @@ if logfile || echo
|
|
75
99
|
db_opts[:loggers] << Logger.new($stdout) if echo
|
76
100
|
end
|
77
101
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
102
|
+
connect_proc = lambda do |database|
|
103
|
+
if File.exist?(database)
|
104
|
+
require 'yaml'
|
105
|
+
env ||= "development"
|
106
|
+
db_config = YAML.load_file(database)
|
107
|
+
db_config = db_config[env] || db_config[env.to_sym] || db_config
|
108
|
+
db_config.each{|(k,v)| db_config[k.to_sym] = db_config.delete(k)}
|
109
|
+
Sequel.connect(db_config.merge!(db_opts))
|
110
|
+
else
|
111
|
+
Sequel.connect(database, db_opts)
|
112
|
+
end
|
85
113
|
end
|
86
114
|
|
87
115
|
begin
|
88
|
-
DB =
|
116
|
+
DB = connect_proc[db]
|
89
117
|
DB.test_connection
|
90
118
|
if migrate_dir
|
91
|
-
|
119
|
+
Sequel.extension :migration
|
92
120
|
Sequel::Migrator.apply(DB, migrate_dir, migrate_ver)
|
93
121
|
exit
|
94
122
|
end
|
123
|
+
if dump_migration
|
124
|
+
Sequel.extension :schema_dumper
|
125
|
+
puts DB.dump_schema_migration(:same_db=>dump_migration==:same_db)
|
126
|
+
exit
|
127
|
+
end
|
128
|
+
if copy_databases
|
129
|
+
Sequel.extension :migration, :schema_dumper
|
130
|
+
|
131
|
+
db2 = ARGV.shift
|
132
|
+
error_proc["Error: Must specify database connection string or path to yaml file as second argument for database you want to copy to"] if db2.nil? || db2.empty?
|
133
|
+
start_time = Time.now
|
134
|
+
TO_DB = connect_proc[db2]
|
135
|
+
TO_DB.test_connection
|
136
|
+
same_db = DB.database_type==TO_DB.database_type
|
137
|
+
|
138
|
+
puts "Databases connections successful"
|
139
|
+
schema_migration = eval(DB.dump_schema_migration(:indexes=>false, :same_db=>same_db))
|
140
|
+
index_migration = eval(DB.dump_indexes_migration(:same_db=>same_db))
|
141
|
+
puts "Migrations dumped successfully"
|
142
|
+
|
143
|
+
schema_migration.apply(TO_DB, :up)
|
144
|
+
puts "Tables created"
|
145
|
+
|
146
|
+
puts "Begin copying data"
|
147
|
+
DB.transaction do
|
148
|
+
TO_DB.transaction do
|
149
|
+
DB.tables.each do |table|
|
150
|
+
puts "Begin copying records for table: #{table}"
|
151
|
+
time = Time.now
|
152
|
+
to_ds = TO_DB.from(table)
|
153
|
+
j = 0
|
154
|
+
DB.from(table).each do |record|
|
155
|
+
if Time.now - time > 5
|
156
|
+
puts "Status: #{j} records copied"
|
157
|
+
time = Time.now
|
158
|
+
end
|
159
|
+
to_ds.insert(record)
|
160
|
+
j += 1
|
161
|
+
end
|
162
|
+
puts "Finished copying #{j} records for table: #{table}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
puts "Finished copying data"
|
167
|
+
|
168
|
+
puts "Begin creating indexes"
|
169
|
+
index_migration.apply(TO_DB, :up)
|
170
|
+
puts "Finished creating indexes"
|
171
|
+
|
172
|
+
if TO_DB.database_type == :postgres
|
173
|
+
TO_DB.tables.each{|t| TO_DB.reset_primary_key_sequence(t)}
|
174
|
+
puts "Primary key sequences reset successfully"
|
175
|
+
end
|
176
|
+
puts "Database copy finished in #{Time.now - start_time} seconds"
|
177
|
+
exit
|
178
|
+
end
|
95
179
|
rescue => e
|
96
|
-
|
97
|
-
|
98
|
-
exit 1
|
180
|
+
raise e if backtrace
|
181
|
+
error_proc["Error: #{e.class}: #{e.message}#{e.backtrace.first}"]
|
99
182
|
end
|
100
183
|
|
101
|
-
Dir["#{
|
184
|
+
load_dirs.each{|d| Dir["#{d}/**/*.rb"].each{|f| load(f)}}
|
102
185
|
|
103
186
|
require 'irb'
|
104
187
|
puts "Your database is stored in DB..."
|
data/doc/reflection.rdoc
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= Reflection
|
2
|
+
|
3
|
+
Sequel supports reflection information in multiple ways.
|
4
|
+
|
5
|
+
== Adapter in Use
|
6
|
+
|
7
|
+
You can get the adapter in use using Database.adapter_scheme. As this is a class method, you generally need to do DB.class.adapter_scheme:
|
8
|
+
|
9
|
+
DB.class.adapter_scheme # e.g. :postgres, :jdbc, :odbc
|
10
|
+
|
11
|
+
== Database Connected To
|
12
|
+
|
13
|
+
In some case, the adapter scheme will be the same as the database to which you are connecting. However, many adapters support multiple databases. You can use the Database#database_type method to get the type of database to which you are connecting:
|
14
|
+
|
15
|
+
DB.database_type # :postgres, :h2, :mssql
|
16
|
+
|
17
|
+
== Tables in the Database
|
18
|
+
|
19
|
+
On many database types/adapters, Database#tables exists and gives an array of table name symbols:
|
20
|
+
|
21
|
+
DB.tables # [:table1, :table2, :table3, ...]
|
22
|
+
|
23
|
+
== Indexes on a table
|
24
|
+
|
25
|
+
On a few database types/adapters, Database#indexes takes a table name gives a hash of index information. Keys are index names, values are subhashes with the keys :columns and :unique :
|
26
|
+
|
27
|
+
DB.indexes(:table1) # {:index1=>{:columns=>[:column1], :unique=>false}, :index2=>{:columns=>[:column2, :column3], :unique=>true}}
|
28
|
+
|
29
|
+
Index information generally does not include partial indexes, functional indexes, or indexes on the primary key of the table.
|
30
|
+
|
31
|
+
== Column Information for a Table
|
32
|
+
|
33
|
+
Database#schema takes a table symbol and returns column information in an array with each element being an array with two elements. The first elements of the subarray is a column symbol, and the second element is a hash of information about that column. The hash should include the following keys:
|
34
|
+
|
35
|
+
* :allow_null - Whether NULL/nil is an allowed value for this column. Used by the Sequel::Model typecasting code.
|
36
|
+
* :db_type - The type of column the database provided, as a string. Used by the schema_dumper plugin for a more specific type translation.
|
37
|
+
* :default - The default value of the column, as either a string or nil. Uses a database specific format. Used by the schema_dumper plugin for converting to a ruby value.
|
38
|
+
* :primary_key - Whether this column is one of the primary key columns for the table. Used by the Sequel::Model code to determine primary key columns.
|
39
|
+
* :type - The type of column, as a symbol (e.g. :string). Used by the Sequel::Model typecasting code.
|
40
|
+
|
41
|
+
Example:
|
42
|
+
|
43
|
+
DB.schema(:table) # [[:column1, {:allow_null=>true, :db_type=>'varchar(255)', :default=>'blah', :primary_key=>false, :type=>:string}], ...]
|
44
|
+
|
45
|
+
== Column Information for a Model
|
46
|
+
|
47
|
+
Model#db_schema returns pretty much the same information, except it returns it as a hash with column keys instead of an array of two element arrays.
|
48
|
+
|
49
|
+
Model.db_schema # {:column1=>{:allow_null=>true, :db_type=>'varchar(255)', :default=>'blah', :primary_key=>false, :type=>:string}, ...}
|
50
|
+
|
51
|
+
== Columns used by a dataset/model
|
52
|
+
|
53
|
+
Dataset#columns returns the columns of the current dataset as an array of symbols:
|
54
|
+
|
55
|
+
DB[:table].columns # [:column1, :column2, :column3, ...]
|
56
|
+
|
57
|
+
Dataset#columns! does the same thing, except it ignores any cached value. In general, the cached value should never be incorrect, unless the database schema is changed after the dataset is created.
|
58
|
+
|
59
|
+
DB[:table].columns! # [:column1, :column2, :column3, ...]
|
60
|
+
|
61
|
+
Model.columns does the same thing as Dataset#columns, using the model's dataset:
|
62
|
+
|
63
|
+
Model.columns # [:column1, :column2, :column3, ...]
|
64
|
+
|
65
|
+
== Associations Defined
|
66
|
+
|
67
|
+
Sequel::Model offers complete introspection capability for all associations.
|
68
|
+
|
69
|
+
You can get an array of association symbols with Model.associations:
|
70
|
+
|
71
|
+
Model.associations # [:association1, :association2, ...]
|
72
|
+
|
73
|
+
You can get the association reflection for a single association via the Model.association_reflection. Association reflections are subclasses of hash, for ease of use and introspection (and backwards compatibility):
|
74
|
+
|
75
|
+
Model.association_reflection(:association1) # {:name=>:association1, :type=>:many_to_one, :model=>Model, :associated_class=>OtherModel, ...}
|
76
|
+
|
77
|
+
You can get an array of all association reflections via Model.all_association_reflections:
|
78
|
+
|
79
|
+
Model.all_association_reflections # [{:name=>:association1, :type=>:many_to_one, :model=>Model, :associated_class=>OtherModel, ...}, ...]
|
80
|
+
|
81
|
+
Finally, you can get a hash of association reflections via Model.association_reflections:
|
82
|
+
|
83
|
+
Model.association_reflections # {:association1=>{:name=>:association1, :type=>:many_to_one, :model=>Model, :associated_class=>OtherModel, ...}, ...}
|
@@ -0,0 +1,406 @@
|
|
1
|
+
New Plugins
|
2
|
+
-----------
|
3
|
+
|
4
|
+
3 new plugins were added that implement features supported by
|
5
|
+
DataMapper: identity_map, tactical_eager_loading, and
|
6
|
+
lazy_attributes. These plugins don't add any real new features,
|
7
|
+
since you can do most of what they allow before simply by being
|
8
|
+
a little more explicit in your Sequel code. However, some people
|
9
|
+
prefer a less explicit approach that uses a bit more magic, and
|
10
|
+
now Sequel can accomodate them.
|
11
|
+
|
12
|
+
* The identity_map plugin allows you to create a 1-1
|
13
|
+
correspondence of model objects to database rows via a temporary
|
14
|
+
thread-local identity map. It makes the following statment true:
|
15
|
+
|
16
|
+
Sequel::Model.with_identity_map do
|
17
|
+
Album.filter{(id > 0) & (id < 2)}.first.object_id == \
|
18
|
+
Album.first(:id=>1).object_id
|
19
|
+
end
|
20
|
+
|
21
|
+
As the code above implies, you need to use the with_identity_map
|
22
|
+
method with a block to use the identity mapping feature.
|
23
|
+
|
24
|
+
By itself, identity maps don't offer much, but Sequel uses them
|
25
|
+
as a cache when looking up objects by primary key or looking up
|
26
|
+
many_to_one associated objects. Basically, it can be used as a
|
27
|
+
performance enhancer, and it also allows the support of the
|
28
|
+
lazy_attributes plugin.
|
29
|
+
|
30
|
+
The identity_map plugin is expected to be most useful in web
|
31
|
+
applications. With that in mind, here's a Rack middleware that
|
32
|
+
wraps each request in a with_identity_map call, so the
|
33
|
+
identity_map features are available inside the web app:
|
34
|
+
|
35
|
+
Sequel::Model.plugin :identity_map
|
36
|
+
class SequelIdentityMap
|
37
|
+
def initialize(app)
|
38
|
+
@app = app
|
39
|
+
end
|
40
|
+
def call(env)
|
41
|
+
Sequel::Model.with_identity_map{@app.call(env)}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
* The tactical_eager_loading plugin allows you to eagerly load an
|
46
|
+
association for all models retrieved in the same group whenever
|
47
|
+
one of the models accesses the association:
|
48
|
+
|
49
|
+
# 2 queries total
|
50
|
+
Album.filter{id<100}.all do |a|
|
51
|
+
a.artists
|
52
|
+
end
|
53
|
+
|
54
|
+
In order for this correctly, you must use Dataset#all to load the
|
55
|
+
records, you cannot iterate over them via Dataset#each. This is
|
56
|
+
because eager loading requires that you have all records in
|
57
|
+
advance, and when using Dataset#each you cannot know about later
|
58
|
+
records in the dataset.
|
59
|
+
|
60
|
+
Before, you could just be explicit about the associations you
|
61
|
+
needed and make sure to eagerly load them using eager before
|
62
|
+
calling Dataset#all.
|
63
|
+
|
64
|
+
* The lazy_attributes plugin builds on the identity_map and
|
65
|
+
tactical_eager_loading plugins and allows you to create
|
66
|
+
attributes that are lazily loaded from the database:
|
67
|
+
|
68
|
+
Album.plugin :lazy_attributes, :review
|
69
|
+
|
70
|
+
This will remove the :review attribute from being selected by
|
71
|
+
default. If you try to access the attribute after it is selected,
|
72
|
+
it'll retrieve the value from the database. If the object was
|
73
|
+
retrieved with a group of other objects and an identity map is in
|
74
|
+
use, it'll retrieve the lazy attribute for the entire group of
|
75
|
+
objects at once, similar to the tatical_eager_loading plugin:
|
76
|
+
|
77
|
+
# 2 queries total
|
78
|
+
Sequel::Model.with_identity_map do
|
79
|
+
Album.filter{id<100}.all do |a|
|
80
|
+
a.review
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Before, you could just set the default selected columns for a model
|
85
|
+
to not include the lazy attributes, and just use select_more to
|
86
|
+
add them to any query where the resulting model objects will
|
87
|
+
need the attributes.
|
88
|
+
|
89
|
+
* A many_through_many plugin was also added. This very powerful
|
90
|
+
plugin allows you to create associations to multiple objects through
|
91
|
+
multiple join tables. Here are some examples:
|
92
|
+
|
93
|
+
# Assume the following many to many associations:
|
94
|
+
Artist.many_to_many :albums
|
95
|
+
Album.many_to_many :tags
|
96
|
+
|
97
|
+
# Same as Artist.many_to_many :albums
|
98
|
+
Artist.many_through_many :albums,
|
99
|
+
[[:albums_artists, :artist_id, :album_id]]
|
100
|
+
|
101
|
+
# All tags associated to any album this artist is associated to
|
102
|
+
Artist.many_through_many :tags,
|
103
|
+
[[:albums_artists, :artist_id, :album_id],
|
104
|
+
[:albums, :id, :id],
|
105
|
+
[:albums_tags, :album_id, :tag_id]]
|
106
|
+
|
107
|
+
# All artists associated to any album this artist is associated to
|
108
|
+
Artist.many_through_many :artists,
|
109
|
+
[[:albums_artists, :artist_id, :album_id],
|
110
|
+
[:albums, :id, :id],
|
111
|
+
[:albums_artists, :album_id, :artist_id]]
|
112
|
+
|
113
|
+
# All albums by artists that are associated to any album this
|
114
|
+
# artist is associated to
|
115
|
+
Artist.many_through_many :artist_albums,
|
116
|
+
[[:albums_artists, :artist_id, :album_id],
|
117
|
+
[:albums, :id, :id],
|
118
|
+
[:albums_artists, :album_id, :artist_id],
|
119
|
+
[:artists, :id, :id],
|
120
|
+
[:albums_artists, :artist_id, :album_id]]
|
121
|
+
|
122
|
+
Basically, for each join table between this model and the
|
123
|
+
associated model, you use an array with a join table name, left key
|
124
|
+
name (key closer to this model), and right key name (key closer to
|
125
|
+
the associated model).
|
126
|
+
|
127
|
+
In usual Sequel fashion, this association type works not just
|
128
|
+
for single objects, but it can also be eagerly loaded via eager or
|
129
|
+
eager_graph. There are numerous additional configuration options,
|
130
|
+
please see the RDoc for details.
|
131
|
+
|
132
|
+
New bin/sequel Features
|
133
|
+
-----------------------
|
134
|
+
|
135
|
+
The bin/sequel command line tool now supports the following options:
|
136
|
+
|
137
|
+
* -C: Copies one database to another. You must specify two database
|
138
|
+
arguments. Works similar to Taps, copying the table schema, then
|
139
|
+
the table data, then creating the indexes.
|
140
|
+
|
141
|
+
* -d: Dump the schema of the database in the database-independent
|
142
|
+
migration format.
|
143
|
+
|
144
|
+
* -D: Dump the schema of the database in the database-specific
|
145
|
+
migration format.
|
146
|
+
|
147
|
+
* -h: Display the help
|
148
|
+
|
149
|
+
* -t: Output the full backtrace if an exception is raised
|
150
|
+
|
151
|
+
The bin/sequel tool is now better about checking which options can
|
152
|
+
be used together. It also now supports using the -L option multiple
|
153
|
+
times and having it load model files from multiple directory trees.
|
154
|
+
|
155
|
+
New Features
|
156
|
+
------------
|
157
|
+
|
158
|
+
* Dataset#qualify_to and #qualify_to_first_source were added. They
|
159
|
+
allow you to qualify unqualified columns in the current dataset
|
160
|
+
to the given table or the first source. This can be used to join
|
161
|
+
a dataset that has unqualified columns to a new table which has
|
162
|
+
columns with the same name.
|
163
|
+
|
164
|
+
For example, take this dataset:
|
165
|
+
|
166
|
+
ds = DB[:albums].select(:name).order(:name).filter(:id=>1)
|
167
|
+
# SELECT name FROM albums WHERE (id = 1) ORDER BY name
|
168
|
+
|
169
|
+
Let's say you want to join it to the artists table:
|
170
|
+
|
171
|
+
ds2 = ds.join(:artists, :id=>:artist_id)
|
172
|
+
# SELECT name FROM albums
|
173
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
174
|
+
# WHERE (id = 1) ORDER BY name
|
175
|
+
|
176
|
+
That's going to give you an error, as the artists table already has
|
177
|
+
columns named id and name. This new feature allows you to do the
|
178
|
+
following:
|
179
|
+
|
180
|
+
ds2 = ds.qualify_to_first_source.join(:artists, :id=>:artist_id)
|
181
|
+
# SELECT albums.name FROM albums
|
182
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
183
|
+
# WHERE (albums.id = 1) ORDER BY albums.name
|
184
|
+
|
185
|
+
By doing this, all unqualified columns are qualified, so you get
|
186
|
+
a usable query. This is expected to be most useful for users that
|
187
|
+
have a default order or filter on their models and want to join
|
188
|
+
the model to another table. Before you had to replace the filters,
|
189
|
+
selection, etc. manually, or use qualified columns by default even
|
190
|
+
though the weren't needed in most cases.
|
191
|
+
|
192
|
+
* Savepoints are now supported using SQLite and MySQL, assuming you
|
193
|
+
are using a database version that supports them. You need to
|
194
|
+
pass the :savepoint option to Database#transaction to use a
|
195
|
+
savepoint.
|
196
|
+
|
197
|
+
* Model plugins can now depend on other plugins, simply by calling
|
198
|
+
the Model.plugin method inside the plugin's apply method:
|
199
|
+
|
200
|
+
module LazyAttributes
|
201
|
+
def self.apply(model)
|
202
|
+
model.plugin :tactical_eager_loading
|
203
|
+
end
|
204
|
+
|
205
|
+
* Model.plugin now takes a block with is passed to the plugin's
|
206
|
+
apply and configure method (see Backwards Compatibility section for
|
207
|
+
more information on the configure method).
|
208
|
+
|
209
|
+
* You can see which plugins are loaded for a model by using
|
210
|
+
Model.plugins.
|
211
|
+
|
212
|
+
* You can use Sequel.extension method to load extensions:
|
213
|
+
|
214
|
+
Sequel.extension :pagination, :query
|
215
|
+
|
216
|
+
This will only load extensions that ship with Sequel, unlike the
|
217
|
+
Model.plugin method which will also load external plugins.
|
218
|
+
|
219
|
+
* You can now use Database#create_table? to create the table if it
|
220
|
+
doesn't already exist (a very common need, it seems). The schema
|
221
|
+
plugin now supports Model.create_table? as well.
|
222
|
+
|
223
|
+
* #sql_subscript is now an allowed method on most SQL expression
|
224
|
+
objects that Sequel generates. Also, arguments to #sql_subscript
|
225
|
+
can now be other expressions instead of just integers.
|
226
|
+
|
227
|
+
* Associations can now take a :cartesian_product_number option, which
|
228
|
+
can be used to tell Sequel whether to turn on duplicate object
|
229
|
+
detection when eagerly loading objects through eager_graph. This
|
230
|
+
number should be 0 if the association can never create multiple
|
231
|
+
rows for each row in the current table, 1 if it can create multiple
|
232
|
+
rows in the each row in the current table, and 2 if the association
|
233
|
+
itself causes a cartesian product.
|
234
|
+
|
235
|
+
* On MySQL, Dataset#insert_ignore now affects #insert as well as
|
236
|
+
multi_insert and import.
|
237
|
+
|
238
|
+
* Database#create_table now supports an :ignore_index_errors option,
|
239
|
+
and Database#add_index now supports an :ignore_errors option.
|
240
|
+
These are used by the schema_dumper when dumping an database
|
241
|
+
schema to be restored on another database type, since indexes
|
242
|
+
aren't usually required for proper operation and some indexes
|
243
|
+
can't be transferred.
|
244
|
+
|
245
|
+
* The ADO adapter now takes a :provider option, which can be used
|
246
|
+
to set the provider.
|
247
|
+
|
248
|
+
* The ADO adapter now takes a :command_timeout option, which tells
|
249
|
+
the connection how long to wait before giving up and raising an
|
250
|
+
exception.
|
251
|
+
|
252
|
+
* The Sequel.amalgalite adapter method was added. Like the
|
253
|
+
Sequel.sqlite method, you can call it with no arguments to get
|
254
|
+
an in memory database.
|
255
|
+
|
256
|
+
Other Improvements
|
257
|
+
------------------
|
258
|
+
|
259
|
+
* MySQL "commands out of sync" errors should no longer occur unless
|
260
|
+
you are nesting queries (calling Dataset#each inside Dataset#each).
|
261
|
+
A bug dating at least to 2007 and possibly since the initial
|
262
|
+
creation of the Sequel MySQL adapter was the cause. Before, SQL
|
263
|
+
that caused a result set that was sent using a method where Sequel
|
264
|
+
doesn't yield a result set would cause the "commands out of sync"
|
265
|
+
error on the following query. For example, the following code
|
266
|
+
would cause the error:
|
267
|
+
|
268
|
+
DB << "SHOW DATABASES"
|
269
|
+
|
270
|
+
If for some reason a "commands out of sync" error does occur,
|
271
|
+
Sequel will disconnect the connection from the connection pool,
|
272
|
+
so it won't continually stay in the pool and raise errors every
|
273
|
+
time it is used.
|
274
|
+
|
275
|
+
* The schema_dumper extension is much better about parsing defaults
|
276
|
+
from the database. It can now correctly parse most defaults on
|
277
|
+
MySQL, SQLite, and PostgreSQL databases. It no longer includes
|
278
|
+
defaults that it can't parse to a ruby object unless a database-
|
279
|
+
specific dump is requested.
|
280
|
+
|
281
|
+
* The schema_dumper extension now dumps tables in alphabetical order.
|
282
|
+
|
283
|
+
* Ordered and limited datasets are now handled correctly when using
|
284
|
+
union, intersect, and except. Also, union, intersect, and except
|
285
|
+
now always return a from_self dataset, so further limiting,
|
286
|
+
filtering, and ordering of them now works as expected.
|
287
|
+
|
288
|
+
* Dataset#graph now works correctly with a complex dataset without
|
289
|
+
having to use from_self. Before, code like the following didn't
|
290
|
+
do what was expected:
|
291
|
+
|
292
|
+
DB[:albums].
|
293
|
+
graph(DB[:artists].filter{name > 'M'}, :id=>:artist_id)
|
294
|
+
|
295
|
+
Before, the filter on DB[:artists] would be dropped. Now, Sequel
|
296
|
+
correctly uses a subselect.
|
297
|
+
|
298
|
+
* You can now specify serialization formats per column in the
|
299
|
+
serialization plugin, either by calling the plugin multiple
|
300
|
+
times or by using the new serialize_attributes method:
|
301
|
+
|
302
|
+
Album.plugin :serialization
|
303
|
+
Album.serialize_attributes :marshal, :review
|
304
|
+
Album.serialize_attributes :yaml, :name
|
305
|
+
Album.serialization_map #{:name=>:yaml, :review=>:marshal}
|
306
|
+
|
307
|
+
The public API for the serialization plugin is still backwards
|
308
|
+
compatible, but the internals have changed slightly to support
|
309
|
+
this new feature.
|
310
|
+
|
311
|
+
* You can now use eager_graph to eagerly load associations for models
|
312
|
+
that lack primary keys.
|
313
|
+
|
314
|
+
* The :eager_graph association option now works when lazily-loading
|
315
|
+
many_to_many associations.
|
316
|
+
|
317
|
+
* Dataset#add_graph_aliases now works correctly even if
|
318
|
+
set_graph_aliases hasn't been used.
|
319
|
+
|
320
|
+
* The PostgreSQL Database#tables method now assumes the public schema
|
321
|
+
if a schema is not given and there is no default_schema.
|
322
|
+
|
323
|
+
* The PostgreSQL Database#indexes method no longer returns partial
|
324
|
+
indexes or functional indexes.
|
325
|
+
|
326
|
+
* The MySQL Database#indexes method no longer returns indexes on
|
327
|
+
partial columns (prefix indexes).
|
328
|
+
|
329
|
+
* Default values for String :text=>true and File columns on MySQL
|
330
|
+
are ignored, since MySQL doesn't support them. They are not
|
331
|
+
ignored if you use text and blob, since then you are using the
|
332
|
+
database-specific syntax and Sequel doesn't do translation when
|
333
|
+
the database-specific syntax is used.
|
334
|
+
|
335
|
+
* On PostgreSQL, attempting the reset the primary key sequence for a
|
336
|
+
table without a primary key no longer causes an error.
|
337
|
+
|
338
|
+
* Using a placeholder string in an association's :condition option
|
339
|
+
now works correctly (e.g. :conditions=>['n = ?', 1])
|
340
|
+
|
341
|
+
* An error is no longer raised if you attempt to load a plugin that
|
342
|
+
has a DatasetMethods module but no public dataset methods.
|
343
|
+
|
344
|
+
* The check for dataset[n] where n is an integer was fixed. It now
|
345
|
+
raises an error inside of returning a limited dataset.
|
346
|
+
|
347
|
+
* On PostgreSQL, Dataset#insert with static SQL now works correctly.
|
348
|
+
|
349
|
+
* A reflection.rdoc file was added giving an overview of Sequel's
|
350
|
+
reflection support.
|
351
|
+
|
352
|
+
* The Migrator now works correctly with file names like
|
353
|
+
001_12312412_file_name.rb.
|
354
|
+
|
355
|
+
* The association code now requires the classes match when looking
|
356
|
+
for a reciprocal association.
|
357
|
+
|
358
|
+
* An unlikely threading bug (race condition) was possible when using
|
359
|
+
the validation_class_methods plugin. The plugin was refactored and
|
360
|
+
now uses a mutex to avoid the issue. One of the refactoring changes
|
361
|
+
makes it so that you can no longer use a class level vaildation
|
362
|
+
inside a Class.new block (since inherited isn't called until the
|
363
|
+
block finishes).
|
364
|
+
|
365
|
+
* The exception messages when Sequel.string_to_* fail have been fixed.
|
366
|
+
|
367
|
+
* The String :text=>true generic database type has been fixed when
|
368
|
+
using the Firebird adapter.
|
369
|
+
|
370
|
+
Backwards Compatibility
|
371
|
+
-----------------------
|
372
|
+
|
373
|
+
* A plugin's apply method is now only called the first time a plugin
|
374
|
+
is loaded. Plugins can now have a configure method that is called
|
375
|
+
every time the plugin is loaded, and is always called after the
|
376
|
+
instance methods, class methods, and dataset method submodules have
|
377
|
+
been added to the model. This is different from apply, which is
|
378
|
+
called before the submodules are loaded.
|
379
|
+
|
380
|
+
If you are a plugin author, please check your implementation to
|
381
|
+
make sure this doesn't cause problems for you. If you have
|
382
|
+
questions, please post on the Sequel mailing list.
|
383
|
+
|
384
|
+
This new plugin feature will make certain things a lot easier, and
|
385
|
+
it should be mostly backwards compatible. However, if a plugin
|
386
|
+
was previously expected to be loaded multiple times with the apply
|
387
|
+
method called each time, it will no longer work correctly.
|
388
|
+
|
389
|
+
* The plugin_opts methods defined now include multiple args in an
|
390
|
+
array if multiple args are given. Before, the plugin_opts methods
|
391
|
+
just returned the first argument.
|
392
|
+
|
393
|
+
* Database#table_exists? no longer checks the cached schema
|
394
|
+
information. By default, it will always do a database query
|
395
|
+
(unless overridden in an adapter). This shouldn't affect the
|
396
|
+
results, but if were using the method a lot and expecting it to
|
397
|
+
use cached information, it doesn't have the same performance
|
398
|
+
characteristics.
|
399
|
+
|
400
|
+
* The internal storage of the :select option for datasets have
|
401
|
+
changed. You can no longer use a hash as a way of aliasing
|
402
|
+
columns. Dataset#select now does the translation from the hash to
|
403
|
+
SQL::AliasedExpression instances. Basically, if you were using
|
404
|
+
Dataset#clone directly with a :select option with hashes for
|
405
|
+
aliasing, you should switch to using Dataset#select or changing
|
406
|
+
the hashes to AliasedExpressions yourself.
|