delayed-threaded 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 32e63adc06acd90d954baed1e872c113239941d9
4
+ data.tar.gz: bb4d43d6376e85f84efe0945c14149143ca59140
5
+ SHA512:
6
+ metadata.gz: 46418908010e3463ca50d1c6a17d069670869b40cb220f2c3729b6924b89b99006ddf07a1e0259f7b2429b5d01e690be9bf8bd9692494c6c39cb3848f9a92403
7
+ data.tar.gz: f50279911f6e6024570079a3f6715a72136d9853e53949ebad1f62591422656881b0eee64fd1db29ab505d2bdfb0d68d46c55f77e9393b3ddeac4bfc9a769db2
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /tmp/
8
+ /test/db/*.db
9
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ sudo: false
2
+ language: ruby
3
+ jdk:
4
+ - oraclejdk8
5
+ rvm:
6
+ - jruby-1.7.26
7
+ - jruby-9.1.16.0
8
+ before_install: gem install bundler -v 1.16.1
9
+ env:
10
+ - delayed_job="~>4.1.5" activerecord="~>4.2"
11
+ - delayed_job="~>4.0.6" activerecord="~>4.2"
12
+ - delayed_job="~>3.0" # activerecord="~>3.2"
13
+ - delayed_job="~>4.1" activerecord="~>4.2.6"
14
+ matrix:
15
+ allow_failures:
16
+ #- rvm: jruby-head
17
+ include:
18
+ - rvm: jruby-9.1.17.0
19
+ env: delayed_job="~>4.1.5" activerecord="~>5.0.5"
20
+ - rvm: jruby-9.1.17.0
21
+ env: delayed_job="~>4.1.5" activerecord="~>5.1.6"
data/Gemfile ADDED
@@ -0,0 +1,36 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ if ENV['delayed_job']
6
+ if ENV['delayed_job'] == 'master'
7
+ gem 'delayed_job', :git => 'git://github.com/collectiveidea/delayed_job.git'
8
+ gem 'delayed_job_active_record', :require => nil
9
+ else
10
+ gem 'delayed_job', version = ENV['delayed_job']
11
+ unless version =~ /~?\s?2\.\d/ # delayed_job_active_record only for DJ >= 3.0
12
+ gem 'delayed_job_active_record', :require => nil
13
+ end
14
+ if version =~ /~?\s?4\.[1]/ # add_dependency "delayed_job", ">= 4.1"
15
+ gem 'delayed_cron_job', :require => nil
16
+ end
17
+ end
18
+ else
19
+ gem 'delayed_job'
20
+ gem 'delayed_job_active_record', :require => nil
21
+ gem 'delayed_cron_job', :require => nil
22
+ end
23
+
24
+ if ENV['activerecord']
25
+ gem 'activerecord', version = ENV['activerecord'], :require => nil
26
+ if version =~ /~?\s?4\.[12]/
27
+ gem 'activerecord-jdbc-adapter', '~> 1.3.20', :require => nil, :platform => :jruby
28
+ else
29
+ gem 'activerecord-jdbc-adapter', :require => nil, :platform => :jruby
30
+ end
31
+ else
32
+ gem 'activerecord', :require => nil # for tests
33
+ gem 'activerecord-jdbc-adapter', :require => nil, :platform => :jruby
34
+ end
35
+
36
+ gem 'jdbc-sqlite3', '~> 3.20.1', :platform => :jruby
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 Karol Bucek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
+ NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Delayed::Threaded
2
+
3
+ Allows to start [DJ][0] in the same process using `Thread.new { ... }`
4
+
5
+ Extracted from [JRuby-Rack-Worker][1]: an effort to run Ruby workers in threads
6
+ with Java (JRuby-Rack) deployments (previously known as `Delayed::JRubyWorker`).
7
+
8
+ **NOTE: JRuby only, for now (PRs welcome)!**
9
+
10
+ ## Installation
11
+
12
+ ```ruby
13
+ gem 'delayed-threaded'
14
+ ```
15
+
16
+ and `bundle` or install it yourself as `gem install delayed-threaded`.
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ def start_worker
22
+ options = { :quiet => true }
23
+ options[:queues] = (ENV['QUEUES'] || ENV['QUEUE'] || '').split(',')
24
+ options[:min_priority] = ENV['MIN_PRIORITY']
25
+ options[:max_priority] = ENV['MAX_PRIORITY']
26
+ # beyond `rake delayed:work` compatibility :
27
+ if read_ahead = ENV['READ_AHEAD'] # DEFAULT_READ_AHEAD = 5
28
+ options[:read_ahead] = read_ahead.to_i
29
+ end
30
+ if sleep_delay = ENV['SLEEP_DELAY'] # DEFAULT_SLEEP_DELAY = 5
31
+ options[:sleep_delay] = sleep_delay.to_f
32
+ end
33
+
34
+ worker = Delayed::Threaded::Worker.new(options)
35
+ worker.start
36
+ rescue Exception => e
37
+ msg = "FATAL #{e.inspect}"
38
+ if backtrace = e.backtrace
39
+ msg << ":\n #{backtrace.join("\n ")}"
40
+ end
41
+ STDERR.puts(msg)
42
+ end
43
+
44
+ Thread.new { start_worker }
45
+ ```
46
+
47
+ ## Development
48
+
49
+ After checking out the repo, run `bin/setup` to install dependencies.
50
+ Then, run `rake test` to run the tests. You can also run `bin/console` for an
51
+ interactive prompt that will allow you to experiment.
52
+
53
+ To install this gem onto your local machine, run `bundle exec rake install`.
54
+
55
+ ## Copyright
56
+
57
+ Copyright (c) 2018 [Karol Bucek](http://kares.org).
58
+ See LICENSE (http://en.wikipedia.org/wiki/MIT_License) for details.
59
+
60
+ [0]: https://github.com/collectiveidea/delayed_job
61
+ [1]: https://github.com/kares/jruby-rack-worker
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "delayed/threaded"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "delayed-threaded"
5
+ spec.authors = ["Karol Bucek"]
6
+ spec.email = ["self@kares.org"]
7
+ spec.licenses = ['MIT']
8
+
9
+ path = File.expand_path('lib/delayed/threaded/version.rb', File.dirname(__FILE__))
10
+ spec.version = File.read(path).match( /.*VERSION\s*=\s*['"](.*)['"]/m )[1]
11
+
12
+ spec.summary = %q{Making Delayed::Job a polite (thread-safe) in process citizen.}
13
+ spec.description = "Allows to start DJ in the same process using Thread.new { ... } "
14
+ spec.homepage = "https://github.com/kares/delayed-threaded"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|temp)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency "delayed_job", ">= 3.0", "< 4.2"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.16"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "test-unit", "~> 2.5.3"
28
+ spec.add_development_dependency "test-unit-context"
29
+ spec.add_development_dependency "mocha"
30
+ end
@@ -0,0 +1,4 @@
1
+ module Delayed
2
+ # @deprecated
3
+ JRubyWorker = Threaded::Worker
4
+ end
@@ -0,0 +1,33 @@
1
+ module Delayed
2
+ module Threaded
3
+ module SleepCalculator
4
+
5
+ def sleep(time)
6
+ Kernel.sleep calc_sleep_time(time)
7
+ end
8
+
9
+ private
10
+
11
+ @@last = java.util.concurrent.atomic.AtomicLong.new
12
+
13
+ def calc_sleep_time(time)
14
+ count = thread_count rescue nil
15
+ return time if ! count || count <= 1 || time <= 0
16
+
17
+ last = @@last.get_and_set now = java.lang.System.current_time_millis
18
+ return time if ( now - last ) > time * 1000
19
+
20
+ # time / count.to_f - optimal pause time between threads
21
+ diff = ( time / count.to_f ) - ( now - last ) * 0.001
22
+ # converge to ~ pauses between worker threads (might add up to 10% to sleep time)
23
+ diff > 0 ? ( time + diff / 5.0 ) : time
24
+ # (now - last) * 0.001 < opt_pause ? ( time + time / 11.1 ) : time
25
+ end
26
+
27
+ def thread_count
28
+ $worker_manager ? $worker_manager.thread_count : false
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'thread'
2
+
3
+ module Delayed
4
+ module Threaded
5
+ module SyncLifecycle
6
+
7
+ def self.extended(base)
8
+ klass = base.singleton_class
9
+ klass.send :alias_method, :setup_lifecycle_impl, :setup_lifecycle
10
+ klass.send :alias_method, :setup_lifecycle, :setup_lifecycle_sync
11
+ klass.send :private, :setup_lifecycle_impl
12
+ end
13
+
14
+ def setup_lifecycle_sync
15
+ # not really necessary, but a plugin might expect setup to run exactly once
16
+ (@lifecycle ||= nil) || synchronize do
17
+ unless @lifecycle
18
+ setup_lifecycle_impl
19
+ #@lifecycle = Delayed::Lifecycle.new
20
+ #lifecycle = @lifecycle
21
+ end
22
+ @lifecycle # make sure returns lifecycle (for DJ < 4.1 compat layer)
23
+ end
24
+ end
25
+
26
+ MUTEX = Mutex.new
27
+ private_constant :MUTEX if respond_to?(:private_constant)
28
+
29
+ def synchronize(&block); MUTEX.synchronize(&block) end
30
+ private :synchronize
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Delayed
2
+ module Threaded
3
+ VERSION = '0.16.0'
4
+ end
5
+ end
@@ -0,0 +1,172 @@
1
+ require 'java'
2
+ require 'delayed_job' unless defined?(Delayed::Worker)
3
+ require 'socket' # due Socket.gethostname in Delayed::Worker#name
4
+
5
+ require 'delayed/threaded/sleep_calculator'
6
+ require 'delayed/threaded/sync_lifecycle'
7
+
8
+ module Delayed::Threaded
9
+ # Threaded DJ worker implementation.
10
+ # - inspired by Delayed::Command
11
+ # - no daemons dependency + thread-safe
12
+ class Worker < Delayed::Worker
13
+
14
+ include SleepCalculator
15
+
16
+ # @override to return the same as Delayed::Worker.lifecycle (uses class instance state)
17
+ def self.lifecycle; Delayed::Worker.lifecycle end
18
+
19
+ # @override since `initialize` (DJ 4.1) does: `self.class.setup_lifecycle`
20
+ def self.setup_lifecycle; Delayed::Worker.setup_lifecycle end
21
+
22
+ unless defined? Delayed::Worker.setup_lifecycle
23
+ # adapt DJ 3.0/4.0 :
24
+ # def self.lifecycle
25
+ # @lifecycle ||= Delayed::Lifecycle.new
26
+ # end
27
+ def superclass.lifecycle
28
+ @lifecycle ||= setup_lifecycle
29
+ end
30
+ def superclass.setup_lifecycle
31
+ @lifecycle = Delayed::Lifecycle.new
32
+ end
33
+ end
34
+
35
+ # @patch make sure concurrent worker threads do not cause multiple initializations
36
+ Delayed::Worker.extend SyncLifecycle if Delayed.const_defined? :Lifecycle
37
+
38
+ THREAD_LOCAL_ACCESSORS = [
39
+ :min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete
40
+ ]
41
+ private_constant :THREAD_LOCAL_ACCESSORS if respond_to?(:private_constant)
42
+ # due Delayed::Worker#initialize(options = {}) :
43
+ #
44
+ # [:min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete].each do |option|
45
+ # self.class.send("#{option}=", options[option]) if options.key?(option)
46
+ # end
47
+
48
+ class Config
49
+ attr_accessor *THREAD_LOCAL_ACCESSORS
50
+ def key?(name); ! instance_variable_get(:"@#{name}").nil? end
51
+ end
52
+
53
+ THREAD_LOCAL_ACCESSORS.each do |name|
54
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
+ def self.#{name}=(val)
56
+ (Thread.current[:delayed_threaded_worker_config] ||= Config.new).#{name} = val
57
+ end
58
+ def self.#{name}
59
+ if (config = Thread.current[:delayed_threaded_worker_config]) && config.key?(:#{name})
60
+ config.#{name}
61
+ else
62
+ superclass.#{name}
63
+ end
64
+ end
65
+ EOS
66
+ end
67
+ # e.g. :
68
+ #
69
+ # def self.min_priority=(value)
70
+ # (Thread.current[:delayed_jruby_worker_config] ||= Config.new).min_priority = value
71
+ # end
72
+ #
73
+ # def self.min_priority
74
+ # if (config = Thread.current[:delayed_jruby_worker_config]) && config.key?(:min_priority)
75
+ # config.min_priority
76
+ # else
77
+ # Worker.min_priority
78
+ # end
79
+ # end
80
+
81
+ def name
82
+ if (@name ||= nil).nil?
83
+ # super - [prefix]host:hostname pid:process_pid
84
+ begin
85
+ @name = "#{super} thread:#{thread_id}".freeze
86
+ rescue
87
+ @name = "#{@name_prefix}thread:#{thread_id}".freeze
88
+ end
89
+ end
90
+ @name
91
+ end
92
+
93
+ def to_s; name; end
94
+
95
+ def thread_id
96
+ # NOTE: JRuby might set a bit long name for Thread.new { ... } code e.g.
97
+ # RubyThread-1: /home/[...]/src/test/ruby/delayed/jruby_worker_test.rb:163
98
+ if name = java.lang.Thread.currentThread.getName
99
+ if name.size > 100 && match = name.match(/(.*?)\:\s.*?[\/\\]+/)
100
+ match[1]
101
+ else
102
+ name
103
+ end
104
+ end
105
+ end
106
+
107
+ def exit!
108
+ return if @exit # #stop?
109
+ say "Stoping job worker"
110
+ @exit = true # #stop
111
+ if Delayed.const_defined?(:Job) && Delayed::Job.respond_to?(:clear_locks!)
112
+ Delayed::Job.clear_locks!(name)
113
+ end
114
+ end
115
+
116
+ unless defined? Delayed::Lifecycle # DJ 2.x (< 3.0)
117
+ require 'benchmark'
118
+ # in case DJ 2.1 loads AS 3.x we're need `[1,2].sum` :
119
+ require 'active_support/core_ext/enumerable' rescue nil
120
+
121
+ def start
122
+ say "Starting job worker"
123
+ trap
124
+
125
+ loop do
126
+ result = nil
127
+
128
+ realtime = Benchmark.realtime do
129
+ result = work_off
130
+ end
131
+
132
+ count = result.sum
133
+
134
+ break if @exit
135
+
136
+ if count.zero?
137
+ sleep(self.class.sleep_delay)
138
+ else
139
+ say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last]
140
+ end
141
+
142
+ break if @exit
143
+ end
144
+ end
145
+
146
+ def stop?; !!@exit; end
147
+ def stop; @exit = true; end
148
+
149
+ end
150
+
151
+ protected
152
+
153
+ def trap(name = nil)
154
+ # catch invocations from #start traps TERM and INT
155
+ at_exit { exit! } if ! name || name.to_s == 'TERM'
156
+ end
157
+
158
+ end
159
+
160
+ end
161
+
162
+ Dir.chdir( Rails.root ) if defined?(Rails.root) && Dir.getwd.to_s != Rails.root.to_s
163
+
164
+ if ! Delayed::Worker.backend && ! Delayed.const_defined?(:Lifecycle)
165
+ Delayed::Worker.guess_backend # deprecated on DJ since 3.0
166
+ end
167
+
168
+ # NOTE: no explicit logger configuration - DJ logger defaults to Rails.logger
169
+ # if this is not desired - e.g. one wants script/delayed_job's logger behavior
170
+ # it's more correct to configure in an initializer rather then forcing the use
171
+ # of delayed_job.log (like Delayed::Command does) ...
172
+ # Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
@@ -0,0 +1,5 @@
1
+ require 'delayed/threaded/version'
2
+
3
+ require 'delayed/threaded/worker'
4
+
5
+ require 'delayed/threaded/compat'
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delayed-threaded
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.16.0
5
+ platform: ruby
6
+ authors:
7
+ - Karol Bucek
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-05-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '3.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '4.2'
22
+ name: delayed_job
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.2'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '1.16'
39
+ name: bundler
40
+ prerelease: false
41
+ type: :development
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.16'
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '10.0'
53
+ name: rake
54
+ prerelease: false
55
+ type: :development
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: 2.5.3
67
+ name: test-unit
68
+ prerelease: false
69
+ type: :development
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 2.5.3
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ name: test-unit-context
82
+ prerelease: false
83
+ type: :development
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ name: mocha
96
+ prerelease: false
97
+ type: :development
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: 'Allows to start DJ in the same process using Thread.new { ... } '
104
+ email:
105
+ - self@kares.org
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".gitignore"
111
+ - ".travis.yml"
112
+ - Gemfile
113
+ - LICENSE
114
+ - README.md
115
+ - Rakefile
116
+ - bin/console
117
+ - bin/setup
118
+ - delayed-threaded.gemspec
119
+ - lib/delayed/threaded.rb
120
+ - lib/delayed/threaded/compat.rb
121
+ - lib/delayed/threaded/sleep_calculator.rb
122
+ - lib/delayed/threaded/sync_lifecycle.rb
123
+ - lib/delayed/threaded/version.rb
124
+ - lib/delayed/threaded/worker.rb
125
+ homepage: https://github.com/kares/delayed-threaded
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.6.14.1
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Making Delayed::Job a polite (thread-safe) in process citizen.
149
+ test_files: []