activejob 0 → 4.2.0.beta1

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
  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