acts_as_bookable 0.1.0

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +43 -0
  6. data/Appraisals +21 -0
  7. data/Gemfile +21 -0
  8. data/Gemfile.lock +168 -0
  9. data/Guardfile +5 -0
  10. data/MIT-LICENSE +20 -0
  11. data/README.md +473 -0
  12. data/Rakefile +19 -0
  13. data/acts_as_bookable.gemspec +41 -0
  14. data/app/assets/images/acts_as_bookable/.keep +0 -0
  15. data/app/assets/javascripts/acts_as_bookable/application.js +13 -0
  16. data/app/assets/stylesheets/acts_as_bookable/application.css +15 -0
  17. data/app/controllers/acts_as_bookable/application_controller.rb +4 -0
  18. data/app/helpers/acts_as_bookable/application_helper.rb +4 -0
  19. data/app/views/layouts/acts_as_bookable/application.html.erb +14 -0
  20. data/bin/rails +12 -0
  21. data/config/locales/en.yml +12 -0
  22. data/config/routes.rb +2 -0
  23. data/db/migrate/20160217085200_create_acts_as_bookable_bookings.rb +14 -0
  24. data/gemfiles/activerecord_3.2.gemfile +16 -0
  25. data/gemfiles/activerecord_4.0.gemfile +16 -0
  26. data/gemfiles/activerecord_4.1.gemfile +16 -0
  27. data/gemfiles/activerecord_4.2.gemfile +17 -0
  28. data/gemfiles/activerecord_5.0.gemfile +17 -0
  29. data/lib/acts_as_bookable/bookable/core.rb +285 -0
  30. data/lib/acts_as_bookable/bookable.rb +56 -0
  31. data/lib/acts_as_bookable/booker.rb +70 -0
  32. data/lib/acts_as_bookable/booking.rb +54 -0
  33. data/lib/acts_as_bookable/db_utils.rb +39 -0
  34. data/lib/acts_as_bookable/engine.rb +5 -0
  35. data/lib/acts_as_bookable/t.rb +11 -0
  36. data/lib/acts_as_bookable/time_utils.rb +135 -0
  37. data/lib/acts_as_bookable/version.rb +3 -0
  38. data/lib/acts_as_bookable.rb +42 -0
  39. data/lib/tasks/acts_as_bookable_tasks.rake +4 -0
  40. data/spec/acts_as_bookable/acts_as_bookable_spec.rb +52 -0
  41. data/spec/acts_as_bookable/acts_as_booker_spec.rb +57 -0
  42. data/spec/acts_as_bookable/bookable/core_spec.rb +593 -0
  43. data/spec/acts_as_bookable/bookable_spec.rb +124 -0
  44. data/spec/acts_as_bookable/booker_spec.rb +140 -0
  45. data/spec/acts_as_bookable/booking_spec.rb +241 -0
  46. data/spec/acts_as_bookable/schedule_spec.rb +137 -0
  47. data/spec/acts_as_bookable/time_utils_spec.rb +525 -0
  48. data/spec/factories/bookable.rb +7 -0
  49. data/spec/factories/booker.rb +5 -0
  50. data/spec/factories/room.rb +11 -0
  51. data/spec/internal/app/models/Bookable.rb +3 -0
  52. data/spec/internal/app/models/Booker.rb +3 -0
  53. data/spec/internal/app/models/Event.rb +3 -0
  54. data/spec/internal/app/models/Generic.rb +2 -0
  55. data/spec/internal/app/models/NotBooker.rb +2 -0
  56. data/spec/internal/app/models/Room.rb +3 -0
  57. data/spec/internal/app/models/Show.rb +3 -0
  58. data/spec/internal/app/models/Unbookable.rb +2 -0
  59. data/spec/internal/config/database.yml.sample +17 -0
  60. data/spec/internal/db/schema.rb +51 -0
  61. data/spec/spec_helper.rb +26 -0
  62. data/spec/support/0-helpers.rb +32 -0
  63. data/spec/support/1-database.rb +42 -0
  64. data/spec/support/2-database_cleaner.rb +21 -0
  65. data/spec/support/3-factory_girl.rb +12 -0
  66. metadata +296 -0
@@ -0,0 +1,525 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ActsAsBookable::TimeUtils' do
4
+
5
+ describe '#time_in_interval?' do
6
+ before :each do
7
+ @interval_start = Time.now
8
+ @interval_end = Time.now + 1.hour
9
+ end
10
+
11
+ describe 'returns true' do
12
+ it 'when time is the interval_start' do
13
+ time = @interval_start
14
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq true
15
+ end
16
+
17
+ it 'when time is bewteen interval_start and interval_end' do
18
+ time = @interval_start + 5.minutes
19
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq true
20
+ end
21
+
22
+ it 'when time is very close to interval end' do
23
+ time = @interval_end - 1.second
24
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq true
25
+ end
26
+
27
+ end
28
+
29
+ describe 'returns false' do
30
+ it 'when time is before interval_start' do
31
+ time = @interval_start - 1.second
32
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq false
33
+ end
34
+
35
+ it 'when time is after interval_end' do
36
+ time = @interval_end + 1.second
37
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq false
38
+ end
39
+
40
+ it 'when time is interval_end' do
41
+ time = @interval_end
42
+ expect(ActsAsBookable::TimeUtils.time_in_interval?(time,@interval_start,@interval_end)).to eq false
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#interval_in_schedule?' do
48
+ before :each do
49
+ @day0 = '2016-01-05'.to_date
50
+ @schedule = IceCube::Schedule.new(@day0,duration: 1.day)
51
+ @schedule.add_recurrence_rule IceCube::Rule.monthly.day_of_month(1,3,5,7)
52
+ end
53
+
54
+ describe 'returns true' do
55
+ it 'when range starts and ends in the middle of an occurrence' do
56
+ time_start = @day0 + 1.hour
57
+ time_end = @day0 + 3.hours
58
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq true
59
+ end
60
+
61
+ it 'when range starts and ends in the middle of another occurrence' do
62
+ time_start = @day0 + 2.days + 1.hour
63
+ time_end = @day0 + 2.days + 3.hours
64
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq true
65
+ end
66
+
67
+ it 'when range starts at the beginning of an occurrence and ends at the end of the same occurence' do
68
+ time_start = @day0
69
+ time_end = @day0 + 1.day - 1.second
70
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq true
71
+ end
72
+ end
73
+
74
+ describe 'retuns false' do
75
+ it 'when range starts and ends outside any occurrence' do
76
+ time_start = '2016-01-15'.to_date
77
+ time_end = time_start + 1.day
78
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
79
+ end
80
+
81
+ it 'when range starts and ends outside any occurrence but contains an occurrence' do
82
+ time_start = @day0 - 1.hour
83
+ time_end = @day0 + 1.day + 1.hour
84
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
85
+ end
86
+
87
+ it 'when range starts within an occurrence but ends outside it' do
88
+ time_start = @day0 + 1.hour
89
+ time_end = @day0 + 1.day + 1.hour
90
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
91
+ end
92
+
93
+ it 'when range starts outside any occurrence but ends within an occurrence' do
94
+ time_start = @day0 - 1.hour
95
+ time_end = @day0 + 1.hour
96
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
97
+ end
98
+
99
+ it 'when range starts within an occurrence and ends within a different occurrence' do
100
+ time_start = @day0 + 1.hour
101
+ time_end = @day0 + 2.days + 1.hour
102
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
103
+ end
104
+
105
+ it 'when range starts within an occurrence and ends just after the end of the same occurrence' do
106
+ time_start = @day0 + 1.hour
107
+ time_end = @day0 + 1.day
108
+ expect(ActsAsBookable::TimeUtils.interval_in_schedule?(@schedule,time_start,time_end)).to eq false
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#time_in_schedule?' do
114
+ before :each do
115
+ @day0 = '2016-01-05'.to_date
116
+ @schedule = IceCube::Schedule.new(@day0,duration: 1.day)
117
+ @schedule.add_recurrence_rule IceCube::Rule.monthly.day_of_month(1,3,5,7)
118
+ end
119
+
120
+ describe 'returns true' do
121
+ it 'when time is at the beginning of an occurrence' do
122
+ time = @day0
123
+ expect(ActsAsBookable::TimeUtils.time_in_schedule?(@schedule,time)).to eq true
124
+ end
125
+
126
+ it 'when time is in the middle of an occurrence' do
127
+ time = @day0 + 5.hours
128
+ expect(ActsAsBookable::TimeUtils.time_in_schedule?(@schedule,time)).to eq true
129
+ end
130
+
131
+ it 'when time is at the end of an occurrence' do
132
+ time = @day0 + 1.day - 1.second
133
+ expect(ActsAsBookable::TimeUtils.time_in_schedule?(@schedule, time)).to eq true
134
+ end
135
+ end
136
+
137
+ describe 'retuns false' do
138
+ it 'when time is outside an occurrence' do
139
+ time = '2016-01-15'.to_date
140
+ expect(ActsAsBookable::TimeUtils.time_in_schedule?(@schedule, time)).to eq false
141
+ end
142
+
143
+ it 'when time is close to the end of an occurrence, but outside it' do
144
+ time = @day0 + 1.day
145
+ expect(ActsAsBookable::TimeUtils.time_in_schedule?(@schedule, time)).to eq false
146
+ end
147
+
148
+ it 'when time is close to the beginning of an occurrence, but outside it' do
149
+ time = @day0 + 2.days - 1.second
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#subintervals' do
155
+ before :each do
156
+ @time = Time.now
157
+ end
158
+
159
+ it 'returns ArgumentError if called without an array' do
160
+ expect{ ActsAsBookable::TimeUtils.subintervals(1) }.to raise_error ArgumentError
161
+ end
162
+
163
+ it 'returns ArgumentError if an interval has no time_start or time_end' do
164
+ intervals = [
165
+ {time_start: @time, time_end: @time + 1.hour},
166
+ {time_start: @time}
167
+ ]
168
+ expect{ ActsAsBookable::TimeUtils.subintervals(1) }.to raise_error ArgumentError
169
+ intervals = [
170
+ {time_start: @time, time_end: @time + 1.hour},
171
+ {time_end: @time}
172
+ ]
173
+ expect{ ActsAsBookable::TimeUtils.subintervals(1) }.to raise_error ArgumentError
174
+ end
175
+
176
+ it 'returns ArgumentError if time_start or time_end is not a Time or Date' do
177
+ intervals = [
178
+ {time_start: @time, time_end: 1}
179
+ ]
180
+ expect{ ActsAsBookable::TimeUtils.subintervals(1) }.to raise_error ArgumentError
181
+ intervals = [
182
+ {time_start: 2, time_end: @time + 1.hour}
183
+ ]
184
+ expect{ ActsAsBookable::TimeUtils.subintervals(1) }.to raise_error ArgumentError
185
+ end
186
+
187
+ it 'returns empty array if input is an empty array' do
188
+ expect(ActsAsBookable::TimeUtils.subintervals([])).to eq []
189
+ end
190
+
191
+ # |----|
192
+ # =>
193
+ # |----|
194
+ it 'returns a copy of the same interval if input is a single interval' do
195
+ intervals = [
196
+ {time_start: @time, time_end: @time + 1.hour}
197
+ ]
198
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
199
+ expect(subintervals.length).to eq 1
200
+ expect(subintervals[0][:time_start]).to eq intervals[0][:time_start]
201
+ expect(subintervals[0][:time_end]).to eq intervals[0][:time_end]
202
+ end
203
+
204
+ # |----| |----| |----|
205
+ # =>
206
+ # |----| |----| |----|
207
+ it 'returns a copy of the same intervals if they are all separated' do
208
+ intervals = [
209
+ {time_start: @time, time_end: @time + 1.hour},
210
+ {time_start: @time + 2.hours, time_end: @time + 3.hours},
211
+ {time_start: @time + 4.hours, time_end: @time + 5.hours}
212
+ ]
213
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
214
+ expect(subintervals.length).to eq 3
215
+ (0..2).each do |i|
216
+ expect(subintervals[i][:time_start]).to eq intervals[i][:time_start]
217
+ expect(subintervals[i][:time_end]).to eq intervals[i][:time_end]
218
+ end
219
+ end
220
+
221
+ # |----|
222
+ # |----|
223
+ # |----|
224
+ # =>
225
+ # |----|
226
+ # |----|
227
+ # |----|
228
+ it 'returns the sub-intervals sorted' do
229
+ time0 = @time
230
+ time1 = @time + 1.hour
231
+ time2 = @time + 2.hours
232
+ time3 = @time + 3.hours
233
+ time4 = @time + 4.hours
234
+ time5 = @time + 5.hours
235
+ time6 = @time + 6.hours
236
+
237
+ intervals = [
238
+ {time_start: time4, time_end: time5},
239
+ {time_start: time2, time_end: time3},
240
+ {time_start: time0, time_end: time1}
241
+ ]
242
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
243
+ expect(subintervals.length).to eq 3
244
+ expect(subintervals[0][:time_start]).to eq time0
245
+ expect(subintervals[0][:time_end]).to eq time1
246
+ expect(subintervals[1][:time_start]).to eq time2
247
+ expect(subintervals[1][:time_end]).to eq time3
248
+ expect(subintervals[2][:time_start]).to eq time4
249
+ expect(subintervals[2][:time_end]).to eq time5
250
+ end
251
+
252
+ # |----|
253
+ # |----|
254
+ # |----|
255
+ # =>
256
+ # |----|
257
+ it 'merges intervals if they have same time_start and time_end' do
258
+ intervals = [
259
+ {time_start: @time, time_end: @time + 1.hour},
260
+ {time_start: @time, time_end: @time + 1.hour},
261
+ {time_start: @time, time_end: @time + 1.hour}
262
+ ]
263
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
264
+ expect(subintervals.length).to eq 1
265
+ expect(subintervals[0][:time_start]).to eq intervals[0][:time_start]
266
+ expect(subintervals[0][:time_end]).to eq intervals[0][:time_end]
267
+ end
268
+
269
+ # |---|
270
+ # |------|
271
+ # =>
272
+ # |---|
273
+ # |--|
274
+ it 'returns two intervals if input is 2 intervals with same time_start and different time_end' do
275
+ time0 = @time
276
+ time1 = @time + 1.hour
277
+ time2 = @time + 2.hours
278
+ intervals = [
279
+ {time_start: time0, time_end: time1},
280
+ {time_start: time0, time_end: time2}
281
+ ]
282
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
283
+ expect(subintervals.length).to eq 2
284
+ expect(subintervals[0][:time_start]).to eq time0
285
+ expect(subintervals[0][:time_end]).to eq time1
286
+ expect(subintervals[1][:time_start]).to eq time1
287
+ expect(subintervals[1][:time_end]).to eq time2
288
+ end
289
+
290
+ # |------|
291
+ # |---|
292
+ # =>
293
+ # |--|
294
+ # |---|
295
+ it 'returns two intervals if input is 2 intervals with same time_end and different time_start' do
296
+ time0 = @time
297
+ time1 = @time + 1.hour
298
+ time2 = @time + 2.hours
299
+ intervals = [
300
+ {time_start: time0, time_end: time2},
301
+ {time_start: time1, time_end: time2}
302
+ ]
303
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
304
+ expect(subintervals.length).to eq 2
305
+ expect(subintervals[0][:time_start]).to eq time0
306
+ expect(subintervals[0][:time_end]).to eq time1
307
+ expect(subintervals[1][:time_start]).to eq time1
308
+ expect(subintervals[1][:time_end]).to eq time2
309
+ end
310
+
311
+ # |---------|
312
+ # |---|
313
+ # =>
314
+ # |--|
315
+ # |---|
316
+ # |--|
317
+ it 'returns three intervals if one includes another' do
318
+ time0 = @time
319
+ time1 = @time + 1.hour
320
+ time2 = @time + 2.hours
321
+ time3 = @time + 3.hours
322
+ intervals = [
323
+ {time_start: time0, time_end: time3},
324
+ {time_start: time1, time_end: time2}
325
+ ]
326
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals)
327
+ expect(subintervals.length).to eq 3
328
+ expect(subintervals[0][:time_start]).to eq time0
329
+ expect(subintervals[0][:time_end]).to eq time1
330
+ expect(subintervals[1][:time_start]).to eq time1
331
+ expect(subintervals[1][:time_end]).to eq time2
332
+ expect(subintervals[2][:time_start]).to eq time2
333
+ expect(subintervals[2][:time_end]).to eq time3
334
+ end
335
+
336
+ # |---2---|
337
+ # |------4------|
338
+ # |----3------|
339
+ # |----1----|
340
+ # |----8----|
341
+ # =>
342
+ # |-5-|
343
+ # |-9-|
344
+ # |-7-|
345
+ # |--4--|
346
+ # |----9----|
347
+ it 'correctly merges interval attributes' do
348
+ time0 = @time
349
+ time1 = @time + 1.hour
350
+ time2 = @time + 2.hours
351
+ time3 = @time + 3.hours
352
+ time4 = @time + 4.hours
353
+ time5 = @time + 5.hours
354
+ time6 = @time + 6.hours
355
+ intervals = [
356
+ {time_start: time0, time_end: time2, attr: 2},
357
+ {time_start: time1, time_end: time4, attr: 4},
358
+ {time_start: time0, time_end: time3, attr: 3},
359
+ {time_start: time5, time_end: time6, attr: 1},
360
+ {time_start: time5, time_end: time6, attr: 8}
361
+ ]
362
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals) do |a,b,op|
363
+ if op == :open
364
+ res = {attr: a[:attr] + b[:attr]}
365
+ end
366
+ if op == :close
367
+ res = {attr: a[:attr] - b[:attr]}
368
+ end
369
+ res
370
+ end
371
+ expect(subintervals.length).to eq 5
372
+ expect(subintervals[0][:time_start]).to eq time0
373
+ expect(subintervals[0][:time_end]).to eq time1
374
+ expect(subintervals[0][:attr]).to eq 5
375
+ expect(subintervals[1][:time_start]).to eq time1
376
+ expect(subintervals[1][:time_end]).to eq time2
377
+ expect(subintervals[1][:attr]).to eq 9
378
+ expect(subintervals[2][:time_start]).to eq time2
379
+ expect(subintervals[2][:time_end]).to eq time3
380
+ expect(subintervals[2][:attr]).to eq 7
381
+ expect(subintervals[3][:time_start]).to eq time3
382
+ expect(subintervals[3][:time_end]).to eq time4
383
+ expect(subintervals[3][:attr]).to eq 4
384
+ expect(subintervals[4][:time_start]).to eq time5
385
+ expect(subintervals[4][:time_end]).to eq time6
386
+ expect(subintervals[4][:attr]).to eq 9
387
+ end
388
+
389
+ # |---2---|
390
+ # |------4------|
391
+ # |----3------|
392
+ # |----1----|
393
+ # |----8----|
394
+ # =>
395
+ # |-5-|
396
+ # |-9-|
397
+ # |-7-|
398
+ # |--4--|
399
+ # |----9----|
400
+ it 'correctly merges interval attributes handling dates and times' do
401
+ time0 = Date.today
402
+ time1 = time0 + 1
403
+ time2 = time0 + 2
404
+ time3 = time0 + 3
405
+ time4 = time0 + 4.days + 1.hours
406
+ time5 = time0 + 5.days + 1.hours
407
+ time6 = time0 + 6.days + 1.hours
408
+ intervals = [
409
+ {time_start: time0, time_end: time2, attr: 2},
410
+ {time_start: time1, time_end: time4, attr: 4},
411
+ {time_start: time0, time_end: time3, attr: 3},
412
+ {time_start: time5, time_end: time6, attr: 1},
413
+ {time_start: time5, time_end: time6, attr: 8}
414
+ ]
415
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals) do |a,b,op|
416
+ if op == :open
417
+ res = {attr: a[:attr] + b[:attr]}
418
+ end
419
+ if op == :close
420
+ res = {attr: a[:attr] - b[:attr]}
421
+ end
422
+ res
423
+ end
424
+ expect(subintervals.length).to eq 5
425
+ expect(subintervals[0][:time_start]).to eq time0
426
+ expect(subintervals[0][:time_end]).to eq time1
427
+ expect(subintervals[0][:attr]).to eq 5
428
+ expect(subintervals[1][:time_start]).to eq time1
429
+ expect(subintervals[1][:time_end]).to eq time2
430
+ expect(subintervals[1][:attr]).to eq 9
431
+ expect(subintervals[2][:time_start]).to eq time2
432
+ expect(subintervals[2][:time_end]).to eq time3
433
+ expect(subintervals[2][:attr]).to eq 7
434
+ expect(subintervals[3][:time_start]).to eq time3
435
+ expect(subintervals[3][:time_end]).to eq time4
436
+ expect(subintervals[3][:attr]).to eq 4
437
+ expect(subintervals[4][:time_start]).to eq time5
438
+ expect(subintervals[4][:time_end]).to eq time6
439
+ expect(subintervals[4][:attr]).to eq 9
440
+ end
441
+
442
+ # |---2---|
443
+ # |---4---|
444
+ # |---3---|
445
+ # |---5---|
446
+ # |---1---|
447
+ # |---1---|
448
+ # =>
449
+ # |---6---|
450
+ # |---10---|
451
+ it 'merges 3 intervals partially matching' do
452
+ time0 = @time
453
+ time1 = @time + 1.hour
454
+ time2 = @time + 2.hours
455
+ intervals = [
456
+ {time_start: time0, time_end: time1, attr: 2},
457
+ {time_start: time1, time_end: time2, attr: 4},
458
+ {time_start: time0, time_end: time1, attr: 3},
459
+ {time_start: time1, time_end: time2, attr: 5},
460
+ {time_start: time0, time_end: time1, attr: 1},
461
+ {time_start: time1, time_end: time2, attr: 1}
462
+ ]
463
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals) do |a,b,op|
464
+ if op == :open
465
+ res = {attr: a[:attr] + b[:attr]}
466
+ end
467
+ if op == :close
468
+ res = {attr: a[:attr] - b[:attr]}
469
+ end
470
+ res
471
+ end
472
+ expect(subintervals.length).to eq 2
473
+ expect(subintervals[0][:time_start]).to eq time0
474
+ expect(subintervals[0][:time_end]).to eq time1
475
+ expect(subintervals[0][:attr]).to eq 6
476
+ expect(subintervals[1][:time_start]).to eq time1
477
+ expect(subintervals[1][:time_end]).to eq time2
478
+ expect(subintervals[1][:attr]).to eq 10
479
+ end
480
+
481
+ # |---2----|
482
+ # |----4---|
483
+ # |---3----|
484
+ # |----5---|
485
+ # |---1----|
486
+ # |----1---|
487
+ # =>
488
+ # |-6-|
489
+ # |-16-|
490
+ # |-10-|
491
+ it 'merges and split 3 intervals partially matching' do
492
+ time0 = @time
493
+ time1 = @time + 1.hour
494
+ time2 = @time + 2.hours
495
+ time3 = @time + 3.hours
496
+ intervals = [
497
+ {time_start: time0, time_end: time2, attr: 2},
498
+ {time_start: time1, time_end: time3, attr: 4},
499
+ {time_start: time0, time_end: time2, attr: 3},
500
+ {time_start: time1, time_end: time3, attr: 5},
501
+ {time_start: time0, time_end: time2, attr: 1},
502
+ {time_start: time1, time_end: time3, attr: 1}
503
+ ]
504
+ subintervals = ActsAsBookable::TimeUtils.subintervals(intervals) do |a,b,op|
505
+ if op == :open
506
+ res = {attr: a[:attr] + b[:attr]}
507
+ end
508
+ if op == :close
509
+ res = {attr: a[:attr] - b[:attr]}
510
+ end
511
+ res
512
+ end
513
+ expect(subintervals.length).to eq 3
514
+ expect(subintervals[0][:time_start]).to eq time0
515
+ expect(subintervals[0][:time_end]).to eq time1
516
+ expect(subintervals[0][:attr]).to eq 6
517
+ expect(subintervals[1][:time_start]).to eq time1
518
+ expect(subintervals[1][:time_end]).to eq time2
519
+ expect(subintervals[1][:attr]).to eq 16
520
+ expect(subintervals[2][:time_start]).to eq time2
521
+ expect(subintervals[2][:time_end]).to eq time3
522
+ expect(subintervals[2][:attr]).to eq 10
523
+ end
524
+ end
525
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :bookable, class: 'Bookable' do
3
+ name 'Bookable name'
4
+ capacity 4
5
+ schedule IceCube::Schedule.new
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :booker, class: 'Booker' do
3
+ name 'Booker name'
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ FactoryGirl.define do
2
+ factory :room, class: 'Room' do
3
+ name 'Room name'
4
+ capacity 4
5
+ schedule {
6
+ schedule = IceCube::Schedule.new(Date.today, duration: 1.day)
7
+ schedule.add_recurrence_rule IceCube::Rule.daily
8
+ schedule
9
+ }
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class Bookable < ActiveRecord::Base
2
+ acts_as_bookable
3
+ end
@@ -0,0 +1,3 @@
1
+ class Booker < ActiveRecord::Base
2
+ acts_as_booker
3
+ end
@@ -0,0 +1,3 @@
1
+ class Event < ActiveRecord::Base
2
+ acts_as_bookable preset: :event
3
+ end
@@ -0,0 +1,2 @@
1
+ class Generic < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class NotBooker < ActiveRecord::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ class Room < ActiveRecord::Base
2
+ acts_as_bookable preset: :room
3
+ end
@@ -0,0 +1,3 @@
1
+ class Show < ActiveRecord::Base
2
+ acts_as_bookable preset: :show
3
+ end
@@ -0,0 +1,2 @@
1
+ class Unbookable < ActiveRecord::Base
2
+ end
@@ -0,0 +1,17 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: ':memory:'
4
+
5
+ mysql:
6
+ adapter: mysql2
7
+ host: localhost
8
+ username: root
9
+ database: acts_as_bookable
10
+ charset: utf8
11
+
12
+ postgresql:
13
+ adapter: postgresql
14
+ hostname: localhost
15
+ username: postgres
16
+ database: acts_as_bookable
17
+ encoding: utf8
@@ -0,0 +1,51 @@
1
+ ActiveRecord::Schema.define version: 0 do
2
+ create_table :acts_as_bookable_bookings, force: true do |t|
3
+ t.references :bookable, polymorphic: true
4
+ t.references :booker, polymorphic: true
5
+ t.column :amount, :integer
6
+ t.column :schedule, :text
7
+ t.column :time_start, :datetime
8
+ t.column :time_end, :datetime
9
+ t.column :time, :datetime
10
+ t.datetime :created_at
11
+ end
12
+
13
+ create_table :bookables, force: true do |t|
14
+ t.column :name, :string
15
+ t.column :schedule, :text
16
+ t.column :capacity, :integer
17
+ end
18
+
19
+ create_table :rooms, force: true do |t|
20
+ t.column :name, :string
21
+ t.column :schedule, :text
22
+ t.column :capacity, :integer
23
+ end
24
+
25
+ create_table :events, force: true do |t|
26
+ t.column :name, :string
27
+ t.column :capacity, :integer
28
+ end
29
+
30
+ create_table :shows, force: true do |t|
31
+ t.column :name, :string
32
+ t.column :schedule, :text
33
+ t.column :capacity, :integer
34
+ end
35
+
36
+ create_table :unbookables, force: true do |t|
37
+ t.column :name, :string
38
+ end
39
+
40
+ create_table :not_bookers, force: true do |t|
41
+ t.column :name, :string
42
+ end
43
+
44
+ create_table :generics, force: true do |t|
45
+ t.column :name, :string
46
+ end
47
+
48
+ create_table :bookers, force: true do |t|
49
+ t.column :name, :string
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'pry-nav'
3
+ rescue LoadError
4
+ end
5
+ $LOAD_PATH << '.' unless $LOAD_PATH.include?('.')
6
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
7
+ require 'logger'
8
+
9
+ require File.expand_path('../../lib/acts_as_bookable', __FILE__)
10
+ I18n.enforce_available_locales = true
11
+ require 'rails'
12
+ require 'barrier'
13
+ require 'database_cleaner'
14
+ require 'factory_girl_rails'
15
+ require 'awesome_print'
16
+
17
+ ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
18
+ Dir[File.join(ENGINE_RAILS_ROOT, "spec/factories/**/*.rb")].each {|f| require f }
19
+
20
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
21
+
22
+ I18n.load_path << File.expand_path("../../config/locales/en.yml", __FILE__)
23
+
24
+ RSpec.configure do |config|
25
+ config.raise_errors_for_deprecations!
26
+ end