activejob 0 → 4.2.0.beta1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed055b7c1ce137d0b117867a22ba1ba004b74cbf
4
- data.tar.gz: ea42dac65bcb13b4cc7b1897604c1f3b105c41ac
3
+ metadata.gz: f5b4155049f893704d35f9712dacadac5c6a63a3
4
+ data.tar.gz: c072445cde94c55babc2a55489e877ff7930529e
5
5
  SHA512:
6
- metadata.gz: 14b4bf2e84d31918d860f075dcd556f494de8ef35e52ba4b864c3f7e5fbe9cd30470f89851b2d36a1a993a929de0c56b0693cac5833660b0d0393bfdcdc5eb97
7
- data.tar.gz: cbf00f328f8149a6e52dc757c362cd46399b5931ef389e959a58b6e79c5e5581f75d2b4937cf3547cb8e4994e38674e9bf7dd65649f7ac25069da25eeb3e6851
6
+ metadata.gz: 563242f9567f12e8cb4ecb8204fc9a0956c93507ddea5eca3d3fe824157aca34afacfd47eff94e73aadadedbfb8972e058af16cb226f9ca53959cab3b8cf2de3
7
+ data.tar.gz: fbc34e79c27f8ce715d350c740b149b8fe02decf1d3175a6c1ab3a859b93caf963a094c1c5c5d01c6b22f8f5bb9cd093504f2263d10f4ca50b0319ef334f66c6
@@ -0,0 +1,141 @@
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 queueing backends. These jobs can be everything from regularly scheduled
5
+ clean-ups, billing charges, or mailings. Anything that can be chopped up into
6
+ small units of work and run in parallel, really.
7
+
8
+ It also serves as the backend for ActionMailer'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
+ of 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
+
21
+ ## Usage
22
+
23
+ Set the queue adapter for Active Job:
24
+
25
+ ``` ruby
26
+ ActiveJob::Base.queue_adapter = :inline # default queue adapter
27
+ # Adapters currently supported: :backburner, :delayed_job, :qu, :que, :queue_classic,
28
+ # :resque, :sidekiq, :sneakers, :sucker_punch
29
+ ```
30
+
31
+ Declare a job like so:
32
+
33
+ ```ruby
34
+ class MyJob < ActiveJob::Base
35
+ queue_as :my_jobs
36
+
37
+ def perform(record)
38
+ record.do_work
39
+ end
40
+ end
41
+ ```
42
+
43
+ Enqueue a job like so:
44
+
45
+ ```ruby
46
+ MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free.
47
+ ```
48
+
49
+ ```ruby
50
+ MyJob.enqueue_at Date.tomorrow.noon, record # Enqueue a job to be performed tomorrow at noon.
51
+ ```
52
+
53
+ ```ruby
54
+ MyJob.enqueue_in 1.week, record # Enqueue a job to be performed 1 week from now.
55
+ ```
56
+
57
+ That's it!
58
+
59
+
60
+ ## GlobalID support
61
+
62
+ Active Job supports [GlobalID serialization](https://github.com/rails/globalid/) for parameters. This makes it possible
63
+ to pass live Active Record objects to your job instead of class/id pairs, which
64
+ you then have to manually deserialize. Before, jobs would look like this:
65
+
66
+ ```ruby
67
+ class TrashableCleanupJob
68
+ def perform(trashable_class, trashable_id, depth)
69
+ trashable = trashable_class.constantize.find(trashable_id)
70
+ trashable.cleanup(depth)
71
+ end
72
+ end
73
+ ```
74
+
75
+ Now you can simply do:
76
+
77
+ ```ruby
78
+ class TrashableCleanupJob
79
+ def perform(trashable, depth)
80
+ trashable.cleanup(depth)
81
+ end
82
+ end
83
+ ```
84
+
85
+ This works with any class that mixes in GlobalID::Identification, which
86
+ by default has been mixed into Active Record classes.
87
+
88
+
89
+ ## Supported queueing systems
90
+
91
+ We currently have adapters for:
92
+
93
+ * [Backburner](https://github.com/nesquena/backburner)
94
+ * [Delayed Job](https://github.com/collectiveidea/delayed_job)
95
+ * [Qu](https://github.com/bkeepers/qu)
96
+ * [Que](https://github.com/chanks/que)
97
+ * [QueueClassic](https://github.com/ryandotsmith/queue_classic)
98
+ * [Resque 1.x](https://github.com/resque/resque)
99
+ * [Sidekiq](https://github.com/mperham/sidekiq)
100
+ * [Sneakers](https://github.com/jondot/sneakers)
101
+ * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch)
102
+
103
+
104
+ ## Auxiliary gems
105
+
106
+ * [activejob-stats](https://github.com/seuros/activejob-stats)
107
+
108
+ ## Download and installation
109
+
110
+ The latest version of Active Job can be installed with RubyGems:
111
+
112
+ ```
113
+ % [sudo] gem install activejob
114
+ ```
115
+
116
+ Source code can be downloaded as part of the Rails project on GitHub
117
+
118
+ * https://github.com/rails/rails/tree/master/activejob
119
+
120
+ ## License
121
+
122
+ ActiveJob is released under the MIT license:
123
+
124
+ * http://www.opensource.org/licenses/MIT
125
+
126
+
127
+ ## Support
128
+
129
+ API documentation is at
130
+
131
+ * http://api.rubyonrails.org
132
+
133
+ Bug reports can be filed for the Ruby on Rails project here:
134
+
135
+ * https://github.com/rails/rails/issues
136
+
137
+ Feature requests should be discussed on the rails-core mailing list here:
138
+
139
+ * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
140
+
141
+
@@ -23,8 +23,8 @@
23
23
 
24
24
  require 'active_support'
25
25
  require 'active_support/rails'
26
-
27
26
  require 'active_job/version'
27
+ require 'global_id'
28
28
 
29
29
  module ActiveJob
30
30
  extend ActiveSupport::Autoload
@@ -0,0 +1,62 @@
1
+ module ActiveJob
2
+ class DeserializationError < StandardError
3
+ attr_reader :original_exception
4
+
5
+ def initialize(e)
6
+ super ("Error while trying to deserialize arguments: #{e.message}")
7
+ @original_exception = e
8
+ set_backtrace e.backtrace
9
+ end
10
+ end
11
+
12
+ module Arguments
13
+ extend self
14
+ TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ]
15
+
16
+ def serialize(arguments)
17
+ arguments.map { |argument| serialize_argument(argument) }
18
+ end
19
+
20
+ def deserialize(arguments)
21
+ arguments.map { |argument| deserialize_argument(argument) }
22
+ end
23
+
24
+ private
25
+ def serialize_argument(argument)
26
+ case argument
27
+ when GlobalID::Identification
28
+ argument.global_id.to_s
29
+ when *TYPE_WHITELIST
30
+ argument
31
+ when Array
32
+ serialize(argument)
33
+ when Hash
34
+ Hash[ argument.map { |key, value| [ serialize_hash_key(key), serialize_argument(value) ] } ]
35
+ else
36
+ raise "Unsupported argument type: #{argument.class.name}"
37
+ end
38
+ end
39
+
40
+ def deserialize_argument(argument)
41
+ case argument
42
+ when Array
43
+ deserialize(argument)
44
+ when Hash
45
+ Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access
46
+ else
47
+ GlobalID::Locator.locate(argument) || argument
48
+ end
49
+ rescue => e
50
+ raise DeserializationError.new(e)
51
+ end
52
+
53
+ def serialize_hash_key(key)
54
+ case key
55
+ when String, Symbol
56
+ key.to_s
57
+ else
58
+ raise "Unsupported hash key type: #{key.class.name}"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,13 +1,22 @@
1
1
  require 'active_job/queue_adapter'
2
2
  require 'active_job/queue_name'
3
3
  require 'active_job/enqueuing'
4
+ require 'active_job/execution'
5
+ require 'active_job/callbacks'
6
+ require 'active_job/identifier'
4
7
  require 'active_job/logging'
5
8
 
6
9
  module ActiveJob
7
10
  class Base
8
11
  extend QueueAdapter
9
- extend QueueName
10
- extend Enqueuing
11
- extend Logging
12
+
13
+ include QueueName
14
+ include Enqueuing
15
+ include Execution
16
+ include Callbacks
17
+ include Identifier
18
+ include Logging
19
+
20
+ ActiveSupport.run_load_hooks(:active_job, self)
12
21
  end
13
22
  end
@@ -0,0 +1,144 @@
1
+ require 'active_support/callbacks'
2
+
3
+ module ActiveJob
4
+ # = Active Job Callbacks
5
+ #
6
+ # Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger
7
+ # logic during the lifecycle of a job. Available callbacks:
8
+ #
9
+ # * <tt>before_enqueue</tt>
10
+ # * <tt>around_enqueue</tt>
11
+ # * <tt>after_enqueue</tt>
12
+ # * <tt>before_perform</tt>
13
+ # * <tt>around_perform</tt>
14
+ # * <tt>after_perform</tt>
15
+ #
16
+ module Callbacks
17
+ extend ActiveSupport::Concern
18
+ include ActiveSupport::Callbacks
19
+
20
+ included do
21
+ define_callbacks :perform
22
+ define_callbacks :enqueue
23
+ end
24
+
25
+ module ClassMethods
26
+ # Defines a callback that will get called right before the
27
+ # job's perform method is executed.
28
+ #
29
+ # class VideoProcessJob < ActiveJob::Base
30
+ # queue_as :default
31
+ #
32
+ # before_perform do |job|
33
+ # UserMailer.notify_video_started_processing(job.arguments.first)
34
+ # end
35
+ #
36
+ # def perform(video_id)
37
+ # Video.find(video_id).process
38
+ # end
39
+ # end
40
+ #
41
+ def before_perform(*filters, &blk)
42
+ set_callback(:perform, :before, *filters, &blk)
43
+ end
44
+
45
+ # Defines a callback that will get called right after the
46
+ # job's perform method has finished.
47
+ #
48
+ # class VideoProcessJob < ActiveJob::Base
49
+ # queue_as :default
50
+ #
51
+ # after_perform do |job|
52
+ # UserMailer.notify_video_processed(job.arguments.first)
53
+ # end
54
+ #
55
+ # def perform(video_id)
56
+ # Video.find(video_id).process
57
+ # end
58
+ # end
59
+ #
60
+ def after_perform(*filters, &blk)
61
+ set_callback(:perform, :after, *filters, &blk)
62
+ end
63
+
64
+ # Defines a callback that will get called around the job's perform method.
65
+ #
66
+ # class VideoProcessJob < ActiveJob::Base
67
+ # queue_as :default
68
+ #
69
+ # around_perform do |job, block|
70
+ # UserMailer.notify_video_started_processing(job.arguments.first)
71
+ # block.call
72
+ # UserMailer.notify_video_processed(job.arguments.first)
73
+ # end
74
+ #
75
+ # def perform(video_id)
76
+ # Video.find(video_id).process
77
+ # end
78
+ # end
79
+ #
80
+ def around_perform(*filters, &blk)
81
+ set_callback(:perform, :around, *filters, &blk)
82
+ end
83
+
84
+ # Defines a callback that will get called right before the
85
+ # job is enqueued.
86
+ #
87
+ # class VideoProcessJob < ActiveJob::Base
88
+ # queue_as :default
89
+ #
90
+ # before_enqueue do |job|
91
+ # $statsd.increment "enqueue-video-job.try"
92
+ # end
93
+ #
94
+ # def perform(video_id)
95
+ # Video.find(video_id).process
96
+ # end
97
+ # end
98
+ #
99
+ def before_enqueue(*filters, &blk)
100
+ set_callback(:enqueue, :before, *filters, &blk)
101
+ end
102
+
103
+ # Defines a callback that will get called right after the
104
+ # job is enqueued.
105
+ #
106
+ # class VideoProcessJob < ActiveJob::Base
107
+ # queue_as :default
108
+ #
109
+ # after_enqueue do |job|
110
+ # $statsd.increment "enqueue-video-job.success"
111
+ # end
112
+ #
113
+ # def perform(video_id)
114
+ # Video.find(video_id).process
115
+ # end
116
+ # end
117
+ #
118
+ def after_enqueue(*filters, &blk)
119
+ set_callback(:enqueue, :after, *filters, &blk)
120
+ end
121
+
122
+ # Defines a callback that will get called before and after the
123
+ # job is enqueued.
124
+ #
125
+ # class VideoProcessJob < ActiveJob::Base
126
+ # queue_as :default
127
+ #
128
+ # around_enqueue do |job, block|
129
+ # $statsd.time "video-job.process" do
130
+ # block.call
131
+ # end
132
+ # end
133
+ #
134
+ # def perform(video_id)
135
+ # Video.find(video_id).process
136
+ # end
137
+ # end
138
+ #
139
+ def around_enqueue(*filters, &blk)
140
+ set_callback(:enqueue, :around, *filters, &blk)
141
+ end
142
+ end
143
+ end
144
+ end
@@ -1,18 +1,71 @@
1
- require 'active_job/parameters'
1
+ require 'active_job/arguments'
2
2
 
3
3
  module ActiveJob
4
4
  module Enqueuing
5
- # Push a job onto the queue. The arguments must be legal JSON types
6
- # (string, int, float, nil, true, false, hash or array) or
7
- # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects
8
- # are not supported.
9
- #
10
- # The return value is adapter-specific and may change in a future
11
- # ActiveJob release.
12
- def enqueue(*args)
13
- serialized_args = Parameters.serialize(args)
14
- ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, args: serialized_args
15
- queue_adapter.queue self, *serialized_args
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Push a job onto the queue. The arguments must be legal JSON types
9
+ # (string, int, float, nil, true, false, hash or array) or
10
+ # GlobalID::Identification instances. Arbitrary Ruby objects
11
+ # are not supported.
12
+ #
13
+ # Returns an instance of the job class queued with args available in
14
+ # Job#arguments.
15
+ def enqueue(*args)
16
+ new(args).tap do |job|
17
+ job.run_callbacks :enqueue do
18
+ queue_adapter.enqueue self, job.job_id, *Arguments.serialize(args)
19
+ end
20
+ end
21
+ end
22
+
23
+ # Enqueue a job to be performed at +interval+ from now.
24
+ #
25
+ # enqueue_in(1.week, "mike")
26
+ #
27
+ # Returns an instance of the job class queued with args available in
28
+ # Job#arguments and the timestamp in Job#enqueue_at.
29
+ def enqueue_in(interval, *args)
30
+ enqueue_at interval.seconds.from_now, *args
31
+ end
32
+
33
+ # Enqueue a job to be performed at an explicit point in time.
34
+ #
35
+ # enqueue_at(Date.tomorrow.midnight, "mike")
36
+ #
37
+ # Returns an instance of the job class queued with args available in
38
+ # Job#arguments and the timestamp in Job#enqueue_at.
39
+ def enqueue_at(timestamp, *args)
40
+ new(args).tap do |job|
41
+ job.enqueued_at = timestamp
42
+
43
+ job.run_callbacks :enqueue do
44
+ queue_adapter.enqueue_at self, timestamp.to_f, job.job_id, *Arguments.serialize(args)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ included do
51
+ attr_accessor :arguments
52
+ attr_accessor :enqueued_at
53
+ end
54
+
55
+ def initialize(arguments = nil)
56
+ @arguments = arguments
57
+ end
58
+
59
+ def retry_now
60
+ self.class.enqueue(*arguments)
61
+ end
62
+
63
+ def retry_in(interval)
64
+ self.class.enqueue_in interval, *arguments
65
+ end
66
+
67
+ def retry_at(timestamp)
68
+ self.class.enqueue_at timestamp, *arguments
16
69
  end
17
70
  end
18
71
  end
@@ -0,0 +1,33 @@
1
+ require 'active_support/rescuable'
2
+ require 'active_job/arguments'
3
+
4
+ module ActiveJob
5
+ module Execution
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveSupport::Rescuable
10
+ end
11
+
12
+ def execute(job_id, *serialized_args)
13
+ self.job_id = job_id
14
+ self.arguments = deserialize_arguments(serialized_args)
15
+
16
+ run_callbacks :perform do
17
+ perform(*arguments)
18
+ end
19
+ rescue => exception
20
+ rescue_with_handler(exception) || raise(exception)
21
+ end
22
+
23
+ def perform(*)
24
+ fail NotImplementedError
25
+ end
26
+
27
+ private
28
+ def deserialize_arguments(serialized_args)
29
+ Arguments.deserialize(serialized_args)
30
+ end
31
+
32
+ end
33
+ end
@@ -8,7 +8,7 @@ module ActiveJob
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
10
  TINY = 0
11
- PRE = "alpha"
11
+ PRE = "beta1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -0,0 +1,15 @@
1
+ require 'active_job/arguments'
2
+
3
+ module ActiveJob
4
+ module Identifier
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_writer :job_id
9
+ end
10
+
11
+ def job_id
12
+ @job_id ||= SecureRandom.uuid
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,90 @@
1
- require 'active_job/log_subscriber'
1
+ require 'active_support/core_ext/string/filters'
2
+ require 'active_support/tagged_logging'
3
+ require 'active_support/logger'
2
4
 
3
5
  module ActiveJob
4
6
  module Logging
5
- mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) }
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) }
11
+
12
+ around_enqueue do |_, block, _|
13
+ tag_logger do
14
+ block.call
15
+ end
16
+ end
17
+
18
+ around_perform do |job, block, _|
19
+ tag_logger(job.class.name, job.job_id) do
20
+ payload = {adapter: job.class.queue_adapter, job: job.class, args: job.arguments}
21
+ ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
22
+ ActiveSupport::Notifications.instrument("perform.active_job", payload) do
23
+ block.call
24
+ end
25
+ end
26
+ end
27
+
28
+ before_enqueue do |job|
29
+ if job.enqueued_at
30
+ ActiveSupport::Notifications.instrument "enqueue_at.active_job",
31
+ adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments, timestamp: job.enqueued_at
32
+ else
33
+ ActiveSupport::Notifications.instrument "enqueue.active_job",
34
+ adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+ def tag_logger(*tags)
41
+ if logger.respond_to?(:tagged)
42
+ tags.unshift "ActiveJob" unless logger_tagged_by_active_job?
43
+ ActiveJob::Base.logger.tagged(*tags){ yield }
44
+ else
45
+ yield
46
+ end
47
+ end
48
+
49
+ def logger_tagged_by_active_job?
50
+ logger.formatter.current_tags.include?("ActiveJob")
51
+ end
52
+
53
+ class LogSubscriber < ActiveSupport::LogSubscriber
54
+ def enqueue(event)
55
+ info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event)
56
+ end
57
+
58
+ def enqueue_at(event)
59
+ info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event)
60
+ end
61
+
62
+ def perform_start(event)
63
+ info "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event)
64
+ end
65
+
66
+ def perform(event)
67
+ info "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms"
68
+ end
69
+
70
+ private
71
+ def queue_name(event)
72
+ event.payload[:adapter].name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})"
73
+ end
74
+
75
+ def args_info(event)
76
+ event.payload[:args].any? ? " with arguments: #{event.payload[:args].map(&:inspect).join(", ")}" : ""
77
+ end
78
+
79
+ def enqueued_at(event)
80
+ Time.at(event.payload[:timestamp]).utc
81
+ end
82
+
83
+ def logger
84
+ ActiveJob::Base.logger
85
+ end
86
+ end
6
87
  end
7
88
  end
89
+
90
+ ActiveJob::Logging::LogSubscriber.attach_to :active_job
@@ -4,15 +4,19 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class BackburnerAdapter
6
6
  class << self
7
- def queue(job, *args)
7
+ def enqueue(job, *args)
8
8
  Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name
9
9
  end
10
+
11
+ def enqueue_at(job, timestamp, *args)
12
+ raise NotImplementedError
13
+ end
10
14
  end
11
15
 
12
16
  class JobWrapper
13
17
  class << self
14
18
  def perform(job_name, *args)
15
- job_name.constantize.new.perform *Parameters.deserialize(args)
19
+ job_name.constantize.new.execute(*args)
16
20
  end
17
21
  end
18
22
  end
@@ -4,14 +4,18 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class DelayedJobAdapter
6
6
  class << self
7
- def queue(job, *args)
7
+ def enqueue(job, *args)
8
8
  JobWrapper.new.delay(queue: job.queue_name).perform(job, *args)
9
9
  end
10
+
11
+ def enqueue_at(job, timestamp, *args)
12
+ JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job, *args)
13
+ end
10
14
  end
11
-
15
+
12
16
  class JobWrapper
13
17
  def perform(job, *args)
14
- job.new.perform *Parameters.deserialize(args)
18
+ job.new.execute(*args)
15
19
  end
16
20
  end
17
21
  end
@@ -2,10 +2,14 @@ module ActiveJob
2
2
  module QueueAdapters
3
3
  class InlineAdapter
4
4
  class << self
5
- def queue(job, *args)
6
- job.new.perform *Parameters.deserialize(args)
5
+ def enqueue(job, *args)
6
+ job.new.execute(*args)
7
+ end
8
+
9
+ def enqueue_at(*)
10
+ raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at https://github.com/rails/activejob")
7
11
  end
8
12
  end
9
13
  end
10
14
  end
11
- end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'qu'
2
+
3
+ module ActiveJob
4
+ module QueueAdapters
5
+ class QuAdapter
6
+ class << self
7
+ def enqueue(job, *args)
8
+ Qu::Payload.new(klass: JobWrapper, args: [job.name, *args]).tap do |payload|
9
+ payload.instance_variable_set(:@queue, job.queue_name)
10
+ end.push
11
+ end
12
+
13
+ def enqueue_at(job, timestamp, *args)
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+
18
+ class JobWrapper < Qu::Job
19
+ def initialize(job_name, *args)
20
+ @job = job_name.constantize
21
+ @args = args
22
+ end
23
+
24
+ def perform
25
+ @job.new.execute(*@args)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,14 +4,18 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class QueAdapter
6
6
  class << self
7
- def queue(job, *args)
8
- JobWrapper.enqueue job, *args, queue: job.queue_name
7
+ def enqueue(job, *args)
8
+ JobWrapper.enqueue job.name, *args, queue: job.queue_name
9
+ end
10
+
11
+ def enqueue_at(job, timestamp, *args)
12
+ raise NotImplementedError
9
13
  end
10
14
  end
11
15
 
12
16
  class JobWrapper < Que::Job
13
- def run(job, *args)
14
- job.new.perform *Parameters.deserialize(args)
17
+ def run(job_name, *args)
18
+ job_name.constantize.new.execute(*args)
15
19
  end
16
20
  end
17
21
  end
@@ -4,14 +4,20 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class QueueClassicAdapter
6
6
  class << self
7
- def queue(job, *args)
8
- QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job, *args)
7
+ def enqueue(job, *args)
8
+ QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.name, *args)
9
+ end
10
+
11
+ def enqueue_at(job, timestamp, *args)
12
+ raise NotImplementedError
9
13
  end
10
14
  end
11
15
 
12
16
  class JobWrapper
13
- def self.perform(job, *args)
14
- job.new.perform *Parameters.deserialize(args)
17
+ class << self
18
+ def perform(job_name, *args)
19
+ job_name.constantize.new.execute(*args)
20
+ end
15
21
  end
16
22
  end
17
23
  end
@@ -2,29 +2,39 @@ require 'resque'
2
2
  require 'active_support/core_ext/enumerable'
3
3
  require 'active_support/core_ext/array/access'
4
4
 
5
+ begin
6
+ require 'resque-scheduler'
7
+ rescue LoadError
8
+ begin
9
+ require 'resque_scheduler'
10
+ rescue LoadError
11
+ false
12
+ end
13
+ end
14
+
5
15
  module ActiveJob
6
16
  module QueueAdapters
7
17
  class ResqueAdapter
8
18
  class << self
9
- def queue(job, *args)
10
- Resque.enqueue JobWrapper.new(job), job, *args
19
+ def enqueue(job, *args)
20
+ Resque.enqueue_to job.queue_name, JobWrapper, job.name, *args
21
+ end
22
+
23
+ def enqueue_at(job, timestamp, *args)
24
+ unless Resque.respond_to?(:enqueue_at_with_queue)
25
+ raise NotImplementedError, "To be able to schedule jobs with Resque you need the " \
26
+ "resque-scheduler gem. Please add it to your Gemfile and run bundle install"
27
+ end
28
+ Resque.enqueue_at_with_queue job.queue_name, timestamp, JobWrapper, job.name, *args
11
29
  end
12
30
  end
13
31
 
14
32
  class JobWrapper
15
33
  class << self
16
34
  def perform(job_name, *args)
17
- job_name.constantize.new.perform *Parameters.deserialize(args)
35
+ job_name.constantize.new.execute(*args)
18
36
  end
19
37
  end
20
-
21
- def initialize(job)
22
- @queue = job.queue_name
23
- end
24
-
25
- def to_s
26
- self.class.name
27
- end
28
38
  end
29
39
  end
30
40
  end
@@ -4,20 +4,30 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class SidekiqAdapter
6
6
  class << self
7
- def queue(job, *args)
7
+ def enqueue(job, *args)
8
+ #Sidekiq::Client does not support symbols as keys
8
9
  Sidekiq::Client.push \
9
10
  'class' => JobWrapper,
10
11
  'queue' => job.queue_name,
11
12
  'args' => [ job, *args ],
12
13
  'retry' => true
13
14
  end
15
+
16
+ def enqueue_at(job, timestamp, *args)
17
+ Sidekiq::Client.push \
18
+ 'class' => JobWrapper,
19
+ 'queue' => job.queue_name,
20
+ 'args' => [ job, *args ],
21
+ 'retry' => true,
22
+ 'at' => timestamp
23
+ end
14
24
  end
15
25
 
16
26
  class JobWrapper
17
27
  include Sidekiq::Worker
18
28
 
19
29
  def perform(job_name, *args)
20
- job_name.constantize.new.perform *Parameters.deserialize(args)
30
+ job_name.constantize.new.execute(*args)
21
31
  end
22
32
  end
23
33
  end
@@ -4,22 +4,29 @@ require 'thread'
4
4
  module ActiveJob
5
5
  module QueueAdapters
6
6
  class SneakersAdapter
7
- @mutex = Mutex.new
8
-
7
+ @monitor = Monitor.new
8
+
9
9
  class << self
10
- def queue(job, *args)
11
- @mutex.synchronize do
10
+ def enqueue(job, *args)
11
+ @monitor.synchronize do
12
12
  JobWrapper.from_queue job.queue_name
13
- JobWrapper.enqueue [ job, *args ]
13
+ JobWrapper.enqueue ActiveSupport::JSON.encode([ job.name, *args ])
14
14
  end
15
15
  end
16
+
17
+ def enqueue_at(job, timestamp, *args)
18
+ raise NotImplementedError
19
+ end
16
20
  end
17
21
 
18
22
  class JobWrapper
19
23
  include Sneakers::Worker
24
+ from_queue 'active_jobs_default'
20
25
 
21
- def work(job, *args)
22
- job.new.perform *Parameters.deserialize(args)
26
+ def work(msg)
27
+ job_name, *args = ActiveSupport::JSON.decode(msg)
28
+ job_name.constantize.new.execute(*args)
29
+ ack!
23
30
  end
24
31
  end
25
32
  end
@@ -4,8 +4,12 @@ module ActiveJob
4
4
  module QueueAdapters
5
5
  class SuckerPunchAdapter
6
6
  class << self
7
- def queue(job, *args)
8
- JobWrapper.new.async.perform(job, *args)
7
+ def enqueue(job, *args)
8
+ JobWrapper.new.async.perform job, *args
9
+ end
10
+
11
+ def enqueue_at(job, timestamp, *args)
12
+ raise NotImplementedError
9
13
  end
10
14
  end
11
15
 
@@ -13,7 +17,7 @@ module ActiveJob
13
17
  include SuckerPunch::Job
14
18
 
15
19
  def perform(job, *args)
16
- job.new.perform *Parameters.deserialize(args)
20
+ job.new.execute(*args)
17
21
  end
18
22
  end
19
23
  end
@@ -1,10 +1,21 @@
1
1
  module ActiveJob
2
2
  module QueueName
3
- mattr_accessor(:queue_base_name) { "active_jobs" }
4
- mattr_accessor(:queue_name) { queue_base_name }
3
+ extend ActiveSupport::Concern
5
4
 
6
- def queue_as(part_name)
7
- self.queue_name = "#{queue_base_name}_#{part_name}"
5
+ module ClassMethods
6
+ mattr_accessor(:queue_name_prefix)
7
+ mattr_accessor(:default_queue_name) { "default" }
8
+
9
+ def queue_as(part_name)
10
+ queue_name = part_name.to_s.presence || default_queue_name
11
+ name_parts = [queue_name_prefix.presence, queue_name]
12
+ self.queue_name = name_parts.compact.join('_')
13
+ end
14
+ end
15
+
16
+ included do
17
+ class_attribute :queue_name
18
+ self.queue_name = default_queue_name
8
19
  end
9
20
  end
10
- end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'global_id/railtie'
2
+ require 'active_job'
3
+
4
+ module ActiveJob
5
+ # = Active Job Railtie
6
+ class Railtie < Rails::Railtie # :nodoc:
7
+ config.active_job = ActiveSupport::OrderedOptions.new
8
+
9
+ initializer 'active_job.logger' do
10
+ ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
11
+ end
12
+
13
+ initializer "active_job.set_configs" do |app|
14
+ options = app.config.active_job
15
+ options.queue_adapter ||= :inline
16
+
17
+ ActiveSupport.on_load(:active_job) do
18
+ options.each { |k,v| send("#{k}=", v) }
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module Rails
4
+ module Generators # :nodoc:
5
+ class JobGenerator < Rails::Generators::NamedBase # :nodoc:
6
+ desc 'This generator creates an active job file at app/jobs'
7
+
8
+ class_option :queue, type: :string, default: 'default', desc: 'The queue name for the generated job'
9
+
10
+ def self.default_generator_root
11
+ File.dirname(__FILE__)
12
+ end
13
+
14
+ check_class_collision suffix: 'Job'
15
+
16
+ def create_job_file
17
+ template 'job.rb', File.join('app/jobs', class_path, "#{file_name}_job.rb")
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Job < ActiveJob::Base
3
+ queue_as :<%= options[:queue] %>
4
+
5
+ def perform(*args)
6
+ # Do something later
7
+ end
8
+ end
9
+ <% end -%>
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activejob
3
3
  version: !ruby/object:Gem::Version
4
- version: '0'
4
+ version: 4.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-20 00:00:00.000000000 Z
11
+ date: 2014-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: globalid
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.1.0
19
+ version: 0.2.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.1.0
27
- - !ruby/object:Gem::Dependency
28
- name: activemodel-globalid
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
26
+ version: 0.2.3
41
27
  description: Declare job classes that can be run by a variety of queueing backends.
42
28
  email: david@loudthinking.com
43
29
  executables: []
@@ -46,17 +32,21 @@ extra_rdoc_files: []
46
32
  files:
47
33
  - CHANGELOG.md
48
34
  - MIT-LICENSE
35
+ - README.md
49
36
  - lib/active_job.rb
37
+ - lib/active_job/arguments.rb
50
38
  - lib/active_job/base.rb
39
+ - lib/active_job/callbacks.rb
51
40
  - lib/active_job/enqueuing.rb
41
+ - lib/active_job/execution.rb
52
42
  - lib/active_job/gem_version.rb
53
- - lib/active_job/log_subscriber.rb
43
+ - lib/active_job/identifier.rb
54
44
  - lib/active_job/logging.rb
55
- - lib/active_job/parameters.rb
56
45
  - lib/active_job/queue_adapter.rb
57
46
  - lib/active_job/queue_adapters/backburner_adapter.rb
58
47
  - lib/active_job/queue_adapters/delayed_job_adapter.rb
59
48
  - lib/active_job/queue_adapters/inline_adapter.rb
49
+ - lib/active_job/queue_adapters/qu_adapter.rb
60
50
  - lib/active_job/queue_adapters/que_adapter.rb
61
51
  - lib/active_job/queue_adapters/queue_classic_adapter.rb
62
52
  - lib/active_job/queue_adapters/resque_adapter.rb
@@ -64,8 +54,10 @@ files:
64
54
  - lib/active_job/queue_adapters/sneakers_adapter.rb
65
55
  - lib/active_job/queue_adapters/sucker_punch_adapter.rb
66
56
  - lib/active_job/queue_name.rb
67
- - lib/active_job/railitie.rb
57
+ - lib/active_job/railtie.rb
68
58
  - lib/active_job/version.rb
59
+ - lib/rails/generators/job/job_generator.rb
60
+ - lib/rails/generators/job/templates/job.rb
69
61
  homepage: http://www.rubyonrails.org
70
62
  licenses:
71
63
  - MIT
@@ -81,14 +73,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
73
  version: 1.9.3
82
74
  required_rubygems_version: !ruby/object:Gem::Requirement
83
75
  requirements:
84
- - - ">="
76
+ - - ">"
85
77
  - !ruby/object:Gem::Version
86
- version: '0'
78
+ version: 1.3.1
87
79
  requirements: []
88
80
  rubyforge_project:
89
81
  rubygems_version: 2.2.2
90
82
  signing_key:
91
83
  specification_version: 4
92
- summary: Job framework with pluggable queues (will be part of Rails).
84
+ summary: Job framework with pluggable queues.
93
85
  test_files: []
94
- has_rdoc:
@@ -1,19 +0,0 @@
1
- require 'active_support/core_ext/string/filters'
2
-
3
- module ActiveJob
4
- class LogSubscriber < ActiveSupport::LogSubscriber
5
- def enqueue(event)
6
- queue_name = event.payload[:adapter].name.demodulize.remove('Adapter')
7
- job_name = event.payload[:job].name
8
- args = event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : ""
9
-
10
- info "Enqueued #{job_name} to #{queue_name}" + args
11
- end
12
-
13
- def logger
14
- ActiveJob::Base.logger
15
- end
16
- end
17
- end
18
-
19
- ActiveJob::LogSubscriber.attach_to :active_job
@@ -1,19 +0,0 @@
1
- require 'active_model/global_locator'
2
- require 'active_support/core_ext/object/try'
3
-
4
- module ActiveJob
5
- class Parameters
6
- TYPE_WHITELIST = [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum]
7
-
8
- def self.serialize(params)
9
- params.collect do |param|
10
- raise "Unsupported parameter type: #{param.class.name}" unless param.respond_to?(:global_id) || TYPE_WHITELIST.include?(param.class)
11
- param.try(:global_id) || param
12
- end
13
- end
14
-
15
- def self.deserialize(params)
16
- params.collect { |param| ActiveModel::GlobalLocator.locate(param) || param }
17
- end
18
- end
19
- end
@@ -1,8 +0,0 @@
1
- module ActiveJob
2
- # = Active Job Railtie
3
- class Railtie < Rails::Railtie # :nodoc:
4
- initializer 'active_job.logger' do
5
- ActiveSupport.on_load(:active_job) { self.logger ||= ::Rails.logger }
6
- end
7
- end
8
- end