sequel 3.11.0 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +70 -0
- data/Rakefile +1 -1
- data/doc/active_record.rdoc +896 -0
- data/doc/advanced_associations.rdoc +46 -31
- data/doc/association_basics.rdoc +14 -9
- data/doc/dataset_basics.rdoc +3 -3
- data/doc/migration.rdoc +1011 -0
- data/doc/model_hooks.rdoc +198 -0
- data/doc/querying.rdoc +811 -86
- data/doc/release_notes/3.12.0.txt +304 -0
- data/doc/sharding.rdoc +17 -0
- data/doc/sql.rdoc +537 -0
- data/doc/validations.rdoc +501 -0
- data/lib/sequel/adapters/jdbc.rb +19 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
- data/lib/sequel/adapters/mysql.rb +5 -4
- data/lib/sequel/adapters/odbc.rb +3 -2
- data/lib/sequel/adapters/shared/mssql.rb +7 -6
- data/lib/sequel/adapters/shared/mysql.rb +2 -7
- data/lib/sequel/adapters/shared/postgres.rb +2 -8
- data/lib/sequel/adapters/shared/sqlite.rb +2 -5
- data/lib/sequel/adapters/sqlite.rb +4 -4
- data/lib/sequel/core.rb +0 -1
- data/lib/sequel/database.rb +2 -1060
- data/lib/sequel/database/connecting.rb +227 -0
- data/lib/sequel/database/dataset.rb +58 -0
- data/lib/sequel/database/dataset_defaults.rb +127 -0
- data/lib/sequel/database/logging.rb +62 -0
- data/lib/sequel/database/misc.rb +246 -0
- data/lib/sequel/database/query.rb +390 -0
- data/lib/sequel/database/schema_generator.rb +7 -3
- data/lib/sequel/database/schema_methods.rb +351 -7
- data/lib/sequel/dataset/actions.rb +9 -2
- data/lib/sequel/dataset/misc.rb +6 -2
- data/lib/sequel/dataset/mutation.rb +3 -11
- data/lib/sequel/dataset/query.rb +49 -6
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/migration.rb +395 -113
- data/lib/sequel/extensions/schema_dumper.rb +21 -13
- data/lib/sequel/model.rb +27 -25
- data/lib/sequel/model/associations.rb +72 -34
- data/lib/sequel/model/base.rb +74 -18
- data/lib/sequel/model/errors.rb +8 -1
- data/lib/sequel/plugins/active_model.rb +8 -0
- data/lib/sequel/plugins/association_pks.rb +87 -0
- data/lib/sequel/plugins/association_proxies.rb +8 -0
- data/lib/sequel/plugins/boolean_readers.rb +12 -6
- data/lib/sequel/plugins/caching.rb +14 -7
- data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
- data/lib/sequel/plugins/composition.rb +2 -1
- data/lib/sequel/plugins/force_encoding.rb +10 -7
- data/lib/sequel/plugins/hook_class_methods.rb +12 -11
- data/lib/sequel/plugins/identity_map.rb +9 -0
- data/lib/sequel/plugins/instance_hooks.rb +23 -13
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +18 -4
- data/lib/sequel/plugins/nested_attributes.rb +1 -0
- data/lib/sequel/plugins/optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +9 -8
- data/lib/sequel/plugins/schema.rb +8 -0
- data/lib/sequel/plugins/serialization.rb +1 -3
- data/lib/sequel/plugins/sharding.rb +135 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
- data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
- data/lib/sequel/plugins/string_stripper.rb +26 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
- data/lib/sequel/plugins/timestamps.rb +15 -2
- data/lib/sequel/plugins/touch.rb +13 -0
- data/lib/sequel/plugins/update_primary_key.rb +48 -0
- data/lib/sequel/plugins/validation_class_methods.rb +8 -0
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +17 -20
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +5 -5
- data/spec/core/core_sql_spec.rb +17 -1
- data/spec/core/database_spec.rb +17 -5
- data/spec/core/dataset_spec.rb +31 -8
- data/spec/core/schema_generator_spec.rb +8 -1
- data/spec/core/schema_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +85 -0
- data/spec/extensions/hook_class_methods_spec.rb +9 -9
- data/spec/extensions/migration_spec.rb +339 -219
- data/spec/extensions/schema_dumper_spec.rb +28 -17
- data/spec/extensions/sharding_spec.rb +272 -0
- data/spec/extensions/single_table_inheritance_spec.rb +92 -4
- data/spec/extensions/skip_create_refresh_spec.rb +17 -0
- data/spec/extensions/string_stripper_spec.rb +23 -0
- data/spec/extensions/update_primary_key_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +5 -5
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
- data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
- data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
- data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
- data/spec/integration/eager_loader_test.rb +20 -20
- data/spec/integration/migrator_test.rb +187 -0
- data/spec/integration/plugin_test.rb +150 -0
- data/spec/integration/schema_test.rb +13 -2
- data/spec/model/associations_spec.rb +41 -14
- data/spec/model/base_spec.rb +69 -0
- data/spec/model/eager_loading_spec.rb +7 -3
- data/spec/model/record_spec.rb +79 -4
- data/spec/model/validations_spec.rb +21 -9
- metadata +66 -5
- data/doc/schema.rdoc +0 -36
- data/lib/sequel/database/schema_sql.rb +0 -320
data/lib/sequel/model/errors.rb
CHANGED
@@ -19,6 +19,11 @@ module Sequel
|
|
19
19
|
def count
|
20
20
|
values.inject(0){|m, v| m + v.length}
|
21
21
|
end
|
22
|
+
|
23
|
+
# Return true if there are no error messages, false otherwise.
|
24
|
+
def empty?
|
25
|
+
count == 0
|
26
|
+
end
|
22
27
|
|
23
28
|
# Returns an array of fully-formatted error messages.
|
24
29
|
def full_messages
|
@@ -32,7 +37,9 @@ module Sequel
|
|
32
37
|
# Returns the array of errors for the given attribute, or nil
|
33
38
|
# if there are no errors for the attribute.
|
34
39
|
def on(att)
|
35
|
-
|
40
|
+
if v = fetch(att, nil) and !v.empty?
|
41
|
+
v
|
42
|
+
end
|
36
43
|
end
|
37
44
|
end
|
38
45
|
end
|
@@ -7,6 +7,14 @@ module Sequel
|
|
7
7
|
# allow the full support of Sequel::Model objects in Rails 3.
|
8
8
|
# This plugin requires active_model in order to use
|
9
9
|
# ActiveModel::Naming.
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# # Make all subclasses active_model compliant (called before loading subclasses)
|
14
|
+
# Sequel::Model.plugin :active_model
|
15
|
+
#
|
16
|
+
# # Make the Album class active_model compliant
|
17
|
+
# Album.plugin :active_model
|
10
18
|
module ActiveModel
|
11
19
|
ClassMethods = ::ActiveModel::Naming
|
12
20
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
# The association_pks plugin adds the association_pks and association_pks=
|
4
|
+
# instance methods to the model class for each association added. These
|
5
|
+
# methods allow for easily returning the primary keys of the associated
|
6
|
+
# objects, and easily modifying the associated objects to set the primary
|
7
|
+
# keys to just the ones given:
|
8
|
+
#
|
9
|
+
# Artist.one_to_many :albums
|
10
|
+
# artist = Artist[1]
|
11
|
+
# artist.album_pks # [1, 2, 3]
|
12
|
+
# artist.album_pks = [2, 4]
|
13
|
+
# artist.album_pks # [2, 4]
|
14
|
+
#
|
15
|
+
# Note that it uses the singular form of the association name. Also note
|
16
|
+
# that the setter both associates to new primary keys not in the assocation
|
17
|
+
# and disassociated from primary keys not provided to the method.
|
18
|
+
#
|
19
|
+
# This plugin makes modifications directly to the underlying tables,
|
20
|
+
# it does not create or return any model objects, and therefore does
|
21
|
+
# not call any callbacks. If you have any association callbacks,
|
22
|
+
# you probably should not use the setter methods.
|
23
|
+
#
|
24
|
+
# This plugin only works with singular primary keys, it does not work
|
25
|
+
# with composite primary keys.
|
26
|
+
#
|
27
|
+
# Usage:
|
28
|
+
#
|
29
|
+
# # Make all model subclass *_to_many associations have association_pks
|
30
|
+
# # methods (called before loading subclasses)
|
31
|
+
# Sequel::Model.plugin :association_pks
|
32
|
+
#
|
33
|
+
# # Make the Album *_to_many associations have association_pks
|
34
|
+
# # methods (called before the association methods)
|
35
|
+
# Album.plugin :association_pks
|
36
|
+
module AssociationPks
|
37
|
+
module ClassMethods
|
38
|
+
private
|
39
|
+
|
40
|
+
# Define a association_pks method using the block for the association reflection
|
41
|
+
def def_association_pks_getter(opts, &block)
|
42
|
+
association_module_def(:"#{singularize(opts[:name])}_pks", &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Define a association_pks= method using the block for the association reflection,
|
46
|
+
# if the association is not read only.
|
47
|
+
def def_association_pks_setter(opts, &block)
|
48
|
+
association_module_def(:"#{singularize(opts[:name])}_pks=", &block) unless opts[:read_only]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a getter that checks the join table for matching records and
|
52
|
+
# a setter that deletes from or inserts into the join table.
|
53
|
+
def def_many_to_many(opts)
|
54
|
+
super
|
55
|
+
def_association_pks_getter(opts) do
|
56
|
+
_join_table_dataset(opts).filter(opts[:left_key]=>send(opts[:left_primary_key])).select_map(opts[:right_key])
|
57
|
+
end
|
58
|
+
def_association_pks_setter(opts) do |pks|
|
59
|
+
checked_transaction do
|
60
|
+
ds = _join_table_dataset(opts).filter(opts[:left_key]=>send(opts[:left_primary_key]))
|
61
|
+
ds.exclude(opts[:right_key]=>pks).delete
|
62
|
+
pks -= ds.select_map(opts[:right_key])
|
63
|
+
pks.each{|pk| ds.insert(opts[:left_key]=>send(opts[:left_primary_key]), opts[:right_key]=>pk)}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Add a getter that checks the association dataset and a setter
|
69
|
+
# that updates the associated table.
|
70
|
+
def def_one_to_many(opts)
|
71
|
+
super
|
72
|
+
return if opts[:type] == :one_to_one
|
73
|
+
def_association_pks_getter(opts) do
|
74
|
+
send(opts.dataset_method).select_map(opts[:primary_key])
|
75
|
+
end
|
76
|
+
def_association_pks_setter(opts) do |pks|
|
77
|
+
checked_transaction do
|
78
|
+
ds = send(opts.dataset_method)
|
79
|
+
ds.unfiltered.filter(opts[:primary_key]=>pks).update(opts[:key]=>pk)
|
80
|
+
ds.exclude(opts[:primary_key]=>pks).update(opts[:key]=>nil)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -5,6 +5,14 @@ module Sequel
|
|
5
5
|
# method returns a dataset. This plugin makes the association method return a proxy
|
6
6
|
# that will load the association and call a method on the association array if sent
|
7
7
|
# an array method, and otherwise send the method to the association's dataset.
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# # Use association proxies in all model subclasses (called before loading subclasses)
|
12
|
+
# Sequel::Model.plugin :association_proxies
|
13
|
+
#
|
14
|
+
# # Use association proxies in a specific model subclass
|
15
|
+
# Album.plugin :association_proxies
|
8
16
|
module AssociationProxies
|
9
17
|
# A proxy for the association. Calling an array method will load the
|
10
18
|
# associated objects and call the method on the associated object array.
|
@@ -4,14 +4,20 @@ module Sequel
|
|
4
4
|
# for boolean columns, which provide a nicer API. By default, the accessors
|
5
5
|
# are created for all columns of type :boolean. However, you can provide a
|
6
6
|
# block to the plugin to change the criteria used to determine if a
|
7
|
-
# column is boolean
|
7
|
+
# column is boolean. The block is yielded with the column symbol for each
|
8
|
+
# column in the models dataset.
|
8
9
|
#
|
9
|
-
#
|
10
|
+
# Usage:
|
10
11
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
12
|
+
# # Add boolean attribute? methods for all columns of type :boolean
|
13
|
+
# # in all model subclasses (called before loading subclasses)
|
14
|
+
# Sequel::Model.plugin :boolean_readers
|
15
|
+
#
|
16
|
+
# # Add boolean readers for all tinyint columns in the Album class
|
17
|
+
# Album.plugin(:boolean_readers){|c| db_schema[c][:db_type] =~ /\Atinyint/}
|
18
|
+
#
|
19
|
+
# # Add a boolean reader for a specific columns in the Artist class
|
20
|
+
# Artist.plugin(:boolean_readers){|c| [:column1, :column2, :column3].include?(c)}
|
15
21
|
module BooleanReaders
|
16
22
|
# Default proc for determining if given column is a boolean, which
|
17
23
|
# just checks that the :type is boolean.
|
@@ -2,12 +2,7 @@ module Sequel
|
|
2
2
|
module Plugins
|
3
3
|
# Sequel's built-in caching plugin supports caching to any object that
|
4
4
|
# implements the Ruby-Memcache API (or memcached API with the :ignore_exceptions
|
5
|
-
# option)
|
6
|
-
#
|
7
|
-
# Model.plugin :caching, store # Cache all models
|
8
|
-
# MyModel.plugin :caching, store # Just cache MyModel
|
9
|
-
#
|
10
|
-
# The cache store should implement the Ruby-Memcache API:
|
5
|
+
# option):
|
11
6
|
#
|
12
7
|
# cache_store.set(key, obj, time) # Associate the obj with the given key
|
13
8
|
# # in the cache for the time (specified
|
@@ -24,6 +19,18 @@ module Sequel
|
|
24
19
|
#
|
25
20
|
# Note that only Model.[] method calls with a primary key argument are cached
|
26
21
|
# using this plugin.
|
22
|
+
#
|
23
|
+
# Usage:
|
24
|
+
#
|
25
|
+
# # Make all subclasses use the same cache (called before loading subclasses)
|
26
|
+
# # using the Ruby-Memcache API, with the cache stored in the CACHE constant
|
27
|
+
# Sequel::Model.plugin :caching, CACHE
|
28
|
+
#
|
29
|
+
# # Make the Album class use the cache with a 30 minute time-to-live
|
30
|
+
# Album.plugin :caching, CACHE, :ttl=>1800
|
31
|
+
#
|
32
|
+
# # Make the Artist class use a cache with the memcached protocol
|
33
|
+
# Artist.plugin :caching, MEMCACHED_CACHE, :ignore_exceptions=>true
|
27
34
|
module Caching
|
28
35
|
# Set the cache_store and cache_ttl attributes for the given model.
|
29
36
|
# If the :ttl option is not given, 3600 seconds is the default.
|
@@ -105,8 +112,8 @@ module Sequel
|
|
105
112
|
module InstanceMethods
|
106
113
|
# Remove the object from the cache when updating
|
107
114
|
def before_update
|
108
|
-
return false if super == false
|
109
115
|
cache_delete
|
116
|
+
super
|
110
117
|
end
|
111
118
|
|
112
119
|
# Return a key unique to the underlying record for caching, based on the
|
@@ -5,11 +5,11 @@ module Sequel
|
|
5
5
|
# unique to that model class (or subclass hierarchy) being stored in the related
|
6
6
|
# table. For example, with this hierarchy:
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
8
|
+
# Employee
|
9
|
+
# / \
|
10
|
+
# Staff Manager
|
11
|
+
# |
|
12
|
+
# Executive
|
13
13
|
#
|
14
14
|
# the following database schema may be used (table - columns):
|
15
15
|
#
|
@@ -54,6 +54,16 @@ module Sequel
|
|
54
54
|
# a = Employee.all # [<#Staff>, <#Manager>, <#Executive>]
|
55
55
|
# a.first.values # {:id=>1, name=>'S', :kind=>'Staff'}
|
56
56
|
# a.first.manager_id # Loads the manager_id attribute from the database
|
57
|
+
#
|
58
|
+
# Usage:
|
59
|
+
#
|
60
|
+
# # Set up class table inheritance in the parent class
|
61
|
+
# # (Not in the subclasses)
|
62
|
+
# Employee.plugin :class_table_inheritance
|
63
|
+
#
|
64
|
+
# # Set the +kind+ column to hold the class name, and
|
65
|
+
# # set the subclass table to map to for each subclass
|
66
|
+
# Employee.plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
57
67
|
module ClassTableInheritance
|
58
68
|
# The class_table_inheritance plugin requires the lazy_attributes plugin
|
59
69
|
# to handle lazily-loaded attributes for subclass instances returned
|
@@ -71,10 +81,6 @@ module Sequel
|
|
71
81
|
# * :table_map - Hash with class name symbol keys and table name symbol
|
72
82
|
# values. Necessary if the implicit table name for the model class
|
73
83
|
# does not match the database table name
|
74
|
-
# Example:
|
75
|
-
# class Employee < Sequel::Model
|
76
|
-
# plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
77
|
-
# end
|
78
84
|
def self.configure(model, opts={}, &block)
|
79
85
|
model.instance_eval do
|
80
86
|
m = method(:constantize)
|
@@ -9,7 +9,8 @@ module Sequel
|
|
9
9
|
# to deal with Date objects in your ruby code. This can be handled
|
10
10
|
# with:
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# Album.plugin :composition
|
13
|
+
# Album.composition :date, :mapping=>[:year, :month, :day]
|
13
14
|
#
|
14
15
|
# The :mapping option is optional, but you can define custom
|
15
16
|
# composition and decomposition procs via the :composer and
|
@@ -10,12 +10,15 @@ module Sequel
|
|
10
10
|
# can either do so in the plugin call itself, or via the
|
11
11
|
# forced_encoding class accessor:
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
13
|
+
# Usage:
|
14
|
+
#
|
15
|
+
# # Force all strings to be UTF8 encoded in a all model subclasses
|
16
|
+
# # (called before loading subclasses)
|
17
|
+
# Sequel::Model.plugin :force_encoding, 'UTF-8'
|
18
|
+
#
|
19
|
+
# # Force the encoding for the Album model to UTF8
|
20
|
+
# Album.plugin :force_encoding
|
21
|
+
# Album.forced_encoding = 'UTF-8'
|
19
22
|
module ForceEncoding
|
20
23
|
# Set the forced_encoding based on the value given in the plugin call.
|
21
24
|
# Note that if a the plugin has been previously loaded, any previous
|
@@ -69,4 +72,4 @@ module Sequel
|
|
69
72
|
end
|
70
73
|
else
|
71
74
|
raise LoadError, 'ForceEncoding plugin only works on Ruby 1.9+'
|
72
|
-
end
|
75
|
+
end
|
@@ -23,6 +23,14 @@ module Sequel
|
|
23
23
|
# Note that returning false in any before hook block will skip further
|
24
24
|
# before hooks and abort the action. So if a before_save hook block returns
|
25
25
|
# false, future before_save hook blocks are not called, and the save is aborted.
|
26
|
+
#
|
27
|
+
# Usage:
|
28
|
+
#
|
29
|
+
# # Allow use of hook class methods in all model subclasses (called before loading subclasses)
|
30
|
+
# Sequel::Model.plugin :hook_class_methods
|
31
|
+
#
|
32
|
+
# # Allow the use of hook class methods in the Album class
|
33
|
+
# Album.plugin :hook_class_methods
|
26
34
|
module HookClassMethods
|
27
35
|
# Set up the hooks instance variable in the model.
|
28
36
|
def self.apply(model)
|
@@ -44,7 +52,7 @@ module Sequel
|
|
44
52
|
# the symbol specifies an instance method to call and adds it to the hook
|
45
53
|
# type.
|
46
54
|
#
|
47
|
-
# If any hook block returns false, the instance method will return false
|
55
|
+
# If any before hook block returns false, the instance method will return false
|
48
56
|
# immediately without running the rest of the hooks of that type.
|
49
57
|
#
|
50
58
|
# It is recommended that you always provide a symbol to this method,
|
@@ -66,7 +74,7 @@ module Sequel
|
|
66
74
|
hooks.each do |hook|
|
67
75
|
@hooks[hook] = []
|
68
76
|
instance_eval("def #{hook}(method = nil, &block); add_hook(:#{hook}, method, &block) end", __FILE__, __LINE__)
|
69
|
-
class_eval("def #{hook};
|
77
|
+
class_eval("def #{hook}; model.hook_blocks(:#{hook}){|b| return false if instance_eval(&b) == false}; end", __FILE__, __LINE__)
|
70
78
|
end
|
71
79
|
end
|
72
80
|
|
@@ -111,15 +119,8 @@ module Sequel
|
|
111
119
|
end
|
112
120
|
|
113
121
|
module InstanceMethods
|
114
|
-
Model::
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
# Runs all hook blocks of given hook type on this object.
|
119
|
-
# Stops running hook blocks and returns false if any hook block returns false.
|
120
|
-
def run_hooks(hook)
|
121
|
-
model.hook_blocks(hook){|block| return false if instance_eval(&block) == false}
|
122
|
-
end
|
122
|
+
Model::BEFORE_HOOKS.each{|h| class_eval("def #{h}; model.hook_blocks(:#{h}){|b| return false if instance_eval(&b) == false}; super; end", __FILE__, __LINE__)}
|
123
|
+
Model::AFTER_HOOKS.each{|h| class_eval("def #{h}; super; model.hook_blocks(:#{h}){|b| instance_eval(&b)}; end", __FILE__, __LINE__)}
|
123
124
|
end
|
124
125
|
end
|
125
126
|
end
|
@@ -21,6 +21,15 @@ module Sequel
|
|
21
21
|
#
|
22
22
|
# Identity maps are thread-local and only presist for the duration of the block,
|
23
23
|
# so they should be should only be considered as a possible performance enhancer.
|
24
|
+
#
|
25
|
+
# Usage:
|
26
|
+
#
|
27
|
+
# # Use an identity map that will affect all model classes (called before loading subclasses)
|
28
|
+
# Sequel::Model.plugin :identity_map
|
29
|
+
#
|
30
|
+
# # Use an identity map just for the Album class
|
31
|
+
# Album.plugin :identity_map
|
32
|
+
# # would need to do Album.with_identity_map{} to use the identity map
|
24
33
|
module IdentityMap
|
25
34
|
module ClassMethods
|
26
35
|
# Returns the current thread-local identity map. Should be a hash if
|
@@ -11,19 +11,28 @@ module Sequel
|
|
11
11
|
# false, no more instance level before hooks are called and false is returned.
|
12
12
|
#
|
13
13
|
# Instance level hooks are cleared when the object is saved successfully.
|
14
|
+
#
|
15
|
+
# Usage:
|
16
|
+
#
|
17
|
+
# # Add the instance hook methods to all model subclass instances (called before loading subclasses)
|
18
|
+
# Sequel::Model.plugin :instance_hooks
|
19
|
+
#
|
20
|
+
# # Add the instance hook methods just to Album instances
|
21
|
+
# Album.plugin :instance_hooks
|
14
22
|
module InstanceHooks
|
15
23
|
module InstanceMethods
|
16
|
-
|
24
|
+
BEFORE_HOOKS = Sequel::Model::BEFORE_HOOKS
|
25
|
+
AFTER_HOOKS = Sequel::Model::AFTER_HOOKS - [:after_initialize]
|
26
|
+
HOOKS = BEFORE_HOOKS + AFTER_HOOKS
|
17
27
|
HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block) end", __FILE__, __LINE__)}
|
18
28
|
|
19
|
-
BEFORE_HOOKS
|
20
|
-
|
21
|
-
AFTER_HOOKS.each{|h| class_eval("def #{h}; super; run_instance_hooks(:#{h}) end", __FILE__, __LINE__)}
|
29
|
+
BEFORE_HOOKS.each{|h| class_eval("def #{h}; run_before_instance_hooks(:#{h}) == false ? false : super end", __FILE__, __LINE__)}
|
30
|
+
(AFTER_HOOKS - [:after_save]).each{|h| class_eval("def #{h}; super; run_after_instance_hooks(:#{h}) end", __FILE__, __LINE__)}
|
22
31
|
|
23
32
|
# Clear the instance level hooks after saving the object.
|
24
33
|
def after_save
|
25
34
|
super
|
26
|
-
|
35
|
+
run_after_instance_hooks(:after_save)
|
27
36
|
@instance_hooks.clear if @instance_hooks
|
28
37
|
end
|
29
38
|
|
@@ -42,14 +51,15 @@ module Sequel
|
|
42
51
|
@instance_hooks[hook] ||= []
|
43
52
|
end
|
44
53
|
|
45
|
-
# Run all hook blocks of the given hook type.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
# Run all hook blocks of the given hook type.
|
55
|
+
def run_after_instance_hooks(hook)
|
56
|
+
instance_hooks(hook).each{|b| b.call}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Run all hook blocks of the given hook type. If a hook block returns false,
|
60
|
+
# immediately return false without running the remaining blocks.
|
61
|
+
def run_before_instance_hooks(hook)
|
62
|
+
instance_hooks(hook).each{|b| return false if b.call == false}
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -16,6 +16,9 @@ module Sequel
|
|
16
16
|
# a.review
|
17
17
|
# end
|
18
18
|
# end
|
19
|
+
#
|
20
|
+
# # You can specify multiple columns to lazily load:
|
21
|
+
# Album.plugin :lazy_attributes, :review, :tracklist
|
19
22
|
module LazyAttributes
|
20
23
|
# Lazy attributes requires the identity map and tactical eager loading plugins
|
21
24
|
def self.apply(model, *attrs)
|
@@ -67,7 +70,7 @@ module Sequel
|
|
67
70
|
# the attribute for the current object.
|
68
71
|
def lazy_attribute_lookup(a)
|
69
72
|
primary_key = model.primary_key
|
70
|
-
model.select(*(Array(primary_key) + [a])).filter(primary_key
|
73
|
+
model.select(*(Array(primary_key) + [a])).filter(primary_key=>::Sequel::SQL::SQLArray.new(retrieved_with.map{|o| o.pk})).all if model.identity_map && retrieved_with
|
71
74
|
values[a] = this.select(a).first[a] unless values.include?(a)
|
72
75
|
values[a]
|
73
76
|
end
|
@@ -8,6 +8,7 @@ module Sequel
|
|
8
8
|
#
|
9
9
|
# The many_through_many plugin would allow this:
|
10
10
|
#
|
11
|
+
# Artist.plugin :many_through_many
|
11
12
|
# Artist.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
12
13
|
#
|
13
14
|
# Which will give you the tags for all of the artist's albums.
|
@@ -28,6 +29,19 @@ module Sequel
|
|
28
29
|
# # All tracks on albums by this artist
|
29
30
|
# Artist.many_through_many :tracks, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id]], \
|
30
31
|
# :right_primary_key=>:album_id
|
32
|
+
#
|
33
|
+
# Often you don't want the current object to appear in the array of associated objects. This is easiest to handle via an :after_load hook:
|
34
|
+
#
|
35
|
+
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_artists, :album_id, :artist_id]],
|
36
|
+
# :after_load=>proc{|artist, associated_artists| associated_artists.delete(artist)}
|
37
|
+
#
|
38
|
+
# You can also handle it by adding a dataset block that excludes the current record (so it won't be retrieved at all), but
|
39
|
+
# that won't work when eagerly loading, which is why the :after_load proc is recommended instead.
|
40
|
+
#
|
41
|
+
# It's also common to not want duplicate records, in which case the :distinct option can be used:
|
42
|
+
#
|
43
|
+
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_artists, :album_id, :artist_id]],
|
44
|
+
# :distinct=>true
|
31
45
|
module ManyThroughMany
|
32
46
|
# The AssociationReflection subclass for many_through_many associations.
|
33
47
|
class ManyThroughManyAssociationReflection < Sequel::Model::Associations::ManyToManyAssociationReflection
|
@@ -165,15 +179,15 @@ module Sequel
|
|
165
179
|
end
|
166
180
|
|
167
181
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
168
|
-
opts[:eager_loader] ||= lambda do |
|
169
|
-
h = key_hash[left_pk]
|
170
|
-
|
182
|
+
opts[:eager_loader] ||= lambda do |eo|
|
183
|
+
h = eo[:key_hash][left_pk]
|
184
|
+
eo[:rows].each{|object| object.associations[name] = []}
|
171
185
|
ds = opts.associated_class
|
172
186
|
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
173
187
|
ft = opts[:final_reverse_edge]
|
174
188
|
conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, SQL::SQLArray.new(h.keys)]] : [[left_key, h.keys]]
|
175
189
|
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
|
176
|
-
model.eager_loading_dataset(opts, ds, Array(opts.select), associations).all do |assoc_record|
|
190
|
+
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo).all do |assoc_record|
|
177
191
|
hash_key = if uses_lcks
|
178
192
|
left_key_alias.map{|k| assoc_record.values.delete(k)}
|
179
193
|
else
|