cloudtasker 0.5.0 → 0.8.2

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 +45 -0
  6. data/README.md +163 -26
  7. data/Rakefile +6 -0
  8. data/_config.yml +1 -0
  9. data/app/controllers/cloudtasker/worker_controller.rb +6 -6
  10. data/cloudtasker.gemspec +3 -2
  11. data/docs/BATCH_JOBS.md +29 -4
  12. data/docs/CRON_JOBS.md +18 -14
  13. data/exe/cloudtasker +13 -1
  14. data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +26 -9
  15. data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +26 -9
  16. data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +27 -10
  17. data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +26 -9
  18. data/gemfiles/rails_5.2.gemfile.lock +25 -8
  19. data/gemfiles/rails_6.0.gemfile.lock +27 -10
  20. data/lib/cloudtasker.rb +0 -1
  21. data/lib/cloudtasker/backend/google_cloud_task.rb +41 -8
  22. data/lib/cloudtasker/backend/memory_task.rb +5 -3
  23. data/lib/cloudtasker/backend/redis_task.rb +24 -13
  24. data/lib/cloudtasker/batch/batch_progress.rb +11 -2
  25. data/lib/cloudtasker/batch/job.rb +18 -4
  26. data/lib/cloudtasker/cli.rb +6 -5
  27. data/lib/cloudtasker/cloud_task.rb +4 -2
  28. data/lib/cloudtasker/config.rb +21 -9
  29. data/lib/cloudtasker/cron/job.rb +2 -2
  30. data/lib/cloudtasker/cron/schedule.rb +26 -14
  31. data/lib/cloudtasker/local_server.rb +44 -22
  32. data/lib/cloudtasker/redis_client.rb +5 -6
  33. data/lib/cloudtasker/unique_job/job.rb +2 -2
  34. data/lib/cloudtasker/version.rb +1 -1
  35. data/lib/cloudtasker/worker.rb +46 -10
  36. data/lib/cloudtasker/worker_handler.rb +5 -3
  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 +21 -6
  41. data/Gemfile.lock +0 -263
  42. data/lib/cloudtasker/railtie.rb +0 -10
@@ -4,9 +4,7 @@ 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
 
@@ -50,9 +48,10 @@ module Cloudtasker
50
48
  # Acquire a lock on a cache entry.
51
49
  #
52
50
  # @example
53
- # RedisClient.with_lock('foo')
54
- # content = RedisClient.fetch('foo')
55
- # RedisClient.set(content.merge(bar: 'bar).to_json)
51
+ # redis = RedisClient.new
52
+ # redis.with_lock('foo')
53
+ # content = redis.fetch('foo')
54
+ # redis.set(content.merge(bar: 'bar).to_json)
56
55
  # end
57
56
  #
58
57
  # @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.5.0'
4
+ VERSION = '0.8.2'
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
 
@@ -32,8 +33,9 @@ module Cloudtasker
32
33
  # @return [Cloudtasker::Worker, nil] The instantiated worker.
33
34
  #
34
35
  def self.from_hash(hash)
35
- # Symbolize payload keys
36
+ # Symbolize metadata keys and stringify job arguments
36
37
  payload = JSON.parse(hash.to_json, symbolize_names: true)
38
+ payload[:job_args] = JSON.parse(payload[:job_args].to_json)
37
39
 
38
40
  # Extract worker parameters
39
41
  klass_name = payload&.dig(:worker)
@@ -44,7 +46,7 @@ module Cloudtasker
44
46
  return nil unless worker_klass.include?(self)
45
47
 
46
48
  # Return instantiated worker
47
- 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))
48
50
  rescue NameError
49
51
  nil
50
52
  end
@@ -80,7 +82,7 @@ module Cloudtasker
80
82
  # @return [Cloudtasker::CloudTask] The Google Task response
81
83
  #
82
84
  def perform_async(*args)
83
- perform_in(nil, *args)
85
+ schedule(args: args)
84
86
  end
85
87
 
86
88
  #
@@ -92,7 +94,7 @@ module Cloudtasker
92
94
  # @return [Cloudtasker::CloudTask] The Google Task response
93
95
  #
94
96
  def perform_in(interval, *args)
95
- new(job_args: args).schedule(interval: interval)
97
+ schedule(args: args, time_in: interval)
96
98
  end
97
99
 
98
100
  #
@@ -104,7 +106,21 @@ module Cloudtasker
104
106
  # @return [Cloudtasker::CloudTask] The Google Task response
105
107
  #
106
108
  def perform_at(time_at, *args)
107
- 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)
108
124
  end
109
125
 
110
126
  #
@@ -123,11 +139,30 @@ module Cloudtasker
123
139
  # @param [Array<any>] job_args The list of perform args.
124
140
  # @param [String] job_id A unique ID identifying this job.
125
141
  #
126
- def initialize(job_args: [], job_id: nil, job_meta: {}, job_retries: 0)
127
- @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 || []
128
144
  @job_id = job_id || SecureRandom.uuid
129
145
  @job_meta = MetaStore.new(job_meta)
130
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
131
166
  end
132
167
 
133
168
  #
@@ -198,10 +233,10 @@ module Cloudtasker
198
233
  # Return a new instance of the worker with the same args and metadata
199
234
  # but with a different id.
200
235
  #
201
- # @return [<Type>] <description>
236
+ # @return [Cloudtasker::Worker] <description>
202
237
  #
203
238
  def new_instance
204
- 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)
205
240
  end
206
241
 
207
242
  #
@@ -215,7 +250,8 @@ module Cloudtasker
215
250
  job_id: job_id,
216
251
  job_args: job_args,
217
252
  job_meta: job_meta.to_h,
218
- job_retries: job_retries
253
+ job_retries: job_retries,
254
+ job_queue: job_queue
219
255
  }
220
256
  end
221
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'
@@ -46,7 +46,8 @@ module Cloudtasker
46
46
  'Authorization' => "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.5.0
4
+ version: 0.8.2
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: 2019-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: github_changelog_generator
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rake
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -234,7 +248,7 @@ dependencies:
234
248
  - - ">="
235
249
  - !ruby/object:Gem::Version
236
250
  version: '0'
237
- description: Manage GCP Cloud Tasks in your app. (alpha)
251
+ description: Background jobs for Ruby using Google Cloud Tasks (beta)
238
252
  email:
239
253
  - arnaud.lachaume@keypup.io
240
254
  executables:
@@ -247,12 +261,13 @@ files:
247
261
  - ".rubocop.yml"
248
262
  - ".travis.yml"
249
263
  - Appraisals
264
+ - CHANGELOG.md
250
265
  - CODE_OF_CONDUCT.md
251
266
  - Gemfile
252
- - Gemfile.lock
253
267
  - LICENSE.txt
254
268
  - README.md
255
269
  - Rakefile
270
+ - _config.yml
256
271
  - app/controllers/cloudtasker/application_controller.rb
257
272
  - app/controllers/cloudtasker/worker_controller.rb
258
273
  - bin/console
@@ -307,7 +322,6 @@ files:
307
322
  - lib/cloudtasker/local_server.rb
308
323
  - lib/cloudtasker/meta_store.rb
309
324
  - lib/cloudtasker/middleware/chain.rb
310
- - lib/cloudtasker/railtie.rb
311
325
  - lib/cloudtasker/redis_client.rb
312
326
  - lib/cloudtasker/testing.rb
313
327
  - lib/cloudtasker/unique_job.rb
@@ -329,6 +343,7 @@ files:
329
343
  - lib/cloudtasker/worker.rb
330
344
  - lib/cloudtasker/worker_handler.rb
331
345
  - lib/cloudtasker/worker_logger.rb
346
+ - lib/cloudtasker/worker_wrapper.rb
332
347
  - lib/tasks/setup_queue.rake
333
348
  homepage: https://github.com/keypup-io/cloudtasker
334
349
  licenses:
@@ -356,5 +371,5 @@ rubyforge_project:
356
371
  rubygems_version: 2.7.9
357
372
  signing_key:
358
373
  specification_version: 4
359
- summary: Manage GCP Cloud Tasks in your app. (alpha)
374
+ summary: Background jobs for Ruby using Google Cloud Tasks (beta)
360
375
  test_files: []
data/Gemfile.lock DELETED
@@ -1,263 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cloudtasker (0.5.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
- fugit (1.3.3)
88
- et-orbi (~> 1.1, >= 1.1.8)
89
- raabro (~> 1.1)
90
- globalid (0.4.2)
91
- activesupport (>= 4.2.0)
92
- google-cloud-tasks (1.3.1)
93
- google-gax (~> 1.8)
94
- googleapis-common-protos (>= 1.3.9, < 2.0)
95
- googleapis-common-protos-types (>= 1.0.4, < 2.0)
96
- grpc-google-iam-v1 (~> 0.6.9)
97
- google-gax (1.8.1)
98
- google-protobuf (~> 3.9)
99
- googleapis-common-protos (>= 1.3.9, < 2.0)
100
- googleauth (~> 0.9)
101
- grpc (~> 1.24)
102
- rly (~> 0.2.3)
103
- google-protobuf (3.10.1-universal-darwin)
104
- googleapis-common-protos (1.3.9)
105
- google-protobuf (~> 3.0)
106
- googleapis-common-protos-types (~> 1.0)
107
- grpc (~> 1.0)
108
- googleapis-common-protos-types (1.0.4)
109
- google-protobuf (~> 3.0)
110
- googleauth (0.10.0)
111
- faraday (~> 0.12)
112
- jwt (>= 1.4, < 3.0)
113
- memoist (~> 0.16)
114
- multi_json (~> 1.11)
115
- os (>= 0.9, < 2.0)
116
- signet (~> 0.12)
117
- grpc (1.25.0-universal-darwin)
118
- google-protobuf (~> 3.8)
119
- googleapis-common-protos-types (~> 1.0)
120
- grpc-google-iam-v1 (0.6.9)
121
- googleapis-common-protos (>= 1.3.1, < 2.0)
122
- grpc (~> 1.0)
123
- hashdiff (1.0.0)
124
- i18n (1.7.0)
125
- concurrent-ruby (~> 1.0)
126
- jaro_winkler (1.5.4)
127
- jwt (2.2.1)
128
- loofah (2.3.1)
129
- crass (~> 1.0.2)
130
- nokogiri (>= 1.5.9)
131
- mail (2.7.1)
132
- mini_mime (>= 0.1.1)
133
- marcel (0.3.3)
134
- mimemagic (~> 0.3.2)
135
- memoist (0.16.1)
136
- method_source (0.9.2)
137
- mimemagic (0.3.3)
138
- mini_mime (1.0.2)
139
- mini_portile2 (2.4.0)
140
- minitest (5.13.0)
141
- multi_json (1.14.1)
142
- multipart-post (2.1.1)
143
- nio4r (2.5.2)
144
- nokogiri (1.10.5)
145
- mini_portile2 (~> 2.4.0)
146
- os (1.0.1)
147
- parallel (1.18.0)
148
- parser (2.6.5.0)
149
- ast (~> 2.4.0)
150
- public_suffix (4.0.1)
151
- raabro (1.1.6)
152
- rack (2.0.7)
153
- rack-test (1.1.0)
154
- rack (>= 1.0, < 3)
155
- rails (6.0.0)
156
- actioncable (= 6.0.0)
157
- actionmailbox (= 6.0.0)
158
- actionmailer (= 6.0.0)
159
- actionpack (= 6.0.0)
160
- actiontext (= 6.0.0)
161
- actionview (= 6.0.0)
162
- activejob (= 6.0.0)
163
- activemodel (= 6.0.0)
164
- activerecord (= 6.0.0)
165
- activestorage (= 6.0.0)
166
- activesupport (= 6.0.0)
167
- bundler (>= 1.3.0)
168
- railties (= 6.0.0)
169
- sprockets-rails (>= 2.0.0)
170
- rails-dom-testing (2.0.3)
171
- activesupport (>= 4.2.0)
172
- nokogiri (>= 1.6)
173
- rails-html-sanitizer (1.3.0)
174
- loofah (~> 2.3)
175
- railties (6.0.0)
176
- actionpack (= 6.0.0)
177
- activesupport (= 6.0.0)
178
- method_source
179
- rake (>= 0.8.7)
180
- thor (>= 0.20.3, < 2.0)
181
- rainbow (3.0.0)
182
- rake (10.5.0)
183
- redis (4.1.3)
184
- rly (0.2.3)
185
- rspec (3.9.0)
186
- rspec-core (~> 3.9.0)
187
- rspec-expectations (~> 3.9.0)
188
- rspec-mocks (~> 3.9.0)
189
- rspec-core (3.9.0)
190
- rspec-support (~> 3.9.0)
191
- rspec-expectations (3.9.0)
192
- diff-lcs (>= 1.2.0, < 2.0)
193
- rspec-support (~> 3.9.0)
194
- rspec-mocks (3.9.0)
195
- diff-lcs (>= 1.2.0, < 2.0)
196
- rspec-support (~> 3.9.0)
197
- rspec-rails (3.9.0)
198
- actionpack (>= 3.0)
199
- activesupport (>= 3.0)
200
- railties (>= 3.0)
201
- rspec-core (~> 3.9.0)
202
- rspec-expectations (~> 3.9.0)
203
- rspec-mocks (~> 3.9.0)
204
- rspec-support (~> 3.9.0)
205
- rspec-support (3.9.0)
206
- rubocop (0.76.0)
207
- jaro_winkler (~> 1.5.1)
208
- parallel (~> 1.10)
209
- parser (>= 2.6)
210
- rainbow (>= 2.2.2, < 4.0)
211
- ruby-progressbar (~> 1.7)
212
- unicode-display_width (>= 1.4.0, < 1.7)
213
- rubocop-rspec (1.36.0)
214
- rubocop (>= 0.68.1)
215
- ruby-progressbar (1.10.1)
216
- safe_yaml (1.0.5)
217
- signet (0.12.0)
218
- addressable (~> 2.3)
219
- faraday (~> 0.9)
220
- jwt (>= 1.5, < 3.0)
221
- multi_json (~> 1.10)
222
- sprockets (4.0.0)
223
- concurrent-ruby (~> 1.0)
224
- rack (> 1, < 3)
225
- sprockets-rails (3.2.1)
226
- actionpack (>= 4.0)
227
- activesupport (>= 4.0)
228
- sprockets (>= 3.0.0)
229
- sqlite3 (1.4.0)
230
- thor (0.20.3)
231
- thread_safe (0.3.6)
232
- timecop (0.9.1)
233
- tzinfo (1.2.5)
234
- thread_safe (~> 0.1)
235
- unicode-display_width (1.6.0)
236
- webmock (3.7.6)
237
- addressable (>= 2.3.6)
238
- crack (>= 0.3.2)
239
- hashdiff (>= 0.4.0, < 2.0.0)
240
- websocket-driver (0.7.1)
241
- websocket-extensions (>= 0.1.0)
242
- websocket-extensions (0.1.4)
243
- zeitwerk (2.2.1)
244
-
245
- PLATFORMS
246
- ruby
247
-
248
- DEPENDENCIES
249
- appraisal
250
- bundler (~> 2.0)
251
- cloudtasker!
252
- rails
253
- rake (~> 10.0)
254
- rspec (~> 3.0)
255
- rspec-rails
256
- rubocop (= 0.76.0)
257
- rubocop-rspec
258
- sqlite3
259
- timecop
260
- webmock
261
-
262
- BUNDLED WITH
263
- 2.0.2