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.
Files changed (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. 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 subclasses
6
- # that use the static cache plugin (or just the current class). Using this plugin
7
- # can avoid the need to query the database every time loading the plugin into a
8
- # model, which can save time when you have a lot of models using the static_cache
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
- static_cache_cache = {}
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
- if rows = Sequel.synchronize{@static_cache_cache[name]}
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 = dataset.all.freeze
85
+ rows = ds.all.freeze
49
86
  raw_rows = rows.map(&:values)
50
- Sequel.synchronize{@static_cache_cache[name] = raw_rows}
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 created, which returns the filter conditions the subset
7
- # uses. This can be useful if you want to use the conditions for a separate
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
- # subset :published, published: true
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 can end up with coverage that shows the
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) || [:adder, :remover, :clearer].all?{|k| unused.include?(k)}
387
- [:setter, :adder, :remover, :clearer].each do |k|
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
@@ -208,7 +208,7 @@ module Sequel
208
208
  next if an && Array(v).all?(&:nil?)
209
209
  next if ab && Array(v).all?(&blank_meth)
210
210
  next if am && Array(a).all?{|x| !o.values.has_key?(x)}
211
- block.call(o,a,v)
211
+ yield(o, a, v)
212
212
  end
213
213
  else
214
214
  block
@@ -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?.