sequel 5.80.0 → 5.92.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +9 -4
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/ibmdb.rb +1 -0
  5. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/derby.rb +3 -3
  7. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  9. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  10. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  11. data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
  12. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  13. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  16. data/lib/sequel/adapters/jdbc.rb +8 -8
  17. data/lib/sequel/adapters/mysql2.rb +8 -1
  18. data/lib/sequel/adapters/shared/access.rb +1 -0
  19. data/lib/sequel/adapters/shared/db2.rb +1 -1
  20. data/lib/sequel/adapters/shared/mssql.rb +18 -5
  21. data/lib/sequel/adapters/shared/mysql.rb +8 -4
  22. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  23. data/lib/sequel/adapters/shared/postgres.rb +106 -13
  24. data/lib/sequel/adapters/shared/sqlite.rb +4 -2
  25. data/lib/sequel/adapters/sqlite.rb +4 -0
  26. data/lib/sequel/adapters/trilogy.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
  28. data/lib/sequel/connection_pool/threaded.rb +26 -10
  29. data/lib/sequel/connection_pool.rb +2 -2
  30. data/lib/sequel/core.rb +15 -0
  31. data/lib/sequel/database/connecting.rb +20 -26
  32. data/lib/sequel/database/dataset_defaults.rb +3 -3
  33. data/lib/sequel/database/misc.rb +46 -10
  34. data/lib/sequel/database/query.rb +11 -11
  35. data/lib/sequel/database/schema_generator.rb +8 -0
  36. data/lib/sequel/database/schema_methods.rb +17 -1
  37. data/lib/sequel/dataset/actions.rb +9 -1
  38. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  39. data/lib/sequel/dataset/prepared_statements.rb +2 -1
  40. data/lib/sequel/dataset/query.rb +9 -5
  41. data/lib/sequel/dataset/sql.rb +25 -5
  42. data/lib/sequel/extensions/caller_logging.rb +2 -0
  43. data/lib/sequel/extensions/connection_validator.rb +15 -10
  44. data/lib/sequel/extensions/dataset_run.rb +41 -0
  45. data/lib/sequel/extensions/migration.rb +23 -3
  46. data/lib/sequel/extensions/null_dataset.rb +2 -2
  47. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  48. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
  49. data/lib/sequel/extensions/pg_enum.rb +3 -3
  50. data/lib/sequel/extensions/pg_json_ops.rb +642 -9
  51. data/lib/sequel/extensions/pg_row.rb +3 -1
  52. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  53. data/lib/sequel/extensions/provenance.rb +2 -0
  54. data/lib/sequel/extensions/query_blocker.rb +172 -0
  55. data/lib/sequel/extensions/schema_caching.rb +24 -9
  56. data/lib/sequel/extensions/schema_dumper.rb +16 -4
  57. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  58. data/lib/sequel/extensions/stdio_logger.rb +48 -0
  59. data/lib/sequel/extensions/string_agg.rb +17 -4
  60. data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
  61. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  62. data/lib/sequel/model/associations.rb +28 -3
  63. data/lib/sequel/model/base.rb +67 -18
  64. data/lib/sequel/plugins/association_pks.rb +1 -1
  65. data/lib/sequel/plugins/column_encryption.rb +1 -1
  66. data/lib/sequel/plugins/composition.rb +1 -1
  67. data/lib/sequel/plugins/defaults_setter.rb +16 -4
  68. data/lib/sequel/plugins/enum.rb +1 -1
  69. data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
  70. data/lib/sequel/plugins/input_transformer.rb +1 -1
  71. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  72. data/lib/sequel/plugins/instance_filters.rb +4 -1
  73. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  74. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  75. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  76. data/lib/sequel/plugins/optimistic_locking.rb +2 -0
  77. data/lib/sequel/plugins/paged_operations.rb +5 -2
  78. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  79. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  80. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  81. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  82. data/lib/sequel/plugins/serialization.rb +11 -5
  83. data/lib/sequel/plugins/sql_comments.rb +7 -2
  84. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  85. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  86. data/lib/sequel/plugins/subset_static_cache.rb +263 -0
  87. data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
  88. data/lib/sequel/plugins/validate_associated.rb +1 -1
  89. data/lib/sequel/sql.rb +16 -6
  90. data/lib/sequel/version.rb +1 -1
  91. metadata +12 -234
  92. data/CHANGELOG +0 -1355
  93. data/README.rdoc +0 -936
  94. data/doc/advanced_associations.rdoc +0 -884
  95. data/doc/association_basics.rdoc +0 -1859
  96. data/doc/bin_sequel.rdoc +0 -146
  97. data/doc/cheat_sheet.rdoc +0 -255
  98. data/doc/code_order.rdoc +0 -102
  99. data/doc/core_extensions.rdoc +0 -405
  100. data/doc/dataset_basics.rdoc +0 -96
  101. data/doc/dataset_filtering.rdoc +0 -222
  102. data/doc/extensions.rdoc +0 -77
  103. data/doc/fork_safety.rdoc +0 -84
  104. data/doc/mass_assignment.rdoc +0 -98
  105. data/doc/migration.rdoc +0 -660
  106. data/doc/model_dataset_method_design.rdoc +0 -129
  107. data/doc/model_hooks.rdoc +0 -254
  108. data/doc/model_plugins.rdoc +0 -270
  109. data/doc/mssql_stored_procedures.rdoc +0 -43
  110. data/doc/object_model.rdoc +0 -563
  111. data/doc/opening_databases.rdoc +0 -436
  112. data/doc/postgresql.rdoc +0 -611
  113. data/doc/prepared_statements.rdoc +0 -144
  114. data/doc/querying.rdoc +0 -1070
  115. data/doc/reflection.rdoc +0 -120
  116. data/doc/release_notes/5.0.0.txt +0 -159
  117. data/doc/release_notes/5.1.0.txt +0 -31
  118. data/doc/release_notes/5.10.0.txt +0 -84
  119. data/doc/release_notes/5.11.0.txt +0 -83
  120. data/doc/release_notes/5.12.0.txt +0 -141
  121. data/doc/release_notes/5.13.0.txt +0 -27
  122. data/doc/release_notes/5.14.0.txt +0 -63
  123. data/doc/release_notes/5.15.0.txt +0 -39
  124. data/doc/release_notes/5.16.0.txt +0 -110
  125. data/doc/release_notes/5.17.0.txt +0 -31
  126. data/doc/release_notes/5.18.0.txt +0 -69
  127. data/doc/release_notes/5.19.0.txt +0 -28
  128. data/doc/release_notes/5.2.0.txt +0 -33
  129. data/doc/release_notes/5.20.0.txt +0 -89
  130. data/doc/release_notes/5.21.0.txt +0 -87
  131. data/doc/release_notes/5.22.0.txt +0 -48
  132. data/doc/release_notes/5.23.0.txt +0 -56
  133. data/doc/release_notes/5.24.0.txt +0 -56
  134. data/doc/release_notes/5.25.0.txt +0 -32
  135. data/doc/release_notes/5.26.0.txt +0 -35
  136. data/doc/release_notes/5.27.0.txt +0 -21
  137. data/doc/release_notes/5.28.0.txt +0 -16
  138. data/doc/release_notes/5.29.0.txt +0 -22
  139. data/doc/release_notes/5.3.0.txt +0 -121
  140. data/doc/release_notes/5.30.0.txt +0 -20
  141. data/doc/release_notes/5.31.0.txt +0 -148
  142. data/doc/release_notes/5.32.0.txt +0 -46
  143. data/doc/release_notes/5.33.0.txt +0 -24
  144. data/doc/release_notes/5.34.0.txt +0 -40
  145. data/doc/release_notes/5.35.0.txt +0 -56
  146. data/doc/release_notes/5.36.0.txt +0 -60
  147. data/doc/release_notes/5.37.0.txt +0 -30
  148. data/doc/release_notes/5.38.0.txt +0 -28
  149. data/doc/release_notes/5.39.0.txt +0 -19
  150. data/doc/release_notes/5.4.0.txt +0 -80
  151. data/doc/release_notes/5.40.0.txt +0 -40
  152. data/doc/release_notes/5.41.0.txt +0 -25
  153. data/doc/release_notes/5.42.0.txt +0 -136
  154. data/doc/release_notes/5.43.0.txt +0 -98
  155. data/doc/release_notes/5.44.0.txt +0 -32
  156. data/doc/release_notes/5.45.0.txt +0 -34
  157. data/doc/release_notes/5.46.0.txt +0 -87
  158. data/doc/release_notes/5.47.0.txt +0 -59
  159. data/doc/release_notes/5.48.0.txt +0 -14
  160. data/doc/release_notes/5.49.0.txt +0 -59
  161. data/doc/release_notes/5.5.0.txt +0 -61
  162. data/doc/release_notes/5.50.0.txt +0 -78
  163. data/doc/release_notes/5.51.0.txt +0 -47
  164. data/doc/release_notes/5.52.0.txt +0 -87
  165. data/doc/release_notes/5.53.0.txt +0 -23
  166. data/doc/release_notes/5.54.0.txt +0 -27
  167. data/doc/release_notes/5.55.0.txt +0 -21
  168. data/doc/release_notes/5.56.0.txt +0 -51
  169. data/doc/release_notes/5.57.0.txt +0 -23
  170. data/doc/release_notes/5.58.0.txt +0 -31
  171. data/doc/release_notes/5.59.0.txt +0 -73
  172. data/doc/release_notes/5.6.0.txt +0 -31
  173. data/doc/release_notes/5.60.0.txt +0 -22
  174. data/doc/release_notes/5.61.0.txt +0 -43
  175. data/doc/release_notes/5.62.0.txt +0 -132
  176. data/doc/release_notes/5.63.0.txt +0 -33
  177. data/doc/release_notes/5.64.0.txt +0 -50
  178. data/doc/release_notes/5.65.0.txt +0 -21
  179. data/doc/release_notes/5.66.0.txt +0 -24
  180. data/doc/release_notes/5.67.0.txt +0 -32
  181. data/doc/release_notes/5.68.0.txt +0 -61
  182. data/doc/release_notes/5.69.0.txt +0 -26
  183. data/doc/release_notes/5.7.0.txt +0 -108
  184. data/doc/release_notes/5.70.0.txt +0 -35
  185. data/doc/release_notes/5.71.0.txt +0 -21
  186. data/doc/release_notes/5.72.0.txt +0 -33
  187. data/doc/release_notes/5.73.0.txt +0 -66
  188. data/doc/release_notes/5.74.0.txt +0 -45
  189. data/doc/release_notes/5.75.0.txt +0 -35
  190. data/doc/release_notes/5.76.0.txt +0 -86
  191. data/doc/release_notes/5.77.0.txt +0 -63
  192. data/doc/release_notes/5.78.0.txt +0 -67
  193. data/doc/release_notes/5.79.0.txt +0 -28
  194. data/doc/release_notes/5.8.0.txt +0 -170
  195. data/doc/release_notes/5.80.0.txt +0 -40
  196. data/doc/release_notes/5.9.0.txt +0 -99
  197. data/doc/schema_modification.rdoc +0 -679
  198. data/doc/security.rdoc +0 -443
  199. data/doc/sharding.rdoc +0 -286
  200. data/doc/sql.rdoc +0 -648
  201. data/doc/testing.rdoc +0 -190
  202. data/doc/thread_safety.rdoc +0 -15
  203. data/doc/transactions.rdoc +0 -250
  204. data/doc/validations.rdoc +0 -558
  205. data/doc/virtual_rows.rdoc +0 -265
@@ -129,7 +129,7 @@ module Sequel
129
129
  #
130
130
  # If a block is provided, it is used to set the :reject_if option.
131
131
  def nested_attributes(*associations, &block)
132
- include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module
132
+ include(@nested_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@nested_attributes_module"}) unless @nested_attributes_module
133
133
  opts = associations.last.is_a?(Hash) ? associations.pop : OPTS
134
134
  reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")}
135
135
  reflections.each do |r|
@@ -188,11 +188,10 @@ module Sequel
188
188
 
189
189
  # Create a new associated object with the given attributes, validate
190
190
  # it when the parent is validated, and save it when the object is saved.
191
- # Returns the object created.
191
+ # Returns the new object.
192
192
  def nested_attributes_create(meta, attributes)
193
+ obj = nested_attributes_new(meta, attributes)
193
194
  reflection = meta[:reflection]
194
- obj = reflection.associated_class.new
195
- nested_attributes_set_attributes(meta, obj, attributes)
196
195
  delay_validate_associated_object(reflection, obj)
197
196
  if reflection.returns_array?
198
197
  public_send(reflection[:name]) << obj
@@ -254,7 +253,13 @@ module Sequel
254
253
  end
255
254
  obj
256
255
  end
257
-
256
+
257
+ # Returns a new object of the associated class with the given attributes set.
258
+ def nested_attributes_new(meta, attributes)
259
+ obj = meta[:reflection].associated_class.new
260
+ nested_attributes_set_attributes(meta, obj, attributes)
261
+ end
262
+
258
263
  # Set the fields in the obj based on the association, only allowing
259
264
  # specific :fields if configured.
260
265
  def nested_attributes_set_attributes(meta, obj, attributes)
@@ -45,6 +45,8 @@ module Sequel
45
45
  columns[lc] = lcv + 1
46
46
  super
47
47
  set_column_value("#{lc}=", lcv + 1)
48
+ changed_columns.delete(lc)
49
+ nil
48
50
  end
49
51
  end
50
52
  end
@@ -157,13 +157,16 @@ module Sequel
157
157
  raise Error, "the paged_operations plugin is not supported on DB2 when using emulated offsets, set the :offset_strategy Database option to 'limit_offset' or 'offset_fetch'"
158
158
  end
159
159
 
160
- case pk = model.primary_key
160
+ case pk = unambiguous_primary_key
161
161
  when Symbol
162
162
  Sequel.identifier(pk)
163
163
  when Array
164
164
  raise Error, "cannot use #{meth} on a model with a composite primary key"
165
- else
165
+ when nil
166
166
  raise Error, "cannot use #{meth} on a model without a primary key"
167
+ else
168
+ # Likely SQL::QualifiedIdentifier, if the dataset is joined.
169
+ pk
167
170
  end
168
171
  end
169
172
 
@@ -135,7 +135,12 @@ module Sequel
135
135
  raise Error, "No pg_auto_constraint_validations setup" unless file = @pg_auto_constraint_validations_cache_file
136
136
  pg_auto_constraint_validations_cache = {}
137
137
  @pg_auto_constraint_validations_cache.sort.each do |k, v|
138
- pg_auto_constraint_validations_cache[k] = v
138
+ h = {}
139
+ v.each do |k, entry|
140
+ entry = Hash[entry.sort] if entry.is_a?(Hash)
141
+ h[k] = entry
142
+ end
143
+ pg_auto_constraint_validations_cache[k] = h
139
144
  end
140
145
  File.open(file, 'wb'){|f| f.write(Marshal.dump(pg_auto_constraint_validations_cache))}
141
146
  nil
@@ -0,0 +1,88 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The pg_auto_validate_enums plugin implements automatic validations for
6
+ # enum columns, ensuring that enum columns have a valid value. With this
7
+ # plugin, trying to save with an invalid enum value results in
8
+ # Sequel::ValidationFailed before saving, instead of Sequel::DatabaseError
9
+ # (wrapping PG::InvalidTextRepresentation or similar exception) during saving.
10
+ #
11
+ # class Person < Sequel::Model
12
+ # # assume state enum column with allowed values active and inactive
13
+ # plugin :pg_auto_validate_enums
14
+ # end
15
+ # p = Person.new(state: "active").valid? # => true
16
+ # p = Person.new(state: "inactive").valid? # => true
17
+ # p = Person.new(state: "other").valid? # => false
18
+ #
19
+ # While you can load this into individual model classes, typical use would
20
+ # be to load it into Sequel::Model or the appropriate model base class,
21
+ # and have all models that inherit from that class automatically pick it up.
22
+ #
23
+ # This plugin depends on the validation_helpers plugin.
24
+ module PgAutoValidateEnums
25
+ # Load the validation_helpers plugin.
26
+ def self.apply(model, opts=OPTS)
27
+ model.plugin(:validation_helpers)
28
+ end
29
+
30
+ # Load the pg_enum extension into the database, and reload the schema
31
+ # if it is already loaded. The opts given are used for the validates_includes
32
+ # validations (with allow_nil: true and from: :values enabled by default,
33
+ # to avoid issues with nullable enum columns and cases where the column
34
+ # method has been overridden.
35
+ def self.configure(model, opts=OPTS)
36
+ model.instance_exec do
37
+ db.extension(:pg_enum) unless @db.instance_variable_get(:@enum_labels)
38
+ if @db_schema
39
+ get_db_schema(true)
40
+ _get_pg_pg_auto_validate_enums_metadata
41
+ end
42
+ @pg_auto_validate_enums_opts = {allow_nil: true, from: :values}.merge!(opts).freeze
43
+ end
44
+ end
45
+
46
+ module ClassMethods
47
+ # Hash with enum column symbol values and arrays of valid string values.
48
+ attr_reader :pg_auto_validate_enums_metadata
49
+
50
+ # Options to pass to the validates_includes calls used by the plugin.
51
+ attr_reader :pg_auto_validate_enums_opts
52
+
53
+ Plugins.after_set_dataset(self, :_get_pg_pg_auto_validate_enums_metadata)
54
+
55
+ Plugins.inherited_instance_variables(self,
56
+ :@pg_auto_validate_enums_metadata=>nil,
57
+ :@pg_auto_validate_enums_opts=>nil)
58
+
59
+ private
60
+
61
+ # Parse the column schema to find columns with :enum_values entries,
62
+ # which will be used to setup validations.
63
+ def _get_pg_pg_auto_validate_enums_metadata
64
+ metadata = {}
65
+ @db_schema.each do |key, sch|
66
+ if enum_values = sch[:enum_values]
67
+ metadata[key] = enum_values
68
+ end
69
+ end
70
+ @pg_auto_validate_enums_metadata = metadata.freeze
71
+ end
72
+ end
73
+
74
+ module InstanceMethods
75
+ # Validate that all of the model's enum columns have valid values.
76
+ def validate
77
+ super
78
+
79
+ klass = self.class
80
+ opts = klass.pg_auto_validate_enums_opts
81
+ klass.pg_auto_validate_enums_metadata.each do |column, values|
82
+ validates_includes(values, column, opts)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,95 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The pg_eager_any_typed_array plugin automatically converts
6
+ # the predicate expressions used for eager loading from:
7
+ #
8
+ # table.column IN (value_list)
9
+ #
10
+ # to:
11
+ #
12
+ # table.column = ANY(array_expr::type[])
13
+ #
14
+ # This makes it easier to use the pg_auto_parameterize_in_array
15
+ # extension with the :treat_string_list_as_text_array option,
16
+ # when using foreign keys with non-text database types that are represented
17
+ # by Ruby strings, such as enum and uuid types.
18
+ #
19
+ # Most association types that ship with Sequel have their predicate
20
+ # expressions converted by this plugin. Here are the exceptions:
21
+ #
22
+ # * associations using composite predicate keys
23
+ # * many_to_pg_array associations
24
+ # * many_to_many/one_through_one associations using :join_table_db option
25
+ # * many_through_many/one_through_many associations using
26
+ # :separate_table_per_query option
27
+ #
28
+ # To avoid predicate conversion for particular associations, set the
29
+ # :eager_loading_predicate_transform association option to nil/false.
30
+ #
31
+ # This plugin loads the pg_array extension into the model's Database.
32
+ module PgEagerAnyTypedArray
33
+ # Add the pg_array extension to the database
34
+ def self.apply(model)
35
+ model.db.extension(:pg_array)
36
+ end
37
+
38
+ module ClassMethods
39
+ TRANSFORM = proc do |values, ref|
40
+ type = ref.send(:cached_fetch, :_pg_eager_any_typed_array_type) do
41
+ key = ref.predicate_key
42
+ next if key.is_a?(Array)
43
+
44
+ while key.is_a?(SQL::QualifiedIdentifier)
45
+ key = key.column
46
+ end
47
+
48
+ # :nocov:
49
+ # many_to_pg_array association type does not need changes, as it
50
+ # already converts the values to a typed postgres array, it does
51
+ # not call the code that uses :eager_loading_predicate_transform.
52
+ #
53
+ # No association type that ships with Sequel can reach this code
54
+ # unless it is one of these types, but external association types
55
+ # could potentially reach it.
56
+ sch = case ref[:type]
57
+ # :nocov:
58
+ when :many_to_one, :one_to_one, :one_to_many, :pg_array_to_many
59
+ ref.associated_class.db_schema
60
+ when :many_to_many, :one_through_one
61
+ # Not compatible with the :join_table_db option, but that option
62
+ # does not call into this code.
63
+ Hash[ref.associated_class.db.schema(ref.join_table_source)]
64
+ when :many_through_many, :one_through_many
65
+ # Not compatible with the :separate_query_per_table option, but
66
+ # that option does not call into this code.
67
+ Hash[ref.associated_class.db.schema(ref[:through][0][:table])]
68
+ end
69
+
70
+ if sch && (sch = sch[key])
71
+ sch[:db_type]
72
+ end
73
+ end
74
+
75
+ if type
76
+ Sequel.function(:ANY, Sequel.pg_array(values, type))
77
+ else
78
+ values
79
+ end
80
+ end
81
+
82
+ # Set the :eager_loading_predicate_transform option if not already set
83
+ def associate(type, name, opts = OPTS, &block)
84
+ res = super
85
+
86
+ unless res.has_key?(:eager_loading_predicate_transform)
87
+ res[:eager_loading_predicate_transform] = TRANSFORM
88
+ end
89
+
90
+ res
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -81,7 +81,7 @@ module Sequel
81
81
 
82
82
  opts = opts.dup
83
83
  opts[:class] = model
84
- opts[:methods_module] = Module.new
84
+ opts[:methods_module] = Sequel.set_temp_name(Module.new){"#{model.name}::_rcte_tree[:methods_module]"}
85
85
  opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all]
86
86
  model.send(:include, opts[:methods_module])
87
87
 
@@ -37,7 +37,8 @@ module Sequel
37
37
  #
38
38
  # # Register custom serializer/deserializer pair, if desired
39
39
  # require 'sequel/plugins/serialization'
40
- # Sequel::Plugins::Serialization.register_format(:reverse, :reverse.to_proc, :reverse.to_proc)
40
+ # require 'base64'
41
+ # Sequel::Plugins::Serialization.register_format(:base64, Base64.method(:encode64), Base64.method(:decode64))
41
42
  #
42
43
  # class User < Sequel::Model
43
44
  # # Built-in format support when loading the plugin
@@ -48,10 +49,10 @@ module Sequel
48
49
  # serialize_attributes :marshal, :permissions
49
50
  #
50
51
  # # Use custom registered serialization format just like built-in format
51
- # serialize_attributes :reverse, :password
52
+ # serialize_attributes :base64, :password
52
53
  #
53
54
  # # Use a custom serializer/deserializer pair without registering
54
- # serialize_attributes [:reverse.to_proc, :reverse.to_proc], :password
55
+ # serialize_attributes [ Base64.method(:encode64), Base64.method(:decode64)], :password
55
56
  # end
56
57
  # user = User.create
57
58
  # user.permissions = {global: 'read-only'}
@@ -123,7 +124,12 @@ module Sequel
123
124
  end
124
125
 
125
126
  # Create instance level reader that deserializes column values on request,
126
- # and instance level writer that stores new deserialized values.
127
+ # and instance level writer that stores new deserialized values. If +format+
128
+ # is a symbol, it should correspond to a previously-registered format using +register_format+.
129
+ # Otherwise, +format+ is expected to be a 2-element array of callables,
130
+ # with the first element being the serializer, used to convert the value used by the application
131
+ # to the value that will be stored in the database, and the second element being the deserializer,
132
+ # used to convert the value stored the database to the value used by the application.
127
133
  def serialize_attributes(format, *columns)
128
134
  if format.is_a?(Symbol)
129
135
  unless format = Sequel.synchronize{REGISTERED_FORMATS[format]}
@@ -140,7 +146,7 @@ module Sequel
140
146
  # Add serializated attribute acessor methods to the serialization_module
141
147
  def define_serialized_attribute_accessor(serializer, deserializer, *columns)
142
148
  m = self
143
- include(@serialization_module ||= Module.new) unless @serialization_module
149
+ include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module
144
150
  @serialization_module.class_eval do
145
151
  columns.each do |column|
146
152
  m.serialization_map[column] = serializer
@@ -40,7 +40,8 @@ module Sequel
40
40
  # Album.sql_comments_dataset_methods :to_csv # csv_serializer plugin
41
41
  #
42
42
  # In order for the sql_comments plugin to work, the sql_comments
43
- # Database extension must be loaded into the model's database.
43
+ # Database extension must be loaded into the model's database, so
44
+ # loading the plugin does this automatically.
44
45
  #
45
46
  # Note that in order to make sure SQL comments are included, some
46
47
  # optimizations are disabled if this plugin is loaded.
@@ -67,6 +68,10 @@ module Sequel
67
68
  # :nocov:
68
69
  end
69
70
 
71
+ def self.apply(model)
72
+ model.db.extension(:sql_comments)
73
+ end
74
+
70
75
  def self.configure(model)
71
76
  model.send(:reset_fast_pk_lookup_sql)
72
77
  end
@@ -85,7 +90,7 @@ module Sequel
85
90
  # Use automatic SQL comments for the given dataset methods.
86
91
  def sql_comments_dataset_methods(*meths)
87
92
  unless @_sql_comments_dataset_module
88
- dataset_module(@_sql_comments_dataset_module = Module.new)
93
+ dataset_module(@_sql_comments_dataset_module = Sequel.set_temp_name(Module.new){"#{name}::@_sql_comments_dataset_module"})
89
94
  end
90
95
  _sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths)
91
96
  end
@@ -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