sequel 5.83.0 → 5.106.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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/lib/sequel/adapters/ado/access.rb +2 -2
- data/lib/sequel/adapters/ado.rb +2 -2
- data/lib/sequel/adapters/amalgalite.rb +16 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -1
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +4 -2
- data/lib/sequel/adapters/jdbc/h2.rb +4 -4
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
- data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
- data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
- data/lib/sequel/adapters/jdbc.rb +29 -15
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/postgres.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +8 -3
- data/lib/sequel/adapters/shared/mssql.rb +9 -8
- data/lib/sequel/adapters/shared/mysql.rb +16 -11
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +438 -47
- data/lib/sequel/adapters/shared/sqlite.rb +9 -7
- data/lib/sequel/adapters/sqlite.rb +20 -2
- data/lib/sequel/adapters/tinytds.rb +2 -2
- data/lib/sequel/adapters/trilogy.rb +2 -3
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
- data/lib/sequel/connection_pool/timed_queue.rb +33 -10
- data/lib/sequel/connection_pool.rb +8 -2
- data/lib/sequel/core.rb +31 -2
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +29 -11
- data/lib/sequel/database/query.rb +12 -12
- data/lib/sequel/database/schema_generator.rb +117 -16
- data/lib/sequel/database/schema_methods.rb +85 -30
- data/lib/sequel/dataset/actions.rb +83 -11
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
- data/lib/sequel/dataset/prepared_statements.rb +76 -28
- data/lib/sequel/dataset/query.rb +26 -11
- data/lib/sequel/dataset/sql.rb +40 -17
- data/lib/sequel/extensions/async_thread_pool.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +1 -3
- data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
- data/lib/sequel/extensions/connection_expiration.rb +44 -5
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/date_arithmetic.rb +6 -6
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
- data/lib/sequel/extensions/eval_inspect.rb +3 -1
- data/lib/sequel/extensions/inflector.rb +2 -2
- data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -12
- data/lib/sequel/extensions/migration.rb +38 -21
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +41 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
- data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
- data/lib/sequel/extensions/pg_enum.rb +4 -4
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
- data/lib/sequel/extensions/pg_json_ops.rb +674 -18
- data/lib/sequel/extensions/pg_multirange.rb +1 -1
- data/lib/sequel/extensions/pg_range.rb +14 -2
- data/lib/sequel/extensions/pg_row.rb +5 -3
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +1 -3
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +17 -5
- data/lib/sequel/extensions/set_literalizer.rb +20 -39
- data/lib/sequel/extensions/split_array_nil.rb +12 -2
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/string_agg.rb +2 -2
- data/lib/sequel/extensions/to_dot.rb +5 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +215 -15
- data/lib/sequel/model/base.rb +198 -35
- data/lib/sequel/model/plugins.rb +12 -1
- data/lib/sequel/model.rb +1 -0
- data/lib/sequel/plugins/accessed_columns.rb +4 -0
- data/lib/sequel/plugins/active_model.rb +5 -6
- data/lib/sequel/plugins/association_pks.rb +2 -0
- data/lib/sequel/plugins/auto_validations.rb +2 -0
- data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
- data/lib/sequel/plugins/columns_updated.rb +4 -0
- data/lib/sequel/plugins/composition.rb +3 -1
- data/lib/sequel/plugins/constraint_validations.rb +15 -10
- data/lib/sequel/plugins/dataset_associations.rb +20 -1
- data/lib/sequel/plugins/deprecated_associations.rb +151 -0
- data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
- data/lib/sequel/plugins/dirty.rb +9 -2
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/insert_returning_select.rb +10 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +8 -1
- data/lib/sequel/plugins/instance_hooks.rb +4 -0
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +6 -10
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +21 -0
- data/lib/sequel/plugins/modification_detection.rb +2 -0
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_array_associations.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +6 -6
- data/lib/sequel/plugins/serialization.rb +23 -6
- data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
- data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
- data/lib/sequel/plugins/split_values.rb +12 -0
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache.rb +13 -0
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +278 -0
- data/lib/sequel/plugins/table_select.rb +7 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
- data/lib/sequel/plugins/unused_associations.rb +11 -3
- data/lib/sequel/plugins/update_primary_key.rb +4 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_contexts.rb +4 -0
- data/lib/sequel/sql.rb +27 -12
- data/lib/sequel/version.rb +1 -1
- metadata +17 -240
- data/CHANGELOG +0 -1393
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -104
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -439
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.83.0.txt +0 -56
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -204
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Sequel
|
|
4
4
|
module Plugins
|
|
5
|
-
# The static_cache_cache plugin allows for caching the row content for
|
|
6
|
-
# that use the
|
|
7
|
-
# can avoid the need to query the database every time loading
|
|
8
|
-
#
|
|
9
|
-
# plugin.
|
|
5
|
+
# The static_cache_cache plugin allows for caching the row content for the current
|
|
6
|
+
# class and subclasses that use the static_cache or subset_static_cache plugins.
|
|
7
|
+
# Using this plugin can avoid the need to query the database every time loading
|
|
8
|
+
# the static_cache plugin into a model (static_cache plugin) or using the
|
|
9
|
+
# cache_subset method (subset_static_cache plugin).
|
|
10
10
|
#
|
|
11
11
|
# Usage:
|
|
12
12
|
#
|
|
@@ -26,11 +26,7 @@ module Sequel
|
|
|
26
26
|
module ClassMethods
|
|
27
27
|
# Dump the in-memory cached rows to the cache file.
|
|
28
28
|
def dump_static_cache_cache
|
|
29
|
-
|
|
30
|
-
@static_cache_cache.sort.each do |k, v|
|
|
31
|
-
static_cache_cache[k] = v
|
|
32
|
-
end
|
|
33
|
-
File.open(@static_cache_cache_file, 'wb'){|f| f.write(Marshal.dump(static_cache_cache))}
|
|
29
|
+
File.open(@static_cache_cache_file, 'wb'){|f| f.write(Marshal.dump(sort_static_cache_hash(@static_cache_cache)))}
|
|
34
30
|
nil
|
|
35
31
|
end
|
|
36
32
|
|
|
@@ -38,16 +34,57 @@ module Sequel
|
|
|
38
34
|
|
|
39
35
|
private
|
|
40
36
|
|
|
37
|
+
# Sort the given static cache hash in a deterministic way, so that
|
|
38
|
+
# the same static cache values will result in the same marshal file.
|
|
39
|
+
def sort_static_cache_hash(cache)
|
|
40
|
+
cache = cache.sort do |a, b|
|
|
41
|
+
a, = a
|
|
42
|
+
b, = b
|
|
43
|
+
if a.is_a?(Array)
|
|
44
|
+
if b.is_a?(Array)
|
|
45
|
+
a_name, a_meth = a
|
|
46
|
+
b_name, b_meth = b
|
|
47
|
+
x = a_name <=> b_name
|
|
48
|
+
if x.zero?
|
|
49
|
+
x = a_meth <=> b_meth
|
|
50
|
+
end
|
|
51
|
+
x
|
|
52
|
+
else
|
|
53
|
+
1
|
|
54
|
+
end
|
|
55
|
+
elsif b.is_a?(Array)
|
|
56
|
+
-1
|
|
57
|
+
else
|
|
58
|
+
a <=> b
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
Hash[cache]
|
|
62
|
+
end
|
|
63
|
+
|
|
41
64
|
# Load the rows for the model from the cache if available.
|
|
42
65
|
# If not available, load the rows from the database, and
|
|
43
66
|
# then update the cache with the raw rows.
|
|
44
67
|
def load_static_cache_rows
|
|
45
|
-
|
|
68
|
+
_load_static_cache_rows(dataset, name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Load the rows for the subset from the cache if available.
|
|
72
|
+
# If not available, load the rows from the database, and
|
|
73
|
+
# then update the cache with the raw rows.
|
|
74
|
+
def load_subset_static_cache_rows(ds, meth)
|
|
75
|
+
_load_static_cache_rows(ds, [name, meth].freeze)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Check the cache first for the key, and return rows without a database
|
|
79
|
+
# query if present. Otherwise, get all records in the provided dataset,
|
|
80
|
+
# and update the cache with them.
|
|
81
|
+
def _load_static_cache_rows(ds, key)
|
|
82
|
+
if rows = Sequel.synchronize{@static_cache_cache[key]}
|
|
46
83
|
rows.map{|row| call(row)}.freeze
|
|
47
84
|
else
|
|
48
|
-
rows =
|
|
85
|
+
rows = ds.all.freeze
|
|
49
86
|
raw_rows = rows.map(&:values)
|
|
50
|
-
Sequel.synchronize{@static_cache_cache[
|
|
87
|
+
Sequel.synchronize{@static_cache_cache[key] = raw_rows}
|
|
51
88
|
rows
|
|
52
89
|
end
|
|
53
90
|
end
|
|
@@ -3,18 +3,31 @@
|
|
|
3
3
|
module Sequel
|
|
4
4
|
module Plugins
|
|
5
5
|
# The subset_conditions plugin creates an additional *_conditions method
|
|
6
|
-
# for every subset
|
|
7
|
-
#
|
|
8
|
-
# filter or combine them with OR.
|
|
6
|
+
# for every `subset`, `where`, and `exclude` method call in a dataset_module
|
|
7
|
+
# block. This method returns the filter conditions, which can be useful if
|
|
8
|
+
# you want to use the conditions for a separate filter or combine them with OR.
|
|
9
|
+
# It also supports where_all and where_any dataset_module methods for
|
|
10
|
+
# combining multiple dataset method filters with AND or OR.
|
|
9
11
|
#
|
|
10
12
|
# Usage:
|
|
11
13
|
#
|
|
12
14
|
# # Add subset_conditions in the Album class
|
|
13
15
|
# Album.plugin :subset_conditions
|
|
14
16
|
#
|
|
15
|
-
# # This will now create a published_conditions method
|
|
16
17
|
# Album.dataset_module do
|
|
17
|
-
#
|
|
18
|
+
# # This will now create a published_conditions method
|
|
19
|
+
# where :published, published: true
|
|
20
|
+
#
|
|
21
|
+
# # This will now create a not_bad_conditions method
|
|
22
|
+
# exclude :not_bad, :bad
|
|
23
|
+
#
|
|
24
|
+
# # This will create good_and_available and
|
|
25
|
+
# # good_and_available_conditions methods
|
|
26
|
+
# where_all :good_and_available, :published, :not_bad
|
|
27
|
+
#
|
|
28
|
+
# # This will create good_or_available and
|
|
29
|
+
# # good_or_available_conditions methods
|
|
30
|
+
# where_any :good_or_available, :published, :not_bad
|
|
18
31
|
# end
|
|
19
32
|
#
|
|
20
33
|
# Album.where(Album.published_conditions).sql
|
|
@@ -25,10 +38,17 @@ module Sequel
|
|
|
25
38
|
#
|
|
26
39
|
# Album.where(Album.published_conditions | {ready: true}).sql
|
|
27
40
|
# # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
|
|
41
|
+
#
|
|
42
|
+
# Album.good_and_available.sql
|
|
43
|
+
# SELECT * FROM albums WHERE ((published IS TRUE) AND NOT bad)
|
|
44
|
+
#
|
|
45
|
+
# Album.good_or_available.sql
|
|
46
|
+
# SELECT * FROM albums WHERE ((published IS TRUE) OR NOT bad)
|
|
28
47
|
module SubsetConditions
|
|
29
48
|
def self.apply(model, &block)
|
|
30
49
|
model.instance_exec do
|
|
31
50
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
|
51
|
+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(SubsetConditions)"}
|
|
32
52
|
include DatasetModuleMethods
|
|
33
53
|
end
|
|
34
54
|
end
|
|
@@ -42,6 +62,66 @@ module Sequel
|
|
|
42
62
|
cond = cond.first if cond.size == 1
|
|
43
63
|
define_method(:"#{name}_conditions"){filter_expr(cond, &block)}
|
|
44
64
|
end
|
|
65
|
+
|
|
66
|
+
# Also create a method that returns the conditions the filter uses.
|
|
67
|
+
def exclude(name, *args, &block)
|
|
68
|
+
super
|
|
69
|
+
cond = args
|
|
70
|
+
cond = cond.first if cond.size == 1
|
|
71
|
+
define_method(:"#{name}_conditions"){Sequel.~(filter_expr(cond, &block))}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Create a method that combines filters from already registered
|
|
75
|
+
# dataset methods, and filters for rows where all of the conditions
|
|
76
|
+
# are satisfied.
|
|
77
|
+
#
|
|
78
|
+
# Employee.dataset_module do
|
|
79
|
+
# where :active, active: true
|
|
80
|
+
# where :started, Sequel::CURRENT_DATE <= :start_date
|
|
81
|
+
# where_all(:active_and_started, :active, :started)
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# Employee.active_and_started.sql
|
|
85
|
+
# # SELECT * FROM employees WHERE ((active IS TRUE) AND (CURRENT_DATE <= start_date))
|
|
86
|
+
def where_all(name, *args)
|
|
87
|
+
_where_any_all(:&, name, args)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Create a method that combines filters from already registered
|
|
91
|
+
# dataset methods, and filters for rows where any of the conditions
|
|
92
|
+
# are satisfied.
|
|
93
|
+
#
|
|
94
|
+
# Employee.dataset_module do
|
|
95
|
+
# where :active, active: true
|
|
96
|
+
# where :started, Sequel::CURRENT_DATE <= :start_date
|
|
97
|
+
# where_any(:active_or_started, :active, :started)
|
|
98
|
+
# end
|
|
99
|
+
#
|
|
100
|
+
# Employee.active_or_started.sql
|
|
101
|
+
# # SELECT * FROM employees WHERE ((active IS TRUE) OR (CURRENT_DATE <= start_date))
|
|
102
|
+
def where_any(name, *args)
|
|
103
|
+
_where_any_all(:|, name, args)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
if RUBY_VERSION >= '2'
|
|
109
|
+
# Backbone of #where_any and #where_all
|
|
110
|
+
def _where_any_all(meth, name, args)
|
|
111
|
+
ds = model.dataset
|
|
112
|
+
# #bind used here because the dataset module may not yet be included in the model's dataset
|
|
113
|
+
where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
|
|
114
|
+
end
|
|
115
|
+
else
|
|
116
|
+
# Cannot bind module method to arbitrary objects in Ruby 1.9.
|
|
117
|
+
# :nocov:
|
|
118
|
+
def _where_any_all(meth, name, args)
|
|
119
|
+
ds = model.dataset.clone
|
|
120
|
+
ds.extend(self)
|
|
121
|
+
where(name, Sequel.send(meth, *args.map{|a| ds.send(:"#{a}_conditions")}))
|
|
122
|
+
end
|
|
123
|
+
# :nocov:
|
|
124
|
+
end
|
|
45
125
|
end
|
|
46
126
|
end
|
|
47
127
|
end
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Plugins
|
|
5
|
+
# The subset_static_cache plugin is designed for model subsets that are not modified at all
|
|
6
|
+
# in production use cases, or at least where modifications to them would usually
|
|
7
|
+
# coincide with an application restart. When caching a model subset, it
|
|
8
|
+
# retrieves all rows in the database and statically caches a ruby array and hash
|
|
9
|
+
# keyed on primary key containing all of the model instances. All of these cached
|
|
10
|
+
# instances are frozen so they won't be modified unexpectedly.
|
|
11
|
+
#
|
|
12
|
+
# With the following code:
|
|
13
|
+
#
|
|
14
|
+
# class StatusType < Sequel::Model
|
|
15
|
+
# dataset_module do
|
|
16
|
+
# where :available, hidden: false
|
|
17
|
+
# end
|
|
18
|
+
# cache_subset :available
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# The following methods will use the cache and not issue a database query:
|
|
22
|
+
#
|
|
23
|
+
# * StatusType.available.with_pk
|
|
24
|
+
# * StatusType.available.all
|
|
25
|
+
# * StatusType.available.each
|
|
26
|
+
# * StatusType.available.first (without block, only supporting no arguments or single integer argument)
|
|
27
|
+
# * StatusType.available.count (without an argument or block)
|
|
28
|
+
# * StatusType.available.map
|
|
29
|
+
# * StatusType.available.as_hash
|
|
30
|
+
# * StatusType.available.to_hash
|
|
31
|
+
# * StatusType.available.to_hash_groups
|
|
32
|
+
#
|
|
33
|
+
# The cache is not used if you chain methods before or after calling the cached
|
|
34
|
+
# method, as doing so would not be safe:
|
|
35
|
+
#
|
|
36
|
+
# StatusType.where{number > 1}.available.all
|
|
37
|
+
# StatusType.available.where{number > 1}.all
|
|
38
|
+
#
|
|
39
|
+
# The cache is also not used if you change the class's dataset after caching
|
|
40
|
+
# the subset, or in subclasses of the model.
|
|
41
|
+
#
|
|
42
|
+
# You should not modify any row that is statically cached when using this plugin,
|
|
43
|
+
# as otherwise you will get different results for cached and uncached method
|
|
44
|
+
# calls.
|
|
45
|
+
module SubsetStaticCache
|
|
46
|
+
def self.configure(model)
|
|
47
|
+
model.class_exec do
|
|
48
|
+
@subset_static_caches ||= ({}.compare_by_identity)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module ClassMethods
|
|
53
|
+
# Cache the given subset statically, so that calling the subset method on
|
|
54
|
+
# the model will return a dataset that will return cached results instead
|
|
55
|
+
# of issuing database queries (assuming the cache has the necessary
|
|
56
|
+
# information).
|
|
57
|
+
#
|
|
58
|
+
# The model must already respond to the given method before cache_subset
|
|
59
|
+
# is called.
|
|
60
|
+
def cache_subset(meth)
|
|
61
|
+
ds = send(meth).with_extend(CachedDatasetMethods)
|
|
62
|
+
cache = ds.instance_variable_get(:@cache)
|
|
63
|
+
|
|
64
|
+
rows, hash = subset_static_cache_rows(ds, meth)
|
|
65
|
+
cache[:subset_static_cache_all] = rows
|
|
66
|
+
cache[:subset_static_cache_map] = hash
|
|
67
|
+
|
|
68
|
+
caches = @subset_static_caches
|
|
69
|
+
caches[meth] = ds
|
|
70
|
+
model = self
|
|
71
|
+
subset_static_cache_module.send(:define_method, meth) do
|
|
72
|
+
if (model == self) && (cached_dataset = caches[meth])
|
|
73
|
+
cached_dataset
|
|
74
|
+
else
|
|
75
|
+
super()
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
Plugins.after_set_dataset(self, :clear_subset_static_caches)
|
|
82
|
+
Plugins.inherited_instance_variables(self, :@subset_static_caches=>proc{{}.compare_by_identity})
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Clear the subset_static_caches. This is used if the model dataset
|
|
87
|
+
# changes, to prevent cached values from being used.
|
|
88
|
+
def clear_subset_static_caches
|
|
89
|
+
@subset_static_caches.clear
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# A module for the subset static cache methods, so that you can define
|
|
93
|
+
# a singleton method in the class with the same name, and call super
|
|
94
|
+
# to get default behavior.
|
|
95
|
+
def subset_static_cache_module
|
|
96
|
+
return @subset_static_cache_module if @subset_static_cache_module
|
|
97
|
+
|
|
98
|
+
# Ensure dataset_methods module is defined and class is extended with
|
|
99
|
+
# it before calling creating this module.
|
|
100
|
+
dataset_methods_module
|
|
101
|
+
|
|
102
|
+
mod_name = "#{name}::@subset_static_cache_module"
|
|
103
|
+
Sequel.synchronize{@subset_static_cache_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
|
104
|
+
extend(@subset_static_cache_module)
|
|
105
|
+
@subset_static_cache_module
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Return the frozen array and hash used for caching the subset
|
|
109
|
+
# of the given dataset.
|
|
110
|
+
def subset_static_cache_rows(ds, meth)
|
|
111
|
+
all = load_subset_static_cache_rows(ds, meth)
|
|
112
|
+
h = {}
|
|
113
|
+
all.each do |o|
|
|
114
|
+
o.errors.freeze
|
|
115
|
+
h[o.pk.freeze] = o.freeze
|
|
116
|
+
end
|
|
117
|
+
[all, h.freeze]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Return a frozen array for all rows in the dataset.
|
|
121
|
+
def load_subset_static_cache_rows(ds, meth)
|
|
122
|
+
ret = super if defined?(super)
|
|
123
|
+
ret || ds.all.freeze
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
module CachedDatasetMethods
|
|
128
|
+
# An array of all of the dataset's instances, without issuing a database
|
|
129
|
+
# query. If a block is given, yields each instance to the block.
|
|
130
|
+
def all(&block)
|
|
131
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
132
|
+
|
|
133
|
+
array = all.dup
|
|
134
|
+
array.each(&block) if block
|
|
135
|
+
array
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Get the number of records in the cache, without issuing a database query,
|
|
139
|
+
# if no arguments or block are provided.
|
|
140
|
+
def count(*a, &block)
|
|
141
|
+
if a.empty? && !block && (all = @cache[:subset_static_cache_all])
|
|
142
|
+
all.size
|
|
143
|
+
else
|
|
144
|
+
super
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# If a block is given, multiple arguments are given, or a single
|
|
149
|
+
# non-Integer argument is given, performs the default behavior of
|
|
150
|
+
# issuing a database query. Otherwise, uses the cached values
|
|
151
|
+
# to return either the first cached instance (no arguments) or an
|
|
152
|
+
# array containing the number of instances specified (single integer
|
|
153
|
+
# argument).
|
|
154
|
+
def first(*args)
|
|
155
|
+
if !defined?(yield) && args.length <= 1 && (args.length == 0 || args[0].is_a?(Integer)) && (all = @cache[:subset_static_cache_all])
|
|
156
|
+
all.first(*args)
|
|
157
|
+
else
|
|
158
|
+
super
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Return the frozen object with the given pk, or nil if no such object exists
|
|
163
|
+
# in the cache, without issuing a database query.
|
|
164
|
+
def with_pk(pk)
|
|
165
|
+
if cache = @cache[:subset_static_cache_map]
|
|
166
|
+
cache[pk]
|
|
167
|
+
else
|
|
168
|
+
super
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Yield each of the dataset's frozen instances to the block, without issuing a database
|
|
173
|
+
# query.
|
|
174
|
+
def each(&block)
|
|
175
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
176
|
+
all.each(&block)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Use the cache instead of a query to get the results.
|
|
180
|
+
def map(column=nil, &block)
|
|
181
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
182
|
+
if column
|
|
183
|
+
raise(Error, "Cannot provide both column and block to map") if block
|
|
184
|
+
if column.is_a?(Array)
|
|
185
|
+
all.map{|r| r.values.values_at(*column)}
|
|
186
|
+
else
|
|
187
|
+
all.map{|r| r[column]}
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
all.map(&block)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Use the cache instead of a query to get the results if possible
|
|
195
|
+
def as_hash(key_column = nil, value_column = nil, opts = OPTS)
|
|
196
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
197
|
+
|
|
198
|
+
if key_column.nil? && value_column.nil?
|
|
199
|
+
if opts[:hash]
|
|
200
|
+
key_column = model.primary_key
|
|
201
|
+
else
|
|
202
|
+
return Hash[@cache[:subset_static_cache_map]]
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
h = opts[:hash] || {}
|
|
207
|
+
if value_column
|
|
208
|
+
if value_column.is_a?(Array)
|
|
209
|
+
if key_column.is_a?(Array)
|
|
210
|
+
all.each{|r| h[r.values.values_at(*key_column)] = r.values.values_at(*value_column)}
|
|
211
|
+
else
|
|
212
|
+
all.each{|r| h[r[key_column]] = r.values.values_at(*value_column)}
|
|
213
|
+
end
|
|
214
|
+
else
|
|
215
|
+
if key_column.is_a?(Array)
|
|
216
|
+
all.each{|r| h[r.values.values_at(*key_column)] = r[value_column]}
|
|
217
|
+
else
|
|
218
|
+
all.each{|r| h[r[key_column]] = r[value_column]}
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
elsif key_column.is_a?(Array)
|
|
222
|
+
all.each{|r| h[r.values.values_at(*key_column)] = r}
|
|
223
|
+
else
|
|
224
|
+
all.each{|r| h[r[key_column]] = r}
|
|
225
|
+
end
|
|
226
|
+
h
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Use the cache instead of a query to get the results.
|
|
230
|
+
def as_set(column)
|
|
231
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
232
|
+
|
|
233
|
+
set = Set.new
|
|
234
|
+
|
|
235
|
+
if column.is_a?(Array)
|
|
236
|
+
all.each{|r| set.add(r.values.values_at(*column))}
|
|
237
|
+
else
|
|
238
|
+
all.each{|r| set.add(r[column])}
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
set
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Alias of as_hash for backwards compatibility.
|
|
245
|
+
def to_hash(*a)
|
|
246
|
+
as_hash(*a)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Use the cache instead of a query to get the results
|
|
250
|
+
def to_hash_groups(key_column, value_column = nil, opts = OPTS)
|
|
251
|
+
return super unless all = @cache[:subset_static_cache_all]
|
|
252
|
+
|
|
253
|
+
h = opts[:hash] || {}
|
|
254
|
+
if value_column
|
|
255
|
+
if value_column.is_a?(Array)
|
|
256
|
+
if key_column.is_a?(Array)
|
|
257
|
+
all.each{|r| (h[r.values.values_at(*key_column)] ||= []) << r.values.values_at(*value_column)}
|
|
258
|
+
else
|
|
259
|
+
all.each{|r| (h[r[key_column]] ||= []) << r.values.values_at(*value_column)}
|
|
260
|
+
end
|
|
261
|
+
else
|
|
262
|
+
if key_column.is_a?(Array)
|
|
263
|
+
all.each{|r| (h[r.values.values_at(*key_column)] ||= []) << r[value_column]}
|
|
264
|
+
else
|
|
265
|
+
all.each{|r| (h[r[key_column]] ||= []) << r[value_column]}
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
elsif key_column.is_a?(Array)
|
|
269
|
+
all.each{|r| (h[r.values.values_at(*key_column)] ||= []) << r}
|
|
270
|
+
else
|
|
271
|
+
all.each{|r| (h[r[key_column]] ||= []) << r}
|
|
272
|
+
end
|
|
273
|
+
h
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
@@ -9,6 +9,13 @@ module Sequel
|
|
|
9
9
|
# in the result sets (and possibly overwrite columns in the
|
|
10
10
|
# current model with the same name).
|
|
11
11
|
#
|
|
12
|
+
# Note that by default on databases that supporting RETURNING,
|
|
13
|
+
# using this plugin will cause instance creations
|
|
14
|
+
# to use two queries (insert and refresh) instead of a single
|
|
15
|
+
# query using RETURNING. You can use the insert_returning_select
|
|
16
|
+
# plugin to automatically use RETURNING for instance creations
|
|
17
|
+
# for models using this plugin.
|
|
18
|
+
#
|
|
12
19
|
# Usage:
|
|
13
20
|
#
|
|
14
21
|
# # Make all model subclasses select table.*
|
|
@@ -124,6 +124,10 @@ module Sequel
|
|
|
124
124
|
# # Make the Album class use tactical eager loading
|
|
125
125
|
# Album.plugin :tactical_eager_loading
|
|
126
126
|
module TacticalEagerLoading
|
|
127
|
+
module ClassMethods
|
|
128
|
+
Plugins.model_instance_variables(self, :@retrieved_by, :@retrieved_with)
|
|
129
|
+
end
|
|
130
|
+
|
|
127
131
|
module InstanceMethods
|
|
128
132
|
# The dataset that retrieved this object, set if the object was
|
|
129
133
|
# reteived via Dataset#all.
|
|
@@ -198,7 +198,8 @@ module Sequel
|
|
|
198
198
|
#
|
|
199
199
|
# Since this plugin is based on coverage information, if you do
|
|
200
200
|
# not have tests that cover all usage of associations in your
|
|
201
|
-
# application, you
|
|
201
|
+
# application, or you are mocking the related association methods
|
|
202
|
+
# and not calling them, you can end up with coverage that shows the
|
|
202
203
|
# association is not used, when it is used in code that is not
|
|
203
204
|
# covered. The output of plugin can still be useful in such cases,
|
|
204
205
|
# as long as you are manually checking it. However, you should
|
|
@@ -208,6 +209,8 @@ module Sequel
|
|
|
208
209
|
# option for any association that you know is used. If an
|
|
209
210
|
# association uses the :is_used association option, this plugin
|
|
210
211
|
# will not modify it if the :modify_associations option is used.
|
|
212
|
+
# It will also not report the association as unused if the :is_used
|
|
213
|
+
# association option is present.
|
|
211
214
|
#
|
|
212
215
|
# This plugin does not handle anonymous classes. Any unused
|
|
213
216
|
# associations defined in anonymous classes will not be
|
|
@@ -347,6 +350,8 @@ module Sequel
|
|
|
347
350
|
coverage_data = options[:coverage_data] || Sequel.parse_json(File.binread(@unused_associations_coverage_file))
|
|
348
351
|
|
|
349
352
|
unused_associations_data = {}
|
|
353
|
+
to_many_modification_methods = [:adder, :remover, :clearer]
|
|
354
|
+
modification_methods = [:setter, :adder, :remover, :clearer]
|
|
350
355
|
|
|
351
356
|
([self] + descendants).each do |sc|
|
|
352
357
|
next unless cov_data = coverage_data[sc.name]
|
|
@@ -360,6 +365,9 @@ module Sequel
|
|
|
360
365
|
# looks in the class's overridable_methods_module
|
|
361
366
|
next if ref[:methods_module]
|
|
362
367
|
|
|
368
|
+
# Do not report associations if they are explicitly marked as used.
|
|
369
|
+
next if ref[:is_used]
|
|
370
|
+
|
|
363
371
|
info = {}
|
|
364
372
|
if reflection_data.include?(assoc.to_s)
|
|
365
373
|
info[:used] = [:reflection]
|
|
@@ -383,8 +391,8 @@ module Sequel
|
|
|
383
391
|
if !info[:used]
|
|
384
392
|
(unused_associations_data[sc.name] ||= {})[assoc.to_s] = 'unused'
|
|
385
393
|
elsif unused = info[:unused]
|
|
386
|
-
if unused.include?(:setter) ||
|
|
387
|
-
|
|
394
|
+
if unused.include?(:setter) || to_many_modification_methods.all?{|k| unused.include?(k)}
|
|
395
|
+
modification_methods.each do |k|
|
|
388
396
|
unused.delete(k)
|
|
389
397
|
end
|
|
390
398
|
unused << :read_only
|
|
@@ -22,6 +22,10 @@ module Sequel
|
|
|
22
22
|
# # Make the Album class support primary key updates
|
|
23
23
|
# Album.plugin :update_primary_key
|
|
24
24
|
module UpdatePrimaryKey
|
|
25
|
+
module ClassMethods
|
|
26
|
+
Plugins.model_instance_variables(self, :@pk_hash)
|
|
27
|
+
end
|
|
28
|
+
|
|
25
29
|
module InstanceMethods
|
|
26
30
|
# Clear the cached primary key.
|
|
27
31
|
def after_update
|
|
@@ -29,6 +29,10 @@ module Sequel
|
|
|
29
29
|
# of that model, you can no longer specify a validation context when
|
|
30
30
|
# validating the instance.
|
|
31
31
|
module ValidationContexts
|
|
32
|
+
module ClassMethods
|
|
33
|
+
Plugins.model_instance_variables(self, :@validation_context)
|
|
34
|
+
end
|
|
35
|
+
|
|
32
36
|
module InstanceMethods
|
|
33
37
|
# The validation context to use for the current validation.
|
|
34
38
|
# Set via the :validation_context option passed to save/valid?.
|