blackcal 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +3 -0
- data/CHANGELOG.md +8 -0
- data/README.md +15 -10
- data/blackcal.gemspec +3 -0
- data/lib/blackcal/range/day_range.rb +12 -1
- data/lib/blackcal/range/month_range.rb +12 -0
- data/lib/blackcal/range/time_of_day_range.rb +79 -11
- data/lib/blackcal/range/time_range.rb +22 -0
- data/lib/blackcal/range/weekday_range.rb +12 -0
- data/lib/blackcal/schedule.rb +37 -17
- data/lib/blackcal/slot_matrix.rb +3 -0
- data/lib/blackcal/time_of_day.rb +36 -0
- data/lib/blackcal/version.rb +2 -1
- metadata +46 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db977cf2c574a0e8d76891abfa30a6b8140e5ec07bfc0f2b23a215b03732d166
|
4
|
+
data.tar.gz: 8b3deb0295ba37f2ff1440cf8e4eed055dcdd77ac728a25e26b48edce491e381
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f13e108edab2e980c18b9650145b084bf01ec391e007d78612991a4a4337d47040a7dae5bcce4ad61acc8ddcb786809bafbdea3b6cf743d5e4e14c7b26035e03
|
7
|
+
data.tar.gz: 31e1e04b3929f06519a67a855e276b3771fc360b4e889d3f35b7bc5b3f4e0928617c8dfe2f0edd0cccca671b95e54d9d01c532cbaa4d7f08a145d219ed903271
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -24,27 +24,27 @@ Schedule Mondays and Tuesdays
|
|
24
24
|
```ruby
|
25
25
|
schedule = Blackcal.schedule(weekdays: [:monday, :tuesday])
|
26
26
|
schedule.cover?('2019-01-01 19:00')
|
27
|
-
# => false
|
28
|
-
schedule.cover?('2019-01-02 11:00')
|
29
27
|
# => true
|
28
|
+
schedule.cover?('2019-01-02 19:00')
|
29
|
+
# => false
|
30
30
|
```
|
31
31
|
|
32
32
|
Schedule between 6pm and 7am every day
|
33
33
|
```ruby
|
34
34
|
schedule = Blackcal.schedule(start_hour: 18, finish_hour: 7)
|
35
35
|
schedule.cover?('2019-01-01 19:00')
|
36
|
-
# => false
|
37
|
-
schedule.cover?('2019-01-01 11:00')
|
38
36
|
# => true
|
37
|
+
schedule.cover?('2019-01-01 11:00')
|
38
|
+
# => false
|
39
39
|
```
|
40
40
|
|
41
41
|
Schedule day 15 and 17 of month
|
42
42
|
```ruby
|
43
43
|
schedule = Blackcal.schedule(days: [15, 17])
|
44
44
|
schedule.cover?('2019-01-15 19:00')
|
45
|
-
# => false
|
46
|
-
schedule.cover?('2019-01-01 11:00')
|
47
45
|
# => true
|
46
|
+
schedule.cover?('2019-01-01 11:00')
|
47
|
+
# => false
|
48
48
|
```
|
49
49
|
|
50
50
|
All options at once - schedule January, Mondays and Tuesdays, day 15-25, between 18pm and 7am
|
@@ -56,9 +56,9 @@ schedule = Blackcal.schedule(
|
|
56
56
|
days: (15..25).to_a
|
57
57
|
)
|
58
58
|
schedule.cover?('2019-01-15 19:00')
|
59
|
-
# => false
|
60
|
-
schedule.cover?('2019-02-01 11:00')
|
61
59
|
# => true
|
60
|
+
schedule.cover?('2019-02-01 11:00')
|
61
|
+
# => false
|
62
62
|
```
|
63
63
|
|
64
64
|
Define when the schedule is active
|
@@ -68,9 +68,14 @@ Blackcal.schedule(start_time: '2018-01-01 11:00', finish_time: '2019-01-01 11:00
|
|
68
68
|
|
69
69
|
Matrix representation
|
70
70
|
```ruby
|
71
|
-
schedule = Blackcal.schedule(weekdays: :friday, start_hour:
|
71
|
+
schedule = Blackcal.schedule(weekdays: :friday, start_hour: 0, finish_hour: 14)
|
72
72
|
schedule.to_matrix(start_date: '2018-09-14', finish_date: '2018-09-16')
|
73
|
-
# => [[true, ...], [
|
73
|
+
# => [[true, ...], [false, ...]]
|
74
|
+
|
75
|
+
# defaults to hour resolution, but you can get minute resolution too
|
76
|
+
schedule = Blackcal.schedule(weekdays: :friday, start_hour: 0, finish_hour: 14)
|
77
|
+
schedule.to_matrix(resolution: :min, start_date: '2018-09-14', finish_date: '2018-09-16')
|
78
|
+
# => [[true, ...], [false, ...]]
|
74
79
|
```
|
75
80
|
|
76
81
|
## Development
|
data/blackcal.gemspec
CHANGED
@@ -24,6 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
+
spec.add_development_dependency 'github-markup', '~> 2.0'
|
28
|
+
spec.add_development_dependency 'redcarpet', '~> 3.4'
|
29
|
+
spec.add_development_dependency 'simplecov', '~> 0.16'
|
27
30
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
28
31
|
spec.add_development_dependency 'byebug'
|
29
32
|
spec.add_development_dependency 'rake', '~> 10.0'
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module Blackcal
|
4
4
|
# Number range
|
5
5
|
class DayRange
|
6
|
-
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @return [Array<Integer>] numbers in range
|
7
9
|
attr_reader :numbers
|
8
10
|
|
9
11
|
# Initialize numbers range
|
@@ -23,5 +25,14 @@ module Blackcal
|
|
23
25
|
|
24
26
|
numbers.include?(timestamp.day)
|
25
27
|
end
|
28
|
+
|
29
|
+
# @return [Array<Integer>] numbers in range
|
30
|
+
alias_method :to_a, :numbers
|
31
|
+
|
32
|
+
# Iterate over range
|
33
|
+
# @see #to_a
|
34
|
+
def each(&block)
|
35
|
+
to_a.each(&block)
|
36
|
+
end
|
26
37
|
end
|
27
38
|
end
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module Blackcal
|
4
4
|
# Month range
|
5
5
|
class MonthRange
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Map month name to number
|
6
9
|
MONTH_MAP = {
|
7
10
|
january: 1,
|
8
11
|
february: 2,
|
@@ -40,5 +43,14 @@ module Blackcal
|
|
40
43
|
MONTH_MAP.fetch(month) == timestamp.month
|
41
44
|
end
|
42
45
|
end
|
46
|
+
|
47
|
+
# @return [Array<Symbol>] months in range
|
48
|
+
alias_method :to_a, :months
|
49
|
+
|
50
|
+
# Iterate over range
|
51
|
+
# @see #to_a
|
52
|
+
def each(&block)
|
53
|
+
to_a.each(&block)
|
54
|
+
end
|
43
55
|
end
|
44
56
|
end
|
@@ -1,26 +1,94 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'blackcal/time_of_day'
|
4
|
+
|
3
5
|
module Blackcal
|
4
6
|
# Time of day range
|
5
7
|
class TimeOfDayRange
|
8
|
+
include Enumerable
|
9
|
+
|
6
10
|
# Initialize time of day range
|
11
|
+
# @param [TimeOfDay, Time, Integer, nil] start
|
12
|
+
# @param [TimeOfDay, Time, Integer, nil] finish
|
7
13
|
def initialize(start, finish = nil)
|
8
|
-
@start = start
|
9
|
-
@finish = finish
|
10
|
-
|
11
|
-
@disallowed_hours = if @finish < @start
|
12
|
-
(@start..23).to_a + (0..@finish).to_a
|
13
|
-
else
|
14
|
-
(@start..@finish).to_a
|
15
|
-
end
|
14
|
+
@start = start
|
15
|
+
@finish = finish
|
16
16
|
end
|
17
17
|
|
18
18
|
# Returns true if it covers timestamp
|
19
19
|
# @return [Boolean]
|
20
20
|
def cover?(timestamp)
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
return false if @start.nil? && @finish.nil?
|
22
|
+
|
23
|
+
t1 = TimeOfDay.new(timestamp.hour, timestamp.min)
|
24
|
+
if start == finish
|
25
|
+
t1 == start
|
26
|
+
elsif start > finish
|
27
|
+
t1 <= finish || t1 >= start
|
28
|
+
else
|
29
|
+
t1 >= start && t1 <= finish
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return start hour
|
34
|
+
# @return [TimeOfDay]
|
35
|
+
def start
|
36
|
+
@start_time ||= to_time_of_day(@start || 0)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return finish hour
|
40
|
+
# @return [TimeOfDay]
|
41
|
+
def finish
|
42
|
+
@finish_time ||= to_time_of_day(@finish || 0)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns range as an array
|
46
|
+
# @param resolution [Symbol] :hour our :min
|
47
|
+
# @return [Array<Array<Integer>>] times
|
48
|
+
def to_a(resolution: :hour)
|
49
|
+
return [] if @start.nil? && @finish.nil?
|
50
|
+
|
51
|
+
if finish < start
|
52
|
+
to_time_array(start, TimeOfDay.new(23), resolution) +
|
53
|
+
to_time_array(TimeOfDay.new(0), finish, resolution)
|
54
|
+
else
|
55
|
+
to_time_array(start, finish, resolution)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Iterate over range
|
60
|
+
# @param resolution [Symbol] :hour our :min
|
61
|
+
def each(resolution: :hour, &block)
|
62
|
+
to_a(resolution: resolution).each(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def to_time_array(start, finish, resolution)
|
68
|
+
if resolution == :hour
|
69
|
+
return (start.hour..finish.hour).map { |hour| [hour, 0] }
|
70
|
+
end
|
71
|
+
|
72
|
+
# minute resolution
|
73
|
+
times = []
|
74
|
+
(start.hour..finish.hour).each do |hour|
|
75
|
+
finish_min = if hour == finish.hour && finish.min != 0
|
76
|
+
finish.min
|
77
|
+
else
|
78
|
+
59
|
79
|
+
end
|
80
|
+
(start.min..finish_min).each { |min| times << [hour, min] }
|
81
|
+
end
|
82
|
+
times
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_time_of_day(number_or_day)
|
86
|
+
return number_or_day if number_or_day.is_a?(TimeOfDay)
|
87
|
+
if number_or_day.is_a?(Time)
|
88
|
+
return TimeOfDay.new(number_or_day.hour, number_or_day.min)
|
89
|
+
end
|
90
|
+
|
91
|
+
TimeOfDay.new(number_or_day)
|
24
92
|
end
|
25
93
|
end
|
26
94
|
end
|
@@ -3,9 +3,13 @@
|
|
3
3
|
module Blackcal
|
4
4
|
# Time range
|
5
5
|
class TimeRange
|
6
|
+
include Enumerable
|
7
|
+
|
6
8
|
attr_reader :start, :finish
|
7
9
|
|
8
10
|
# Initialize time range
|
11
|
+
# @param [Time, nil] start_time
|
12
|
+
# @param [Time, nil] finish_time optional
|
9
13
|
def initialize(start_time, finish_time = nil)
|
10
14
|
@start = start_time
|
11
15
|
@finish = finish_time
|
@@ -23,5 +27,23 @@ module Blackcal
|
|
23
27
|
|
24
28
|
false
|
25
29
|
end
|
30
|
+
|
31
|
+
# Returns range as array
|
32
|
+
# @param resolution [Symbol] :hour our :min
|
33
|
+
# @return [Array<Array<Integer>>] times
|
34
|
+
def to_a(resolution: :hour)
|
35
|
+
resolution_multiplier = resolution == :hour ? 60 * 60 : 60
|
36
|
+
time_units = ((start - finish) / resolution_multiplier).abs.to_i
|
37
|
+
|
38
|
+
time_units.times.map do |time_unit|
|
39
|
+
start + (time_unit * resolution_multiplier)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Iterate over range
|
44
|
+
# @param resolution [Symbol] :hour our :min
|
45
|
+
def each(resolution: :hour, &block)
|
46
|
+
to_a(resolution: resolution).each(&block)
|
47
|
+
end
|
26
48
|
end
|
27
49
|
end
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module Blackcal
|
4
4
|
# Weekday range
|
5
5
|
class WeekdayRange
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Map weekday name to number
|
6
9
|
WEEKDAY_MAP = {
|
7
10
|
sunday: 0,
|
8
11
|
monday: 1,
|
@@ -35,5 +38,14 @@ module Blackcal
|
|
35
38
|
WEEKDAY_MAP.fetch(weekday) == timestamp.wday
|
36
39
|
end
|
37
40
|
end
|
41
|
+
|
42
|
+
# @return [Array<Symbol>] weekdays in range
|
43
|
+
alias_method :to_a, :weekdays
|
44
|
+
|
45
|
+
# Iterate over range
|
46
|
+
# @see #to_a
|
47
|
+
def each(&block)
|
48
|
+
to_a.each(&block)
|
49
|
+
end
|
38
50
|
end
|
39
51
|
end
|
data/lib/blackcal/schedule.rb
CHANGED
@@ -15,11 +15,16 @@ module Blackcal
|
|
15
15
|
# Initialize rule
|
16
16
|
# @param start_time [Time, Date, String, nil]
|
17
17
|
# @param finish_time [Time, Date, String, nil]
|
18
|
-
# @param start_hour [Integer, nil]
|
19
|
-
# @param finish_hour [Integer, nil]
|
18
|
+
# @param start_hour [TimeOfDay, Time, Integer, nil]
|
19
|
+
# @param finish_hour [TimeOfDay, Time, Integer, nil]
|
20
20
|
# @param months [Array<String>, Array<Symbol>, String, Symbol, nil]
|
21
21
|
# @param weekdays [Array<String>, Array<Symbol>, String, Symbol, nil]
|
22
22
|
# @param days [Array<Integer>, Integer, nil]
|
23
|
+
# @see TimeRange#initialize
|
24
|
+
# @see TimeOfDayRange#initialize
|
25
|
+
# @see MonthRange#initialize
|
26
|
+
# @see WeekdayRange#initialize
|
27
|
+
# @see DayRange#initialize
|
23
28
|
def initialize(
|
24
29
|
start_time: nil,
|
25
30
|
finish_time: nil,
|
@@ -30,11 +35,25 @@ module Blackcal
|
|
30
35
|
# weeks_of_month: nil, # TODO
|
31
36
|
days: nil
|
32
37
|
)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
if start_time || finish_time
|
39
|
+
@rule_range = TimeRange.new(parse_time(start_time), parse_time(finish_time))
|
40
|
+
end
|
41
|
+
|
42
|
+
if start_hour || finish_hour
|
43
|
+
@time_of_day = TimeOfDayRange.new(start_hour, finish_hour)
|
44
|
+
end
|
45
|
+
|
46
|
+
if months
|
47
|
+
@months = MonthRange.new(months)
|
48
|
+
end
|
49
|
+
|
50
|
+
if weekdays
|
51
|
+
@weekdays = WeekdayRange.new(weekdays)
|
52
|
+
end
|
53
|
+
|
54
|
+
if days
|
55
|
+
@days = DayRange.new(days)
|
56
|
+
end
|
38
57
|
end
|
39
58
|
|
40
59
|
# Returns true if calendar is open for timestamp
|
@@ -42,28 +61,29 @@ module Blackcal
|
|
42
61
|
# @return [Boolean]
|
43
62
|
def cover?(timestamp)
|
44
63
|
timestamp = parse_time(timestamp)
|
45
|
-
return
|
64
|
+
return false if @rule_range && !@rule_range.cover?(timestamp)
|
46
65
|
|
47
|
-
[@months, @weekdays, @days, @time_of_day].
|
48
|
-
|
49
|
-
end
|
66
|
+
ranges = [@months, @weekdays, @days, @time_of_day].compact
|
67
|
+
return false if ranges.empty?
|
50
68
|
|
51
|
-
|
69
|
+
ranges.all? { |range| range.cover?(timestamp) }
|
52
70
|
end
|
53
71
|
|
54
72
|
# Returns schedule represented as a matrix
|
55
73
|
# @param start_date [Date]
|
56
74
|
# @param finish_date [Date]
|
57
75
|
# @return [Array<Array<Boolean>>]
|
58
|
-
def to_matrix(start_date:, finish_date:)
|
76
|
+
def to_matrix(start_date:, finish_date:, resolution: :hour)
|
59
77
|
start_time = parse_time(start_date).to_time
|
60
78
|
finish_time = parse_time(finish_date).to_time
|
61
|
-
|
79
|
+
slots = resolution == :hour ? 24 : 24 * 60
|
80
|
+
matrix = SlotMatrix.new(slots)
|
62
81
|
|
63
82
|
# TODO: This is needlessly inefficient..
|
64
|
-
|
65
|
-
|
66
|
-
|
83
|
+
time_range = TimeRange.new(start_time, finish_time)
|
84
|
+
time_range.each(resolution: resolution) do |time|
|
85
|
+
matrix << cover?(time)
|
86
|
+
end
|
67
87
|
|
68
88
|
matrix.data
|
69
89
|
end
|
data/lib/blackcal/slot_matrix.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blackcal
|
4
|
+
# Represents a time of day (hour and min)
|
5
|
+
class TimeOfDay
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# @return [Integer] hour
|
9
|
+
attr_reader :hour
|
10
|
+
|
11
|
+
# @return [Integer] minutes defaults to 0
|
12
|
+
attr_reader :min
|
13
|
+
|
14
|
+
# Initialize time of day
|
15
|
+
# @param [Integer] hour
|
16
|
+
# @param [Integer, nil] min optional argument
|
17
|
+
def initialize(hour, min = nil)
|
18
|
+
@hour = hour
|
19
|
+
@min = min || 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Compares two time of days
|
23
|
+
# @param [TimeOfDay, Integer] other if a number is passed it will be used as the hour
|
24
|
+
# @return [Integer] 1 if greater than, 0 if equal, -1 if less than
|
25
|
+
def <=>(other)
|
26
|
+
other_seconds = if other.is_a?(self.class)
|
27
|
+
(other.hour * 60 * 60) + (other.min * 60)
|
28
|
+
else
|
29
|
+
other * 60 * 60
|
30
|
+
end
|
31
|
+
seconds = (hour * 60 * 60) + (min * 60)
|
32
|
+
|
33
|
+
seconds <=> other_seconds
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/blackcal/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blackcal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Burenstam
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: github-markup
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redcarpet
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.16'
|
13
55
|
- !ruby/object:Gem::Dependency
|
14
56
|
name: bundler
|
15
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,6 +121,7 @@ files:
|
|
79
121
|
- ".rubocop.yml"
|
80
122
|
- ".ruby-style-guide.yml"
|
81
123
|
- ".travis.yml"
|
124
|
+
- ".yardopts"
|
82
125
|
- CHANGELOG.md
|
83
126
|
- Gemfile
|
84
127
|
- LICENSE.txt
|
@@ -95,6 +138,7 @@ files:
|
|
95
138
|
- lib/blackcal/range/weekday_range.rb
|
96
139
|
- lib/blackcal/schedule.rb
|
97
140
|
- lib/blackcal/slot_matrix.rb
|
141
|
+
- lib/blackcal/time_of_day.rb
|
98
142
|
- lib/blackcal/version.rb
|
99
143
|
homepage: https://github.com/buren/blackcal
|
100
144
|
licenses:
|