availability 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -9
- data/availability.gemspec +2 -0
- data/examples/scheduler_spec.rb +1 -12
- data/lib/availability.rb +1 -0
- data/lib/availability/abstract_availability.rb +17 -13
- data/lib/availability/exclusion.rb +50 -1
- data/lib/availability/factory_methods.rb +1 -1
- data/lib/availability/hourly.rb +29 -0
- data/lib/availability/once.rb +4 -0
- data/lib/availability/version.rb +1 -1
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3be1734eee2d550422776a8d399236e4e9fdd2ca
|
4
|
+
data.tar.gz: 4c98f924c2a0a12daafb846886a8be06a4a85358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d334f811b0745a5774ca32a5e0357694abf6b72973d67c59668b93c06b44bd8b640ae5dedeae529d164265273bb4307dc94418a59d405cd6b2cd523a18fbf13
|
7
|
+
data.tar.gz: 71ff6a5d558627ce59c4bfafdb6e81102ea60c7e8dda203ed30b9d52513559670b2f224dc7163202ffbc604b65cac33b75a9a3e1ecdcd58cf9dbd5bad5159629
|
data/README.md
CHANGED
@@ -20,9 +20,9 @@ There are a few convenience factory methods beyond the main factory methods:
|
|
20
20
|
- `Availability.every_two_{days,weeks,months,years}` and `Availability.every_other_{day,week,month,year}` will create the appropriate availability with an interval of 2
|
21
21
|
- There are other `every_*` factory methods for intervals of 3, 4, and 5 (e.g. `every_three_months`)
|
22
22
|
|
23
|
-
##
|
23
|
+
## Basic Usage
|
24
24
|
|
25
|
-
### [#
|
25
|
+
### [#includes?/1][INCLUDES]
|
26
26
|
This method takes a date or time and responds with a boolean indicating whether or not it is covered by the receiver.
|
27
27
|
|
28
28
|
### [#corresponds_to?/1][CORRESPONDS_TO]
|
@@ -40,14 +40,55 @@ This returns a single time object for the next occurrence on or after the given
|
|
40
40
|
## Examples
|
41
41
|
|
42
42
|
```ruby
|
43
|
-
# Every Monday from 9:00 AM to 10:00 AM starting on May 2, 2016
|
44
|
-
Availability.every_other_week(start_time: Time.new(2016, 5, 2, 9), duration: 1.hour)
|
43
|
+
# Every other Monday from 9:00 AM to 10:00 AM starting on May 2, 2016
|
44
|
+
every_other_monday = Availability.every_other_week(start_time: Time.new(2016, 5, 2, 9), duration: 1.hour)
|
45
|
+
every_other_monday.includes? Time.new(2016, 5, 30, 9) # => true
|
46
|
+
every_other_monday.includes? Time.new(2016, 5, 30, 10) # => false, because it lasts only an hour
|
47
|
+
every_other_monday.includes? Time.new(2016, 5, 23, 9) # => false, because it's not a covered Monday
|
48
|
+
every_other_monday.includes? Time.new(2016, 5, 18, 9) # => false, because it's not a Monday
|
45
49
|
|
46
50
|
# A business week starting on May 2, 2016 going from 1:30 PM until 2:00 PM every day
|
47
|
-
Availability.daily(start_time: Time.new(2016, 5, 2, 13, 30), stops_by: Time.new(2016, 5, 6), duration: 30.minutes)
|
51
|
+
biz_week = Availability.daily(start_time: Time.new(2016, 5, 2, 13, 30), stops_by: Time.new(2016, 5, 6), duration: 30.minutes)
|
52
|
+
|
53
|
+
biz_week.includes? Time.new(2016, 5, 3, 13, 30) #=> true
|
54
|
+
biz_week.includes? Time.new(2016, 5, 3, 14, 30) #=> false
|
55
|
+
biz_week.includes? Time.new(2016, 5, 6, 13, 30) #=> true
|
48
56
|
|
49
57
|
# A semi-monthly availability occurring all day, without an end
|
50
|
-
Availability.every_other_month(start_time: Time.new(2016, 1, 1), duration: 1.day)
|
58
|
+
every_other_month = Availability.every_other_month(start_time: Time.new(2016, 1, 1), duration: 1.day)
|
59
|
+
|
60
|
+
every_other_month.includes? Time.new(2016, 3, 1) #=> true
|
61
|
+
every_other_month.includes? Time.new(4037, 7, 1) #=> true
|
62
|
+
```
|
63
|
+
|
64
|
+
Exclusion rules can be added to an availability to further restrict it. For instance, if you wanted to create an availability for business days that spanned more than a single week you might do something like the following (note that exclusion rules need only to respond to `violated_by?(time)`).
|
65
|
+
```ruby
|
66
|
+
class BusinessDayRule
|
67
|
+
def initialize
|
68
|
+
@not_on_sunday = Availability::Exclusion.on_day_of_week(0)
|
69
|
+
@not_on_saturday = Availability::Exclusion.on_day_of_week(6)
|
70
|
+
@after_work_hours = Availability::Exclusion.after_time(Time.parse('18:00'))
|
71
|
+
@before_work_hours = Availability::Exclusion.before_time(Time.parse('08:00'))
|
72
|
+
end
|
73
|
+
|
74
|
+
def violated_by?(time)
|
75
|
+
@not_on_saturday.violated_by?(time) ||
|
76
|
+
@not_on_sunday.violated_by?(time) ||
|
77
|
+
@after_work_hours.violated_by?(time) ||
|
78
|
+
@before_work_hours.violated_by?(time)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
business_days = Availability.daily(
|
83
|
+
start_time: Time.new(2016, 1, 1, 8),
|
84
|
+
duration: 1.hour,
|
85
|
+
exclusions: [Availability::Exclusion.new(BusinessDayRule.new)]
|
86
|
+
)
|
87
|
+
|
88
|
+
business_days.includes? Time.new(2016, 5, 2, 8) #=> true
|
89
|
+
business_days.includes? Time.new(2016, 5, 2, 10) #=> true
|
90
|
+
business_days.includes? Time.new(2016, 5, 2, 7) #=> false
|
91
|
+
business_days.includes? Time.new(2016, 5, 2, 18) #=> false
|
51
92
|
```
|
52
93
|
|
53
94
|
## TODO
|
@@ -89,8 +130,8 @@ see <http://unlicense.org/> or the accompanying [UNLICENSE]{UNLICENSE} file.
|
|
89
130
|
[YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
90
131
|
[PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
|
91
132
|
[DOCS]: http://www.rubydoc.info/gems/availability
|
92
|
-
[
|
133
|
+
[INCLUDES]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#includes%3F-instance_method
|
93
134
|
[CORRESPONDS_TO]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#corresponds_to%3F-instance_method
|
94
135
|
[LAST_OCCURRENCE]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#last_occurrence-instance_method
|
95
|
-
[NEXT_N_OCCURS]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#next_n_occurrences
|
96
|
-
[NEXT_OCCUR]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#next_occurrence
|
136
|
+
[NEXT_N_OCCURS]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#next_n_occurrences-instance_method
|
137
|
+
[NEXT_OCCUR]: http://www.rubydoc.info/gems/availability/Availability/AbstractAvailability#next_occurrence-instance_method
|
data/availability.gemspec
CHANGED
@@ -26,4 +26,6 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
27
|
spec.add_development_dependency "rspec"
|
28
28
|
spec.add_development_dependency "rspec-its"
|
29
|
+
spec.add_development_dependency "pry"
|
30
|
+
spec.add_development_dependency "pry-byebug"
|
29
31
|
end
|
data/examples/scheduler_spec.rb
CHANGED
@@ -71,19 +71,8 @@ RSpec.describe Scheduler do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
describe '#allow?' do
|
74
|
-
context 'enforces parameter values' do
|
75
|
-
it { expect{bobs_schedule.allow?}.to raise_error ArgumentError }
|
76
|
-
it { expect{bobs_schedule.allow? availability_request: nil}.to raise_error ArgumentError }
|
77
|
-
it { expect{bobs_schedule.allow? start_time: nil }.to raise_error ArgumentError }
|
78
|
-
it { expect{bobs_schedule.allow? start_time: Date.today }.to raise_error ArgumentError }
|
79
|
-
it { expect{bobs_schedule.allow? end_time: nil }.to raise_error ArgumentError }
|
80
|
-
it { expect{bobs_schedule.allow? end_time: Date.today }.to raise_error ArgumentError }
|
81
|
-
it { expect{bobs_schedule.allow? availability_request: one_hour_slot_per_week(start_time: T(0))}.not_to raise_error }
|
82
|
-
it { expect{bobs_schedule.allow? start_time: Date.today, end_time: Date.tomorrow}.not_to raise_error }
|
83
|
-
end
|
84
|
-
|
85
74
|
context 'with Availability objects' do
|
86
|
-
it 'allows an event
|
75
|
+
it 'allows an event request that starts in the first week and goes through the end of the availability' do
|
87
76
|
bob_availabilities.each do |a|
|
88
77
|
expect(bobs_schedule.allow? availability_request: a).to be_truthy, "at #{a.start_time}"
|
89
78
|
end
|
data/lib/availability.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative "availability/version"
|
|
4
4
|
require_relative 'availability/createable'
|
5
5
|
require_relative 'availability/exclusion'
|
6
6
|
require_relative 'availability/abstract_availability'
|
7
|
+
require_relative 'availability/hourly'
|
7
8
|
require_relative 'availability/daily'
|
8
9
|
require_relative 'availability/weekly'
|
9
10
|
require_relative 'availability/monthly'
|
@@ -51,13 +51,14 @@ module Availability
|
|
51
51
|
# @return [Boolean] true or false
|
52
52
|
#
|
53
53
|
def corresponds_to?(availability)
|
54
|
-
return false unless
|
54
|
+
return false unless includes?(availability.start_time) \
|
55
|
+
&& includes?(availability.start_time + availability.duration - 1.second)
|
55
56
|
if !!stops_by
|
56
57
|
that_last = availability.last_occurrence
|
57
|
-
!that_last.nil?
|
58
|
-
|
59
|
-
|
60
|
-
that_last.to_date <= self.last_occurrence.to_date
|
58
|
+
!that_last.nil? \
|
59
|
+
&& includes?(that_last) \
|
60
|
+
&& includes?(that_last + availability.duration - 1.second) \
|
61
|
+
&& that_last.to_date <= self.last_occurrence.to_date
|
61
62
|
else
|
62
63
|
true
|
63
64
|
end
|
@@ -66,12 +67,15 @@ module Availability
|
|
66
67
|
#
|
67
68
|
# Whether or not the given time is covered by the receiver
|
68
69
|
#
|
69
|
-
# @param [Time] time the
|
70
|
+
# @param [Time] time the Time to test for coverage
|
70
71
|
#
|
71
72
|
# @return [Boolean] true or false
|
72
73
|
#
|
73
|
-
def
|
74
|
-
|
74
|
+
def includes?(time)
|
75
|
+
next_occurrence = next_occurrence(time) || last_occurrence
|
76
|
+
residue_for(time) == @residue \
|
77
|
+
&& !next_occurrence.nil? \
|
78
|
+
&& time_overlaps?(time, next_occurrence, next_occurrence + duration)
|
75
79
|
end
|
76
80
|
|
77
81
|
# @!endgroup
|
@@ -105,7 +109,7 @@ module Availability
|
|
105
109
|
residue = @residue - residue_for(from_date)
|
106
110
|
date = move_by from_date, residue.modulo(interval)
|
107
111
|
time = Time.new(date.year, date.month, date.day, start_time.hour, start_time.min, start_time.sec)
|
108
|
-
if exclusions.
|
112
|
+
if (exx = exclusions.detect {|rule| rule.violated_by? time})
|
109
113
|
if stops_by && time > stops_by
|
110
114
|
nil
|
111
115
|
else
|
@@ -173,10 +177,10 @@ module Availability
|
|
173
177
|
# @!group Helpers
|
174
178
|
|
175
179
|
def time_overlaps?(time, start_time, end_time)
|
176
|
-
that_start = time.
|
177
|
-
this_start = start_time.
|
178
|
-
this_end = end_time.
|
179
|
-
(this_start
|
180
|
+
that_start = time.to_i
|
181
|
+
this_start = start_time.to_i
|
182
|
+
this_end = end_time.to_i
|
183
|
+
(this_start...this_end).include?(that_start)
|
180
184
|
end
|
181
185
|
|
182
186
|
# @!endgroup
|
@@ -1,12 +1,17 @@
|
|
1
1
|
module Availability
|
2
2
|
class Exclusion
|
3
|
-
|
3
|
+
attr_reader :rule
|
4
4
|
|
5
5
|
def self.after_day(date)
|
6
6
|
raise ArgumentError, "invalid date" if date.nil?
|
7
7
|
new Rule::AfterDate.new(date.to_date)
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.after_date_and_time(time)
|
11
|
+
raise ArgumentError, "invalid time" if time.nil?
|
12
|
+
new Rule::AfterDateAndTime.new(time.to_time)
|
13
|
+
end
|
14
|
+
|
10
15
|
def self.after_time(time)
|
11
16
|
raise ArgumentError, "invalid time" if time.nil?
|
12
17
|
new Rule::AfterTime.new(time.to_time)
|
@@ -22,11 +27,23 @@ module Availability
|
|
22
27
|
new Rule::BeforeDate.new(date.to_date)
|
23
28
|
end
|
24
29
|
|
30
|
+
def self.before_date_and_time(time)
|
31
|
+
raise ArgumentError, "invalid time" if time.nil?
|
32
|
+
new Rule::BeforeDateAndTime.new(time.to_time)
|
33
|
+
end
|
34
|
+
|
25
35
|
def self.before_time(time)
|
26
36
|
raise ArgumentError, "invalid time" if time.nil?
|
27
37
|
new Rule::BeforeTime.new(time.to_time)
|
28
38
|
end
|
29
39
|
|
40
|
+
def self.on_day_of_week(day_of_week) # 0=Sunday, 6=Saturday
|
41
|
+
unless day_of_week.is_a?(Fixnum) && (0..6).include?(day_of_week)
|
42
|
+
raise ArgumentError, "invalid day of week"
|
43
|
+
end
|
44
|
+
new Rule::OnDayOfWeek.new(day_of_week)
|
45
|
+
end
|
46
|
+
|
30
47
|
def initialize(rule)
|
31
48
|
@rule = rule
|
32
49
|
end
|
@@ -47,6 +64,17 @@ module Availability
|
|
47
64
|
end
|
48
65
|
end
|
49
66
|
|
67
|
+
class AfterDateAndTime
|
68
|
+
def initialize(time)
|
69
|
+
@after_date = AfterDate.new time.to_date
|
70
|
+
@after_time = AfterTime.new time
|
71
|
+
end
|
72
|
+
|
73
|
+
def violated_by?(time)
|
74
|
+
@after_date.violated_by?(time) || @after_time.violated_by?(time)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
50
78
|
class AfterTime
|
51
79
|
def initialize(date_or_time)
|
52
80
|
@compare_to = date_or_time.to_time
|
@@ -67,6 +95,17 @@ module Availability
|
|
67
95
|
end
|
68
96
|
end
|
69
97
|
|
98
|
+
class BeforeDateAndTime
|
99
|
+
def initialize(time)
|
100
|
+
@before_date = BeforeDate.new time.to_date
|
101
|
+
@before_time = BeforeTime.new time
|
102
|
+
end
|
103
|
+
|
104
|
+
def violated_by?(time)
|
105
|
+
@before_date.violated_by?(time) || @before_time.violated_by?(time)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
70
109
|
class BeforeTime
|
71
110
|
def initialize(date_or_time)
|
72
111
|
@compare_to = date_or_time.to_time
|
@@ -86,6 +125,16 @@ module Availability
|
|
86
125
|
time.to_date == @date
|
87
126
|
end
|
88
127
|
end
|
128
|
+
|
129
|
+
class OnDayOfWeek
|
130
|
+
def initialize(day_of_week)
|
131
|
+
@day_of_week = day_of_week
|
132
|
+
end
|
133
|
+
|
134
|
+
def violated_by?(time)
|
135
|
+
time.wday == @day_of_week
|
136
|
+
end
|
137
|
+
end
|
89
138
|
end
|
90
139
|
end
|
91
140
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'abstract_availability'
|
2
|
+
require_relative 'daily'
|
3
|
+
|
4
|
+
module Availability
|
5
|
+
class Hourly < Daily
|
6
|
+
extend Createable
|
7
|
+
|
8
|
+
def interval_difference(this, that)
|
9
|
+
first, second = [this, that].sort
|
10
|
+
(second.to_i - first.to_i) / 1.hour
|
11
|
+
end
|
12
|
+
|
13
|
+
def move_by(time, amount)
|
14
|
+
time + amount.hours
|
15
|
+
end
|
16
|
+
|
17
|
+
def includes?(time)
|
18
|
+
return true if super
|
19
|
+
return false if residue_for(time) != residue
|
20
|
+
hours_on_same_day = next_n_occurrences(24, time).select {|t| t.wday == time.wday && t <= time }
|
21
|
+
hours_on_same_day.none? {|hour| exclusions.any?{|excl| excl.violated_by? hour}} &&
|
22
|
+
hours_on_same_day.any?{|hour| time_overlaps? time, hour, hour + duration}
|
23
|
+
end
|
24
|
+
|
25
|
+
def residue_for(time)
|
26
|
+
interval_difference(time, beginning.to_time).modulo(@interval)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/availability/once.rb
CHANGED
data/lib/availability/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: availability
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Rogers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
description: Use modular arithmetic and residue classes to calculate schedule availability
|
84
112
|
for dates (times handled separately).
|
85
113
|
email:
|
@@ -113,6 +141,7 @@ files:
|
|
113
141
|
- lib/availability/daily.rb
|
114
142
|
- lib/availability/exclusion.rb
|
115
143
|
- lib/availability/factory_methods.rb
|
144
|
+
- lib/availability/hourly.rb
|
116
145
|
- lib/availability/monthly.rb
|
117
146
|
- lib/availability/once.rb
|
118
147
|
- lib/availability/version.rb
|