gush 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e588202fe13ded7c99f192bda5bc33d3d6e8994deb3fb17f5a823d4882c4552f
4
- data.tar.gz: 759593f344caf579cce496b1da9c64c9431d6f2cbb92cbcced181d49421fdefb
3
+ metadata.gz: 0b1ccfa69ff21ec6820d9178edb51872cdbf28b7686bcf3a4de4170e3bc7f370
4
+ data.tar.gz: c44dae0cc5480366a3cf65ccd461ca55c991945ee090dfa3a386061f66001e09
5
5
  SHA512:
6
- metadata.gz: 5d6068f23d178beb5dbfaa9cf317ae086542dedf33aeab82f82c8cab5a6ea569d932b2dc0a787978629e892a39f25270827c55fa4a6b0a6bb349f9ab87fca1db
7
- data.tar.gz: 69ddc27452586d4b188969ece9ab73aefd90377fd93503f3e83c9fd074722d808a09cc620f98953756d7c684bab808db14550c15bada3e93111815613e52b78c
6
+ metadata.gz: 474efac8af5d337283f29fbb7c4e9027690519c413f4d71c08d1d4c36aa78bc805d68b199fbf4395744854d40e238b19c570f0c2daa6c0676efd58f746b38b1c
7
+ data.tar.gz: c8635120b6071458cf6f99542f846adb806a4233e17b4a77444acd191763b470e2d41560f14b17e5b59d6dfeb514c52933eac05ce5119ca590f12b07209626b9
@@ -39,6 +39,10 @@ jobs:
39
39
  RAILS_VERSION: "${{ matrix.rails_version }}"
40
40
  - name: Install Graphviz
41
41
  run: sudo apt-get install graphviz
42
+ - name: Run code lint
43
+ run: bundle exec rubocop
44
+ env:
45
+ RAILS_VERSION: "${{ matrix.rails_version }}"
42
46
  - name: Run tests
43
47
  run: bundle exec rspec
44
48
  env:
data/.rubocop.yml ADDED
@@ -0,0 +1,232 @@
1
+ require:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ NewCops: disable
7
+
8
+ Gemspec/OrderedDependencies:
9
+ Enabled: false
10
+
11
+ Layout/ArgumentAlignment:
12
+ Enabled: false
13
+
14
+ Layout/CaseIndentation:
15
+ Enabled: false
16
+
17
+ Layout/EmptyLinesAroundBlockBody:
18
+ Enabled: false
19
+
20
+ Layout/ExtraSpacing:
21
+ Enabled: false
22
+
23
+ Layout/FirstHashElementIndentation:
24
+ Enabled: false
25
+
26
+ Layout/HashAlignment:
27
+ Enabled: false
28
+
29
+ Layout/SpaceAroundEqualsInParameterDefault:
30
+ Enabled: false
31
+
32
+ Layout/SpaceAroundOperators:
33
+ Enabled: false
34
+
35
+ Layout/SpaceBeforeBlockBraces:
36
+ Enabled: false
37
+
38
+ Layout/SpaceInsideBlockBraces:
39
+ Enabled: false
40
+
41
+ Layout/SpaceInsideHashLiteralBraces:
42
+ Enabled: false
43
+
44
+ Lint/ConstantDefinitionInBlock:
45
+ Exclude:
46
+ - spec/**/*
47
+
48
+ Lint/RedundantSplatExpansion:
49
+ Enabled: false
50
+
51
+ Lint/ToJSON:
52
+ Enabled: false
53
+
54
+ Lint/UnusedBlockArgument:
55
+ Enabled: false
56
+
57
+ Lint/UnusedMethodArgument:
58
+ Enabled: false
59
+
60
+ Lint/UselessAssignment:
61
+ Enabled: false
62
+
63
+ Metrics/AbcSize:
64
+ Enabled: false
65
+
66
+ Metrics/BlockLength:
67
+ Enabled: false
68
+
69
+ Metrics/ClassLength:
70
+ Enabled: false
71
+
72
+ Metrics/CyclomaticComplexity:
73
+ Enabled: false
74
+
75
+ Metrics/MethodLength:
76
+ Enabled: false
77
+
78
+ Naming/MemoizedInstanceVariableName:
79
+ Enabled: false
80
+
81
+ Naming/PredicateName:
82
+ Enabled: false
83
+
84
+ Naming/RescuedExceptionsVariableName:
85
+ Enabled: false
86
+
87
+ Style/BlockDelimiters:
88
+ Enabled: false
89
+
90
+ Style/ClassVars:
91
+ Enabled: false
92
+
93
+ Style/CombinableLoops:
94
+ Enabled: false
95
+
96
+ Style/ConditionalAssignment:
97
+ Enabled: false
98
+
99
+ Style/Documentation:
100
+ Enabled: false
101
+
102
+ Style/EmptyCaseCondition:
103
+ Enabled: false
104
+
105
+ Style/EmptyMethod:
106
+ Enabled: false
107
+
108
+ Style/FrozenStringLiteralComment:
109
+ Enabled: false
110
+
111
+ Style/GuardClause:
112
+ Enabled: false
113
+
114
+ Style/HashSyntax:
115
+ Enabled: false
116
+
117
+ Style/IfUnlessModifier:
118
+ Enabled: false
119
+
120
+ Style/InverseMethods:
121
+ Enabled: false
122
+
123
+ Style/MethodCallWithoutArgsParentheses:
124
+ Enabled: false
125
+
126
+ Style/NumericLiteralPrefix:
127
+ Enabled: false
128
+
129
+ Style/PercentLiteralDelimiters:
130
+ Enabled: false
131
+
132
+ Style/RaiseArgs:
133
+ Enabled: false
134
+
135
+ Style/SafeNavigation:
136
+ Enabled: false
137
+
138
+ Style/SpecialGlobalVars:
139
+ Enabled: false
140
+
141
+ Style/StringLiterals:
142
+ Enabled: false
143
+
144
+ Style/SymbolProc:
145
+ Enabled: false
146
+
147
+ Style/UnlessElse:
148
+ Enabled: false
149
+
150
+ Style/WordArray:
151
+ Enabled: false
152
+
153
+ Layout/LineLength:
154
+ Enabled: false
155
+
156
+ RSpec/AnyInstance:
157
+ Enabled: false
158
+
159
+ RSpec/BeEq:
160
+ Enabled: false
161
+
162
+ RSpec/ContextWording:
163
+ Enabled: false
164
+
165
+ RSpec/DescribedClass:
166
+ Enabled: false
167
+
168
+ RSpec/EmptyExampleGroup:
169
+ Enabled: false
170
+
171
+ RSpec/EmptyLineAfterExampleGroup:
172
+ Enabled: false
173
+
174
+ RSpec/EmptyLineAfterSubject:
175
+ Enabled: false
176
+
177
+ RSpec/ExampleLength:
178
+ Enabled: false
179
+
180
+ RSpec/ExampleWording:
181
+ Enabled: false
182
+
183
+ RSpec/ExpectChange:
184
+ Enabled: false
185
+
186
+ RSpec/HookArgument:
187
+ EnforcedStyle: each
188
+
189
+ RSpec/LeakyConstantDeclaration:
190
+ Enabled: false
191
+
192
+ RSpec/LetSetup:
193
+ Enabled: false
194
+
195
+ RSpec/MatchArray:
196
+ Enabled: false
197
+
198
+ RSpec/MessageSpies:
199
+ EnforcedStyle: receive
200
+
201
+ RSpec/MultipleExpectations:
202
+ Enabled: false
203
+
204
+ RSpec/MultipleMemoizedHelpers:
205
+ Enabled: false
206
+
207
+ RSpec/NamedSubject:
208
+ Enabled: false
209
+
210
+ RSpec/NestedGroups:
211
+ Enabled: false
212
+
213
+ RSpec/NotToNot:
214
+ Enabled: false
215
+
216
+ RSpec/PredicateMatcher:
217
+ Enabled: false
218
+
219
+ RSpec/ReceiveCounts:
220
+ Enabled: false
221
+
222
+ RSpec/SpecFilePathFormat:
223
+ Enabled: false
224
+
225
+ RSpec/StubbedMock:
226
+ Enabled: false
227
+
228
+ RSpec/SubjectStub:
229
+ Enabled: false
230
+
231
+ RSpec/VerifiedDoubles:
232
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,79 +1,3 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
- and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
-
8
- ## 2.1.0
9
-
10
- ### Added
11
-
12
- - Allow RedisMutex’s locking duration and polling interval to be customizable, thanks to @thukim! [See pull request](https://github.com/chaps-io/gush/pull/74)
13
- - Support for Rails 7.0 and Ruby 3.0-3.1, thanks to @joshRpowell and @kzkn!
14
-
15
- ## 2.0.1
16
-
17
- ### Fixed
18
-
19
- - Fix bug when retried jobs didn't correctly reset their failed flag when ran again (Thanks to @theo-delaune-argus and @mickael-palma-argus! [See issue](https://github.com/chaps-io/gush/issues/61))
20
-
21
- ## 2.0.0
22
-
23
- ### Changed
24
-
25
- - *[BREAKING]* Store gush jobs on redis hash instead of plain keys - this improves performance when retrieving keys (Thanks to @Saicheg! [See pull request](https://github.com/chaps-io/gush/pull/56))
26
-
27
-
28
- ### Added
29
-
30
- - Allow setting queue for each job via `:queue` option in `run` method (Thanks to @devilankur18! [See pull request](https://github.com/chaps-io/gush/pull/58))
31
-
32
-
33
- ## 1.1.1 - 2018-06-09
34
-
35
- ### Changed
36
-
37
- - Relax dependency on ActiveSupport to work with 4.2 up to 5.X (Thanks to @iacobus! [See pull request](https://github.com/chaps-io/gush/pull/54))
38
-
39
-
40
- ## 1.1.0 - 2018-02-05
41
-
42
- ### Added
43
-
44
- - Added ability to specify TTL for Redis keys and manually expire whole workflows (Thanks to @dmitrypol! [See pull request](https://github.com/chaps-io/gush/pull/48))
45
- - Loosened dependency on redis-rb library to >= 3.2 and < 5.0 (Thanks to @mofumofu3n! [See pull request](https://github.com/chaps-io/gush/pull/52))
46
-
47
- ### Fixed
48
-
49
- - Improved performance of (de)serializing workflows by not storing job array inside workflow JSON and other smaller improvements ([See pull request](https://github.com/chaps-io/gush/pull/53))
50
-
51
-
52
- ## 1.0.0 - 2017-10-02
53
-
54
- ### Added
55
-
56
- - **BREAKING CHANGE** Gush now uses ActiveJob instead of directly Sidekiq, this allows programmers to use multiple backends, instead of just one. Including in-process or even synchronous backends. See http://guides.rubyonrails.org/active_job_basics.html
57
-
58
- ### Fixed
59
-
60
- - Fix graph rendering with `gush viz` command. Sometimes it rendered the last job detached from others, because it was using a class name instead of job name as ID.
61
- - Fix performance problems with unserializing jobs. This greatly **increased performance** by avoiding redundant calls to Redis storage. Should help a lot with huge workflows spawning thousands of jobs. Previously each job loaded whole workflow instance when executed.
62
-
63
- ### Changed
64
-
65
- - **BREAKING CHANGE** `Gushfile.rb` is now renamed to `Gushfile`
66
- - **BREAKING CHANGE** Internal code for reporting status via Redis pub/sub has been removed, since it wasn't used for a long time.
67
- - **BREAKING CHANGE** jobs are expected to have a `perform` method instead of `work` like in < 1.0.0 versions.
68
- - **BREAKING CHANGE** `payloads` method available inside jobs is now an array of hashes, instead of a hash, this allows for a more flexible approach to reusing a single job in many situations. Previously payloads were grouped by predecessor's class name, so you were forced to hardcode that class name in its descendants' code.
69
-
70
- ### Removed
71
-
72
- - `gush workers` command is now removed. This is now up to the developer to start background processes depending on chosen ActiveJob adapter.
73
- - `environment` was removed since it was no longer needed (it was Sidekiq specific)
74
-
75
- ## 0.4.0
76
-
77
- ### Removed
78
-
79
- - remove hard dependency on Yajl, so Gush can work with non-MRI Rubies ([#31](https://github.com/chaps-io/gush/pull/31) by [Nick Rakochy](https://github.com/chaps-io/gush/pull/31))
3
+ Changes for each release are available at https://github.com/chaps-io/gush/releases
data/README.md CHANGED
@@ -159,9 +159,11 @@ Let's assume we are writing a book publishing workflow which needs to know where
159
159
 
160
160
  ```ruby
161
161
  class PublishBookWorkflow < Gush::Workflow
162
- def configure(url, isbn)
162
+ def configure(url, isbn, publish: false)
163
163
  run FetchBook, params: { url: url }
164
- run PublishBook, params: { book_isbn: isbn }, after: FetchBook
164
+ if publish
165
+ run PublishBook, params: { book_isbn: isbn }, after: FetchBook
166
+ end
165
167
  end
166
168
  end
167
169
  ```
@@ -169,7 +171,7 @@ end
169
171
  and then create your workflow with those arguments:
170
172
 
171
173
  ```ruby
172
- PublishBookWorkflow.create("http://url.com/book.pdf", "978-0470081204")
174
+ PublishBookWorkflow.create("http://url.com/book.pdf", "978-0470081204", publish: true)
173
175
  ```
174
176
 
175
177
  and that's basically it for defining workflows, see below on how to define jobs:
@@ -254,8 +256,53 @@ flow.status
254
256
 
255
257
  `reload` is needed to see the latest status, since workflows are updated asynchronously.
256
258
 
259
+ ## Loading workflows
260
+
261
+ ### Finding a workflow by id
262
+
263
+ ```
264
+ flow = Workflow.find(id)
265
+ ```
266
+
267
+ ### Paging through workflows
268
+
269
+ To get workflows with pagination, use start and stop (inclusive) index values:
270
+
271
+ ```
272
+ flows = Workflow.page(0, 99)
273
+ ```
274
+
275
+ Or in reverse order:
276
+
277
+ ```
278
+ flows = Workflow.page(0, 99, order: :desc)
279
+ ```
280
+
257
281
  ## Advanced features
258
282
 
283
+ ### Global parameters for jobs
284
+
285
+ Workflows can accept a hash of `globals` that are automatically forwarded as parameters to all jobs.
286
+
287
+ This is useful to have common functionality across workflow and job classes, such as tracking the creator id for all instances:
288
+
289
+ ```ruby
290
+ class SimpleWorkflow < Gush::Workflow
291
+ def configure(url_to_fetch_from)
292
+ run DownloadJob, params: { url: url_to_fetch_from }
293
+ end
294
+ end
295
+
296
+ flow = SimpleWorkflow.create('http://foo.com', globals: { creator_id: 123 })
297
+ flow.globals
298
+ => {:creator_id=>123}
299
+ flow.jobs.first.params
300
+ => {:creator_id=>123, :url=>"http://foo.com"}
301
+ ```
302
+
303
+ **Note:** job params with the same key as globals will take precedence over the globals.
304
+
305
+
259
306
  ### Pipelining
260
307
 
261
308
  Gush offers a useful tool to pass results of a job to its dependencies, so they can act differently.
@@ -383,6 +430,37 @@ class NotifyWorkflow < Gush::Workflow
383
430
  end
384
431
  ```
385
432
 
433
+ ### Customization of ActiveJob enqueueing
434
+
435
+ There might be a case when you want to customize enqueing a job with more than just the above two options (`queue` and `wait`).
436
+
437
+ To pass additional options to `ActiveJob.set`, override `Job#worker_options`, e.g.:
438
+
439
+ ```ruby
440
+
441
+ class ScheduledJob < Gush::Job
442
+
443
+ def worker_options
444
+ super.merge(wait_until: Time.at(params[:start_at]))
445
+ end
446
+
447
+ end
448
+ ```
449
+
450
+ Or to entirely customize the ActiveJob integration, override `Job#enqueue_worker!`, e.g.:
451
+
452
+ ```ruby
453
+
454
+ class SynchronousJob < Gush::Job
455
+
456
+ def enqueue_worker!(options = {})
457
+ Gush::Worker.perform_now(workflow_id, name)
458
+ end
459
+
460
+ end
461
+ ```
462
+
463
+
386
464
  ## Command line interface (CLI)
387
465
 
388
466
  ### Checking status
@@ -393,12 +471,18 @@ end
393
471
  bundle exec gush show <workflow_id>
394
472
  ```
395
473
 
396
- - of all created workflows:
474
+ - of a page of workflows:
397
475
 
398
476
  ```
399
477
  bundle exec gush list
400
478
  ```
401
479
 
480
+ - of the most recent 100 workflows
481
+
482
+ ```
483
+ bundle exec gush list -99 -1
484
+ ```
485
+
402
486
  ### Vizualizing workflows as image
403
487
 
404
488
  This requires that you have imagemagick installed on your computer:
@@ -424,7 +508,9 @@ end
424
508
 
425
509
  ### Cleaning up afterwards
426
510
 
427
- Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is ran. This data might be useful for analysis but at a certain point it can be purged via Redis TTL. By default gush and Redis will keep keys forever. To configure expiration you need to 2 things. Create initializer (specify config.ttl in seconds, be different per environment).
511
+ Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is run. This data might be useful for analysis but at a certain point it can be purged. By default gush and Redis will keep keys forever. To configure expiration you need to do two things.
512
+
513
+ 1. Create an initializer that specifies `config.ttl` in seconds. Best NOT to set TTL to be too short (like minutes) but about a week in length.
428
514
 
429
515
  ```ruby
430
516
  # config/initializers/gush.rb
@@ -435,7 +521,9 @@ Gush.configure do |config|
435
521
  end
436
522
  ```
437
523
 
438
- And you need to call `flow.expire!` (optionally passing custom TTL value overriding `config.ttl`). This gives you control whether to expire data for specific workflow. Best NOT to set TTL to be too short (like minutes) but about a week in length. And you can run `Client.expire_workflow` and `Client.expire_job` passing appropriate IDs and TTL (pass -1 to NOT expire) values.
524
+ 2. Call `Client#expire_workflows` periodically, which will clear all expired stored workflow and job data and indexes. This method can be called at any rate, but ideally should be called at least once for every 1000 workflows created.
525
+
526
+ If you need more control over individual workflow expiration, you can call `flow.expire!(ttl)` with a TTL different from the Gush configuration, or with -1 to never expire the workflow.
439
527
 
440
528
  ### Avoid overlapping workflows
441
529
 
@@ -453,6 +541,19 @@ def find_by_class klass
453
541
  end
454
542
  ```
455
543
 
544
+ ## Gush 3.0 Migration
545
+
546
+ Gush 3.0 adds indexing for fast workflow pagination and changes the mechanism for expiring workflow data from Redis.
547
+
548
+ ### Migration
549
+
550
+ Run `bundle exec gush migrate` after upgrading. This will update internal data structures.
551
+
552
+ ### Expiration API
553
+
554
+ Periodically run `Gush::Client.new.expire_workflows` to expire data. Workflows will be automatically enrolled in this expiration, so there is no longer a need to call `workflow.expire!`.
555
+
556
+
456
557
  ## Contributors
457
558
 
458
559
  - [Mateusz Lenik](https://github.com/mlen)
data/gush.gemspec CHANGED
@@ -1,5 +1,4 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
 
5
4
  require_relative 'lib/gush/version'
@@ -33,6 +32,9 @@ Gem::Specification.new do |spec|
33
32
  spec.add_dependency "launchy", "~> 2.4"
34
33
  spec.add_development_dependency "bundler"
35
34
  spec.add_development_dependency "rake", "~> 12"
35
+ spec.add_development_dependency "rubocop", '~> 1.65.0'
36
+ spec.add_development_dependency "rubocop-rake", '~> 0.6.0'
37
+ spec.add_development_dependency "rubocop-rspec", '~> 3.0.3'
36
38
  spec.add_development_dependency "rspec", '~> 3.0'
37
39
  spec.add_development_dependency "pry", '~> 0.10'
38
40
  end
@@ -34,6 +34,7 @@ module Gush
34
34
  end
35
35
 
36
36
  private
37
+
37
38
  def rows
38
39
  [].tap do |rows|
39
40
  columns.each_pair do |name, value|
@@ -91,6 +92,7 @@ module Gush
91
92
 
92
93
  def jobs_by_type(type)
93
94
  return sorted_jobs if type == :all
95
+
94
96
  jobs.select{|j| j.public_send("#{type}?") }
95
97
  end
96
98
 
data/lib/gush/cli.rb CHANGED
@@ -70,9 +70,14 @@ module Gush
70
70
  client.destroy_workflow(workflow)
71
71
  end
72
72
 
73
- desc "list", "Lists all workflows with their statuses"
74
- def list
75
- workflows = client.all_workflows
73
+ desc "list START STOP", "Lists workflows from START index through STOP index with their statuses"
74
+ option :start, type: :numeric, default: nil
75
+ option :stop, type: :numeric, default: nil
76
+ def list(start=nil, stop=nil)
77
+ workflows = client.workflow_ids(start, stop).map do |id|
78
+ client.find_workflow(id)
79
+ end
80
+
76
81
  rows = workflows.map do |workflow|
77
82
  [workflow.id, (Time.at(workflow.started_at) if workflow.started_at), workflow.class, {alignment: :center, value: status_for(workflow)}]
78
83
  end
@@ -101,7 +106,7 @@ module Gush
101
106
  begin
102
107
  workflow = class_or_id.constantize.new
103
108
  rescue NameError => e
104
- STDERR.puts Paint["'#{class_or_id}' is not a valid workflow class or id", :red]
109
+ warn Paint["'#{class_or_id}' is not a valid workflow class or id", :red]
105
110
  exit 1
106
111
  end
107
112
  end
@@ -120,6 +125,24 @@ module Gush
120
125
  end
121
126
  end
122
127
 
128
+ desc "migrate", "Runs all unapplied migrations to Gush storage"
129
+ def migrate
130
+ Dir[File.join(__dir__, 'migrate', '*.rb')].each {|file| require file }
131
+
132
+ applied = Gush::Migration.subclasses.sort(&:version).count do |klass|
133
+ migration = klass.new
134
+ next if migration.migrated?
135
+
136
+ puts "Migrating to #{klass.name} (#{migration.version})"
137
+ migration.migrate
138
+ puts "== #{migration.version} #{klass.name}: migrated ==="
139
+
140
+ true
141
+ end
142
+
143
+ puts "#{applied} #{'migrations'.pluralize(applied)} applied"
144
+ end
145
+
123
146
  private
124
147
 
125
148
  def client