agent-q 0.0.1r2

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.
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ /coverage
6
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+
4
+ group :test do
5
+ gem 'simplecov', '>= 0.4.0', :require => false
6
+ gem 'rspec'
7
+ end
8
+ # Specify your gem's dependencies in q.gemspec
9
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/lib/q.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "q/version"
2
+
3
+ module Q
4
+ # Your code goes here...
5
+ end
data/lib/q/array.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Array
2
+ def symbolize_keys!
3
+ map do |item|
4
+ item.symbolize_keys! if item.respond_to? :symbolize_keys!
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ class BinaryString < String
2
+ def self.create from_string
3
+ from_string.force_encoding "BINARY"
4
+ from_string
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ module Collector
2
+ include Enumerable
3
+
4
+ def [] key
5
+ collected_items[key] ||= fetch_item key
6
+ end
7
+ def keys
8
+ @keys ||= fetch_keys
9
+ end
10
+ def each
11
+ keys.each do |key|
12
+ yield self[key]
13
+ end
14
+ end
15
+ private
16
+ def collected_items
17
+ @collected_items ||= {}
18
+ end
19
+
20
+ end
@@ -0,0 +1,29 @@
1
+ module Q
2
+ module Dispatcher
3
+ class AdapterNotFoundError < StandardError; end
4
+ class AdapterNotSpecifiedError < StandardError; end
5
+
6
+ def require_pattern pattern
7
+ @require_pattern = pattern
8
+ end
9
+ def constant_suffix suffix
10
+ @constant_suffix = suffix
11
+ end
12
+ def dispatchables
13
+ @dispatchables ||= {}
14
+ end
15
+ private :dispatchables
16
+
17
+ def [] key
18
+ return dispatchables[key] unless dispatchables[key].nil?
19
+ begin
20
+ require(@require_pattern % key.to_s)
21
+ dispatchables[key] = const_get "#{key.to_s.capitalize}#{@constant_suffix}"
22
+ rescue LoadError
23
+ m = "Unable to find #{key} adapter, make sure it is in your load path"
24
+ raise AdapterNotFoundError, m
25
+ end
26
+ end
27
+
28
+ end
29
+ end
data/lib/q/hash.rb ADDED
@@ -0,0 +1,12 @@
1
+ class Hash
2
+ def symbolize_keys!
3
+ keys.each do |key|
4
+ value = delete key
5
+ if value.is_a? Hash
6
+ value.symbolize_keys!
7
+ end
8
+ self[(key.to_sym rescue key) || key] = value
9
+ end
10
+ self
11
+ end
12
+ end
@@ -0,0 +1,97 @@
1
+ require 'monitor'
2
+
3
+ class ResourcePool
4
+ class ResourcePoolTimeoutError < StandardError; end
5
+
6
+ def initialize config
7
+ extend MonitorMixin
8
+ @config = config
9
+ end
10
+
11
+ def with_resource
12
+ result = yield resource
13
+ checkin
14
+ result
15
+ end
16
+
17
+ def create_resource
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def config
22
+ @config ||= {}
23
+ end
24
+
25
+ private
26
+ def queue
27
+ @queue ||= new_cond
28
+ end
29
+ def resources
30
+ @resources ||= []
31
+ end
32
+ def checked_out
33
+ @checked_out ||= {}
34
+ end
35
+ def timeout
36
+ config[:timeout] || 5
37
+ end
38
+ def size
39
+ config[:pool] || 5
40
+ end
41
+
42
+ def resource
43
+ checked_out[Thread.current.object_id] ||= checkout
44
+ end
45
+
46
+ def checkin(thread_id=Thread.current.object_id)
47
+ synchronize do
48
+ checked_out.delete(thread_id)
49
+ queue.signal
50
+ end
51
+ end
52
+
53
+ def find_resource
54
+ if resource_available?
55
+ find_existing_resource
56
+ elsif can_create_new?
57
+ add_new_resource
58
+ end
59
+ end
60
+
61
+ def clear_stale_resources!
62
+ #find all currenly live threads and check in their corresponding connections
63
+ alive = Thread.list.find_all { |t| t.alive? }.map { |thread| thread.object_id }
64
+ dead = checked_out.keys - alive
65
+ dead.each { |t| checkin t }
66
+ end
67
+
68
+ def resource_available?
69
+ checked_out.keys.size < resources.size
70
+ end
71
+
72
+ def can_create_new?
73
+ resources.size < size
74
+ end
75
+
76
+ def checkout
77
+ #Checkout an available connection or create a new one
78
+ synchronize do
79
+ resource = find_resource
80
+ return resource unless resource.nil?
81
+ queue.wait timeout
82
+ clear_stale_resources!
83
+ raise ResourcePoolTimeoutError unless can_create_new? or resource_available?
84
+ end
85
+ checkout
86
+ end
87
+
88
+ def find_existing_resource
89
+ (resources - checked_out.values).first
90
+ end
91
+
92
+ def add_new_resource
93
+ resources << r = create_resource; r
94
+ end
95
+
96
+ end
97
+
data/lib/q/string.rb ADDED
@@ -0,0 +1,5 @@
1
+ class String
2
+ def to_pascal
3
+ split(/_/).map { |string| string.capitalize }.join
4
+ end
5
+ end
data/lib/q/symbol.rb ADDED
@@ -0,0 +1,5 @@
1
+ class Symbol
2
+ def to_pascal
3
+ to_s.to_pascal
4
+ end
5
+ end
data/lib/q/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Q
2
+ VERSION = "0.0.1r2"
3
+ end
data/q.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "q/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "agent-q"
7
+ s.version = Q::VERSION
8
+ s.authors = ["Jacob Morris"]
9
+ s.email = ["jacob.s.morris@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Q - He's the guy with all the gadgets}
12
+ s.description = %q{Supplies you with stuff you need, exactly when you need it.}
13
+
14
+ s.rubyforge_project = "q"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ require 'q/array'
3
+
4
+ describe Array do
5
+ describe '#symbolize_keys!' do
6
+ it 'runs symbolize_keys! on hash elements' do
7
+ wrapped_hash = [{'superman' => 'is awesome'}].symbolize_keys!
8
+ wrapped_hash[0][:superman].should == 'is awesome'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ require 'q/binary_string'
3
+
4
+ describe BinaryString do
5
+ describe 'create' do
6
+ it 'creates a binary string from an existing string' do
7
+ b = BinaryString.create "from a string"
8
+ b.encoding.should == Encoding::ASCII_8BIT
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+ require 'q/collector'
3
+
4
+ class FakeCollector
5
+ include Collector
6
+ def fetch_keys
7
+ [:hello, :world]
8
+ end
9
+ def fetch_item key
10
+ "hello from key -> #{key.to_s}"
11
+ end
12
+ end
13
+
14
+ class ItemProvider
15
+ def self.next_item
16
+
17
+ end
18
+ end
19
+
20
+ describe Collector do
21
+ before :each do
22
+ @collector = FakeCollector.new
23
+ end
24
+ it 'includes enumerable' do
25
+ @collector.class.included_modules.should include(Enumerable)
26
+ end
27
+ it 'defines :each' do
28
+ @collector.should respond_to(:each)
29
+ end
30
+ it 'does not define <=>' do
31
+ @collector.should_not respond_to(:<)
32
+ @collector.should_not respond_to('=')
33
+ @collector.should_not respond_to(:>)
34
+ end
35
+ describe '#keys' do
36
+ it 'calls :fetch_keys to retrieve a list of keys that correspond to collectible objects' do
37
+ @collector.should_receive :fetch_keys
38
+ @collector.keys
39
+ end
40
+ it 'caches the responce from :fetch_keys' do
41
+ keys = @collector.keys
42
+ keys.should be @collector.keys
43
+ end
44
+ end
45
+
46
+ describe '#[]' do
47
+ it 'calls #fetch_item to retreive an item' do
48
+ @collector.should_receive(:fetch_item).with(:key)
49
+ @collector[:key]
50
+ end
51
+ it 'caches the respond from #fetch_item' do
52
+ @collector[:key].should be(@collector[:key])
53
+ end
54
+ it 'fetches items that have not been cached' do
55
+ @collector[:key].should == "hello from key -> key"
56
+ @collector[:second_key].should == 'hello from key -> second_key'
57
+ end
58
+ end
59
+
60
+ describe '#each' do
61
+ it 'fetches items for all the keys and yeilds them to the block' do
62
+ @collector.keys.length.should > 0
63
+ @collector.each do |item|
64
+ item.should_not be_nil
65
+ end
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,39 @@
1
+ require 'helper'
2
+ require 'q/dispatcher'
3
+
4
+
5
+
6
+ module Q
7
+ module FakeDispatcher
8
+ extend Dispatcher
9
+ end
10
+
11
+ describe Dispatcher do
12
+ it 'responds to require_pattern' do
13
+ FakeDispatcher.should respond_to(:require_pattern)
14
+ end
15
+ it 'responds to constant_suffix' do
16
+ FakeDispatcher.should respond_to(:constant_suffix)
17
+ end
18
+ it 'sets the require pattern variable' do
19
+ FakeDispatcher.require_pattern "support/%s_adapter"
20
+ FakeDispatcher.instance_variable_get(:@require_pattern).should == "support/%s_adapter"
21
+ end
22
+ it 'sets the constant suffix variable' do
23
+ FakeDispatcher.constant_suffix "Adapter"
24
+ FakeDispatcher.instance_variable_get(:@constant_suffix).should == "Adapter"
25
+ end
26
+
27
+ describe '#[]' do
28
+ it 'retrieves a constant for the specified adapter' do
29
+ ->{ FakeAdapter }.should raise_error(NameError)
30
+ FakeDispatcher[:fake].should be(FakeAdapter)
31
+ end
32
+
33
+ it 'raises AdapterNotFoundError when the specified adapter does not exist' do
34
+ ->{ FakeDispatcher[:batman] }.should raise_error(Dispatcher::AdapterNotFoundError)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
data/spec/hash_spec.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'helper'
2
+ require 'q/hash'
3
+
4
+ describe Hash do
5
+ describe '#symbolize_keys!' do
6
+ def hash
7
+ @hash ||= {
8
+ 'superheros' => {
9
+ 'batman' => {
10
+ 'whiny' => true,
11
+ 'actual_powers' => nil
12
+ },
13
+ 'superman' => {
14
+ 'whiny' => 'so absolutely false',
15
+ 'actual_powers' => [
16
+ 'leaps tall buildings in single bound',
17
+ 'faster than speeding bullet'
18
+ ]
19
+ }
20
+ }
21
+ }
22
+ end
23
+ it 'symbolizes keys' do
24
+ hash.symbolize_keys!
25
+ hash.keys.should include(:superheros)
26
+ end
27
+ it 'symbolizes keys recursively' do
28
+ hash.symbolize_keys!
29
+ hash[:superheros].keys.should include(:superman)
30
+ hash[:superheros].keys.should include(:batman)
31
+ hash[:superheros][:batman].keys.should include(:whiny)
32
+ hash[:superheros][:superman].keys.should include(:actual_powers)
33
+ end
34
+ end
35
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+ require 'rspec'
@@ -0,0 +1,119 @@
1
+ require 'helper.rb'
2
+ require 'q/resource_pool'
3
+
4
+ class StringPool < ResourcePool
5
+ def create_resource
6
+ "resource ##{@resources.size}"
7
+ end
8
+ end
9
+
10
+ module Arc
11
+ describe ResourcePool do
12
+
13
+ def checked_out(pool)
14
+ pool.instance_variable_get(:@checked_out)
15
+ end
16
+
17
+ def thread_resources(pool, count=4)
18
+ threads = []
19
+ count.times do |i|
20
+ threads << Thread.start do
21
+ resource = pool.send :resource
22
+ resource.should be_a(String)
23
+ Thread.current[:resource] = resource
24
+ end
25
+ end
26
+ threads.each {|t| t.join }
27
+ end
28
+
29
+ describe '#resource' do
30
+
31
+ it "throws NotImplemented when running create_resource, which should be overridden" do
32
+ p = ResourcePool.new nil
33
+ ->{ p.create_resource }.should raise_error(NotImplementedError)
34
+ end
35
+ it 'creates a new object for each running thread' do
36
+ pool = StringPool.new :pool => 5, :timeout => 5
37
+ threads = thread_resources pool, 4
38
+ checked_out(pool).keys.size.should === 4
39
+ while t = threads.pop do
40
+ resources = checked_out(pool)
41
+ #verify connection is associated to the appropriate thread
42
+ t[:resource].should === resources.delete(t.object_id)
43
+ #make sure the array isn't just holding duplicate objects
44
+ resources.values.should_not include(t[:resource])
45
+ end
46
+ end
47
+
48
+ it 'clears resources from expired threads' do
49
+ pool = StringPool.new :pool => 5, :timeout => 0.1
50
+ threads = thread_resources(pool, 5)
51
+ checked_out(pool).keys.size.should == 5
52
+ c = pool.with_resource do |resource|
53
+ checked_out(pool).keys.size.should === 1
54
+ checked_out(pool)[Thread.current.object_id].should === resource
55
+ end
56
+ end
57
+
58
+ it 'raises error when no resources are available after timeout' do
59
+ pool = StringPool.new :pool => 1, :timeout => 0.1
60
+ pool.with_resource do |resource|
61
+ checked_out(pool).keys.size.should === 1
62
+ Thread.start do
63
+ ->{ pool.with_resource }.should raise_error ResourcePool::ResourcePoolTimeoutError
64
+ end.join
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ describe '#checkin' do
71
+ it 'makes a connection available for use by another thread' do
72
+ pool = StringPool.new :pool => 1, :timeout => 0.1
73
+ resource = pool.with_resource do |resource|
74
+ checked_out(pool).keys.size.should === 1
75
+ Thread.start do
76
+ ->{ pool.with_resource }.should raise_error ResourcePool::ResourcePoolTimeoutError
77
+ end.join
78
+ resource
79
+ end
80
+ final = Thread.start do
81
+ Thread.current[:resource] = pool.with_resource { |resource| resource }
82
+ end.join
83
+ final[:resource].should === resource
84
+ end
85
+
86
+ it 'uses the current thread id when no argument is passed' do
87
+ pool = StringPool.new :pool => 5, :timeout => 5
88
+ c = pool.with_resource do |resource|
89
+ resource
90
+ end
91
+ pool.with_resource do |resource|
92
+ resource.should be(c)
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ describe '#with_resource' do
99
+ it 'uses a resource then checks it back in' do
100
+ pool = StringPool.new :pool => 5, :timeout => 5
101
+ pool.with_resource do |resource|
102
+ resource.should be_a(String)
103
+ checked_out(pool).keys.size.should == 1
104
+ end
105
+ checked_out(pool).keys.size.should == 0
106
+ end
107
+ it 'returns the expected value' do
108
+ pool = StringPool.new :pool => 5, :timeout => 5
109
+ string = pool.with_resource do |resource|
110
+ resource.should be_a(String)
111
+ checked_out(pool).keys.size.should == 1
112
+ "this is the value"
113
+ end
114
+ string.should == "this is the value"
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,2 @@
1
+ class FakeAdapter
2
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent-q
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1r2
5
+ prerelease: 5
6
+ platform: ruby
7
+ authors:
8
+ - Jacob Morris
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-21 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Supplies you with stuff you need, exactly when you need it.
15
+ email:
16
+ - jacob.s.morris@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .DS_Store
22
+ - .gitignore
23
+ - Gemfile
24
+ - Rakefile
25
+ - lib/q.rb
26
+ - lib/q/array.rb
27
+ - lib/q/binary_string.rb
28
+ - lib/q/collector.rb
29
+ - lib/q/dispatcher.rb
30
+ - lib/q/hash.rb
31
+ - lib/q/resource_pool.rb
32
+ - lib/q/string.rb
33
+ - lib/q/symbol.rb
34
+ - lib/q/version.rb
35
+ - q.gemspec
36
+ - spec/array_spec.rb
37
+ - spec/binary_string_spec.rb
38
+ - spec/collector_spec.rb
39
+ - spec/dispatcher_spec.rb
40
+ - spec/hash_spec.rb
41
+ - spec/helper.rb
42
+ - spec/resource_pool_spec.rb
43
+ - spec/support/fake_adapter.rb
44
+ homepage: ''
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>'
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.1
62
+ requirements: []
63
+ rubyforge_project: q
64
+ rubygems_version: 1.8.10
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Q - He's the guy with all the gadgets
68
+ test_files:
69
+ - spec/array_spec.rb
70
+ - spec/binary_string_spec.rb
71
+ - spec/collector_spec.rb
72
+ - spec/dispatcher_spec.rb
73
+ - spec/hash_spec.rb
74
+ - spec/helper.rb
75
+ - spec/resource_pool_spec.rb
76
+ - spec/support/fake_adapter.rb