blackcal 0.2.0 → 0.3.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.
- 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:
|