active_job-performs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7850f0de3bce1b5668903aa8d5144c3eb220fb4655013fc0ed4424357215627d
4
+ data.tar.gz: b13c325e25d4f470bed021bcf4d1bc302c04953d9bb287389d532d0df1327a5d
5
+ SHA512:
6
+ metadata.gz: 9790fdd3cd0812abd555f46cd3f295be1a4ed9fd5843ed4b0563fd61cf5b8c069c881f1373dae8ac2d130f3f9bddb6bc4bdf46a0f95c88ab8b5da5a98829a391
7
+ data.tar.gz: 659b0e1cdb12cf76f91df7f158800c71573866b132db1ed215bdced87bf72e837da7b96c1e6641b39abb6c4db35e5450bab52a4e9f7d5d04e375b5a1e8c5272b
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-09-27
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in active_job-performs.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+ gem "minitest-sprint"
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ active_job-performs (0.1.0)
5
+ activejob (>= 6.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activejob (7.0.4)
11
+ activesupport (= 7.0.4)
12
+ globalid (>= 0.3.6)
13
+ activesupport (7.0.4)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ tzinfo (~> 2.0)
18
+ concurrent-ruby (1.1.10)
19
+ globalid (1.0.0)
20
+ activesupport (>= 5.0)
21
+ i18n (1.12.0)
22
+ concurrent-ruby (~> 1.0)
23
+ minitest (5.16.3)
24
+ minitest-sprint (1.2.2)
25
+ path_expander (~> 1.1)
26
+ path_expander (1.1.1)
27
+ rake (13.0.6)
28
+ tzinfo (2.0.5)
29
+ concurrent-ruby (~> 1.0)
30
+
31
+ PLATFORMS
32
+ arm64-darwin-20
33
+ x86_64-linux
34
+
35
+ DEPENDENCIES
36
+ active_job-performs!
37
+ minitest (~> 5.0)
38
+ minitest-sprint
39
+ rake (~> 13.0)
40
+
41
+ BUNDLED WITH
42
+ 2.3.21
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Kasper Timm Hansen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # ActiveJob::Performs
2
+
3
+ `ActiveJob::Performs` adds the `performs` macro to set up jobs by convention.
4
+
5
+ ## Usage with `include GlobalID::Identification` objects
6
+
7
+ `ActiveJob::Performs` works with any object that has `include GlobalID::Identification` and responds to that interface.
8
+
9
+ `ActiveRecord::Base` implements this, so here's how that looks:
10
+
11
+ ```ruby
12
+ class Post < ActiveRecord::Base
13
+ extend ActiveJob::Performs # We technically auto-extend ActiveRecord::Base, but other object hierarchies need this.
14
+
15
+ # `performs` builds a `Post::PublishJob` and routes configs over to it.
16
+ performs :publish, queue_as: :important, discard_on: SomeError do
17
+ retry_on TimeoutError, wait: :exponentially_longer
18
+ end
19
+
20
+ def publish
21
+
22
+ end
23
+ end
24
+ ```
25
+
26
+ Here's what `performs` generates under the hood:
27
+
28
+ ```ruby
29
+ class Post < ActiveRecord::Base
30
+ # We setup a general Job class that's shared between method jobs.
31
+ class Job < ApplicationJob; end
32
+
33
+ # Individual method jobs inherit from the `Post::Job` defined above.
34
+ class PublishJob < Job
35
+ queue_as :important
36
+ discard_on SomeError
37
+ retry_on TimeoutError, wait: :exponentially_longer
38
+
39
+ # We generate `perform` passing in the `post` and calling `publish` on it.
40
+ def perform(post, *arguments, **options)
41
+ post.publish(*arguments, **options)
42
+ end
43
+ end
44
+
45
+ # We generate `publish_later` to wrap the job execution.
46
+ def publish_later(*arguments, **options)
47
+ PublishJob.perform_later(self, *arguments, **options)
48
+ end
49
+
50
+ def publish
51
+
52
+ end
53
+ end
54
+ ```
55
+
56
+ We generate the `Post::Job` class above to share configuration between method level jobs. E.g. if you had a retract method that was setup very similar, you could do:
57
+
58
+ ```ruby
59
+ class Post < ActiveRecord::Base
60
+ performs queue_as: :important
61
+ performs :publish
62
+ performs :retract
63
+
64
+ def publish
65
+
66
+ end
67
+
68
+ def retract(reason:)
69
+
70
+ end
71
+ end
72
+ ```
73
+
74
+ Which would then become:
75
+
76
+ ```ruby
77
+ class Post < ActiveRecord::Base
78
+ class Job < ApplicationJob
79
+ queue_as :important
80
+ end
81
+
82
+ class PublishJob < Job
83
+
84
+ end
85
+
86
+ class RetractJob < Job
87
+
88
+ end
89
+
90
+
91
+ end
92
+ ```
93
+
94
+ ### Usage with ActiveRecord::AssociatedObject
95
+
96
+ The [`ActiveRecord::AssociatedObject`](https://github.com/kaspth/active_record-associated_object) gem also implements `GlobalID::Identification`, so you can do this too:
97
+
98
+ ```ruby
99
+ class Post::Publisher < ActiveRecord::AssociatedObject
100
+ extend ActiveJob::Performs # We technically auto-extend ActiveRecord::AssociatedObject, but other object hierarchies need this.
101
+
102
+ performs queue_as: :important
103
+ performs :publish
104
+ performs :retract
105
+
106
+ def publish
107
+
108
+ end
109
+
110
+ def retract(reason:)
111
+
112
+ end
113
+ end
114
+ ```
115
+
116
+ ### Passing `wait` to `performs`
117
+
118
+ If there's a job you want to defer, `performs` can set it for each invocation:
119
+
120
+ ```ruby
121
+ class Post < ActiveRecord::Base
122
+ mattr_reader :config, default: Rails.application.config_for(:posts)
123
+
124
+ performs :social_media_boost, wait: config.social_media_boost_after
125
+ performs :social_media_boost, wait: 5.minutes # Alternatively, this works too.
126
+
127
+ # Additionally, a block can be passed to have access to the `post`:
128
+ performs :social_media_boost, wait: -> post { post.social_media_boost_grace_period }
129
+ end
130
+ ```
131
+
132
+ Now, `social_media_boost_later` can be called immediately, but automatically run after the grace period.
133
+
134
+ `wait_until` is also supported:
135
+
136
+ ```ruby
137
+ class Post < ActiveRecord::Base
138
+ performs :publish, wait_until: -> post { Date.tomorrow.noon if post.graceful? }
139
+ end
140
+ ```
141
+
142
+ ## Installation
143
+
144
+ Install the gem and add to the application's Gemfile by executing:
145
+
146
+ $ bundle add active_job-performs
147
+
148
+ If bundler is not being used to manage dependencies, install the gem by executing:
149
+
150
+ $ gem install active_job-performs
151
+
152
+ ## Development
153
+
154
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
155
+
156
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
157
+
158
+ ## Contributing
159
+
160
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kaspth/active_job-performs.
161
+
162
+ ## License
163
+
164
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/active_job/performs/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "active_job-performs"
7
+ spec.version = ActiveJob::Performs::VERSION
8
+ spec.authors = ["Kasper Timm Hansen"]
9
+ spec.email = ["hey@kaspth.com"]
10
+
11
+ spec.summary = "ActiveJob::Performs adds the `performs` macro to set up jobs by convention."
12
+ spec.homepage = "https://github.com/kaspth/active_job-performs"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 3.0.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "activejob", ">= 6.1"
30
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module Performs
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "performs/version"
4
+
5
+ module ActiveJob; end
6
+ module ActiveJob::Performs
7
+ module Waiting
8
+ def Proc(value)
9
+ value.respond_to?(:call) ? value : proc { value }
10
+ end unless Kernel.respond_to?(:Proc) # Optimistically assume Ruby gets this and it'll work fine.
11
+
12
+ def wait(value = nil)
13
+ @wait = Proc(value) if value
14
+ @wait
15
+ end
16
+
17
+ def wait_until(value = nil)
18
+ @wait_until = Proc(value) if value
19
+ @wait_until
20
+ end
21
+
22
+ def scoped_by_wait(record)
23
+ if waits = { wait: wait&.call(record), wait_until: wait_until&.call(record) }.compact and waits.any?
24
+ set(waits)
25
+ else
26
+ self
27
+ end
28
+ end
29
+ end
30
+
31
+ def performs(method = nil, **configs, &block)
32
+ @job ||= safe_define("Job") { ApplicationJob }.tap { _1.extend Waiting }
33
+
34
+ if method.nil?
35
+ apply_performs_to(@job, **configs, &block)
36
+ else
37
+ job = safe_define("#{method}_job".classify) { @job }
38
+ apply_performs_to(job, **configs, &block)
39
+
40
+ job.class_eval <<~RUBY, __FILE__, __LINE__ + 1 unless job.instance_method(:perform).owner == job
41
+ def perform(object, *arguments, **options)
42
+ object.#{method}(*arguments, **options)
43
+ end
44
+ RUBY
45
+
46
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
47
+ def #{method}_later(*arguments, **options)
48
+ #{job}.scoped_by_wait(self).perform_later(self, *arguments, **options)
49
+ end
50
+ RUBY
51
+ end
52
+ end
53
+
54
+ private
55
+ def safe_define(name)
56
+ name.safe_constantize || const_set(name, Class.new(yield))
57
+ end
58
+
59
+ def apply_performs_to(job_class, **configs, &block)
60
+ configs.each { job_class.public_send(_1, _2) }
61
+ job_class.class_exec(&block) if block_given?
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_job-performs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kasper Timm Hansen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-09-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activejob
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ description:
28
+ email:
29
+ - hey@kaspth.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - CHANGELOG.md
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - active_job-performs.gemspec
41
+ - lib/active_job/performs.rb
42
+ - lib/active_job/performs/version.rb
43
+ homepage: https://github.com/kaspth/active_job-performs
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ homepage_uri: https://github.com/kaspth/active_job-performs
48
+ source_code_uri: https://github.com/kaspth/active_job-performs
49
+ changelog_uri: https://github.com/kaspth/active_job-performs/blob/main/CHANGELOG.md
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 3.0.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.3.21
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: ActiveJob::Performs adds the `performs` macro to set up jobs by convention.
69
+ test_files: []