axle-producer_consumer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in producer_consumer.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alex Gibbons
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # ProducerConsumer
2
+
3
+ Threaded [producer/consumer](http://en.wikipedia.org/wiki/Producer-consumer_problem) model. Useful when you have an expensive data producer, such as fetching data over many http connections, and an expensive consumer, such as ingesting into a database.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'axle-producer_consumer', :require => 'producer_consumer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install axle-producer_consumer
18
+
19
+ ## Usage
20
+
21
+ A simple example with simulated expense using an array as the iterator.
22
+
23
+ require 'producer_consumer/core_ext/enumerable'
24
+
25
+ results = []
26
+ array = ('a'..'z').to_a
27
+
28
+ array.produce(3) do |item|
29
+ sleep 1
30
+ "--#{item}--"
31
+ end.consume(2) do |item|
32
+ sleep 0.2
33
+ results << item
34
+ end
35
+
36
+ results.inspect #=> ["--a--", "--b--", "--c--", "--d--" ...
37
+
38
+ In the above we have used three threads to produce each item, sleeping for one second between each iteration of the array. The data is consumed in an additional two threads sleeping for 0.2 seconds between each new item produced by the producers.
39
+
40
+ Or use the `WorkerPool` and `ConsumerPool` classes directly.
41
+
42
+ require 'open-uri'
43
+ require 'net/smtp'
44
+ require 'producer_consumer'
45
+ include ProducerConsumer
46
+
47
+ urls = ['http://www.google.com', 'http://www.yahoo.com', 'http://www.bing.com']
48
+
49
+ producer = WorkerPool.new(urls, 3)
50
+ producer.run do |url|
51
+ [url.gsub('http://', ''), open(url).read]
52
+ end
53
+
54
+ consumer = ConsumerPool.new(producer, 1)
55
+ consumer.run do |(page, page_content)|
56
+ File.open("#{page}.html", "w") { |f| f << page_content }
57
+ end
58
+
59
+ producer.wait
60
+ consumer.wait
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it
65
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
66
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
67
+ 4. Push to the branch (`git push origin my-new-feature`)
68
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ require "thread"
2
+ require "producer_consumer/version"
3
+ require "producer_consumer/consumer_pool"
@@ -0,0 +1,21 @@
1
+ require 'producer_consumer/worker_pool'
2
+
3
+ module ProducerConsumer
4
+
5
+ # A ConsumerPool is a special type of worker pool where the input is
6
+ # another WorkerPool instance.
7
+ class ConsumerPool < WorkerPool
8
+
9
+ def initialize(producer, number_of_threads=1)
10
+ @producer = producer
11
+ super([], number_of_threads)
12
+ end
13
+
14
+ def do_work
15
+ while @producer.alive? || (not @producer.output_queue.empty?)
16
+ @output_queue << yield(@producer.output_queue.pop)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'producer_consumer'
2
+
3
+ module Enumerable
4
+ def produce(number_of_threads=1, &blk)
5
+ @producer_pool = ProducerConsumer::WorkerPool.new(self, number_of_threads)
6
+ @producer_pool.run(&blk)
7
+ self
8
+ end
9
+
10
+ def consume(number_of_threads=1, &blk)
11
+ @consumer_pool = ProducerConsumer::ConsumerPool.new(@producer_pool, number_of_threads)
12
+ @consumer_pool.run(&blk)
13
+ @producer_pool.wait
14
+ @consumer_pool.wait
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module ProducerConsumer
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,41 @@
1
+ module ProducerConsumer
2
+ class WorkerPool
3
+ include Enumerable
4
+ attr_reader :output_queue
5
+
6
+ def initialize(input, number_of_threads=1)
7
+ @input = input
8
+ @number_of_threads = number_of_threads
9
+ @workers = []
10
+ @worker_input_queue = Queue.new
11
+ @output_queue = Queue.new
12
+ end
13
+
14
+ def run(&blk)
15
+ # put input's items into queue for thread safe access
16
+ @input.each { |item| @worker_input_queue << item }
17
+
18
+ @number_of_threads.times do
19
+ @workers << Thread.new do
20
+ do_work(&blk)
21
+ end
22
+ end
23
+ end
24
+
25
+ # worker's unit of work, can be overriden
26
+ def do_work
27
+ until @worker_input_queue.empty?
28
+ @output_queue << yield(@worker_input_queue.pop)
29
+ end
30
+ end
31
+
32
+ def alive?
33
+ @workers.any? { |t| t.alive? }
34
+ end
35
+
36
+ def wait
37
+ @workers.each { |t| t.join }
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'producer_consumer/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "axle-producer_consumer"
8
+ gem.version = ProducerConsumer::VERSION
9
+ gem.authors = ["Alex Gibbons"]
10
+ gem.email = ["alex.gibbons@gmail.com"]
11
+ gem.description = %q{Threaded producer/consumer model. Useful when you have an expensive data producer, such as fetching data over many http connections, and an expensive consumer, such as ingesting into a database.}
12
+ gem.summary = %q{Simple producer/consumer model.}
13
+ gem.homepage = "https://github.com/alexgb/producer_consumer"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rspec", "~> 2.6"
21
+ end
@@ -0,0 +1,32 @@
1
+ require 'producer_consumer'
2
+ require 'producer_consumer/core_ext/enumerable'
3
+
4
+ describe ProducerConsumer do
5
+
6
+ describe Array do
7
+
8
+ subject { ('a'..'z').to_a }
9
+
10
+ it "should respond to produce" do
11
+ should respond_to(:produce)
12
+ end
13
+
14
+ it "should respond to consume" do
15
+ should respond_to(:consume)
16
+ end
17
+
18
+ it "should iterate through all items through producer and consumer" do
19
+ results = []
20
+ subject.produce(3) do |item|
21
+ "--#{item}--"
22
+ end.consume do |item|
23
+ results << item
24
+ end
25
+
26
+ results.size.should eql(26)
27
+ results.all? { |i| i =~ /--\w--/ }.should be
28
+ end
29
+
30
+ end
31
+
32
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: axle-producer_consumer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alex Gibbons
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.6'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.6'
30
+ description: Threaded producer/consumer model. Useful when you have an expensive data
31
+ producer, such as fetching data over many http connections, and an expensive consumer,
32
+ such as ingesting into a database.
33
+ email:
34
+ - alex.gibbons@gmail.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - .gitignore
40
+ - Gemfile
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - lib/producer_consumer.rb
45
+ - lib/producer_consumer/consumer_pool.rb
46
+ - lib/producer_consumer/core_ext/enumerable.rb
47
+ - lib/producer_consumer/version.rb
48
+ - lib/producer_consumer/worker_pool.rb
49
+ - producer_consumer.gemspec
50
+ - spec/producer_consumer_spec.rb
51
+ homepage: https://github.com/alexgb/producer_consumer
52
+ licenses: []
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.24
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Simple producer/consumer model.
75
+ test_files:
76
+ - spec/producer_consumer_spec.rb