sequel 3.16.0 → 3.17.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.
- data/CHANGELOG +28 -0
- data/doc/release_notes/3.17.0.txt +58 -0
- data/lib/sequel/adapters/jdbc/as400.rb +51 -8
- data/lib/sequel/adapters/mysql.rb +4 -2
- data/lib/sequel/adapters/mysql2.rb +3 -2
- data/lib/sequel/adapters/shared/oracle.rb +9 -0
- data/lib/sequel/adapters/swift.rb +5 -2
- data/lib/sequel/adapters/swift/postgres.rb +21 -2
- data/lib/sequel/database/connecting.rb +3 -1
- data/lib/sequel/database/logging.rb +6 -1
- data/lib/sequel/database/misc.rb +3 -0
- data/lib/sequel/dataset/graph.rb +25 -12
- data/lib/sequel/model/associations.rb +18 -7
- data/lib/sequel/model/base.rb +9 -2
- data/lib/sequel/plugins/identity_map.rb +2 -1
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/nested_attributes.rb +9 -6
- data/lib/sequel/plugins/optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +11 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/core/database_spec.rb +43 -4
- data/spec/core/object_graph_spec.rb +34 -0
- data/spec/extensions/inflector_spec.rb +4 -0
- data/spec/extensions/many_through_many_spec.rb +19 -0
- data/spec/extensions/nested_attributes_spec.rb +2 -2
- data/spec/extensions/optimistic_locking_spec.rb +26 -1
- data/spec/extensions/xml_serializer_spec.rb +10 -0
- metadata +6 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
=== 3.17.0 (2010-11-05)
|
2
|
+
|
3
|
+
* Ensure that the optimistic locking plugin increments the lock column when using Model#modified! (jfirebaugh)
|
4
|
+
|
5
|
+
* Correctly handle nil values in the xml_serializer plugin, instead of converting them to empty strings (george.haff) (#313)
|
6
|
+
|
7
|
+
* Use a default wait_timeout that's allowed on Windows for the mysql and mysql2 adapters (jeremyevans) (#314)
|
8
|
+
|
9
|
+
* Add support for connecting to MySQL over SSL using the :sslca, :sslkey, and related options (jeremyevans)
|
10
|
+
|
11
|
+
* Fix Database#each_server when used with jdbc or do connection strings without separate :adapter option (jeremyevans) (#312)
|
12
|
+
|
13
|
+
* Much better support in the AS400 JDBC subadapter (bhauff)
|
14
|
+
|
15
|
+
* Allow cloning of many_through_many associations (gucki, jeremyevans)
|
16
|
+
|
17
|
+
* In the nested_attributes plugin, don't make unnecessary update calls to modify associated objects that are about to be deleted (jeremyevans, gucki)
|
18
|
+
|
19
|
+
* Allow Dataset#(add|set)_graph_aliases to accept as hash values symbols and arrays with a single element (jeremyevans)
|
20
|
+
|
21
|
+
* Add Databse#views and #view_exists? to the Oracle adapter (gpheruson)
|
22
|
+
|
23
|
+
* Add Database#sql_log_level for changing the level at which SQL queries are logged (jeremyevans)
|
24
|
+
|
25
|
+
* Remove unintended use of prepared statements in swift adapter (jeremyevans)
|
26
|
+
|
27
|
+
* Fix logging in the swift PostgreSQL subadapter (jeremyevans)
|
28
|
+
|
1
29
|
=== 3.16.0 (2010-10-01)
|
2
30
|
|
3
31
|
* Support composite foreign keys for associations in the identity_map plugin (harukizaemon, jeremyevans) (#310)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* You can now change the level at which Sequel logs SQL statements,
|
4
|
+
by calling Database#sql_log_level= with the method name symbol.
|
5
|
+
The default is still :info for backwards compatibility. Previously,
|
6
|
+
you had to use a proxy logger to get similar capability.
|
7
|
+
|
8
|
+
* You can now specify graph aliases where the alias would be the same
|
9
|
+
as the table column name using just the table name symbol, instead
|
10
|
+
having to repeat the alias as the second element of an array. More
|
11
|
+
clearly:
|
12
|
+
|
13
|
+
# < 3.17.0:
|
14
|
+
DB[:a].graph(:b, :a_id=>:id).
|
15
|
+
set_graph_aliases(:c=>[:a, :c], :d=>[:b, :d])
|
16
|
+
# >= 3.17.0:
|
17
|
+
DB[:a].graph(:b, :a_id=>:id).set_graph_aliases(:c=>:a, :d=>:b)
|
18
|
+
|
19
|
+
Both of these now yield the SQL:
|
20
|
+
|
21
|
+
SELECT a.c, b.d FROM a LEFT OUTER JOIN b ON (b.a_id = a.id)
|
22
|
+
|
23
|
+
* You should now be able to connect to MySQL over SSL in the native
|
24
|
+
MySQL adapter using the :sslca, :sslkey, and related options.
|
25
|
+
|
26
|
+
* Database#views and Database#view_exists? methods were added to the
|
27
|
+
Oracle adapter, allowing you to get a an array of view name symbols
|
28
|
+
and to check whether a given view exists.
|
29
|
+
|
30
|
+
= Other Improvements
|
31
|
+
|
32
|
+
* The nested_attributes plugin now avoids unnecessary update calls
|
33
|
+
when deleting associated objects, resulting in better performance.
|
34
|
+
|
35
|
+
* The optimistic_locking plugin now increments the lock column if no
|
36
|
+
other columns were modified but the Model#modified! was called. This
|
37
|
+
means it now works correctly with the nested_attributes plugin when
|
38
|
+
no changes to the main model object are made.
|
39
|
+
|
40
|
+
* The xml_serializer plugin can now round-trip nil values correctly.
|
41
|
+
Previously, nil values would be converted into empty strings. This
|
42
|
+
is accomplished by including a nil attribute in the xml tag.
|
43
|
+
|
44
|
+
* Database#each_server now works correctly when using the jdbc and do
|
45
|
+
adapters and a connection string without a separate :adapter option.
|
46
|
+
|
47
|
+
* You can now clone many_through_many associations.
|
48
|
+
|
49
|
+
* The default wait_timeout used by the mysql and mysql2 adapters was
|
50
|
+
decreased slightly so that it works correctly with MySQL database
|
51
|
+
servers that run on Windows.
|
52
|
+
|
53
|
+
* Many improvements were made to the AS400 jdbc subadapter.
|
54
|
+
|
55
|
+
* Many improvements were made to the swift adapter and subadapters.
|
56
|
+
|
57
|
+
* Dataset#ungraphed now removes any cached graph aliases set with
|
58
|
+
set_graph_aliases or add_graph_aliases.
|
@@ -4,6 +4,10 @@ module Sequel
|
|
4
4
|
module AS400
|
5
5
|
# Instance methods for AS400 Database objects accessed via JDBC.
|
6
6
|
module DatabaseMethods
|
7
|
+
TRANSACTION_BEGIN = 'Transaction.begin'.freeze
|
8
|
+
TRANSACTION_COMMIT = 'Transaction.commit'.freeze
|
9
|
+
TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
|
10
|
+
|
7
11
|
# AS400 uses the :as400 database type.
|
8
12
|
def database_type
|
9
13
|
:as400
|
@@ -18,28 +22,67 @@ module Sequel
|
|
18
22
|
def last_insert_id(conn, opts={})
|
19
23
|
nil
|
20
24
|
end
|
25
|
+
|
26
|
+
# AS400 supports transaction isolation levels
|
27
|
+
def supports_transaction_isolation_levels?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Use JDBC connection's setAutoCommit to false to start transactions
|
34
|
+
def begin_transaction(conn, opts={})
|
35
|
+
set_transaction_isolation(conn, opts)
|
36
|
+
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
37
|
+
conn
|
38
|
+
end
|
39
|
+
|
40
|
+
# Use JDBC connection's commit method to commit transactions
|
41
|
+
def commit_transaction(conn, opts={})
|
42
|
+
log_yield(TRANSACTION_COMMIT){conn.commit}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use JDBC connection's setAutoCommit to true to enable default
|
46
|
+
# auto-commit mode
|
47
|
+
def remove_transaction(conn)
|
48
|
+
conn.setAutoCommit(true) if conn
|
49
|
+
@transactions.delete(Thread.current)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Use JDBC connection's rollback method to rollback transactions
|
53
|
+
def rollback_transaction(conn, opts={})
|
54
|
+
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
55
|
+
end
|
21
56
|
end
|
22
57
|
|
23
58
|
# Dataset class for AS400 datasets accessed via JDBC.
|
24
59
|
class Dataset < JDBC::Dataset
|
25
60
|
WILDCARD = Sequel::LiteralString.new('*').freeze
|
26
61
|
|
27
|
-
# AS400 needs to use a couple of subselects for
|
62
|
+
# AS400 needs to use a couple of subselects for queries with offsets.
|
28
63
|
def select_sql
|
29
|
-
return super unless
|
30
|
-
|
64
|
+
return super unless o = @opts[:offset]
|
65
|
+
l = @opts[:limit]
|
31
66
|
order = @opts[:order]
|
32
67
|
dsa1 = dataset_alias(1)
|
33
68
|
dsa2 = dataset_alias(2)
|
34
69
|
rn = row_number_column
|
35
70
|
irn = Sequel::SQL::Identifier.new(rn).qualify(dsa2)
|
36
71
|
subselect_sql(unlimited.
|
37
|
-
|
38
|
-
|
72
|
+
from_self(:alias=>dsa1).
|
73
|
+
select_more(Sequel::SQL::QualifiedIdentifier.new(dsa1, WILDCARD),
|
39
74
|
Sequel::SQL::WindowFunction.new(SQL::Function.new(:ROW_NUMBER), Sequel::SQL::Window.new(:order=>order)).as(rn)).
|
40
|
-
|
41
|
-
|
42
|
-
|
75
|
+
from_self(:alias=>dsa2).
|
76
|
+
select(Sequel::SQL::QualifiedIdentifier.new(dsa2, WILDCARD)).
|
77
|
+
where(l ? ((irn > o) & (irn <= l + o)) : (irn > o))) # Leave off limit in case of limit(nil, offset)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Modify the sql to limit the number of rows returned
|
81
|
+
def select_limit_sql(sql)
|
82
|
+
if @opts[:limit]
|
83
|
+
sql << " FETCH FIRST ROW ONLY" if @opts[:limit] == 1
|
84
|
+
sql << " FETCH FIRST #{@opts[:limit]} ROWS ONLY" if @opts[:limit] > 1
|
85
|
+
end
|
43
86
|
end
|
44
87
|
|
45
88
|
def supports_window_functions?
|
@@ -93,6 +93,7 @@ module Sequel
|
|
93
93
|
conn = Mysql.init
|
94
94
|
conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
|
95
95
|
conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
|
96
|
+
conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
|
96
97
|
if encoding = opts[:encoding] || opts[:charset]
|
97
98
|
# Set encoding before connecting so that the mysql driver knows what
|
98
99
|
# encoding we want to use, but this can be overridden by READ_DEFAULT_GROUP.
|
@@ -116,8 +117,9 @@ module Sequel
|
|
116
117
|
# that feature.
|
117
118
|
sqls << "SET NAMES #{literal(encoding.to_s)}" if encoding
|
118
119
|
|
119
|
-
#
|
120
|
-
|
120
|
+
# Increase timeout so mysql server doesn't disconnect us
|
121
|
+
# Value used by default is maximum allowed value on Windows.
|
122
|
+
sqls << "SET @@wait_timeout = #{opts[:timeout] || 2147483}"
|
121
123
|
|
122
124
|
# By default, MySQL 'where id is null' selects the last inserted id
|
123
125
|
sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
|
@@ -46,8 +46,9 @@ module Sequel
|
|
46
46
|
sqls << "SET NAMES #{conn.escape(encoding.to_s)}"
|
47
47
|
end
|
48
48
|
|
49
|
-
#
|
50
|
-
|
49
|
+
# Increase timeout so mysql server doesn't disconnect us.
|
50
|
+
# Value used by default is maximum allowed value on Windows.
|
51
|
+
sqls << "SET @@wait_timeout = #{opts[:timeout] || 2147483}"
|
51
52
|
|
52
53
|
# By default, MySQL 'where id is null' selects the last inserted id
|
53
54
|
sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
|
@@ -30,6 +30,15 @@ module Sequel
|
|
30
30
|
from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
|
31
31
|
end
|
32
32
|
|
33
|
+
def views(opts={})
|
34
|
+
ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW')
|
35
|
+
ds.map{|r| ds.send(:output_identifier, r[:tname])}
|
36
|
+
end
|
37
|
+
|
38
|
+
def view_exists?(name)
|
39
|
+
from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'VIEW').count > 0
|
40
|
+
end
|
41
|
+
|
33
42
|
private
|
34
43
|
|
35
44
|
def auto_increment_sql
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
synchronize(opts[:server]) do |conn|
|
65
65
|
begin
|
66
66
|
res = nil
|
67
|
-
log_yield(sql){res = conn.
|
67
|
+
log_yield(sql){conn.execute(sql); res = conn.results}
|
68
68
|
yield res if block_given?
|
69
69
|
nil
|
70
70
|
rescue SwiftError => e
|
@@ -92,9 +92,12 @@ module Sequel
|
|
92
92
|
def execute_insert(sql, opts={})
|
93
93
|
synchronize(opts[:server]) do |conn|
|
94
94
|
begin
|
95
|
-
|
95
|
+
res = nil
|
96
|
+
log_yield(sql){conn.execute(sql); (res = conn.results).insert_id}
|
96
97
|
rescue SwiftError => e
|
97
98
|
raise_error(e)
|
99
|
+
ensure
|
100
|
+
res.finish if res
|
98
101
|
end
|
99
102
|
end
|
100
103
|
end
|
@@ -46,6 +46,21 @@ module Sequel
|
|
46
46
|
Sequel::Swift::Postgres::Dataset.new(self, opts)
|
47
47
|
end
|
48
48
|
|
49
|
+
# Run the SELECT SQL on the database and yield the rows
|
50
|
+
def execute(sql, opts={})
|
51
|
+
synchronize(opts[:server]) do |conn|
|
52
|
+
begin
|
53
|
+
conn.execute(sql)
|
54
|
+
res = conn.results
|
55
|
+
yield res if block_given?
|
56
|
+
rescue SwiftError => e
|
57
|
+
raise_error(e)
|
58
|
+
ensure
|
59
|
+
res.finish if res
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
49
64
|
# Run the DELETE/UPDATE SQL on the database and return the number
|
50
65
|
# of matched rows.
|
51
66
|
def execute_dui(sql, opts={})
|
@@ -62,8 +77,12 @@ module Sequel
|
|
62
77
|
# for the record.
|
63
78
|
def execute_insert(sql, opts={})
|
64
79
|
synchronize(opts[:server]) do |conn|
|
65
|
-
|
66
|
-
|
80
|
+
begin
|
81
|
+
conn.execute(sql)
|
82
|
+
insert_result(conn, opts[:table], opts[:values])
|
83
|
+
rescue SwiftError => e
|
84
|
+
raise_error(e)
|
85
|
+
end
|
67
86
|
end
|
68
87
|
end
|
69
88
|
|
@@ -15,6 +15,8 @@ module Sequel
|
|
15
15
|
# Raises Sequel::AdapterNotFound if the adapter
|
16
16
|
# could not be loaded.
|
17
17
|
def self.adapter_class(scheme)
|
18
|
+
return scheme if scheme.is_a?(Class)
|
19
|
+
|
18
20
|
scheme = scheme.to_s.gsub('-', '_').to_sym
|
19
21
|
|
20
22
|
unless klass = ADAPTER_MAP[scheme]
|
@@ -58,7 +60,7 @@ module Sequel
|
|
58
60
|
end
|
59
61
|
when Hash
|
60
62
|
opts = conn_string.merge(opts)
|
61
|
-
c = adapter_class(opts[:adapter] || opts['adapter'])
|
63
|
+
c = adapter_class(opts[:adapter_class] || opts[:adapter] || opts['adapter'])
|
62
64
|
else
|
63
65
|
raise Error, "Sequel::Database.connect takes either a Hash or a String, given: #{conn_string.inspect}"
|
64
66
|
end
|
@@ -12,6 +12,11 @@ module Sequel
|
|
12
12
|
# Array of SQL loggers to use for this database.
|
13
13
|
attr_accessor :loggers
|
14
14
|
|
15
|
+
# Log level at which to log SQL queries. This is actually the method
|
16
|
+
# sent to the logger, so it should be the method name symbol. The default
|
17
|
+
# is :info, it can be set to :debug to log at DEBUG level.
|
18
|
+
attr_accessor :sql_log_level
|
19
|
+
|
15
20
|
# Log a message at level info to all loggers.
|
16
21
|
def log_info(message, args=nil)
|
17
22
|
log_each(:info, args ? "#{message}; #{args.inspect}" : message)
|
@@ -51,7 +56,7 @@ module Sequel
|
|
51
56
|
# Log message with message prefixed by duration at info level, or
|
52
57
|
# warn level if duration is greater than log_warn_duration.
|
53
58
|
def log_duration(duration, message)
|
54
|
-
log_each((lwd = log_warn_duration and duration >= lwd) ? :warn :
|
59
|
+
log_each((lwd = log_warn_duration and duration >= lwd) ? :warn : sql_log_level, "(#{sprintf('%0.6fs', duration)}) #{message}")
|
55
60
|
end
|
56
61
|
|
57
62
|
# Log message at level (which should be :error, :warn, or :info)
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -32,6 +32,7 @@ module Sequel
|
|
32
32
|
# :quote_identifiers :: Whether to quote identifiers
|
33
33
|
# :servers :: A hash specifying a server/shard specific options, keyed by shard symbol
|
34
34
|
# :single_threaded :: Whether to use a single-threaded connection pool
|
35
|
+
# :sql_log_level :: Method to use to log SQL to a logger, :info by default.
|
35
36
|
#
|
36
37
|
# All options given are also passed to the connection pool. If a block
|
37
38
|
# is given, it is used as the connection_proc for the ConnectionPool.
|
@@ -43,6 +44,7 @@ module Sequel
|
|
43
44
|
@opts[:disconnection_proc] ||= proc{|conn| disconnect_connection(conn)}
|
44
45
|
block ||= proc{|server| connect(server)}
|
45
46
|
@opts[:servers] = {} if @opts[:servers].is_a?(String)
|
47
|
+
@opts[:adapter_class] = self.class
|
46
48
|
|
47
49
|
@opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, @@single_threaded))
|
48
50
|
@schemas = {}
|
@@ -52,6 +54,7 @@ module Sequel
|
|
52
54
|
@identifier_input_method = nil
|
53
55
|
@identifier_output_method = nil
|
54
56
|
@quote_identifiers = nil
|
57
|
+
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
|
55
58
|
@pool = ConnectionPool.get_pool(@opts, &block)
|
56
59
|
|
57
60
|
::Sequel::DATABASES.push(self)
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -15,7 +15,8 @@ module Sequel
|
|
15
15
|
# # SELECT ..., table.column AS some_alias
|
16
16
|
# # => {:table=>{:column=>some_alias_value, ...}, ...}
|
17
17
|
def add_graph_aliases(graph_aliases)
|
18
|
-
|
18
|
+
columns, graph_aliases = graph_alias_columns(graph_aliases)
|
19
|
+
ds = select_more(*columns)
|
19
20
|
ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || (ds.opts[:graph][:column_aliases] rescue {}) || {}).merge(graph_aliases)
|
20
21
|
ds
|
21
22
|
end
|
@@ -183,20 +184,25 @@ module Sequel
|
|
183
184
|
# graphing is used.
|
184
185
|
#
|
185
186
|
# graph_aliases :: Should be a hash with keys being symbols of
|
186
|
-
# column aliases, and values being arrays with
|
187
|
-
#
|
188
|
-
#
|
187
|
+
# column aliases, and values being either symbols or arrays with one to three elements.
|
188
|
+
# If the value is a symbol, it is assumed to be the same as a one element
|
189
|
+
# array containing that symbol.
|
190
|
+
# The first element of the array should be the table alias symbol.
|
191
|
+
# The second should be the actual column name symbol. If the array only
|
192
|
+
# has a single element the column name symbol will be assumed to be the
|
193
|
+
# same as the corresponding hash key. If the array
|
189
194
|
# has a third element, it is used as the value returned, instead of
|
190
195
|
# table_alias.column_name.
|
191
196
|
#
|
192
197
|
# DB[:artists].graph(:albums, :artist_id=>:id).
|
193
|
-
# set_graph_aliases(:
|
198
|
+
# set_graph_aliases(:name=>:artists,
|
194
199
|
# :album_name=>[:albums, :name],
|
195
200
|
# :forty_two=>[:albums, :fourtwo, 42]).first
|
196
|
-
# # SELECT artists.name
|
201
|
+
# # SELECT artists.name, albums.name AS album_name, 42 AS forty_two ...
|
197
202
|
# # => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name, :fourtwo=>42}}
|
198
203
|
def set_graph_aliases(graph_aliases)
|
199
|
-
|
204
|
+
columns, graph_aliases = graph_alias_columns(graph_aliases)
|
205
|
+
ds = select(*columns)
|
200
206
|
ds.opts[:graph_aliases] = graph_aliases
|
201
207
|
ds
|
202
208
|
end
|
@@ -204,18 +210,25 @@ module Sequel
|
|
204
210
|
# Remove the splitting of results into subhashes, and all metadata
|
205
211
|
# related to the current graph (if any).
|
206
212
|
def ungraphed
|
207
|
-
clone(:graph=>nil)
|
213
|
+
clone(:graph=>nil, :graph_aliases=>nil)
|
208
214
|
end
|
209
215
|
|
210
216
|
private
|
211
217
|
|
212
|
-
# Transform the hash of graph aliases
|
218
|
+
# Transform the hash of graph aliases and return a two element array
|
219
|
+
# where the first element is an array of identifiers suitable to pass to
|
220
|
+
# a select method, and the second is a new hash of preprocessed graph aliases.
|
213
221
|
def graph_alias_columns(graph_aliases)
|
214
|
-
|
215
|
-
|
216
|
-
|
222
|
+
gas = {}
|
223
|
+
identifiers = graph_aliases.collect do |col_alias, tc|
|
224
|
+
table, column, value = Array(tc)
|
225
|
+
column ||= col_alias
|
226
|
+
gas[col_alias] = [table, column]
|
227
|
+
identifier = value || SQL::QualifiedIdentifier.new(table, column)
|
228
|
+
identifier = SQL::AliasedExpression.new(identifier, col_alias) if value or column != col_alias
|
217
229
|
identifier
|
218
230
|
end
|
231
|
+
[identifiers, gas]
|
219
232
|
end
|
220
233
|
|
221
234
|
# Fetch the rows, split them into component table parts,
|
@@ -97,7 +97,7 @@ module Sequel
|
|
97
97
|
def need_associated_primary_key?
|
98
98
|
false
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
# Returns the reciprocal association variable, if one exists. The reciprocal
|
102
102
|
# association is the association in the associated class that is the opposite
|
103
103
|
# of the current association. For example, Album.many_to_one :artist and
|
@@ -128,6 +128,12 @@ module Sequel
|
|
128
128
|
:"remove_all_#{self[:name]}"
|
129
129
|
end
|
130
130
|
|
131
|
+
# Whether associated objects need to be removed from the association before
|
132
|
+
# being destroyed in order to preserve referential integrity.
|
133
|
+
def remove_before_destroy?
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
131
137
|
# Name symbol for the remove_ association method
|
132
138
|
def remove_method
|
133
139
|
:"remove_#{singularize(self[:name])}"
|
@@ -144,7 +150,7 @@ module Sequel
|
|
144
150
|
true
|
145
151
|
end
|
146
152
|
|
147
|
-
# The columns to select when loading the association
|
153
|
+
# The columns to select when loading the association.
|
148
154
|
def select
|
149
155
|
self[:select]
|
150
156
|
end
|
@@ -259,22 +265,27 @@ module Sequel
|
|
259
265
|
self[:primary_key] ||= self[:model].primary_key
|
260
266
|
end
|
261
267
|
|
262
|
-
# One to many associations set the reciprocal to self when loading associated records.
|
263
|
-
def set_reciprocal_to_self?
|
264
|
-
true
|
265
|
-
end
|
266
|
-
|
267
268
|
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
268
269
|
# false for a one_to_many association.
|
269
270
|
def reciprocal_array?
|
270
271
|
false
|
271
272
|
end
|
272
273
|
|
274
|
+
# Destroying one_to_many associated objects automatically deletes the foreign key.
|
275
|
+
def remove_before_destroy?
|
276
|
+
false
|
277
|
+
end
|
278
|
+
|
273
279
|
# The one_to_many association needs to check that an object to be removed already is associated.
|
274
280
|
def remove_should_check_existing?
|
275
281
|
true
|
276
282
|
end
|
277
283
|
|
284
|
+
# One to many associations set the reciprocal to self when loading associated records.
|
285
|
+
def set_reciprocal_to_self?
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
278
289
|
private
|
279
290
|
|
280
291
|
# The reciprocal type of a one_to_many association is a many_to_one association.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1236,7 +1236,7 @@ module Sequel
|
|
1236
1236
|
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
1237
1237
|
changed_columns.reject!{|c| columns.include?(c)}
|
1238
1238
|
end
|
1239
|
-
|
1239
|
+
_update_columns(@columns_updated)
|
1240
1240
|
@this = nil
|
1241
1241
|
after_update
|
1242
1242
|
after_save
|
@@ -1264,7 +1264,14 @@ module Sequel
|
|
1264
1264
|
Array(primary_key).each{|x| v.delete(x) unless changed_columns.include?(x)}
|
1265
1265
|
v
|
1266
1266
|
end
|
1267
|
-
|
1267
|
+
|
1268
|
+
# Call _update with the given columns, if any are present.
|
1269
|
+
# Plugins can override this method in order to update with
|
1270
|
+
# additional columns, even when the column hash is initially empty.
|
1271
|
+
def _update_columns(columns)
|
1272
|
+
_update(columns) unless columns.empty?
|
1273
|
+
end
|
1274
|
+
|
1268
1275
|
# Update this instance's dataset with the supplied column hash.
|
1269
1276
|
def _update(columns)
|
1270
1277
|
n = _update_dataset.update(columns)
|
@@ -24,7 +24,8 @@ module Sequel
|
|
24
24
|
#
|
25
25
|
# The identity_map plugin is not compatible with the standard eager loading of
|
26
26
|
# many_to_many and many_through_many associations. If you want to use the identity_map plugin,
|
27
|
-
# you should use +eager_graph+ instead of +eager+ for those associations.
|
27
|
+
# you should use +eager_graph+ instead of +eager+ for those associations. It is also
|
28
|
+
# not compatible with the eager loading in the +rcte_tree+ plugin.
|
28
29
|
#
|
29
30
|
# Usage:
|
30
31
|
#
|
@@ -141,8 +141,8 @@ module Sequel
|
|
141
141
|
# array of symbols for a composite key association.
|
142
142
|
# * :uniq - Adds a after_load callback that makes the array of objects unique.
|
143
143
|
def many_through_many(name, through, opts={}, &block)
|
144
|
-
associate(:many_through_many, name, opts.merge(:through=>through), &block)
|
145
|
-
end
|
144
|
+
associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
|
145
|
+
end
|
146
146
|
|
147
147
|
private
|
148
148
|
|
@@ -123,15 +123,18 @@ module Sequel
|
|
123
123
|
end
|
124
124
|
|
125
125
|
# Remove the matching associated object from the current object.
|
126
|
-
# If the :destroy option is given, destroy the object after disassociating it
|
126
|
+
# If the :destroy option is given, destroy the object after disassociating it
|
127
|
+
# (unless destroying the object would automatically disassociate it).
|
127
128
|
# Returns the object removed, if it exists.
|
128
129
|
def nested_attributes_remove(reflection, pk, opts={})
|
129
130
|
if obj = nested_attributes_find(reflection, pk)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
if !opts[:destroy] || reflection.remove_before_destroy?
|
132
|
+
before_save_hook do
|
133
|
+
if reflection.returns_array?
|
134
|
+
send(reflection.remove_method, obj)
|
135
|
+
else
|
136
|
+
send(reflection.setter_method, nil)
|
137
|
+
end
|
135
138
|
end
|
136
139
|
end
|
137
140
|
after_save_hook{obj.destroy} if opts[:destroy]
|
@@ -205,9 +205,9 @@ module Sequel
|
|
205
205
|
klass.from_xml_node(node)
|
206
206
|
end
|
207
207
|
elsif cols.include?(k)
|
208
|
-
self[k.to_sym] = node.children.first.to_s
|
208
|
+
self[k.to_sym] = node[:nil] ? nil : node.children.first.to_s
|
209
209
|
elsif meths.include?("#{k}=")
|
210
|
-
send("#{k}=", node.children.first.to_s)
|
210
|
+
send("#{k}=", node[:nil] ? nil : node.children.first.to_s)
|
211
211
|
else
|
212
212
|
raise Error, "Entry in XML not an association or column and no setter method exists: #{k}"
|
213
213
|
end
|
@@ -268,7 +268,15 @@ module Sequel
|
|
268
268
|
x = model.xml_builder(opts)
|
269
269
|
x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name)).to_s]) do |x1|
|
270
270
|
cols.each do |c|
|
271
|
-
|
271
|
+
attrs = {}
|
272
|
+
if types
|
273
|
+
attrs[:type] = db_schema.fetch(c, {})[:type]
|
274
|
+
end
|
275
|
+
v = vals[c]
|
276
|
+
if v.nil?
|
277
|
+
attrs[:nil] = ''
|
278
|
+
end
|
279
|
+
x1.send(name_proc[c.to_s], v, attrs)
|
272
280
|
end
|
273
281
|
if inc.is_a?(Hash)
|
274
282
|
inc.each{|k, v| to_xml_include(x1, k, v)}
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 3
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 17
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
data/spec/core/database_spec.rb
CHANGED
@@ -23,6 +23,11 @@ context "A new Database" do
|
|
23
23
|
Sequel::Database.new(1 => 2, :logger => [4], :loggers => [3]).loggers.should == [4,3]
|
24
24
|
end
|
25
25
|
|
26
|
+
specify "should set the sql_log_level from opts[:sql_log_level]" do
|
27
|
+
db = Sequel::Database.new(1 => 2, :sql_log_level=>:debug).sql_log_level.should == :debug
|
28
|
+
db = Sequel::Database.new(1 => 2, :sql_log_level=>'debug').sql_log_level.should == :debug
|
29
|
+
end
|
30
|
+
|
26
31
|
specify "should create a connection pool" do
|
27
32
|
@db.pool.should be_a_kind_of(Sequel::ConnectionPool)
|
28
33
|
@db.pool.max_size.should == 4
|
@@ -262,6 +267,15 @@ context "Database#log_yield" do
|
|
262
267
|
@o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
|
263
268
|
end
|
264
269
|
|
270
|
+
specify "should respect sql_log_level setting" do
|
271
|
+
@db.sql_log_level = :debug
|
272
|
+
@db.log_yield('blah'){}
|
273
|
+
@o.logs.length.should == 1
|
274
|
+
@o.logs.first.length.should == 2
|
275
|
+
@o.logs.first.first.should == :debug
|
276
|
+
@o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
|
277
|
+
end
|
278
|
+
|
265
279
|
specify "should log message with duration at warn level if duration greater than log_warn_duration" do
|
266
280
|
@db.log_warn_duration = 0
|
267
281
|
@db.log_yield('blah'){}
|
@@ -954,19 +968,19 @@ context "A Database adapter with a scheme" do
|
|
954
968
|
c = Sequel.ccc('mydb')
|
955
969
|
p = proc{c.opts.delete_if{|k,v| k == :disconnection_proc || k == :single_threaded}}
|
956
970
|
c.should be_a_kind_of(CCC)
|
957
|
-
p.call.should == {:adapter=>:ccc, :database => 'mydb'}
|
971
|
+
p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>CCC}
|
958
972
|
|
959
973
|
c = Sequel.ccc('mydb', :host => 'localhost')
|
960
974
|
c.should be_a_kind_of(CCC)
|
961
|
-
p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost'}
|
975
|
+
p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
|
962
976
|
|
963
977
|
c = Sequel.ccc
|
964
978
|
c.should be_a_kind_of(CCC)
|
965
|
-
p.call.should == {:adapter=>:ccc}
|
979
|
+
p.call.should == {:adapter=>:ccc, :adapter_class=>CCC}
|
966
980
|
|
967
981
|
c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
|
968
982
|
c.should be_a_kind_of(CCC)
|
969
|
-
p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost'}
|
983
|
+
p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
|
970
984
|
end
|
971
985
|
|
972
986
|
specify "should be accessible through Sequel.connect with options" do
|
@@ -1484,6 +1498,31 @@ context "Database#remove_servers" do
|
|
1484
1498
|
end
|
1485
1499
|
end
|
1486
1500
|
|
1501
|
+
context "Database#each_server with do/jdbc adapter connection string without :adapter option" do
|
1502
|
+
before do
|
1503
|
+
klass = Class.new(Sequel::Database)
|
1504
|
+
klass.should_receive(:adapter_class).once.with(:jdbc).and_return(MockDatabase)
|
1505
|
+
@db = klass.connect('jdbc:blah:', :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
|
1506
|
+
def @db.connect(server)
|
1507
|
+
server_opts(server)
|
1508
|
+
end
|
1509
|
+
def @db.disconnect_connection(c)
|
1510
|
+
end
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
specify "should yield a separate database object for each server" do
|
1514
|
+
hosts = []
|
1515
|
+
@db.each_server do |db|
|
1516
|
+
db.should be_a_kind_of(Sequel::Database)
|
1517
|
+
db.should_not == @db
|
1518
|
+
db.opts[:adapter_class].should == MockDatabase
|
1519
|
+
db.opts[:database].should == 2
|
1520
|
+
hosts << db.opts[:host]
|
1521
|
+
end
|
1522
|
+
hosts.sort.should == [1, 3]
|
1523
|
+
end
|
1524
|
+
end
|
1525
|
+
|
1487
1526
|
context "Database#each_server" do
|
1488
1527
|
before do
|
1489
1528
|
@db = Sequel.connect(:adapter=>:mock, :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
|
@@ -160,6 +160,20 @@ describe Sequel::Dataset, " graphing" do
|
|
160
160
|
].should(include(ds.sql))
|
161
161
|
end
|
162
162
|
|
163
|
+
it "#set_graph_aliases should allow a single array entry to specify a table, assuming the same column as the key" do
|
164
|
+
ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points], :y=>[:lines])
|
165
|
+
['SELECT points.x, lines.y FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)',
|
166
|
+
'SELECT lines.y, points.x FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
167
|
+
].should(include(ds.sql))
|
168
|
+
end
|
169
|
+
|
170
|
+
it "#set_graph_aliases should allow hash values to be symbols specifying table, assuming the same column as the key" do
|
171
|
+
ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>:points, :y=>:lines)
|
172
|
+
['SELECT points.x, lines.y FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)',
|
173
|
+
'SELECT lines.y, points.x FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
174
|
+
].should(include(ds.sql))
|
175
|
+
end
|
176
|
+
|
163
177
|
it "#set_graph_aliases should only alias columns if necessary" do
|
164
178
|
ds = @ds1.set_graph_aliases(:x=>[:points, :x], :y=>[:lines, :y])
|
165
179
|
['SELECT points.x, lines.y FROM points',
|
@@ -282,6 +296,26 @@ describe Sequel::Dataset, " graphing" do
|
|
282
296
|
results.first.should == {:points=>{:z1=>2}, :lines=>{:z2=>3}}
|
283
297
|
end
|
284
298
|
|
299
|
+
it "#graph_each should correctly map values when #set_graph_aliases is used with a single argument for each entry" do
|
300
|
+
ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points], :y=>[:lines])
|
301
|
+
def ds.fetch_rows(sql, &block)
|
302
|
+
yield({:x=>2,:y=>3})
|
303
|
+
end
|
304
|
+
results = ds.all
|
305
|
+
results.length.should == 1
|
306
|
+
results.first.should == {:points=>{:x=>2}, :lines=>{:y=>3}}
|
307
|
+
end
|
308
|
+
|
309
|
+
it "#graph_each should correctly map values when #set_graph_aliases is used with a symbol for each entry" do
|
310
|
+
ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>:points, :y=>:lines)
|
311
|
+
def ds.fetch_rows(sql, &block)
|
312
|
+
yield({:x=>2,:y=>3})
|
313
|
+
end
|
314
|
+
results = ds.all
|
315
|
+
results.length.should == 1
|
316
|
+
results.first.should == {:points=>{:x=>2}, :lines=>{:y=>3}}
|
317
|
+
end
|
318
|
+
|
285
319
|
it "#graph_each should run the row_proc for graphed datasets" do
|
286
320
|
@ds1.row_proc = proc{|h| h.keys.each{|k| h[k] *= 2}; h}
|
287
321
|
@ds2.row_proc = proc{|h| h.keys.each{|k| h[k] *= 3}; h}
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
2
2
|
|
3
|
+
if defined?(ActiveSupport::Inflector)
|
4
|
+
skip_warn "inflector extension: active_support inflector loaded"
|
5
|
+
else
|
3
6
|
describe String do
|
4
7
|
it "#camelize and #camelcase should transform the word to CamelCase" do
|
5
8
|
"egg_and_hams".camelize.should == "EggAndHams"
|
@@ -179,3 +182,4 @@ describe 'Default inflections' do
|
|
179
182
|
end
|
180
183
|
end
|
181
184
|
end
|
185
|
+
end
|
@@ -42,6 +42,25 @@ describe Sequel::Model, "many_through_many" do
|
|
42
42
|
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], :album_tags]}.should raise_error(Sequel::Error)
|
43
43
|
end
|
44
44
|
|
45
|
+
it "should allow only two arguments with the :through option" do
|
46
|
+
@c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
47
|
+
n = @c1.load(:id => 1234)
|
48
|
+
a = n.tags_dataset
|
49
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
50
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
51
|
+
n.tags.should == [@c2.load(:id=>1)]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be clonable" do
|
55
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
56
|
+
@c1.many_through_many :other_tags, :clone=>:tags
|
57
|
+
n = @c1.load(:id => 1234)
|
58
|
+
a = n.other_tags_dataset
|
59
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
60
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
61
|
+
n.tags.should == [@c2.load(:id=>1)]
|
62
|
+
end
|
63
|
+
|
45
64
|
it "should use join tables given" do
|
46
65
|
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
47
66
|
n = @c1.load(:id => 1234)
|
@@ -186,7 +186,7 @@ describe "NestedAttributes plugin" do
|
|
186
186
|
ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
|
187
187
|
@mods.should == []
|
188
188
|
ar.save
|
189
|
-
@mods.should == [[:u, :
|
189
|
+
@mods.should == [[:u, :artists, {:name=>"Ar"}, "(id = 20)"], [:d, :albums, "(id = 10)"]]
|
190
190
|
end
|
191
191
|
|
192
192
|
it "should support destroying one_to_many objects" do
|
@@ -196,7 +196,7 @@ describe "NestedAttributes plugin" do
|
|
196
196
|
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
197
197
|
@mods.should == []
|
198
198
|
ar.save
|
199
|
-
@mods.should == [[:u, :
|
199
|
+
@mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:d, :albums, '(id = 10)']]
|
200
200
|
end
|
201
201
|
|
202
202
|
it "should support destroying many_to_many objects" do
|
@@ -20,6 +20,14 @@ describe "optimistic_locking plugin" do
|
|
20
20
|
else
|
21
21
|
0
|
22
22
|
end
|
23
|
+
when /UPDATE people SET #{lv} = (\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
24
|
+
m = h[$2.to_i]
|
25
|
+
if m && m[:lock_version] == $3.to_i
|
26
|
+
m.merge!(:lock_version=>$1.to_i)
|
27
|
+
1
|
28
|
+
else
|
29
|
+
0
|
30
|
+
end
|
23
31
|
else
|
24
32
|
puts update_sql(opts)
|
25
33
|
end
|
@@ -96,5 +104,22 @@ describe "optimistic_locking plugin" do
|
|
96
104
|
p2 = c[1]
|
97
105
|
p1.update(:name=>'Jim')
|
98
106
|
proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
99
|
-
end
|
107
|
+
end
|
108
|
+
|
109
|
+
specify "should increment the lock column when #modified! even if no columns are changed" do
|
110
|
+
p1 = @c[1]
|
111
|
+
p1.modified!
|
112
|
+
lv = p1.lock_version
|
113
|
+
p1.save_changes
|
114
|
+
p1.lock_version.should == lv + 1
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "should not increment the lock column when the update fails" do
|
118
|
+
@c.dataset.meta_def(:update) { raise Exception }
|
119
|
+
p1 = @c[1]
|
120
|
+
p1.modified!
|
121
|
+
lv = p1.lock_version
|
122
|
+
proc{p1.save_changes}.should raise_error(Exception)
|
123
|
+
p1.lock_version.should == lv
|
124
|
+
end
|
100
125
|
end
|
@@ -36,6 +36,16 @@ describe "Sequel::Plugins::XmlSerializer" do
|
|
36
36
|
Album.from_xml(@album.to_xml).should == @album
|
37
37
|
end
|
38
38
|
|
39
|
+
it "should round trip successfully with empty strings" do
|
40
|
+
artist = Artist.load(:id=>2, :name=>'')
|
41
|
+
Artist.from_xml(artist.to_xml).should == artist
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should round trip successfully with nil values" do
|
45
|
+
artist = Artist.load(:id=>2, :name=>nil)
|
46
|
+
Artist.from_xml(artist.to_xml).should == artist
|
47
|
+
end
|
48
|
+
|
39
49
|
it "should handle the :only option" do
|
40
50
|
Artist.from_xml(@artist.to_xml(:only=>:name)).should == Artist.load(:name=>@artist.name)
|
41
51
|
Album.from_xml(@album.to_xml(:only=>[:id, :name])).should == Album.load(:id=>@album.id, :name=>@album.name)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 67
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
-
|
8
|
+
- 17
|
9
9
|
- 0
|
10
|
-
version: 3.
|
10
|
+
version: 3.17.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Evans
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-05 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -80,6 +80,7 @@ extra_rdoc_files:
|
|
80
80
|
- doc/release_notes/3.14.0.txt
|
81
81
|
- doc/release_notes/3.15.0.txt
|
82
82
|
- doc/release_notes/3.16.0.txt
|
83
|
+
- doc/release_notes/3.17.0.txt
|
83
84
|
files:
|
84
85
|
- COPYING
|
85
86
|
- CHANGELOG
|
@@ -127,6 +128,7 @@ files:
|
|
127
128
|
- doc/release_notes/3.14.0.txt
|
128
129
|
- doc/release_notes/3.15.0.txt
|
129
130
|
- doc/release_notes/3.16.0.txt
|
131
|
+
- doc/release_notes/3.17.0.txt
|
130
132
|
- doc/sharding.rdoc
|
131
133
|
- doc/sql.rdoc
|
132
134
|
- doc/virtual_rows.rdoc
|