omg-activejob 8.0.0.alpha1
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 +7 -0
- data/CHANGELOG.md +10 -0
- data/MIT-LICENSE +21 -0
- data/README.md +131 -0
- data/lib/active_job/arguments.rb +197 -0
- data/lib/active_job/base.rb +79 -0
- data/lib/active_job/callbacks.rb +167 -0
- data/lib/active_job/configured_job.rb +22 -0
- data/lib/active_job/core.rb +202 -0
- data/lib/active_job/deprecator.rb +7 -0
- data/lib/active_job/enqueue_after_transaction_commit.rb +28 -0
- data/lib/active_job/enqueuing.rb +141 -0
- data/lib/active_job/exceptions.rb +206 -0
- data/lib/active_job/execution.rb +72 -0
- data/lib/active_job/gem_version.rb +17 -0
- data/lib/active_job/instrumentation.rb +52 -0
- data/lib/active_job/log_subscriber.rb +226 -0
- data/lib/active_job/logging.rb +49 -0
- data/lib/active_job/queue_adapter.rb +77 -0
- data/lib/active_job/queue_adapters/abstract_adapter.rb +27 -0
- data/lib/active_job/queue_adapters/async_adapter.rb +116 -0
- data/lib/active_job/queue_adapters/backburner_adapter.rb +40 -0
- data/lib/active_job/queue_adapters/delayed_job_adapter.rb +67 -0
- data/lib/active_job/queue_adapters/inline_adapter.rb +27 -0
- data/lib/active_job/queue_adapters/queue_classic_adapter.rb +66 -0
- data/lib/active_job/queue_adapters/resque_adapter.rb +53 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/active_job/queue_adapters/sneakers_adapter.rb +48 -0
- data/lib/active_job/queue_adapters/test_adapter.rb +94 -0
- data/lib/active_job/queue_adapters.rb +140 -0
- data/lib/active_job/queue_name.rb +68 -0
- data/lib/active_job/queue_priority.rb +60 -0
- data/lib/active_job/railtie.rb +107 -0
- data/lib/active_job/serializers/big_decimal_serializer.rb +22 -0
- data/lib/active_job/serializers/date_serializer.rb +20 -0
- data/lib/active_job/serializers/date_time_serializer.rb +16 -0
- data/lib/active_job/serializers/duration_serializer.rb +25 -0
- data/lib/active_job/serializers/module_serializer.rb +21 -0
- data/lib/active_job/serializers/object_serializer.rb +55 -0
- data/lib/active_job/serializers/range_serializer.rb +23 -0
- data/lib/active_job/serializers/symbol_serializer.rb +20 -0
- data/lib/active_job/serializers/time_object_serializer.rb +13 -0
- data/lib/active_job/serializers/time_serializer.rb +16 -0
- data/lib/active_job/serializers/time_with_zone_serializer.rb +25 -0
- data/lib/active_job/serializers.rb +72 -0
- data/lib/active_job/test_case.rb +11 -0
- data/lib/active_job/test_helper.rb +770 -0
- data/lib/active_job/timezones.rb +13 -0
- data/lib/active_job/translation.rb +13 -0
- data/lib/active_job/version.rb +10 -0
- data/lib/active_job.rb +71 -0
- data/lib/rails/generators/job/USAGE +19 -0
- data/lib/rails/generators/job/job_generator.rb +48 -0
- data/lib/rails/generators/job/templates/application_job.rb.tt +9 -0
- data/lib/rails/generators/job/templates/job.rb.tt +9 -0
- metadata +130 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 802ba9cb0255881dee3342c6c3f02acfff2cbcf17cb34b2c252377808e433d6b
|
|
4
|
+
data.tar.gz: b055d444e0fff0513ee33b289d4e600269ea3b994c5100d546b6e2c11091327b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 99e08ad43087e35807d581c7906b1bacf2bdbd73e270175fd29b4df4fc6c18ffb32fd5360ed89b15313f4f9c2261ceb21a808de4a0e853c149e5d7e08fc42d3d
|
|
7
|
+
data.tar.gz: 855ef48da4071c96bbce3510e9820cc51bac73aa86f0d3f653dd4e3ea3392b451eab27d6547bad47ac68e9b13fc9fcc52847eb5e628bd8ddccb5863437b3953b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
* Remove `sucker_punch` as an adapter option [since author himself recommends using AJ's own AsyncAdapter](https://github.com/brandonhilkert/sucker_punch?tab=readme-ov-file#faq).
|
|
2
|
+
If you're using this adapter, change to `adapter: async` for the same functionality.
|
|
3
|
+
|
|
4
|
+
*Dino Maric*
|
|
5
|
+
|
|
6
|
+
* Use `RAILS_MAX_THREADS` in `ActiveJob::AsyncAdapter`. If it is not set, use 5 as default.
|
|
7
|
+
|
|
8
|
+
*heka1024*
|
|
9
|
+
|
|
10
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activejob/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) David Heinemeier Hansson
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Active Job – Make work happen later
|
|
2
|
+
|
|
3
|
+
Active Job is a framework for declaring jobs and making them run on a variety
|
|
4
|
+
of queuing backends. These jobs can be everything from regularly scheduled
|
|
5
|
+
clean-ups, to billing charges, to mailings — anything that can be chopped up into
|
|
6
|
+
small units of work and run in parallel.
|
|
7
|
+
|
|
8
|
+
It also serves as the backend for Action Mailer's #deliver_later functionality
|
|
9
|
+
that makes it easy to turn any mailing into a job for running later. That's
|
|
10
|
+
one of the most common jobs in a modern web application: sending emails outside
|
|
11
|
+
the request-response cycle, so the user doesn't have to wait on it.
|
|
12
|
+
|
|
13
|
+
The main point is to ensure that all \Rails apps will have a job infrastructure
|
|
14
|
+
in place, even if it's in the form of an "immediate runner". We can then have
|
|
15
|
+
framework features and other gems build on top of that, without having to worry
|
|
16
|
+
about API differences between Delayed Job and Resque. Picking your queuing
|
|
17
|
+
backend becomes more of an operational concern, then. And you'll be able to
|
|
18
|
+
switch between them without having to rewrite your jobs.
|
|
19
|
+
|
|
20
|
+
You can read more about Active Job in the [Active Job Basics](https://guides.rubyonrails.org/active_job_basics.html) guide.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
To learn how to use your preferred queuing backend see its adapter
|
|
25
|
+
documentation at
|
|
26
|
+
[ActiveJob::QueueAdapters](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
|
|
27
|
+
|
|
28
|
+
Declare a job like so:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
class MyJob < ActiveJob::Base
|
|
32
|
+
queue_as :my_jobs
|
|
33
|
+
|
|
34
|
+
def perform(record)
|
|
35
|
+
record.do_work
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Enqueue a job like so:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
MyJob.perform_later record # Enqueue a job to be performed as soon as the queuing system is free.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record) # Enqueue a job to be performed tomorrow at noon.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
MyJob.set(wait: 1.week).perform_later(record) # Enqueue a job to be performed 1 week from now.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
That's it!
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## GlobalID support
|
|
58
|
+
|
|
59
|
+
Active Job supports [GlobalID serialization](https://github.com/rails/globalid/) for parameters. This makes it possible
|
|
60
|
+
to pass live Active Record objects to your job instead of class/id pairs, which
|
|
61
|
+
you then have to manually deserialize. Before, jobs would look like this:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
class TrashableCleanupJob
|
|
65
|
+
def perform(trashable_class, trashable_id, depth)
|
|
66
|
+
trashable = trashable_class.constantize.find(trashable_id)
|
|
67
|
+
trashable.cleanup(depth)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Now you can simply do:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
class TrashableCleanupJob
|
|
76
|
+
def perform(trashable, depth)
|
|
77
|
+
trashable.cleanup(depth)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This works with any class that mixes in GlobalID::Identification, which
|
|
83
|
+
by default has been mixed into Active Record classes.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## Supported queuing systems
|
|
87
|
+
|
|
88
|
+
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
|
|
89
|
+
Resque, Delayed Job and others). To get an up-to-date list of the adapters
|
|
90
|
+
see the API Documentation for [ActiveJob::QueueAdapters](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
|
|
91
|
+
|
|
92
|
+
**Please note:** We are not accepting pull requests for new adapters. We
|
|
93
|
+
encourage library authors to provide an ActiveJob adapter as part of
|
|
94
|
+
their gem, or as a stand-alone gem. For discussion about this see the
|
|
95
|
+
following PRs: [23311](https://github.com/rails/rails/issues/23311#issuecomment-176275718),
|
|
96
|
+
[21406](https://github.com/rails/rails/pull/21406#issuecomment-138813484), and [#32285](https://github.com/rails/rails/pull/32285).
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
## Download and installation
|
|
100
|
+
|
|
101
|
+
The latest version of Active Job can be installed with RubyGems:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
$ gem install activejob
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Source code can be downloaded as part of the \Rails project on GitHub:
|
|
108
|
+
|
|
109
|
+
* https://github.com/rails/rails/tree/main/activejob
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
Active Job is released under the MIT license:
|
|
115
|
+
|
|
116
|
+
* https://opensource.org/licenses/MIT
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## Support
|
|
120
|
+
|
|
121
|
+
API documentation is at:
|
|
122
|
+
|
|
123
|
+
* https://api.rubyonrails.org
|
|
124
|
+
|
|
125
|
+
Bug reports for the Ruby on \Rails project can be filed here:
|
|
126
|
+
|
|
127
|
+
* https://github.com/rails/rails/issues
|
|
128
|
+
|
|
129
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
|
130
|
+
|
|
131
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bigdecimal"
|
|
4
|
+
require "active_support/core_ext/hash"
|
|
5
|
+
|
|
6
|
+
module ActiveJob
|
|
7
|
+
# Raised when an exception is raised during job arguments deserialization.
|
|
8
|
+
#
|
|
9
|
+
# Wraps the original exception raised as +cause+.
|
|
10
|
+
class DeserializationError < StandardError
|
|
11
|
+
def initialize # :nodoc:
|
|
12
|
+
super("Error while trying to deserialize arguments: #{$!.message}")
|
|
13
|
+
set_backtrace $!.backtrace
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Raised when an unsupported argument type is set as a job argument. We
|
|
18
|
+
# currently support String, Integer, Float, NilClass, TrueClass, FalseClass,
|
|
19
|
+
# BigDecimal, Symbol, Date, Time, DateTime, ActiveSupport::TimeWithZone,
|
|
20
|
+
# ActiveSupport::Duration, Hash, ActiveSupport::HashWithIndifferentAccess,
|
|
21
|
+
# Array, Range, or GlobalID::Identification instances, although this can be
|
|
22
|
+
# extended by adding custom serializers.
|
|
23
|
+
# Raised if you set the key for a Hash something else than a string or
|
|
24
|
+
# a symbol. Also raised when trying to serialize an object which can't be
|
|
25
|
+
# identified with a GlobalID - such as an unpersisted Active Record model.
|
|
26
|
+
class SerializationError < ArgumentError; end
|
|
27
|
+
|
|
28
|
+
module Arguments
|
|
29
|
+
extend self
|
|
30
|
+
# Serializes a set of arguments. Intrinsic types that can safely be
|
|
31
|
+
# serialized without mutation are returned as-is. Arrays/Hashes are
|
|
32
|
+
# serialized element by element. All other types are serialized using
|
|
33
|
+
# GlobalID.
|
|
34
|
+
def serialize(arguments)
|
|
35
|
+
arguments.map { |argument| serialize_argument(argument) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Deserializes a set of arguments. Intrinsic types that can safely be
|
|
39
|
+
# deserialized without mutation are returned as-is. Arrays/Hashes are
|
|
40
|
+
# deserialized element by element. All other types are deserialized using
|
|
41
|
+
# GlobalID.
|
|
42
|
+
def deserialize(arguments)
|
|
43
|
+
arguments.map { |argument| deserialize_argument(argument) }
|
|
44
|
+
rescue
|
|
45
|
+
raise DeserializationError
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
# :nodoc:
|
|
50
|
+
GLOBALID_KEY = "_aj_globalid"
|
|
51
|
+
# :nodoc:
|
|
52
|
+
SYMBOL_KEYS_KEY = "_aj_symbol_keys"
|
|
53
|
+
# :nodoc:
|
|
54
|
+
RUBY2_KEYWORDS_KEY = "_aj_ruby2_keywords"
|
|
55
|
+
# :nodoc:
|
|
56
|
+
WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access"
|
|
57
|
+
# :nodoc:
|
|
58
|
+
OBJECT_SERIALIZER_KEY = "_aj_serialized"
|
|
59
|
+
|
|
60
|
+
# :nodoc:
|
|
61
|
+
RESERVED_KEYS = [
|
|
62
|
+
GLOBALID_KEY, GLOBALID_KEY.to_sym,
|
|
63
|
+
SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym,
|
|
64
|
+
RUBY2_KEYWORDS_KEY, RUBY2_KEYWORDS_KEY.to_sym,
|
|
65
|
+
OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym,
|
|
66
|
+
WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
|
|
67
|
+
]
|
|
68
|
+
private_constant :RESERVED_KEYS, :GLOBALID_KEY,
|
|
69
|
+
:SYMBOL_KEYS_KEY, :RUBY2_KEYWORDS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
|
|
70
|
+
|
|
71
|
+
def serialize_argument(argument)
|
|
72
|
+
case argument
|
|
73
|
+
when nil, true, false, Integer, Float # Types that can hardly be subclassed
|
|
74
|
+
argument
|
|
75
|
+
when String
|
|
76
|
+
if argument.class == String
|
|
77
|
+
argument
|
|
78
|
+
else
|
|
79
|
+
begin
|
|
80
|
+
Serializers.serialize(argument)
|
|
81
|
+
rescue SerializationError
|
|
82
|
+
argument
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
when GlobalID::Identification
|
|
86
|
+
convert_to_global_id_hash(argument)
|
|
87
|
+
when Array
|
|
88
|
+
argument.map { |arg| serialize_argument(arg) }
|
|
89
|
+
when ActiveSupport::HashWithIndifferentAccess
|
|
90
|
+
serialize_indifferent_hash(argument)
|
|
91
|
+
when Hash
|
|
92
|
+
symbol_keys = argument.each_key.grep(Symbol).map!(&:to_s)
|
|
93
|
+
aj_hash_key = if Hash.ruby2_keywords_hash?(argument)
|
|
94
|
+
RUBY2_KEYWORDS_KEY
|
|
95
|
+
else
|
|
96
|
+
SYMBOL_KEYS_KEY
|
|
97
|
+
end
|
|
98
|
+
result = serialize_hash(argument)
|
|
99
|
+
result[aj_hash_key] = symbol_keys
|
|
100
|
+
result
|
|
101
|
+
else
|
|
102
|
+
if argument.respond_to?(:permitted?) && argument.respond_to?(:to_h)
|
|
103
|
+
serialize_indifferent_hash(argument.to_h)
|
|
104
|
+
else
|
|
105
|
+
Serializers.serialize(argument)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def deserialize_argument(argument)
|
|
111
|
+
case argument
|
|
112
|
+
when nil, true, false, String, Integer, Float
|
|
113
|
+
argument
|
|
114
|
+
when Array
|
|
115
|
+
argument.map { |arg| deserialize_argument(arg) }
|
|
116
|
+
when Hash
|
|
117
|
+
if serialized_global_id?(argument)
|
|
118
|
+
deserialize_global_id argument
|
|
119
|
+
elsif custom_serialized?(argument)
|
|
120
|
+
Serializers.deserialize(argument)
|
|
121
|
+
else
|
|
122
|
+
deserialize_hash(argument)
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def serialized_global_id?(hash)
|
|
130
|
+
hash.size == 1 && hash.include?(GLOBALID_KEY)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def deserialize_global_id(hash)
|
|
134
|
+
GlobalID::Locator.locate hash[GLOBALID_KEY]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def custom_serialized?(hash)
|
|
138
|
+
hash.key?(OBJECT_SERIALIZER_KEY)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def serialize_hash(argument)
|
|
142
|
+
argument.each_with_object({}) do |(key, value), hash|
|
|
143
|
+
hash[serialize_hash_key(key)] = serialize_argument(value)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def deserialize_hash(serialized_hash)
|
|
148
|
+
result = serialized_hash.transform_values { |v| deserialize_argument(v) }
|
|
149
|
+
if result.delete(WITH_INDIFFERENT_ACCESS_KEY)
|
|
150
|
+
result = result.with_indifferent_access
|
|
151
|
+
elsif symbol_keys = result.delete(SYMBOL_KEYS_KEY)
|
|
152
|
+
result = transform_symbol_keys(result, symbol_keys)
|
|
153
|
+
elsif symbol_keys = result.delete(RUBY2_KEYWORDS_KEY)
|
|
154
|
+
result = transform_symbol_keys(result, symbol_keys)
|
|
155
|
+
result = Hash.ruby2_keywords_hash(result)
|
|
156
|
+
end
|
|
157
|
+
result
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def serialize_hash_key(key)
|
|
161
|
+
case key
|
|
162
|
+
when *RESERVED_KEYS
|
|
163
|
+
raise SerializationError.new("Can't serialize a Hash with reserved key #{key.inspect}")
|
|
164
|
+
when String, Symbol
|
|
165
|
+
key.to_s
|
|
166
|
+
else
|
|
167
|
+
raise SerializationError.new("Only string and symbol hash keys may be serialized as job arguments, but #{key.inspect} is a #{key.class}")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def serialize_indifferent_hash(indifferent_hash)
|
|
172
|
+
result = serialize_hash(indifferent_hash)
|
|
173
|
+
result[WITH_INDIFFERENT_ACCESS_KEY] = serialize_argument(true)
|
|
174
|
+
result
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def transform_symbol_keys(hash, symbol_keys)
|
|
178
|
+
# NOTE: HashWithIndifferentAccess#transform_keys always
|
|
179
|
+
# returns stringified keys with indifferent access
|
|
180
|
+
# so we call #to_h here to ensure keys are symbolized.
|
|
181
|
+
hash.to_h.transform_keys do |key|
|
|
182
|
+
if symbol_keys.include?(key)
|
|
183
|
+
key.to_sym
|
|
184
|
+
else
|
|
185
|
+
key
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def convert_to_global_id_hash(argument)
|
|
191
|
+
{ GLOBALID_KEY => argument.to_global_id.to_s }
|
|
192
|
+
rescue URI::GID::MissingModelIdError
|
|
193
|
+
raise SerializationError, "Unable to serialize #{argument.class} " \
|
|
194
|
+
"without an id. (Maybe you forgot to call save?)"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_job/core"
|
|
4
|
+
require "active_job/queue_adapter"
|
|
5
|
+
require "active_job/queue_name"
|
|
6
|
+
require "active_job/queue_priority"
|
|
7
|
+
require "active_job/enqueuing"
|
|
8
|
+
require "active_job/execution"
|
|
9
|
+
require "active_job/callbacks"
|
|
10
|
+
require "active_job/exceptions"
|
|
11
|
+
require "active_job/log_subscriber"
|
|
12
|
+
require "active_job/logging"
|
|
13
|
+
require "active_job/instrumentation"
|
|
14
|
+
require "active_job/timezones"
|
|
15
|
+
require "active_job/translation"
|
|
16
|
+
|
|
17
|
+
module ActiveJob # :nodoc:
|
|
18
|
+
# = Active Job \Base
|
|
19
|
+
#
|
|
20
|
+
# Active Job objects can be configured to work with different backend
|
|
21
|
+
# queuing frameworks. To specify a queue adapter to use:
|
|
22
|
+
#
|
|
23
|
+
# ActiveJob::Base.queue_adapter = :inline
|
|
24
|
+
#
|
|
25
|
+
# A list of supported adapters can be found in QueueAdapters.
|
|
26
|
+
#
|
|
27
|
+
# Active Job objects can be defined by creating a class that inherits
|
|
28
|
+
# from the ActiveJob::Base class. The only necessary method to
|
|
29
|
+
# implement is the "perform" method.
|
|
30
|
+
#
|
|
31
|
+
# To define an Active Job object:
|
|
32
|
+
#
|
|
33
|
+
# class ProcessPhotoJob < ActiveJob::Base
|
|
34
|
+
# def perform(photo)
|
|
35
|
+
# photo.watermark!('Rails')
|
|
36
|
+
# photo.rotate!(90.degrees)
|
|
37
|
+
# photo.resize_to_fit!(300, 300)
|
|
38
|
+
# photo.upload!
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# Records that are passed in are serialized/deserialized using Global
|
|
43
|
+
# ID. More information can be found in Arguments.
|
|
44
|
+
#
|
|
45
|
+
# To enqueue a job to be performed as soon as the queuing system is free:
|
|
46
|
+
#
|
|
47
|
+
# ProcessPhotoJob.perform_later(photo)
|
|
48
|
+
#
|
|
49
|
+
# To enqueue a job to be processed at some point in the future:
|
|
50
|
+
#
|
|
51
|
+
# ProcessPhotoJob.set(wait_until: Date.tomorrow.noon).perform_later(photo)
|
|
52
|
+
#
|
|
53
|
+
# More information can be found in ActiveJob::Core::ClassMethods#set
|
|
54
|
+
#
|
|
55
|
+
# A job can also be processed immediately without sending to the queue:
|
|
56
|
+
#
|
|
57
|
+
# ProcessPhotoJob.perform_now(photo)
|
|
58
|
+
#
|
|
59
|
+
# == Exceptions
|
|
60
|
+
#
|
|
61
|
+
# * DeserializationError - Error class for deserialization errors.
|
|
62
|
+
# * SerializationError - Error class for serialization errors.
|
|
63
|
+
class Base
|
|
64
|
+
include Core
|
|
65
|
+
include QueueAdapter
|
|
66
|
+
include QueueName
|
|
67
|
+
include QueuePriority
|
|
68
|
+
include Enqueuing
|
|
69
|
+
include Execution
|
|
70
|
+
include Callbacks
|
|
71
|
+
include Exceptions
|
|
72
|
+
include Instrumentation
|
|
73
|
+
include Logging
|
|
74
|
+
include Timezones
|
|
75
|
+
include Translation
|
|
76
|
+
|
|
77
|
+
ActiveSupport.run_load_hooks(:active_job, self)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/callbacks"
|
|
4
|
+
require "active_support/core_ext/module/attribute_accessors"
|
|
5
|
+
|
|
6
|
+
module ActiveJob
|
|
7
|
+
# = Active Job \Callbacks
|
|
8
|
+
#
|
|
9
|
+
# Active Job provides hooks during the life cycle of a job. Callbacks allow you
|
|
10
|
+
# to trigger logic during this cycle. Available callbacks are:
|
|
11
|
+
#
|
|
12
|
+
# * <tt>before_enqueue</tt>
|
|
13
|
+
# * <tt>around_enqueue</tt>
|
|
14
|
+
# * <tt>after_enqueue</tt>
|
|
15
|
+
# * <tt>before_perform</tt>
|
|
16
|
+
# * <tt>around_perform</tt>
|
|
17
|
+
# * <tt>after_perform</tt>
|
|
18
|
+
module Callbacks
|
|
19
|
+
extend ActiveSupport::Concern
|
|
20
|
+
include ActiveSupport::Callbacks
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
include ActiveSupport::Callbacks
|
|
24
|
+
define_callbacks :execute
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
included do
|
|
28
|
+
define_callbacks :perform, skip_after_callbacks_if_terminated: true
|
|
29
|
+
define_callbacks :enqueue, skip_after_callbacks_if_terminated: true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# These methods will be included into any Active Job object, adding
|
|
33
|
+
# callbacks for +perform+ and +enqueue+ methods.
|
|
34
|
+
module ClassMethods
|
|
35
|
+
# Defines a callback that will get called right before the
|
|
36
|
+
# job's perform method is executed.
|
|
37
|
+
#
|
|
38
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
39
|
+
# queue_as :default
|
|
40
|
+
#
|
|
41
|
+
# before_perform do |job|
|
|
42
|
+
# UserMailer.notify_video_started_processing(job.arguments.first)
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# def perform(video_id)
|
|
46
|
+
# Video.find(video_id).process
|
|
47
|
+
# end
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
def before_perform(*filters, &blk)
|
|
51
|
+
set_callback(:perform, :before, *filters, &blk)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Defines a callback that will get called right after the
|
|
55
|
+
# job's perform method has finished.
|
|
56
|
+
#
|
|
57
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
58
|
+
# queue_as :default
|
|
59
|
+
#
|
|
60
|
+
# after_perform do |job|
|
|
61
|
+
# UserMailer.notify_video_processed(job.arguments.first)
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# def perform(video_id)
|
|
65
|
+
# Video.find(video_id).process
|
|
66
|
+
# end
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
def after_perform(*filters, &blk)
|
|
70
|
+
set_callback(:perform, :after, *filters, &blk)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Defines a callback that will get called around the job's perform method.
|
|
74
|
+
#
|
|
75
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
76
|
+
# queue_as :default
|
|
77
|
+
#
|
|
78
|
+
# around_perform do |job, block|
|
|
79
|
+
# UserMailer.notify_video_started_processing(job.arguments.first)
|
|
80
|
+
# block.call
|
|
81
|
+
# UserMailer.notify_video_processed(job.arguments.first)
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# def perform(video_id)
|
|
85
|
+
# Video.find(video_id).process
|
|
86
|
+
# end
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
# You can access the return value of the job only if the execution wasn't halted.
|
|
90
|
+
#
|
|
91
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
92
|
+
# around_perform do |job, block|
|
|
93
|
+
# value = block.call
|
|
94
|
+
# puts value # => "Hello World!"
|
|
95
|
+
# end
|
|
96
|
+
#
|
|
97
|
+
# def perform
|
|
98
|
+
# "Hello World!"
|
|
99
|
+
# end
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
def around_perform(*filters, &blk)
|
|
103
|
+
set_callback(:perform, :around, *filters, &blk)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Defines a callback that will get called right before the
|
|
107
|
+
# job is enqueued.
|
|
108
|
+
#
|
|
109
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
110
|
+
# queue_as :default
|
|
111
|
+
#
|
|
112
|
+
# before_enqueue do |job|
|
|
113
|
+
# $statsd.increment "enqueue-video-job.try"
|
|
114
|
+
# end
|
|
115
|
+
#
|
|
116
|
+
# def perform(video_id)
|
|
117
|
+
# Video.find(video_id).process
|
|
118
|
+
# end
|
|
119
|
+
# end
|
|
120
|
+
#
|
|
121
|
+
def before_enqueue(*filters, &blk)
|
|
122
|
+
set_callback(:enqueue, :before, *filters, &blk)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Defines a callback that will get called right after the
|
|
126
|
+
# job is enqueued.
|
|
127
|
+
#
|
|
128
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
129
|
+
# queue_as :default
|
|
130
|
+
#
|
|
131
|
+
# after_enqueue do |job|
|
|
132
|
+
# result = job.successfully_enqueued? ? "success" : "failure"
|
|
133
|
+
# $statsd.increment "enqueue-video-job.#{result}"
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
# def perform(video_id)
|
|
137
|
+
# Video.find(video_id).process
|
|
138
|
+
# end
|
|
139
|
+
# end
|
|
140
|
+
#
|
|
141
|
+
def after_enqueue(*filters, &blk)
|
|
142
|
+
set_callback(:enqueue, :after, *filters, &blk)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Defines a callback that will get called around the enqueuing
|
|
146
|
+
# of the job.
|
|
147
|
+
#
|
|
148
|
+
# class VideoProcessJob < ActiveJob::Base
|
|
149
|
+
# queue_as :default
|
|
150
|
+
#
|
|
151
|
+
# around_enqueue do |job, block|
|
|
152
|
+
# $statsd.time "video-job.process" do
|
|
153
|
+
# block.call
|
|
154
|
+
# end
|
|
155
|
+
# end
|
|
156
|
+
#
|
|
157
|
+
# def perform(video_id)
|
|
158
|
+
# Video.find(video_id).process
|
|
159
|
+
# end
|
|
160
|
+
# end
|
|
161
|
+
#
|
|
162
|
+
def around_enqueue(*filters, &blk)
|
|
163
|
+
set_callback(:enqueue, :around, *filters, &blk)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveJob
|
|
4
|
+
class ConfiguredJob # :nodoc:
|
|
5
|
+
def initialize(job_class, options = {})
|
|
6
|
+
@options = options
|
|
7
|
+
@job_class = job_class
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def perform_now(...)
|
|
11
|
+
@job_class.new(...).set(@options).perform_now
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def perform_later(...)
|
|
15
|
+
@job_class.new(...).enqueue @options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def perform_all_later(multi_args)
|
|
19
|
+
@job_class.perform_all_later(multi_args, options: @options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|