threaded 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: db08fff84d920a28793a4f9d73340879fdcbb03c
4
+ data.tar.gz: 884731a111fbf0c32bcbc3b5ee4e5f105e290566
5
+ SHA512:
6
+ metadata.gz: bdb4e659e9eab461a2eaf0939b6951f269f42d41d812da326cb4de43336d6633afeb8b6287a82bcf989a31bc4d98c29fea35073bb2ff8da36ba486be9c68435d
7
+ data.tar.gz: 86e6fc98fbd9245ae59642ffd1832ffdba70abdcbeeb128cf25c7a6b153ec02a296bdf143cf8fbe55cc2e376ee3e7c0f62cd3fe794b4b1d6f5009b48c0543132
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - ruby-head
7
+ - jruby-19mode
8
+ - rbx-19mode
9
+
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: ruby-head
13
+ - rvm: rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ threaded (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ metaclass (0.0.2)
10
+ mocha (1.0.0)
11
+ metaclass (~> 0.0.1)
12
+ rake (10.1.1)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ mocha
19
+ rake
20
+ threaded!
@@ -0,0 +1,108 @@
1
+ ## Threaded
2
+
3
+ [![Build Status](https://travis-ci.org/schneems/threaded.png?branch=master)](https://travis-ci.org/schneems/threaded)
4
+
5
+ Simpler than actors, easier than threads. Get threaded!
6
+
7
+ ## Why
8
+
9
+ Projects like [Resque](https://github.com/resque/resque), [delayed job](https://github.com/collectiveidea/delayed_job), [queue classic](https://github.com/ryandotsmith/queue_classic), and [sidekiq](https://github.com/mperham/sidekiq) are great. They use data stores like postgres and redis to store information to be processed later. If you're prototyping a system or don't have access to a data store, you might still want to push off some work to a background process. If that's the case an in-memory threaded queue might be a good fit.
10
+
11
+ ## Install
12
+
13
+ In your `Gemfile`:
14
+
15
+ ```ruby
16
+ gem 'threaded'
17
+ ```
18
+
19
+ Then run `$ bundle install`
20
+
21
+ ## Use it
22
+
23
+ Define your task to be processed:
24
+
25
+ ```ruby
26
+ class Archive
27
+ def self.call(repo_id, branch = 'master')
28
+ repo = Repository.find(repo_id)
29
+ repo.create_archive(branch)
30
+ end
31
+ end
32
+ ```
33
+
34
+ It can be any object that responds to `call` but we recommend a class or module which makes switching to a durable queue later easier.
35
+
36
+ Then to enqueue a task to be run in the background use `Threaded.enqueue`:
37
+
38
+ ```ruby
39
+ repo = Repo.last
40
+ Threaded.enqueue(Archive, repo.id, 'staging')
41
+ ```
42
+
43
+ The first argument is a class that defines the task to be processed and the rest of the arguments are passed to the task when it is run.
44
+
45
+
46
+
47
+ # Configure
48
+
49
+ The default number of worker threads is 16, you can configure that when you start your queue:
50
+
51
+ ```ruby
52
+ Threaded.config do |config|
53
+ config.size = 5
54
+ end
55
+ ```
56
+
57
+ By default jobs have a timeout value of 60 seconds. Since this is an in-memory queue (goes away when your process terminates) it is in your best interests to keep jobs small and quick, and not overload the queue. You can configure a different timeout on start:
58
+
59
+ ```ruby
60
+ Threaded.config do |config|
61
+ config.timeout = 90 # timeout is in seconds
62
+ end
63
+ ```
64
+
65
+ Want a different logger? Specify a different Logger:
66
+
67
+ ```ruby
68
+ Threaded.config do |config|
69
+ config.logger = Logger.new(STDOUT)
70
+ end
71
+ ```
72
+
73
+ As soon as you call `enqueue` a new thread will be started, if you wish to explicitly start all threads you can call `Threaded.start`. You can also inline your config if you want when you start the queue:
74
+
75
+ ```ruby
76
+ Threaded.start(size: 5, timeout: 90, logger: Logger.new(STDOUT))
77
+ ```
78
+
79
+ For testing or guaranteed code execution use the `inline` option:
80
+
81
+ ```ruby
82
+ Threaded.inline = true
83
+ ```
84
+
85
+ This option bypasses the queue and executes code as it comes.
86
+
87
+ ## Thread Considerations
88
+
89
+ This worker operates in the same process as your app, that means if your app is CPU bound, it will not be very useful. This worker uses threads which means that to be useful your app needs to either use IO (database calls, file writes/reads, shelling out, etc.) or run on JRuby or Rubinius.
90
+
91
+ To make sure all items in your queue are processed you can add a condition `at_exit` to your program:
92
+
93
+ ```ruby
94
+ at_exit do
95
+ Threaded.stop
96
+ end
97
+ ```
98
+
99
+ This call takes an optional timeout value (in seconds).
100
+
101
+ ```ruby
102
+ Threaded.stop(42)
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
108
+
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ require 'bundler/gem_tasks'
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ task :default => [:test]
8
+
9
+ test_task = Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = false
14
+ end
@@ -0,0 +1,79 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+ require 'logger'
4
+
5
+ require 'threaded/version'
6
+ require 'threaded/timeout'
7
+
8
+ module Threaded
9
+ STOP_TIMEOUT = 10 # seconds
10
+ extend self
11
+ attr_accessor :inline, :logger, :size, :timeout
12
+ alias :inline? :inline
13
+
14
+ @mutex = Mutex.new
15
+
16
+ def start(options = {})
17
+ self.master = options
18
+ self.master.start
19
+ return self
20
+ end
21
+
22
+ def configure(&block)
23
+ raise "Queue is already started, must configure queue before starting" if started?
24
+ @mutex.synchronize do
25
+ yield self
26
+ end
27
+ end
28
+ alias :config :configure
29
+
30
+ def started?
31
+ return false unless master
32
+ master.alive?
33
+ end
34
+
35
+ def stopped?
36
+ !started?
37
+ end
38
+
39
+ def master(options = {})
40
+ @mutex.synchronize do
41
+ return @master if @master
42
+ self.logger = options[:logger] if options[:logger]
43
+ self.size = options[:size] if options[:size]
44
+ self.timeout = options[:timeout] if options[:timeout]
45
+ @master = Master.new(logger: self.logger,
46
+ size: self.size,
47
+ timeout: self.timeout)
48
+ end
49
+ end
50
+ alias :master= :master
51
+
52
+ def later(&block)
53
+ Threaded::Promise.new(&block).later
54
+ end
55
+
56
+ def enqueue(job, *args)
57
+ if inline?
58
+ job.call(*args)
59
+ else
60
+ master.enqueue(job, *args)
61
+ end
62
+ return true
63
+ end
64
+
65
+ def stop(timeout = STOP_TIMEOUT)
66
+ return true unless master
67
+ master.stop(timeout)
68
+ return true
69
+ end
70
+ end
71
+
72
+ Threaded.logger = Logger.new(STDOUT)
73
+ Threaded.logger.level = Logger::INFO
74
+
75
+
76
+ require 'threaded/errors'
77
+ require 'threaded/worker'
78
+ require 'threaded/master'
79
+ require 'threaded/promise'
@@ -0,0 +1,5 @@
1
+ module Threaded
2
+ class NoWorkersError < RuntimeError; end
3
+
4
+ class WorkerNotStarted < RuntimeError; end
5
+ end
@@ -0,0 +1,80 @@
1
+ module Threaded
2
+ class Master
3
+ include Threaded::Timeout
4
+ attr_reader :workers, :logger
5
+
6
+ DEFAULT_TIMEOUT = 60 # seconds, 1 minute
7
+ DEFAULT_SIZE = 16
8
+
9
+ def initialize(options = {})
10
+ @queue = Queue.new
11
+ @mutex = Mutex.new
12
+ @stopping = false
13
+ @max = options[:size] || DEFAULT_SIZE
14
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
15
+ @logger = options[:logger] || Threaded.logger
16
+ @workers = []
17
+ end
18
+
19
+ def enqueue(job, *json)
20
+ @queue.enq([job, json])
21
+
22
+ new_worker if needs_workers? && @queue.size > 0
23
+ raise NoWorkersError unless alive?
24
+ return true
25
+ end
26
+
27
+ def alive?
28
+ return false if workers.empty?
29
+ workers.detect {|w| w.alive? }
30
+ end
31
+
32
+ def start
33
+ return self if alive?
34
+ @max.times { new_worker }
35
+ return self
36
+ end
37
+
38
+ def stop(timeout = 10)
39
+ poison
40
+ timeout(timeout, "waiting for workers to stop") do
41
+ while self.alive?
42
+ sleep 0.1
43
+ end
44
+ join
45
+ end
46
+ return self
47
+ end
48
+
49
+ def size
50
+ @workers.size
51
+ end
52
+
53
+ private
54
+
55
+ def needs_workers?
56
+ size < @max
57
+ end
58
+
59
+ def new_worker
60
+ @mutex.synchronize do
61
+ return false unless needs_workers?
62
+ return false if @stopping
63
+ @workers << Worker.new(@queue, timeout: @timeout)
64
+ end
65
+ end
66
+
67
+ def join
68
+ workers.each {|w| w.join }
69
+ return self
70
+ end
71
+
72
+ def poison
73
+ @mutex.synchronize do
74
+ @stopping = true
75
+ end
76
+ workers.each {|w| w.poison }
77
+ return self
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,80 @@
1
+ module Threaded
2
+ class Promise
3
+ class NoJobError < StandardError
4
+ def initialize
5
+ super "No job present for #{self.inspect}"
6
+ end
7
+ end
8
+
9
+ attr_reader :has_run; alias :has_run? :has_run
10
+ attr_reader :running; alias :running? :running
11
+ attr_reader :error
12
+
13
+ def initialize(&job)
14
+ raise "Must supply a job" unless job
15
+ @mutex = Mutex.new
16
+ @has_run = false
17
+ @running = false
18
+ @result = nil
19
+ @error = nil
20
+ @job = job
21
+ end
22
+
23
+ def later
24
+ Threaded.enqueue(self)
25
+ self
26
+ end
27
+
28
+ def call
29
+ @mutex.synchronize do
30
+ return true if running? || has_run?
31
+ begin
32
+ if @job
33
+ @running = true
34
+ @result = @job.call
35
+ else
36
+ raise NoJobError
37
+ end
38
+ rescue Exception => error
39
+ @error = error
40
+ ensure
41
+ @has_run = true
42
+ end
43
+ end
44
+ end
45
+
46
+ def now
47
+ wait_for_it!
48
+ raise error, error.message, error.backtrace if error
49
+ @result
50
+ end
51
+ alias :join :now
52
+ alias :value :now
53
+
54
+ private
55
+ def wait_for_it!
56
+ return true if has_run?
57
+
58
+ if running?
59
+ @mutex.synchronize {} # waits for lock to be released
60
+ else
61
+ call
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ # job = Threaded.later do
68
+
69
+
70
+ # end
71
+
72
+ # job.now
73
+
74
+ # job = Threaded::Promise.new
75
+ # job.enqueue do
76
+
77
+
78
+ # end
79
+
80
+ # job.now
@@ -0,0 +1,11 @@
1
+ module Threaded
2
+ module Timeout
3
+ def timeout(timeout, message = "", &block)
4
+ ::Timeout.timeout(timeout) do
5
+ yield
6
+ end
7
+ rescue ::Timeout::Error
8
+ logger.error("Took longer than #{timeout} to #{message.inspect}")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Threaded
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,48 @@
1
+ module Threaded
2
+ class Worker
3
+ DEFAULT_TIMEOUT = 60 # seconds, 1 minute
4
+ POISON = "poison"
5
+ include Threaded::Timeout
6
+ attr_reader :queue, :logger, :thread
7
+
8
+ def initialize(queue, options = {})
9
+ @queue = queue
10
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
11
+ @logger = options[:logger] || Threaded.logger
12
+ @thread = create_thread
13
+ end
14
+
15
+ def poison
16
+ @queue.enq(POISON)
17
+ end
18
+
19
+ def start
20
+ puts "start is deprecated, thread is started when worker created"
21
+ end
22
+
23
+ def alive?
24
+ thread.alive?
25
+ end
26
+
27
+ def join
28
+ thread.join
29
+ end
30
+
31
+ private
32
+ def create_thread
33
+ Thread.new {
34
+ logger.debug("Threaded In Memory Queue Worker '#{object_id}' ready")
35
+ loop do
36
+ payload = queue.pop
37
+ job, json = *payload
38
+ break if payload == POISON
39
+
40
+ self.timeout(@timeout, "job: #{job.to_s}") do
41
+ job.call(*json)
42
+ end
43
+ end
44
+ logger.debug("Threaded In Memory Queue Worker '#{object_id}' stopped")
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ Bundler.require
2
+
3
+ require 'threaded'
4
+ require 'test/unit'
5
+ require "mocha/setup"
6
+
7
+
8
+ module Dummy
9
+ end
10
+
11
+ Threaded.logger.level = Logger::WARN
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class ThreadedTest < Test::Unit::TestCase
4
+
5
+ def test_started?
6
+ Threaded.start
7
+ assert Threaded.started?
8
+ Threaded.stop
9
+ sleep 1
10
+ assert Threaded.stopped?
11
+ refute Threaded.started?
12
+ end
13
+
14
+ def test_inline
15
+ Dummy.expects(:process).with(1).once
16
+ job = Proc.new {|x| Dummy.process(x) }
17
+
18
+ Threaded.inline = true
19
+ Threaded.enqueue(job, 1)
20
+ assert Threaded.inline
21
+ assert Threaded.stopped?
22
+ ensure
23
+ Threaded.inline = false
24
+ end
25
+
26
+ def test_enqueues
27
+ Dummy.expects(:process).with(1).once
28
+ Dummy.expects(:process).with(2).once
29
+
30
+ Threaded.start
31
+
32
+ job = Proc.new {|x| Dummy.process(x) }
33
+
34
+ Threaded.enqueue(job, 1)
35
+ Threaded.enqueue(job, 2)
36
+ ensure
37
+ Threaded.stop
38
+ end
39
+
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+ require 'stringio'
3
+
4
+ class ConfigTest < Test::Unit::TestCase
5
+
6
+ def teardown
7
+ Threaded.stop
8
+ end
9
+
10
+ def test_config_works
11
+ fake_out = StringIO.new
12
+ logger = Logger.new(fake_out)
13
+ size = rand(1..99)
14
+ timeout = rand(1..99)
15
+
16
+ Threaded.configure do |config|
17
+ config.size = size
18
+ config.logger = logger
19
+ config.timeout = timeout
20
+ end
21
+
22
+ Threaded.start
23
+
24
+ assert_equal size, Threaded.size
25
+ assert_equal timeout, Threaded.timeout
26
+ assert_equal logger, Threaded.logger
27
+ end
28
+
29
+ def test_config_cannot_call_after_start
30
+ Threaded.start
31
+ assert_raise(RuntimeError) do
32
+ Threaded.configure do |config|
33
+ config.size = size
34
+ config.logger = logger
35
+ config.timeout = timeout
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,96 @@
1
+ require 'test_helper'
2
+
3
+ class MasterTest < Test::Unit::TestCase
4
+
5
+ def test_non_explicit_thread_start
6
+ Dummy.expects(:process).with(1).once
7
+ Dummy.expects(:process).with(2).once
8
+
9
+ master = Threaded::Master.new(size: 1)
10
+
11
+ job = Proc.new {|x| Dummy.process(x) }
12
+
13
+ master.enqueue(job, 1)
14
+ master.enqueue(job, 2)
15
+ master.stop
16
+ end
17
+
18
+ def test_creates_up_to_max_workers
19
+ Dummy.expects(:process).with(1).once
20
+ Dummy.expects(:process).with(2).once
21
+
22
+ master = Threaded::Master.new(size: 2)
23
+
24
+ job = Proc.new {|x| sleep 0.5; Dummy.process(x) }
25
+
26
+ master.enqueue(job, 1)
27
+ master.enqueue(job, 2)
28
+
29
+ assert_equal 2, master.size
30
+
31
+ Dummy.expects(:process).with(3).times(3)
32
+ master.enqueue(job, 3)
33
+ master.enqueue(job, 3)
34
+ master.enqueue(job, 3)
35
+
36
+ assert_equal 2, master.size
37
+ master.stop
38
+ end
39
+
40
+ def test_thread_worker_creation
41
+ size = 1
42
+ master = Threaded::Master.new(size: size)
43
+ master.start
44
+ assert_equal size, master.workers.size
45
+ master.stop
46
+
47
+ size = 3
48
+ master = Threaded::Master.new(size: size)
49
+ master.start
50
+ assert_equal size, master.workers.size
51
+ master.stop
52
+
53
+ size = 6
54
+ master = Threaded::Master.new(size: size)
55
+ master.start
56
+ assert_equal size, master.workers.size
57
+ master.stop
58
+
59
+ size = 16
60
+ master = Threaded::Master.new(size: size)
61
+ master.start
62
+ assert_equal size, master.workers.size
63
+ master.stop
64
+ end
65
+
66
+ def test_calls_contents_of_blocks
67
+ Dummy.expects(:process).with(1).once
68
+ Dummy.expects(:process).with(2).once
69
+
70
+ master = Threaded::Master.new(size: 1)
71
+ master.start
72
+
73
+ job = Proc.new {|x| Dummy.process(x) }
74
+
75
+ master.enqueue(job, 1)
76
+ master.enqueue(job, 2)
77
+ master.stop
78
+ end
79
+
80
+ def test_calls_context_of_klass
81
+ Dummy.expects(:process).with(1).once
82
+ Dummy.expects(:process).with(2).once
83
+
84
+ job = Class.new do
85
+ def self.call(num)
86
+ Dummy.process(num)
87
+ end
88
+ end
89
+
90
+ master = Threaded::Master.new(size: 1)
91
+ master.start
92
+ master.enqueue(job, 1)
93
+ master.enqueue(job, 2)
94
+ master.stop
95
+ end
96
+ end
File without changes
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ class WorkerTest < Test::Unit::TestCase
4
+ def setup
5
+ @worker = Threaded::Worker.new(Queue.new, timeout: 1)
6
+ end
7
+
8
+ def teardown
9
+
10
+ end
11
+
12
+ def test_calls_contents_of_blocks
13
+ Dummy.expects(:process).with(1).once
14
+ Dummy.expects(:process).with(2).once
15
+
16
+ job = Proc.new {|x| Dummy.process(x) }
17
+
18
+ @worker.queue << [job, 1]
19
+ @worker.queue << [job, 2]
20
+ @worker.poison
21
+ @worker.join
22
+ end
23
+
24
+ def test_calls_context_of_klass
25
+ Dummy.expects(:process).with(1).once
26
+ Dummy.expects(:process).with(2).once
27
+
28
+ job = Class.new do
29
+ def self.call(num)
30
+ Dummy.process(num)
31
+ end
32
+ end
33
+
34
+ @worker.queue << [job, 1]
35
+ @worker.queue << [job, 2]
36
+ @worker.poison
37
+ @worker.join
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'threaded/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "threaded"
8
+ gem.version = Threaded::VERSION
9
+ gem.authors = ["Richard Schneeman"]
10
+ gem.email = ["richard.schneeman+rubygems@gmail.com"]
11
+ gem.description = %q{ Queue stuff in memory }
12
+ gem.summary = %q{ Memory, Enqueue stuff you will }
13
+ gem.homepage = "https://github.com/schneems/threaded"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency "rake", "~> 10.1"
22
+ gem.add_development_dependency "mocha", "~> 1.0"
23
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: threaded
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Richard Schneeman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mocha
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: " Queue stuff in memory "
42
+ email:
43
+ - richard.schneeman+rubygems@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".travis.yml"
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - README.md
53
+ - Rakefile
54
+ - lib/threaded.rb
55
+ - lib/threaded/errors.rb
56
+ - lib/threaded/master.rb
57
+ - lib/threaded/promise.rb
58
+ - lib/threaded/timeout.rb
59
+ - lib/threaded/version.rb
60
+ - lib/threaded/worker.rb
61
+ - test/test_helper.rb
62
+ - test/threaded.rb
63
+ - test/threaded/config_test.rb
64
+ - test/threaded/master_test.rb
65
+ - test/threaded/promise_test.rb
66
+ - test/threaded/worker_test.rb
67
+ - threaded.gemspec
68
+ homepage: https://github.com/schneems/threaded
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.2.0
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Memory, Enqueue stuff you will
92
+ test_files:
93
+ - test/test_helper.rb
94
+ - test/threaded.rb
95
+ - test/threaded/config_test.rb
96
+ - test/threaded/master_test.rb
97
+ - test/threaded/promise_test.rb
98
+ - test/threaded/worker_test.rb
99
+ has_rdoc: