sneakers 1.0.4 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 840d0c4111ae6ab4c2b97719597fa928632dd34b
4
- data.tar.gz: f24a305085c2b43bee74922011ffd3daf5031d24
3
+ metadata.gz: 6b646dd7a8a5c9389275bd3406ea7e7839d09cd7
4
+ data.tar.gz: 293440a5a50a41f30064ef85d14ac69d84f26652
5
5
  SHA512:
6
- metadata.gz: 936db286d2c0638746771efa54c4bdc8cee4f78c641bdc38db42eaa8fad7bed13428223ac6988edcc10f9a5c883a28c054aeb7d4c842f4befcbc24654014c274
7
- data.tar.gz: 426cf688b5e91c2e02bcb4c45fe420989592c6041419e0bf1cd8d4ae59b145426164ab669e17ac6e620481dc7734c798fcb53cb1d5c8e2ad1dc42c54f2881998
6
+ metadata.gz: 9f5b28202fece758dbd64f29239439da4f023e289da2d1f8ef2c484667bec034a580f97c31dc2ab1931b5315130e177df86ba8927b8f94c06d471fba21dd93e1
7
+ data.tar.gz: 1f53048671fc9e83480891998bf0a01a28796fa7e835906fc40c14489b4143e18b23059f7544ef6b58459eafa288c56e8a06beb3933f4cf133163de0fc605d34
data/.travis.yml CHANGED
@@ -3,4 +3,4 @@ rvm:
3
3
  - 2.2
4
4
  - 2.1
5
5
  - 2.0.0
6
- - 1.9.3
6
+
data/README.md CHANGED
@@ -27,16 +27,21 @@ complete docs.
27
27
 
28
28
  Add this line to your application's Gemfile:
29
29
 
30
- gem 'sneakers'
30
+ ``` ruby
31
+ gem 'sneakers'
32
+ ```
31
33
 
32
34
  And then execute:
33
35
 
34
- $ bundle
36
+ ``` shell-session
37
+ $ bundle
38
+ ```
35
39
 
36
40
  Or install it yourself as:
37
41
 
38
- $ gem install sneakers
39
-
42
+ ``` shell-session
43
+ $ gem install sneakers
44
+ ```
40
45
 
41
46
  ## Quick start
42
47
 
@@ -74,9 +79,7 @@ class Processor
74
79
  end
75
80
  ```
76
81
 
77
-
78
- As an example, make a message look like this:
79
- We'll count errors and error types with Redis. Specifically for an error that looks like this:
82
+ We'll count errors and error types with Redis. As an example, make a message that looks like this:
80
83
 
81
84
  ```javascript
82
85
  {
@@ -90,8 +93,8 @@ We'll count errors and error types with Redis. Specifically for an error that lo
90
93
  Let's test it out quickly from the command line:
91
94
 
92
95
 
93
- ```bash
94
- sneakers work Processor --require boot.rb
96
+ ```shell-session
97
+ $ sneakers work Processor --require boot.rb
95
98
  ```
96
99
 
97
100
  We just told Sneakers to spawn a worker named `Processor`, but first `--require` a file that we dedicate to setting up environment, including workers and what-not.
@@ -109,8 +112,8 @@ If you go to your RabbitMQ admin now, you'll see a new queue named `logs` was cr
109
112
  And redis will show this:
110
113
 
111
114
 
112
- ```
113
- ➜ ~ redis-cli monitor
115
+ ``` shell-session
116
+ $ redis-cli monitor
114
117
  1381520329.888581 [0 127.0.0.1:49182] "incr" "processor:CODE001"
115
118
  ```
116
119
 
@@ -142,13 +145,17 @@ Now push a message again and you'll see:
142
145
  2013-10-11T19:44:37Z p-9219 t-oxh8owywg INFO: INC: work.Processor.handled.ack
143
146
  ```
144
147
 
145
- Which increments start + end, and times the work unit.
146
-
148
+ Which increments `started` and `handled.ack`, and times the work unit.
147
149
 
148
150
 
149
151
  From here, you can continue over to the
150
152
  [Wiki](https://github.com/jondot/sneakers/wiki)
151
153
 
154
+ # Compatibility
155
+
156
+ * Sneakers 1.1.x and up (using the new generation Bunny 2.x) - Ruby 2.x.x
157
+ * Sneakers 1.x.x and down - Ruby 1.9.x, 2.x.x
158
+
152
159
  # Contributing
153
160
 
154
161
  Fork, implement, add tests, pull request, get my everlasting thanks and a respectable place here :).
@@ -162,11 +169,4 @@ To all Sneakers [Contributors](https://github.com/jondot/sneakers/graphs/contrib
162
169
 
163
170
  # Copyright
164
171
 
165
- Copyright (c) 2015 [Dotan Nahum](http://gplus.to/dotan) [@jondot](http://twitter.com/jondot). See MIT-LICENSE for further details.
166
-
167
-
168
-
169
-
170
-
171
-
172
-
172
+ Copyright (c) 2015 [Dotan Nahum](http://gplus.to/dotan) [@jondot](http://twitter.com/jondot). See [LICENSE](LICENSE.txt) for further details.
data/bin/sneakers CHANGED
@@ -2,4 +2,5 @@
2
2
  require 'sneakers'
3
3
  require 'sneakers/cli'
4
4
 
5
+ Sneakers.server = true
5
6
  Sneakers::CLI.start
@@ -10,7 +10,8 @@ class BenchmarkWorker
10
10
  :prefetch => 50,
11
11
  :timeout_job_after => 1,
12
12
  :exchange => 'dummy',
13
- :heartbeat => 5
13
+ :heartbeat => 5,
14
+ :amqp_heartbeat => 10
14
15
  def work(msg)
15
16
  ack!
16
17
  end
@@ -21,6 +21,7 @@ WORKER_OPTIONS = {
21
21
  :prefetch => 1,
22
22
  :timeout_job_after => 60,
23
23
  :heartbeat => 5,
24
+ :amqp_heartbeat => 10,
24
25
  :retry_timeout => 5000
25
26
  }
26
27
 
@@ -33,7 +33,8 @@ class ProfilingWorker
33
33
  :prefetch => 50,
34
34
  :timeout_job_after => 1,
35
35
  :exchange => 'sneakers',
36
- :heartbeat => 5
36
+ :heartbeat => 5,
37
+ :amqp_heartbeat => 10
37
38
  def work(msg)
38
39
  ack!
39
40
  end
@@ -66,4 +67,3 @@ if profiling
66
67
  end
67
68
 
68
69
  r.run
69
-
@@ -10,7 +10,8 @@ class WorkflowWorker
10
10
  :prefetch => 50,
11
11
  :timeout_job_after => 1,
12
12
  :exchange => 'dummy',
13
- :heartbeat => 5
13
+ :heartbeat => 5,
14
+ :amqp_heartbeat => 10
14
15
 
15
16
  def work(msg)
16
17
  logger.info("Seriously, i'm DONE.")
data/lib/sneakers.rb CHANGED
@@ -47,6 +47,10 @@ module Sneakers
47
47
  logger.level = loglevel
48
48
  end
49
49
 
50
+ def logger=(logger)
51
+ @logger = logger
52
+ end
53
+
50
54
  def logger
51
55
  @logger
52
56
  end
@@ -59,13 +63,25 @@ module Sneakers
59
63
  @configured
60
64
  end
61
65
 
66
+ def server=(server)
67
+ @server = server
68
+ end
69
+
70
+ def server?
71
+ @server
72
+ end
73
+
74
+ def configure_server
75
+ yield self if server?
76
+ end
77
+
62
78
  private
63
79
 
64
80
  def setup_general_logger!
65
81
  if [:info, :debug, :error, :warn].all?{ |meth| CONFIG[:log].respond_to?(meth) }
66
82
  @logger = CONFIG[:log]
67
83
  else
68
- @logger = Logger.new(CONFIG[:log])
84
+ @logger = ServerEngine::DaemonLogger.new(CONFIG[:log])
69
85
  @logger.formatter = Sneakers::Support::ProductionFormatter
70
86
  end
71
87
  end
@@ -4,7 +4,7 @@ module Sneakers
4
4
  class Configuration
5
5
 
6
6
  extend Forwardable
7
- def_delegators :@hash, :to_hash, :[], :[]=, :==, :fetch, :delete
7
+ def_delegators :@hash, :to_hash, :[], :[]=, :==, :fetch, :delete, :has_key?
8
8
 
9
9
  DEFAULTS = {
10
10
  # runner
@@ -15,16 +15,19 @@ module Sneakers
15
15
  :workers => 4,
16
16
  :log => STDOUT,
17
17
  :pid_path => 'sneakers.pid',
18
+ :amqp_heartbeat => 10,
18
19
 
19
20
  # workers
20
21
  :timeout_job_after => 5,
21
22
  :prefetch => 10,
22
23
  :threads => 10,
24
+ :share_threads => false,
23
25
  :durable => true,
24
26
  :ack => true,
25
27
  :heartbeat => 2,
26
28
  :exchange => 'sneakers',
27
29
  :exchange_type => :direct,
30
+ :exchange_arguments => {}, # Passed as :arguments to Bunny::Channel#exchange
28
31
  :hooks => {}
29
32
  }.freeze
30
33
 
@@ -40,10 +43,23 @@ module Sneakers
40
43
  end
41
44
 
42
45
  def merge!(hash)
43
- # parse vhost from amqp if vhost is not specified explicitly
44
- if hash[:vhost].nil? && !hash[:amqp].nil?
45
- hash = hash.dup
46
- hash[:vhost] = AMQ::Settings.parse_amqp_url(hash[:amqp]).fetch(:vhost, '/')
46
+ hash = hash.dup
47
+
48
+ # parse vhost from amqp if vhost is not specified explicitly, only
49
+ # if we're not given a connection to use.
50
+ if hash[:connection].nil?
51
+ if hash[:vhost].nil? && !hash[:amqp].nil?
52
+ hash[:vhost] = AMQ::Settings.parse_amqp_url(hash[:amqp]).fetch(:vhost, '/')
53
+ end
54
+ else
55
+ # If we are given a Bunny object, ignore params we'd otherwise use to
56
+ # create one. This removes any question about where config params are
57
+ # coming from, and makes it more likely that downstream code that needs
58
+ # this info gets it from the right place.
59
+ [:vhost, :amqp, :heartbeat].each do |k|
60
+ hash.delete(k)
61
+ @hash.delete(k)
62
+ end
47
63
  end
48
64
 
49
65
  @hash.merge!(hash)
@@ -55,5 +71,16 @@ module Sneakers
55
71
  instance.merge! hash
56
72
  instance
57
73
  end
74
+
75
+ def inspect_with_redaction
76
+ redacted = self.class.new
77
+ redacted.merge! to_hash
78
+
79
+ # redact passwords
80
+ redacted[:amqp] = redacted[:amqp].sub(/(?<=\Aamqp:\/)[^@]+(?=@)/, "<redacted>")
81
+ return redacted.inspect_without_redaction
82
+ end
83
+ alias_method :inspect_without_redaction, :inspect
84
+ alias_method :inspect, :inspect_with_redaction
58
85
  end
59
86
  end
@@ -23,7 +23,7 @@ module Sneakers
23
23
  @bunny = Bunny.new(@opts[:amqp], heartbeat: @opts[:heartbeat], vhost: @opts[:vhost], :logger => Sneakers::logger)
24
24
  @bunny.start
25
25
  @channel = @bunny.create_channel
26
- @exchange = @channel.exchange(@opts[:exchange], type: @opts[:exchange_type], durable: @opts[:durable])
26
+ @exchange = @channel.exchange(@opts[:exchange], type: @opts[:exchange_type], durable: @opts[:durable], arguments: @opts[:exchange_arguments])
27
27
  end
28
28
 
29
29
  def connected?
@@ -16,7 +16,10 @@ class Sneakers::Queue
16
16
  # :ack
17
17
  #
18
18
  def subscribe(worker)
19
- @bunny = Bunny.new(@opts[:amqp], :vhost => @opts[:vhost], :heartbeat => @opts[:heartbeat], :logger => Sneakers::logger)
19
+ # If we've already got a bunny object, use it. This allows people to
20
+ # specify all kinds of options we don't need to know about (e.g. for ssl).
21
+ @bunny = @opts[:connection]
22
+ @bunny ||= create_bunny_connection
20
23
  @bunny.start
21
24
 
22
25
  @channel = @bunny.create_channel
@@ -25,7 +28,8 @@ class Sneakers::Queue
25
28
  exchange_name = @opts[:exchange]
26
29
  @exchange = @channel.exchange(exchange_name,
27
30
  :type => @opts[:exchange_type],
28
- :durable => @opts[:durable])
31
+ :durable => @opts[:durable],
32
+ :arguments => @opts[:exchange_arguments])
29
33
 
30
34
  routing_key = @opts[:routing_key] || @name
31
35
  routing_keys = [*routing_key]
@@ -60,4 +64,9 @@ class Sneakers::Queue
60
64
  @consumer.cancel if @consumer
61
65
  @consumer = nil
62
66
  end
67
+
68
+ def create_bunny_connection
69
+ Bunny.new(@opts[:amqp], :vhost => @opts[:vhost], :heartbeat => @opts[:heartbeat], :logger => Sneakers::logger)
70
+ end
71
+ private :create_bunny_connection
63
72
  end
@@ -63,19 +63,22 @@ module Sneakers
63
63
  config
64
64
  end
65
65
 
66
- private
67
- def make_serverengine_config
66
+ private
67
+
68
+ def make_serverengine_config
68
69
  # From Sneakers#setup_general_logger, there's support for a Logger object
69
70
  # in CONFIG[:log]. However, serverengine takes an object in :logger.
70
71
  # Pass our logger object so there's no issue about sometimes passing a
71
72
  # file and sometimes an object.
72
- without_log = Sneakers::CONFIG.merge(@conf)
73
- without_log.delete(:log)
74
- Sneakers::CONFIG.merge(@conf).merge({
73
+ serverengine_config = Sneakers::CONFIG.merge(@conf)
74
+ serverengine_config.merge!(
75
75
  :logger => Sneakers.logger,
76
76
  :worker_type => 'process',
77
77
  :worker_classes => @worker_classes
78
- })
78
+ )
79
+ serverengine_config.delete(:log)
80
+
81
+ serverengine_config
79
82
  end
80
83
  end
81
84
 
@@ -5,7 +5,13 @@ task :environment
5
5
 
6
6
  namespace :sneakers do
7
7
  desc "Start work (set $WORKERS=Klass1,Klass2)"
8
- task :run => :environment do
8
+ task :run do
9
+ Sneakers.server = true
10
+ Rake::Task['environment'].invoke
11
+
12
+ if defined?(::Rails)
13
+ ::Rails.application.eager_load!
14
+ end
9
15
 
10
16
  workers, missing_workers = Sneakers::Utils.parse_workers(ENV['WORKERS'])
11
17
 
@@ -26,7 +32,7 @@ Please set the classes of the workers you want to run like so:
26
32
  EOF
27
33
  exit(1)
28
34
  end
29
- opts = (ENV['WORKER_COUNT'].present? ? {:workers => ENV['WORKER_COUNT'].to_i} : {})
35
+ opts = (!ENV['WORKER_COUNT'].nil? ? {:workers => ENV['WORKER_COUNT'].to_i} : {})
30
36
  r = Sneakers::Runner.new(workers, opts)
31
37
 
32
38
  r.run
@@ -1,3 +1,3 @@
1
1
  module Sneakers
2
- VERSION = "1.0.4"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -19,7 +19,11 @@ module Sneakers
19
19
  def run
20
20
  after_fork
21
21
 
22
- @workers = config[:worker_classes].map{|w| w.new }
22
+ # Allocate single thread pool if share_threads is set. This improves load balancing
23
+ # when used with many workers.
24
+ pool = config[:share_threads] ? Thread.pool(config[:threads]) : nil
25
+
26
+ @workers = config[:worker_classes].map{|w| w.new(nil, pool) }
23
27
  # if more than one worker this should be per worker
24
28
  # accumulate clients and consumers as well
25
29
  @workers.each do |worker|
@@ -27,7 +31,7 @@ module Sneakers
27
31
  end
28
32
  # end per worker
29
33
  #
30
- until @stop_flag.wait_for_set(10.0)
34
+ until @stop_flag.wait_for_set(Sneakers::CONFIG[:amqp_heartbeat])
31
35
  Sneakers.logger.debug("Heartbeat: running threads [#{Thread.list.count}]")
32
36
  # report aggregated stats?
33
37
  end
@@ -44,4 +48,3 @@ module Sneakers
44
48
 
45
49
  end
46
50
  end
47
-
data/sneakers.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(/^(test|spec|features)\//)
18
18
  gem.require_paths = ['lib']
19
19
  gem.add_dependency 'serverengine', '~> 1.5.5'
20
- gem.add_dependency 'bunny', '~> 1.7.0'
20
+ gem.add_dependency 'bunny', ['>= 1.7.0', '<= 2.0.0']
21
21
  gem.add_dependency 'thread', '~> 0.1.7'
22
22
  gem.add_dependency 'thor'
23
23
 
@@ -2,67 +2,89 @@ require 'spec_helper'
2
2
 
3
3
  describe Sneakers::Configuration do
4
4
 
5
- it 'should assign a default value for :amqp' do
6
- with_env('RABBITMQ_URL', nil) do
7
- config = Sneakers::Configuration.new
8
- config[:amqp].must_equal 'amqp://guest:guest@localhost:5672'
5
+ describe 'with a connection' do
6
+ let(:connection) { Object.new }
7
+
8
+ it 'does not use vhost option if it is specified' do
9
+ url = 'amqp://foo:bar@localhost:5672/foobarvhost'
10
+ with_env('RABBITMQ_URL', url) do
11
+ config = Sneakers::Configuration.new
12
+ config.merge!({ :vhost => 'test_host', :connection => connection })
13
+ config.has_key?(:vhost).must_equal false
14
+ end
9
15
  end
10
- end
11
16
 
12
- it 'should assign a default value for :vhost' do
13
- with_env('RABBITMQ_URL', nil) do
17
+ it 'does not amqp option if it is specified' do
18
+ url = 'amqp://foo:bar@localhost:5672'
14
19
  config = Sneakers::Configuration.new
15
- config[:vhost].must_equal '/'
20
+ config.merge!({ :amqp => url, :connection => connection })
21
+ config.has_key?(:vhost).must_equal false
16
22
  end
17
23
  end
18
24
 
19
- it 'should read the value for amqp from RABBITMQ_URL' do
20
- url = 'amqp://foo:bar@localhost:5672'
21
- with_env('RABBITMQ_URL', url) do
22
- config = Sneakers::Configuration.new
23
- config[:amqp].must_equal url
25
+ describe 'without a connection' do
26
+ it 'should assign a default value for :amqp' do
27
+ with_env('RABBITMQ_URL', nil) do
28
+ config = Sneakers::Configuration.new
29
+ config[:amqp].must_equal 'amqp://guest:guest@localhost:5672'
30
+ end
24
31
  end
25
- end
26
32
 
27
- it 'should read the value for vhost from RABBITMQ_URL' do
28
- url = 'amqp://foo:bar@localhost:5672/foobarvhost'
29
- with_env('RABBITMQ_URL', url) do
30
- config = Sneakers::Configuration.new
31
- config[:vhost].must_equal 'foobarvhost'
33
+ it 'should assign a default value for :vhost' do
34
+ with_env('RABBITMQ_URL', nil) do
35
+ config = Sneakers::Configuration.new
36
+ config[:vhost].must_equal '/'
37
+ end
32
38
  end
33
- end
34
39
 
35
- it 'should parse vhost from amqp option' do
36
- env_url = 'amqp://foo:bar@localhost:5672/foobarvhost'
37
- with_env('RABBITMQ_URL', env_url) do
38
- url = 'amqp://foo:bar@localhost:5672/testvhost'
39
- config = Sneakers::Configuration.new
40
- config.merge!({ :amqp => url })
41
- config[:vhost].must_equal 'testvhost'
40
+ it 'should read the value for amqp from RABBITMQ_URL' do
41
+ url = 'amqp://foo:bar@localhost:5672'
42
+ with_env('RABBITMQ_URL', url) do
43
+ config = Sneakers::Configuration.new
44
+ config[:amqp].must_equal url
45
+ end
42
46
  end
43
- end
44
47
 
45
- it 'should not parse vhost from amqp option if vhost is specified explicitly' do
46
- url = 'amqp://foo:bar@localhost:5672/foobarvhost'
47
- config = Sneakers::Configuration.new
48
- config.merge!({ :amqp => url, :vhost => 'test_host' })
49
- config[:vhost].must_equal 'test_host'
50
- end
48
+ it 'should read the value for vhost from RABBITMQ_URL' do
49
+ url = 'amqp://foo:bar@localhost:5672/foobarvhost'
50
+ with_env('RABBITMQ_URL', url) do
51
+ config = Sneakers::Configuration.new
52
+ config[:vhost].must_equal 'foobarvhost'
53
+ end
54
+ end
55
+
56
+ it 'should parse vhost from amqp option' do
57
+ env_url = 'amqp://foo:bar@localhost:5672/foobarvhost'
58
+ with_env('RABBITMQ_URL', env_url) do
59
+ url = 'amqp://foo:bar@localhost:5672/testvhost'
60
+ config = Sneakers::Configuration.new
61
+ config.merge!({ :amqp => url })
62
+ config[:vhost].must_equal 'testvhost'
63
+ end
64
+ end
51
65
 
52
- it 'should use vhost option if it is specified' do
53
- url = 'amqp://foo:bar@localhost:5672/foobarvhost'
54
- with_env('RABBITMQ_URL', url) do
66
+ it 'should not parse vhost from amqp option if vhost is specified explicitly' do
67
+ url = 'amqp://foo:bar@localhost:5672/foobarvhost'
55
68
  config = Sneakers::Configuration.new
56
- config.merge!({ :vhost => 'test_host' })
69
+ config.merge!({ :amqp => url, :vhost => 'test_host' })
57
70
  config[:vhost].must_equal 'test_host'
58
71
  end
59
- end
60
72
 
61
- it 'should use default vhost if vhost is not specified in amqp option' do
62
- url = 'amqp://foo:bar@localhost:5672'
63
- config = Sneakers::Configuration.new
64
- config.merge!({ :amqp => url })
65
- config[:vhost].must_equal '/'
73
+ it 'should use vhost option if it is specified' do
74
+ url = 'amqp://foo:bar@localhost:5672/foobarvhost'
75
+ with_env('RABBITMQ_URL', url) do
76
+ config = Sneakers::Configuration.new
77
+ config.merge!({ :vhost => 'test_host' })
78
+ config[:vhost].must_equal 'test_host'
79
+ end
80
+ end
81
+
82
+ it 'should use default vhost if vhost is not specified in amqp option' do
83
+ url = 'amqp://foo:bar@localhost:5672'
84
+ config = Sneakers::Configuration.new
85
+ config.merge!({ :amqp => url })
86
+ config[:vhost].must_equal '/'
87
+ end
66
88
  end
67
89
 
68
90
  def with_env(key, value)
@@ -60,11 +60,12 @@ describe Sneakers::Publisher do
60
60
  amqp: 'amqp://someuser:somepassword@somehost:5672',
61
61
  heartbeat: 1, exchange: 'another_exchange',
62
62
  exchange_type: :topic,
63
+ exchange_arguments: { 'x-arg' => 'value' },
63
64
  log: logger,
64
65
  durable: false)
65
66
 
66
67
  channel = Object.new
67
- mock(channel).exchange('another_exchange', type: :topic, durable: false) do
68
+ mock(channel).exchange('another_exchange', type: :topic, durable: false, arguments: { 'x-arg' => 'value' }) do
68
69
  mock(Object.new).publish('test msg', routing_key: 'downloads')
69
70
  end
70
71
 
@@ -1,8 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'sneakers'
3
3
 
4
-
5
-
6
4
  describe Sneakers::Queue do
7
5
  let :queue_vars do
8
6
  {
@@ -12,103 +10,137 @@ describe Sneakers::Queue do
12
10
  :heartbeat => 2,
13
11
  :vhost => '/',
14
12
  :exchange => "sneakers",
15
- :exchange_type => :direct
13
+ :exchange_type => :direct,
14
+ :exchange_arguments => { 'x-arg' => 'value' }
16
15
  }
17
16
  end
18
17
 
19
18
  before do
20
19
  Sneakers.configure
21
20
 
22
- @mkbunny = Object.new
21
+ @mkworker = Object.new
22
+ stub(@mkworker).opts { { :exchange => 'test-exchange' } }
23
23
  @mkchan = Object.new
24
+ mock(@mkchan).prefetch(25)
24
25
  @mkex = Object.new
25
26
  @mkqueue = Object.new
26
- @mkqueue_nondurable = Object.new
27
- @mkworker = Object.new
28
-
29
- mock(@mkbunny).start {}
30
- mock(@mkbunny).create_channel{ @mkchan }
31
- mock(Bunny).new(anything, :vhost => '/', :heartbeat => 2){ @mkbunny }
32
-
33
- mock(@mkchan).prefetch(25)
34
-
35
- stub(@mkworker).opts { { :exchange => 'test-exchange' } }
36
27
  end
37
28
 
38
- describe "#subscribe with sneakers exchange" do
29
+ describe 'with our own Bunny object' do
39
30
  before do
40
- mock(@mkchan).exchange("sneakers", :type => :direct, :durable => true){ @mkex }
31
+ @mkbunny = Object.new
32
+ @mkqueue_nondurable = Object.new
33
+
34
+ mock(@mkbunny).start {}
35
+ mock(@mkbunny).create_channel{ @mkchan }
36
+ mock(Bunny).new(anything, :vhost => '/', :heartbeat => 2){ @mkbunny }
41
37
  end
42
38
 
43
- it "should setup a bunny queue according to configuration values" do
44
- mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
45
- q = Sneakers::Queue.new("downloads", queue_vars)
39
+ describe "#subscribe with sneakers exchange" do
40
+ before do
41
+ mock(@mkchan).exchange("sneakers",
42
+ :type => :direct,
43
+ :durable => true,
44
+ :arguments => { 'x-arg' => 'value' }){ @mkex }
45
+ end
46
46
 
47
- mock(@mkqueue).bind(@mkex, :routing_key => "downloads")
48
- mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
47
+ it "should setup a bunny queue according to configuration values" do
48
+ mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
49
+ q = Sneakers::Queue.new("downloads", queue_vars)
49
50
 
50
- q.subscribe(@mkworker)
51
- end
51
+ mock(@mkqueue).bind(@mkex, :routing_key => "downloads")
52
+ mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
52
53
 
53
- it "supports multiple routing_keys" do
54
- mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
55
- q = Sneakers::Queue.new("downloads",
56
- queue_vars.merge(:routing_key => ["alpha", "beta"]))
54
+ q.subscribe(@mkworker)
55
+ end
57
56
 
58
- mock(@mkqueue).bind(@mkex, :routing_key => "alpha")
59
- mock(@mkqueue).bind(@mkex, :routing_key => "beta")
60
- mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
57
+ it "supports multiple routing_keys" do
58
+ mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
59
+ q = Sneakers::Queue.new("downloads",
60
+ queue_vars.merge(:routing_key => ["alpha", "beta"]))
61
61
 
62
- q.subscribe(@mkworker)
63
- end
62
+ mock(@mkqueue).bind(@mkex, :routing_key => "alpha")
63
+ mock(@mkqueue).bind(@mkex, :routing_key => "beta")
64
+ mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
64
65
 
65
- it "will use whatever handler the worker specifies" do
66
- mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
67
- @handler = Object.new
68
- worker_opts = { :handler => @handler }
69
- stub(@mkworker).opts { worker_opts }
70
- mock(@handler).new(@mkchan, @mkqueue, worker_opts).once
71
-
72
- stub(@mkqueue).bind
73
- stub(@mkqueue).subscribe
74
- q = Sneakers::Queue.new("downloads", queue_vars)
75
- q.subscribe(@mkworker)
76
- end
66
+ q.subscribe(@mkworker)
67
+ end
77
68
 
78
- it "creates a non-durable queue if :queue_durable => false" do
79
- mock(@mkchan).queue("test_nondurable", :durable => false) { @mkqueue_nondurable }
80
- queue_vars[:queue_durable] = false
81
- q = Sneakers::Queue.new("test_nondurable", queue_vars)
69
+ it "will use whatever handler the worker specifies" do
70
+ mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
71
+ @handler = Object.new
72
+ worker_opts = { :handler => @handler }
73
+ stub(@mkworker).opts { worker_opts }
74
+ mock(@handler).new(@mkchan, @mkqueue, worker_opts).once
75
+
76
+ stub(@mkqueue).bind
77
+ stub(@mkqueue).subscribe
78
+ q = Sneakers::Queue.new("downloads", queue_vars)
79
+ q.subscribe(@mkworker)
80
+ end
82
81
 
83
- mock(@mkqueue_nondurable).bind(@mkex, :routing_key => "test_nondurable")
84
- mock(@mkqueue_nondurable).subscribe(:block => false, :manual_ack => true)
82
+ it "creates a non-durable queue if :queue_durable => false" do
83
+ mock(@mkchan).queue("test_nondurable", :durable => false) { @mkqueue_nondurable }
84
+ queue_vars[:queue_durable] = false
85
+ q = Sneakers::Queue.new("test_nondurable", queue_vars)
85
86
 
86
- q.subscribe(@mkworker)
87
- myqueue = q.instance_variable_get(:@queue)
88
- end
89
- end
87
+ mock(@mkqueue_nondurable).bind(@mkex, :routing_key => "test_nondurable")
88
+ mock(@mkqueue_nondurable).subscribe(:block => false, :manual_ack => true)
90
89
 
91
- describe "#subscribe with default exchange" do
92
- before do
93
- # expect default exchange
94
- queue_vars[:exchange] = ""
95
- mock(@mkchan).exchange("", :type => :direct, :durable => true){ @mkex }
90
+ q.subscribe(@mkworker)
91
+ myqueue = q.instance_variable_get(:@queue)
92
+ end
96
93
  end
97
94
 
98
- it "does not bind to exchange" do
99
- mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
100
- @handler = Object.new
101
- worker_opts = { :handler => @handler }
102
- stub(@mkworker).opts { worker_opts }
103
- mock(@handler).new(@mkchan, @mkqueue, worker_opts).once
95
+ describe "#subscribe with default exchange" do
96
+ before do
97
+ # expect default exchange
98
+ queue_vars[:exchange] = ""
99
+ mock(@mkchan).exchange("", :type => :direct, :durable => true){ @mkex }
100
+ end
104
101
 
105
- stub(@mkqueue).bind do
106
- raise "bind should not be called"
102
+ it "does not bind to exchange" do
103
+ mock(@mkchan).queue("downloads", :durable => true) { @mkqueue }
104
+ @handler = Object.new
105
+ worker_opts = { :handler => @handler }
106
+ stub(@mkworker).opts { worker_opts }
107
+ mock(@handler).new(@mkchan, @mkqueue, worker_opts).once
108
+
109
+ stub(@mkqueue).bind do
110
+ raise "bind should not be called"
111
+ end
112
+
113
+ stub(@mkqueue).subscribe
114
+ q = Sneakers::Queue.new("downloads", queue_vars)
115
+ q.subscribe(@mkworker)
107
116
  end
117
+ end
118
+ end
108
119
 
109
- stub(@mkqueue).subscribe
110
- q = Sneakers::Queue.new("downloads", queue_vars)
111
- q.subscribe(@mkworker)
120
+ describe 'with an externally-provided connection' do
121
+ describe '#subscribe' do
122
+ before do
123
+ @external_connection = Bunny.new
124
+ mock(@external_connection).start {}
125
+ mock(@external_connection).create_channel{ @mkchan }
126
+ mock(@mkchan).exchange("sneakers",
127
+ :type => :direct,
128
+ :durable => true,
129
+ :arguments => { 'x-arg' => 'value' }){ @mkex }
130
+
131
+ queue_name = 'foo'
132
+ mock(@mkchan).queue(queue_name, :durable => true) { @mkqueue }
133
+ mock(@mkqueue).bind(@mkex, :routing_key => queue_name)
134
+ mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
135
+
136
+ my_vars = queue_vars.merge(:connection => @external_connection)
137
+ @q = Sneakers::Queue.new(queue_name, my_vars)
138
+ end
139
+
140
+ it 'uses that object' do
141
+ @q.subscribe(@mkworker)
142
+ @q.instance_variable_get(:@bunny).must_equal @external_connection
143
+ end
112
144
  end
113
145
  end
114
146
  end
@@ -24,3 +24,21 @@ describe Sneakers::Runner do
24
24
  end
25
25
  end
26
26
  end
27
+
28
+ describe Sneakers::RunnerConfig do
29
+ let(:logger) { Logger.new("logtest.log") }
30
+ let(:runner_config) { Sneakers::Runner.new([]).instance_variable_get("@runnerconfig") }
31
+
32
+ before { Sneakers.configure(log: logger) }
33
+
34
+
35
+ describe "#reload_config!" do
36
+ it "must not have :log key" do
37
+ runner_config.reload_config!.has_key?(:log).must_equal false
38
+ end
39
+
40
+ it "must have :logger key as an instance of Logger" do
41
+ runner_config.reload_config![:logger].is_a?(Logger).must_equal true
42
+ end
43
+ end
44
+ end
@@ -54,14 +54,16 @@ describe Sneakers do
54
54
 
55
55
 
56
56
  describe '#setup_general_logger' do
57
+ let(:logger_class) { ServerEngine::DaemonLogger }
58
+
57
59
  it 'should detect a string and configure a logger' do
58
60
  Sneakers.configure(:log => 'sneakers.log')
59
- Sneakers.logger.kind_of?(Logger).must_equal(true)
61
+ Sneakers.logger.kind_of?(logger_class).must_equal(true)
60
62
  end
61
63
 
62
64
  it 'should detect a file-like thing and configure a logger' do
63
65
  Sneakers.configure(:log => STDOUT)
64
- Sneakers.logger.kind_of?(Logger).must_equal(true)
66
+ Sneakers.logger.kind_of?(logger_class).must_equal(true)
65
67
  end
66
68
 
67
69
  it 'should detect an actual logger and configure it' do
@@ -171,15 +171,18 @@ describe Sneakers::Worker do
171
171
  :timeout_job_after => 5,
172
172
  :prefetch => 10,
173
173
  :threads => 10,
174
+ :share_threads => false,
174
175
  :durable => true,
175
176
  :ack => true,
176
177
  :amqp => "amqp://guest:guest@localhost:5672",
177
178
  :vhost => "/",
178
179
  :exchange => "sneakers",
179
180
  :exchange_type => :direct,
181
+ :exchange_arguments => {},
180
182
  :hooks => {},
181
183
  :handler => Sneakers::Handlers::Oneshot,
182
- :heartbeat => 2
184
+ :heartbeat => 2,
185
+ :amqp_heartbeat => 10
183
186
  )
184
187
  end
185
188
 
@@ -196,15 +199,18 @@ describe Sneakers::Worker do
196
199
  :timeout_job_after => 1,
197
200
  :prefetch => 40,
198
201
  :threads => 50,
202
+ :share_threads => false,
199
203
  :durable => false,
200
204
  :ack => false,
201
205
  :amqp => "amqp://guest:guest@localhost:5672",
202
206
  :vhost => "/",
203
207
  :exchange => "dummy",
204
208
  :exchange_type => :direct,
209
+ :exchange_arguments => {},
205
210
  :hooks => {},
206
211
  :handler => Sneakers::Handlers::Oneshot,
207
- :heartbeat => 5
212
+ :heartbeat => 5,
213
+ :amqp_heartbeat => 10
208
214
  )
209
215
  end
210
216
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sneakers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dotan Nahum
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-28 00:00:00.000000000 Z
11
+ date: 2015-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: serverengine
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: bunny
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.7.0
34
+ - - "<="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.0.0
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: 1.7.0
44
+ - - "<="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.0.0
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: thread
43
49
  requirement: !ruby/object:Gem::Requirement