cloudtasker 0.7.0 → 0.9.1

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +41 -0
  6. data/README.md +145 -25
  7. data/_config.yml +1 -0
  8. data/app/controllers/cloudtasker/worker_controller.rb +21 -5
  9. data/cloudtasker.gemspec +2 -2
  10. data/docs/BATCH_JOBS.md +28 -3
  11. data/docs/CRON_JOBS.md +3 -1
  12. data/exe/cloudtasker +13 -1
  13. data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +26 -9
  14. data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +26 -9
  15. data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +27 -10
  16. data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +26 -9
  17. data/gemfiles/rails_5.2.gemfile.lock +28 -11
  18. data/gemfiles/rails_6.0.gemfile.lock +29 -12
  19. data/lib/cloudtasker.rb +1 -1
  20. data/lib/cloudtasker/backend/google_cloud_task.rb +65 -12
  21. data/lib/cloudtasker/backend/memory_task.rb +5 -3
  22. data/lib/cloudtasker/backend/redis_task.rb +24 -13
  23. data/lib/cloudtasker/batch/batch_progress.rb +11 -2
  24. data/lib/cloudtasker/batch/job.rb +18 -4
  25. data/lib/cloudtasker/cli.rb +6 -5
  26. data/lib/cloudtasker/cloud_task.rb +6 -2
  27. data/lib/cloudtasker/config.rb +33 -9
  28. data/lib/cloudtasker/cron/job.rb +2 -2
  29. data/lib/cloudtasker/cron/schedule.rb +26 -14
  30. data/lib/cloudtasker/local_server.rb +44 -22
  31. data/lib/cloudtasker/max_task_size_exceeded_error.rb +14 -0
  32. data/lib/cloudtasker/redis_client.rb +10 -7
  33. data/lib/cloudtasker/unique_job/job.rb +2 -2
  34. data/lib/cloudtasker/version.rb +1 -1
  35. data/lib/cloudtasker/worker.rb +45 -10
  36. data/lib/cloudtasker/worker_handler.rb +7 -5
  37. data/lib/cloudtasker/worker_logger.rb +1 -1
  38. data/lib/cloudtasker/worker_wrapper.rb +52 -0
  39. data/lib/tasks/setup_queue.rake +12 -2
  40. metadata +7 -6
  41. data/Gemfile.lock +0 -280
  42. data/lib/cloudtasker/railtie.rb +0 -10
@@ -4,19 +4,21 @@ require 'redis'
4
4
 
5
5
  module Cloudtasker
6
6
  # A wrapper with helper methods for redis
7
- module RedisClient
8
- module_function
9
-
7
+ class RedisClient
10
8
  # Suffix added to cache keys when locking them
11
9
  LOCK_KEY_PREFIX = 'cloudtasker/lock'
12
10
 
11
+ def self.client
12
+ @client ||= Redis.new(Cloudtasker.config.redis || {})
13
+ end
14
+
13
15
  #
14
16
  # Return the underlying redis client.
15
17
  #
16
18
  # @return [Redis] The redis client.
17
19
  #
18
20
  def client
19
- @client ||= Redis.new(Cloudtasker.config.redis || {})
21
+ @client ||= self.class.client
20
22
  end
21
23
 
22
24
  #
@@ -50,9 +52,10 @@ module Cloudtasker
50
52
  # Acquire a lock on a cache entry.
51
53
  #
52
54
  # @example
53
- # RedisClient.with_lock('foo')
54
- # content = RedisClient.fetch('foo')
55
- # RedisClient.set(content.merge(bar: 'bar).to_json)
55
+ # redis = RedisClient.new
56
+ # redis.with_lock('foo')
57
+ # content = redis.fetch('foo')
58
+ # redis.set(content.merge(bar: 'bar).to_json)
56
59
  # end
57
60
  #
58
61
  # @param [String] cache_key The cache key to access.
@@ -100,10 +100,10 @@ module Cloudtasker
100
100
  #
101
101
  # Return the Cloudtasker redis client.
102
102
  #
103
- # @return [Class] The Cloudtasker::RedisClient wrapper.
103
+ # @return [Cloudtasker::RedisClient] The cloudtasker redis client.
104
104
  #
105
105
  def redis
106
- Cloudtasker::RedisClient
106
+ @redis ||= Cloudtasker::RedisClient.new
107
107
  end
108
108
 
109
109
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.7.0'
4
+ VERSION = '0.9.1'
5
5
  end
@@ -6,6 +6,7 @@ module Cloudtasker
6
6
  # Add class method to including class
7
7
  def self.included(base)
8
8
  base.extend(ClassMethods)
9
+ base.attr_writer :job_queue
9
10
  base.attr_accessor :job_args, :job_id, :job_meta, :job_reenqueued, :job_retries
10
11
  end
11
12
 
@@ -34,7 +35,7 @@ module Cloudtasker
34
35
  def self.from_hash(hash)
35
36
  # Symbolize metadata keys and stringify job arguments
36
37
  payload = JSON.parse(hash.to_json, symbolize_names: true)
37
- payload[:job_args] = JSON.parse(hash[:job_args].to_json)
38
+ payload[:job_args] = JSON.parse(payload[:job_args].to_json)
38
39
 
39
40
  # Extract worker parameters
40
41
  klass_name = payload&.dig(:worker)
@@ -45,7 +46,7 @@ module Cloudtasker
45
46
  return nil unless worker_klass.include?(self)
46
47
 
47
48
  # Return instantiated worker
48
- worker_klass.new(payload.slice(:job_args, :job_id, :job_meta, :job_retries))
49
+ worker_klass.new(payload.slice(:job_queue, :job_args, :job_id, :job_meta, :job_retries))
49
50
  rescue NameError
50
51
  nil
51
52
  end
@@ -81,7 +82,7 @@ module Cloudtasker
81
82
  # @return [Cloudtasker::CloudTask] The Google Task response
82
83
  #
83
84
  def perform_async(*args)
84
- perform_in(nil, *args)
85
+ schedule(args: args)
85
86
  end
86
87
 
87
88
  #
@@ -93,7 +94,7 @@ module Cloudtasker
93
94
  # @return [Cloudtasker::CloudTask] The Google Task response
94
95
  #
95
96
  def perform_in(interval, *args)
96
- new(job_args: args).schedule(interval: interval)
97
+ schedule(args: args, time_in: interval)
97
98
  end
98
99
 
99
100
  #
@@ -105,7 +106,21 @@ module Cloudtasker
105
106
  # @return [Cloudtasker::CloudTask] The Google Task response
106
107
  #
107
108
  def perform_at(time_at, *args)
108
- new(job_args: args).schedule(time_at: time_at)
109
+ schedule(args: args, time_at: time_at)
110
+ end
111
+
112
+ #
113
+ # Enqueue a worker with explicity options.
114
+ #
115
+ # @param [Array<any>] args The job arguments.
116
+ # @param [Time, Integer] time_in The delay in seconds.
117
+ # @param [Time, Integer] time_at The time at which the job should run.
118
+ # @param [String, Symbol] queue The queue on which the worker should run.
119
+ #
120
+ # @return [Cloudtasker::CloudTask] The Google Task response
121
+ #
122
+ def schedule(args: nil, time_in: nil, time_at: nil, queue: nil)
123
+ new(job_args: args, job_queue: queue).schedule({ interval: time_in, time_at: time_at }.compact)
109
124
  end
110
125
 
111
126
  #
@@ -124,11 +139,30 @@ module Cloudtasker
124
139
  # @param [Array<any>] job_args The list of perform args.
125
140
  # @param [String] job_id A unique ID identifying this job.
126
141
  #
127
- def initialize(job_args: [], job_id: nil, job_meta: {}, job_retries: 0)
128
- @job_args = job_args
142
+ def initialize(job_queue: nil, job_args: nil, job_id: nil, job_meta: {}, job_retries: 0)
143
+ @job_args = job_args || []
129
144
  @job_id = job_id || SecureRandom.uuid
130
145
  @job_meta = MetaStore.new(job_meta)
131
146
  @job_retries = job_retries || 0
147
+ @job_queue = job_queue
148
+ end
149
+
150
+ #
151
+ # Return the class name of the worker.
152
+ #
153
+ # @return [String] The class name.
154
+ #
155
+ def job_class_name
156
+ self.class.to_s
157
+ end
158
+
159
+ #
160
+ # Return the queue to use for this worker.
161
+ #
162
+ # @return [String] The name of queue.
163
+ #
164
+ def job_queue
165
+ (@job_queue ||= self.class.cloudtasker_options_hash[:queue] || Config::DEFAULT_JOB_QUEUE).to_s
132
166
  end
133
167
 
134
168
  #
@@ -199,10 +233,10 @@ module Cloudtasker
199
233
  # Return a new instance of the worker with the same args and metadata
200
234
  # but with a different id.
201
235
  #
202
- # @return [<Type>] <description>
236
+ # @return [Cloudtasker::Worker] <description>
203
237
  #
204
238
  def new_instance
205
- self.class.new(job_args: job_args, job_meta: job_meta)
239
+ self.class.new(job_queue: job_queue, job_args: job_args, job_meta: job_meta)
206
240
  end
207
241
 
208
242
  #
@@ -216,7 +250,8 @@ module Cloudtasker
216
250
  job_id: job_id,
217
251
  job_args: job_args,
218
252
  job_meta: job_meta.to_h,
219
- job_retries: job_retries
253
+ job_retries: job_retries,
254
+ job_queue: job_queue
220
255
  }
221
256
  end
222
257
 
@@ -5,7 +5,7 @@ require 'google/cloud/tasks'
5
5
  module Cloudtasker
6
6
  # Build, serialize and schedule tasks on the processing backend.
7
7
  class WorkerHandler
8
- attr_reader :worker, :job_args
8
+ attr_reader :worker
9
9
 
10
10
  # Alrogith used to sign the verification token
11
11
  JWT_ALG = 'HS256'
@@ -42,11 +42,12 @@ module Cloudtasker
42
42
  http_method: 'POST',
43
43
  url: Cloudtasker.config.processor_url,
44
44
  headers: {
45
- 'Content-Type' => 'application/json',
46
- 'Authorization' => "Bearer #{Authenticator.verification_token}"
45
+ Cloudtasker::Config::CONTENT_TYPE_HEADER => 'application/json',
46
+ Cloudtasker::Config::AUTHORIZATION_HEADER => "Bearer #{Authenticator.verification_token}"
47
47
  },
48
48
  body: worker_payload.to_json
49
- }
49
+ },
50
+ queue: worker.job_queue
50
51
  }
51
52
  end
52
53
 
@@ -64,7 +65,8 @@ module Cloudtasker
64
65
  #
65
66
  def worker_payload
66
67
  @worker_payload ||= {
67
- worker: worker.class.to_s,
68
+ worker: worker.job_class_name,
69
+ job_queue: worker.job_queue,
68
70
  job_id: worker.job_id,
69
71
  job_args: worker.job_args,
70
72
  job_meta: worker.job_meta.to_h
@@ -11,7 +11,7 @@ module Cloudtasker
11
11
  end
12
12
 
13
13
  # Only log the job meta information by default (exclude arguments)
14
- DEFAULT_CONTEXT_PROCESSOR = ->(worker) { worker.to_h.slice(:worker, :job_id, :job_meta) }
14
+ DEFAULT_CONTEXT_PROCESSOR = ->(worker) { worker.to_h.slice(:worker, :job_id, :job_meta, :job_queue) }
15
15
 
16
16
  #
17
17
  # Build a new instance of the class.
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cloudtasker/worker'
4
+
5
+ module Cloudtasker
6
+ # A worker class used to schedule jobs without actually
7
+ # instantiating the worker class. This is useful for middlewares
8
+ # needing to enqueue jobs in a Rails initializer. Rails 6 complains
9
+ # about instantiating workers in an iniitializer because of autoloading
10
+ # in zeitwerk mode.
11
+ #
12
+ # Downside of this wrapper: any cloudtasker_options specified on on the
13
+ # worker_class will be ignored.
14
+ #
15
+ # See: https://github.com/rails/rails/issues/36363
16
+ #
17
+ class WorkerWrapper
18
+ include Worker
19
+
20
+ attr_accessor :worker_name
21
+
22
+ #
23
+ # Build a new instance of the class.
24
+ #
25
+ # @param [String] worker_class The name of the worker class.
26
+ # @param [Hash] **opts The worker arguments.
27
+ #
28
+ def initialize(worker_name:, **opts)
29
+ @worker_name = worker_name
30
+ super(opts)
31
+ end
32
+
33
+ #
34
+ # Override parent. Return the underlying worker class name.
35
+ #
36
+ # @return [String] The worker class.
37
+ #
38
+ def job_class_name
39
+ worker_name
40
+ end
41
+
42
+ #
43
+ # Return a new instance of the worker with the same args and metadata
44
+ # but with a different id.
45
+ #
46
+ # @return [Cloudtasker::WorkerWrapper] <description>
47
+ #
48
+ def new_instance
49
+ self.class.new(worker_name: worker_name, job_queue: job_queue, job_args: job_args, job_meta: job_meta)
50
+ end
51
+ end
52
+ end
@@ -1,10 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cloudtasker/backend/google_cloud_task'
4
+ require 'cloudtasker/config'
5
+
6
+ ENV['GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS'] ||= 'true'
4
7
 
5
8
  namespace :cloudtasker do
6
- desc 'Setup the Cloud Task queue'
9
+ desc 'Setup a Cloud Task queue. (default options: ' \
10
+ "name=#{Cloudtasker::Config::DEFAULT_JOB_QUEUE}, " \
11
+ "concurrency=#{Cloudtasker::Config::DEFAULT_QUEUE_CONCURRENCY}, " \
12
+ "retries=#{Cloudtasker::Config::DEFAULT_QUEUE_RETRIES})"
7
13
  task setup_queue: :environment do
8
- Cloudtasker::Backend::GoogleCloudTask.setup_queue
14
+ puts Cloudtasker::Backend::GoogleCloudTask.setup_queue(
15
+ name: ENV['name'],
16
+ concurrency: ENV['concurrency'],
17
+ retries: ENV['retries']
18
+ )
9
19
  end
10
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudtasker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-25 00:00:00.000000000 Z
11
+ date: 2020-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -248,7 +248,7 @@ dependencies:
248
248
  - - ">="
249
249
  - !ruby/object:Gem::Version
250
250
  version: '0'
251
- description: Background jobs for Ruby using Google Cloud Tasks (alpha)
251
+ description: Background jobs for Ruby using Google Cloud Tasks (beta)
252
252
  email:
253
253
  - arnaud.lachaume@keypup.io
254
254
  executables:
@@ -264,10 +264,10 @@ files:
264
264
  - CHANGELOG.md
265
265
  - CODE_OF_CONDUCT.md
266
266
  - Gemfile
267
- - Gemfile.lock
268
267
  - LICENSE.txt
269
268
  - README.md
270
269
  - Rakefile
270
+ - _config.yml
271
271
  - app/controllers/cloudtasker/application_controller.rb
272
272
  - app/controllers/cloudtasker/worker_controller.rb
273
273
  - bin/console
@@ -320,9 +320,9 @@ files:
320
320
  - lib/cloudtasker/engine.rb
321
321
  - lib/cloudtasker/invalid_worker_error.rb
322
322
  - lib/cloudtasker/local_server.rb
323
+ - lib/cloudtasker/max_task_size_exceeded_error.rb
323
324
  - lib/cloudtasker/meta_store.rb
324
325
  - lib/cloudtasker/middleware/chain.rb
325
- - lib/cloudtasker/railtie.rb
326
326
  - lib/cloudtasker/redis_client.rb
327
327
  - lib/cloudtasker/testing.rb
328
328
  - lib/cloudtasker/unique_job.rb
@@ -344,6 +344,7 @@ files:
344
344
  - lib/cloudtasker/worker.rb
345
345
  - lib/cloudtasker/worker_handler.rb
346
346
  - lib/cloudtasker/worker_logger.rb
347
+ - lib/cloudtasker/worker_wrapper.rb
347
348
  - lib/tasks/setup_queue.rake
348
349
  homepage: https://github.com/keypup-io/cloudtasker
349
350
  licenses:
@@ -371,5 +372,5 @@ rubyforge_project:
371
372
  rubygems_version: 2.7.9
372
373
  signing_key:
373
374
  specification_version: 4
374
- summary: Background jobs for Ruby using Google Cloud Tasks (alpha)
375
+ summary: Background jobs for Ruby using Google Cloud Tasks (beta)
375
376
  test_files: []
@@ -1,280 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cloudtasker (0.7.0)
5
- activesupport
6
- fugit
7
- google-cloud-tasks
8
- jwt
9
- redis
10
-
11
- GEM
12
- remote: https://rubygems.org/
13
- specs:
14
- actioncable (6.0.0)
15
- actionpack (= 6.0.0)
16
- nio4r (~> 2.0)
17
- websocket-driver (>= 0.6.1)
18
- actionmailbox (6.0.0)
19
- actionpack (= 6.0.0)
20
- activejob (= 6.0.0)
21
- activerecord (= 6.0.0)
22
- activestorage (= 6.0.0)
23
- activesupport (= 6.0.0)
24
- mail (>= 2.7.1)
25
- actionmailer (6.0.0)
26
- actionpack (= 6.0.0)
27
- actionview (= 6.0.0)
28
- activejob (= 6.0.0)
29
- mail (~> 2.5, >= 2.5.4)
30
- rails-dom-testing (~> 2.0)
31
- actionpack (6.0.0)
32
- actionview (= 6.0.0)
33
- activesupport (= 6.0.0)
34
- rack (~> 2.0)
35
- rack-test (>= 0.6.3)
36
- rails-dom-testing (~> 2.0)
37
- rails-html-sanitizer (~> 1.0, >= 1.2.0)
38
- actiontext (6.0.0)
39
- actionpack (= 6.0.0)
40
- activerecord (= 6.0.0)
41
- activestorage (= 6.0.0)
42
- activesupport (= 6.0.0)
43
- nokogiri (>= 1.8.5)
44
- actionview (6.0.0)
45
- activesupport (= 6.0.0)
46
- builder (~> 3.1)
47
- erubi (~> 1.4)
48
- rails-dom-testing (~> 2.0)
49
- rails-html-sanitizer (~> 1.1, >= 1.2.0)
50
- activejob (6.0.0)
51
- activesupport (= 6.0.0)
52
- globalid (>= 0.3.6)
53
- activemodel (6.0.0)
54
- activesupport (= 6.0.0)
55
- activerecord (6.0.0)
56
- activemodel (= 6.0.0)
57
- activesupport (= 6.0.0)
58
- activestorage (6.0.0)
59
- actionpack (= 6.0.0)
60
- activejob (= 6.0.0)
61
- activerecord (= 6.0.0)
62
- marcel (~> 0.3.1)
63
- activesupport (6.0.0)
64
- concurrent-ruby (~> 1.0, >= 1.0.2)
65
- i18n (>= 0.7, < 2)
66
- minitest (~> 5.1)
67
- tzinfo (~> 1.1)
68
- zeitwerk (~> 2.1, >= 2.1.8)
69
- addressable (2.7.0)
70
- public_suffix (>= 2.0.2, < 5.0)
71
- appraisal (2.2.0)
72
- bundler
73
- rake
74
- thor (>= 0.14.0)
75
- ast (2.4.0)
76
- builder (3.2.3)
77
- concurrent-ruby (1.1.5)
78
- crack (0.4.3)
79
- safe_yaml (~> 1.0.0)
80
- crass (1.0.5)
81
- diff-lcs (1.3)
82
- erubi (1.9.0)
83
- et-orbi (1.2.2)
84
- tzinfo
85
- faraday (0.17.0)
86
- multipart-post (>= 1.2, < 3)
87
- faraday-http-cache (2.0.0)
88
- faraday (~> 0.8)
89
- fugit (1.3.3)
90
- et-orbi (~> 1.1, >= 1.1.8)
91
- raabro (~> 1.1)
92
- github_changelog_generator (1.15.0)
93
- activesupport
94
- faraday-http-cache
95
- multi_json
96
- octokit (~> 4.6)
97
- rainbow (>= 2.2.1)
98
- rake (>= 10.0)
99
- retriable (~> 3.0)
100
- globalid (0.4.2)
101
- activesupport (>= 4.2.0)
102
- google-cloud-tasks (1.3.1)
103
- google-gax (~> 1.8)
104
- googleapis-common-protos (>= 1.3.9, < 2.0)
105
- googleapis-common-protos-types (>= 1.0.4, < 2.0)
106
- grpc-google-iam-v1 (~> 0.6.9)
107
- google-gax (1.8.1)
108
- google-protobuf (~> 3.9)
109
- googleapis-common-protos (>= 1.3.9, < 2.0)
110
- googleauth (~> 0.9)
111
- grpc (~> 1.24)
112
- rly (~> 0.2.3)
113
- google-protobuf (3.10.1-universal-darwin)
114
- googleapis-common-protos (1.3.9)
115
- google-protobuf (~> 3.0)
116
- googleapis-common-protos-types (~> 1.0)
117
- grpc (~> 1.0)
118
- googleapis-common-protos-types (1.0.4)
119
- google-protobuf (~> 3.0)
120
- googleauth (0.10.0)
121
- faraday (~> 0.12)
122
- jwt (>= 1.4, < 3.0)
123
- memoist (~> 0.16)
124
- multi_json (~> 1.11)
125
- os (>= 0.9, < 2.0)
126
- signet (~> 0.12)
127
- grpc (1.25.0-universal-darwin)
128
- google-protobuf (~> 3.8)
129
- googleapis-common-protos-types (~> 1.0)
130
- grpc-google-iam-v1 (0.6.9)
131
- googleapis-common-protos (>= 1.3.1, < 2.0)
132
- grpc (~> 1.0)
133
- hashdiff (1.0.0)
134
- i18n (1.7.0)
135
- concurrent-ruby (~> 1.0)
136
- jaro_winkler (1.5.4)
137
- jwt (2.2.1)
138
- loofah (2.3.1)
139
- crass (~> 1.0.2)
140
- nokogiri (>= 1.5.9)
141
- mail (2.7.1)
142
- mini_mime (>= 0.1.1)
143
- marcel (0.3.3)
144
- mimemagic (~> 0.3.2)
145
- memoist (0.16.1)
146
- method_source (0.9.2)
147
- mimemagic (0.3.3)
148
- mini_mime (1.0.2)
149
- mini_portile2 (2.4.0)
150
- minitest (5.13.0)
151
- multi_json (1.14.1)
152
- multipart-post (2.1.1)
153
- nio4r (2.5.2)
154
- nokogiri (1.10.5)
155
- mini_portile2 (~> 2.4.0)
156
- octokit (4.14.0)
157
- sawyer (~> 0.8.0, >= 0.5.3)
158
- os (1.0.1)
159
- parallel (1.18.0)
160
- parser (2.6.5.0)
161
- ast (~> 2.4.0)
162
- public_suffix (4.0.1)
163
- raabro (1.1.6)
164
- rack (2.0.7)
165
- rack-test (1.1.0)
166
- rack (>= 1.0, < 3)
167
- rails (6.0.0)
168
- actioncable (= 6.0.0)
169
- actionmailbox (= 6.0.0)
170
- actionmailer (= 6.0.0)
171
- actionpack (= 6.0.0)
172
- actiontext (= 6.0.0)
173
- actionview (= 6.0.0)
174
- activejob (= 6.0.0)
175
- activemodel (= 6.0.0)
176
- activerecord (= 6.0.0)
177
- activestorage (= 6.0.0)
178
- activesupport (= 6.0.0)
179
- bundler (>= 1.3.0)
180
- railties (= 6.0.0)
181
- sprockets-rails (>= 2.0.0)
182
- rails-dom-testing (2.0.3)
183
- activesupport (>= 4.2.0)
184
- nokogiri (>= 1.6)
185
- rails-html-sanitizer (1.3.0)
186
- loofah (~> 2.3)
187
- railties (6.0.0)
188
- actionpack (= 6.0.0)
189
- activesupport (= 6.0.0)
190
- method_source
191
- rake (>= 0.8.7)
192
- thor (>= 0.20.3, < 2.0)
193
- rainbow (3.0.0)
194
- rake (10.5.0)
195
- redis (4.1.3)
196
- retriable (3.1.2)
197
- rly (0.2.3)
198
- rspec (3.9.0)
199
- rspec-core (~> 3.9.0)
200
- rspec-expectations (~> 3.9.0)
201
- rspec-mocks (~> 3.9.0)
202
- rspec-core (3.9.0)
203
- rspec-support (~> 3.9.0)
204
- rspec-expectations (3.9.0)
205
- diff-lcs (>= 1.2.0, < 2.0)
206
- rspec-support (~> 3.9.0)
207
- rspec-mocks (3.9.0)
208
- diff-lcs (>= 1.2.0, < 2.0)
209
- rspec-support (~> 3.9.0)
210
- rspec-rails (3.9.0)
211
- actionpack (>= 3.0)
212
- activesupport (>= 3.0)
213
- railties (>= 3.0)
214
- rspec-core (~> 3.9.0)
215
- rspec-expectations (~> 3.9.0)
216
- rspec-mocks (~> 3.9.0)
217
- rspec-support (~> 3.9.0)
218
- rspec-support (3.9.0)
219
- rubocop (0.76.0)
220
- jaro_winkler (~> 1.5.1)
221
- parallel (~> 1.10)
222
- parser (>= 2.6)
223
- rainbow (>= 2.2.2, < 4.0)
224
- ruby-progressbar (~> 1.7)
225
- unicode-display_width (>= 1.4.0, < 1.7)
226
- rubocop-rspec (1.36.0)
227
- rubocop (>= 0.68.1)
228
- ruby-progressbar (1.10.1)
229
- safe_yaml (1.0.5)
230
- sawyer (0.8.2)
231
- addressable (>= 2.3.5)
232
- faraday (> 0.8, < 2.0)
233
- signet (0.12.0)
234
- addressable (~> 2.3)
235
- faraday (~> 0.9)
236
- jwt (>= 1.5, < 3.0)
237
- multi_json (~> 1.10)
238
- sprockets (4.0.0)
239
- concurrent-ruby (~> 1.0)
240
- rack (> 1, < 3)
241
- sprockets-rails (3.2.1)
242
- actionpack (>= 4.0)
243
- activesupport (>= 4.0)
244
- sprockets (>= 3.0.0)
245
- sqlite3 (1.4.0)
246
- thor (0.20.3)
247
- thread_safe (0.3.6)
248
- timecop (0.9.1)
249
- tzinfo (1.2.5)
250
- thread_safe (~> 0.1)
251
- unicode-display_width (1.6.0)
252
- webmock (3.7.6)
253
- addressable (>= 2.3.6)
254
- crack (>= 0.3.2)
255
- hashdiff (>= 0.4.0, < 2.0.0)
256
- websocket-driver (0.7.1)
257
- websocket-extensions (>= 0.1.0)
258
- websocket-extensions (0.1.4)
259
- zeitwerk (2.2.1)
260
-
261
- PLATFORMS
262
- ruby
263
-
264
- DEPENDENCIES
265
- appraisal
266
- bundler (~> 2.0)
267
- cloudtasker!
268
- github_changelog_generator
269
- rails
270
- rake (~> 10.0)
271
- rspec (~> 3.0)
272
- rspec-rails
273
- rubocop (= 0.76.0)
274
- rubocop-rspec
275
- sqlite3
276
- timecop
277
- webmock
278
-
279
- BUNDLED WITH
280
- 2.0.2