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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c23e691a5c3523795779efef15fe053f74136c0f0c1ce61acd8a57f8b988f761
4
- data.tar.gz: 631199640ed9b4af0c913ae4a8b6c54ec11d5b3098996dcc0dedcac7dfb3c1e5
3
+ metadata.gz: db977cf2c574a0e8d76891abfa30a6b8140e5ec07bfc0f2b23a215b03732d166
4
+ data.tar.gz: 8b3deb0295ba37f2ff1440cf8e4eed055dcdd77ac728a25e26b48edce491e381
5
5
  SHA512:
6
- metadata.gz: 15d610fec732303f9ea7badd1f8139298cb1161bdabf1baf6cee2c355a11cd826f3544747e7baa9159afed3818e83ae06d7fa32a68df7a4612c573e7daa49e27
7
- data.tar.gz: 1792395fc246472ca622b39920c67f5829d911f1431c8853c1697ab7d380fba6bc278af3a4c24c21f73d579d6019e3f89f2b58c6bef5eefa8299513944f474f0
6
+ metadata.gz: f13e108edab2e980c18b9650145b084bf01ec391e007d78612991a4a4337d47040a7dae5bcce4ad61acc8ddcb786809bafbdea3b6cf743d5e4e14c7b26035e03
7
+ data.tar.gz: 31e1e04b3929f06519a67a855e276b3771fc360b4e889d3f35b7bc5b3f4e0928617c8dfe2f0edd0cccca671b95e54d9d01c532cbaa4d7f08a145d219ed903271
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
3
+ --no-private
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ # v0.3.0
4
+
5
+ * Minute level resolution support
6
+ - Enhance `Schedule#to_matrix`
7
+ - Enhance `TimeOfDayRange`
8
+ - New `TimeOfDay` class
9
+ * Include `Enumerable` in all ranges
10
+
3
11
  # v0.2.0
4
12
 
5
13
  * Add `Blackcal::schedule`
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: 10, finish_hour: 14)
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, ...], [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
- # @return [Array<Symbol>] numbers in range
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 || 0
9
- @finish = finish || 0
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
- hour = timestamp.hour
22
- # min = timestamp.min # TODO: Support minutes
23
- @disallowed_hours.include?(hour)
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
@@ -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
- @rule_range = TimeRange.new(parse_time(start_time), parse_time(finish_time)) if start_time || finish_time # rubocop:disable Metrics/LineLength
34
- @time_of_day = TimeOfDayRange.new(start_hour, finish_hour) if start_hour || finish_hour # rubocop:disable Metrics/LineLength
35
- @months = MonthRange.new(months) if months
36
- @weekdays = WeekdayRange.new(weekdays) if weekdays
37
- @days = DayRange.new(days) if days
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 true if @rule_range && !@rule_range.cover?(timestamp)
64
+ return false if @rule_range && !@rule_range.cover?(timestamp)
46
65
 
47
- [@months, @weekdays, @days, @time_of_day].each do |range|
48
- return true if range && !range.cover?(timestamp)
49
- end
66
+ ranges = [@months, @weekdays, @days, @time_of_day].compact
67
+ return false if ranges.empty?
50
68
 
51
- false
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
- matrix = SlotMatrix.new(24)
79
+ slots = resolution == :hour ? 24 : 24 * 60
80
+ matrix = SlotMatrix.new(slots)
62
81
 
63
82
  # TODO: This is needlessly inefficient..
64
- seconds = (start_time - finish_time).abs
65
- hours = (seconds / (60 * 60)).to_i
66
- hours.times { |hour| matrix << cover?(start_time + (hour * 60 * 60)) }
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
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Blackcal
4
+ # Slot matrix
4
5
  class SlotMatrix
6
+ # Initialize slot matrix
7
+ # @param [Integer] slots max elements in each slot
5
8
  def initialize(slots)
6
9
  @matrix = [[]]
7
10
  @slots = slots
@@ -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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Blackcal
4
- VERSION = '0.2.0'.freeze
4
+ # Gem version
5
+ VERSION = '0.3.0'.freeze
5
6
  end
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.2.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-15 00:00:00.000000000 Z
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: