sequel 5.39.0 → 5.64.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +318 -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/schema_modification.rdoc +1 -1
  41. data/doc/security.rdoc +9 -9
  42. data/doc/sql.rdoc +27 -15
  43. data/doc/testing.rdoc +22 -11
  44. data/doc/transactions.rdoc +6 -6
  45. data/doc/virtual_rows.rdoc +2 -2
  46. data/lib/sequel/adapters/ado/access.rb +1 -1
  47. data/lib/sequel/adapters/ado.rb +17 -17
  48. data/lib/sequel/adapters/amalgalite.rb +3 -5
  49. data/lib/sequel/adapters/ibmdb.rb +2 -2
  50. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  51. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  52. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  54. data/lib/sequel/adapters/jdbc.rb +16 -18
  55. data/lib/sequel/adapters/mysql.rb +80 -67
  56. data/lib/sequel/adapters/mysql2.rb +54 -49
  57. data/lib/sequel/adapters/odbc.rb +6 -2
  58. data/lib/sequel/adapters/oracle.rb +4 -3
  59. data/lib/sequel/adapters/postgres.rb +83 -40
  60. data/lib/sequel/adapters/shared/access.rb +11 -1
  61. data/lib/sequel/adapters/shared/db2.rb +30 -0
  62. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  63. data/lib/sequel/adapters/shared/mysql.rb +40 -2
  64. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  65. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  66. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  67. data/lib/sequel/adapters/shared/sqlite.rb +103 -11
  68. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  69. data/lib/sequel/adapters/sqlite.rb +60 -18
  70. data/lib/sequel/adapters/tinytds.rb +1 -1
  71. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  72. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  73. data/lib/sequel/ast_transformer.rb +6 -0
  74. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  75. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  76. data/lib/sequel/connection_pool/single.rb +6 -8
  77. data/lib/sequel/connection_pool/threaded.rb +8 -8
  78. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  79. data/lib/sequel/connection_pool.rb +47 -30
  80. data/lib/sequel/core.rb +28 -18
  81. data/lib/sequel/database/connecting.rb +26 -2
  82. data/lib/sequel/database/misc.rb +69 -14
  83. data/lib/sequel/database/query.rb +73 -2
  84. data/lib/sequel/database/schema_generator.rb +45 -52
  85. data/lib/sequel/database/schema_methods.rb +17 -1
  86. data/lib/sequel/dataset/actions.rb +107 -13
  87. data/lib/sequel/dataset/features.rb +20 -0
  88. data/lib/sequel/dataset/misc.rb +1 -1
  89. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  90. data/lib/sequel/dataset/query.rb +118 -16
  91. data/lib/sequel/dataset/sql.rb +177 -47
  92. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  93. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  94. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  95. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  96. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  97. data/lib/sequel/extensions/blank.rb +8 -0
  98. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  99. data/lib/sequel/extensions/core_refinements.rb +36 -11
  100. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  101. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  102. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  103. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  104. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  105. data/lib/sequel/extensions/inflector.rb +9 -1
  106. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  107. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  108. data/lib/sequel/extensions/migration.rb +7 -2
  109. data/lib/sequel/extensions/named_timezones.rb +26 -6
  110. data/lib/sequel/extensions/pagination.rb +1 -1
  111. data/lib/sequel/extensions/pg_array.rb +23 -3
  112. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  113. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  114. data/lib/sequel/extensions/pg_enum.rb +1 -1
  115. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  116. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  117. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  118. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  119. data/lib/sequel/extensions/pg_inet.rb +10 -11
  120. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  121. data/lib/sequel/extensions/pg_interval.rb +45 -19
  122. data/lib/sequel/extensions/pg_json.rb +13 -15
  123. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  124. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  125. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  126. data/lib/sequel/extensions/pg_range.rb +10 -23
  127. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  128. data/lib/sequel/extensions/pg_row.rb +19 -13
  129. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  130. data/lib/sequel/extensions/query.rb +2 -0
  131. data/lib/sequel/extensions/s.rb +2 -1
  132. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  133. data/lib/sequel/extensions/server_block.rb +8 -12
  134. data/lib/sequel/extensions/sql_comments.rb +110 -3
  135. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  136. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  137. data/lib/sequel/extensions/string_agg.rb +1 -1
  138. data/lib/sequel/extensions/string_date_time.rb +19 -23
  139. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  140. data/lib/sequel/model/associations.rb +330 -96
  141. data/lib/sequel/model/base.rb +51 -27
  142. data/lib/sequel/model/errors.rb +10 -1
  143. data/lib/sequel/model/inflections.rb +1 -1
  144. data/lib/sequel/model/plugins.rb +5 -0
  145. data/lib/sequel/plugins/association_proxies.rb +2 -0
  146. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  147. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  148. data/lib/sequel/plugins/auto_validations.rb +87 -15
  149. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  150. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  151. data/lib/sequel/plugins/column_encryption.rb +728 -0
  152. data/lib/sequel/plugins/composition.rb +10 -4
  153. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  154. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  155. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  156. data/lib/sequel/plugins/dirty.rb +1 -1
  157. data/lib/sequel/plugins/enum.rb +124 -0
  158. data/lib/sequel/plugins/finder.rb +3 -1
  159. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  160. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  161. data/lib/sequel/plugins/json_serializer.rb +39 -24
  162. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  163. data/lib/sequel/plugins/list.rb +3 -1
  164. data/lib/sequel/plugins/many_through_many.rb +109 -10
  165. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  166. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  167. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  168. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  169. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  170. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  171. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  172. data/lib/sequel/plugins/serialization.rb +9 -3
  173. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  174. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  175. data/lib/sequel/plugins/sql_comments.rb +189 -0
  176. data/lib/sequel/plugins/static_cache.rb +1 -1
  177. data/lib/sequel/plugins/subclasses.rb +28 -11
  178. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  179. data/lib/sequel/plugins/timestamps.rb +1 -1
  180. data/lib/sequel/plugins/unused_associations.rb +521 -0
  181. data/lib/sequel/plugins/update_or_create.rb +1 -1
  182. data/lib/sequel/plugins/validate_associated.rb +22 -12
  183. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  184. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  185. data/lib/sequel/sql.rb +1 -1
  186. data/lib/sequel/timezones.rb +12 -14
  187. data/lib/sequel/version.rb +1 -1
  188. metadata +99 -43
@@ -39,8 +39,8 @@ module Sequel
39
39
  # also be implemented as:
40
40
  #
41
41
  # Album.composition :date,
42
- # :composer=>proc{Date.new(year, month, day) if year || month || day},
43
- # :decomposer=>(proc do
42
+ # composer: proc{Date.new(year, month, day) if year || month || day},
43
+ # decomposer: (proc do
44
44
  # if d = compositions[:date]
45
45
  # self.year = d.year
46
46
  # self.month = d.month
@@ -143,10 +143,14 @@ module Sequel
143
143
  compositions[name] = send(composer_meth)
144
144
  end
145
145
  end
146
- define_method("#{name}=") do |v|
146
+ alias_method(name, name)
147
+
148
+ meth = :"#{name}="
149
+ define_method(meth) do |v|
147
150
  modified!
148
151
  compositions[name] = v
149
152
  end
153
+ alias_method(meth, meth)
150
154
  end
151
155
  end
152
156
 
@@ -167,8 +171,10 @@ module Sequel
167
171
 
168
172
  # Freeze compositions hash when freezing model instance.
169
173
  def freeze
170
- compositions.freeze
174
+ compositions
171
175
  super
176
+ compositions.freeze
177
+ self
172
178
  end
173
179
 
174
180
  # For each composition, set the columns in the model class based
@@ -0,0 +1,174 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ extension 'async_thread_pool'
5
+
6
+ module Plugins
7
+ # The concurrent_eager_loading plugin allows for eager loading multiple associations
8
+ # concurrently in separate threads. You must load the async_thread_pool Database
9
+ # extension into the Database object the model class uses in order for this plugin
10
+ # to work.
11
+ #
12
+ # By default in Sequel, eager loading happens in a serial manner. If you have code
13
+ # such as:
14
+ #
15
+ # Album.eager(:artist, :genre, :tracks)
16
+ #
17
+ # Sequel will load the albums, then the artists for the albums, then
18
+ # the genres for the albums, then the tracks for the albums.
19
+ #
20
+ # With the concurrent_eager_loading plugin, you can use the +eager_load_concurrently+
21
+ # method to allow for concurrent eager loading:
22
+ #
23
+ # Album.eager_load_concurrently.eager(:artist, :genre, :tracks)
24
+ #
25
+ # This will load the albums, first, since it needs to load the albums to know
26
+ # which artists, genres, and tracks to eagerly load. However, it will load the
27
+ # artists, genres, and tracks for the albums concurrently in separate threads.
28
+ # This can significantly improve performance, especially if there is significant
29
+ # latency between the application and the database. Note that using separate threads
30
+ # is only used in the case where there are multiple associations to eagerly load.
31
+ # With only a single association to eagerly load, there is no reason to use a
32
+ # separate thread, since it would not improve performance.
33
+ #
34
+ # If you want to make concurrent eager loading the default, you can load the
35
+ # plugin with the +:always+ option. In this case, all eager loads will be
36
+ # concurrent. If you want to force a non-concurrent eager load, you can use
37
+ # +eager_load_serially+:
38
+ #
39
+ # Album.eager_load_serially.eager(:artist, :genre, :tracks)
40
+ #
41
+ # Note that making concurrent eager loading the default is probably a bad idea
42
+ # if you are eager loading inside transactions and want the eager load to
43
+ # reflect changes made inside the transaction, unless you plan to use
44
+ # +eager_load_serially+ for such cases. See the async_thread_pool
45
+ # Database extension documentation for more general caveats regarding its use.
46
+ #
47
+ # The default eager loaders for all of the association types that ship with Sequel
48
+ # support safe concurrent eager loading. However, if you are specifying a custom
49
+ # +:eager_loader+ for an association, it may not work safely unless it it modified to
50
+ # support concurrent eager loading. Taking this example from the
51
+ # {Advanced Associations guide}[rdoc-ref:doc/advanced_associations.rdoc]
52
+ #
53
+ # Album.many_to_one :artist, eager_loader: (proc do |eo_opts|
54
+ # eo_opts[:rows].each{|album| album.associations[:artist] = nil}
55
+ # id_map = eo_opts[:id_map]
56
+ # Artist.where(id: id_map.keys).all do |artist|
57
+ # if albums = id_map[artist.id]
58
+ # albums.each do |album|
59
+ # album.associations[:artist] = artist
60
+ # end
61
+ # end
62
+ # end
63
+ # end)
64
+ #
65
+ # This would not support concurrent eager loading safely. To support safe
66
+ # concurrent eager loading, you need to make sure you are not modifying
67
+ # the associations for objects concurrently by separate threads. This is
68
+ # implemented using a mutex, which you can access via <tt>eo_opts[:mutex]</tt>.
69
+ # To keep things simple, you can use +Sequel.synchronize_with+ to only
70
+ # use this mutex if it is available. You want to use the mutex around the
71
+ # code that initializes the associations (usually to +nil+ or <tt>[]</tt>),
72
+ # and also around the code that sets the associatied objects appropriately
73
+ # after they have been retreived. You do not want to use the mutex around
74
+ # the code that loads the objects, since that will prevent concurrent loading.
75
+ # So after the changes, the custom eager loader would look like this:
76
+ #
77
+ # Album.many_to_one :artist, eager_loader: (proc do |eo_opts|
78
+ # Sequel.synchronize_with(eo[:mutex]) do
79
+ # eo_opts[:rows].each{|album| album.associations[:artist] = nil}
80
+ # end
81
+ # id_map = eo_opts[:id_map]
82
+ # rows = Artist.where(id: id_map.keys).all
83
+ # Sequel.synchronize_with(eo[:mutex]) do
84
+ # rows.each do |artist|
85
+ # if albums = id_map[artist.id]
86
+ # albums.each do |album|
87
+ # album.associations[:artist] = artist
88
+ # end
89
+ # end
90
+ # end
91
+ # end
92
+ # end)
93
+ #
94
+ # Usage:
95
+ #
96
+ # # Make all model subclass datasets support concurrent eager loading
97
+ # Sequel::Model.plugin :concurrent_eager_loading
98
+ #
99
+ # # Make the Album class datasets support concurrent eager loading
100
+ # Album.plugin :concurrent_eager_loading
101
+ #
102
+ # # Make all model subclass datasets concurrently eager load by default
103
+ # Sequel::Model.plugin :concurrent_eager_loading, always: true
104
+ module ConcurrentEagerLoading
105
+ def self.configure(mod, opts=OPTS)
106
+ if opts.has_key?(:always)
107
+ mod.instance_variable_set(:@always_eager_load_concurrently, opts[:always])
108
+ end
109
+ end
110
+
111
+ module ClassMethods
112
+ Plugins.inherited_instance_variables(self, :@always_eager_load_concurrently => nil)
113
+ Plugins.def_dataset_methods(self, [:eager_load_concurrently, :eager_load_serially])
114
+
115
+ # Whether datasets for this class should eager load concurrently by default.
116
+ def always_eager_load_concurrently?
117
+ @always_eager_load_concurrently
118
+ end
119
+ end
120
+
121
+ module DatasetMethods
122
+ # Return a cloned dataset that will eager load associated results concurrently
123
+ # using the async thread pool.
124
+ def eager_load_concurrently
125
+ cached_dataset(:_eager_load_concurrently) do
126
+ clone(:eager_load_concurrently=>true)
127
+ end
128
+ end
129
+
130
+ # Return a cloned dataset that will noteager load associated results concurrently
131
+ # using the async thread pool. Only useful if the current dataset has been marked
132
+ # as loading concurrently, or loading concurrently is the model's default behavior.
133
+ def eager_load_serially
134
+ cached_dataset(:_eager_load_serially) do
135
+ clone(:eager_load_concurrently=>false)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ # Whether this particular dataset will eager load results concurrently.
142
+ def eager_load_concurrently?
143
+ v = @opts[:eager_load_concurrently]
144
+ v.nil? ? model.always_eager_load_concurrently? : v
145
+ end
146
+
147
+ # If performing eager loads concurrently, and at least 2 associations are being
148
+ # eagerly loaded, create a single mutex used for all eager loads. After the
149
+ # eager loads have been performed, force loading of any async results, so that
150
+ # all eager loads will have been completed before this method returns.
151
+ def perform_eager_loads(eager_load_data)
152
+ return super if !eager_load_concurrently? || eager_load_data.length < 2
153
+
154
+ mutex = Mutex.new
155
+ eager_load_data.each_value do |eo|
156
+ eo[:mutex] = mutex
157
+ end
158
+
159
+ super.each do |v|
160
+ if Sequel::Database::AsyncThreadPool::BaseProxy === v
161
+ v.__value
162
+ end
163
+ end
164
+ end
165
+
166
+ # If performing eager loads concurrently, perform this eager load using the
167
+ # async thread pool.
168
+ def perform_eager_load(loader, eo)
169
+ eo[:mutex] ? db.send(:async_run){super} : super
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -220,7 +220,8 @@ module Sequel
220
220
  '%'
221
221
  when '%'
222
222
  '.*'
223
- when '_'
223
+ else
224
+ #when '_'
224
225
  '.'
225
226
  end
226
227
  end
@@ -62,7 +62,10 @@ module Sequel
62
62
  ret = super
63
63
  r = association_reflection(name)
64
64
  meth = r.returns_array? ? name : pluralize(name).to_sym
65
- dataset_module{define_method(meth){associated(name)}}
65
+ dataset_module do
66
+ define_method(meth){associated(name)}
67
+ alias_method(meth, meth)
68
+ end
66
69
  ret
67
70
  end
68
71
 
@@ -37,7 +37,7 @@ module Sequel
37
37
  #
38
38
  # It also saves the previously changed values after an update:
39
39
  #
40
- # artist.update(:name=>'Bar')
40
+ # artist.update(name: 'Bar')
41
41
  # artist.column_changes # => {}
42
42
  # artist.previous_changes # => {:name=>['Foo', 'Bar']}
43
43
  #
@@ -0,0 +1,124 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The enum plugin allows for easily adding methods to modify the value of
6
+ # a column. It allows treating the column itself as an enum, returning a
7
+ # symbol for the related enum value. It also allows for setting up dataset
8
+ # methods to easily find records having or not having each enum value.
9
+ #
10
+ # After loading the plugin, you can call the +enum+ method to define the
11
+ # methods. The +enum+ method accepts a symbol for the underlying
12
+ # database column, and a hash with symbol keys for the enum values.
13
+ # For example, the following call:
14
+ #
15
+ # Album.enum :status_id, good: 1, bad: 2
16
+ #
17
+ # Will define the following instance methods:
18
+ #
19
+ # Album#good! :: Change +status_id+ to +1+ (does not save the receiver)
20
+ # Album#bad! :: Change +status_id+ to +2+ (does not save the receiver)
21
+ # Album#good? :: Return whether +status_id+ is +1+
22
+ # Album#bad? :: Return whether +status_id+ is +2+
23
+ #
24
+ # It will override the following instance methods:
25
+ #
26
+ # Album#status_id :: Return +:good+/+:bad+ instead of +1+/+2+ (other values returned as-is)
27
+ # Album#status_id= :: Allow calling with +:good+/+:bad+ to set +status_id+ to +1+/+2+ (other values,
28
+ # such as <tt>'good'</tt>/<tt>'bad'</tt> set as-is)
29
+ #
30
+ # If will define the following dataset methods:
31
+ #
32
+ # Album.dataset.good :: Return a dataset filtered to rows where +status_id+ is +1+
33
+ # Album.dataset.not_good :: Return a dataset filtered to rows where +status_id+ is not +1+
34
+ # Album.dataset.bad:: Return a dataset filtered to rows where +status_id+ is +2+
35
+ # Album.dataset.not_bad:: Return a dataset filtered to rows where +status_id+ is not +2+
36
+ #
37
+ # When calling +enum+, you can also provide the following options:
38
+ #
39
+ # :prefix :: Use a prefix for methods defined for each enum value. If +true+ is provided at the value, use the column name as the prefix.
40
+ # For example, with <tt>prefix: 'status'</tt>, the instance methods defined above would be +status_good?+, +status_bad?+,
41
+ # +status_good!+, and +status_bad!+, and the dataset methods defined would be +status_good+, +status_not_good+, +status_bad+,
42
+ # and +status_not_bad+.
43
+ # :suffix :: Use a suffix for methods defined for each enum value. If +true+ is provided at the value, use the column name as the suffix.
44
+ # For example, with <tt>suffix: 'status'</tt>, the instance methods defined above would be +good_status?+, +bad_status?+,
45
+ # +good_status!+, and +bad_status!+, and the dataset methods defined would be +good_status+, +not_good_status+, +bad_status+,
46
+ # and +not_bad_status+.
47
+ # :override_accessors :: Set to +false+ to not override the column accessor methods.
48
+ # :dataset_methods :: Set to +false+ to not define dataset methods.
49
+ #
50
+ # Note that this does not use a true enum column in the database. If you are
51
+ # looking for enum support in the database, and your are using PostgreSQL,
52
+ # Sequel supports that via the pg_enum Database extension.
53
+ #
54
+ # Usage:
55
+ #
56
+ # # Make all model subclasses handle enums
57
+ # Sequel::Model.plugin :enum
58
+ #
59
+ # # Make the Album class handle enums
60
+ # Album.plugin :enum
61
+ module Enum
62
+ module ClassMethods
63
+ # Define instance and dataset methods in this class to treat column
64
+ # as a enum. See Enum documentation for usage.
65
+ def enum(column, values, opts=OPTS)
66
+ raise Sequel::Error, "enum column must be a symbol" unless column.is_a?(Symbol)
67
+ raise Sequel::Error, "enum values must be provided as a hash with symbol keys" unless values.is_a?(Hash) && values.all?{|k,| k.is_a?(Symbol)}
68
+
69
+ if prefix = opts[:prefix]
70
+ prefix = column if prefix == true
71
+ prefix = "#{prefix}_"
72
+ end
73
+
74
+ if suffix = opts[:suffix]
75
+ suffix = column if suffix == true
76
+ suffix = "_#{suffix}"
77
+ end
78
+
79
+ values = Hash[values].freeze
80
+ inverted = values.invert.freeze
81
+
82
+ unless @enum_methods
83
+ @enum_methods = Module.new
84
+ include @enum_methods
85
+ end
86
+
87
+ @enum_methods.module_eval do
88
+ unless opts[:override_accessors] == false
89
+ define_method(column) do
90
+ v = super()
91
+ inverted.fetch(v, v)
92
+ end
93
+
94
+ define_method(:"#{column}=") do |v|
95
+ super(values.fetch(v, v))
96
+ end
97
+ end
98
+
99
+ values.each do |key, value|
100
+ define_method(:"#{prefix}#{key}#{suffix}!") do
101
+ self[column] = value
102
+ nil
103
+ end
104
+
105
+ define_method(:"#{prefix}#{key}#{suffix}?") do
106
+ self[column] == value
107
+ end
108
+ end
109
+ end
110
+
111
+ unless opts[:dataset_methods] == false
112
+ dataset_module do
113
+ values.each do |key, value|
114
+ cond = Sequel[column=>value]
115
+ where :"#{prefix}#{key}#{suffix}", cond
116
+ where :"#{prefix}not_#{key}#{suffix}", ~cond
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -97,7 +97,9 @@ module Sequel
97
97
  # Artist.first_by_name(nil)
98
98
  # # WHERE (name IS NULL)
99
99
  #
100
- # See Dataset::PlaceholderLiteralizer for additional caveats.
100
+ # See Dataset::PlaceholderLiteralizer for additional caveats. Note that if the model's
101
+ # dataset does not support placeholder literalizers, you will not be able to use this
102
+ # method.
101
103
  def finder(meth=OPTS, opts=OPTS, &block)
102
104
  if block
103
105
  raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
@@ -22,6 +22,10 @@ module Sequel
22
22
  # for that album. See the PostgreSQL and SQLite adapter documention for
23
23
  # the options you can pass to the insert_conflict method.
24
24
  #
25
+ # You should not attempt to use this plugin to ignore conflicts when
26
+ # inserting, you should only use it to turn insert conflicts into updates.
27
+ # Any usage to ignore conflicts is not recommended or supported.
28
+ #
25
29
  # Usage:
26
30
  #
27
31
  # # Make all model subclasses support insert_conflict
@@ -29,7 +29,7 @@ module Sequel
29
29
  # end
30
30
  #
31
31
  # +first_track+ is not instance specific, but +last_track+ and +recent_tracks+ are.
32
- # +last_trac+ is because the +num_tracks+ call in the block is calling
32
+ # +last_track+ is because the +num_tracks+ call in the block is calling
33
33
  # <tt>Album#num_tracks</tt>. +recent_tracks+ is because the value will change over
34
34
  # time. This plugin allows you to find these cases, and set the :instance_specific
35
35
  # option appropriately for them:
@@ -133,21 +133,39 @@ module Sequel
133
133
  end
134
134
  end
135
135
 
136
- # Helper class used for making sure that cascading options
137
- # for model associations works correctly. Cascaded options
138
- # work by creating instances of this class, which take a
139
- # literal JSON string and have +to_json+ return it.
136
+ # SEQUEL6: Remove
137
+ # :nocov:
140
138
  class Literal
141
- # Store the literal JSON to use
142
139
  def initialize(json)
143
140
  @json = json
144
141
  end
145
142
 
146
- # Return the literal JSON to use
147
143
  def to_json(*a)
148
144
  @json
149
145
  end
150
146
  end
147
+ # :nocov:
148
+ Sequel::Deprecation.deprecate_constant(self, :Literal)
149
+
150
+ # Convert the given object to a JSON data structure using the given arguments.
151
+ def self.object_to_json_data(obj, *args, &block)
152
+ if obj.is_a?(Array)
153
+ obj.map{|x| object_to_json_data(x, *args, &block)}
154
+ else
155
+ if obj.respond_to?(:to_json_data)
156
+ obj.to_json_data(*args, &block)
157
+ else
158
+ begin
159
+ Sequel.parse_json(Sequel.object_to_json(obj, *args, &block))
160
+ # :nocov:
161
+ rescue Sequel.json_parser_error_class
162
+ # Support for old Ruby code that only supports parsing JSON object/array
163
+ Sequel.parse_json(Sequel.object_to_json([obj], *args, &block))[0]
164
+ # :nocov:
165
+ end
166
+ end
167
+ end
168
+ end
151
169
 
152
170
  module ClassMethods
153
171
  # The default opts to use when serializing model objects to JSON.
@@ -324,20 +342,7 @@ module Sequel
324
342
  end
325
343
 
326
344
  v = v.empty? ? [] : [v]
327
-
328
- objs = public_send(k)
329
-
330
- is_array = if r = model.association_reflection(k)
331
- r.returns_array?
332
- else
333
- objs.is_a?(Array)
334
- end
335
-
336
- h[key_name] = if is_array
337
- objs.map{|obj| Literal.new(Sequel.object_to_json(obj, *v))}
338
- else
339
- Literal.new(Sequel.object_to_json(objs, *v))
340
- end
345
+ h[key_name] = JsonSerializer.object_to_json_data(public_send(k), *v)
341
346
  end
342
347
  else
343
348
  Array(inc).each do |c|
@@ -347,7 +352,8 @@ module Sequel
347
352
  else
348
353
  key_name = c.to_s
349
354
  end
350
- h[key_name] = public_send(c)
355
+
356
+ h[key_name] = JsonSerializer.object_to_json_data(public_send(c))
351
357
  end
352
358
  end
353
359
  end
@@ -359,9 +365,18 @@ module Sequel
359
365
  h = {root => h}
360
366
  end
361
367
 
362
- h = yield h if block_given?
368
+ h = yield h if defined?(yield)
363
369
  Sequel.object_to_json(h, *a)
364
370
  end
371
+
372
+ # Convert the receiver to a JSON data structure using the given arguments.
373
+ def to_json_data(*args, &block)
374
+ if block
375
+ to_json(*args){|x| return block.call(x)}
376
+ else
377
+ to_json(*args){|x| return x}
378
+ end
379
+ end
365
380
  end
366
381
 
367
382
  module DatasetMethods
@@ -420,13 +435,13 @@ module Sequel
420
435
  else
421
436
  all
422
437
  end
423
- array.map{|obj| Literal.new(Sequel.object_to_json(obj, opts, &opts[:instance_block]))}
438
+ JsonSerializer.object_to_json_data(array, opts, &opts[:instance_block])
424
439
  else
425
440
  all
426
441
  end
427
442
 
428
443
  res = {collection_root => res} if collection_root
429
- res = yield res if block_given?
444
+ res = yield res if defined?(yield)
430
445
 
431
446
  Sequel.object_to_json(res, *a)
432
447
  end
@@ -52,7 +52,9 @@ module Sequel
52
52
  unless select = dataset.opts[:select]
53
53
  select = dataset.columns.map{|c| Sequel.qualify(dataset.first_source, c)}
54
54
  end
55
+ db_schema = @db_schema
55
56
  set_dataset(dataset.select(*select.reject{|c| attrs.include?(dataset.send(:_hash_key_symbol, c))}))
57
+ @db_schema = db_schema
56
58
  attrs.each{|a| define_lazy_attribute_getter(a)}
57
59
  end
58
60
 
@@ -71,6 +73,7 @@ module Sequel
71
73
  super()
72
74
  end
73
75
  end
76
+ alias_method(a, a)
74
77
  end
75
78
  end
76
79
  end
@@ -33,7 +33,9 @@ module Sequel
33
33
  # You can provide a <tt>:scope</tt> option to scope the list. This option
34
34
  # can be a symbol or array of symbols specifying column name(s), or a proc
35
35
  # that accepts a model instance and returns a dataset representing the list
36
- # the object is in.
36
+ # the object is in. You will need to provide a <tt>:scope</tt> option if
37
+ # the model's dataset uses a subquery (such as when using the class_table_inheritance
38
+ # plugin).
37
39
  #
38
40
  # For example, if each item has a +user_id+ field, and you want every user
39
41
  # to have their own list: