karousel 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p194@karousel --create
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ branches:
6
+ only:
7
+ - master
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'jeweler'
4
+
5
+ group :test, :development do
6
+ gem 'cucumber'
7
+ gem 'rspec'
8
+ gem 'debugger'
9
+ end
10
+
11
+
@@ -0,0 +1,47 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (3.1.3)
5
+ columnize (0.3.6)
6
+ cucumber (1.2.1)
7
+ builder (>= 2.1.2)
8
+ diff-lcs (>= 1.1.3)
9
+ gherkin (~> 2.11.0)
10
+ json (>= 1.4.6)
11
+ debugger (1.2.0)
12
+ columnize (>= 0.3.1)
13
+ debugger-linecache (~> 1.1.1)
14
+ debugger-ruby_core_source (~> 1.1.3)
15
+ debugger-linecache (1.1.2)
16
+ debugger-ruby_core_source (>= 1.1.1)
17
+ debugger-ruby_core_source (1.1.3)
18
+ diff-lcs (1.1.3)
19
+ gherkin (2.11.2)
20
+ json (>= 1.4.6)
21
+ git (1.2.5)
22
+ jeweler (1.8.4)
23
+ bundler (~> 1.0)
24
+ git (>= 1.2.5)
25
+ rake
26
+ rdoc
27
+ json (1.7.5)
28
+ rake (0.9.2.2)
29
+ rdoc (3.12)
30
+ json (~> 1.4)
31
+ rspec (2.11.0)
32
+ rspec-core (~> 2.11.0)
33
+ rspec-expectations (~> 2.11.0)
34
+ rspec-mocks (~> 2.11.0)
35
+ rspec-core (2.11.1)
36
+ rspec-expectations (2.11.3)
37
+ diff-lcs (~> 1.1.3)
38
+ rspec-mocks (2.11.2)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ cucumber
45
+ debugger
46
+ jeweler
47
+ rspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Marine Biological Laboratory
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 PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL 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.
@@ -0,0 +1,79 @@
1
+ = Karousel
2
+
3
+ {<img src="https://secure.travis-ci.org/GlobalNamesArchitecture/karousel.png" />}[http://travis-ci.org/GlobalNamesArchitecture/karousel] {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/GlobalNamesArchitecture/karousel]
4
+
5
+ Karousel is an intelligent, client-side job dispenser that accesses a parallelized service without overloading its queue. It dishes out jobs onto a defined number of 'seats' on the 'karousel', tosses out completed jobs, and refills empty 'seats' with new jobs. As a result, the queue on the parallelized service remains lean and clients make responsible use of the service.
6
+
7
+ == Principle of operation
8
+
9
+ The gem works on a carousel principle. Jobs are loaded onto a carousel-like object. The size of the 'karousel' (i.e. its 'seats') determines how many objects are active at any given time. For example, if the size of the 'karousel' is 20, only 20 objects at a time will access the service.
10
+
11
+ Karousel loads all jobs, initiates its rotation, and sends each job in succession to a service. For each new job, 'karousel' waits for an 'OK' from the service, captures the status of the job and optionally stores the response from the service. Starting with the oldest submitted job, 'karousel' checks to see if the job is finished. If it is finished, it is processed, removed from the 'karousel', and the seat is freed for a new job. After a rotation of the 'karousel' empty seats are filled with new jobs and the 'karousel' continues to rotate.
12
+
13
+ == Installation
14
+
15
+ gem install karousel
16
+
17
+ == Usage
18
+
19
+ Given a Karousel-compatible job class (e.g. KarouselJob) the usage is:
20
+
21
+ require 'karousel'
22
+ karousel = Karousel.new(KarouselJob, 20, 5)
23
+ karousel.run
24
+
25
+ where KarouselJob is the name of your job class, 20 is number of 'seats' on your 'karousel', and 5 is the time interval in seconds between complete rotations of the 'karousel'.
26
+
27
+ You can optionally supply a block to the 'run' method to perform your own logging, collect data, or other uses. For example:
28
+
29
+ count = 0
30
+ karousel.run { puts count += 1 }
31
+
32
+ or
33
+
34
+ result = []
35
+ karousel.run { result << karousel.cycle_data }
36
+
37
+ == How to write a Job Class
38
+
39
+ A Job class requires the following signature:
40
+
41
+ class MyJob < Karousel::ClientJob
42
+
43
+ def self.populate(number_of_seats)
44
+ ... #returns an array of the karousel size (number_of_seats would be 20 in our example)
45
+ end
46
+
47
+ def send
48
+ ... #sends the job to the service, returns true if it receives an expected response, false otherwise
49
+ end
50
+
51
+ def finished?
52
+ ... #returns true if job is done, false otherwise
53
+ end
54
+
55
+ def process
56
+ ... #processes the finished job
57
+ end
58
+
59
+ end
60
+
61
+ You can also check out https://github.com/GlobalNamesArchitecture/karousel/blob/master/spec/support/client_job_dummy.rb file or https://github.com/GlobalNamesArchitecture/karousel_example
62
+
63
+ == Contributing to karousel
64
+
65
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't yet been fixed.
66
+ * Check out the issue tracker to make sure someone hasn't already submitted a similar request and/or a solution.
67
+ * Fork the project.
68
+ * Start a feature/bugfix branch.
69
+ * Commit and push until you are happy with your contribution.
70
+ * Make sure you add tests. This is important so I don't unintentionally break Karousel in the future.
71
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, that's fine, but please isolate to its commit so I can cherry-pick around it.
72
+
73
+ == Copyright
74
+
75
+ Authors: Dmitry Mozzherin, David Shorthouse
76
+
77
+ Copyright (c) 2012 Marine Biological Laboratory. See LICENSE.txt for
78
+ further details.
79
+
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "karousel"
18
+ gem.homepage = "http://github.com/dimus/karousel"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Job dispenser for parallel workers}
21
+ gem.description = %Q{Use it if you have waaay to many items in your workers' queue}
22
+ gem.email = "dmozzherin@gmail.com"
23
+ gem.authors = ["Dmitry Mozzherin", "David Shorthouse"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ require 'cucumber/rake/task'
40
+ Cucumber::Rake::Task.new(:features)
41
+
42
+ task :default => :spec
43
+
44
+ require 'rdoc/task'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "karousel #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.8
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
11
+ require 'karousel'
12
+
13
+ require 'rspec/expectations'
@@ -0,0 +1,58 @@
1
+ Dir[File.join(File.dirname(__FILE__), "karousel", "*.rb")].each {|f| require f}
2
+
3
+
4
+ class Karousel
5
+ attr_reader :size, :seats, :time_interval, :cycle_data
6
+
7
+ def initialize(klass, size=10, time_interval = 0)
8
+ @klass = klass
9
+ @size = size
10
+ @time_interval = time_interval
11
+ @seats = []
12
+ @cycle_data = []
13
+ end
14
+
15
+ def populate
16
+ new_seats = []
17
+ @klass.populate(@size - @seats.size).each do |inst|
18
+ new_seats << Job.new(inst)
19
+ end
20
+ @seats = new_seats + @seats
21
+ end
22
+
23
+ def run(&block)
24
+ populate
25
+ until @seats.empty? do
26
+ send_request
27
+ sleep(@time_interval)
28
+ check_response
29
+ yield if block
30
+ populate
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def send_request
37
+ @cursor = 0
38
+ @seats.each_with_index do |job, index|
39
+ if job.status == :sent
40
+ @cursor = index
41
+ break
42
+ end
43
+ job.send
44
+ end
45
+ end
46
+
47
+ def check_response
48
+ @cycle_data = []
49
+ @seats = @seats[@cursor..-1] + @seats[0...@cursor] if @cursor != 0
50
+ @seats.size.times do
51
+ job = @seats.shift
52
+ @cycle_data << job
53
+ (job.status != :failure && job.finished? && job.status == :success) ? job.process : @seats.push(job)
54
+ end
55
+ @cursor = 0
56
+ end
57
+
58
+ end
@@ -0,0 +1,32 @@
1
+ class Karousel
2
+ class ClientJob
3
+ attr_accessor :status
4
+
5
+ def initialize
6
+ @status = 1
7
+ end
8
+
9
+ def self.populate
10
+ not_implemented_error
11
+ end
12
+
13
+ def send
14
+ not_implemented_error
15
+ end
16
+
17
+ def finished?
18
+ not_implemented_error
19
+ end
20
+
21
+ def process
22
+ not_implemented_error
23
+ end
24
+
25
+ private
26
+
27
+ def not_implemented_error
28
+ raise Karousel::NotImplementedError
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ class Karousel
2
+ class Job
3
+ STATUS = { 1 => :init, 2 => :sent, 3 => :success, 4 => :failure }
4
+ attr_reader :client_job
5
+
6
+ def initialize(client_job)
7
+ raise TypeError.new("Unknown client job type") unless client_job.is_a?(Karousel::ClientJob)
8
+ @client_job = client_job
9
+ @invert_status = STATUS.invert
10
+ end
11
+
12
+ def status
13
+ @status = STATUS[@client_job.status]
14
+ raise TypeError.new("status must be an integer between 1 and 4") unless @status
15
+ @status
16
+ end
17
+
18
+ def status= (new_status)
19
+ new_status = @invert_status[new_status]
20
+ raise TypeError.new("Unknown status") unless new_status
21
+ @client_job.status = new_status
22
+ end
23
+
24
+ def send
25
+ is_ok = @client_job.send
26
+ self.status = is_ok ? :sent : :failure
27
+ is_ok
28
+ end
29
+
30
+ def finished?
31
+ is_ok = @client_job.finished?
32
+ self.status = is_ok ? :success : :failure
33
+ is_ok
34
+ end
35
+
36
+ def process
37
+ is_ok = @client_job.process
38
+ self.status = is_ok ? :success : :failure
39
+ is_ok
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Karousel::Job do
4
+
5
+ it "should instantiate" do
6
+ j = Karousel::Job.new(ClientJobDummy.new)
7
+ j.class.should == Karousel::Job
8
+ j.client_job.class.should == ClientJobDummy
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Karousel" do
4
+ before(:all) do
5
+ @karousel = Karousel.new(ClientJobDummy, 20, 0)
6
+ end
7
+
8
+ before(:each) do
9
+ ClientJobDummy.reset
10
+ end
11
+
12
+ it "should initiate" do
13
+ k = Karousel.new(ClientJobDummy, 20, 0)
14
+ k.class.should == Karousel
15
+ end
16
+
17
+ it "should have size" do
18
+ @karousel.size.should == 20
19
+ @karousel.seats.size.should == 0
20
+ @karousel.time_interval.should == 0
21
+ end
22
+
23
+ it "should run with block" do
24
+ karousel = Karousel.new(ClientJobDummy, 20, 0)
25
+ karousel.run do
26
+ karousel.cycle_data.size.should > 0
27
+ statuses = karousel.cycle_data.map {|c| c.status}
28
+ statuses.uniq!
29
+ [:failure, :success].should == statuses.sort! if statuses.size > 1
30
+ end
31
+ end
32
+
33
+ it "should be able to get loaded with jobs" do
34
+ @karousel.seats.size.should == 0
35
+ @karousel.populate
36
+ @karousel.seats.size.should == 20
37
+ @karousel.seats[0].class == Karousel::Job
38
+ @karousel.seats[0].status.should == :init
39
+ end
40
+
41
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'karousel'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,34 @@
1
+ require_relative './dummy_server'
2
+
3
+ class ClientJobDummy < Karousel::ClientJob
4
+ @@dummy_data = 0
5
+
6
+ def self.reset
7
+ @@dummy_data = 0
8
+ end
9
+
10
+ def self.reset_failures
11
+ end
12
+
13
+ def self.populate(karousel_size)
14
+ @@dummy_data += karousel_size
15
+ return [] if @@dummy_data > 100
16
+ karousel_size.times.map { self.new }
17
+ end
18
+
19
+ def send
20
+ @server = DummyServer.new
21
+ @server.first_request
22
+ rand(0..10) > 9 ? false : true
23
+ end
24
+
25
+ def finished?
26
+ @server.following_request
27
+ rand(0..10) > 8 ? false : true
28
+ end
29
+
30
+ def process
31
+ end
32
+
33
+ end
34
+
@@ -0,0 +1,13 @@
1
+ class DummyServer
2
+
3
+ def first_request
4
+ @wait_num = rand(1..10)
5
+ @tries_num = 0
6
+ end
7
+
8
+ def following_request
9
+ @tries_num += 1
10
+ @tries_num == @wait_num ? true : false
11
+ end
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: karousel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.8
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dmitry Mozzherin
9
+ - David Shorthouse
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-09-21 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: jeweler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: cucumber
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: debugger
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ description: Use it if you have waaay to many items in your workers' queue
80
+ email: dmozzherin@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files:
84
+ - LICENSE.txt
85
+ - README.rdoc
86
+ files:
87
+ - .document
88
+ - .rspec
89
+ - .rvmrc
90
+ - .travis.yml
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE.txt
94
+ - README.rdoc
95
+ - Rakefile
96
+ - VERSION
97
+ - features/karousel.feature
98
+ - features/step_definitions/karousel_steps.rb
99
+ - features/support/env.rb
100
+ - lib/karousel.rb
101
+ - lib/karousel/client_job.rb
102
+ - lib/karousel/job.rb
103
+ - spec/job_spec.rb
104
+ - spec/karousel_spec.rb
105
+ - spec/spec_helper.rb
106
+ - spec/support/client_job_dummy.rb
107
+ - spec/support/dummy_server.rb
108
+ homepage: http://github.com/dimus/karousel
109
+ licenses:
110
+ - MIT
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ segments:
122
+ - 0
123
+ hash: -572150406200940966
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 1.8.24
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: Job dispenser for parallel workers
136
+ test_files: []