queue_classic_plus 1.0.0 → 4.0.0.alpha9

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
- SHA1:
3
- metadata.gz: 7d499c51f34d828e8abf2c5e405d82ea3ae49b19
4
- data.tar.gz: 335713b0d79d1b54b9dc342a36c88c2008fccab2
2
+ SHA256:
3
+ metadata.gz: 998da8f0e2f5f397e1125b9f47f447ba3cdb17908e79bb4eb9d34a5acb87f7d8
4
+ data.tar.gz: fb21b8b1175142d27792885d814597c145b68fe2e82e8541d6db78a000c30a86
5
5
  SHA512:
6
- metadata.gz: a529d8ca6f1a179f186f6b57bc90543bec9607a2c87e700db996a87301bea7073b353880ade9796d93d733573112f5545ef1e22440c29156127bd11edeb87212
7
- data.tar.gz: c23fde5d11939355a2c8a90a92001f336da6ecf3c422aca1bd11ebed5e55f019b0d8fc5720495ad65439a95b79c11f77cd00f4433533a8d7baf38b19f807fcc7
6
+ metadata.gz: 84850b2d1be63a0127adf54eab5d810b6fde5de1d9ee2826a2121f76343fc3442cac74de674858234fcf5ae1f25c418f70d578e242280f83485a892e935f13e3
7
+ data.tar.gz: 1e2e3b3ca6d2280c5aa445737b7179e2bd7f800c8c5a971d81fc3978f753189f437c99fe6ea81b7d69c079b631e4da41c1adf09032f9fe92863c8003e3fd8ee4
@@ -0,0 +1,67 @@
1
+ version: 2.1
2
+
3
+ jobs:
4
+ test:
5
+ docker:
6
+ - image: circleci/ruby:2.7.4-node
7
+ auth:
8
+ username: $DOCKERHUB_USERNAME
9
+ password: $DOCKERHUB_TOKEN
10
+ environment:
11
+ DATABASE_URL: postgres://circleci:circleci@127.0.0.1:5432/queue_classic_plus_test
12
+ - image: circleci/postgres:9.6.6-alpine
13
+ auth:
14
+ username: $DOCKERHUB_USERNAME
15
+ password: $DOCKERHUB_TOKEN
16
+ environment:
17
+ POSTGRES_USER: circleci
18
+ POSTGRES_PASSWORD: circleci
19
+ POSTGRES_DB: queue_classic_plus_test
20
+ steps:
21
+ - checkout
22
+ - run:
23
+ name: run tests
24
+ command: |
25
+ bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
26
+ bundle exec rspec
27
+
28
+ push_to_rubygems:
29
+ docker:
30
+ - image: circleci/ruby:2.7.4
31
+ auth:
32
+ username: $DOCKERHUB_USERNAME
33
+ password: $DOCKERHUB_TOKEN
34
+ steps:
35
+ - checkout
36
+ - run:
37
+ name: Create .gem/credentials file
38
+ command: |
39
+ mkdir ~/.gem
40
+ echo "---
41
+ :rubygems_api_key: $RUBYGEMS_API_KEY
42
+ " > ~/.gem/credentials
43
+ chmod 600 ~/.gem/credentials
44
+ - run:
45
+ name: Release to rubygems
46
+ command: |
47
+ gem build queue_classic_plus
48
+ gem push queue_classic_plus-*.gem
49
+
50
+ workflows:
51
+ version: 2
52
+ gem_release:
53
+ jobs:
54
+ - test:
55
+ context:
56
+ - DockerHub
57
+
58
+ - push_to_rubygems:
59
+ filters:
60
+ branches:
61
+ ignore:
62
+ - /.*/
63
+ tags:
64
+ only:
65
+ - /^v.*/
66
+ context:
67
+ - DockerHub
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "13:00"
8
+ open-pull-requests-limit: 10
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  *.gem
2
2
  *.rbc
3
3
  .bundle
4
+ .byebug_history
4
5
  .config
5
6
  .yardoc
6
7
  Gemfile.lock
@@ -9,6 +10,7 @@ _yardoc
9
10
  coverage
10
11
  doc/
11
12
  lib/bundler/man
13
+ log/
12
14
  pkg
13
15
  rdoc
14
16
  spec/reports
data/Gemfile CHANGED
@@ -12,7 +12,10 @@ group :development do
12
12
  end
13
13
 
14
14
  group :test do
15
+ gem 'byebug'
15
16
  gem 'rake'
16
17
  gem 'rspec'
17
18
  gem 'timecop'
19
+ gem 'newrelic_rpm'
20
+ gem 'ddtrace'
18
21
  end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # QueueClassicPlus
2
2
 
3
- [![Build Status](https://travis-ci.org/rainforestapp/queue_classic_plus.svg?branch=master)](https://travis-ci.org/rainforestapp/queue_classic_plus)
3
+ [![rainforestapp](https://circleci.com/gh/rainforestapp/queue_classic_plus.svg?branch=master)](https://app.circleci.com/pipelines/github/rainforestapp/queue_classic_plus?branch=master)
4
4
 
5
5
  [queue_classic](https://github.com/QueueClassic/queue_classic) is a simple Postgresql backed DB queue. However, it's a little too simple to use it as the main queueing system of a medium to large app. This was developed at [Rainforest QA](https://www.rainforestqa.com/).
6
6
 
@@ -130,7 +130,7 @@ If you want to log exceptions in your favorite exception tracker. You can config
130
130
 
131
131
  ```ruby
132
132
  QueueClassicPlus.exception_handler = -> (exception, job) do
133
- Raven.capture_exception(exception, extra: {job: job, env: ENV})
133
+ Sentry.capture_exception(exception, extra: { job: job, env: ENV })
134
134
  end
135
135
  ```
136
136
 
@@ -150,6 +150,12 @@ If you are using NewRelic and want to push performance data to it, you can add t
150
150
  require "queue_classic_plus/new_relic"
151
151
  ```
152
152
 
153
+ To instrument DataDog monitoring add this to your QC initializer:
154
+
155
+ ```ruby
156
+ require "queue_classic_plus/datadog"
157
+ ```
158
+
153
159
  ## Contributing
154
160
 
155
161
  1. Fork it ( https://github.com/[my-github-username]/queue_classic_plus/fork )
@@ -163,3 +169,7 @@ require "queue_classic_plus/new_relic"
163
169
  ```
164
170
  createdb queue_classic_plus_test
165
171
  ```
172
+
173
+ ## Releasing
174
+
175
+ Releasing is done in CircleCI via the `push_to_rubygems`, triggered by pushing a tagged commit. To do so, simply [create a new GitHub release](https://github.com/rainforestapp/queue_classic_plus/releases/new).
@@ -67,7 +67,7 @@ module QueueClassicPlus
67
67
  )
68
68
  AS x"
69
69
 
70
- result = QC.default_conn_adapter.execute(q, @queue, method, args.to_json)
70
+ result = QC.default_conn_adapter.execute(q, @queue, method, JSON.dump(serialized(args)))
71
71
  result['count'].to_i == 0
72
72
  else
73
73
  true
@@ -76,7 +76,7 @@ module QueueClassicPlus
76
76
 
77
77
  def self.enqueue(method, *args)
78
78
  if can_enqueue?(method, *args)
79
- queue.enqueue(method, *args)
79
+ queue.enqueue(method, *serialized(args))
80
80
  end
81
81
  end
82
82
 
@@ -86,11 +86,11 @@ module QueueClassicPlus
86
86
 
87
87
  def self.enqueue_perform_in(time, *args)
88
88
  raise "Can't enqueue in the future for locked jobs" if locked?
89
- queue.enqueue_in(time, "#{self.to_s}._perform", *args)
89
+ queue.enqueue_in(time, "#{self.to_s}._perform", *serialized(args))
90
90
  end
91
91
 
92
92
  def self.restart_in(time, remaining_retries, *args)
93
- queue.enqueue_retry_in(time, "#{self.to_s}._perform", remaining_retries, *args)
93
+ queue.enqueue_retry_in(time, "#{self.to_s}._perform", remaining_retries, *serialized(args))
94
94
  end
95
95
 
96
96
  def self.do(*args)
@@ -102,13 +102,13 @@ module QueueClassicPlus
102
102
  def self._perform(*args)
103
103
  Metrics.timing("qu_perform_time", source: librato_key) do
104
104
  if skip_transaction
105
- perform(*args)
105
+ perform(*deserialized(args))
106
106
  else
107
107
  transaction do
108
108
  # .to_i defaults to 0, which means no timeout in postgres
109
109
  timeout = ENV['POSTGRES_STATEMENT_TIMEOUT'].to_i * 1000
110
110
  execute "SET LOCAL statement_timeout = #{timeout}"
111
- perform(*args)
111
+ perform(*deserialized(args))
112
112
  end
113
113
  end
114
114
  end
@@ -119,7 +119,7 @@ module QueueClassicPlus
119
119
  end
120
120
 
121
121
  def self.transaction(options = {}, &block)
122
- if defined?(ActiveRecord)
122
+ if defined?(ActiveRecord) && ActiveRecord::Base.connected?
123
123
  # If ActiveRecord is loaded, we use it's own transaction mechanisn since
124
124
  # it has slightly different semanctics for rollback.
125
125
  ActiveRecord::Base.transaction(options, &block)
@@ -142,7 +142,26 @@ module QueueClassicPlus
142
142
  execute q
143
143
  end
144
144
 
145
+ protected
146
+
147
+ def self.serialized(args)
148
+ if defined?(Rails)
149
+ ActiveJob::Arguments.serialize(args)
150
+ else
151
+ args
152
+ end
153
+ end
154
+
155
+ def self.deserialized(args)
156
+ if defined?(Rails)
157
+ ActiveJob::Arguments.deserialize(args)
158
+ else
159
+ args
160
+ end
161
+ end
162
+
145
163
  private
164
+
146
165
  def self.execute(sql, *args)
147
166
  QC.default_conn_adapter.execute(sql, *args)
148
167
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QueueClassicDatadog
4
+ def _perform(*args)
5
+ Datadog.tracer.trace('qc.job', service_name: 'qc.job', resource: "#{name}#perform") do |_|
6
+ super
7
+ end
8
+ end
9
+
10
+ QueueClassicPlus::Base.singleton_class.send(:prepend, QueueClassicDatadog)
11
+ end
@@ -1,30 +1,29 @@
1
1
  require 'new_relic/agent/method_tracer'
2
2
 
3
- QueueClassicPlus::Base.class_eval do
4
- class << self
5
- include NewRelic::Agent::Instrumentation::ControllerInstrumentation
3
+ module QueueClassicNewRelic
4
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
6
5
 
7
- def new_relic_key
8
- "Custom/QueueClassicPlus/#{librato_key}"
9
- end
6
+ def new_relic_key
7
+ "Custom/QueueClassicPlus/#{librato_key}"
8
+ end
10
9
 
11
- def _perform_with_new_relic(*args)
12
- opts = {
13
- name: 'perform',
14
- class_name: self.name,
15
- category: 'OtherTransaction/QueueClassicPlus',
16
- }
10
+ def _perform(*args)
11
+ opts = {
12
+ name: 'perform',
13
+ class_name: self.name,
14
+ category: 'OtherTransaction/QueueClassicPlus',
15
+ }
17
16
 
18
- perform_action_with_newrelic_trace(opts) do
19
- if NewRelic::Agent.config[:'queue_classic_plus.capture_params']
20
- NewRelic::Agent.add_custom_parameters(job_arguments: args)
21
- end
22
- _perform_without_new_relic *args
17
+ perform_action_with_newrelic_trace(opts) do
18
+ if NewRelic::Agent.config[:'queue_classic_plus.capture_params']
19
+ NewRelic::Agent.add_custom_parameters(job_arguments: args)
23
20
  end
24
- end
25
21
 
26
- alias_method_chain :_perform, :new_relic
22
+ super
23
+ end
27
24
  end
25
+
26
+ QueueClassicPlus::Base.singleton_class.send(:prepend, QueueClassicNewRelic)
28
27
  end
29
28
 
30
29
  QueueClassicPlus::CustomWorker.class_eval do
@@ -3,7 +3,7 @@ module QC
3
3
 
4
4
  def enqueue_retry_in(seconds, method, remaining_retries, *args)
5
5
  QC.log_yield(:measure => 'queue.enqueue') do
6
- s = "INSERT INTO #{TABLE_NAME} (q_name, method, args, scheduled_at, remaining_retries)
6
+ s = "INSERT INTO #{QC.table_name} (q_name, method, args, scheduled_at, remaining_retries)
7
7
  VALUES ($1, $2, $3, now() + interval '#{seconds.to_i} seconds', $4)"
8
8
 
9
9
  conn_adapter.execute(s, name, method, JSON.dump(args), remaining_retries)
@@ -12,16 +12,36 @@ module QC
12
12
 
13
13
  def lock
14
14
  QC.log_yield(:measure => 'queue.lock') do
15
- s = "SELECT * FROM lock_head($1, $2)"
16
- if r = conn_adapter.execute(s, name, top_bound)
15
+ s = <<~SQL
16
+ WITH selected_job AS (
17
+ SELECT id
18
+ FROM queue_classic_jobs
19
+ WHERE
20
+ locked_at IS NULL AND
21
+ q_name = $1 AND
22
+ scheduled_at <= now()
23
+ LIMIT 1
24
+ FOR NO KEY UPDATE SKIP LOCKED
25
+ )
26
+ UPDATE queue_classic_jobs
27
+ SET
28
+ locked_at = now(),
29
+ locked_by = pg_backend_pid()
30
+ FROM selected_job
31
+ WHERE queue_classic_jobs.id = selected_job.id
32
+ RETURNING *
33
+ SQL
34
+
35
+ if r = conn_adapter.execute(s, name)
17
36
  {}.tap do |job|
18
37
  job[:id] = r["id"]
19
38
  job[:q_name] = r["q_name"]
20
39
  job[:method] = r["method"]
21
40
  job[:args] = JSON.parse(r["args"])
22
- job[:remaining_retries] = r["remaining_retries"]
41
+ job[:remaining_retries] = r["remaining_retries"]&.to_s
23
42
  if r["scheduled_at"]
24
- job[:scheduled_at] = Time.parse(r["scheduled_at"])
43
+ # ActiveSupport may cast time strings to Time
44
+ job[:scheduled_at] = r["scheduled_at"].kind_of?(Time) ? r["scheduled_at"] : Time.parse(r["scheduled_at"])
25
45
  ttl = Integer((Time.now - job[:scheduled_at]) * 1000)
26
46
  QC.measure("time-to-lock=#{ttl}ms source=#{name}")
27
47
  end
@@ -3,10 +3,15 @@ namespace :qc_plus do
3
3
  task :work => :environment do
4
4
  puts "Starting up worker for queue #{ENV['QUEUE']}"
5
5
 
6
- if defined? Raven
6
+ # ActiveRecord::RecordNotFound is ignored by Sentry by default,
7
+ # which shouldn't happen in background jobs.
8
+ if defined?(Sentry)
9
+ Sentry.init do |config|
10
+ config.excluded_exceptions = []
11
+ config.background_worker_threads = 0 if Gem::Version.new(Sentry::VERSION) >= Gem::Version.new('4.1.0')
12
+ end
13
+ elsif defined?(Raven)
7
14
  Raven.configure do |config|
8
- # ActiveRecord::RecordNotFound is ignored by Raven by default,
9
- # which shouldn't happen in background jobs.
10
15
  config.excluded_exceptions = []
11
16
  end
12
17
  end
@@ -1,3 +1,3 @@
1
1
  module QueueClassicPlus
2
- VERSION = "1.0.0"
2
+ VERSION = '4.0.0.alpha9'.freeze
3
3
  end
@@ -7,17 +7,8 @@ module QueueClassicPlus
7
7
  BACKOFF_WIDTH = 10
8
8
  FailedQueue = QC::Queue.new("failed_jobs")
9
9
 
10
- def enqueue_failed(job, e)
11
- sql = "INSERT INTO #{QC::TABLE_NAME} (q_name, method, args, last_error) VALUES ('failed_jobs', $1, $2, $3)"
12
- last_error = e.backtrace ? ([e.message] + e.backtrace ).join("\n") : e.message
13
- QC.default_conn_adapter.execute sql, job[:method], JSON.dump(job[:args]), last_error
14
-
15
- QueueClassicPlus.exception_handler.call(e, job)
16
- Metrics.increment("qc.errors", source: @q_name)
17
- end
18
-
19
10
  def handle_failure(job, e)
20
- QueueClassicPlus.logger.info "Handling exception #{e.message} for job #{job[:id]}"
11
+ QueueClassicPlus.logger.info "Handling exception #{e.class} - #{e.message} for job #{job[:id]}"
21
12
 
22
13
  force_retry = false
23
14
  if connection_error?(e)
@@ -33,52 +24,50 @@ module QueueClassicPlus
33
24
  force_retry = true
34
25
  end
35
26
 
36
- klass = job_klass(job)
27
+ @failed_job = job
28
+ @raw_args = job[:args]
29
+ @failed_job_args = failed_job_class ? failed_job_class.deserialized(@raw_args) : @raw_args
37
30
 
38
- if force_retry && !(klass.respond_to?(:disable_retries) && klass.disable_retries)
31
+ if force_retry && !(failed_job_class.respond_to?(:disable_retries) && failed_job_class.disable_retries)
39
32
  Metrics.increment("qc.force_retry", source: @q_name)
40
-
41
- retry_with_remaining(klass, job, e, 0)
33
+ retry_with_remaining(e)
42
34
  # The mailers doesn't have a retries_on?
43
- elsif klass && klass.respond_to?(:retries_on?) && klass.retries_on?(e)
35
+ elsif failed_job_class.respond_to?(:retries_on?) && failed_job_class.retries_on?(e)
44
36
  Metrics.increment("qc.retry", source: @q_name)
45
-
46
- backoff = (max_retries(klass) - remaining_retries(klass, job)) * BACKOFF_WIDTH
47
-
48
- retry_with_remaining(klass, job, e, backoff)
37
+ retry_with_remaining(e)
49
38
  else
50
- enqueue_failed(job, e)
39
+ enqueue_failed(e)
51
40
  end
52
41
 
53
- FailedQueue.delete(job[:id])
42
+ FailedQueue.delete(@failed_job[:id])
54
43
  end
55
44
 
56
45
  private
57
46
 
58
- def retry_with_remaining(klass, job, e, backoff)
59
- @remaining_retries = remaining_retries(klass, job)
60
-
61
- if @remaining_retries > 0
62
- klass.restart_in(backoff, @remaining_retries, *job[:args])
47
+ def retry_with_remaining(e)
48
+ if remaining_retries > 0
49
+ failed_job_class.restart_in(backoff, remaining_retries - 1, *@failed_job_args)
63
50
  else
64
- enqueue_failed(job, e)
51
+ enqueue_failed(e)
65
52
  end
66
53
  end
67
54
 
68
- def max_retries(klass)
69
- klass.respond_to?(:max_retries) ? klass.max_retries : 5
55
+ def max_retries
56
+ failed_job_class.respond_to?(:max_retries) ? failed_job_class.max_retries : 5
70
57
  end
71
58
 
72
- def remaining_retries(klass, job)
73
- @remaining_retries ? @remaining_retries - 1 : (job[:remaining_retries] || max_retries(klass)).to_i - 1
59
+ def remaining_retries
60
+ (@failed_job[:remaining_retries] || max_retries).to_i
74
61
  end
75
62
 
76
- def job_klass(job)
77
- begin
78
- Object.const_get(job[:method].split('.')[0])
79
- rescue NameError
80
- nil
81
- end
63
+ def failed_job_class
64
+ Object.const_get(@failed_job[:method].split('.')[0])
65
+ rescue NameError
66
+ nil
67
+ end
68
+
69
+ def backoff
70
+ (max_retries - (remaining_retries - 1)) * BACKOFF_WIDTH
82
71
  end
83
72
 
84
73
  def connection_error?(e)
@@ -86,5 +75,15 @@ module QueueClassicPlus
86
75
  (e.respond_to?(:original_exception) &&
87
76
  CONNECTION_ERRORS.any? { |klass| e.original_exception.kind_of? klass })
88
77
  end
78
+
79
+ def enqueue_failed(e)
80
+ sql = "INSERT INTO #{QC.table_name} (q_name, method, args, last_error) VALUES ('failed_jobs', $1, $2, $3)"
81
+ last_error = e.backtrace ? ([e.message] + e.backtrace ).join("\n") : e.message
82
+
83
+ QC.default_conn_adapter.execute sql, @failed_job[:method], JSON.dump(@raw_args), last_error
84
+
85
+ QueueClassicPlus.exception_handler.call(e, @failed_job)
86
+ Metrics.increment("qc.errors", source: @q_name)
87
+ end
89
88
  end
90
89
  end
@@ -14,13 +14,13 @@ module QueueClassicPlus
14
14
  require 'queue_classic_plus/railtie' if defined?(Rails)
15
15
 
16
16
  def self.migrate(c = QC::default_conn_adapter.connection)
17
- conn = QC::ConnAdapter.new(c)
17
+ conn = QC::ConnAdapter.new(connection: c)
18
18
  conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN last_error TEXT")
19
19
  conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN remaining_retries INTEGER")
20
20
  end
21
21
 
22
22
  def self.demigrate(c = QC::default_conn_adapter.connection)
23
- conn = QC::ConnAdapter.new(c)
23
+ conn = QC::ConnAdapter.new(connection: c)
24
24
  conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN last_error")
25
25
  conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN remaining_retries")
26
26
  end
@@ -18,7 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "queue_classic", ">= 3.1.0"
22
- spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_dependency "queue_classic", "4.0.0.pre.alpha1"
22
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ else
25
+ spec.add_development_dependency "bundler", "~> 2.0"
26
+ end
23
27
  spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "activerecord", "~> 6.0"
29
+ spec.add_development_dependency "activejob"
24
30
  end
data/spec/base_spec.rb CHANGED
@@ -12,7 +12,19 @@ describe QueueClassicPlus::Base do
12
12
  it "does not allow multiple enqueues" do
13
13
  subject.do
14
14
  subject.do
15
- subject.should have_queue_size_of(1)
15
+ expect(subject).to have_queue_size_of(1)
16
+ end
17
+
18
+ it "checks for an existing job using the same serializing as job enqueuing" do
19
+ # simulate a case where obj#to_json and JSON.dump(obj) do not match
20
+ require 'active_support/core_ext/date_time'
21
+ require 'active_support/json'
22
+ ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
23
+
24
+ date = DateTime.new(2020, 11, 3)
25
+ subject.do(date)
26
+ subject.do(date)
27
+ expect(subject).to have_queue_size_of(1)
16
28
  end
17
29
 
18
30
  it "does allow multiple enqueues if something got locked for too long" do
@@ -20,7 +32,7 @@ describe QueueClassicPlus::Base do
20
32
  one_day_ago = Time.now - 60*60*24
21
33
  execute "UPDATE queue_classic_jobs SET locked_at = '#{one_day_ago}' WHERE q_name = 'test'"
22
34
  subject.do
23
- subject.should have_queue_size_of(2)
35
+ expect(subject).to have_queue_size_of(2)
24
36
  end
25
37
  end
26
38
 
@@ -39,13 +51,15 @@ describe QueueClassicPlus::Base do
39
51
  end
40
52
 
41
53
  it "calls perform in a transaction" do
42
- QueueClassicPlus::Base.should_receive(:transaction).and_call_original
54
+ expect(QueueClassicPlus::Base).to receive(:transaction).and_call_original
55
+
43
56
  subject._perform
44
57
  end
45
58
 
46
59
  it "measures the time" do
47
- QueueClassicPlus::Metrics.should_receive(:timing).with("qu_perform_time", {source: "funky.name"}).and_call_original
48
- subject._perform
60
+ expect(QueueClassicPlus::Metrics).to receive(:timing).with("qu_perform_time", {source: "funky.name"}).and_call_original
61
+
62
+ subject._perform
49
63
  end
50
64
  end
51
65
 
@@ -61,7 +75,8 @@ describe QueueClassicPlus::Base do
61
75
  end
62
76
 
63
77
  it "calls perform outside of a transaction" do
64
- QueueClassicPlus::Base.should_not_receive(:transaction)
78
+ expect(QueueClassicPlus::Base).to_not receive(:transaction)
79
+
65
80
  subject._perform
66
81
  end
67
82
  end
@@ -79,15 +94,15 @@ describe QueueClassicPlus::Base do
79
94
  end
80
95
 
81
96
  it "retries on specified exception" do
82
- subject.retries_on?(SomeException.new).should be(true)
97
+ expect(subject.retries_on?(SomeException.new)).to be(true)
83
98
  end
84
99
 
85
100
  it "does not retry on unspecified exceptions" do
86
- subject.retries_on?(RuntimeError).should be(false)
101
+ expect(subject.retries_on?(RuntimeError)).to be(false)
87
102
  end
88
103
 
89
104
  it "sets max retries" do
90
- subject.max_retries.should == 5
105
+ expect(subject.max_retries).to eq(5)
91
106
  end
92
107
  end
93
108
 
@@ -104,16 +119,16 @@ describe QueueClassicPlus::Base do
104
119
  end
105
120
 
106
121
  it "retries on all specified exceptions" do
107
- subject.retries_on?(SomeException.new).should be(true)
108
- subject.retries_on?(SomeOtherException.new).should be(true)
122
+ expect(subject.retries_on?(SomeException.new)).to be(true)
123
+ expect(subject.retries_on?(SomeOtherException.new)).to be(true)
109
124
  end
110
125
 
111
126
  it "does not retry on unspecified exceptions" do
112
- subject.retries_on?(RuntimeError).should be(false)
127
+ expect(subject.retries_on?(RuntimeError)).to be(false)
113
128
  end
114
129
 
115
130
  it "sets max retries" do
116
- subject.max_retries.should == 5
131
+ expect(subject.max_retries).to eq(5)
117
132
  end
118
133
  end
119
134
 
@@ -133,22 +148,45 @@ describe QueueClassicPlus::Base do
133
148
  end
134
149
 
135
150
  it "retries on a subclass of a specified exception" do
136
- subject.retries_on?(ServiceReallyUnavailable.new).should be(true)
151
+ expect(subject.retries_on?(ServiceReallyUnavailable.new)).to be(true)
137
152
  end
138
153
 
139
154
  it "does not retry on unspecified exceptions" do
140
- subject.retries_on?(RuntimeError).should be(false)
155
+ expect(subject.retries_on?(RuntimeError)).to be(false)
141
156
  end
142
157
 
143
158
  it "sets max retries" do
144
- subject.max_retries.should == 5
159
+ expect(subject.max_retries).to eq(5)
160
+ end
161
+ end
162
+
163
+ context "with Rails defined", rails: true do
164
+ subject do
165
+ Class.new(QueueClassicPlus::Base) do
166
+ @queue = :test
167
+
168
+ def self.perform(foo, bar)
169
+ end
170
+ end
171
+ end
172
+
173
+ it "serializes parameters when enqueuing a job" do
174
+ expect(ActiveJob::Arguments).to receive(:serialize).with([42, true])
175
+
176
+ subject.do(42, true)
177
+ end
178
+
179
+ it "deserializes parameters when performing an enqueued job" do
180
+ expect(ActiveJob::Arguments).to receive(:deserialize).with([42, true]) { [42, true] }
181
+
182
+ subject._perform(42, true)
145
183
  end
146
184
  end
147
185
  end
148
186
 
149
187
  describe ".librato_key" do
150
188
  it "removes unsupported caracter from the classname" do
151
- Jobs::Tests::TestJob.librato_key.should == 'jobs.tests.test_job'
189
+ expect(Jobs::Tests::TestJob.librato_key).to eq('jobs.tests.test_job')
152
190
  end
153
191
  end
154
192
  end
@@ -0,0 +1,18 @@
1
+ describe 'requiring queue_classic_plus/new_relic' do
2
+ class FunkyName < QueueClassicPlus::Base
3
+ @queue = :test
4
+
5
+ def self.perform
6
+ end
7
+ end
8
+
9
+ subject { FunkyName._perform }
10
+
11
+ it 'adds Datadog profiling support' do
12
+ require 'queue_classic_plus/datadog'
13
+ expect(Datadog.tracer).to receive(:trace).with(
14
+ 'qc.job', service_name: 'qc.job', resource: 'FunkyName#perform'
15
+ )
16
+ subject
17
+ end
18
+ end
data/spec/helpers.rb CHANGED
@@ -4,6 +4,6 @@ module QcHelpers
4
4
  end
5
5
 
6
6
  def find_job(id)
7
- execute("SELECT * FROM #{QC::TABLE_NAME} WHERE id = $1", id)
7
+ execute("SELECT * FROM #{QC.table_name} WHERE id = $1", id)
8
8
  end
9
9
  end
@@ -0,0 +1,26 @@
1
+ describe 'requiring queue_classic_plus/new_relic' do
2
+ subject do
3
+ Class.new(QueueClassicPlus::Base) do
4
+ @queue = :test
5
+
6
+ def self.perform
7
+ end
8
+
9
+ def self.name
10
+ 'Funky::Name'
11
+ end
12
+ end
13
+ end
14
+
15
+ it 'adds NewRelic profiling support' do
16
+ expect(subject).to receive(:perform_action_with_newrelic_trace).once.with({
17
+ name: 'perform',
18
+ class_name: 'Funky::Name',
19
+ category: 'OtherTransaction/QueueClassicPlus',
20
+ })
21
+
22
+ subject._perform
23
+ require 'queue_classic_plus/new_relic'
24
+ subject._perform
25
+ end
26
+ end
@@ -1,12 +1,23 @@
1
1
  require 'spec_helper'
2
+ require 'active_record'
2
3
 
3
4
  describe QC do
4
-
5
5
  describe ".lock" do
6
+ context "with a connection from ActiveRecord that casts return types" do
7
+ before do
8
+ @old_conn_adapter = QC.default_conn_adapter
9
+ @activerecord_conn = ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
10
+ QC.default_conn_adapter = QC::ConnAdapter.new(
11
+ connection: ActiveRecord::Base.connection.raw_connection
12
+ )
13
+ end
6
14
 
7
- context "lock" do
15
+ after do
16
+ @activerecord_conn.disconnect!
17
+ QC.default_conn_adapter = @old_conn_adapter
18
+ end
8
19
 
9
- it "should lock the job with remaining_retries" do
20
+ it "locks the job with remaining_retries" do
10
21
  QC.enqueue_retry_in(1, "puts", 5, 2)
11
22
  sleep 1
12
23
  job = QC.lock
@@ -18,6 +29,15 @@ describe QC do
18
29
  end
19
30
  end
20
31
 
21
- end
32
+ it "locks the job with remaining_retries" do
33
+ QC.enqueue_retry_in(1, "puts", 5, 2)
34
+ sleep 1
35
+ job = QC.lock
22
36
 
37
+ expect(job[:q_name]).to eq("default")
38
+ expect(job[:method]).to eq("puts")
39
+ expect(job[:args][0]).to be(2)
40
+ expect(job[:remaining_retries]).to eq("5")
41
+ end
42
+ end
23
43
  end
data/spec/sample_jobs.rb CHANGED
@@ -14,7 +14,7 @@ module Jobs
14
14
  @queue = :low
15
15
  retry! on: SomeException, max: 5
16
16
 
17
- def self.perform should_raise
17
+ def self.perform(should_raise)
18
18
  raise SomeException if should_raise
19
19
  end
20
20
  end
@@ -27,7 +27,7 @@ module Jobs
27
27
 
28
28
  @queue = :low
29
29
 
30
- def self.perform should_raise
30
+ def self.perform(should_raise)
31
31
  raise Custom if should_raise
32
32
  end
33
33
  end
@@ -35,9 +35,9 @@ module Jobs
35
35
 
36
36
  class TestJob < QueueClassicPlus::Base
37
37
  @queue = :low
38
- retry! on: SomeException, max: 5
38
+ retry! on: SomeException, max: 1
39
39
 
40
- def self.perform should_raise
40
+ def self.perform(should_raise)
41
41
  raise SomeException if should_raise
42
42
  end
43
43
  end
data/spec/spec_helper.rb CHANGED
@@ -4,8 +4,11 @@ require 'timecop'
4
4
  require 'queue_classic_matchers'
5
5
  require_relative './sample_jobs'
6
6
  require_relative './helpers'
7
+ require 'byebug'
7
8
  require 'pry'
9
+ require 'ddtrace'
8
10
 
11
+ ENV["QC_RAILS_DATABASE"] ||= "false" # test on QC::ConnAdapter by default
9
12
  ENV["DATABASE_URL"] ||= "postgres:///queue_classic_plus_test"
10
13
 
11
14
  RSpec.configure do |config|
@@ -20,5 +23,14 @@ RSpec.configure do |config|
20
23
 
21
24
  config.before(:each) do
22
25
  QC.default_conn_adapter.execute "TRUNCATE queue_classic_jobs;"
26
+ # Reset the default (memoized) queue instance between specs
27
+ QC.default_queue = nil
28
+ end
29
+
30
+ config.before(:each, rails: true) do
31
+ require 'active_job'
32
+ require 'active_job/arguments'
33
+
34
+ stub_const('Rails', Struct.new(:logger).new(Logger.new(STDOUT)))
23
35
  end
24
36
  end
data/spec/worker_spec.rb CHANGED
@@ -9,22 +9,42 @@ describe QueueClassicPlus::CustomWorker do
9
9
 
10
10
  it "record failures in the failed queue" do
11
11
  queue.enqueue("Kerklfadsjflaksj", 1, 2, 3)
12
- failed_queue.count.should == 0
12
+ expect(failed_queue.count).to eq(0)
13
13
  worker.work
14
- failed_queue.count.should == 1
14
+ expect(failed_queue.count).to eq(1)
15
15
  job = failed_queue.lock
16
- job[:method].should == "Kerklfadsjflaksj"
17
- job[:args].should == [1, 2, 3]
16
+ expect(job[:method]).to eq("Kerklfadsjflaksj")
17
+ expect(job[:args]).to eq([1, 2, 3])
18
18
  full_job = find_job(job[:id])
19
19
 
20
- full_job['last_error'].should_not be_nil
20
+ expect(full_job['last_error']).to_not be_nil
21
21
  end
22
22
 
23
23
  it "records normal errors" do
24
24
  queue.enqueue("Jobs::Tests::TestJobNoRetry.perform", true)
25
- failed_queue.count.should == 0
25
+ expect(failed_queue.count).to eq(0)
26
26
  worker.work
27
- failed_queue.count.should == 1
27
+ expect(failed_queue.count).to eq(1)
28
+ end
29
+
30
+ context 'when Rails is defined', rails: true do
31
+ let(:job_type) { Jobs::Tests::TestJobNoRetry }
32
+ let(:queue) { job_type.queue }
33
+
34
+ it 'properly serializes arguments for jobs in the failed queue' do
35
+ job_type.enqueue_perform(:raise)
36
+ expect(failed_queue.count).to eq(0)
37
+ worker.work
38
+
39
+ expect(failed_queue.count).to eq(1)
40
+ job = QueueClassicMatchers::QueueClassicRspec.find_by_args(
41
+ 'failed_jobs',
42
+ 'Jobs::Tests::TestJobNoRetry._perform',
43
+ [:raise]).first
44
+
45
+ expect(job).to_not be_nil
46
+ expect(job['last_error']).to_not be_nil
47
+ end
28
48
  end
29
49
  end
30
50
 
@@ -42,28 +62,62 @@ describe QueueClassicPlus::CustomWorker do
42
62
  job_type.enqueue_perform(true)
43
63
  end.to change_queue_size_of(job_type).by(1)
44
64
 
45
- Jobs::Tests::LockedTestJob.should have_queue_size_of(1)
46
- failed_queue.count.should == 0
47
- QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].should be_nil
65
+ expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
66
+ expect(failed_queue.count).to eq(0)
67
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
48
68
 
49
- QueueClassicPlus::Metrics.should_receive(:increment).with('qc.retry', source: nil )
69
+ expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil )
50
70
 
51
71
  Timecop.freeze do
52
72
  worker.work
53
73
 
54
- failed_queue.count.should == 0 # not enqueued on Failed
55
- QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].should eq "4"
56
- Jobs::Tests::LockedTestJob.should have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
74
+ expect(failed_queue.count).to eq(0) # not enqueued on Failed
75
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to eq "4"
76
+ expect(Jobs::Tests::LockedTestJob).to have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
57
77
  end
58
78
 
59
79
  Timecop.freeze(Time.now + (described_class::BACKOFF_WIDTH * 2)) do
60
80
  # the job should be re-enqueued with a decremented retry count
61
81
  jobs = QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true])
62
- jobs.size.should == 1
82
+ expect(jobs.size).to eq(1)
63
83
  job = jobs.first
64
- job['remaining_retries'].to_i.should == job_type.max_retries - 1
65
- job['locked_by'].should be_nil
66
- job['locked_at'].should be_nil
84
+ expect(job['remaining_retries'].to_i).to eq(job_type.max_retries - 1)
85
+ expect(job['locked_by']).to be_nil
86
+ expect(job['locked_at']).to be_nil
87
+ end
88
+ end
89
+
90
+ context 'when Rails is defined', rails: true do
91
+ it 'retries' do
92
+ expect do
93
+ job_type.enqueue_perform(:foo)
94
+ end.to change_queue_size_of(job_type).by(1)
95
+
96
+ expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
97
+ expect(failed_queue.count).to eq(0)
98
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo]).first['remaining_retries']).to be_nil
99
+
100
+ expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil).twice
101
+
102
+ Timecop.freeze do
103
+ worker.work
104
+
105
+ expect(failed_queue.count).to eq(0) # not enqueued on Failed
106
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo]).first['remaining_retries']).to eq "4"
107
+ expect(Jobs::Tests::LockedTestJob).to have_scheduled(:foo).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
108
+ end
109
+
110
+ Timecop.freeze(Time.now + (described_class::BACKOFF_WIDTH * 2)) do
111
+ # the job should be re-enqueued with a decremented retry count
112
+ jobs = QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo])
113
+ expect(jobs.size).to eq(1)
114
+ job = jobs.first
115
+ expect(job['remaining_retries'].to_i).to eq(job_type.max_retries - 1)
116
+ expect(job['locked_by']).to be_nil
117
+ expect(job['locked_at']).to be_nil
118
+ end
119
+
120
+ worker.work
67
121
  end
68
122
  end
69
123
 
@@ -71,11 +125,11 @@ describe QueueClassicPlus::CustomWorker do
71
125
  before { Jobs::Tests::ConnectionReapedTestJob.enqueue_perform }
72
126
 
73
127
  it 'retries' do
74
- QueueClassicPlus::Metrics.should_receive(:increment).with('qc.force_retry', source: nil )
128
+ expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.force_retry', source: nil )
75
129
  Timecop.freeze do
76
130
  worker.work
77
131
  expect(failed_queue.count).to eq 0
78
- QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::ConnectionReapedTestJob._perform', []).first['remaining_retries'].should eq "4"
132
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::ConnectionReapedTestJob._perform', []).first['remaining_retries']).to eq "4"
79
133
  end
80
134
  end
81
135
 
@@ -88,6 +142,19 @@ describe QueueClassicPlus::CustomWorker do
88
142
  end
89
143
  end
90
144
 
145
+ context 'with a custom exception having max: 1 retry' do
146
+ before { Jobs::Tests::TestJob.enqueue_perform(true) }
147
+
148
+ it 'retries' do
149
+ expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil )
150
+ Timecop.freeze do
151
+ worker.work
152
+ expect(failed_queue.count).to eq 0
153
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::TestJob._perform', [true]).first['remaining_retries']).to eq "0"
154
+ end
155
+ end
156
+ end
157
+
91
158
  context 'with non-connection based PG jobs' do
92
159
  before { Jobs::Tests::UniqueViolationTestJob.enqueue_perform }
93
160
 
@@ -113,16 +180,16 @@ describe QueueClassicPlus::CustomWorker do
113
180
  job_type.enqueue_perform(true)
114
181
  end.to change_queue_size_of(job_type).by(1)
115
182
 
116
- Jobs::Tests::LockedTestJob.should have_queue_size_of(1)
117
- failed_queue.count.should == 0
118
- QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].should be_nil
183
+ expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
184
+ expect(failed_queue.count).to eq(0)
185
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
119
186
 
120
187
  Timecop.freeze do
121
188
  worker.work
122
189
 
123
- QueueClassicMatchers::QueueClassicRspec.find_by_args('failed_jobs', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].should be_nil
124
- failed_queue.count.should == 1 # not enqueued on Failed
125
- Jobs::Tests::LockedTestJob.should_not have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
190
+ expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('failed_jobs', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
191
+ expect(failed_queue.count).to eq(1) # not enqueued on Failed
192
+ expect(Jobs::Tests::LockedTestJob).to_not have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
126
193
  end
127
194
  end
128
195
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queue_classic_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 4.0.0.alpha9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Mathieu
@@ -10,36 +10,36 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-04-18 00:00:00.000000000 Z
13
+ date: 2021-10-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: queue_classic
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - ">="
19
+ - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 3.1.0
21
+ version: 4.0.0.pre.alpha1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - ">="
26
+ - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 3.1.0
28
+ version: 4.0.0.pre.alpha1
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: bundler
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '1.6'
35
+ version: '2.0'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '1.6'
42
+ version: '2.0'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: rake
45
45
  requirement: !ruby/object:Gem::Requirement
@@ -54,6 +54,34 @@ dependencies:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: activerecord
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '6.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '6.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: activejob
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
57
85
  description: ''
58
86
  email:
59
87
  - simon.math@gmail.com
@@ -63,10 +91,11 @@ executables: []
63
91
  extensions: []
64
92
  extra_rdoc_files: []
65
93
  files:
94
+ - ".circleci/config.yml"
95
+ - ".github/dependabot.yml"
66
96
  - ".gitignore"
67
97
  - ".rspec"
68
98
  - ".rvmrc"
69
- - ".travis.yml"
70
99
  - Gemfile
71
100
  - Guardfile
72
101
  - LICENSE.txt
@@ -74,6 +103,7 @@ files:
74
103
  - Rakefile
75
104
  - lib/queue_classic_plus.rb
76
105
  - lib/queue_classic_plus/base.rb
106
+ - lib/queue_classic_plus/datadog.rb
77
107
  - lib/queue_classic_plus/inflector.rb
78
108
  - lib/queue_classic_plus/inheritable_attr.rb
79
109
  - lib/queue_classic_plus/metrics.rb
@@ -89,8 +119,10 @@ files:
89
119
  - lib/rails/generators/qc_plus_job/templates/job_spec.rb.erb
90
120
  - queue_classic_plus.gemspec
91
121
  - spec/base_spec.rb
122
+ - spec/datadog_spec.rb
92
123
  - spec/helpers.rb
93
124
  - spec/inflector_spec.rb
125
+ - spec/new_relic_spec.rb
94
126
  - spec/queue_classic/queue_spec.rb
95
127
  - spec/sample_jobs.rb
96
128
  - spec/spec_helper.rb
@@ -111,19 +143,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
143
  version: '0'
112
144
  required_rubygems_version: !ruby/object:Gem::Requirement
113
145
  requirements:
114
- - - ">="
146
+ - - ">"
115
147
  - !ruby/object:Gem::Version
116
- version: '0'
148
+ version: 1.3.1
117
149
  requirements: []
118
- rubyforge_project:
119
- rubygems_version: 2.6.13
150
+ rubygems_version: 3.1.6
120
151
  signing_key:
121
152
  specification_version: 4
122
153
  summary: Useful extras for Queue Classic
123
154
  test_files:
124
155
  - spec/base_spec.rb
156
+ - spec/datadog_spec.rb
125
157
  - spec/helpers.rb
126
158
  - spec/inflector_spec.rb
159
+ - spec/new_relic_spec.rb
127
160
  - spec/queue_classic/queue_spec.rb
128
161
  - spec/sample_jobs.rb
129
162
  - spec/spec_helper.rb
data/.travis.yml DELETED
@@ -1,12 +0,0 @@
1
- language: ruby
2
- before_install:
3
- - gem update bundler
4
- install: bundle install --without development
5
- before_script:
6
- - psql -c 'create database queue_classic_plus_test;' -U postgres
7
-
8
- rvm:
9
- - 2.0.0
10
- - 2.1.5
11
- - 2.2.0
12
- - 2.3.1