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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/release.yml +59 -0
  4. data/.github/workflows/ruby.yml +19 -0
  5. data/.rubocop.yml +22 -2
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +23 -0
  8. data/Gemfile +2 -0
  9. data/README.md +13 -2
  10. data/Rakefile +6 -4
  11. data/gemfiles/rails60.gemfile +8 -0
  12. data/gemfiles/rails61.gemfile +8 -0
  13. data/lib/timely.rb +2 -0
  14. data/lib/timely/date.rb +6 -2
  15. data/lib/timely/date_chooser.rb +29 -30
  16. data/lib/timely/date_range.rb +17 -19
  17. data/lib/timely/date_time.rb +3 -1
  18. data/lib/timely/rails.rb +2 -0
  19. data/lib/timely/rails/calendar_tag.rb +3 -3
  20. data/lib/timely/rails/date.rb +3 -1
  21. data/lib/timely/rails/date_group.rb +42 -18
  22. data/lib/timely/rails/date_range_validity_module.rb +8 -6
  23. data/lib/timely/rails/date_time.rb +5 -3
  24. data/lib/timely/rails/extensions.rb +12 -37
  25. data/lib/timely/rails/period.rb +14 -12
  26. data/lib/timely/rails/season.rb +14 -33
  27. data/lib/timely/rails/time.rb +7 -3
  28. data/lib/timely/railtie.rb +2 -0
  29. data/lib/timely/range.rb +4 -2
  30. data/lib/timely/string.rb +4 -2
  31. data/lib/timely/time.rb +7 -3
  32. data/lib/timely/time_since.rb +5 -3
  33. data/lib/timely/trackable_date_set.rb +21 -21
  34. data/lib/timely/version.rb +3 -1
  35. data/lib/timely/week_days.rb +22 -14
  36. data/rails/init.rb +2 -0
  37. data/spec/calendar_tag_spec.rb +11 -10
  38. data/spec/date_chooser_spec.rb +67 -62
  39. data/spec/date_group_spec.rb +103 -5
  40. data/spec/date_range_spec.rb +29 -19
  41. data/spec/date_spec.rb +3 -1
  42. data/spec/extensions_spec.rb +5 -9
  43. data/spec/rails/date_spec.rb +5 -3
  44. data/spec/rails/date_time_spec.rb +9 -7
  45. data/spec/rails/period_spec.rb +2 -0
  46. data/spec/rails/time_spec.rb +6 -4
  47. data/spec/schema.rb +4 -3
  48. data/spec/season_spec.rb +23 -26
  49. data/spec/spec_helper.rb +16 -1
  50. data/spec/support/coverage_loader.rb +3 -1
  51. data/spec/temporal_patterns_spec.rb +5 -5
  52. data/spec/time_since_spec.rb +6 -4
  53. data/spec/time_spec.rb +6 -4
  54. data/spec/trackable_date_set_spec.rb +20 -18
  55. data/spec/week_days_spec.rb +41 -16
  56. data/timely.gemspec +23 -19
  57. metadata +54 -26
  58. data/.travis.yml +0 -22
  59. data/gemfiles/rails5.gemfile +0 -6
  60. data/gemfiles/rails6.gemfile +0 -6
  61. data/spec/string_spec.rb +0 -14
@@ -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, month, day = date.year, date.month, date.day
8
+ year = date.year
9
+ month = date.month
10
+ day = date.day
7
11
  end
8
12
 
9
- raise ArgumentError, "Year, month, and day needed" unless [year, month, day].all?
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
- alias_method :on, :on_date
18
+ alias on on_date
15
19
  end
16
20
  end
@@ -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 - self.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
- # Todo: remove
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 == 0
53
- new_for_date(date_string.to_date, :duration => duration)
54
+ duration = 1 if duration.zero?
55
+ new_for_date(date_string.to_date, duration: duration)
54
56
  end
55
57
 
56
- def dates
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
- alias_method :find_done, :dates_done
69
+ alias find_done dates_done
70
70
 
71
71
  # Yield each date to do
72
72
  def each_date_to_do
73
- # Sort method needed as Ruby 1.8 set's aren't ordered
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
- # Sort method needed as Ruby 1.8 set's aren't ordered
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 has_done?(date_or_date_range)
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
- alias_method :number_of_nights, :duration
135
+ alias number_of_nights duration
137
136
 
138
- # Can't say whole_period anymore... it's not necessarily sequential dates
139
- # def whole_period
140
- # self.dates
141
- # end
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Timely
2
- VERSION = "0.4.2"
4
+ VERSION = '0.9.0'
3
5
  end
@@ -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 == "1")
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
- "You must initialize with an Integer, Hash or Array"
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
- raise ArgumentError, "Invalid week day index #{key}"
68
- end
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
- selected = @weekdays.select { |_day, day_selected| day_selected }
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(", ") + ", and " + last_day
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 ** index if @weekdays[day]
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
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'timely/rails'
@@ -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
- :id => 'test',
20
- :class => 'datepicker',
21
- :size => 10,
22
- :maxlength => 10,
23
- :name => 'test',
24
- :type => 'text',
25
- :value => '25-12-2000'
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
@@ -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 = "01-01-2011".to_date
6
- @to = "01-03-2011".to_date
7
+ @from = '01-01-2011'.to_date
8
+ @to = '01-03-2011'.to_date
7
9
  end
8
10
 
9
- #Validation
10
- it "rejects blank FROM dates" do
11
- expect { Timely::DateChooser.new({:from=>""}) }.to raise_error(
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 "rejects TO dates later than FROM dates" do
16
- expect {Timely::DateChooser.new(
17
- :multiple_dates => true, :from => @from + 10, :to => @from
18
- )}.to raise_error(Timely::DateChooserException, 'Start Date is after End Date')
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 "returns today if client only wants single date" do
26
+ # Operation
27
+ it 'returns today if client only wants single date' do
23
28
  expect(Timely::DateChooser.new(
24
- :multiple_dates => false, :from => @from
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 "returns from and to as same (one) day in the case that only from date is given" do
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
- :multiple_dates => true, :from => @from, :to => ''
36
+ multiple_dates: true, from: @from, to: ''
32
37
  ).choose_dates).to eq [@from]
33
38
  end
34
39
 
35
- it "returns all days between from and to days in the basic case" do
40
+ it 'returns all days between from and to days in the basic case' do
36
41
  expect(Timely::DateChooser.new(
37
- :multiple_dates => true, :from => @from, :to => @to
38
- ).choose_dates).to eq (@from..@to).to_a
42
+ multiple_dates: true, from: @from, to: @to
43
+ ).choose_dates).to eq((@from..@to).to_a)
39
44
  end
40
45
 
41
- it "returns the recurring dates within the range if this option is picked" do
46
+ it 'returns the recurring dates within the range if this option is picked' do
42
47
  expect(Timely::DateChooser.new(
43
- :multiple_dates => true, :select => 'days', :dates => '1,11,3', :from => @from, :to => @to
44
- ).choose_dates).to eq [
45
- "1-01-2011", "3-01-2011", "11-01-2011", "1-02-2011",
46
- "3-02-2011", "11-02-2011", "1-03-2011"
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 "returns the specific dates, within or outside of the range, if this option is picked" do
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
- :multiple_dates => true, :select => 'specific_days', :specific_dates => '11-01-2011, 18-02-2011, 22-06-2011', :from => @from, :to => @to
53
- ).choose_dates).to eq [
54
- "11-01-2011", "18-02-2011", "22-06-2011"
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 "returns every sunday correctly" do
63
+ # Test days of week, every X weeks
64
+ it 'returns every sunday correctly' do
60
65
  expect(Timely::DateChooser.new(
61
- :multiple_dates => true, :select => 'weekdays', :from => @from, :to => @to,
62
- :interval => {:level => "1", :unit => "week"}, :weekdays => {:sun => true}
63
- ).choose_dates).to eq [
64
- "2-01-2011", "9-01-2011", "16-01-2011", "23-01-2011", "30-01-2011",
65
- "06-02-2011", "13-02-2011", "20-02-2011", "27-02-2011"
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 "returns every thursday and sunday correctly" do
74
+ it 'returns every thursday and sunday correctly' do
70
75
  expect(Timely::DateChooser.new(
71
- :multiple_dates => true, :select => 'weekdays',
72
- :interval => {:level => "1", :unit => "week"},
73
- :weekdays => {:sun => true, :thu => true}, :from => @from, :to => @to
74
- ).choose_dates).to eq [
75
- "2-01-2011", "6-01-2011", "9-01-2011", "13-01-2011", "16-01-2011",
76
- "20-01-2011", "23-01-2011", "27-01-2011", "30-01-2011", "3-02-2011",
77
- "06-02-2011", "10-02-2011", "13-02-2011", "17-02-2011", "20-02-2011",
78
- "24-02-2011", "27-02-2011"
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 "returns every 2nd thursday and sunday correctly" do
87
+ it 'returns every 2nd thursday and sunday correctly' do
83
88
  expect(Timely::DateChooser.new(
84
- :multiple_dates => true, :select => 'weekdays',
85
- :interval => {:level => "2", :unit => "week"},
86
- :weekdays => {:sun => "1", :thu => "1"}, :from => @from, :to => @to
87
- ).choose_dates).to eq [
88
- "2-01-2011", "6-01-2011", "16-01-2011", "20-01-2011", "30-01-2011",
89
- "3-02-2011", "13-02-2011", "17-02-2011", "27-02-2011"
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 "returns every 1st Tuesday" do
98
+ # Test correct results for every Nth week of month
99
+ it 'returns every 1st Tuesday' do
95
100
  expect(Timely::DateChooser.new(
96
- :multiple_dates => true, :select => 'weekdays', :from => @from, :to => @to,
97
- :interval => {:level => "1", :unit => "week_of_month"}, :weekdays => {:tue => true}
98
- ).choose_dates).to eq ["4-01-2011", "1-02-2011", "1-03-2011"].map(&:to_date)
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 "returns every 3st Monday and Friday" do
106
+ it 'returns every 3st Monday and Friday' do
102
107
  expect(Timely::DateChooser.new(
103
- :multiple_dates => true, :select => 'weekdays',
104
- :interval => {:level => "3", :unit => "week_of_month"},
105
- :weekdays => {:mon => true, :fri => true}, :from => @from, :to => @to
106
- ).choose_dates).to eq [
107
- "17-01-2011", "21-01-2011", "18-02-2011", "21-02-2011"
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