cron_swanson 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5b92fde06c654c2a37b0162a87e0d1701db2d24f28ae707bbfa174d168d73aa
4
- data.tar.gz: 33d169cfefdb9e1ab75aca3b93f2aedfc2f6c0d6634df1f9a536115b5a964bc9
3
+ metadata.gz: d117286d60628186a24607e387fb3b51eb4b915d8e75a96d92ebfce8ea51a9ed
4
+ data.tar.gz: 7d8fb576c1aead5cbe16c2ccd81f1acda7f96149fb725bd780359a4ba8d2360c
5
5
  SHA512:
6
- metadata.gz: abd309004421dc9cf12886a4e9df6a58717ab51dc74c2cee0ab4fbdbbb4cb4248b462d4d684f6113a0103a8999559eac2cb51580467c6c5ed18b9ac14d73a1b6
7
- data.tar.gz: af381bd1b0054755f8a08248962f546db9ce82ea0706657ace6f47fc5c9aa0425266de8295ee39bfe523bfc2b78e3d3243aa94c1eb8a49edd40ac355f7296557
6
+ metadata.gz: 99a782ff24a871fe8f5ea77f5a4e26c06b0af27519f03873dd62d4e2a5e9a2cd7da478230d3283fcb6b9af28ae03b69d7c3ea64f650683bba7d5f6786017abed
7
+ data.tar.gz: e054da7a55fd73c7a0f8c00e4912f13572467840984b45cb7a1d3041a4c13efdbc9ab965fd13748f26a16eea94afcdad86dba227cf1e2c487d52b97dacd199c8
data/.rspec CHANGED
@@ -1 +1 @@
1
- --require spec_helper
1
+ --require spec_helper --format d
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4
4
+ - 2.5
5
+ - 2.6
6
+ script:
7
+ - bundle exec rake spec
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 : "Under my utelage, you will grow from boys to men." : November 3, 2019
4
+
5
+ Improvements in whenever integration.
6
+
7
+ * Jobs are scheduled based on their contents, not their source location.
8
+ * Added support for `roles`.
9
+
10
+ ## 0.1.0 : "I'm not interested in caring about people." : November 1, 2019
11
+
12
+ Initial release
data/README.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # CronSwanson
2
2
 
3
- CronSwanson can help if you have many systems running the same cron job, but you
4
- don't want them all to start at exactly the same time. (To prevent daily load
5
- spikes at midnight, as every single app starts its maintenance cron jobs.)
3
+ `CronSwanson` helps schedule cron jobs.
4
+
5
+ ![Never half-ass two things.](whole-ass.jpg)
6
+
7
+ If you've ever had load spikes when many applications all starting the same
8
+ cron job at the same time, `CronSwanson` can help you.
9
+
10
+ The library generates crontab schedule strings which are consistent (they aren't
11
+ random) but which are fuzzed/shifted depending on some input.
12
+
13
+ ## Build Status
14
+
15
+ [![Build Status](https://travis-ci.org/alexdean/cron_swanson.svg?branch=master)](https://travis-ci.org/alexdean/cron_swanson)
6
16
 
7
17
  ## Installation
8
18
 
@@ -32,6 +42,17 @@ CronSwanson.schedule 'whiskey'
32
42
  #=> "33 18 * * *"
33
43
  ```
34
44
 
45
+ **To keep two applications running the same job from executing at once**, make the
46
+ application name part of the schedule key.
47
+
48
+ ```ruby
49
+ CronSwanson.schedule 'application-a whiskey'
50
+ #=> "4 19 * * *"
51
+
52
+ CronSwanson.schedule 'application-b whiskey'
53
+ #=> "11 7 * * *"
54
+ ```
55
+
35
56
  An `interval` (in seconds) can be supplied if you want a job to be run more than
36
57
  once/day. This `interval` must be a factor of 24 hours.
37
58
 
@@ -40,14 +61,27 @@ CronSwanson.schedule 'bacon', interval: 60 * 60 * 4
40
61
  #=> "26 2,6,10,14,18,22 * * *"
41
62
  ```
42
63
 
64
+ You can also use `ActiveSupport::Duration` instances.
65
+
66
+ ```ruby
67
+ CronSwanson.schedule 'bacon', interval: 4.hours
68
+ #=> "26 2,6,10,14,18,22 * * *"
69
+ ```
70
+
43
71
  ### Whenever Integration
44
72
 
45
73
  `CronSwanson` is built to integrate with the fantastic [whenever](https://github.com/javan/whenever) gem.
46
74
 
75
+ `CronSwanson::Whenever.add` will calculate a run time for jobs by hashing the text
76
+ of the job definitions in the given block.
77
+
78
+ **NOTE**: This means that if you change the jobs in the block, you will also change the schedule time
79
+ for these jobs.
80
+
47
81
  #### Daily
48
82
 
49
83
  ```ruby
50
- # in the config/schedule.rb file
84
+ # in config/schedule.rb
51
85
  CronSwanson::Whenever.add(self) do
52
86
  rake 'sample:job'
53
87
  end
@@ -59,7 +93,7 @@ determined by `CronSwanson`.
59
93
  #### Multiple times/day
60
94
 
61
95
  ```ruby
62
- # in the config/schedule.rb file
96
+ # in config/schedule.rb
63
97
 
64
98
  # with ActiveSupport
65
99
  CronSwanson::Whenever.add(self, interval: 4.hours) do
@@ -74,7 +108,7 @@ end
74
108
 
75
109
  #### job types
76
110
 
77
- The block is evaluated by `whenever`, so any custom job types will work.
111
+ Any custom job types which have been defined will work.
78
112
 
79
113
  ```ruby
80
114
  # in config/schedule.rb
@@ -85,15 +119,21 @@ CronSwanson::Whenever.add(self) do
85
119
  end
86
120
  ```
87
121
 
88
- #### Limitation
122
+ #### roles
89
123
 
90
- The whenever integration code currently derives a scheduled time from the source
91
- location of the `add` call. This means that moving the `.add` invocation to
92
- a different line in schedule.rb will cause it to be run at a different time.
124
+ Roles are supported. See the [whenever documentation](https://github.com/javan/whenever#capistrano-roles)
125
+ for more information on this.
93
126
 
94
- This limitation exists because I (currently) don't know of a way to inspect
95
- the contents of a block at runtime. If a way to do this can be found, I
96
- would prefer to calculate the time based on the block's contents.
127
+ ```ruby
128
+ CronSwanson::Whenever.add(self) do
129
+ rake 'will_run_on_all_roles'
130
+ end
131
+
132
+ # will only be added to servers with the :restricted role
133
+ CronSwanson::Whenever.add(self, roles: [:restricted]) do
134
+ rake 'restricted_only'
135
+ end
136
+ ```
97
137
 
98
138
  ## Development
99
139
 
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc 'Run all specs in spec directory (excluding plugin specs)'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
2
7
  task default: :spec
@@ -15,7 +15,7 @@ module CronSwanson
15
15
  # offset within a time period
16
16
  #
17
17
  # if the interval is 6 hours, the returned offset will be some number of seconds
18
- # from 0 to 6 hours.
18
+ # between 0 and 60 * 60 * 6 seconds (6 hours).
19
19
  #
20
20
  # @param [String] job_identifier
21
21
  # if nil, method will determine this on its own
@@ -27,7 +27,7 @@ module CronSwanson
27
27
  # largest possible hex sha256 value
28
28
  max_sha256_value = (16**64).to_f
29
29
 
30
- # what % of the max sha256 is the current app?
30
+ # what % of the max sha256 is the job_identifier?
31
31
  sha_pct_of_max_sha256 = sha.to_i(16) / max_sha256_value
32
32
 
33
33
  # apply that same % to the desired interval to get an offset
@@ -49,8 +49,6 @@ module CronSwanson
49
49
  # figure out how many times job will happen in a day
50
50
  runs_per_day = SECONDS_PER_DAY / interval
51
51
 
52
- # raise if runs_per_day has a decimal component.
53
-
54
52
  run_at = Time.at(offset(job_identifier, interval: interval)).utc
55
53
 
56
54
  hours = []
@@ -1,3 +1,3 @@
1
1
  module CronSwanson
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  module CronSwanson
2
2
  # integration for the whenever gem: https://github.com/javan/whenever
3
- module Whenever
3
+ class Whenever
4
4
  # CronSwanson integration for whenever
5
5
  #
6
6
  # The given block can use any job types understood by your whenever configuration.
@@ -15,13 +15,13 @@ module CronSwanson
15
15
  # would prefer to calculate the time based on the block's contents.
16
16
  #
17
17
  # @example run a job once/day
18
- # # in the config/schedule.rb file
18
+ # # in config/schedule.rb
19
19
  # CronSwanson::Whenever.add(self) do
20
20
  # rake 'job'
21
21
  # end
22
22
  #
23
- # @example run a job four times daily
24
- # # in the config/schedule.rb file
23
+ # @example schedule a job to four times daily
24
+ # # in config/schedule.rb
25
25
  #
26
26
  # # with ActiveSupport
27
27
  # CronSwanson::Whenever.add(self, interval: 4.hours) do
@@ -33,19 +33,61 @@ module CronSwanson
33
33
  # rake 'job'
34
34
  # end
35
35
  #
36
+ # @example run a job only on servers with a given role
37
+ # # in config/schedule.rb
38
+ # CronSwanson::Whenever.add(self, roles: [:app]) do
39
+ # rake 'job'
40
+ # end
41
+ #
36
42
  # @param [Whenever::JobList] whenever_job_list For code in `config/schedule.rb`
37
43
  # this can be referred to as `self`.
38
- # @param [Integer] interval how many seconds do you want between runs of this job
39
- def self.add(whenever_job_list, interval: CronSwanson.default_interval, &block)
44
+ # @param [Integer, ActiveSupport::Duration] interval how many seconds do you want between runs
45
+ # of this job
46
+ # @param [Array<Symbol>] roles capistrano roles that jobs in this block should be deployed to
47
+ def self.add(whenever_job_list, interval: CronSwanson.default_interval, roles: [], &block)
48
+ @whenever_jobs = []
49
+ @whenever_job_list = whenever_job_list
50
+
40
51
  if !whenever_job_list.is_a?(::Whenever::JobList)
41
52
  raise ArgumentError, "supply a Whenever::JobList. (In schedule.rb code, use `self`.)"
42
53
  end
43
54
 
44
55
  raise ArgumentError, "provide a block containing jobs to schedule." if !block_given?
45
56
 
46
- # TODO: ideally we'd hash the contents of the block, not the location it was defined at
47
- schedule = CronSwanson.schedule(block.source_location, interval: interval)
48
- whenever_job_list.every(schedule, &Proc.new)
57
+ # execute the block in the context of CronSwanson::Whenever (rather than in the context
58
+ # of the Whenever::JobList where it will be invoked) so that we can intercept
59
+ # calls to `rake` and similar (via method_missing below).
60
+ instance_eval(&block)
61
+
62
+ # make a schedule based on the contents of the jobs which were defined in the block
63
+ schedule_seed = @whenever_jobs.map do |job_config|
64
+ m, args, _block = *job_config
65
+ "#{m} #{args.join}"
66
+ end
67
+ schedule = CronSwanson.schedule(schedule_seed, interval: interval)
68
+
69
+ # now that we know when to schedule the jobs, actually pass the block to Whenever
70
+ if roles.size > 0
71
+ whenever_job_list.every(schedule, roles: roles, &Proc.new)
72
+ else
73
+ whenever_job_list.every(schedule, &Proc.new)
74
+ end
75
+
76
+ @whenever_job_list = nil
77
+ end
78
+
79
+ # during .add, we accumulate calls to whenever job types
80
+ # this allows us to make a schedule hash from the actual jobs which are defined.
81
+ def self.method_missing(m, *args, &block)
82
+ if @whenever_job_list.nil? || @whenever_jobs.nil?
83
+ raise "#{self.name}.method_missing invoked outside of #{self.name}.add"
84
+ end
85
+
86
+ if @whenever_job_list.respond_to?(m)
87
+ @whenever_jobs << [m, args, block]
88
+ else
89
+ raise "#{m} is not defined. Call `job_type` to resolve this."
90
+ end
49
91
  end
50
92
  end
51
93
  end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cron_swanson
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dean
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-01 00:00:00.000000000 Z
11
+ date: 2019-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -49,6 +49,8 @@ files:
49
49
  - ".rspec"
50
50
  - ".rubocop.yml"
51
51
  - ".ruby-version"
52
+ - ".travis.yml"
53
+ - CHANGELOG.md
52
54
  - Gemfile
53
55
  - Guardfile
54
56
  - README.md
@@ -62,6 +64,7 @@ files:
62
64
  - lib/cron_swanson.rb
63
65
  - lib/cron_swanson/version.rb
64
66
  - lib/cron_swanson/whenever.rb
67
+ - whole-ass.jpg
65
68
  homepage: https://github.com/alexdean/cron_swanson
66
69
  licenses: []
67
70
  metadata: {}