timely 0.4.2 → 0.9.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/.github/dependabot.yml +6 -0
- data/.github/workflows/release.yml +59 -0
- data/.github/workflows/ruby.yml +19 -0
- data/.rubocop.yml +22 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +23 -0
- data/Gemfile +2 -0
- data/README.md +13 -2
- data/Rakefile +6 -4
- data/gemfiles/rails60.gemfile +8 -0
- data/gemfiles/rails61.gemfile +8 -0
- data/lib/timely.rb +2 -0
- data/lib/timely/date.rb +6 -2
- data/lib/timely/date_chooser.rb +29 -30
- data/lib/timely/date_range.rb +17 -19
- data/lib/timely/date_time.rb +3 -1
- data/lib/timely/rails.rb +2 -0
- data/lib/timely/rails/calendar_tag.rb +3 -3
- data/lib/timely/rails/date.rb +3 -1
- data/lib/timely/rails/date_group.rb +42 -18
- data/lib/timely/rails/date_range_validity_module.rb +8 -6
- data/lib/timely/rails/date_time.rb +5 -3
- data/lib/timely/rails/extensions.rb +12 -37
- data/lib/timely/rails/period.rb +14 -12
- data/lib/timely/rails/season.rb +14 -33
- data/lib/timely/rails/time.rb +7 -3
- data/lib/timely/railtie.rb +2 -0
- data/lib/timely/range.rb +4 -2
- data/lib/timely/string.rb +4 -2
- data/lib/timely/time.rb +7 -3
- data/lib/timely/time_since.rb +5 -3
- data/lib/timely/trackable_date_set.rb +21 -21
- data/lib/timely/version.rb +3 -1
- data/lib/timely/week_days.rb +22 -14
- data/rails/init.rb +2 -0
- data/spec/calendar_tag_spec.rb +11 -10
- data/spec/date_chooser_spec.rb +67 -62
- data/spec/date_group_spec.rb +103 -5
- data/spec/date_range_spec.rb +29 -19
- data/spec/date_spec.rb +3 -1
- data/spec/extensions_spec.rb +5 -9
- data/spec/rails/date_spec.rb +5 -3
- data/spec/rails/date_time_spec.rb +9 -7
- data/spec/rails/period_spec.rb +2 -0
- data/spec/rails/time_spec.rb +6 -4
- data/spec/schema.rb +4 -3
- data/spec/season_spec.rb +23 -26
- data/spec/spec_helper.rb +16 -1
- data/spec/support/coverage_loader.rb +3 -1
- data/spec/temporal_patterns_spec.rb +5 -5
- data/spec/time_since_spec.rb +6 -4
- data/spec/time_spec.rb +6 -4
- data/spec/trackable_date_set_spec.rb +20 -18
- data/spec/week_days_spec.rb +41 -16
- data/timely.gemspec +23 -19
- metadata +54 -26
- data/.travis.yml +0 -22
- data/gemfiles/rails5.gemfile +0 -6
- data/gemfiles/rails6.gemfile +0 -6
- data/spec/string_spec.rb +0 -14
data/lib/timely/time.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Timely
|
2
4
|
module Time
|
3
5
|
def on_date(year, month = nil, day = nil)
|
4
6
|
if year.is_a?(Date)
|
5
7
|
date = year
|
6
|
-
year
|
8
|
+
year = date.year
|
9
|
+
month = date.month
|
10
|
+
day = date.day
|
7
11
|
end
|
8
12
|
|
9
|
-
raise ArgumentError,
|
13
|
+
raise ArgumentError, 'Year, month, and day needed' unless [year, month, day].all?
|
10
14
|
|
11
15
|
::Time.local(year, month, day, hour, min, sec)
|
12
16
|
end
|
13
17
|
|
14
|
-
|
18
|
+
alias on on_date
|
15
19
|
end
|
16
20
|
end
|
data/lib/timely/time_since.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Timely
|
2
4
|
module TimeSince
|
3
5
|
def seconds_since
|
4
|
-
::DateTime.now.to_i -
|
6
|
+
::DateTime.now.to_i - to_i
|
5
7
|
end
|
6
8
|
|
7
9
|
def minutes_since
|
8
|
-
seconds_since/60
|
10
|
+
seconds_since / 60
|
9
11
|
end
|
10
12
|
|
11
13
|
def hours_since
|
12
|
-
minutes_since/60
|
14
|
+
minutes_since / 60
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
# Track a set of dates (usually a range)
|
@@ -40,22 +42,20 @@ module Timely
|
|
40
42
|
@dates_to_do = @dates.dup
|
41
43
|
end
|
42
44
|
|
43
|
-
def self.new_for_date(date, opts={})
|
45
|
+
def self.new_for_date(date, opts = {})
|
44
46
|
duration = opts[:duration] || 1
|
45
47
|
TrackableDateSet.new(date..(date + duration - 1))
|
46
48
|
end
|
47
49
|
|
48
|
-
#
|
50
|
+
# TODO: remove
|
49
51
|
# Initialize from a date + duration
|
50
|
-
def self.from_params(date_string, duration_string=nil)
|
52
|
+
def self.from_params(date_string, duration_string = nil)
|
51
53
|
duration = duration_string.to_i
|
52
|
-
duration = 1 if duration
|
53
|
-
new_for_date(date_string.to_date, :
|
54
|
+
duration = 1 if duration.zero?
|
55
|
+
new_for_date(date_string.to_date, duration: duration)
|
54
56
|
end
|
55
57
|
|
56
|
-
|
57
|
-
@dates
|
58
|
-
end
|
58
|
+
attr_reader :dates
|
59
59
|
|
60
60
|
# Find the set of dates which are YET to do
|
61
61
|
def find_to_do
|
@@ -66,18 +66,16 @@ module Timely
|
|
66
66
|
def dates_done
|
67
67
|
@dates - @dates_to_do
|
68
68
|
end
|
69
|
-
|
69
|
+
alias find_done dates_done
|
70
70
|
|
71
71
|
# Yield each date to do
|
72
72
|
def each_date_to_do
|
73
|
-
|
74
|
-
@dates_to_do.sort.each{|date| yield date}
|
73
|
+
@dates_to_do.each { |date| yield date }
|
75
74
|
end
|
76
75
|
|
77
76
|
# Yield each date in the whole set
|
78
77
|
def each_date
|
79
|
-
|
80
|
-
@dates.sort.each{|date| yield date}
|
78
|
+
@dates.each { |date| yield date }
|
81
79
|
end
|
82
80
|
|
83
81
|
# Set dates as done
|
@@ -94,9 +92,9 @@ module Timely
|
|
94
92
|
@dates_to_do.clear
|
95
93
|
end
|
96
94
|
|
97
|
-
def
|
95
|
+
def done_dates?(date_or_date_range)
|
98
96
|
if date_or_date_range.is_a?(Enumerable)
|
99
|
-
@dates_to_do.none?{|date_to_do| date_or_date_range.include?(date_to_do)}
|
97
|
+
@dates_to_do.none? { |date_to_do| date_or_date_range.include?(date_to_do) }
|
100
98
|
else
|
101
99
|
!@dates_to_do.include? date_or_date_range
|
102
100
|
end
|
@@ -112,8 +110,9 @@ module Timely
|
|
112
110
|
#
|
113
111
|
# action_name => Name to track
|
114
112
|
# {:job_done_when} => Block to call, passed result of yield
|
115
|
-
def do_once(action_name, opts={})
|
113
|
+
def do_once(action_name, opts = {})
|
116
114
|
return if action_applied?(action_name)
|
115
|
+
|
117
116
|
result = yield
|
118
117
|
|
119
118
|
job_done = opts[:job_done_when].blank? || opts[:job_done_when].call(result)
|
@@ -133,14 +132,15 @@ module Timely
|
|
133
132
|
def duration
|
134
133
|
@dates.size
|
135
134
|
end
|
136
|
-
|
135
|
+
alias number_of_nights duration
|
137
136
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
# Can't say whole_period anymore... it's not necessarily sequential dates
|
138
|
+
# def whole_period
|
139
|
+
# self.dates
|
140
|
+
# end
|
142
141
|
|
143
142
|
private
|
143
|
+
|
144
144
|
def actions_applied
|
145
145
|
@actions_applied ||= Set.new
|
146
146
|
end
|
data/lib/timely/version.rb
CHANGED
data/lib/timely/week_days.rb
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Timely
|
2
4
|
class WeekDays
|
3
|
-
WEEKDAY_KEYS = %i[sun mon tue wed thu fri sat]
|
5
|
+
WEEKDAY_KEYS = %i[sun mon tue wed thu fri sat].freeze
|
6
|
+
|
7
|
+
def self.from_range(date_range)
|
8
|
+
dates = Array(date_range)
|
9
|
+
return ALL_WEEKDAYS if dates.count >= WEEKDAY_KEYS.count
|
10
|
+
|
11
|
+
new(dates.each_with_object({}) do |date, result|
|
12
|
+
# e.g. {3: true, 5: true}
|
13
|
+
result[date.to_date.wday] = true
|
14
|
+
end)
|
15
|
+
end
|
4
16
|
|
5
17
|
# Create a new Weekdays object
|
6
18
|
# weekdays can be in three formats
|
@@ -26,7 +38,7 @@ module Timely
|
|
26
38
|
when Integer
|
27
39
|
# 4 -> 0000100 (binary) -> "0010000" (reversed string) -> {:tue => true}
|
28
40
|
weekdays.to_s(2).reverse.each_char.with_index do |char, index|
|
29
|
-
set_day(index, char ==
|
41
|
+
set_day(index, char == '1')
|
30
42
|
end
|
31
43
|
when Hash
|
32
44
|
weekdays.each_pair do |day, value|
|
@@ -48,7 +60,7 @@ module Timely
|
|
48
60
|
}
|
49
61
|
else
|
50
62
|
raise ArgumentError,
|
51
|
-
|
63
|
+
'You must initialize with an Integer, Hash or Array'
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
@@ -63,10 +75,9 @@ module Timely
|
|
63
75
|
|
64
76
|
def set_day(day, set)
|
65
77
|
key = day_to_index(day)
|
66
|
-
unless WEEKDAY_KEYS.include?(key)
|
67
|
-
|
68
|
-
|
69
|
-
@weekdays[key] = [true, "true", 1, "1"].include?(set)
|
78
|
+
raise ArgumentError, "Invalid week day index #{key}" unless WEEKDAY_KEYS.include?(key)
|
79
|
+
|
80
|
+
@weekdays[key] = [true, 'true', 1, '1'].include?(set)
|
70
81
|
end
|
71
82
|
|
72
83
|
def applies_for_date?(date)
|
@@ -95,10 +106,7 @@ module Timely
|
|
95
106
|
# Returns array of weekday selected
|
96
107
|
# e.g. [:sun, :sat]
|
97
108
|
def weekdays
|
98
|
-
|
99
|
-
# Ruby 1.8 returns an array for Hash#select and loses order
|
100
|
-
return selected.keys if selected.is_a?(Hash)
|
101
|
-
selected.map(&:first).sort_by { |v| WEEKDAY_KEYS.index(v) }
|
109
|
+
@weekdays.select { |_day, day_selected| day_selected }.keys
|
102
110
|
end
|
103
111
|
|
104
112
|
# Returns comma separated and capitalized in Sun-Sat order
|
@@ -107,7 +115,7 @@ module Timely
|
|
107
115
|
days = weekdays.map { |day| day.to_s.capitalize }
|
108
116
|
last_day = days.pop
|
109
117
|
|
110
|
-
days.empty? ? last_day : days.join(
|
118
|
+
days.empty? ? last_day : days.join(', ') + ', and ' + last_day
|
111
119
|
end
|
112
120
|
|
113
121
|
# 7 bits encoded in decimal number
|
@@ -116,7 +124,7 @@ module Timely
|
|
116
124
|
def weekdays_int
|
117
125
|
int = 0
|
118
126
|
WEEKDAY_KEYS.each.with_index do |day, index|
|
119
|
-
int += 2
|
127
|
+
int += 2**index if @weekdays[day]
|
120
128
|
end
|
121
129
|
int
|
122
130
|
end
|
@@ -133,6 +141,6 @@ module Timely
|
|
133
141
|
end
|
134
142
|
end
|
135
143
|
|
136
|
-
ALL_WEEKDAYS = WeekDays.new(%w[1 1 1 1 1 1 1])
|
144
|
+
ALL_WEEKDAYS = WeekDays.new(%w[1 1 1 1 1 1 1]).freeze
|
137
145
|
end
|
138
146
|
end
|
data/rails/init.rb
CHANGED
data/spec/calendar_tag_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
class ActionViewTest
|
@@ -8,22 +10,21 @@ describe Timely::ActionViewHelpers do
|
|
8
10
|
subject { ActionViewTest.new }
|
9
11
|
let(:string) { double(:string) }
|
10
12
|
let(:date) { Date.new(2000, 12, 25) }
|
11
|
-
before
|
13
|
+
before do
|
12
14
|
expect(date).to receive(:to_s).with(:calendar).and_return('25-12-2000')
|
13
15
|
expect(Timely).to receive(:current_date).and_return(date)
|
14
|
-
|
16
|
+
end
|
15
17
|
|
16
18
|
it 'should generate calendar tags' do
|
17
19
|
expect(string).to receive(:html_safe)
|
18
20
|
expect(subject).to receive(:tag).with(:input,
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
).and_return(string)
|
21
|
+
id: 'test',
|
22
|
+
class: 'datepicker',
|
23
|
+
size: 10,
|
24
|
+
maxlength: 10,
|
25
|
+
name: 'test',
|
26
|
+
type: 'text',
|
27
|
+
value: '25-12-2000').and_return(string)
|
27
28
|
subject.calendar_tag :test
|
28
29
|
end
|
29
30
|
end
|
data/spec/date_chooser_spec.rb
CHANGED
@@ -1,110 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Timely::DateChooser do
|
4
6
|
before(:all) do
|
5
|
-
@from =
|
6
|
-
@to =
|
7
|
+
@from = '01-01-2011'.to_date
|
8
|
+
@to = '01-03-2011'.to_date
|
7
9
|
end
|
8
10
|
|
9
|
-
#Validation
|
10
|
-
it
|
11
|
-
expect { Timely::DateChooser.new(
|
12
|
-
Timely::DateChooserException, 'A Start Date is required'
|
11
|
+
# Validation
|
12
|
+
it 'rejects blank FROM dates' do
|
13
|
+
expect { Timely::DateChooser.new(from: '') }.to raise_error(
|
14
|
+
Timely::DateChooserException, 'A Start Date is required'
|
15
|
+
)
|
13
16
|
end
|
14
17
|
|
15
|
-
it
|
16
|
-
expect
|
17
|
-
|
18
|
-
|
18
|
+
it 'rejects TO dates later than FROM dates' do
|
19
|
+
expect do
|
20
|
+
Timely::DateChooser.new(
|
21
|
+
multiple_dates: true, from: @from + 10, to: @from
|
22
|
+
)
|
23
|
+
end .to raise_error(Timely::DateChooserException, 'Start Date is after End Date')
|
19
24
|
end
|
20
25
|
|
21
|
-
#Operation
|
22
|
-
it
|
26
|
+
# Operation
|
27
|
+
it 'returns today if client only wants single date' do
|
23
28
|
expect(Timely::DateChooser.new(
|
24
|
-
:
|
29
|
+
multiple_dates: false, from: @from
|
25
30
|
).choose_dates).to eq [@from]
|
26
31
|
end
|
27
32
|
|
28
|
-
#Test specific date of month
|
29
|
-
it
|
33
|
+
# Test specific date of month
|
34
|
+
it 'returns from and to as same (one) day in the case that only from date is given' do
|
30
35
|
expect(Timely::DateChooser.new(
|
31
|
-
:
|
36
|
+
multiple_dates: true, from: @from, to: ''
|
32
37
|
).choose_dates).to eq [@from]
|
33
38
|
end
|
34
39
|
|
35
|
-
it
|
40
|
+
it 'returns all days between from and to days in the basic case' do
|
36
41
|
expect(Timely::DateChooser.new(
|
37
|
-
:
|
38
|
-
).choose_dates).to eq
|
42
|
+
multiple_dates: true, from: @from, to: @to
|
43
|
+
).choose_dates).to eq((@from..@to).to_a)
|
39
44
|
end
|
40
45
|
|
41
|
-
it
|
46
|
+
it 'returns the recurring dates within the range if this option is picked' do
|
42
47
|
expect(Timely::DateChooser.new(
|
43
|
-
:
|
44
|
-
).choose_dates).to eq [
|
45
|
-
|
46
|
-
|
48
|
+
multiple_dates: true, select: 'days', dates: '1,11,3', from: @from, to: @to
|
49
|
+
).choose_dates).to eq %w[
|
50
|
+
1-01-2011 3-01-2011 11-01-2011 1-02-2011
|
51
|
+
3-02-2011 11-02-2011 1-03-2011
|
47
52
|
].map(&:to_date)
|
48
53
|
end
|
49
54
|
|
50
|
-
it
|
55
|
+
it 'returns the specific dates, within or outside of the range, if this option is picked' do
|
51
56
|
expect(Timely::DateChooser.new(
|
52
|
-
:
|
53
|
-
).choose_dates).to eq [
|
54
|
-
|
57
|
+
multiple_dates: true, select: 'specific_days', specific_dates: '11-01-2011, 18-02-2011, 22-06-2011', from: @from, to: @to
|
58
|
+
).choose_dates).to eq %w[
|
59
|
+
11-01-2011 18-02-2011 22-06-2011
|
55
60
|
].map(&:to_date)
|
56
61
|
end
|
57
62
|
|
58
|
-
#Test days of week, every X weeks
|
59
|
-
it
|
63
|
+
# Test days of week, every X weeks
|
64
|
+
it 'returns every sunday correctly' do
|
60
65
|
expect(Timely::DateChooser.new(
|
61
|
-
:
|
62
|
-
:
|
63
|
-
).choose_dates).to eq [
|
64
|
-
|
65
|
-
|
66
|
+
multiple_dates: true, select: 'weekdays', from: @from, to: @to,
|
67
|
+
interval: { level: '1', unit: 'week' }, weekdays: { sun: true }
|
68
|
+
).choose_dates).to eq %w[
|
69
|
+
2-01-2011 9-01-2011 16-01-2011 23-01-2011 30-01-2011
|
70
|
+
06-02-2011 13-02-2011 20-02-2011 27-02-2011
|
66
71
|
].map(&:to_date)
|
67
72
|
end
|
68
73
|
|
69
|
-
it
|
74
|
+
it 'returns every thursday and sunday correctly' do
|
70
75
|
expect(Timely::DateChooser.new(
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
).choose_dates).to eq [
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
multiple_dates: true, select: 'weekdays',
|
77
|
+
interval: { level: '1', unit: 'week' },
|
78
|
+
weekdays: { sun: true, thu: true }, from: @from, to: @to
|
79
|
+
).choose_dates).to eq %w[
|
80
|
+
2-01-2011 6-01-2011 9-01-2011 13-01-2011 16-01-2011
|
81
|
+
20-01-2011 23-01-2011 27-01-2011 30-01-2011 3-02-2011
|
82
|
+
06-02-2011 10-02-2011 13-02-2011 17-02-2011 20-02-2011
|
83
|
+
24-02-2011 27-02-2011
|
79
84
|
].map(&:to_date)
|
80
85
|
end
|
81
86
|
|
82
|
-
it
|
87
|
+
it 'returns every 2nd thursday and sunday correctly' do
|
83
88
|
expect(Timely::DateChooser.new(
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
).choose_dates).to eq [
|
88
|
-
|
89
|
-
|
89
|
+
multiple_dates: true, select: 'weekdays',
|
90
|
+
interval: { level: '2', unit: 'week' },
|
91
|
+
weekdays: { sun: '1', thu: '1' }, from: @from, to: @to
|
92
|
+
).choose_dates).to eq %w[
|
93
|
+
2-01-2011 6-01-2011 16-01-2011 20-01-2011 30-01-2011
|
94
|
+
3-02-2011 13-02-2011 17-02-2011 27-02-2011
|
90
95
|
].map(&:to_date)
|
91
96
|
end
|
92
97
|
|
93
|
-
#Test correct results for every Nth week of month
|
94
|
-
it
|
98
|
+
# Test correct results for every Nth week of month
|
99
|
+
it 'returns every 1st Tuesday' do
|
95
100
|
expect(Timely::DateChooser.new(
|
96
|
-
:
|
97
|
-
:
|
98
|
-
).choose_dates).to eq [
|
101
|
+
multiple_dates: true, select: 'weekdays', from: @from, to: @to,
|
102
|
+
interval: { level: '1', unit: 'week_of_month' }, weekdays: { tue: true }
|
103
|
+
).choose_dates).to eq %w[4-01-2011 1-02-2011 1-03-2011].map(&:to_date)
|
99
104
|
end
|
100
105
|
|
101
|
-
it
|
106
|
+
it 'returns every 3st Monday and Friday' do
|
102
107
|
expect(Timely::DateChooser.new(
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
).choose_dates).to eq [
|
107
|
-
|
108
|
+
multiple_dates: true, select: 'weekdays',
|
109
|
+
interval: { level: '3', unit: 'week_of_month' },
|
110
|
+
weekdays: { mon: true, fri: true }, from: @from, to: @to
|
111
|
+
).choose_dates).to eq %w[
|
112
|
+
17-01-2011 21-01-2011 18-02-2011 21-02-2011
|
108
113
|
].map(&:to_date)
|
109
114
|
end
|
110
115
|
end
|