sequel 5.39.0 → 5.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +336 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/release_notes/5.65.0.txt +21 -0
  41. data/doc/release_notes/5.66.0.txt +24 -0
  42. data/doc/schema_modification.rdoc +1 -1
  43. data/doc/security.rdoc +9 -9
  44. data/doc/sql.rdoc +28 -16
  45. data/doc/testing.rdoc +22 -11
  46. data/doc/transactions.rdoc +6 -6
  47. data/doc/virtual_rows.rdoc +2 -2
  48. data/lib/sequel/adapters/ado/access.rb +1 -1
  49. data/lib/sequel/adapters/ado.rb +17 -17
  50. data/lib/sequel/adapters/amalgalite.rb +3 -5
  51. data/lib/sequel/adapters/ibmdb.rb +2 -2
  52. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  53. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  54. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  56. data/lib/sequel/adapters/jdbc.rb +16 -18
  57. data/lib/sequel/adapters/mysql.rb +80 -67
  58. data/lib/sequel/adapters/mysql2.rb +54 -49
  59. data/lib/sequel/adapters/odbc.rb +6 -2
  60. data/lib/sequel/adapters/oracle.rb +4 -3
  61. data/lib/sequel/adapters/postgres.rb +83 -40
  62. data/lib/sequel/adapters/shared/access.rb +11 -1
  63. data/lib/sequel/adapters/shared/db2.rb +30 -0
  64. data/lib/sequel/adapters/shared/mssql.rb +83 -7
  65. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  66. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  67. data/lib/sequel/adapters/shared/postgres.rb +430 -174
  68. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  69. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  70. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  71. data/lib/sequel/adapters/sqlite.rb +60 -18
  72. data/lib/sequel/adapters/tinytds.rb +1 -1
  73. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  74. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  75. data/lib/sequel/ast_transformer.rb +6 -0
  76. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  77. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  78. data/lib/sequel/connection_pool/single.rb +6 -8
  79. data/lib/sequel/connection_pool/threaded.rb +8 -8
  80. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  81. data/lib/sequel/connection_pool.rb +47 -30
  82. data/lib/sequel/core.rb +28 -18
  83. data/lib/sequel/database/connecting.rb +26 -2
  84. data/lib/sequel/database/misc.rb +69 -14
  85. data/lib/sequel/database/query.rb +73 -2
  86. data/lib/sequel/database/schema_generator.rb +45 -52
  87. data/lib/sequel/database/schema_methods.rb +17 -1
  88. data/lib/sequel/dataset/actions.rb +108 -14
  89. data/lib/sequel/dataset/features.rb +20 -0
  90. data/lib/sequel/dataset/misc.rb +12 -2
  91. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  92. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  93. data/lib/sequel/dataset/query.rb +118 -16
  94. data/lib/sequel/dataset/sql.rb +182 -47
  95. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  96. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  97. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  98. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  99. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  100. data/lib/sequel/extensions/blank.rb +8 -0
  101. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  102. data/lib/sequel/extensions/core_refinements.rb +36 -11
  103. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  104. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  105. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  106. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  107. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  108. data/lib/sequel/extensions/inflector.rb +9 -1
  109. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  110. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  111. data/lib/sequel/extensions/migration.rb +7 -2
  112. data/lib/sequel/extensions/named_timezones.rb +26 -6
  113. data/lib/sequel/extensions/pagination.rb +1 -1
  114. data/lib/sequel/extensions/pg_array.rb +23 -3
  115. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  116. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  117. data/lib/sequel/extensions/pg_enum.rb +1 -1
  118. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  119. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  120. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  121. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  122. data/lib/sequel/extensions/pg_inet.rb +10 -11
  123. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  124. data/lib/sequel/extensions/pg_interval.rb +45 -19
  125. data/lib/sequel/extensions/pg_json.rb +13 -15
  126. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  127. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  128. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  129. data/lib/sequel/extensions/pg_range.rb +10 -23
  130. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  131. data/lib/sequel/extensions/pg_row.rb +19 -13
  132. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  133. data/lib/sequel/extensions/query.rb +2 -0
  134. data/lib/sequel/extensions/s.rb +2 -1
  135. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  136. data/lib/sequel/extensions/server_block.rb +8 -12
  137. data/lib/sequel/extensions/sql_comments.rb +110 -3
  138. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  139. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  140. data/lib/sequel/extensions/string_agg.rb +1 -1
  141. data/lib/sequel/extensions/string_date_time.rb +19 -23
  142. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  143. data/lib/sequel/model/associations.rb +345 -101
  144. data/lib/sequel/model/base.rb +51 -27
  145. data/lib/sequel/model/errors.rb +10 -1
  146. data/lib/sequel/model/inflections.rb +1 -1
  147. data/lib/sequel/model/plugins.rb +5 -0
  148. data/lib/sequel/plugins/association_proxies.rb +2 -0
  149. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  150. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  151. data/lib/sequel/plugins/auto_validations.rb +87 -15
  152. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  153. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  154. data/lib/sequel/plugins/column_encryption.rb +728 -0
  155. data/lib/sequel/plugins/composition.rb +10 -4
  156. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  157. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  158. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  159. data/lib/sequel/plugins/dirty.rb +1 -1
  160. data/lib/sequel/plugins/enum.rb +124 -0
  161. data/lib/sequel/plugins/finder.rb +4 -2
  162. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  163. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  164. data/lib/sequel/plugins/json_serializer.rb +39 -24
  165. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  166. data/lib/sequel/plugins/list.rb +3 -1
  167. data/lib/sequel/plugins/many_through_many.rb +109 -10
  168. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  169. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  170. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  171. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  172. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  173. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  174. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  175. data/lib/sequel/plugins/serialization.rb +9 -3
  176. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  177. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  178. data/lib/sequel/plugins/sql_comments.rb +189 -0
  179. data/lib/sequel/plugins/static_cache.rb +1 -1
  180. data/lib/sequel/plugins/subclasses.rb +28 -11
  181. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  182. data/lib/sequel/plugins/timestamps.rb +1 -1
  183. data/lib/sequel/plugins/unused_associations.rb +521 -0
  184. data/lib/sequel/plugins/update_or_create.rb +1 -1
  185. data/lib/sequel/plugins/validate_associated.rb +22 -12
  186. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  187. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  188. data/lib/sequel/sql.rb +1 -1
  189. data/lib/sequel/timezones.rb +12 -14
  190. data/lib/sequel/version.rb +1 -1
  191. metadata +103 -43
@@ -0,0 +1,67 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The require_valid_schema plugin makes Sequel raise or warn if attempting
6
+ # to set the dataset of a model class to a simple table, where the database
7
+ # supports schema parsing, but schema parsing does not work for the model's
8
+ # table.
9
+ #
10
+ # The plugin's default behavior requires that all models that select from a
11
+ # single identifier have a valid table schema, if the database supports
12
+ # schema parsing. If the schema cannot be determined for such
13
+ # a model, an error is raised:
14
+ #
15
+ # Sequel::Model.plugin :require_valid_schema
16
+ #
17
+ # If you load the plugin with an argument of :warn, Sequel will warn instead
18
+ # of raising for such tables:
19
+ #
20
+ # Sequel::Model.plugin :require_valid_schema, :warn
21
+ #
22
+ # This can catch bugs where you expect models to have valid schema, but
23
+ # they do not. This setting only affects future attempts to set datasets
24
+ # in the current class and subclasses created in the future.
25
+ #
26
+ # If you load the plugin with an argument of false, it will not require valid schema.
27
+ # This can be used in subclasses where you do not want to require valid schema,
28
+ # but the plugin must be loaded before a dataset with invalid schema is set:
29
+ #
30
+ # Sequel::Model.plugin :require_valid_schema
31
+ # InvalidSchemaAllowed = Class.new(Sequel::Model)
32
+ # InvalidSchemaAllowed.plugin :require_valid_schema, false
33
+ # class MyModel < InvalidSchemaAllowed
34
+ # end
35
+ module RequireValidSchema
36
+ # Modify the current model's dataset selection, if the model
37
+ # has a dataset.
38
+ def self.configure(model, setting=true)
39
+ model.instance_variable_set(:@require_valid_schema, setting)
40
+ end
41
+
42
+ module ClassMethods
43
+ Plugins.inherited_instance_variables(self, :@require_valid_schema=>nil)
44
+
45
+ private
46
+
47
+ # If the schema cannot be determined, the model uses a simple table,
48
+ # require_valid_schema is set, and the database supports schema parsing, raise or
49
+ # warn based on the require_valid_schema setting.
50
+ def get_db_schema_array(reload)
51
+ schema_array = super
52
+
53
+ if !schema_array && simple_table && @require_valid_schema
54
+ message = "Not able to parse schema for model: #{inspect}, table: #{simple_table}"
55
+ if @require_valid_schema == :warn
56
+ warn message
57
+ else
58
+ raise Error, message
59
+ end
60
+ end
61
+
62
+ schema_array
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -127,7 +127,7 @@ module Sequel
127
127
  def serialize_attributes(format, *columns)
128
128
  if format.is_a?(Symbol)
129
129
  unless format = Sequel.synchronize{REGISTERED_FORMATS[format]}
130
- raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.map(&:inspect).join})")
130
+ raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.inspect})")
131
131
  end
132
132
  end
133
133
  serializer, deserializer = format
@@ -154,7 +154,10 @@ module Sequel
154
154
  deserialized_values[column] = deserialize_value(column, super())
155
155
  end
156
156
  end
157
- define_method("#{column}=") do |v|
157
+ alias_method(column, column)
158
+
159
+ setter = :"#{column}="
160
+ define_method(setter) do |v|
158
161
  cc = changed_columns
159
162
  if !cc.include?(column) && (new? || get_column_value(column) != v)
160
163
  cc << column
@@ -164,6 +167,7 @@ module Sequel
164
167
 
165
168
  deserialized_values[column] = v
166
169
  end
170
+ alias_method(setter, setter)
167
171
  end
168
172
  end
169
173
  end
@@ -177,8 +181,10 @@ module Sequel
177
181
 
178
182
  # Freeze the deserialized values
179
183
  def freeze
180
- deserialized_values.freeze
184
+ deserialized_values
181
185
  super
186
+ deserialized_values.freeze
187
+ self
182
188
  end
183
189
 
184
190
  # Serialize deserialized values before saving
@@ -50,8 +50,9 @@ module Sequel
50
50
  # Freeze the original deserialized values when freezing the instance.
51
51
  def freeze
52
52
  @original_deserialized_values ||= {}
53
- @original_deserialized_values.freeze
54
53
  super
54
+ @original_deserialized_values.freeze
55
+ self
55
56
  end
56
57
 
57
58
  private
@@ -253,6 +253,14 @@ module Sequel
253
253
 
254
254
  private
255
255
 
256
+ # Limit tactical eager loading objects to objects that support the same association.
257
+ def _filter_tactical_eager_load_objects(opts)
258
+ objects = defined?(super) ? super : retrieved_with.dup
259
+ name = opts[:name]
260
+ objects.select!{|x| x.model.association_reflections.include?(name)}
261
+ objects
262
+ end
263
+
256
264
  # Don't allow use of prepared statements.
257
265
  def use_prepared_statements_for?(type)
258
266
  false
@@ -0,0 +1,189 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The sql_comments plugin will automatically use SQL comments on
6
+ # queries for the model it is loaded into. These comments will
7
+ # show the related model, what type of method was called, and
8
+ # the method name (or association name for queries to load
9
+ # associations):
10
+ #
11
+ # album = Album[1]
12
+ # # SELECT * FROM albums WHERE (id = 1) LIMIT 1
13
+ # # -- model:Album,method_type:class,method:[]
14
+ #
15
+ # album.update(name: 'A')
16
+ # # UPDATE albums SET name = 'baz' WHERE (id = 1)
17
+ # # -- model:Album,method_type:instance,method:update
18
+ #
19
+ # album.artist
20
+ # # SELECT * FROM artists WHERE (artists.id = 1)
21
+ # # -- model:Album,method_type:association_load,association:artist
22
+ #
23
+ # Album.eager(:artists).all
24
+ # # SELECT * FROM albums
25
+ # # SELECT * FROM artists WHERE (artists.id IN (1))
26
+ # # -- model:Album,method_type:association_eager_load,association:artist
27
+ #
28
+ # Album.where(id: 1).delete
29
+ # # DELETE FROM albums WHERE (id = 1)
30
+ # # -- model:Album,method_type:dataset,method:delete
31
+ #
32
+ # This plugin automatically supports the class, instance, and dataset
33
+ # methods are are supported by default in Sequel::Model. To support
34
+ # custom class, instance, and dataset methods, such as those added by
35
+ # other plugins, you can use the appropriate <tt>sql_comments_*_methods</tt>
36
+ # class method:
37
+ #
38
+ # Album.sql_comments_class_methods :first_by_name # example from finder plugin, with :mod option
39
+ # Album.sql_comments_instance_methods :lazy_attribute_lookup # lazy_attributes plugin
40
+ # Album.sql_comments_dataset_methods :to_csv # csv_serializer plugin
41
+ #
42
+ # In order for the sql_comments plugin to work, the sql_comments
43
+ # Database extension must be loaded into the model's database.
44
+ #
45
+ # Note that in order to make sure SQL comments are included, some
46
+ # optimizations are disabled if this plugin is loaded.
47
+ #
48
+ # Usage:
49
+ #
50
+ # # Make all model subclasses support automatic SQL comments
51
+ # # (called before loading subclasses)
52
+ # Sequel::Model.plugin :sql_comments
53
+ #
54
+ # # Make the Album class support automatic SQL comments
55
+ # Album.plugin :sql_comments
56
+ module SqlComments
57
+ # Define a method +meth+ on the given module +mod+ that will use automatic
58
+ # SQL comments with the given model, method_type, and method.
59
+ def self.def_sql_commend_method(mod, model, method_type, meth)
60
+ mod.send(:define_method, meth) do |*a, &block|
61
+ model.db.with_comments(:model=>model, :method_type=>method_type, :method=>meth) do
62
+ super(*a, &block)
63
+ end
64
+ end
65
+ # :nocov:
66
+ mod.send(:ruby2_keywords, meth) if mod.respond_to?(:ruby2_keywords, true)
67
+ # :nocov:
68
+ end
69
+
70
+ def self.configure(model)
71
+ model.send(:reset_fast_pk_lookup_sql)
72
+ end
73
+
74
+ module ClassMethods
75
+ # Use automatic SQL comments for the given class methods.
76
+ def sql_comments_class_methods(*meths)
77
+ _sql_comments_methods(singleton_class, :class, meths)
78
+ end
79
+
80
+ # Use automatic SQL comments for the given instance methods.
81
+ def sql_comments_instance_methods(*meths)
82
+ _sql_comments_methods(self, :instance, meths)
83
+ end
84
+
85
+ # Use automatic SQL comments for the given dataset methods.
86
+ def sql_comments_dataset_methods(*meths)
87
+ unless @_sql_comments_dataset_module
88
+ dataset_module(@_sql_comments_dataset_module = Module.new)
89
+ end
90
+ _sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths)
91
+ end
92
+
93
+ [:[], :create, :find, :find_or_create, :with_pk, :with_pk!].each do |meth|
94
+ define_method(meth) do |*a, &block|
95
+ db.with_comments(:model=>self, :method_type=>:class, :method=>meth) do
96
+ super(*a, &block)
97
+ end
98
+ end
99
+ # :nocov:
100
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
101
+ # :nocov:
102
+ end
103
+
104
+ private
105
+
106
+ # Don't optimize the fast PK lookups, as it uses static SQL that
107
+ # won't support the SQL comments.
108
+ def reset_fast_pk_lookup_sql
109
+ @fast_pk_lookup_sql = @fast_instance_delete_sql = nil
110
+ end
111
+
112
+ # Define automatic SQL comment methods in +mod+ for each method in +meths+,
113
+ # with the given +method_type+.
114
+ def _sql_comments_methods(mod, method_type, meths)
115
+ meths.each do |meth|
116
+ SqlComments.def_sql_commend_method(mod, self, method_type, meth)
117
+ end
118
+ end
119
+ end
120
+
121
+ module InstanceMethods
122
+ [:delete, :destroy, :lock!, :refresh, :save, :save_changes, :update, :update_fields].each do |meth|
123
+ define_method(meth) do |*a, &block|
124
+ t = Sequel.current
125
+ return super(*a, &block) if (hash = Sequel.synchronize{db.comment_hashes[t]}) && hash[:model]
126
+
127
+ db.with_comments(:model=>model, :method_type=>:instance, :method=>meth) do
128
+ super(*a, &block)
129
+ end
130
+ end
131
+ # :nocov:
132
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
133
+ # :nocov:
134
+ end
135
+
136
+ private
137
+
138
+ # Do not use a placeholder loader for associations.
139
+ def _associated_object_loader(opts, dynamic_opts)
140
+ nil
141
+ end
142
+
143
+ # Use SQL comments on normal association load queries, showing they are association loads.
144
+ def _load_associated_objects(opts, dynamic_opts=OPTS)
145
+ db.with_comments(:model=>model, :method_type=>:association_load, :association=>opts[:name]) do
146
+ super
147
+ end
148
+ end
149
+ end
150
+
151
+ module DatasetMethods
152
+ Dataset::ACTION_METHODS.each do |meth|
153
+ define_method(meth) do |*a, &block|
154
+ t = Sequel.current
155
+ return super(*a, &block) if (hash = Sequel.synchronize{db.comment_hashes[t]}) && hash[:model]
156
+
157
+ db.with_comments(:model=>model, :method_type=>:dataset, :method=>meth) do
158
+ super(*a, &block)
159
+ end
160
+ end
161
+ # :nocov:
162
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
163
+ # :nocov:
164
+ end
165
+
166
+ private
167
+
168
+ # Add the association name as part of the eager load data, so
169
+ # perform_eager_load has access to it.
170
+ def prepare_eager_load(a, reflections, eager_assoc)
171
+ res = super
172
+
173
+ reflections.each do |r|
174
+ res[r[:eager_loader]][:association] = r[:name]
175
+ end
176
+
177
+ res
178
+ end
179
+
180
+ # Use SQL comments on eager load queries, showing they are eager loads.
181
+ def perform_eager_load(loader, eo)
182
+ db.with_comments(:model=>model, :method_type=>:association_eager_load, :method=>nil, :association=>eo[:association]) do
183
+ super
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -87,7 +87,7 @@ module Sequel
87
87
  # array containing the number of instances specified (single integer
88
88
  # argument).
89
89
  def first(*args)
90
- if block_given? || args.length > 1 || (args.length == 1 && !args[0].is_a?(Integer))
90
+ if defined?(yield) || args.length > 1 || (args.length == 1 && !args[0].is_a?(Integer))
91
91
  super
92
92
  else
93
93
  @all.first(*args)
@@ -5,7 +5,7 @@ module Sequel
5
5
  # The subclasses plugin keeps track of all subclasses of the
6
6
  # current model class. Direct subclasses are available via the
7
7
  # subclasses method, and all descendent classes are available via the
8
- # descendents method:
8
+ # descendants method:
9
9
  #
10
10
  # c = Class.new(Sequel::Model)
11
11
  # c.plugin :subclasses
@@ -16,7 +16,7 @@ module Sequel
16
16
  # sc1.subclasses # [ssc1]
17
17
  # sc2.subclasses # []
18
18
  # ssc1.subclasses # []
19
- # c.descendents # [sc1, ssc1, sc2]
19
+ # c.descendants # [sc1, ssc1, sc2]
20
20
  #
21
21
  # You can also finalize the associations and then freeze the classes
22
22
  # in all descendent classes. Doing so is a recommended practice after
@@ -35,9 +35,14 @@ module Sequel
35
35
  # class B < Sequel::Model; end
36
36
  # a # => [A, B]
37
37
  module Subclasses
38
+ NEED_SUBCLASSES = !Object.respond_to?(:subclasses) || Object.method(:subclasses).source_location
39
+ private_constant :NEED_SUBCLASSES
40
+
38
41
  # Initialize the subclasses instance variable for the model.
39
42
  def self.apply(model, &block)
40
- model.instance_variable_set(:@subclasses, [])
43
+ # :nocov:
44
+ model.instance_variable_set(:@subclasses, []) if NEED_SUBCLASSES
45
+ # :nocov:
41
46
  model.instance_variable_set(:@on_subclass, block)
42
47
  end
43
48
 
@@ -46,21 +51,31 @@ module Sequel
46
51
  # class created.
47
52
  attr_reader :on_subclass
48
53
 
49
- # All subclasses for the current model. Does not
50
- # include the model itself.
51
- attr_reader :subclasses
54
+ # :nocov:
55
+ if NEED_SUBCLASSES
56
+ # All subclasses for the current model. Does not
57
+ # include the model itself.
58
+ attr_reader :subclasses
59
+ end
60
+ # :nocov:
52
61
 
53
62
  # All descendent classes of this model.
54
- def descendents
55
- Sequel.synchronize{subclasses.dup}.map{|x| [x] + x.send(:descendents)}.flatten
63
+ def descendants
64
+ Sequel.synchronize{subclasses.dup}.map{|x| [x] + x.send(:descendants)}.flatten
56
65
  end
57
66
 
67
+ # SEQUEL6: Remove
68
+ alias descendents descendants
69
+
58
70
  # Freeze all descendent classes. This also finalizes the associations for those
59
71
  # classes before freezing.
60
- def freeze_descendents
61
- descendents.each(&:finalize_associations).each(&:freeze)
72
+ def freeze_descendants
73
+ descendants.each(&:finalize_associations).each(&:freeze)
62
74
  end
63
75
 
76
+ # SEQUEL6: Remove
77
+ alias freeze_descendents freeze_descendants
78
+
64
79
  Plugins.inherited_instance_variables(self, :@subclasses=>lambda{|v| []}, :@on_subclass=>nil)
65
80
 
66
81
  private
@@ -70,7 +85,9 @@ module Sequel
70
85
  # in the subclass.
71
86
  def inherited(subclass)
72
87
  super
73
- Sequel.synchronize{subclasses << subclass}
88
+ # :nocov:
89
+ Sequel.synchronize{subclasses << subclass} if NEED_SUBCLASSES
90
+ # :nocov:
74
91
  on_subclass.call(subclass) if on_subclass
75
92
  end
76
93
  end
@@ -109,6 +109,13 @@ module Sequel
109
109
  # to eagerly set the associated objects, and having separate threads
110
110
  # modify the same model instance is not thread-safe.
111
111
  #
112
+ # Because this plugin will automatically use eager loading for
113
+ # performance, it can break code that defines associations that
114
+ # do not support eager loading, without marking that they do not
115
+ # support eager loading via the <tt>allow_eager: false</tt> option.
116
+ # Make sure to set <tt>allow_eager: false</tt> on any association
117
+ # used with this plugin if the association doesn't support eager loading.
118
+ #
112
119
  # Usage:
113
120
  #
114
121
  # # Make all model subclass instances use tactical eager loading (called before loading subclasses)
@@ -143,19 +150,25 @@ module Sequel
143
150
  def load_associated_objects(opts, dynamic_opts=OPTS, &block)
144
151
  dynamic_opts = load_association_objects_options(dynamic_opts, &block)
145
152
  name = opts[:name]
146
- if (!associations.include?(name) || dynamic_opts[:eager_reload]) && opts[:allow_eager] != false && retrieved_by && !frozen? && !dynamic_opts[:callback] && !dynamic_opts[:reload]
147
- begin
148
- retrieved_by.send(:eager_load, retrieved_with.reject(&:frozen?), name=>dynamic_opts[:eager] || OPTS)
149
- rescue Sequel::UndefinedAssociation
150
- # This can happen if class table inheritance is used and the association
151
- # is only defined in a subclass. This particular instance can use the
152
- # association, but it can't be eagerly loaded as the parent class doesn't
153
- # have access to the association, and that's the class doing the eager loading.
154
- nil
155
- end
153
+ eager_reload = dynamic_opts[:eager_reload]
154
+ if (!associations.include?(name) || eager_reload) && opts[:allow_eager] != false && retrieved_by && !frozen? && !dynamic_opts[:callback] && !dynamic_opts[:reload]
155
+ retrieved_by.send(:eager_load, _filter_tactical_eager_load_objects(:eager_reload=>eager_reload, :name=>name), {name=>dynamic_opts[:eager] || OPTS}, model)
156
156
  end
157
157
  super
158
158
  end
159
+
160
+ # Filter the objects used when tactical eager loading.
161
+ # By default, this removes frozen objects and objects that alreayd have the association loaded
162
+ def _filter_tactical_eager_load_objects(opts)
163
+ objects = defined?(super) ? super : retrieved_with.dup
164
+ if opts[:eager_reload]
165
+ objects.reject!(&:frozen?)
166
+ else
167
+ name = opts[:name]
168
+ objects.reject!{|x| x.frozen? || x.associations.include?(name)}
169
+ end
170
+ objects
171
+ end
159
172
  end
160
173
 
161
174
  module DatasetMethods
@@ -19,7 +19,7 @@ module Sequel
19
19
  #
20
20
  # # Timestamp Artist instances, forcing an overwrite of the create
21
21
  # # timestamp, and setting the update timestamp when creating
22
- # Album.plugin :timestamps, force: true, update_on_create: true
22
+ # Artist.plugin :timestamps, force: true, update_on_create: true
23
23
  module Timestamps
24
24
  # Configure the plugin by setting the available options. Note that
25
25
  # if this method is run more than once, previous settings are ignored,