torque-postgresql 1.1.1 → 2.0.1

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 (72) hide show
  1. checksums.yaml +5 -5
  2. data/Rakefile +5 -2
  3. data/lib/torque/postgresql.rb +0 -2
  4. data/lib/torque/postgresql/adapter.rb +0 -1
  5. data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
  6. data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
  7. data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
  8. data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
  9. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
  10. data/lib/torque/postgresql/associations.rb +0 -3
  11. data/lib/torque/postgresql/associations/association_scope.rb +18 -61
  12. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +2 -1
  13. data/lib/torque/postgresql/associations/preloader.rb +0 -24
  14. data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
  15. data/lib/torque/postgresql/auxiliary_statement.rb +12 -17
  16. data/lib/torque/postgresql/coder.rb +1 -2
  17. data/lib/torque/postgresql/config.rb +0 -4
  18. data/lib/torque/postgresql/inheritance.rb +13 -17
  19. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  20. data/lib/torque/postgresql/relation.rb +11 -16
  21. data/lib/torque/postgresql/relation/auxiliary_statement.rb +9 -15
  22. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  23. data/lib/torque/postgresql/schema_cache.rb +19 -11
  24. data/lib/torque/postgresql/version.rb +1 -1
  25. data/spec/en.yml +19 -0
  26. data/spec/factories/authors.rb +6 -0
  27. data/spec/factories/comments.rb +13 -0
  28. data/spec/factories/posts.rb +6 -0
  29. data/spec/factories/tags.rb +5 -0
  30. data/spec/factories/texts.rb +5 -0
  31. data/spec/factories/users.rb +6 -0
  32. data/spec/factories/videos.rb +5 -0
  33. data/spec/mocks/cache_query.rb +16 -0
  34. data/spec/mocks/create_table.rb +35 -0
  35. data/spec/models/activity.rb +3 -0
  36. data/spec/models/activity_book.rb +4 -0
  37. data/spec/models/activity_post.rb +7 -0
  38. data/spec/models/activity_post/sample.rb +4 -0
  39. data/spec/models/author.rb +4 -0
  40. data/spec/models/author_journalist.rb +4 -0
  41. data/spec/models/comment.rb +3 -0
  42. data/spec/models/course.rb +2 -0
  43. data/spec/models/geometry.rb +2 -0
  44. data/spec/models/guest_comment.rb +4 -0
  45. data/spec/models/post.rb +6 -0
  46. data/spec/models/tag.rb +2 -0
  47. data/spec/models/text.rb +2 -0
  48. data/spec/models/time_keeper.rb +2 -0
  49. data/spec/models/user.rb +8 -0
  50. data/spec/models/video.rb +2 -0
  51. data/spec/schema.rb +141 -0
  52. data/spec/spec_helper.rb +59 -0
  53. data/spec/tests/arel_spec.rb +72 -0
  54. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  55. data/spec/tests/belongs_to_many_spec.rb +240 -0
  56. data/spec/tests/coder_spec.rb +367 -0
  57. data/spec/tests/collector_spec.rb +59 -0
  58. data/spec/tests/distinct_on_spec.rb +65 -0
  59. data/spec/tests/enum_set_spec.rb +306 -0
  60. data/spec/tests/enum_spec.rb +628 -0
  61. data/spec/tests/geometric_builder_spec.rb +221 -0
  62. data/spec/tests/has_many_spec.rb +390 -0
  63. data/spec/tests/interval_spec.rb +167 -0
  64. data/spec/tests/lazy_spec.rb +24 -0
  65. data/spec/tests/period_spec.rb +954 -0
  66. data/spec/tests/quoting_spec.rb +24 -0
  67. data/spec/tests/range_spec.rb +36 -0
  68. data/spec/tests/relation_spec.rb +57 -0
  69. data/spec/tests/table_inheritance_spec.rb +416 -0
  70. metadata +103 -16
  71. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  72. data/lib/torque/postgresql/schema_dumper.rb +0 -88
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Interval' do
4
+ let(:connection) { ActiveRecord::Base.connection }
5
+
6
+ context 'on settings' do
7
+ it 'must be set to ISO 8601' do
8
+ expect(connection.select_value('SHOW IntervalStyle')).to eql('iso_8601')
9
+ end
10
+ end
11
+
12
+ context 'on table definition' do
13
+ subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
14
+
15
+ it 'has the interval method' do
16
+ expect(subject).to respond_to(:interval)
17
+ end
18
+
19
+ it 'can define an interval column' do
20
+ subject.interval('duration')
21
+ expect(subject['duration'].name).to eql('duration')
22
+ expect(subject['duration'].type).to eql(:interval)
23
+ end
24
+ end
25
+
26
+ context 'on schema' do
27
+ it 'can be used on tables too' do
28
+ dump_io = StringIO.new
29
+ ActiveRecord::SchemaDumper.dump(connection, dump_io)
30
+ expect(dump_io.string).to match /t\.interval +"duration"/
31
+ end
32
+ end
33
+
34
+ context 'on OID' do
35
+ let(:reference) { 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds }
36
+ subject { Torque::PostgreSQL::Adapter::OID::Interval.new }
37
+
38
+ context 'on deserialize' do
39
+ it 'returns nil' do
40
+ expect(subject.deserialize(nil)).to be_nil
41
+ end
42
+
43
+ it 'returns duration' do
44
+ value = subject.deserialize('P1Y2M3DT4H5M6S')
45
+
46
+ expect(value).to be_a(ActiveSupport::Duration)
47
+ expect(value).to eq(reference)
48
+ end
49
+ end
50
+
51
+ context 'on serialize' do
52
+ it 'returns nil' do
53
+ expect(subject.serialize(nil)).to be_nil
54
+ end
55
+
56
+ it 'returns seconds as string' do
57
+ expect(subject.serialize(3600.seconds)).to eq('PT3600S')
58
+ end
59
+
60
+ it 'retruns sample as string' do
61
+ expect(subject.serialize(reference)).to eq('P1Y2M3DT4H5M6S')
62
+ end
63
+
64
+ it 'transforms weeks into days' do
65
+ reference = subject.cast(1000000)
66
+ expect(subject.serialize(reference)).to eq('P11DT13H46M40S')
67
+ end
68
+ end
69
+
70
+ context 'on cast' do
71
+ it 'accepts nil' do
72
+ expect(subject.cast(nil)).to be_nil
73
+ end
74
+
75
+ it 'accepts string' do
76
+ value = subject.cast('P1Y2M3DT4H5M6S')
77
+ expect(value).to be_a(ActiveSupport::Duration)
78
+ expect(value).to eq(reference)
79
+ end
80
+
81
+ it 'accepts duration' do
82
+ value = subject.cast(5.days)
83
+ expect(value).to be_a(ActiveSupport::Duration)
84
+ expect(value).to eql(value)
85
+ end
86
+
87
+ it 'accepts small seconds numeric' do
88
+ value = subject.cast(30)
89
+ expect(value).to be_a(ActiveSupport::Duration)
90
+ expect(value).to eq(30)
91
+ end
92
+
93
+ it 'accepts long seconds numeric' do
94
+ value = subject.cast(reference.to_i)
95
+ expect(value).to be_a(ActiveSupport::Duration)
96
+ expect(value).to eq(reference)
97
+ end
98
+
99
+ it 'accepts array with Y-M-D H:M:S format' do
100
+ value = subject.cast([1, 2, 3, 4, 5, 6])
101
+ expect(value).to be_a(ActiveSupport::Duration)
102
+ expect(value).to eq(reference)
103
+ end
104
+
105
+ it 'accepts array with empty values' do
106
+ value = subject.cast([nil, 0, 12, 30, 0])
107
+ sample = 12.hours + 30.minutes
108
+ expect(value).to be_a(ActiveSupport::Duration)
109
+ expect(value.inspect).to eq(sample.inspect)
110
+ expect(value).to eq(sample)
111
+ end
112
+
113
+ it 'accepts array with string' do
114
+ value = subject.cast(['45', '15'])
115
+ sample = 45.minutes + 15.seconds
116
+ expect(value).to be_a(ActiveSupport::Duration)
117
+ expect(value.inspect).to eq(sample.inspect)
118
+ expect(value).to eq(sample)
119
+ end
120
+
121
+ it 'accepts hash' do
122
+ value = subject.cast({years: 1, months: 2, days: 3, hours: 4, minutes: 5, seconds: 6})
123
+ expect(value).to be_a(ActiveSupport::Duration)
124
+ expect(value).to eq(reference)
125
+ end
126
+
127
+ it 'accepts hash with extra elements' do
128
+ value = subject.cast({extra: 1, hours: 12, minutes: 30})
129
+ sample = 12.hours + 30.minutes
130
+ expect(value).to be_a(ActiveSupport::Duration)
131
+ expect(value).to eq(sample)
132
+ end
133
+
134
+ it 'returns any other type of value as it is' do
135
+ value = subject.cast(true)
136
+ expect(value).to eql(true)
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'on I18n' do
142
+ it 'transforms the value into singular text' do
143
+ expect(I18n.l 1.year).to eql('1 year')
144
+ expect(I18n.l 1.months).to eql('1 month')
145
+ expect(I18n.l 1.weeks).to eql('1 week')
146
+ expect(I18n.l 1.days).to eql('1 day')
147
+ expect(I18n.l 1.hours).to eql('1 hour')
148
+ expect(I18n.l 1.minutes).to eql('1 minute')
149
+ expect(I18n.l 1.seconds).to eql('1 second')
150
+ end
151
+
152
+ it 'transforms the value into plural text' do
153
+ expect(I18n.l 2.year).to eql('2 years')
154
+ expect(I18n.l 2.months).to eql('2 months')
155
+ expect(I18n.l 2.weeks).to eql('2 weeks')
156
+ expect(I18n.l 2.days).to eql('2 days')
157
+ expect(I18n.l 2.hours).to eql('2 hours')
158
+ expect(I18n.l 2.minutes).to eql('2 minutes')
159
+ expect(I18n.l 2.seconds).to eql('2 seconds')
160
+ end
161
+
162
+ it 'transforms multiple values' do
163
+ value = 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds
164
+ expect(I18n.l value).to eql('1 year, 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds')
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Lazy', type: :helper do
4
+ subject { Torque::PostgreSQL::Attributes::Lazy }
5
+
6
+ it 'is consider nil' do
7
+ expect(subject.new(String, '')).to be_nil
8
+ end
9
+
10
+ it 'inspects as nil' do
11
+ expect(subject.new(String, '').inspect).to be_eql('nil')
12
+ end
13
+
14
+ it 'compares to nil only' do
15
+ expect(subject.new(String, '') == nil).to be_truthy
16
+ expect(subject.new(String, '') == '').to be_falsey
17
+ expect(subject.new(String, '') == 0).to be_falsey
18
+ end
19
+
20
+ it 'starts the object only on method call' do
21
+ expect(subject.new(String, '').to_s).to be_a(String)
22
+ expect(subject.new(String, '')).to respond_to(:chop)
23
+ end
24
+ end
@@ -0,0 +1,954 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: Convert to shared examples
4
+ RSpec.describe 'Period' do
5
+ let(:model) { Class.new(TimeKeeper) }
6
+ let(:instance) { model.new }
7
+ let(:fields) { %i[available period tzperiod] }
8
+ let(:method_names) { Torque::PostgreSQL::config.period.method_names }
9
+ let(:attribute_klass) { Torque::PostgreSQL::Attributes::Period }
10
+
11
+ let(:true_value) { 'TRUE' }
12
+ let(:false_value) { 'FALSE' }
13
+
14
+ let(:klass_methods_range) { (0..22) }
15
+ let(:instance_methods_range) { (23..29) }
16
+
17
+ let(:klass_method_names) { method_names.to_a[klass_methods_range].to_h }
18
+ let(:instance_method_names) { method_names.to_a[instance_methods_range].to_h }
19
+
20
+ before { Time.zone = 'UTC' }
21
+
22
+ def decorate(model, field, options = {})
23
+ attribute_klass.include_on(model, :period_for)
24
+ model.period_for(field, **options)
25
+ end
26
+
27
+ context 'on config' do
28
+ let(:direct_method_names) do
29
+ list = method_names.dup
30
+ list.merge!(Torque::PostgreSQL::config.period.direct_method_names)
31
+ list.values.map { |v| v.gsub(/_?%s_?/, '') }
32
+ end
33
+
34
+ let(:other_method_names) do
35
+ method_names.transform_values.with_index { |_, idx| "p__#{idx}" }
36
+ end
37
+
38
+ it 'has definition method on the model' do
39
+ attribute_klass.include_on(ActiveRecord::Base, :period_for)
40
+ expect(model).to respond_to(:period_for)
41
+ ActiveRecord::Base.singleton_class.send(:undef_method, :period_for)
42
+ end
43
+
44
+ it 'create the methods with custom names' do
45
+ decorate(model, :tzperiod, threshold: 5.minutes, methods: other_method_names)
46
+
47
+ klass_method_names.size.times do |i|
48
+ expect(model).to respond_to("p__#{i}")
49
+ end
50
+
51
+ initial = instance_methods_range.min
52
+ instance_method_names.size.times do |i|
53
+ expect(instance).to respond_to("p__#{initial + i}")
54
+ end
55
+ end
56
+
57
+ it 'creates non prefixed methods if requested' do
58
+ decorate(model, :tzperiod, prefixed: false, threshold: 5.minutes)
59
+
60
+ direct_method_names[klass_methods_range].each do |m|
61
+ expect(model).to respond_to(m)
62
+ end
63
+
64
+ direct_method_names[instance_methods_range].each do |m|
65
+ expect(instance).to respond_to(m)
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'on tsrange' do
71
+ let(:type) { :tsrange }
72
+ let(:value) { Time.zone.now.beginning_of_minute }
73
+ let(:db_field) { '"time_keepers"."period"' }
74
+ let(:db_value) { "'#{value.strftime('%F %T')}'" }
75
+
76
+ let(:cast_type) { '::timestamp' }
77
+ let(:cast_db_value) { "#{db_value}#{cast_type}" }
78
+ let(:empty_condition) { "#{type}(NULL, NULL)" }
79
+ let(:nullif_condition) { "nullif(#{db_field}, #{empty_condition})" }
80
+
81
+ let(:date_type) { :daterange }
82
+ let(:lower_date) { "lower(#{db_field})::date" }
83
+ let(:upper_date) { "upper(#{db_field})::date" }
84
+ let(:date_db_field) { "#{date_type}(#{lower_date}, #{upper_date}, '[]')" }
85
+
86
+ context 'on model' do
87
+ before { decorate(model, :period) }
88
+
89
+ it 'queries current on period' do
90
+ expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
91
+ coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
92
+ SQL
93
+ end
94
+
95
+ it 'queries current period' do
96
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
97
+ coalesce(#{nullif_condition} @>
98
+ SQL
99
+
100
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
101
+ #{cast_type}, #{true_value})
102
+ SQL
103
+ end
104
+
105
+ it 'queries not current period' do
106
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
107
+ NOT (coalesce(#{nullif_condition} @>
108
+ SQL
109
+
110
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
111
+ #{cast_type}, #{true_value})
112
+ SQL
113
+ end
114
+
115
+ it 'queries containing period' do
116
+ expect(model.period_containing(:test).to_sql).to include(<<-SQL.squish)
117
+ #{db_field} @> "time_keepers"."test"
118
+ SQL
119
+
120
+ expect(model.period_containing(value).to_sql).to include(<<-SQL.squish)
121
+ #{db_field} @> #{db_value}
122
+ SQL
123
+ end
124
+
125
+ it 'queries not containing period' do
126
+ expect(model.period_not_containing(:test).to_sql).to include(<<-SQL.squish)
127
+ NOT (#{db_field} @> "time_keepers"."test")
128
+ SQL
129
+
130
+ expect(model.period_not_containing(value).to_sql).to include(<<-SQL.squish)
131
+ NOT (#{db_field} @> #{db_value})
132
+ SQL
133
+ end
134
+
135
+ it 'queries overlapping period' do
136
+ expect(model.period_overlapping(:test).to_sql).to include(<<-SQL.squish)
137
+ #{db_field} && "time_keepers"."test"
138
+ SQL
139
+
140
+ expect(model.period_overlapping(value, value).to_sql).to include(<<-SQL.squish)
141
+ #{db_field} && #{type}(#{db_value}, #{db_value})
142
+ SQL
143
+ end
144
+
145
+ it 'queries not overlapping period' do
146
+ expect(model.period_not_overlapping(:test).to_sql).to include(<<-SQL.squish)
147
+ NOT (#{db_field} && "time_keepers"."test")
148
+ SQL
149
+
150
+ expect(model.period_not_overlapping(value, value).to_sql).to include(<<-SQL.squish)
151
+ NOT (#{db_field} && #{type}(#{db_value}, #{db_value}))
152
+ SQL
153
+ end
154
+
155
+ it 'queries starting after period' do
156
+ expect(model.period_starting_after(:test).to_sql).to include(<<-SQL.squish)
157
+ lower(#{db_field}) > "time_keepers"."test"
158
+ SQL
159
+
160
+ expect(model.period_starting_after(value).to_sql).to include(<<-SQL.squish)
161
+ lower(#{db_field}) > #{db_value}
162
+ SQL
163
+ end
164
+
165
+ it 'queries starting before period' do
166
+ expect(model.period_starting_before(:test).to_sql).to include(<<-SQL.squish)
167
+ lower(#{db_field}) < "time_keepers"."test"
168
+ SQL
169
+
170
+ expect(model.period_starting_before(value).to_sql).to include(<<-SQL.squish)
171
+ lower(#{db_field}) < #{db_value}
172
+ SQL
173
+ end
174
+
175
+ it 'queries finishing after period' do
176
+ expect(model.period_finishing_after(:test).to_sql).to include(<<-SQL.squish)
177
+ upper(#{db_field}) > "time_keepers"."test"
178
+ SQL
179
+
180
+ expect(model.period_finishing_after(value).to_sql).to include(<<-SQL.squish)
181
+ upper(#{db_field}) > #{db_value}
182
+ SQL
183
+ end
184
+
185
+ it 'queries finishing before period' do
186
+ expect(model.period_finishing_before(:test).to_sql).to include(<<-SQL.squish)
187
+ upper(#{db_field}) < "time_keepers"."test"
188
+ SQL
189
+
190
+ expect(model.period_finishing_before(value).to_sql).to include(<<-SQL.squish)
191
+ upper(#{db_field}) < #{db_value}
192
+ SQL
193
+ end
194
+
195
+ it 'does not have real starting after for period' do
196
+ expect(model.all).not_to respond_to(:real_starting_after)
197
+ end
198
+
199
+ it 'does not have real starting before for period' do
200
+ expect(model.all).not_to respond_to(:real_starting_before)
201
+ end
202
+
203
+ it 'does not have real finishing after for period' do
204
+ expect(model.all).not_to respond_to(:real_finishing_after)
205
+ end
206
+
207
+ it 'does not have real finishing before for period' do
208
+ expect(model.all).not_to respond_to(:real_finishing_before)
209
+ end
210
+
211
+ it 'queries containing date period' do
212
+ expect(model.period_containing_date(:test).to_sql).to include(<<-SQL.squish)
213
+ #{date_db_field} @> "time_keepers"."test"
214
+ SQL
215
+
216
+ expect(model.period_containing_date(value).to_sql).to include(<<-SQL.squish)
217
+ #{date_db_field} @> #{db_value}::date
218
+ SQL
219
+ end
220
+
221
+ it 'queries not containing date period' do
222
+ expect(model.period_not_containing_date(:test).to_sql).to include(<<-SQL.squish)
223
+ NOT (#{date_db_field} @> "time_keepers"."test")
224
+ SQL
225
+
226
+ expect(model.period_not_containing_date(value).to_sql).to include(<<-SQL.squish)
227
+ NOT (#{date_db_field} @> #{db_value}::date)
228
+ SQL
229
+ end
230
+
231
+ it 'queries overlapping date period' do
232
+ expect(model.period_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
233
+ #{date_db_field} && "time_keepers"."test"
234
+ SQL
235
+
236
+ expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
237
+ #{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
238
+ SQL
239
+ end
240
+
241
+ it 'queries not overlapping date period' do
242
+ expect(model.period_not_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
243
+ NOT (#{date_db_field} && "time_keepers"."test")
244
+ SQL
245
+
246
+ expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
247
+ NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
248
+ SQL
249
+ end
250
+
251
+ it 'does not have real containing date period' do
252
+ expect(model.all).not_to respond_to(:period_real_containing_date)
253
+ end
254
+
255
+ it 'does not have real overlapping date period' do
256
+ expect(model.all).not_to respond_to(:period_real_overlapping_date)
257
+ end
258
+ end
259
+
260
+ context 'on instance' do
261
+ before { decorate(model, :period) }
262
+
263
+ it 'checks for current value' do
264
+ instance.period = 1.hour.ago.utc..1.hour.from_now.utc
265
+ expect(instance).to be_current_period
266
+
267
+ instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
268
+ expect(instance).not_to be_current_period
269
+ end
270
+
271
+ it 'checks fro current based on a value' do
272
+ instance.period = 1.hour.ago.utc..1.hour.from_now.utc
273
+ expect(instance).to be_current_period_on(5.minutes.from_now.utc)
274
+
275
+ instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
276
+ expect(instance).not_to be_current_period_on(5.minutes.from_now.utc)
277
+ end
278
+
279
+ it 'returns the start time' do
280
+ instance.period = 1.hour.ago.utc..1.hour.from_now.utc
281
+ expect(instance.period_start).to be_eql(instance.period.min)
282
+
283
+ instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
284
+ expect(instance.period_start).to be_eql(instance.period.min)
285
+ end
286
+
287
+ it 'returns the finish time' do
288
+ instance.period = 1.hour.ago.utc..1.hour.from_now.utc
289
+ expect(instance.period_finish).to be_eql(instance.period.max)
290
+
291
+ instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
292
+ expect(instance.period_finish).to be_eql(instance.period.max)
293
+ end
294
+ end
295
+
296
+ context 'with field threshold' do
297
+ before { decorate(model, :period, threshold: :th) }
298
+
299
+ let(:lower_db_field) { "(lower(#{db_field}) - #{threshold_value})" }
300
+ let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})" }
301
+ let(:threshold_value) { '"time_keepers"."th"' }
302
+ let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
303
+ let(:nullif_condition) { "nullif(#{threshold_db_field}, #{empty_condition})" }
304
+ let(:threshold_date_db_field) do
305
+ "daterange(#{lower_db_field}::date, #{upper_db_field}::date, '[]')"
306
+ end
307
+
308
+ context 'on model' do
309
+ it 'queries current on period' do
310
+ expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
311
+ coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
312
+ SQL
313
+ end
314
+
315
+ it 'queries current period' do
316
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
317
+ coalesce(#{nullif_condition} @>
318
+ SQL
319
+
320
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
321
+ #{cast_type}, #{true_value})
322
+ SQL
323
+ end
324
+
325
+ it 'queries not current period' do
326
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
327
+ NOT (coalesce(#{nullif_condition} @>
328
+ SQL
329
+
330
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
331
+ #{cast_type}, #{true_value})
332
+ SQL
333
+ end
334
+
335
+ it 'queries real containing period' do
336
+ expect(model.period_real_containing(:test).to_sql).to include(<<-SQL.squish)
337
+ #{threshold_db_field} @> "time_keepers"."test"
338
+ SQL
339
+
340
+ expect(model.period_real_containing(value).to_sql).to include(<<-SQL.squish)
341
+ #{threshold_db_field} @> #{db_value}
342
+ SQL
343
+ end
344
+
345
+ it 'queries real overlapping period' do
346
+ expect(model.period_real_overlapping(:test).to_sql).to include(<<-SQL.squish)
347
+ #{threshold_db_field} && "time_keepers"."test"
348
+ SQL
349
+
350
+ expect(model.period_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
351
+ #{threshold_db_field} && #{type}(#{db_value}, #{db_value})
352
+ SQL
353
+ end
354
+
355
+ it 'queries real starting after for period' do
356
+ expect(model.period_real_starting_after(:test).to_sql).to include(<<-SQL.squish)
357
+ #{lower_db_field} > "time_keepers"."test"
358
+ SQL
359
+
360
+ expect(model.period_real_starting_after(value).to_sql).to include(<<-SQL.squish)
361
+ #{lower_db_field} > #{db_value}
362
+ SQL
363
+ end
364
+
365
+ it 'queries real starting before for period' do
366
+ expect(model.period_real_starting_before(:test).to_sql).to include(<<-SQL.squish)
367
+ #{lower_db_field} < "time_keepers"."test"
368
+ SQL
369
+
370
+ expect(model.period_real_starting_before(value).to_sql).to include(<<-SQL.squish)
371
+ #{lower_db_field} < #{db_value}
372
+ SQL
373
+ end
374
+
375
+ it 'queries real finishing after for period' do
376
+ expect(model.period_real_finishing_after(:test).to_sql).to include(<<-SQL.squish)
377
+ #{upper_db_field} > "time_keepers"."test"
378
+ SQL
379
+
380
+ expect(model.period_real_finishing_after(value).to_sql).to include(<<-SQL.squish)
381
+ #{upper_db_field} > #{db_value}
382
+ SQL
383
+ end
384
+
385
+ it 'queries real finishing before for period' do
386
+ expect(model.period_real_finishing_before(:test).to_sql).to include(<<-SQL.squish)
387
+ #{upper_db_field} < "time_keepers"."test"
388
+ SQL
389
+
390
+ expect(model.period_real_finishing_before(value).to_sql).to include(<<-SQL.squish)
391
+ #{upper_db_field} < #{db_value}
392
+ SQL
393
+ end
394
+
395
+ it 'queries containing date period' do
396
+ expect(model.period_containing_date(:test).to_sql).to include(<<-SQL.squish)
397
+ #{date_db_field} @> "time_keepers"."test"
398
+ SQL
399
+
400
+ expect(model.period_containing_date(value).to_sql).to include(<<-SQL.squish)
401
+ #{date_db_field} @> #{db_value}::date
402
+ SQL
403
+ end
404
+
405
+ it 'queries not containing date period' do
406
+ expect(model.period_not_containing_date(:test).to_sql).to include(<<-SQL.squish)
407
+ NOT (#{date_db_field} @> "time_keepers"."test")
408
+ SQL
409
+
410
+ expect(model.period_not_containing_date(value).to_sql).to include(<<-SQL.squish)
411
+ NOT (#{date_db_field} @> #{db_value}::date)
412
+ SQL
413
+ end
414
+
415
+ it 'queries overlapping date period' do
416
+ expect(model.period_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
417
+ #{date_db_field} && "time_keepers"."test"
418
+ SQL
419
+
420
+ expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
421
+ #{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
422
+ SQL
423
+ end
424
+
425
+ it 'queries not overlapping date period' do
426
+ expect(model.period_not_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
427
+ NOT (#{date_db_field} && "time_keepers"."test")
428
+ SQL
429
+
430
+ expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
431
+ NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
432
+ SQL
433
+ end
434
+
435
+ it 'queries real containing date period' do
436
+ expect(model.period_real_containing_date(:test).to_sql).to include(<<-SQL.squish)
437
+ #{threshold_date_db_field} @> "time_keepers"."test"
438
+ SQL
439
+
440
+ expect(model.period_real_containing_date(value).to_sql).to include(<<-SQL.squish)
441
+ #{threshold_date_db_field} @> #{db_value}::date
442
+ SQL
443
+ end
444
+
445
+ it 'queries real overlapping date period' do
446
+ expect(model.period_real_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
447
+ #{threshold_date_db_field} && "time_keepers"."test"
448
+ SQL
449
+
450
+ expect(model.period_real_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
451
+ #{threshold_date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
452
+ SQL
453
+ end
454
+ end
455
+
456
+ context 'on instance' do
457
+ before { decorate(model, :period, threshold: :th) }
458
+ before { instance.th = 1.hour }
459
+
460
+ it 'checks for current value' do
461
+ instance.period = nil
462
+ expect(instance).to be_current_period
463
+
464
+ instance.period = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
465
+ expect(instance).to be_current_period
466
+
467
+ instance.period = (Time.zone.now + 90.minutes)..(Time.zone.now + 3.hour)
468
+ expect(instance).not_to be_current_period
469
+ end
470
+
471
+ it 'checks for current based on a value' do
472
+ instance.period = nil
473
+ expect(instance).to be_current_period_on(5.minutes.from_now.utc)
474
+
475
+ instance.period = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
476
+ expect(instance).to be_current_period_on(5.minutes.from_now.utc)
477
+
478
+ instance.period = 90.minutes.from_now.utc..3.hour.from_now.utc
479
+ expect(instance).not_to be_current_period_on(5.minutes.from_now.utc)
480
+ end
481
+
482
+ it 'returns the real range' do
483
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
484
+ instance.period = value
485
+ expect(instance.real_period.min).to be_eql(value.min - 1.hour)
486
+ expect(instance.real_period.max).to be_eql(value.max + 1.hour)
487
+ end
488
+
489
+ it 'returns the real start' do
490
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
491
+ instance.period = value
492
+ expect(instance.period_real_start).to be_eql(value.min - 1.hour)
493
+ end
494
+
495
+ it 'returns the real finish' do
496
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
497
+ instance.period = value
498
+ expect(instance.period_real_finish).to be_eql(value.max + 1.hour)
499
+ end
500
+ end
501
+ end
502
+
503
+ context 'with value threshold' do
504
+ before { decorate(model, :period, threshold: 5.minutes) }
505
+
506
+ let(:lower_db_field) { "(lower(#{db_field}) - #{threshold_value})" }
507
+ let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})" }
508
+ let(:threshold_value) { "'300 seconds'::interval" }
509
+ let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
510
+ let(:nullif_condition) { "nullif(#{threshold_db_field}, #{empty_condition})" }
511
+
512
+ context 'on model' do
513
+ it 'queries current on period' do
514
+ expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
515
+ coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
516
+ SQL
517
+ end
518
+
519
+ it 'queries current period' do
520
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
521
+ coalesce(#{nullif_condition} @>
522
+ SQL
523
+
524
+ expect(model.current_period.to_sql).to include(<<-SQL.squish)
525
+ #{cast_type}, #{true_value})
526
+ SQL
527
+ end
528
+
529
+ it 'queries not current period' do
530
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
531
+ NOT (coalesce(#{nullif_condition} @>
532
+ SQL
533
+
534
+ expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
535
+ #{cast_type}, #{true_value})
536
+ SQL
537
+ end
538
+
539
+ it 'queries real containing period' do
540
+ expect(model.period_real_containing(:test).to_sql).to include(<<-SQL.squish)
541
+ #{threshold_db_field} @> "time_keepers"."test"
542
+ SQL
543
+
544
+ expect(model.period_real_containing(value).to_sql).to include(<<-SQL.squish)
545
+ #{threshold_db_field} @> #{db_value}
546
+ SQL
547
+ end
548
+
549
+ it 'queries real overlapping period' do
550
+ expect(model.period_real_overlapping(:test).to_sql).to include(<<-SQL.squish)
551
+ #{threshold_db_field} && "time_keepers"."test"
552
+ SQL
553
+
554
+ expect(model.period_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
555
+ #{threshold_db_field} && #{type}(#{db_value}, #{db_value})
556
+ SQL
557
+ end
558
+
559
+ it 'queries real starting after for period' do
560
+ expect(model.period_real_starting_after(:test).to_sql).to include(<<-SQL.squish)
561
+ #{lower_db_field} > "time_keepers"."test"
562
+ SQL
563
+
564
+ expect(model.period_real_starting_after(value).to_sql).to include(<<-SQL.squish)
565
+ #{lower_db_field} > #{db_value}
566
+ SQL
567
+ end
568
+
569
+ it 'queries real starting before for period' do
570
+ expect(model.period_real_starting_before(:test).to_sql).to include(<<-SQL.squish)
571
+ #{lower_db_field} < "time_keepers"."test"
572
+ SQL
573
+
574
+ expect(model.period_real_starting_before(value).to_sql).to include(<<-SQL.squish)
575
+ #{lower_db_field} < #{db_value}
576
+ SQL
577
+ end
578
+
579
+ it 'queries real finishing after for period' do
580
+ expect(model.period_real_finishing_after(:test).to_sql).to include(<<-SQL.squish)
581
+ #{upper_db_field} > "time_keepers"."test"
582
+ SQL
583
+
584
+ expect(model.period_real_finishing_after(value).to_sql).to include(<<-SQL.squish)
585
+ #{upper_db_field} > #{db_value}
586
+ SQL
587
+ end
588
+
589
+ it 'queries real finishing before for period' do
590
+ expect(model.period_real_finishing_before(:test).to_sql).to include(<<-SQL.squish)
591
+ #{upper_db_field} < "time_keepers"."test"
592
+ SQL
593
+
594
+ expect(model.period_real_finishing_before(value).to_sql).to include(<<-SQL.squish)
595
+ #{upper_db_field} < #{db_value}
596
+ SQL
597
+ end
598
+
599
+ it 'queries containing date period' do
600
+ expect(model.period_containing_date(:test).to_sql).to include(<<-SQL.squish)
601
+ #{date_db_field} @> "time_keepers"."test"
602
+ SQL
603
+
604
+ expect(model.period_containing_date(value).to_sql).to include(<<-SQL.squish)
605
+ #{date_db_field} @> #{db_value}
606
+ SQL
607
+ end
608
+
609
+ it 'queries not containing date period' do
610
+ expect(model.period_not_containing_date(:test).to_sql).to include(<<-SQL.squish)
611
+ NOT (#{date_db_field} @> "time_keepers"."test")
612
+ SQL
613
+
614
+ expect(model.period_not_containing_date(value).to_sql).to include(<<-SQL.squish)
615
+ NOT (#{date_db_field} @> #{db_value}::date)
616
+ SQL
617
+ end
618
+
619
+ it 'queries overlapping date period' do
620
+ expect(model.period_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
621
+ #{date_db_field} && "time_keepers"."test"
622
+ SQL
623
+
624
+ expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
625
+ #{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
626
+ SQL
627
+ end
628
+
629
+ it 'queries not overlapping date period' do
630
+ expect(model.period_not_overlapping_date(:test).to_sql).to include(<<-SQL.squish)
631
+ NOT (#{date_db_field} && "time_keepers"."test")
632
+ SQL
633
+
634
+ expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
635
+ NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
636
+ SQL
637
+ end
638
+ end
639
+
640
+ context 'on instance' do
641
+ before { decorate(model, :period, threshold: 45.minutes) }
642
+
643
+ it 'checks for current value' do
644
+ instance.period = nil
645
+ expect(instance).to be_current_period
646
+
647
+ instance.period = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
648
+ expect(instance).to be_current_period
649
+
650
+ instance.period = (Time.zone.now + 90.minutes)..(Time.zone.now + 3.hour)
651
+ expect(instance).not_to be_current_period
652
+ end
653
+
654
+ it 'checks for current based on a value' do
655
+ instance.period = nil
656
+ expect(instance).to be_current_period_on(5.minutes.from_now.utc)
657
+
658
+ instance.period = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
659
+ expect(instance).to be_current_period_on(5.minutes.from_now.utc)
660
+
661
+ instance.period = 90.minutes.from_now.utc..3.hour.from_now.utc
662
+ expect(instance).not_to be_current_period_on(5.minutes.from_now.utc)
663
+ end
664
+
665
+ it 'returns the real range' do
666
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
667
+ instance.period = value
668
+ expect(instance.real_period.min).to be_eql(value.min - 45.minutes)
669
+ expect(instance.real_period.max).to be_eql(value.max + 45.minutes)
670
+ end
671
+
672
+ it 'returns the real start' do
673
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
674
+ instance.period = value
675
+ expect(instance.period_real_start).to be_eql(value.min - 45.minutes)
676
+ end
677
+
678
+ it 'returns the real finish' do
679
+ value = (Time.zone.now - 1.hour)..(Time.zone.now + 1.hour)
680
+ instance.period = value
681
+ expect(instance.period_real_finish).to be_eql(value.max + 45.minutes)
682
+ end
683
+ end
684
+ end
685
+ end
686
+
687
+ context 'on daterange' do
688
+ let(:type) { :daterange }
689
+ let(:value) { Date.today }
690
+ let(:db_field) { '"time_keepers"."available"' }
691
+ let(:db_value) { "'#{value.strftime('%F')}'" }
692
+
693
+ let(:cast_type) { '::date' }
694
+ let(:cast_db_value) { "#{db_value}#{cast_type}" }
695
+ let(:empty_condition) { "#{type}(NULL, NULL)" }
696
+ let(:nullif_condition) { "nullif(#{threshold_db_field}, #{empty_condition})" }
697
+
698
+ let(:lower_db_field) { "(lower(#{db_field}) - #{threshold_value})::date" }
699
+ let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})::date" }
700
+ let(:threshold_value) { "'86400 seconds'::interval" }
701
+ let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
702
+
703
+ before { decorate(model, :available, pessimistic: true, threshold: 1.day) }
704
+
705
+ context 'on model' do
706
+ it 'queries current on available' do
707
+ expect(model.available_on(value).to_sql).to include(<<-SQL.squish)
708
+ coalesce(#{nullif_condition} @> #{cast_db_value}, #{false_value})
709
+ SQL
710
+ end
711
+
712
+ it 'queries current available' do
713
+ expect(model.current_available.to_sql).to include(<<-SQL.squish)
714
+ coalesce(#{nullif_condition} @>
715
+ SQL
716
+
717
+ expect(model.current_available.to_sql).to include(<<-SQL.squish)
718
+ #{cast_type}, #{false_value})
719
+ SQL
720
+ end
721
+
722
+ it 'queries not current available' do
723
+ expect(model.not_current_available.to_sql).to include(<<-SQL.squish)
724
+ NOT (coalesce(#{nullif_condition} @>
725
+ SQL
726
+
727
+ expect(model.not_current_available.to_sql).to include(<<-SQL.squish)
728
+ #{cast_type}, #{false_value})
729
+ SQL
730
+ end
731
+
732
+ it 'queries containing available' do
733
+ expect(model.available_containing(:test).to_sql).to include(<<-SQL.squish)
734
+ #{db_field} @> "time_keepers"."test"
735
+ SQL
736
+
737
+ expect(model.available_containing(value).to_sql).to include(<<-SQL.squish)
738
+ #{db_field} @> #{db_value}
739
+ SQL
740
+ end
741
+
742
+ it 'queries not containing available' do
743
+ expect(model.available_not_containing(:test).to_sql).to include(<<-SQL.squish)
744
+ NOT (#{db_field} @> "time_keepers"."test")
745
+ SQL
746
+
747
+ expect(model.available_not_containing(value).to_sql).to include(<<-SQL.squish)
748
+ NOT (#{db_field} @> #{db_value})
749
+ SQL
750
+ end
751
+
752
+ it 'queries overlapping available' do
753
+ expect(model.available_overlapping(:test).to_sql).to include(<<-SQL.squish)
754
+ #{db_field} && "time_keepers"."test"
755
+ SQL
756
+
757
+ expect(model.available_overlapping(value, value).to_sql).to include(<<-SQL.squish)
758
+ #{db_field} && #{type}(#{db_value}, #{db_value})
759
+ SQL
760
+ end
761
+
762
+ it 'queries not overlapping available' do
763
+ expect(model.available_not_overlapping(:test).to_sql).to include(<<-SQL.squish)
764
+ NOT (#{db_field} && "time_keepers"."test")
765
+ SQL
766
+
767
+ expect(model.available_not_overlapping(value, value).to_sql).to include(<<-SQL.squish)
768
+ NOT (#{db_field} && #{type}(#{db_value}, #{db_value}))
769
+ SQL
770
+ end
771
+
772
+ it 'queries starting after available' do
773
+ expect(model.available_starting_after(:test).to_sql).to include(<<-SQL.squish)
774
+ lower(#{db_field}) > "time_keepers"."test"
775
+ SQL
776
+
777
+ expect(model.available_starting_after(value).to_sql).to include(<<-SQL.squish)
778
+ lower(#{db_field}) > #{db_value}
779
+ SQL
780
+ end
781
+
782
+ it 'queries starting before available' do
783
+ expect(model.available_starting_before(:test).to_sql).to include(<<-SQL.squish)
784
+ lower(#{db_field}) < "time_keepers"."test"
785
+ SQL
786
+
787
+ expect(model.available_starting_before(value).to_sql).to include(<<-SQL.squish)
788
+ lower(#{db_field}) < #{db_value}
789
+ SQL
790
+ end
791
+
792
+ it 'queries finishing after available' do
793
+ expect(model.available_finishing_after(:test).to_sql).to include(<<-SQL.squish)
794
+ upper(#{db_field}) > "time_keepers"."test"
795
+ SQL
796
+
797
+ expect(model.available_finishing_after(value).to_sql).to include(<<-SQL.squish)
798
+ upper(#{db_field}) > #{db_value}
799
+ SQL
800
+ end
801
+
802
+ it 'queries finishing before available' do
803
+ expect(model.available_finishing_before(:test).to_sql).to include(<<-SQL.squish)
804
+ upper(#{db_field}) < "time_keepers"."test"
805
+ SQL
806
+
807
+ expect(model.available_finishing_before(value).to_sql).to include(<<-SQL.squish)
808
+ upper(#{db_field}) < #{db_value}
809
+ SQL
810
+ end
811
+
812
+ it 'queries real containing available' do
813
+ expect(model.available_real_containing(:test).to_sql).to include(<<-SQL.squish)
814
+ #{threshold_db_field} @> "time_keepers"."test"
815
+ SQL
816
+
817
+ expect(model.available_real_containing(value).to_sql).to include(<<-SQL.squish)
818
+ #{threshold_db_field} @> #{db_value}
819
+ SQL
820
+ end
821
+
822
+ it 'queries real overlapping available' do
823
+ expect(model.available_real_overlapping(:test).to_sql).to include(<<-SQL.squish)
824
+ #{threshold_db_field} && "time_keepers"."test"
825
+ SQL
826
+
827
+ expect(model.available_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
828
+ #{threshold_db_field} && #{type}(#{db_value}, #{db_value})
829
+ SQL
830
+ end
831
+
832
+ it 'queries real starting after for available' do
833
+ expect(model.available_real_starting_after(:test).to_sql).to include(<<-SQL.squish)
834
+ #{lower_db_field} > "time_keepers"."test"
835
+ SQL
836
+
837
+ expect(model.available_real_starting_after(value).to_sql).to include(<<-SQL.squish)
838
+ #{lower_db_field} > #{db_value}
839
+ SQL
840
+ end
841
+
842
+ it 'queries real starting before for available' do
843
+ expect(model.available_real_starting_before(:test).to_sql).to include(<<-SQL.squish)
844
+ #{lower_db_field} < "time_keepers"."test"
845
+ SQL
846
+
847
+ expect(model.available_real_starting_before(value).to_sql).to include(<<-SQL.squish)
848
+ #{lower_db_field} < #{db_value}
849
+ SQL
850
+ end
851
+
852
+ it 'queries real finishing after for available' do
853
+ expect(model.available_real_finishing_after(:test).to_sql).to include(<<-SQL.squish)
854
+ #{upper_db_field} > "time_keepers"."test"
855
+ SQL
856
+
857
+ expect(model.available_real_finishing_after(value).to_sql).to include(<<-SQL.squish)
858
+ #{upper_db_field} > #{db_value}
859
+ SQL
860
+ end
861
+
862
+ it 'queries real finishing before for available' do
863
+ expect(model.available_real_finishing_before(:test).to_sql).to include(<<-SQL.squish)
864
+ #{upper_db_field} < "time_keepers"."test"
865
+ SQL
866
+
867
+ expect(model.available_real_finishing_before(value).to_sql).to include(<<-SQL.squish)
868
+ #{upper_db_field} < #{db_value}
869
+ SQL
870
+ end
871
+
872
+ it 'does not query containing date available' do
873
+ expect(model.all).not_to respond_to(:available_containing_date)
874
+ end
875
+
876
+ it 'does not query not containing date available' do
877
+ expect(model.all).not_to respond_to(:available_not_containing_date)
878
+ end
879
+
880
+ it 'does not query overlapping date available' do
881
+ expect(model.all).not_to respond_to(:available_overlapping_date)
882
+ end
883
+
884
+ it 'does not query not overlapping date available' do
885
+ expect(model.all).not_to respond_to(:available_not_overlapping_date)
886
+ end
887
+
888
+ it 'does not query real containing date available' do
889
+ expect(model.all).not_to respond_to(:available_real_containing_date)
890
+ end
891
+
892
+ it 'does not query real overlapping date available' do
893
+ expect(model.all).not_to respond_to(:available_real_overlapping_date)
894
+ end
895
+ end
896
+
897
+ context 'on instance' do
898
+ it 'checks for current value' do
899
+ instance.available = nil
900
+ expect(instance).not_to be_current_available
901
+
902
+ instance.available = Date.yesterday..Date.tomorrow
903
+ expect(instance).to be_current_available
904
+
905
+ instance.available = Date.new.prev_month..Date.new.next_month
906
+ expect(instance).not_to be_current_available
907
+ end
908
+
909
+ it 'checks fro current based on a value' do
910
+ instance.available = nil
911
+ expect(instance).not_to be_current_available_on(Date.tomorrow)
912
+
913
+ instance.available = Date.yesterday..Date.tomorrow
914
+ expect(instance).to be_current_available_on(Date.tomorrow)
915
+
916
+ instance.available = Date.new.prev_month..Date.new.next_month
917
+ expect(instance).to be_current_available_on(Date.new.next_month)
918
+ end
919
+
920
+ it 'returns the start date' do
921
+ instance.available = Date.yesterday..Date.tomorrow
922
+ expect(instance.available_start).to be_eql(instance.available.min)
923
+
924
+ instance.available = Date.new.prev_month..Date.new.next_month
925
+ expect(instance.available_start).to be_eql(instance.available.min)
926
+ end
927
+
928
+ it 'returns the finish date' do
929
+ instance.available = Date.yesterday..Date.tomorrow
930
+ expect(instance.available_finish).to be_eql(instance.available.max)
931
+
932
+ instance.available = Date.new.prev_month..Date.new.next_month
933
+ expect(instance.available_finish).to be_eql(instance.available.max)
934
+ end
935
+
936
+ it 'returns the real range' do
937
+ value = Date.yesterday..Date.tomorrow
938
+ instance.available = value
939
+ expect(instance.real_available.min).to be_eql(value.min.prev_day)
940
+ expect(instance.real_available.max).to be_eql(value.max.next_day)
941
+ end
942
+
943
+ it 'returns the real start date' do
944
+ instance.available = Date.yesterday..Date.tomorrow
945
+ expect(instance.available_real_start).to be_eql(instance.available.min.prev_day)
946
+ end
947
+
948
+ it 'returns the real finish date' do
949
+ instance.available = Date.yesterday..Date.tomorrow
950
+ expect(instance.available_real_finish).to be_eql(instance.available.max.next_day)
951
+ end
952
+ end
953
+ end
954
+ end