sequel 3.11.0 → 3.12.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 +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
|