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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ad5b5230c51dc3ab72192b82882b333fe9fc72b49fb2e8324881e7a6b7fd269
4
- data.tar.gz: 1de47a9da2ee6f3708c049f85fed2654e7ec4900935056f80dbe42db2842cf7b
3
+ metadata.gz: 5aeec444ee90189672055555263e90807c7f2bad4e366c5e44d74dd4d578cd30
4
+ data.tar.gz: e5bf46db86fee6448233e03aaba2dfe7dc7f66771f6d82c30db2e231e03b48b9
5
5
  SHA512:
6
- metadata.gz: 8d9bde900b4005fdcf26cc826e53858eb47067f6ccd6e01c633861efb305970d37ceddfd7bc1d70b2c68bae1a53abccc178ca1b95f20fc5ae5032cc8ef0e3e11
7
- data.tar.gz: 8d1145ecf55ee9deb075d459e9f2399ec28bf0da6519f60b47cf1fddd679312383c6df4d9e83d8f6222e2aa89c7791b25927efb1705260e880ad54682b4b8037
6
+ metadata.gz: a47c451934a28dda82fae5eb78145791b5d8727e371683f506ade5f3ee4f609167bf5bec209c1dd545ae8399fa82df22ec994903df4c000c36b844b8c42255f3
7
+ data.tar.gz: bc96a85cae09b52d5381a915847c1f76df9613592e632403ca1d69dca74486f7d7bf464b5777d850bb88689cba0ded45101c49503470f2c0e8242d3f09a1f420
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
@@ -0,0 +1,59 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v2
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ bundler-cache: true
18
+ - run: bundle exec rake
19
+
20
+ release:
21
+ needs: build
22
+ name: Release
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v2
27
+
28
+ - name: Generate Changelog
29
+ run: |
30
+ # Get version from github ref (remove 'refs/tags/' and prefix 'v')
31
+ version="${GITHUB_REF#refs/tags/v}"
32
+ npx changelog-parser CHANGELOG.md | jq -cr ".versions | .[] | select(.version == \"$version\") | .body" > ${{ github.workflow }}-CHANGELOG.txt
33
+
34
+ - name: Release
35
+ uses: softprops/action-gh-release@v1
36
+ with:
37
+ body_path: ${{ github.workflow }}-CHANGELOG.txt
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+
41
+ publish:
42
+ needs: [build, release]
43
+ name: Publish
44
+ runs-on: ubuntu-latest
45
+
46
+ steps:
47
+ - uses: actions/checkout@v2
48
+ - uses: ruby/setup-ruby@v1
49
+
50
+ - name: Publish to RubyGems
51
+ run: |
52
+ mkdir -p $HOME/.gem
53
+ touch $HOME/.gem/credentials
54
+ chmod 0600 $HOME/.gem/credentials
55
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
56
+ gem build *.gemspec
57
+ gem push *.gem
58
+ env:
59
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
@@ -0,0 +1,19 @@
1
+ name: Build and Test
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ gemfile: [rails60, rails61]
9
+ ruby: ["2.6", "2.7", "3.0"]
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
13
+ steps:
14
+ - uses: actions/checkout@v2
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rake
@@ -1,8 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ Exclude:
4
+ - lib/timely/temporal_patterns/*.rb
5
+
1
6
  Metrics/LineLength:
2
- Max: 80
7
+ Max: 200
3
8
 
4
9
  Style/Documentation:
5
10
  Enabled: false
6
11
 
7
12
  Style/FrozenStringLiteralComment:
8
- Enabled: false # Using Ruby 2.4 doesn't need it
13
+ Enabled: true
14
+
15
+ Metrics/BlockLength:
16
+ Enabled: false
17
+
18
+ Metrics/MethodLength:
19
+ Enabled: false
20
+
21
+ Metrics/AbcSize:
22
+ Enabled: false
23
+
24
+ Metrics/CyclomaticComplexity:
25
+ Enabled: false
26
+
27
+ Metrics/PerceivedComplexity:
28
+ Enabled: false
@@ -1 +1 @@
1
- 2.6.3
1
+ 3.0.0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0
4
+
5
+ * [TT-8614] Update to build with github actions / ruby 3.0 / rails 6.1
6
+
7
+ ## 0.8.0
8
+
9
+ * [TT-6441] Turns out we don't actually need time difference in QT
10
+ * [TT-6661] Fix issue when detecting intersecting date groups
11
+
12
+ ## 0.7.0
13
+
14
+ * [TT-6441] Due to TimeDifference being unmaintained bring it into the timely library
15
+
16
+ ## 0.6.0
17
+
18
+ * [TT-6402] Require date group weekdays bit field to be not null/nil
19
+ IMPORTANT: Rails projects must add a migration to make this field not null!
20
+ * [TT-6401] Remove Rails 3 support, unused methods
21
+
22
+ ## 0.5.0
23
+
24
+ * [TT-6193] Date group scopes for more efficient searches/restrictions
25
+
3
26
  ## 0.4.2
4
27
 
5
28
  * [TT-5794] Fix belongs_to associations are not explicitly marked optional
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
  gemspec
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Timely
2
2
 
3
- [![Build Status](https://travis-ci.org/sealink/timely.png?branch=master)](https://travis-ci.org/sealink/timely)
3
+ [![Build Status](https://github.com/sealink/timely/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/sealink/timely/actions)
4
4
  [![Coverage Status](https://coveralls.io/repos/sealink/timely/badge.png)](https://coveralls.io/r/sealink/timely)
5
- [![Dependency Status](https://gemnasium.com/sealink/timely.png?travis)](https://gemnasium.com/sealink/timely)
6
5
  [![Code Climate](https://codeclimate.com/github/sealink/timely.png)](https://codeclimate.com/github/sealink/timely)
7
6
 
8
7
  # DESCRIPTION
@@ -59,3 +58,15 @@ some_date.at_time(some_time) # => Sat May 03 13:40:22 -0500 2008
59
58
  some_time.on(some_date) # => Sat May 03 13:40:22 -0500 2008
60
59
  some_date.at(some_time) # => Sat May 03 13:40:22 -0500 2008
61
60
  ```
61
+
62
+ # Release
63
+
64
+ To publish a new version of this gem the following steps must be taken.
65
+
66
+ * Update the version in the following files
67
+ ```
68
+ CHANGELOG.md
69
+ lib/timely/version.rb
70
+ ````
71
+ * Create a tag using the format v0.1.0
72
+ * Follow build progress in GitHub actions
data/Rakefile CHANGED
@@ -1,12 +1,14 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
2
4
 
3
5
  desc 'Default: run specs.'
4
- task :default => :spec
6
+ task default: :spec
5
7
 
6
8
  require 'rspec/core/rake_task'
7
9
 
8
- desc "Run specs"
10
+ desc 'Run specs'
9
11
  RSpec::Core::RakeTask.new do |t|
10
- t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
12
+ t.pattern = './spec/**/*_spec.rb' # don't need this, it's default.
11
13
  # Put spec opts in a file named .rspec in root
12
14
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec path: '../'
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '~> 6.0'
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec path: '../'
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '~> 6.1'
8
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Timely
2
4
  require 'time'
3
5
  require 'date'
@@ -1,15 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Timely
2
4
  module Date
3
5
  def at_time(hour = nil, minute = nil, second = nil)
4
6
  if hour.is_a?(::Time)
5
7
  time = hour
6
- hour, minute, second = time.hour, time.min, time.sec
8
+ hour = time.hour
9
+ minute = time.min
10
+ second = time.sec
7
11
  end
8
12
 
9
13
  ::Time.local(year, month, day, hour, minute, second)
10
14
  end
11
15
 
12
- alias_method :at, :at_time
16
+ alias at at_time
13
17
 
14
18
  # returns true if date between from and to
15
19
  # however if from and/or to are nil, it ignores that query
@@ -1,18 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Timely
2
4
  class DateChooser
3
5
  # Where is this used... so far only in one place, _date_range.html.haml
4
6
  # May be good to refactor this as well, after the class behaviour is refactored.
5
7
  INTERVALS = [
6
- {:code => 'week', :name => 'week(s)', :description =>
7
- 'Weekdays selected will be chosen every {{n}} weeks for the date range'},
8
- {:code => 'week_of_month', :name => 'week of month', :description =>
9
- 'Weekdays selected will be chosen in their {{ord}} occurance every month,
8
+ { code: 'week', name: 'week(s)', description: 'Weekdays selected will be chosen every {{n}} weeks for the date range' },
9
+ { code: 'week_of_month', name: 'week of month', description: 'Weekdays selected will be chosen in their {{ord}} occurance every month,
10
10
  e.g. if wednesday and thursday are selected, the first wednesday and
11
11
  first thursday are selected. Note: this may mean the booking is copied
12
- to Thursday 1st and Wednesday 7th'}
13
- ]
12
+ to Thursday 1st and Wednesday 7th' }
13
+ ].freeze
14
14
 
15
- attr_accessor :multiple_dates, :from, :to, :select, :dates, :interval, :weekdays
15
+ attr_accessor :multiple_dates, :from, :to, :select, :dates, :interval, :weekdays
16
16
 
17
17
  def initialize(options)
18
18
  @multiple_dates = options[:multiple_dates] || false
@@ -28,8 +28,8 @@ module Timely
28
28
 
29
29
  def process_date(date)
30
30
  case date
31
- when Date; date
32
- when NilClass; nil
31
+ when Date then date
32
+ when NilClass then nil
33
33
  when String
34
34
  date !~ /[^[:space:]]/ ? nil : date.to_date
35
35
  end
@@ -53,7 +53,7 @@ module Timely
53
53
  # so every friday and saturday each fornight
54
54
  def choose_dates
55
55
  # Not multiple dates - just return the From date.
56
- return [@from] if !@multiple_dates
56
+ return [@from] unless @multiple_dates
57
57
 
58
58
  # Multiple dates - return the array, adjusted as per input
59
59
  all_days = (@from..@to).to_a
@@ -66,21 +66,21 @@ module Timely
66
66
  days = @specific_dates.gsub(/\s/, '').split(',')
67
67
  days.map(&:to_date)
68
68
  when 'weekdays'
69
- raise DateChooserException, "No days of the week selected" if @weekdays.weekdays.empty?
70
- raise DateChooserException, "No weekly interval selected" if @interval && @interval.empty?
69
+ raise DateChooserException, 'No days of the week selected' if @weekdays.weekdays.empty?
70
+ raise DateChooserException, 'No weekly interval selected' if @interval&.empty?
71
71
 
72
72
  all_days.select do |date|
73
- if @weekdays.has_day?(date.wday)
74
- case @interval[:unit]
75
- when 'week'
76
- # 0 = first week, 1 = second week, 2 = third week, etc.
77
- nth_week = (date - @from).to_i / 7
78
- # true every 2nd week (0, 2, 4, 6, etc.)
79
- (nth_week % @interval[:level].to_i).zero?
80
- when 'week_of_month'
81
- week = @interval[:level].to_i
82
- (date.mday > (week-1)*7 && date.mday <= week*7)
83
- end
73
+ next unless @weekdays.has_day?(date.wday)
74
+
75
+ case @interval[:unit]
76
+ when 'week'
77
+ # 0 = first week, 1 = second week, 2 = third week, etc.
78
+ nth_week = (date - @from).to_i / 7
79
+ # true every 2nd week (0, 2, 4, 6, etc.)
80
+ (nth_week % @interval[:level].to_i).zero?
81
+ when 'week_of_month'
82
+ week = @interval[:level].to_i
83
+ (date.mday > (week - 1) * 7 && date.mday <= week * 7)
84
84
  end
85
85
  end
86
86
  else
@@ -89,15 +89,14 @@ module Timely
89
89
  end
90
90
 
91
91
  private
92
+
92
93
  def validate
93
- if !@from
94
- raise DateChooserException, "A Start Date is required"
95
- elsif @multiple_dates
96
- @to ||= @from
97
- raise DateChooserException, "Start Date is after End Date" if @from > @to
98
- end
94
+ raise DateChooserException, 'A Start Date is required' unless @from
95
+ raise DateChooserException, 'Start Date is after End Date' if @multiple_dates && @to && @from > @to
96
+
97
+ @to ||= @from if @multiple_dates
99
98
  end
100
99
  end
101
100
 
102
- class DateChooserException < Exception; end
101
+ class DateChooserException < RuntimeError; end
103
102
  end
@@ -1,4 +1,4 @@
1
- require 'date' # Ensure Date class is loaded for old rubies (1.8)
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Timely
4
4
  class DateRange < ::Range
@@ -15,14 +15,14 @@ module Timely
15
15
  super(args.first.to_date, args.last.to_date)
16
16
  end
17
17
  end
18
- alias_method :start_date, :first
19
- alias_method :end_date, :last
18
+ alias start_date first
19
+ alias end_date last
20
20
 
21
21
  def self.validate_range(first, last)
22
- raise ArgumentError, "Date range missing start date" if first.nil?
23
- raise ArgumentError, "Date range missing end date" if last.nil?
24
- raise ArgumentError, "Start date is not a date" unless first.is_a? Date
25
- raise ArgumentError, "End date is not a date" unless last.is_a? Date
22
+ raise ArgumentError, 'Date range missing start date' if first.nil?
23
+ raise ArgumentError, 'Date range missing end date' if last.nil?
24
+ raise ArgumentError, 'Start date is not a date' unless first.is_a? Date
25
+ raise ArgumentError, 'End date is not a date' unless last.is_a? Date
26
26
  end
27
27
 
28
28
  def self.from_params(start_date, duration = nil)
@@ -33,19 +33,18 @@ module Timely
33
33
  end
34
34
 
35
35
  def intersecting_dates(date_range)
36
- start_of_intersection = [self.start_date, date_range.first].max
37
- end_of_intersection = [self.end_date, date_range.last].min
38
- intersection = if end_of_intersection >= start_of_intersection
39
- (start_of_intersection..end_of_intersection)
40
- else
41
- []
42
- end
36
+ r_start = [start_date, date_range.first].max
37
+ r_end = [end_date, date_range.last].min
38
+
39
+ return [] if r_end < r_start
40
+
41
+ r_start..r_end
43
42
  end
44
43
 
45
44
  def number_of_nights
46
45
  ((last - first) + 1).to_i
47
46
  end
48
- alias_method :duration, :number_of_nights
47
+ alias duration number_of_nights
49
48
 
50
49
  def to_s(fmt = '%b %Y', date_fmt = '%Y-%m-%d')
51
50
  Timely::DateRange.to_s(first, last, fmt, date_fmt)
@@ -65,21 +64,20 @@ module Timely
65
64
  "#{first.strftime(month_fmt)} to #{last.strftime(month_fmt)}"
66
65
  end
67
66
  else
68
- "#{first.strftime(fmt)} to #{last.strftime(fmt)}#{" (inclusive)" if is_date}"
67
+ "#{first.strftime(fmt)} to #{last.strftime(fmt)}#{' (inclusive)' if is_date}"
69
68
  end
70
69
  elsif first
71
70
  "on or after #{first.strftime(fmt)}"
72
71
  elsif last
73
72
  "on or before #{last.strftime(fmt)}"
74
73
  else
75
- "no date range"
74
+ 'no date range'
76
75
  end
77
76
  end
78
77
 
79
-
80
78
  private
81
79
 
82
- def self.default_date_format
80
+ private_class_method def self.default_date_format
83
81
  # ::Date as we want Ruby's Date not Timely::Date
84
82
  date_format = ::Date::DATE_FORMATS[:short] if ::Date.const_defined?('DATE_FORMATS')
85
83
  date_format || '%Y-%m-%d'
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Timely
2
4
  module DateTime
3
5
  def on_date(date)
4
- self.change(:year => date.year, :month => date.month, :day => date.day)
6
+ change(year: date.year, month: date.month, day: date.day)
5
7
  end
6
8
  end
7
9
  end