sequel 4.2.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +28 -0
- data/doc/extensions.rdoc +84 -0
- data/doc/model_plugins.rdoc +270 -0
- data/doc/release_notes/4.3.0.txt +40 -0
- data/doc/testing.rdoc +3 -0
- data/lib/sequel/adapters/jdbc/as400.rb +4 -0
- data/lib/sequel/adapters/shared/mysql.rb +6 -1
- data/lib/sequel/adapters/shared/postgres.rb +2 -0
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/extensions/error_sql.rb +71 -0
- data/lib/sequel/extensions/migration.rb +0 -1
- data/lib/sequel/extensions/pagination.rb +6 -2
- data/lib/sequel/extensions/pg_array.rb +12 -5
- data/lib/sequel/extensions/pg_hstore.rb +5 -3
- data/lib/sequel/extensions/pg_inet.rb +3 -3
- data/lib/sequel/extensions/pg_interval.rb +3 -3
- data/lib/sequel/extensions/pg_json.rb +3 -3
- data/lib/sequel/extensions/pg_range.rb +3 -3
- data/lib/sequel/extensions/pg_row.rb +3 -3
- data/lib/sequel/extensions/server_block.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +59 -39
- data/lib/sequel/plugins/tree.rb +13 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +17 -0
- data/spec/core/dataset_spec.rb +14 -0
- data/spec/core/schema_spec.rb +1 -0
- data/spec/extensions/error_sql_spec.rb +20 -0
- data/spec/extensions/migration_spec.rb +15 -0
- data/spec/extensions/pagination_spec.rb +19 -0
- data/spec/extensions/pg_array_spec.rb +3 -2
- data/spec/extensions/rcte_tree_spec.rb +135 -0
- data/spec/extensions/tree_spec.rb +130 -0
- data/spec/integration/database_test.rb +5 -0
- data/spec/integration/dataset_test.rb +4 -0
- data/spec/integration/plugin_test.rb +163 -177
- data/spec/integration/spec_helper.rb +4 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5cf40a4c3d9c93b6ed7fa958db814a768fbc322
|
4
|
+
data.tar.gz: 06fb6c46cdf9af739fe547de06aedb598b46b11e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d05bce34cc981ada0a4429d9cfbb861f8ad52b9fe83ae604f3ea0395f27deffe766e865d4a57e5ebd791df799215570f945b488fd6e7a317bd6b9f5d821e3bd
|
7
|
+
data.tar.gz: 7bd18b9518488d92f7b45024d93ae31b0b497580e681f65cbc1ee2f1a914f1c4f69a250fd1e4759ddcd37004a6ce4c4125f81cd61b11bf51428652bd7d97fefb
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
=== 4.3.0 (2013-10-02)
|
2
|
+
|
3
|
+
* Fix literalization of empty blobs on MySQL (jeremyevans) (#715)
|
4
|
+
|
5
|
+
* Ensure Dataset#page_count in pagination extension is at least one (jeremyevans) (#714)
|
6
|
+
|
7
|
+
* Recognize another disconnect error in the jdbc/as400 adapter (jeremyevans)
|
8
|
+
|
9
|
+
* Make Dataset#qualify and Sequel.delay work together (jeremyevans)
|
10
|
+
|
11
|
+
* Recognize citext type as string on PostgreSQL (isc) (#710)
|
12
|
+
|
13
|
+
* Support composite keys in the rcte_tree plugin (jeremyevans)
|
14
|
+
|
15
|
+
* Support composite keys in the tree plugin (jeremyevans)
|
16
|
+
|
17
|
+
* Make Migrator.migrator_class public (robertjpayne, jeremyevans) (#708)
|
18
|
+
|
19
|
+
* Make PostgreSQL empty array literalization work correctly on PostgreSQL <8.4 (jeremyevans)
|
20
|
+
|
21
|
+
* Add Sequel extensions guide (jeremyevans)
|
22
|
+
|
23
|
+
* Add model plugins guide (jeremyevans)
|
24
|
+
|
25
|
+
* Add error_sql Database extension, allowing DatabaseError#sql to return SQL query that caused underlying exception (jeremyevans)
|
26
|
+
|
27
|
+
* Make Dataset#each_page in pagination extension return enumerator if no block is given (justinj) (#702)
|
28
|
+
|
1
29
|
=== 4.2.0 (2013-09-01)
|
2
30
|
|
3
31
|
* Support custom :flags option in mysql2 adapter (jeremyevans) (#700)
|
data/doc/extensions.rdoc
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
= Sequel Extensions
|
2
|
+
|
3
|
+
Sequel has an official extension system, for adding global, Database, and Dataset extensions.
|
4
|
+
|
5
|
+
== Global Extensions
|
6
|
+
|
7
|
+
Global extensions can add or modify the behavior of any part of Sequel. Technically, they are not limited to affecting Sequel, as they can also modify code outside of Sequel (e.g. the blank extension). However, extensions that modify things outside of Sequel generally do so only for backwards compatibility.
|
8
|
+
|
9
|
+
Global extensions are loaded via <tt>Sequel.extension</tt>:
|
10
|
+
|
11
|
+
Sequel.extension :named_timezones
|
12
|
+
|
13
|
+
What this does is require the relevent extension from <tt>sequel/extensions/named_timezones</tt> somewhere in the ruby path. Actually, that is all that does. Global extensions are just a simpler, consistent way to require code that modifies Sequel.
|
14
|
+
|
15
|
+
== Database Extensions
|
16
|
+
|
17
|
+
Database extensions should add or modify the behavior of a single <tt>Sequel::Database</tt> instance. They are loaded via <tt>Sequel::Database#extension</tt>:
|
18
|
+
|
19
|
+
DB.extension :server_block
|
20
|
+
|
21
|
+
The first thing that this does is load the relevent extension globally. However, Database extensions should be structured in a way that loading the relevent extension globally just adds a module with the related behavior, it doesn't modify any other state. After loading the extension globally, it modifies the related <tt>Sequel::Database</tt> object to modify it's behavior, usually by extending it with a module.
|
22
|
+
|
23
|
+
If you want a Database extension loaded into all future Database instances, you can use <tt>Sequel::Database.extension</tt>:
|
24
|
+
|
25
|
+
Sequel::Database.extension :server_block
|
26
|
+
|
27
|
+
All future <tt>Sequel::Database</tt> instances created afterward will then automatically have the server_block extension loaded.
|
28
|
+
|
29
|
+
== Dataset Extensions
|
30
|
+
|
31
|
+
Dataset extensions should add or modify the behavior of a single <tt>Sequel::Dataset</tt> instance. They are loaded via <tt>Sequel::Dataset#extension</tt> or <tt>Sequel::Dataset#extension!</tt>. <tt>Sequel::Dataset#extension</tt> returns a modifies copy of the dataset that includes the extension (similar to how most dataset query methods work):
|
32
|
+
|
33
|
+
ds = DB[:a].extension(:columns_introspection)
|
34
|
+
|
35
|
+
<tt>Sequel::Dataset#extension!</tt> modifies a dataset to include the extension (similar to how dataset mutation methods work):
|
36
|
+
|
37
|
+
ds = DB[:a]
|
38
|
+
ds.extension!(:columns_introspection)
|
39
|
+
|
40
|
+
It is recommended you only use <tt>Sequel::Dataset#extension!</tt> if you have good reasons to.
|
41
|
+
|
42
|
+
The first thing loading a Dataset extension does is load the relevent extension globally. Similar to Database extensions, loading a Dataset extension globally should not affect state other than maybe adding a module. After loading the extension globally, it modifies the related <tt>Sequel::Dataset</tt> instance to modify its behavior.
|
43
|
+
|
44
|
+
If you want to load an extension into all future datasets for a given <tt>Sequel::Database</tt> instance, you can also load it as a Database extension:
|
45
|
+
|
46
|
+
DB.extension :columns_introspection
|
47
|
+
|
48
|
+
Likewise, if you want to load an extension into all future datasets for all future databases, you can load it via <tt>Sequel::Database.extension</tt>:
|
49
|
+
|
50
|
+
Sequel::Database.extension :columns_introspection
|
51
|
+
|
52
|
+
== Creating Global Extensions
|
53
|
+
|
54
|
+
If you want to create a global extension, you just need to store your code so that you can require it via <tt>sequel/extensions/extension_name</tt>. Then users can load it via:
|
55
|
+
|
56
|
+
Sequel.extension :extension_name
|
57
|
+
|
58
|
+
It is recommended you only create a global extension if what you want to do would not work as a Database or Dataset extension.
|
59
|
+
|
60
|
+
== Creating Database Extensions
|
61
|
+
|
62
|
+
Creating Database extensions is similar to global extensions in terms of creating the file. However, somewhere in the file, you need to call <tt>Sequel::Database.register_extension</tt>. Usually you would call this with the module that will be added to the related <tt>Sequel::Database</tt> instance when the extension is loaded. For example, the server_block extension uses something like:
|
63
|
+
|
64
|
+
Sequel::Database.register_extension(:server_block, Sequel::ServerBlock)
|
65
|
+
|
66
|
+
The first argument is the name of the extension as a symbol, and the second is the module.
|
67
|
+
|
68
|
+
In some cases, just extending the <tt>Sequel::Database</tt> instance with a module is not sufficient. So <tt>Sequel::Database.register_extension</tt> also accepts a proc instead of a second argument. This proc is called with the <tt>Sequel::Database</tt> instance, and can then run any related code:
|
69
|
+
|
70
|
+
Sequel::Database.register_extension(:arbitrary_servers){|db| db.pool.extend(Sequel::ArbitraryServers)}
|
71
|
+
|
72
|
+
== Creating Dataset Extensions
|
73
|
+
|
74
|
+
Creating Dataset extensions is very similar to creating Database extensions, but instead of calling <tt>Sequel::Database.register_extension</tt>, you call <tt>Sequel::Dataset.register_extension</tt>. In general, you would call this with the module that will be added to the related <tt>Sequel::Dataset</tt> instance when the extension is loaded. For example, the columns_introspection extension uses something like:
|
75
|
+
|
76
|
+
Sequel::Dataset.register_extension(:columns_introspection, Sequel::ColumnsIntrospection)
|
77
|
+
|
78
|
+
The first argument is the name of the extension as a symbol, and the second is the module. When you call the <tt>Sequel::Dataset.register_extension</tt> method with a module, it in turn calls <tt>Sequel::Database.register_extension</tt> and adds a Database extension that loads this Dataset extension into all future Datasets created from the Database.
|
79
|
+
|
80
|
+
You can also call <tt>Sequel::Dataset.register_extension</tt> with a proc:
|
81
|
+
|
82
|
+
Sequel::Dataset.register_extension(:extension_name){|ds| ...}
|
83
|
+
|
84
|
+
Note that if you use a proc, a corresponding Database extension will not be created automatically (you can still call <tt>Sequel::Database.register_extension</tt> manually in this case).
|
@@ -0,0 +1,270 @@
|
|
1
|
+
= Model Plugins
|
2
|
+
|
3
|
+
Sequel::Model (and Sequel in general) is designed around the idea of a small core, to which application-specific behavior can easily be added. Sequel::Model implements this design using a plugin system. Plugins are modules that include submodules for model class methods, model instance methods, and model dataset methods. All plugins can override the class, instance, and dataset methods added by earlier plugins, and call super to get the behavior before the plugin was added.
|
4
|
+
|
5
|
+
== Default Plugins
|
6
|
+
|
7
|
+
The Sequel::Model class is completely empty by default, in that it has no class methods or instance methods. Sequel::Model is itself a plugin, and it is the first plugin loaded, and it is loaded into itself (meta!). So methods in Sequel::Model::ClassMethods become Sequel::Model class methods, methods in Sequel::Model::InstanceMethods become Sequel::Model instance methods, and methods in Sequel::Model::DatasetMethods become Sequel::Model dataset methods. The Sequel::Model plugin is often referred to as the base plugin.
|
8
|
+
|
9
|
+
By default, the Sequel::Model class also has the Sequel::Model::Associations plugin loaded by default, though it is possible to disable this.
|
10
|
+
|
11
|
+
== Loading Plugins
|
12
|
+
|
13
|
+
Loading a plugin into a model class is generally as simple as calling the Sequel::Model.plugin method with the name of the plugin, for example:
|
14
|
+
|
15
|
+
Sequel::Model.plugin :subclasses
|
16
|
+
|
17
|
+
What is does is require the <tt>sequel/plugins/subclasses</tt> file, and then assumes that that file defines the <tt>Sequel::Plugins::Subclasses</tt> plugin module.
|
18
|
+
|
19
|
+
It's possible to pass module instances to the plugin method to load plugins that are stored in arbitrary files or namespaces:
|
20
|
+
|
21
|
+
Sequel::Model.plugin MyApp::Plugins::Foo
|
22
|
+
|
23
|
+
In the examples shown above, the plugin is loaded into Sequel::Model, which means it is loaded into all subclasses that are created afterward. With many plugins, you are not going to want to add them to Sequel::Model, but to a specific subclass:
|
24
|
+
|
25
|
+
class Node < Sequel::Model
|
26
|
+
plugin :tree
|
27
|
+
end
|
28
|
+
|
29
|
+
Doing this, only Node and future subclasses of Node will have the tree plugin loaded.
|
30
|
+
|
31
|
+
== Plugin Arguments/Options
|
32
|
+
|
33
|
+
Some plugins require arguments and/or support options. For example, the single_table_inheritance plugin requires an argument containing the column that specifies the class to use, and options:
|
34
|
+
|
35
|
+
class Employee < Sequel::Model
|
36
|
+
plugin :single_table_inheritance, :type_id, :model_map=>{1=>:Staff, 2=>:Manager}
|
37
|
+
end
|
38
|
+
|
39
|
+
You should read the documentation for the plugin to determine if it requires arguments and what if any options are supported.
|
40
|
+
|
41
|
+
== Creating Plugins
|
42
|
+
|
43
|
+
The simplest possible plugin is an empty module in a file stored in <tt>sequel/plugins/plugin_name</tt> somewhere in ruby's load path:
|
44
|
+
|
45
|
+
module Sequel
|
46
|
+
module Plugins
|
47
|
+
module PluginName
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Well, technically, that's not the simplest possible plugin, but it is the simplest one you can load by name. The absolute simplest plugin would be an empty module:
|
53
|
+
|
54
|
+
Sequel::Model.plugin Module.new
|
55
|
+
|
56
|
+
== Example Formatting
|
57
|
+
|
58
|
+
In general, loading plugins by module instead of by name is not recommended, so this guide will assume that plugins are loaded by name. For simplicity, we'll also use the following format for example plugin code (and assume a plugin named Foo stored in <tt>sequel/plugins/foo</tt>):
|
59
|
+
|
60
|
+
module Sequel::Plugins::Foo
|
61
|
+
end
|
62
|
+
|
63
|
+
This saves 4 lines per example. However, it's recommended that you use the nested example displayed earlier for production code.
|
64
|
+
|
65
|
+
The examples also assume that the following model class exists:
|
66
|
+
|
67
|
+
class Bar < Sequel::Model
|
68
|
+
end
|
69
|
+
|
70
|
+
== Adding Class Methods
|
71
|
+
|
72
|
+
If you want your plugin to add class methods to the model class it is loaded into, define a ClassMethods module under the plugin module:
|
73
|
+
|
74
|
+
module Sequel::Plugins::Foo
|
75
|
+
module ClassMethods
|
76
|
+
def a
|
77
|
+
1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
This allows a plugin user to do:
|
83
|
+
|
84
|
+
Bar.plugin :foo
|
85
|
+
Bar.a # => 1
|
86
|
+
|
87
|
+
== Adding Instance Methods
|
88
|
+
|
89
|
+
If you want your plugin to add instance methods to the model class it is loaded into, define an InstanceMethods module under the plugin module:
|
90
|
+
|
91
|
+
module Sequel::Plugins::Foo
|
92
|
+
module InstanceMethods
|
93
|
+
def a
|
94
|
+
1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
This allows a plugin user to do:
|
100
|
+
|
101
|
+
Bar.plugin :foo
|
102
|
+
Bar.new.a # => 1
|
103
|
+
|
104
|
+
== Adding Dataset Methods
|
105
|
+
|
106
|
+
If you want your plugin to add methods to the dataset of the model class it is loaded into, define a DatasetMethods module under the plugin module:
|
107
|
+
|
108
|
+
module Sequel::Plugins::Foo
|
109
|
+
module DatasetMethods
|
110
|
+
def a
|
111
|
+
1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
This allows a plugin user to do:
|
117
|
+
|
118
|
+
Bar.plugin :foo
|
119
|
+
Bar.dataset.a # => 1
|
120
|
+
|
121
|
+
== Calling super to get Previous Behavior
|
122
|
+
|
123
|
+
No matter if you are dealing with class, instance, or dataset methods, you can call super inside the method to get the previous behavior. This makes it easy to hook into the method, add your own behavior, but still (optionally?) get the previous behavior:
|
124
|
+
|
125
|
+
module Sequel::Plugins::Foo
|
126
|
+
module InstanceMethods
|
127
|
+
def save
|
128
|
+
if allow_saving?
|
129
|
+
super
|
130
|
+
else
|
131
|
+
raise Sequel::Error, 'saving not allowed for this object'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def allow_saving?
|
138
|
+
`pom` =~ /Waxing/
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
== Running Code When the Plugin is Loaded
|
144
|
+
|
145
|
+
Some plugins require more than just adding methods. Any plugin that requires state is going to have to initialize that state and store it somewhere. If you want to run code when a plugin is loaded (usually to initialize state, but possibly for other reasons), there are two methods you can define to do so. The first method is apply, and it is called only the first time the plugin is loaded into the class, before it is loaded into the class. This is generally only used if a plugin depends on another plugin or for initializing state. You define this method as a singleton method of the plugin module:
|
146
|
+
|
147
|
+
module Sequel::Plugins::Foo
|
148
|
+
def self.apply(model)
|
149
|
+
model.instance_eval do
|
150
|
+
plugin :plugin_that_foo_depends_on
|
151
|
+
@foo_states = {}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
The other method is called configure, and it is called everytime the plugin is loaded into the class, after it is loaded into the class:
|
157
|
+
|
158
|
+
module Sequel::Plugins::Foo
|
159
|
+
def self.configure(model)
|
160
|
+
model.instance_eval do
|
161
|
+
@foo_state[:initial] = :baz
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
Note that in the configure method, you know apply has already been called at least once (so @foo_state will definitely exist).
|
167
|
+
|
168
|
+
If you want your plugin to take arguments and/or support options, you handle that by making your apply and configure methods take arguments and/or an options hash. For example, if you want the user to be able to set the initial state via an option, you can do:
|
169
|
+
|
170
|
+
module Sequel::Plugins::Foo
|
171
|
+
def self.apply(model, opts={})
|
172
|
+
model.instance_eval do
|
173
|
+
plugin :plugin_foo_depends_on
|
174
|
+
@foo_states = {}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.configure(model, opts={})
|
179
|
+
model.instance_eval do
|
180
|
+
@foo_state[:initial] = opts[:initial_state] || :baz
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
This allows a user of the plugin to do either of the following
|
186
|
+
|
187
|
+
Bar.plugin :foo
|
188
|
+
Bar.plugin :foo, :initial_state=>:quux
|
189
|
+
|
190
|
+
If you want to require the initial state to be provided as an argument:
|
191
|
+
|
192
|
+
module Sequel::Plugins::Foo
|
193
|
+
def self.apply(model, initial_state)
|
194
|
+
model.instance_eval do
|
195
|
+
plugin :plugin_foo_depends_on
|
196
|
+
@foo_states = {}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.configure(model, initial_state)
|
201
|
+
model.instance_eval do
|
202
|
+
@foo_state[:initial] = initial_state
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
This requires that the user of the plugin specify the argument:
|
208
|
+
|
209
|
+
Bar.plugin :foo, :quux
|
210
|
+
|
211
|
+
In general you should only require plugin arguments if you absolutely must have a value and there is no good default.
|
212
|
+
|
213
|
+
== Handling Subclasses
|
214
|
+
|
215
|
+
Sequel::Model uses a copy-on-subclassing approach to model state. So instead of model subclasses asking their parent class for a value if they don't have it defined, the value is automatically copied from the parent class to the subclass when the subclass is created. While you can do this by overriding the inherited class method, there is a available shortcut that handles most cases:
|
216
|
+
|
217
|
+
module Sequel::Plugins::Foo
|
218
|
+
module ClassMethods
|
219
|
+
Sequel::Plugins.inherited_instance_variables(self, :@foo_state=>:dup)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
Inside the ClassMethods submodule, you call the Sequel::Plugins.inherited_instance_variables method with the first argument being self. The second argument should be a hash describing how to copy the value from the parent class into the subclass. The keys of this hash are instance variable names, including the @ symbol (e.g. :@foo_state). The values of this hash describe how to copy it:
|
224
|
+
|
225
|
+
nil :: Use the value directly.
|
226
|
+
:dup :: Call dup on the value.
|
227
|
+
:hash_dup :: Create a new hash with the same keys, but a dup of all the values.
|
228
|
+
Proc :: An arbitrary proc that is called with the parent class value and should return the value to set into the subclass.
|
229
|
+
|
230
|
+
== Handling Changes to the Model's Dataset
|
231
|
+
|
232
|
+
In many plugins, if the model class changes the dataset, you need to change the state for the plugin. While you can do this by overriding the set_dataset class method, there is an available shortcut:
|
233
|
+
|
234
|
+
module Sequel::Plugins::Foo
|
235
|
+
module ClassMethods
|
236
|
+
Sequel::Plugins.after_set_dataset(self, :set_foo_table)
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def set_foo_table
|
241
|
+
@foo_state[:table] = table_name
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
With this code, any time the model's dataset changes, the state of the plugin will be updated to set the correct table name.
|
247
|
+
|
248
|
+
== Making Dataset Methods Callable as Class Methods
|
249
|
+
|
250
|
+
In some cases, when dataset methods are added, you want to also create a model class method that will call the dataset method, so you can write:
|
251
|
+
|
252
|
+
Model.method
|
253
|
+
|
254
|
+
instead of:
|
255
|
+
|
256
|
+
Model.dataset.method
|
257
|
+
|
258
|
+
There is an available shortcut that automatically creates the class methods:
|
259
|
+
|
260
|
+
module Sequel::Plugins::Foo
|
261
|
+
module ClassMethods
|
262
|
+
Sequel::Plugins.def_dataset_methods(self, :quux)
|
263
|
+
end
|
264
|
+
|
265
|
+
module DatasetMethods
|
266
|
+
def quux
|
267
|
+
2
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* The tree and rcte_tree plugins now support composite keys.
|
4
|
+
|
5
|
+
* An error_sql Database extension has been added. This extension
|
6
|
+
adds the DatabaseError#sql method, which should return the
|
7
|
+
database query that caused the error. This is useful for
|
8
|
+
drivers that don't include the SQL used as part of the error
|
9
|
+
message.
|
10
|
+
|
11
|
+
= Other Improvements
|
12
|
+
|
13
|
+
* Empty blobs are now literalized correctly on MySQL.
|
14
|
+
|
15
|
+
* Empty arrays are now literalized correctly on PostgreSQL <8.4.
|
16
|
+
|
17
|
+
* In the pagination extension, Dataset#page_count is now 1 even if
|
18
|
+
the dataset is empty. This fixes issues with last_page? and
|
19
|
+
page_range returning bad values for empty datasets.
|
20
|
+
|
21
|
+
* In the pagination extension, calling Dataset#each_page without a
|
22
|
+
block now returns an Enumerator.
|
23
|
+
|
24
|
+
* Dataset#qualify and Sequel.delay now work together, qualifying
|
25
|
+
the object returned by the delayed evaluation.
|
26
|
+
|
27
|
+
* Migrator.migrator_class is now a public method.
|
28
|
+
|
29
|
+
* The PostgreSQL citext type is now recognized as a string.
|
30
|
+
|
31
|
+
* Another disconnect error is now recognized in the jdbc/as400
|
32
|
+
adapter.
|
33
|
+
|
34
|
+
* Guides about using and creating Sequel extensions and model
|
35
|
+
plugins have been added.
|
36
|
+
|
37
|
+
= Backwards Compatibility
|
38
|
+
|
39
|
+
* If you were expecting Dataset#page_count on a empty paginated
|
40
|
+
dataset to return 0, you need to update your code.
|
data/doc/testing.rdoc
CHANGED
@@ -163,5 +163,8 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
|
|
163
163
|
=== Other
|
164
164
|
|
165
165
|
SEQUEL_COLUMNS_INTROSPECTION :: Whether to run the specs with the columns_introspection extension loaded by default
|
166
|
+
SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
|
167
|
+
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
168
|
+
SEQUEL_NO_CHECK_SQLS :: Don't check for specific SQL syntax when running the specs
|
166
169
|
SEQUEL_NO_PENDING :: Don't mark any specs as pending, try running all specs
|
167
170
|
SKIPPED_TEST_WARN :: Warn when skipping any tests because libraries aren't available
|