sequel 5.52.0 → 5.55.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06302dbd32fc7e13539c83a4cf86fa72d974ffb15d403f30ca13d08af83c0605
4
- data.tar.gz: ad8cc158183399ac93c5a44e0e33d0693a92599ccb5092f542ff2993f4701d25
3
+ metadata.gz: dfed01eed8d575d13963be4568c8935f82c47a59b2be32d382439d50153f032d
4
+ data.tar.gz: 9ac182c00cdc1f54c26626005f990c5e0a05eebaae3b3c5b88bac0fcd770cd8e
5
5
  SHA512:
6
- metadata.gz: 9b0943434af7e097bf7589ec885b6509f40b9677d7e1be56cac17f5093bc2c9a6c1e79918b547a4d04a2623b5562e4e8897247551f1a89ecde5c53339e6c9a48
7
- data.tar.gz: a82852fa8dd6b598b7463acafab29bd6d1b5e514b639e3d13ee33855c300d9daa662d4ea1af27525d7557644c2eb8bd1ccceec376aa6b1dc26286658db5b63e7
6
+ metadata.gz: c9aceee8528a749c541f74eeb07610b520e4342ac8d11abda3222fd0d760e2ff2a16e1b08b7931f5d24cb0ae9fced020ef695798ffac6c4b4af15508e8927468
7
+ data.tar.gz: 19e40f6386373bdbba07b5a65faf5ffc99a5693e81b0eaeaf1abffc9b07cc29c2688ea658629f633eb36a3427f74857cab28216d18cfb208cca97a75ea10788e
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ === 5.55.0 (2022-04-01)
2
+
3
+ * Support :setup_regexp_function Database option in the sqlite adapter to allow the use of regexps when querying (jeremyevans)
4
+
5
+ * Add auto_restrict_eager_graph plugin for automatically disallow eager_graph with associations needing but lacking graph options (jeremyevans)
6
+
7
+ * Fix placeholder literalizer optimization for dataset aggregate methods on a model dataset (belousovAV) (#1847, #1848)
8
+
9
+ === 5.54.0 (2022-03-01)
10
+
11
+ * Add enum plugin for treating columns as enums in a model (jeremyevans) (#1839)
12
+
13
+ === 5.53.0 (2022-02-01)
14
+
15
+ * Make Dataset#_sql_comment private when using the Database sql_comments extension (jeremyevans)
16
+
17
+ * Fix prepared statements in the mysql2 adapter to reuse native prepared statements (jeremyevans) (#1832)
18
+
19
+ * Support H2 version 2+ in the jdbc/h2 adapter (jeremyevans) (#1817)
20
+
21
+ * Work around active_support breaking subclasses plugin on Ruby <3.1 (jeremyevans) (#1816)
22
+
23
+ * Fix error handling if trying to setup column_encryption plugin without keys (jeremyevans) (#1815)
24
+
1
25
  === 5.52.0 (2022-01-01)
2
26
 
3
27
  * Use Class#subclasses if available in the subclasses plugin, instead of a custom Model.subclasses accessor (jeremyevans)
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008-2021 Jeremy Evans
2
+ Copyright (c) 2008-2022 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
@@ -383,6 +383,9 @@ The following additional options are supported:
383
383
 
384
384
  :readonly :: open database in read-only mode
385
385
  :timeout :: the busy timeout to use in milliseconds (default: 5000).
386
+ :setup_regexp_function :: Whether to setup a REGEXP function in the underlying SQLite3::Database object. Doing so
387
+ allows you to use regexp support in dataset expressions. Note that this creates a new
388
+ Regexp object per call to the function, so it is not an efficient implementation.
386
389
 
387
390
  Note that SQLite memory databases are restricted to a single connection by
388
391
  default. This is because SQLite does not allow multiple connections to
data/doc/querying.rdoc CHANGED
@@ -357,7 +357,9 @@ For ranges, Sequel uses a pair of inequality statements:
357
357
  # SELECT * FROM artists WHERE ((id >= 1) AND (id < 5))
358
358
 
359
359
  Finally, for regexps, Sequel uses an SQL regular expression. Note that this
360
- is probably only supported on PostgreSQL and MySQL.
360
+ is only supported by default on PostgreSQL and MySQL. It can also be supported
361
+ on SQLite when using the sqlite adapter with the :setup_regexp_function
362
+ Database option.
361
363
 
362
364
  Artist.where(name: /JM$/)
363
365
  # SELECT * FROM artists WHERE (name ~ 'JM$')
@@ -0,0 +1,23 @@
1
+ = Improvements
2
+
3
+ * The jdbc/h2 subadapter now supports H2 version 2.0. It continues to
4
+ support H2 versions 1.3 and 1.4.
5
+
6
+ * The mysql2 adapter's prepared statement support now reuses existing
7
+ native prepared statements, instead of only binding variables on
8
+ newly prepared statements. This was the intended behavior
9
+ previously, and should result in increased performance in cases
10
+ where preparing a query takes significant time.
11
+
12
+ * The subclasses plugin now ignores an existing Class#subclasses
13
+ method if it is defined in Ruby. This fixes cases where usage of
14
+ ActiveSupport would break the subclasses plugin.
15
+
16
+ * Database#call_sproc in the jdbc adapter will now always close the
17
+ prepared call it creates. Before, if there was an exception raised
18
+ when setting the arguments for the prepared call, the prepared call
19
+ would not be closed.
20
+
21
+ * A more appropriate error is now issued if you try to use the
22
+ column_encryption plugin to encrypt a column without setting up an
23
+ encryption key.
@@ -0,0 +1,27 @@
1
+ = New Feature
2
+
3
+ * An enum plugin has been added. This plugin allows you to create
4
+ model-level enums, giving names to underlying values of a column.
5
+ For example:
6
+
7
+ Album.plugin :enum
8
+ Album.enum :status_id, good: 1, bad: 2
9
+
10
+ Adds Album#good! and Album#bad! for changing the status_id to 1 or
11
+ 2 respectively. It adds Album#good? and Album#bad? for checking
12
+ whether the status_id is 1 or 2 respectively. It overrides
13
+ Album#status_id to return :good or :bad instead of 1 or 2,
14
+ respectively, and overrides Album#status_id= to accept :good or
15
+ :bad instead of 1 or 2 respectively.
16
+
17
+ Additionally, it adds good and bad dataset methods for filtering
18
+ the model's dataset to records where status_id is 1 or 2
19
+ respectively. It also adds not_good and not_bad dataset methods
20
+ for filtering the model's dataset to records where status_id is not
21
+ 1 or not 2 respectively.
22
+
23
+ You can use :prefix and :suffix options when calling enum to
24
+ add a prefix or suffix to the method names created. You can
25
+ set the :override_accessors option to false to not override
26
+ the accessor methods for the column, and set the :dataset_methods
27
+ option to false to not add dataset methods.
@@ -0,0 +1,21 @@
1
+ = New Features
2
+
3
+ * An auto_restrict_eager_graph plugin has been added for automatically
4
+ disallowing the use of eager_graph with associations using blocks but
5
+ lacking graph_* options. This can prevent potentionally invalid usage,
6
+ as the restrictions added by the block are not used by eager_graph.
7
+
8
+ * The sqlite adapter now supports the :setup_regexp_function
9
+ Database option. This option will define a REGEXP function in the
10
+ database that will allow regexp support in queries, such as:
11
+
12
+ DB[:table].where(column: /(some|pattern)/)
13
+
14
+ Note that this creates a Ruby Regexp object per column value tested,
15
+ so it isn't the most optimal approach.
16
+
17
+ = Other Improvements
18
+
19
+ * Calling dataset aggregate methods such as #max on a model dataset now
20
+ works correctly. Previously, it could fail if called enough times to
21
+ optimize using a placeholder literalizer.
data/doc/sql.rdoc CHANGED
@@ -528,7 +528,7 @@ Inverting the LIKE operator works like other inversions:
528
528
 
529
529
  ~Sequel.like(:name, 'A%') # ("name" NOT LIKE 'A%' ESCAPE '\')
530
530
 
531
- Sequel also supports SQL regular expressions on MySQL and PostgreSQL. You can use these by passing a Ruby regular expression to +like+ or +ilike+, or by making the regular expression a hash value:
531
+ Sequel also supports SQL regular expressions on MySQL and PostgreSQL (and SQLite when using the sqlite adapter with the :setup_regexp_function Database option). You can use these by passing a Ruby regular expression to +like+ or +ilike+, or by making the regular expression a hash value:
532
532
 
533
533
  Sequel.like(:name, /^A/) # ("name" ~ '^A')
534
534
  ~Sequel.ilike(:name, /^A/) # ("name" !~* '^A')
@@ -24,6 +24,7 @@ module Sequel
24
24
 
25
25
  def freeze
26
26
  h2_version
27
+ version2?
27
28
  super
28
29
  end
29
30
 
@@ -140,13 +141,36 @@ module Sequel
140
141
  DATABASE_ERROR_REGEXPS
141
142
  end
142
143
 
143
- # Use IDENTITY() to get the last inserted id.
144
+ def execute_statement_insert(stmt, sql)
145
+ stmt.executeUpdate(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS)
146
+ end
147
+
148
+ def prepare_jdbc_statement(conn, sql, opts)
149
+ opts[:type] == :insert ? conn.prepareStatement(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS) : super
150
+ end
151
+
152
+ # Get the last inserted id using getGeneratedKeys, scope_identity, or identity.
144
153
  def last_insert_id(conn, opts=OPTS)
145
- statement(conn) do |stmt|
146
- sql = 'SELECT IDENTITY();'
147
- rs = log_connection_yield(sql, conn){stmt.executeQuery(sql)}
148
- rs.next
149
- rs.getLong(1)
154
+ if stmt = opts[:stmt]
155
+ rs = stmt.getGeneratedKeys
156
+ begin
157
+ if rs.next
158
+ begin
159
+ rs.getLong(1)
160
+ rescue
161
+ rs.getObject(1) rescue nil
162
+ end
163
+ end
164
+ ensure
165
+ rs.close
166
+ end
167
+ elsif !version2?
168
+ statement(conn) do |stmt|
169
+ sql = 'SELECT IDENTITY()'
170
+ rs = log_connection_yield(sql, conn){stmt.executeQuery(sql)}
171
+ rs.next
172
+ rs.getLong(1)
173
+ end
150
174
  end
151
175
  end
152
176
 
@@ -161,7 +185,12 @@ module Sequel
161
185
 
162
186
  # Use BIGINT IDENTITY for identity columns that use :Bignum type
163
187
  def type_literal_generic_bignum_symbol(column)
164
- column[:identity] ? 'BIGINT IDENTITY' : super
188
+ column[:identity] ? 'BIGINT AUTO_INCREMENT' : super
189
+ end
190
+
191
+ def version2?
192
+ return @version2 if defined?(@version2)
193
+ @version2 = h2_version.to_i >= 2
165
194
  end
166
195
  end
167
196
 
@@ -209,9 +238,21 @@ module Sequel
209
238
 
210
239
  # H2 expects hexadecimal strings for blob values
211
240
  def literal_blob_append(sql, v)
212
- sql << "'" << v.unpack("H*").first << "'"
241
+ if db.send(:version2?)
242
+ super
243
+ else
244
+ sql << "'" << v.unpack("H*").first << "'"
245
+ end
246
+ end
247
+
248
+ def literal_false
249
+ 'FALSE'
213
250
  end
214
251
 
252
+ def literal_true
253
+ 'TRUE'
254
+ end
255
+
215
256
  # H2 handles fractional seconds in timestamps, but not in times
216
257
  def literal_sqltime(v)
217
258
  v.strftime("'%H:%M:%S'")
@@ -223,8 +264,12 @@ module Sequel
223
264
  end
224
265
 
225
266
  def select_only_offset_sql(sql)
226
- sql << " LIMIT -1 OFFSET "
227
- literal_append(sql, @opts[:offset])
267
+ if db.send(:version2?)
268
+ super
269
+ else
270
+ sql << " LIMIT -1 OFFSET "
271
+ literal_append(sql, @opts[:offset])
272
+ end
228
273
  end
229
274
 
230
275
  # H2 supports quoted function names.
@@ -188,12 +188,12 @@ module Sequel
188
188
  args = opts[:args] || []
189
189
  sql = "{call #{name}(#{args.map{'?'}.join(',')})}"
190
190
  synchronize(opts[:server]) do |conn|
191
- cps = conn.prepareCall(sql)
191
+ begin
192
+ cps = conn.prepareCall(sql)
192
193
 
193
- i = 0
194
- args.each{|arg| set_ps_arg(cps, arg, i+=1)}
194
+ i = 0
195
+ args.each{|arg| set_ps_arg(cps, arg, i+=1)}
195
196
 
196
- begin
197
197
  if defined?(yield)
198
198
  yield log_connection_yield(sql, conn){cps.executeQuery}
199
199
  else
@@ -205,7 +205,7 @@ module Sequel
205
205
  rescue *DATABASE_ERROR_CLASSES => e
206
206
  raise_error(e)
207
207
  ensure
208
- cps.close
208
+ cps.close if cps
209
209
  end
210
210
  end
211
211
  end
@@ -86,20 +86,26 @@ module Sequel
86
86
  if NativePreparedStatements
87
87
  # Use a native mysql2 prepared statement to implement prepared statements.
88
88
  def execute_prepared_statement(ps_name, opts, &block)
89
- ps = prepared_statement(ps_name)
89
+ if ps_name.is_a?(Sequel::Dataset::ArgumentMapper)
90
+ ps = ps_name
91
+ ps_name = ps.prepared_statement_name
92
+ else
93
+ ps = prepared_statement(ps_name)
94
+ end
90
95
  sql = ps.prepared_sql
91
96
 
92
97
  synchronize(opts[:server]) do |conn|
93
98
  stmt, ps_sql = conn.prepared_statements[ps_name]
94
99
  unless ps_sql == sql
95
100
  stmt.close if stmt
96
- stmt = log_connection_yield(conn, "Preparing #{ps_name}: #{sql}"){conn.prepare(sql)}
101
+ stmt = log_connection_yield("Preparing #{ps_name}: #{sql}", conn){conn.prepare(sql)}
97
102
  conn.prepared_statements[ps_name] = [stmt, sql]
98
103
  end
99
104
 
100
- if ps.log_sql
101
- opts = Hash[opts]
102
- opts = opts[:log_sql] = " (#{sql})"
105
+ opts = Hash[opts]
106
+ opts[:sql] = "Executing #{ps_name || sql}"
107
+ if ps_name && ps.log_sql
108
+ opts[:log_sql] = " (#{sql})"
103
109
  end
104
110
 
105
111
  _execute(conn, stmt, opts, &block)
@@ -120,6 +126,7 @@ module Sequel
120
126
  case sql
121
127
  when ::Mysql2::Statement
122
128
  stmt = sql
129
+ sql = opts[:sql] || ''
123
130
  when Dataset
124
131
  sql = sql.sql
125
132
  close_stmt = true
@@ -98,6 +98,11 @@ module Sequel
98
98
  # The conversion procs to use for this database
99
99
  attr_reader :conversion_procs
100
100
 
101
+ def initialize(opts = OPTS)
102
+ super
103
+ @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
104
+ end
105
+
101
106
  # Connect to the database. Since SQLite is a file based database,
102
107
  # available options are limited:
103
108
  #
@@ -119,6 +124,12 @@ module Sequel
119
124
  end
120
125
 
121
126
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
+
128
+ if typecast_value_boolean(opts[:setup_regexp_function])
129
+ db.create_function("regexp", 2) do |func, regexp_str, string|
130
+ func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
+ end
132
+ end
122
133
 
123
134
  class << db
124
135
  attr_reader :prepared_statements
@@ -128,6 +139,12 @@ module Sequel
128
139
  db
129
140
  end
130
141
 
142
+ # Whether this Database instance is setup to allow regexp matching.
143
+ # True if the :setup_regexp_function option was passed when creating the Database.
144
+ def allow_regexp?
145
+ @allow_regexp
146
+ end
147
+
131
148
  # Disconnect given connections from the database.
132
149
  def disconnect_connection(c)
133
150
  c.prepared_statements.each_value{|v| v.first.close}
@@ -321,6 +338,28 @@ module Sequel
321
338
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
322
339
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
323
340
 
341
+ # Support regexp functions if using :setup_regexp_function Database option.
342
+ def complex_expression_sql_append(sql, op, args)
343
+ case op
344
+ when :~, :'!~', :'~*', :'!~*'
345
+ return super unless supports_regexp?
346
+
347
+ case_insensitive = [:'~*', :'!~*'].include?(op)
348
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
349
+ sql << '('
350
+ sql << 'LOWER(' if case_insensitive
351
+ literal_append(sql, args[0])
352
+ sql << ')' if case_insensitive
353
+ sql << ' REGEXP '
354
+ sql << 'LOWER(' if case_insensitive
355
+ literal_append(sql, args[1])
356
+ sql << ')' if case_insensitive
357
+ sql << ')'
358
+ else
359
+ super
360
+ end
361
+ end
362
+
324
363
  def fetch_rows(sql)
325
364
  execute(sql) do |result|
326
365
  cps = db.conversion_procs
@@ -344,6 +383,11 @@ module Sequel
344
383
  end
345
384
  end
346
385
  end
386
+
387
+ # Support regexp if using :setup_regexp_function Database option.
388
+ def supports_regexp?
389
+ db.allow_regexp?
390
+ end
347
391
 
348
392
  private
349
393
 
@@ -34,7 +34,7 @@ module Sequel
34
34
  def execute(sql, opts=OPTS, &block)
35
35
  if opts[:sproc]
36
36
  call_sproc(sql, opts, &block)
37
- elsif sql.is_a?(Symbol)
37
+ elsif sql.is_a?(Symbol) || sql.is_a?(Sequel::Dataset::ArgumentMapper)
38
38
  execute_prepared_statement(sql, opts, &block)
39
39
  else
40
40
  synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
@@ -894,9 +894,10 @@ module Sequel
894
894
  # Clone of this dataset usable in aggregate operations. Does
895
895
  # a from_self if dataset contains any parameters that would
896
896
  # affect normal aggregation, or just removes an existing
897
- # order if not.
897
+ # order if not. Also removes the row_proc, which isn't needed
898
+ # for aggregate calculations.
898
899
  def aggregate_dataset
899
- options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
900
+ (options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered).naked
900
901
  end
901
902
 
902
903
  # Append aliasing expression to SQL string.
@@ -108,7 +108,7 @@ module Sequel
108
108
 
109
109
  # Call the ANY function:
110
110
  #
111
- # array_op.all # ANY(array)
111
+ # array_op.any # ANY(array)
112
112
  #
113
113
  # Usually used like:
114
114
  #
@@ -170,6 +170,8 @@ module Sequel
170
170
  module DatasetSQLComments
171
171
  include Sequel::SQLComments
172
172
 
173
+ private
174
+
173
175
  # Include comments added via Database#with_comments in the output SQL.
174
176
  def _sql_comment
175
177
  specific_comment = super
@@ -0,0 +1,62 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The auto_restrict_eager_graph plugin will automatically disallow the use
6
+ # of eager_graph for associations that have associated blocks but no :graph_*
7
+ # association options. The reason for this is the block will have an effect
8
+ # during regular and eager loading, but not loading via eager_graph, and it
9
+ # is likely that whatever the block is doing should have an equivalent done
10
+ # when eager_graphing. Most likely, not including a :graph_* option was either
11
+ # an oversight (and one should be added), or use with eager_graph was never
12
+ # intended (and usage should be forbidden). Disallowing eager_graph in this
13
+ # case prevents likely unexpected behavior during eager_graph.
14
+ #
15
+ # As an example of this, consider the following code:
16
+ #
17
+ # Album.one_to_many :popular_tracks, class: :Track do |ds|
18
+ # ds = ds.where(popular: true)
19
+ # end
20
+ #
21
+ # Album.eager(:popular_tracks).all
22
+ # # SELECT * FROM albums
23
+ # # SELECT * FROM tracks WHERE ((popular IS TRUE) AND (album_id IN (...)))
24
+ #
25
+ # # Notice that no condition for tracks.popular is added.
26
+ # Album.eager_graph(:popular_tracks).all
27
+ # # SELECT ... FROM albums LEFT JOIN tracks ON (tracks.album_id = albums.id)
28
+ #
29
+ # With the auto_restrict_eager_graph plugin, the eager_graph call above will
30
+ # raise an error, alerting you to the fact that you either should not be
31
+ # using eager_graph with the association, or that you should be adding an
32
+ # appropriate :graph_* option, such as:
33
+ #
34
+ # Album.one_to_many :popular_tracks, class: :Track, graph_conditions: {popular: true} do |ds|
35
+ # ds = ds.where(popular: true)
36
+ # end
37
+ #
38
+ # Usage:
39
+ #
40
+ # # Automatically restrict eager_graph for associations if appropriate for all
41
+ # # model subclasses (called before loading subclasses)
42
+ # Sequel::Model.plugin :auto_restrict_eager_graph
43
+ #
44
+ # # Automatically restrict eager_graph for associations in Album class
45
+ # Album.plugin :auto_restrict_eager_graph
46
+ module AutoRestrictEagerGraph
47
+ module ClassMethods
48
+ # When defining an association, if a block is given for the association, but
49
+ # a :graph_* option is not used, disallow the use of eager_graph.
50
+ def associate(type, name, opts = OPTS, &block)
51
+ opts = super
52
+
53
+ if opts[:block] && !opts.has_key?(:allow_eager_graph) && !opts[:orig_opts].any?{|k,| /\Agraph_/ =~ k}
54
+ opts[:allow_eager_graph] = false
55
+ end
56
+
57
+ opts
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -356,7 +356,7 @@ module Sequel
356
356
 
357
357
  # Keys should be an array of arrays containing key_id, key string, auth_data, and padding.
358
358
  def initialize(keys)
359
- if keys.empty?
359
+ if !keys || keys.empty?
360
360
  raise Error, "Cannot initialize encryptor without encryption key"
361
361
  end
362
362
 
@@ -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
@@ -35,7 +35,7 @@ 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)
38
+ NEED_SUBCLASSES = !Object.respond_to?(:subclasses) || Object.method(:subclasses).source_location
39
39
  private_constant :NEED_SUBCLASSES
40
40
 
41
41
  # Initialize the subclasses instance variable for the model.
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 52
9
+ MINOR = 55
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.52.0
4
+ version: 5.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-01 00:00:00.000000000 Z
11
+ date: 2022-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -197,6 +197,9 @@ extra_rdoc_files:
197
197
  - doc/release_notes/5.50.0.txt
198
198
  - doc/release_notes/5.51.0.txt
199
199
  - doc/release_notes/5.52.0.txt
200
+ - doc/release_notes/5.53.0.txt
201
+ - doc/release_notes/5.54.0.txt
202
+ - doc/release_notes/5.55.0.txt
200
203
  - doc/release_notes/5.6.0.txt
201
204
  - doc/release_notes/5.7.0.txt
202
205
  - doc/release_notes/5.8.0.txt
@@ -277,6 +280,9 @@ files:
277
280
  - doc/release_notes/5.50.0.txt
278
281
  - doc/release_notes/5.51.0.txt
279
282
  - doc/release_notes/5.52.0.txt
283
+ - doc/release_notes/5.53.0.txt
284
+ - doc/release_notes/5.54.0.txt
285
+ - doc/release_notes/5.55.0.txt
280
286
  - doc/release_notes/5.6.0.txt
281
287
  - doc/release_notes/5.7.0.txt
282
288
  - doc/release_notes/5.8.0.txt
@@ -475,6 +481,7 @@ files:
475
481
  - lib/sequel/plugins/association_pks.rb
476
482
  - lib/sequel/plugins/association_proxies.rb
477
483
  - lib/sequel/plugins/async_thread_pool.rb
484
+ - lib/sequel/plugins/auto_restrict_eager_graph.rb
478
485
  - lib/sequel/plugins/auto_validations.rb
479
486
  - lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb
480
487
  - lib/sequel/plugins/before_after_save.rb
@@ -499,6 +506,7 @@ files:
499
506
  - lib/sequel/plugins/eager_each.rb
500
507
  - lib/sequel/plugins/eager_graph_eager.rb
501
508
  - lib/sequel/plugins/empty_failure_backtraces.rb
509
+ - lib/sequel/plugins/enum.rb
502
510
  - lib/sequel/plugins/error_splitter.rb
503
511
  - lib/sequel/plugins/finder.rb
504
512
  - lib/sequel/plugins/forbid_lazy_load.rb
@@ -592,7 +600,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
592
600
  - !ruby/object:Gem::Version
593
601
  version: '0'
594
602
  requirements: []
595
- rubygems_version: 3.3.3
603
+ rubygems_version: 3.3.7
596
604
  signing_key:
597
605
  specification_version: 4
598
606
  summary: The Database Toolkit for Ruby