dbee-active_record 2.0.3 → 2.1.2

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -12
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +3 -10
  5. data/CHANGELOG.md +24 -1
  6. data/README.md +1 -1
  7. data/dbee-active_record.gemspec +17 -8
  8. data/exe/.gitkeep +0 -0
  9. data/lib/dbee/providers/active_record_provider/expression_builder.rb +55 -24
  10. data/lib/dbee/providers/active_record_provider/maker.rb +37 -0
  11. data/lib/dbee/providers/active_record_provider/{expression_builder/constraint_maker.rb → makers/constraint.rb} +12 -12
  12. data/lib/dbee/providers/active_record_provider/{expression_builder/order_maker.rb → makers/order.rb} +9 -9
  13. data/lib/dbee/providers/active_record_provider/makers/select.rb +81 -0
  14. data/lib/dbee/providers/active_record_provider/makers/where.rb +111 -0
  15. data/lib/dbee/providers/active_record_provider/version.rb +1 -1
  16. data/spec/db_helper.rb +134 -14
  17. data/spec/dbee/providers/active_record_provider/expression_builder_spec.rb +117 -0
  18. data/spec/dbee/providers/active_record_provider/makers/where_spec.rb +260 -0
  19. data/spec/dbee/providers/active_record_provider_spec.rb +101 -3
  20. data/spec/fixtures/active_record_snapshots/one_table_empty_query.yaml +10 -0
  21. data/spec/fixtures/active_record_snapshots/one_table_query_with_filters.yaml +24 -16
  22. data/spec/fixtures/active_record_snapshots/two_table_query_with_aggregation.yaml +71 -0
  23. data/spec/fixtures/active_record_snapshots/two_table_query_with_pivoting.yaml +88 -0
  24. data/spec/fixtures/models.yaml +20 -0
  25. data/spec/spec_helper.rb +5 -1
  26. metadata +51 -19
  27. data/lib/dbee/providers/active_record_provider/expression_builder/select_maker.rb +0 -33
  28. data/lib/dbee/providers/active_record_provider/expression_builder/where_maker.rb +0 -56
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Dbee
11
+ module Providers
12
+ class ActiveRecordProvider
13
+ module Makers # :nodoc: all
14
+ # Derives Arel#where predicates.
15
+ class Where
16
+ include Singleton
17
+
18
+ def make(filter, arel_column)
19
+ # If the filter has a value of nil, then simply return an IS (NOT) NULL predicate
20
+ return make_is_null_predicate(arel_column, filter.class) if filter.value.nil?
21
+
22
+ values = Array(filter.value).flatten
23
+
24
+ # This logic helps ensure that if a null exists that it translates to an IS NULL
25
+ # predicate and does not get put into an in or not_in clause.
26
+ predicates =
27
+ if values.include?(nil)
28
+ [make_is_null_predicate(arel_column, filter.class)]
29
+ else
30
+ []
31
+ end
32
+
33
+ predicates += make_predicates(filter, arel_column, values - [nil])
34
+
35
+ # Chain all predicates together
36
+ predicates.inject(predicates.shift) do |memo, predicate|
37
+ memo.or(predicate)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ FILTER_EVALUATORS = {
44
+ Query::Filters::Contains => ->(node, val) { node.matches("%#{val}%") },
45
+ Query::Filters::Equals => ->(node, val) { node.eq(val) },
46
+ Query::Filters::GreaterThan => ->(node, val) { node.gt(val) },
47
+ Query::Filters::GreaterThanOrEqualTo => ->(node, val) { node.gteq(val) },
48
+ Query::Filters::LessThan => ->(node, val) { node.lt(val) },
49
+ Query::Filters::LessThanOrEqualTo => ->(node, val) { node.lteq(val) },
50
+ Query::Filters::NotContain => ->(node, val) { node.does_not_match("%#{val}%") },
51
+ Query::Filters::NotEquals => ->(node, val) { node.not_eq(val) },
52
+ Query::Filters::NotStartWith => ->(node, val) { node.does_not_match("#{val}%") },
53
+ Query::Filters::StartsWith => ->(node, val) { node.matches("#{val}%") }
54
+ }.freeze
55
+
56
+ NULL_PREDICATE_MAP = {
57
+ Query::Filters::Contains => Query::Filters::Equals,
58
+ Query::Filters::Equals => Query::Filters::Equals,
59
+ Query::Filters::GreaterThan => Query::Filters::Equals,
60
+ Query::Filters::GreaterThanOrEqualTo => Query::Filters::Equals,
61
+ Query::Filters::LessThan => Query::Filters::Equals,
62
+ Query::Filters::LessThanOrEqualTo => Query::Filters::Equals,
63
+ Query::Filters::NotContain => Query::Filters::NotEquals,
64
+ Query::Filters::NotEquals => Query::Filters::NotEquals,
65
+ Query::Filters::NotStartWith => Query::Filters::NotEquals,
66
+ Query::Filters::StartsWith => Query::Filters::Equals
67
+ }.freeze
68
+
69
+ private_constant :FILTER_EVALUATORS, :NULL_PREDICATE_MAP
70
+
71
+ def make_predicates(filter, arel_column, values)
72
+ if use_in?(filter, values)
73
+ [arel_column.in(values)]
74
+ elsif use_not_in?(filter, values)
75
+ [arel_column.not_in(values)]
76
+ else
77
+ make_or_predicates(filter, arel_column, values)
78
+ end
79
+ end
80
+
81
+ def use_in?(filter, values)
82
+ filter.is_a?(Query::Filters::Equals) && values.length > 1
83
+ end
84
+
85
+ def use_not_in?(filter, values)
86
+ filter.is_a?(Query::Filters::NotEquals) && values.length > 1
87
+ end
88
+
89
+ def make_or_predicates(filter, arel_column, values)
90
+ values.map do |value|
91
+ make_predicate(arel_column, filter.class, value)
92
+ end
93
+ end
94
+
95
+ def make_predicate(arel_column, filter_class, value)
96
+ method = FILTER_EVALUATORS[filter_class]
97
+
98
+ raise ArgumentError, "cannot compile filter: #{filter}" unless method
99
+
100
+ method.call(arel_column, value)
101
+ end
102
+
103
+ def make_is_null_predicate(arel_column, requested_filter_class)
104
+ actual_filter_class = NULL_PREDICATE_MAP[requested_filter_class]
105
+ make_predicate(arel_column, actual_filter_class, nil)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -10,7 +10,7 @@
10
10
  module Dbee
11
11
  module Providers
12
12
  class ActiveRecordProvider
13
- VERSION = '2.0.3'
13
+ VERSION = '2.1.2'
14
14
  end
15
15
  end
16
16
  end
@@ -10,6 +10,27 @@
10
10
  # Enable logging using something like:
11
11
  # ActiveRecord::Base.logger = Logger.new(STDERR)
12
12
 
13
+ class Field < ActiveRecord::Base
14
+ has_many :patient_field_values
15
+ end
16
+
17
+ class Patient < ActiveRecord::Base
18
+ has_many :patient_field_values
19
+ has_many :patient_payments
20
+
21
+ accepts_nested_attributes_for :patient_field_values
22
+ accepts_nested_attributes_for :patient_payments
23
+ end
24
+
25
+ class PatientFieldValue < ActiveRecord::Base
26
+ belongs_to :patient
27
+ belongs_to :field
28
+ end
29
+
30
+ class PatientPayment < ActiveRecord::Base
31
+ belongs_to :patient
32
+ end
33
+
13
34
  def connect_to_db(name)
14
35
  config = yaml_file_read('spec', 'config', 'database.yaml')[name.to_s]
15
36
  ActiveRecord::Base.establish_connection(config)
@@ -17,39 +38,40 @@ end
17
38
 
18
39
  def load_schema
19
40
  ActiveRecord::Schema.define do
41
+ # Movie Theater Schema
20
42
  create_table :theaters do |t|
21
- t.column :name, :string
43
+ t.column :name, :string
22
44
  t.column :partition, :string
23
- t.column :active, :boolean
45
+ t.column :active, :boolean
24
46
  t.column :inspected, :boolean
25
47
  t.timestamps
26
48
  end
27
49
 
28
50
  create_table :members do |t|
29
- t.column :tid, :integer
51
+ t.column :tid, :integer
30
52
  t.column :account_number, :string
31
- t.column :partition, :string
53
+ t.column :partition, :string
32
54
  t.timestamps
33
55
  end
34
56
 
35
57
  create_table :demographics do |t|
36
58
  t.column :member_id, :integer
37
- t.column :name, :string
59
+ t.column :name, :string
38
60
  t.timestamps
39
61
  end
40
62
 
41
63
  create_table :phone_numbers do |t|
42
64
  t.column :demographic_id, :integer
43
- t.column :phone_type, :string
44
- t.column :phone_number, :string
65
+ t.column :phone_type, :string
66
+ t.column :phone_number, :string
45
67
  t.timestamps
46
68
  end
47
69
 
48
70
  create_table :movies do |t|
49
71
  t.column :member_id, :integer
50
- t.column :name, :string
51
- t.column :genre, :string
52
- t.column :favorite, :boolean, default: false, null: false
72
+ t.column :name, :string
73
+ t.column :genre, :string
74
+ t.column :favorite, :boolean, default: false, null: false
53
75
  t.timestamps
54
76
  end
55
77
 
@@ -60,10 +82,10 @@ def load_schema
60
82
 
61
83
  create_table :animals do |t|
62
84
  t.column :owner_id, :integer
63
- t.column :toy_id, :integer
64
- t.column :type, :string
65
- t.column :name, :string
66
- t.column :deleted, :boolean
85
+ t.column :toy_id, :integer
86
+ t.column :type, :string
87
+ t.column :name, :string
88
+ t.column :deleted, :boolean
67
89
  t.timestamps
68
90
  end
69
91
 
@@ -76,5 +98,103 @@ def load_schema
76
98
  t.column :laser, :boolean
77
99
  t.timestamps
78
100
  end
101
+
102
+ # Patient Schema
103
+ create_table :fields do |t|
104
+ t.column :section, :string
105
+ t.column :key, :string
106
+ t.timestamps
107
+ end
108
+
109
+ add_index :fields, %i[section key], unique: true
110
+
111
+ create_table :patients do |t|
112
+ t.column :first, :string
113
+ t.column :middle, :string
114
+ t.column :last, :string
115
+ t.timestamps
116
+ end
117
+
118
+ create_table :patient_field_values do |t|
119
+ t.column :patient_id, :integer, foreign_key: true
120
+ t.column :field_id, :integer, foreign_key: true
121
+ t.column :value, :string
122
+ t.timestamps
123
+ end
124
+
125
+ add_index :patient_field_values, %i[patient_id field_id], unique: true
126
+
127
+ create_table :patient_payments do |t|
128
+ t.column :patient_id, :integer, foreign_key: true
129
+ t.column :amount, :decimal
130
+ t.timestamps
131
+ end
79
132
  end
80
133
  end
134
+
135
+ def load_data
136
+ demo_dob_field = Field.create!(section: 'demographics', key: 'dob')
137
+ demo_drivers_license_field = Field.create!(section: 'demographics', key: 'drivers_license')
138
+ demo_notes_field = Field.create!(section: 'demographics', key: 'notes')
139
+
140
+ contact_phone_number_field = Field.create!(section: 'contact', key: 'phone_number')
141
+ contact_notes_field = Field.create!(section: 'contact', key: 'notes')
142
+
143
+ Patient.create!(
144
+ first: 'Bozo',
145
+ middle: 'The',
146
+ last: 'Clown',
147
+ patient_field_values_attributes: [
148
+ {
149
+ field: demo_dob_field,
150
+ value: '1904-04-04'
151
+ },
152
+ {
153
+ field: demo_notes_field,
154
+ value: 'The patient is funny!'
155
+ },
156
+ {
157
+ field: demo_drivers_license_field,
158
+ value: '82-54-hut-hut-hike!'
159
+ },
160
+ {
161
+ field: contact_phone_number_field,
162
+ value: '555-555-5555'
163
+ },
164
+ {
165
+ field: contact_notes_field,
166
+ value: 'Do not call this patient at night!'
167
+ }
168
+ ],
169
+ patient_payments_attributes: [
170
+ { amount: 5 },
171
+ { amount: 10 },
172
+ { amount: 15 }
173
+ ]
174
+ )
175
+
176
+ Patient.create!(
177
+ first: 'Frank',
178
+ last: 'Rizzo',
179
+ patient_payments_attributes: [
180
+ { amount: 50 },
181
+ { amount: 150 }
182
+ ]
183
+ )
184
+
185
+ Patient.create!(
186
+ first: 'Bugs',
187
+ middle: 'The',
188
+ last: 'Bunny',
189
+ patient_field_values_attributes: [
190
+ {
191
+ field: demo_dob_field,
192
+ value: '2040-01-01'
193
+ },
194
+ {
195
+ field: contact_notes_field,
196
+ value: 'Call anytime!!'
197
+ }
198
+ ]
199
+ )
200
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'db_helper'
12
+
13
+ describe Dbee::Providers::ActiveRecordProvider::ExpressionBuilder do
14
+ let(:model) { Dbee::Model.make(models['Patients']) }
15
+ let(:alias_maker) { Dbee::Providers::ActiveRecordProvider::SafeAliasMaker.new }
16
+
17
+ let(:id_and_average_query) do
18
+ Dbee::Query.make(
19
+ fields: [
20
+ {
21
+ key_path: :id,
22
+ display: 'ID #'
23
+ },
24
+ {
25
+ key_path: 'patient_payments.amount',
26
+ display: 'Ave Payment',
27
+ aggregator: :ave
28
+ }
29
+ ],
30
+ sorters: [
31
+ {
32
+ key_path: :id
33
+ }
34
+ ],
35
+ filters: [
36
+ {
37
+ key_path: :id,
38
+ value: 123
39
+ }
40
+ ]
41
+ )
42
+ end
43
+
44
+ let(:first_and_count_query) do
45
+ Dbee::Query.make(
46
+ fields: [
47
+ {
48
+ key_path: :first,
49
+ display: 'First Name'
50
+ },
51
+ {
52
+ key_path: 'patient_payments.amount',
53
+ display: 'Number of Payments',
54
+ aggregator: :count
55
+ }
56
+ ]
57
+ )
58
+ end
59
+
60
+ subject { described_class.new(model, alias_maker, alias_maker) }
61
+
62
+ before(:all) do
63
+ connect_to_db(:sqlite)
64
+ end
65
+
66
+ describe '#clear' do
67
+ it 'provides fluent interface (returns self)' do
68
+ expect(subject.clear).to eq(subject)
69
+ end
70
+
71
+ it 'resets selecting, grouping, sorting, and filtering' do
72
+ subject.add(id_and_average_query)
73
+
74
+ sql = subject.to_sql
75
+
76
+ expect(sql).not_to include('*')
77
+ expect(sql).to include('GROUP')
78
+ expect(sql).to include('WHERE')
79
+ expect(sql).to include('ORDER')
80
+
81
+ sql = subject.clear.to_sql
82
+
83
+ expect(sql).to include('*')
84
+ expect(sql).not_to include('GROUP')
85
+ expect(sql).not_to include('WHERE')
86
+ expect(sql).not_to include('ORDER')
87
+ end
88
+ end
89
+
90
+ describe '#to_sql' do
91
+ specify 'when called with no fields, then called with fields removes star select' do
92
+ expect(subject.to_sql).to include('*')
93
+
94
+ subject.add(id_and_average_query)
95
+
96
+ expect(subject.to_sql).not_to include('*')
97
+ end
98
+
99
+ context 'with aggregation' do
100
+ it 'generates the same sql when called multiple times' do
101
+ subject.add(id_and_average_query)
102
+
103
+ first_sql = subject.to_sql
104
+ second_sql = subject.to_sql
105
+
106
+ expect(first_sql).to eq(second_sql)
107
+
108
+ subject.add(first_and_count_query)
109
+
110
+ first_sql = subject.to_sql
111
+ second_sql = subject.to_sql
112
+
113
+ expect(first_sql).to eq(second_sql)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'db_helper'
12
+
13
+ # rubocop:disable Layout/LineLength
14
+ # Disable the line length cop beause there are some lengthy 'expected' SQL strings in spec which
15
+ # cannot be shortened.
16
+ describe Dbee::Providers::ActiveRecordProvider::Makers::Where do
17
+ before(:all) { connect_to_db(:sqlite) }
18
+
19
+ let(:subject) { described_class.instance }
20
+ let(:column) { Arel::Table.new(:test)[:foo] }
21
+
22
+ describe 'equals' do
23
+ specify 'a string value' do
24
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: 'bar')
25
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" = 'bar')
26
+ end
27
+
28
+ specify 'a null value' do
29
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: nil)
30
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
31
+ end
32
+
33
+ specify 'a set with a null value' do
34
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: ['a', nil, 'c'])
35
+ expected = %q[("test"."foo" IS NULL OR "test"."foo" IN ('a', 'c'))]
36
+ expect(subject.make(filter, column).to_sql).to eq expected
37
+ end
38
+
39
+ specify 'a set without a null value' do
40
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: %w[a b c])
41
+ expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" IN ('a', 'b', 'c')]
42
+ end
43
+ end
44
+
45
+ describe 'not equals' do
46
+ specify 'a string value' do
47
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: 'bar')
48
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" != 'bar')
49
+ end
50
+
51
+ specify 'a null value' do
52
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: nil)
53
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
54
+ end
55
+
56
+ specify 'a set with a null value' do
57
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: ['a', nil, 'c'])
58
+ expected = %q[("test"."foo" IS NOT NULL OR "test"."foo" NOT IN ('a', 'c'))]
59
+ expect(subject.make(filter, column).to_sql).to eq expected
60
+ end
61
+
62
+ specify 'a set without a null value' do
63
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: %w[a b c])
64
+ expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" NOT IN ('a', 'b', 'c')]
65
+ end
66
+ end
67
+
68
+ describe 'contains' do
69
+ specify 'a string value' do
70
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: 'bar')
71
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE '%bar%')
72
+ end
73
+
74
+ specify 'a null value' do
75
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: nil)
76
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
77
+ end
78
+
79
+ specify 'a set with a null value' do
80
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: ['a', nil, 'c'])
81
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE '%a%') OR "test"."foo" LIKE '%c%')]
82
+ expect(subject.make(filter, column).to_sql).to eq expected
83
+ end
84
+
85
+ specify 'a set without a null value' do
86
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: %w[a b c])
87
+ expected = %q[(("test"."foo" LIKE '%a%' OR "test"."foo" LIKE '%b%') OR "test"."foo" LIKE '%c%')]
88
+ expect(subject.make(filter, column).to_sql).to eq expected
89
+ end
90
+ end
91
+
92
+ describe 'not contains' do
93
+ specify 'a string value' do
94
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: 'bar')
95
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE '%bar%')
96
+ end
97
+
98
+ specify 'a null value' do
99
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: nil)
100
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
101
+ end
102
+
103
+ specify 'a set with a null value' do
104
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: ['a', nil, 'c'])
105
+ expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE '%a%') OR "test"."foo" NOT LIKE '%c%')]
106
+ expect(subject.make(filter, column).to_sql).to eq expected
107
+ end
108
+
109
+ specify 'a set without a null value' do
110
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: %w[a b c])
111
+ expected = %q[(("test"."foo" NOT LIKE '%a%' OR "test"."foo" NOT LIKE '%b%') OR "test"."foo" NOT LIKE '%c%')]
112
+ expect(subject.make(filter, column).to_sql).to eq expected
113
+ end
114
+ end
115
+
116
+ describe 'starts with' do
117
+ specify 'a string value' do
118
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: 'bar')
119
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE 'bar%')
120
+ end
121
+
122
+ specify 'a null value' do
123
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: nil)
124
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
125
+ end
126
+
127
+ specify 'a set with a null value' do
128
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: ['a', nil, 'c'])
129
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE 'a%') OR "test"."foo" LIKE 'c%')]
130
+ expect(subject.make(filter, column).to_sql).to eq expected
131
+ end
132
+
133
+ specify 'a set without a null value' do
134
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: %w[a b c])
135
+ expected = %q[(("test"."foo" LIKE 'a%' OR "test"."foo" LIKE 'b%') OR "test"."foo" LIKE 'c%')]
136
+ expect(subject.make(filter, column).to_sql).to eq expected
137
+ end
138
+ end
139
+
140
+ describe 'not start with' do
141
+ specify 'a string value' do
142
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: 'bar')
143
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE 'bar%')
144
+ end
145
+
146
+ specify 'a null value' do
147
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: nil)
148
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
149
+ end
150
+
151
+ specify 'a set with a null value' do
152
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: ['a', nil, 'c'])
153
+ expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE 'a%') OR "test"."foo" NOT LIKE 'c%')]
154
+ expect(subject.make(filter, column).to_sql).to eq expected
155
+ end
156
+
157
+ specify 'a set without a null value' do
158
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: %w[a b c])
159
+ expected = %q[(("test"."foo" NOT LIKE 'a%' OR "test"."foo" NOT LIKE 'b%') OR "test"."foo" NOT LIKE 'c%')]
160
+ expect(subject.make(filter, column).to_sql).to eq expected
161
+ end
162
+ end
163
+
164
+ describe 'greater than' do
165
+ specify 'a string value' do
166
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: 'bar')
167
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" > 'bar')
168
+ end
169
+
170
+ specify 'a null value' do
171
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: nil)
172
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
173
+ end
174
+
175
+ specify 'a set with a null value' do
176
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: ['a', nil, 'c'])
177
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" > 'a') OR "test"."foo" > 'c')]
178
+ expect(subject.make(filter, column).to_sql).to eq expected
179
+ end
180
+
181
+ specify 'a set without a null value' do
182
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: %w[a b c])
183
+ expected = %q[(("test"."foo" > 'a' OR "test"."foo" > 'b') OR "test"."foo" > 'c')]
184
+ expect(subject.make(filter, column).to_sql).to eq expected
185
+ end
186
+ end
187
+
188
+ describe 'greater than or equal to' do
189
+ specify 'a string value' do
190
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: 'bar')
191
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" >= 'bar')
192
+ end
193
+
194
+ specify 'a null value' do
195
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: nil)
196
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
197
+ end
198
+
199
+ specify 'a set with a null value' do
200
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
201
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" >= 'a') OR "test"."foo" >= 'c')]
202
+ expect(subject.make(filter, column).to_sql).to eq expected
203
+ end
204
+
205
+ specify 'a set without a null value' do
206
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
207
+ expected = %q[(("test"."foo" >= 'a' OR "test"."foo" >= 'b') OR "test"."foo" >= 'c')]
208
+ expect(subject.make(filter, column).to_sql).to eq expected
209
+ end
210
+ end
211
+
212
+ describe 'less than' do
213
+ specify 'a string value' do
214
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: 'bar')
215
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" < 'bar')
216
+ end
217
+
218
+ specify 'a null value' do
219
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: nil)
220
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
221
+ end
222
+
223
+ specify 'a set with a null value' do
224
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: ['a', nil, 'c'])
225
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" < 'a') OR "test"."foo" < 'c')]
226
+ expect(subject.make(filter, column).to_sql).to eq expected
227
+ end
228
+
229
+ specify 'a set without a null value' do
230
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: %w[a b c])
231
+ expected = %q[(("test"."foo" < 'a' OR "test"."foo" < 'b') OR "test"."foo" < 'c')]
232
+ expect(subject.make(filter, column).to_sql).to eq expected
233
+ end
234
+ end
235
+
236
+ describe 'less than or equal to' do
237
+ specify 'a string value' do
238
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: 'bar')
239
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" <= 'bar')
240
+ end
241
+
242
+ specify 'a null value' do
243
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: nil)
244
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
245
+ end
246
+
247
+ specify 'a set with a null value' do
248
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
249
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" <= 'a') OR "test"."foo" <= 'c')]
250
+ expect(subject.make(filter, column).to_sql).to eq expected
251
+ end
252
+
253
+ specify 'a set without a null value' do
254
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
255
+ expected = %q[(("test"."foo" <= 'a' OR "test"."foo" <= 'b') OR "test"."foo" <= 'c')]
256
+ expect(subject.make(filter, column).to_sql).to eq expected
257
+ end
258
+ end
259
+ end
260
+ # rubocop:enable Layout/LineLength