timely 0.4.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|