Skip to content

Commit 2acc579

Browse files
author
Ivan Tyurin
committed
Add dataset filter methods
1 parent 695727b commit 2acc579

3 files changed

Lines changed: 158 additions & 41 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,34 @@ item.created? # => true
106106
item.selected? # => false
107107
```
108108

109+
### Filter methods
110+
111+
Plugin can define dataset methods for all enum values:
112+
113+
```ruby
114+
Item.plugin :enum_values, filter_methods: true # default is `false`
115+
116+
Item.dataset.one # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"type\" = 'one')">
117+
Item.dataset.another # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"type\" = 'another')">
118+
119+
Item.dataset.created # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"status\" = 'created')">
120+
Item.dataset.selected # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"status\" = 'selected')">
121+
```
122+
123+
Or just for specific fields:
124+
125+
```ruby
126+
Item.plugin :enum_values, filter_methods: %i[status]
127+
# or just `:status` for single value
128+
129+
item = Item.new(type: 'one', status: 'created')
130+
131+
Item.dataset.one # => NoMethodError
132+
133+
Item.dataset.created # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"status\" = 'created')">
134+
Item.dataset.selected # => #<Sequel::Dataset: "SELECT * FROM \"items\" WHERE (\"status\" = 'selected')">
135+
```
136+
109137
## Development
110138

111139
After checking out the repo, run `bundle install` to install dependencies.

lib/sequel/plugins/enum_values.rb

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ def self.apply(model, _options = {})
2323
## @option options [Boolean, Symbol, Array<Symbol>]
2424
## predicate_methods (false)
2525
## enum fields for which predicate methods will be defined
26+
## @option options [Boolean, Symbol, Array<Symbol>]
27+
## filter_methods (false)
28+
## enum fields for which dataset filter methods will be defined
2629
## @example Disable caching
2730
## Item.plugin :enum_values, caching: false
2831
## @example Define predicate methods for all enum fields
@@ -31,18 +34,17 @@ def self.apply(model, _options = {})
3134
## Item.plugin :enum_values, predicate_methods: %[status type]
3235
## @example Define predicate methods for specific enum field
3336
## Item.plugin :enum_values, predicate_methods: :status
37+
## @example Define filter methods for all enum fields
38+
## Item.plugin :enum_values, filter_methods: true
39+
## @example Define filter methods for specific enum field
40+
## Item.plugin :enum_values, filter_methods: :status
41+
## @example Define filter methods for specific enum fields
42+
## Item.plugin :enum_values, filter_methods: %[status type]
3443
def self.configure(model, options = {})
3544
model.instance_exec do
36-
@enum_values_caching = options.fetch(:caching, @enum_values_caching)
37-
38-
predicate_methods = options.fetch(:predicate_methods, false)
45+
@enum_values_options = options
3946

40-
transform_predicate_methods_to_enum_fields(predicate_methods)
41-
.each do |field|
42-
all_enum_fields[field][:enum_values].each do |enum_value|
43-
define_predicate_method field, enum_value
44-
end
45-
end
47+
process_enum_values_options
4648
end
4749
end
4850

@@ -68,20 +70,30 @@ def enum_values(field)
6870

6971
private
7072

71-
def transform_predicate_methods_to_enum_fields(predicate_methods)
72-
case predicate_methods
73-
when TrueClass
74-
all_enum_fields.keys
75-
when FalseClass
76-
[]
77-
else
78-
Array(predicate_methods)
73+
def process_enum_values_options
74+
@enum_values_caching =
75+
@enum_values_options.fetch(:caching, @enum_values_caching)
76+
77+
process_enum_values_option(:filter_methods) do |field, value|
78+
dataset.methods.include?(value.to_sym) &&
79+
warn("WARNING: Redefining method #{value}")
80+
81+
dataset_module { where value, field => value }
82+
end
83+
84+
process_enum_values_option(:predicate_methods) do |field, value|
85+
define_method("#{value}?", -> { public_send(field) == value })
7986
end
8087
end
8188

82-
def define_predicate_method(field, enum_value)
83-
define_method "#{enum_value}?" do
84-
public_send(field) == enum_value
89+
def process_enum_values_option(key)
90+
return unless (option = @enum_values_options[key])
91+
92+
enum_fields = all_enum_fields
93+
enum_fields = enum_fields.slice(*option) unless option == true
94+
95+
enum_fields.each do |field, field_schema|
96+
field_schema[:enum_values].each { |value| yield field, value }
8597
end
8698
end
8799

spec/sequel/plugins/enum_values_spec.rb

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal:true
22

33
describe Sequel::Plugins::EnumValues do
4-
type_enum_values = %w[first second third]
4+
type_enum_values = %w[one another yet_another]
55
status_enum_values = %w[created selected canceled]
66
item_enum_values = type_enum_values + status_enum_values
77

@@ -71,6 +71,10 @@
7171
end
7272

7373
describe '.configure' do
74+
let(:enum_values_options) { {} }
75+
76+
before { item_class.plugin :enum_values, **enum_values_options }
77+
7478
describe 'caching option' do
7579
shared_examples 'works correctly' do
7680
before do
@@ -107,10 +111,7 @@
107111
5.times { item_class.enum_values(:type) }
108112
end
109113

110-
before do
111-
item_class.plugin :enum_values, caching: true
112-
end
113-
114+
let(:enum_values_options) { { caching: true } }
114115
let(:all_enum_fields_received_times) { 1 }
115116
let(:schema_received_times) { 1 }
116117

@@ -122,10 +123,7 @@
122123
5.times { item_class.enum_values(:type) }
123124
end
124125

125-
before do
126-
item_class.plugin :enum_values, caching: false
127-
end
128-
126+
let(:enum_values_options) { { caching: false } }
129127
let(:all_enum_fields_received_times) { 5 }
130128
let(:schema_received_times) { 5 }
131129

@@ -180,29 +178,23 @@
180178
end
181179

182180
context 'with true value' do
183-
before do
184-
item_class.plugin :enum_values, predicate_methods: true
185-
end
181+
let(:enum_values_options) { { predicate_methods: true } }
186182

187183
include_examples 'for all enum predicate methods',
188184
item_enum_predicate_methods,
189185
-> { expect(subject).to be false }
190186
end
191187

192188
context 'with false value' do
193-
before do
194-
item_class.plugin :enum_values, predicate_methods: false
195-
end
189+
let(:enum_values_options) { { predicate_methods: false } }
196190

197191
include_examples 'for all enum predicate methods',
198192
item_enum_predicate_methods,
199193
-> { expect { subject }.to raise_error(NoMethodError) }
200194
end
201195

202196
context 'with Array value' do
203-
before do
204-
item_class.plugin :enum_values, predicate_methods: %i[status]
205-
end
197+
let(:enum_values_options) { { predicate_methods: %i[status] } }
206198

207199
include_examples 'for all enum predicate methods',
208200
status_enum_predicate_methods,
@@ -214,9 +206,7 @@
214206
end
215207

216208
context 'with Symbol value' do
217-
before do
218-
item_class.plugin :enum_values, predicate_methods: :status
219-
end
209+
let(:enum_values_options) { { predicate_methods: :status } }
220210

221211
include_examples 'for all enum predicate methods',
222212
status_enum_predicate_methods,
@@ -227,6 +217,93 @@
227217
-> { expect { subject }.to raise_error(NoMethodError) }
228218
end
229219
end
220+
221+
shared_context('with filter_methods environment') do
222+
subject(:enum_filter) { item_class.dataset.public_send(enum_value) }
223+
224+
let(:filter_sql) do
225+
"SELECT * FROM \"items\" WHERE (\"#{field}\" = '#{enum_value}')"
226+
end
227+
end
228+
229+
shared_examples('returns correct sql') do |field, field_value|
230+
subject { enum_filter.sql }
231+
232+
describe field_value do
233+
let(:field) { field }
234+
let(:enum_value) { field_value }
235+
236+
it { is_expected.to eq filter_sql }
237+
end
238+
end
239+
240+
shared_examples('raises NoMethodError') do |field_value|
241+
describe field_value do
242+
let(:enum_value) { field_value }
243+
244+
it { expect { enum_filter }.to raise_error(NoMethodError) }
245+
end
246+
end
247+
248+
describe 'filter_methods default option' do
249+
include_context('with filter_methods environment')
250+
251+
item_enum_values.each do |filter_method_name|
252+
include_examples('raises NoMethodError', filter_method_name)
253+
end
254+
end
255+
256+
describe 'filter_methods true option' do
257+
let(:enum_values_options) { { filter_methods: true } }
258+
259+
include_context('with filter_methods environment')
260+
261+
type_enum_values.each do |filter_method_name|
262+
include_examples('returns correct sql', :type, filter_method_name)
263+
end
264+
265+
status_enum_values.each do |filter_method_name|
266+
include_examples('returns correct sql', :status, filter_method_name)
267+
end
268+
end
269+
270+
describe 'filter_methods false option' do
271+
let(:enum_values_options) { { filter_methods: false } }
272+
273+
include_context('with filter_methods environment')
274+
275+
item_enum_values.each do |filter_method_name|
276+
include_examples('raises NoMethodError', filter_method_name)
277+
end
278+
end
279+
280+
describe 'filter_methods Array option' do
281+
let(:enum_values_options) { { filter_methods: %i[status] } }
282+
283+
include_context('with filter_methods environment')
284+
285+
status_enum_values.each do |filter_method_name|
286+
include_examples('returns correct sql', :status, filter_method_name)
287+
end
288+
289+
type_enum_values.each do |filter_method_name|
290+
include_examples('raises NoMethodError', filter_method_name)
291+
end
292+
end
293+
294+
describe 'filter_methods Symbol option' do
295+
let(:enum_values_options) { { filter_methods: :status } }
296+
297+
include_context('with filter_methods environment')
298+
299+
status_enum_values.each do |filter_method_name|
300+
include_examples('returns correct sql', :status, filter_method_name)
301+
end
302+
303+
type_enum_values.each do |filter_method_name|
304+
include_examples('raises NoMethodError', filter_method_name)
305+
end
306+
end
230307
end
231308
end
232309
end

0 commit comments

Comments
 (0)