torque-postgresql 1.1.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql.rb +0 -2
  3. data/lib/torque/postgresql/adapter.rb +0 -1
  4. data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
  5. data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
  6. data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
  7. data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
  8. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
  9. data/lib/torque/postgresql/associations.rb +0 -3
  10. data/lib/torque/postgresql/associations/association.rb +0 -4
  11. data/lib/torque/postgresql/associations/association_scope.rb +18 -60
  12. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
  13. data/lib/torque/postgresql/associations/preloader.rb +0 -32
  14. data/lib/torque/postgresql/associations/preloader/association.rb +13 -10
  15. data/lib/torque/postgresql/autosave_association.rb +4 -4
  16. data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
  17. data/lib/torque/postgresql/coder.rb +1 -2
  18. data/lib/torque/postgresql/config.rb +0 -6
  19. data/lib/torque/postgresql/inheritance.rb +13 -17
  20. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  21. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
  22. data/lib/torque/postgresql/relation.rb +11 -16
  23. data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
  24. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  25. data/lib/torque/postgresql/version.rb +1 -1
  26. data/spec/en.yml +19 -0
  27. data/spec/factories/authors.rb +6 -0
  28. data/spec/factories/comments.rb +13 -0
  29. data/spec/factories/posts.rb +6 -0
  30. data/spec/factories/tags.rb +5 -0
  31. data/spec/factories/texts.rb +5 -0
  32. data/spec/factories/users.rb +6 -0
  33. data/spec/factories/videos.rb +5 -0
  34. data/spec/mocks/cache_query.rb +16 -0
  35. data/spec/mocks/create_table.rb +35 -0
  36. data/spec/models/activity.rb +3 -0
  37. data/spec/models/activity_book.rb +4 -0
  38. data/spec/models/activity_post.rb +7 -0
  39. data/spec/models/activity_post/sample.rb +4 -0
  40. data/spec/models/author.rb +4 -0
  41. data/spec/models/author_journalist.rb +4 -0
  42. data/spec/models/comment.rb +3 -0
  43. data/spec/models/course.rb +2 -0
  44. data/spec/models/geometry.rb +2 -0
  45. data/spec/models/guest_comment.rb +4 -0
  46. data/spec/models/post.rb +6 -0
  47. data/spec/models/tag.rb +2 -0
  48. data/spec/models/text.rb +2 -0
  49. data/spec/models/time_keeper.rb +2 -0
  50. data/spec/models/user.rb +8 -0
  51. data/spec/models/video.rb +2 -0
  52. data/spec/schema.rb +141 -0
  53. data/spec/spec_helper.rb +59 -0
  54. data/spec/tests/arel_spec.rb +72 -0
  55. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  56. data/spec/tests/belongs_to_many_spec.rb +240 -0
  57. data/spec/tests/coder_spec.rb +367 -0
  58. data/spec/tests/collector_spec.rb +59 -0
  59. data/spec/tests/distinct_on_spec.rb +65 -0
  60. data/spec/tests/enum_set_spec.rb +306 -0
  61. data/spec/tests/enum_spec.rb +621 -0
  62. data/spec/tests/geometric_builder_spec.rb +221 -0
  63. data/spec/tests/has_many_spec.rb +390 -0
  64. data/spec/tests/interval_spec.rb +167 -0
  65. data/spec/tests/lazy_spec.rb +24 -0
  66. data/spec/tests/period_spec.rb +954 -0
  67. data/spec/tests/quoting_spec.rb +24 -0
  68. data/spec/tests/range_spec.rb +36 -0
  69. data/spec/tests/relation_spec.rb +57 -0
  70. data/spec/tests/table_inheritance_spec.rb +403 -0
  71. metadata +103 -15
  72. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  73. data/lib/torque/postgresql/schema_dumper.rb +0 -101
@@ -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