cloudwatch_chrono 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c8a24dc71df54c6c133cf55c03373ce27d52bf2cd8e33d787e3d521b684eebf9
4
+ data.tar.gz: d91fcb71572877e9d468752caf8626dc9d96360fa8be389e486c69f17d3ea58d
5
+ SHA512:
6
+ metadata.gz: cc34848789572696d2467a4364568f3bf31d41f2a9ca750ffd3d4a3e27ec1ed40f39202579fd0a845a8223278a8bedd40e7bc1902f3c620c5ee7826da5b73ac7
7
+ data.tar.gz: 912ffe56fb3496accc291278d704a572fd4cf09d10a969616c8f34ffcb4d1b5586e05effdb3c21c7d5a9343bec5388bbc96929383449db667387e4dc24457468
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cloudwatch_chrono.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cloudwatch_chrono (0.1.0)
5
+ chrono
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (6.0.2.2)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
15
+ zeitwerk (~> 2.2)
16
+ chrono (0.4.0)
17
+ activesupport
18
+ coderay (1.1.2)
19
+ concurrent-ruby (1.1.6)
20
+ diff-lcs (1.3)
21
+ i18n (1.8.2)
22
+ concurrent-ruby (~> 1.0)
23
+ method_source (1.0.0)
24
+ minitest (5.14.0)
25
+ pry (0.13.1)
26
+ coderay (~> 1.1)
27
+ method_source (~> 1.0)
28
+ rake (12.3.3)
29
+ rspec (3.9.0)
30
+ rspec-core (~> 3.9.0)
31
+ rspec-expectations (~> 3.9.0)
32
+ rspec-mocks (~> 3.9.0)
33
+ rspec-core (3.9.1)
34
+ rspec-support (~> 3.9.1)
35
+ rspec-expectations (3.9.1)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.9.0)
38
+ rspec-mocks (3.9.1)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.9.0)
41
+ rspec-support (3.9.2)
42
+ thread_safe (0.3.6)
43
+ tzinfo (1.2.7)
44
+ thread_safe (~> 0.1)
45
+ zeitwerk (2.3.0)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ cloudwatch_chrono!
52
+ pry
53
+ rake (~> 12.0)
54
+ rspec (~> 3.0)
55
+
56
+ BUNDLED WITH
57
+ 2.1.4
@@ -0,0 +1,17 @@
1
+ # CloudwatchChrono
2
+
3
+ Wrapper of [chrono](https://github.com/r7kamura/chrono) for [Amazon CloudWatch Events Cron Expressions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions).
4
+
5
+ ## Iterator
6
+ Parses cron syntax and determines next scheduled run.
7
+
8
+ ```ruby
9
+ Time.now #=> 2020-05-01 19:57:52.020655 +0900
10
+ iterator = CloudwatchChrono::Iterator.new("0 18 ? * MON-FRI *")
11
+ iterator.next #=> 2020-05-04 18:00:00 +0900
12
+ iterator.next #=> 2020-05-05 18:00:00 +0900
13
+ iterator.next #=> 2020-05-06 18:00:00 +0900
14
+ ```
15
+
16
+ ## Syntax
17
+ See https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cloudwatch_chrono"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ require_relative 'lib/cloudwatch_chrono/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "cloudwatch_chrono"
5
+ spec.version = CloudwatchChrono::VERSION
6
+ spec.authors = ["Takuya Kosugiyama"]
7
+ spec.email = ["re@itkq.jp"]
8
+
9
+ spec.summary = %q{Chronology for CloudWatch cron expression.}
10
+ spec.description = %q{Chronology for CloudWatch cron expression.}
11
+ spec.homepage = "https://github.com/itkq/cloudwatch_chrono"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/itkq/cloudwatch_chrono"
16
+ spec.metadata["changelog_uri"] = "https://github.com/itkq/cloudwatch_chrono/blob/master/CHANGELOG.md"
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_dependency "chrono"
28
+ spec.add_development_dependency "pry"
29
+ end
@@ -0,0 +1,5 @@
1
+ require "cloudwatch_chrono/fields"
2
+ require "cloudwatch_chrono/iterator"
3
+ require "cloudwatch_chrono/next_time"
4
+ require "cloudwatch_chrono/schedule"
5
+ require "cloudwatch_chrono/version"
@@ -0,0 +1,7 @@
1
+ require "chrono/fields/base"
2
+ require "chrono/fields/day"
3
+ require "chrono/fields/hour"
4
+ require "chrono/fields/minute"
5
+ require "chrono/fields/month"
6
+ require "cloudwatch_chrono/fields/wday"
7
+ require "cloudwatch_chrono/fields/year"
@@ -0,0 +1,28 @@
1
+ require "chrono/fields/wday"
2
+
3
+ module CloudwatchChrono
4
+ module Fields
5
+ class Wday < Chrono::Fields::Wday
6
+ CLOUDWATCH_REGEXP = %r<\A(?:(?<step>(?:\*|\?|(?:(?<atom>\d+|sun|mon|tue|wed|thu|fri|sat)(?:-\g<atom>)?))(?:/\d+)?)(?:,\g<step>)*)\z>ix.freeze
7
+
8
+ def initialize(source)
9
+ if source == 'L'
10
+ @last_wday = true
11
+ else
12
+ unless CLOUDWATCH_REGEXP =~ source
13
+ raise InvalidField.new('Unparsable field', source)
14
+ end
15
+ end
16
+ @source = source
17
+ end
18
+
19
+ def interpolated
20
+ if @last_wday
21
+ '6-6'
22
+ else
23
+ super.gsub("?", "#{range.first}-#{range.last}")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module CloudwatchChrono
2
+ module Fields
3
+ class Year < Chrono::Fields::Base
4
+ private
5
+
6
+ def range
7
+ 1970..2199
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require "chrono/iterator"
2
+
3
+ module CloudwatchChrono
4
+ class Iterator < Chrono::Iterator
5
+ def next
6
+ self.now = NextTime.new(now: now, source: source).to_time
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ require "chrono/next_time"
2
+
3
+ module CloudwatchChrono
4
+ class NextTime < Chrono::NextTime
5
+ def to_time
6
+ # the longest cycle is 4 years (leap year)
7
+ # Note that the combination of day-month and wday is OR
8
+ max_time = time + (365 * 3 + 366).days
9
+ while @time < max_time
10
+ case
11
+ when !scheduled_in_this_year?
12
+ carry_year
13
+ when !scheduled_in_this_month?
14
+ carry_month
15
+ when !scheduled_in_this_day?
16
+ carry_day
17
+ when !scheduled_in_this_hour?
18
+ carry_hour
19
+ when !scheduled_in_this_minute?
20
+ carry_minute
21
+ else
22
+ return @time
23
+ end
24
+ end
25
+ raise ArgumentError, "invalid cron string '#{@source}'"
26
+ end
27
+
28
+ private
29
+
30
+ def scheduled_in_this_day?
31
+ if schedule.last_day?
32
+ return time.day == time.end_of_month.day
33
+ elsif schedule.latest_weekday
34
+ return time.day == calc_latest_weekday
35
+ elsif schedule.ordered_weekday
36
+ return time.day == calc_ordered_weekday
37
+ end
38
+
39
+ super
40
+ end
41
+
42
+ def calc_latest_weekday
43
+ target = time.change(day: schedule.latest_weekday)
44
+ case target.wday
45
+ when 0 # SUN
46
+ target.day + 1
47
+ when 6 # SAT
48
+ target.day - 1
49
+ else
50
+ target.day
51
+ end
52
+ end
53
+
54
+ def calc_ordered_weekday
55
+ wday, order = schedule.ordered_weekday
56
+
57
+ month = time.month
58
+ curr = time.beginning_of_month
59
+ while curr.wday != wday
60
+ curr = curr.advance(days: 1)
61
+ end
62
+
63
+ if curr.month != month
64
+ curr = curr.advance(weeks: 1)
65
+ end
66
+
67
+ (order - 1).times do
68
+ curr = curr.advance(weeks: 1)
69
+ end
70
+
71
+ if curr.month != month
72
+ raise ArgumentError, "invalid cron string '#{@source}'"
73
+ end
74
+
75
+ curr.day
76
+ end
77
+
78
+ def schedule
79
+ @schedule ||= Schedule.new(source)
80
+ end
81
+
82
+ def scheduled_in_this_year?
83
+ schedule.years.include?(time.year)
84
+ end
85
+
86
+ def carry_year
87
+ self.time = time.next_year.beginning_of_year
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,44 @@
1
+ require "chrono/schedule"
2
+
3
+ module CloudwatchChrono
4
+ class Schedule < Chrono::Schedule
5
+ def initialize(source)
6
+ if %r<\A[ \t]*(?:(?<field>\S+)[ \t]+){5}\g<field>[ \t]*\z> !~ source
7
+ raise Chrono::Fields::Base::InvalidField.new('invalid source', source)
8
+ end
9
+ @source = source
10
+ end
11
+
12
+ def wdays
13
+ Fields::Wday.new(fields[4]).to_a
14
+ end
15
+
16
+ def years
17
+ Fields::Year.new(fields[5]).to_a
18
+ end
19
+
20
+ def days?
21
+ !%w[* ?].include?(fields[2])
22
+ end
23
+
24
+ def wdays?
25
+ !%w[* ?].include?(fields[4])
26
+ end
27
+
28
+ def last_day?
29
+ fields[2] == 'L'
30
+ end
31
+
32
+ def latest_weekday
33
+ fields[2].slice(/\A(\d+)W\z/, 1)
34
+ end
35
+
36
+ # e.g. 3#2 means the second Tuesday and returns [3, 2]
37
+ def ordered_weekday
38
+ m = fields[2].match(/\A([0-6])#([1-5])\z/)
39
+ if m
40
+ m[1, 2].map(&:to_i)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module CloudwatchChrono
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudwatch_chrono
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Takuya Kosugiyama
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chrono
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Chronology for CloudWatch cron expression.
42
+ email:
43
+ - re@itkq.jp
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - ".travis.yml"
51
+ - Gemfile
52
+ - Gemfile.lock
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/setup
57
+ - cloudwatch_chrono.gemspec
58
+ - lib/cloudwatch_chrono.rb
59
+ - lib/cloudwatch_chrono/fields.rb
60
+ - lib/cloudwatch_chrono/fields/wday.rb
61
+ - lib/cloudwatch_chrono/fields/year.rb
62
+ - lib/cloudwatch_chrono/iterator.rb
63
+ - lib/cloudwatch_chrono/next_time.rb
64
+ - lib/cloudwatch_chrono/schedule.rb
65
+ - lib/cloudwatch_chrono/version.rb
66
+ homepage: https://github.com/itkq/cloudwatch_chrono
67
+ licenses: []
68
+ metadata:
69
+ homepage_uri: https://github.com/itkq/cloudwatch_chrono
70
+ source_code_uri: https://github.com/itkq/cloudwatch_chrono
71
+ changelog_uri: https://github.com/itkq/cloudwatch_chrono/blob/master/CHANGELOG.md
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.3.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.1.2
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Chronology for CloudWatch cron expression.
91
+ test_files: []