officer 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/CONTRIBUTORS ADDED
@@ -0,0 +1,2 @@
1
+ In order of contribution:
2
+ Nelson Pascoal (nelsondcp@gmail.com)
data/README.markdown CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Officer is designed to help you coordinate distributed processes and avoid race conditions. Inspiration comes from [elock](http://github.com/dustin/elock).
4
4
 
5
+ Read more in my blog post: [http://remesch.com/officer-the-ruby-lock-server-and-client](http://remesch.com/officer-the-ruby-lock-server-and-client)
6
+
5
7
  ## Installation
6
8
 
7
9
  gem install officer
@@ -9,23 +11,37 @@ Officer is designed to help you coordinate distributed processes and avoid race
9
11
  ## Usage
10
12
 
11
13
  Officer uses the 'daemons' gem to simplify creating long lived background processes.
12
- Here are some simple examples in case you aren't familiar with it.
13
14
 
14
- 'daemons' help information:
15
- sudo officer --help
15
+ Help information:
16
+ officer --help
17
+
18
+ Usage: officer [-hofplsd]
19
+ -h, --host=HOST The hostname or IP to bind to (default: 0.0.0.0)
20
+ -o, --socket-type=OPTION TCP or UNIX (default: TCP)
21
+ -f, --socket-file=FILE Full path and name to the UNIX socket file (only used if --socket-type=UNIX, default: /tmp/officer.sock)
22
+ -p, --port=PORT The port to listen on (default: 11500)
23
+ -l, --log-level Set the log level to debug, info, or error (default: error)
24
+ -s, --stats Log stats every 5 seconds (default: off, required log level: info)
25
+ -d, --pid-dir Set directory where pid file will be saved (default: operating system's run directory)
26
+ --help
16
27
 
17
- Officer's help information:
18
- sudo officer run -- --help
19
28
 
20
29
  Run Officer in the foreground with full logging and statistics:
21
- sudo officer run -- -l debug -s
30
+
31
+ officer run -- -l debug -s -d /tmp
22
32
 
23
33
  Run Officer in the background (production mode) and listen on a specific IP and port:
24
- sudo officer start -- -h 127.0.0.1 -p 9999
34
+
35
+ officer start -- -h 127.0.0.1 -p 9999 -d /tmp
36
+
37
+ ### Other notes:
25
38
 
26
39
  - The server listens on 0.0.0.0:11500 by default.
27
40
  - All debugging and error output goes to stdout for now.
28
- - The daemons gem will create a pid file in /var/run and redirect stdout to /var/log/officer.output when using the 'start' option for background mode.
41
+ - By default, a pid file is created in /var/run and stdout is written to /var/log/officer.output. This will require root permissions which is normally a bad idea. You can avoid this by picking a different directory (example: officer start -- -d /tmp).
42
+ - I personally run Officer in production using Ruby Enterprise Edition (REE) which is based on Ruby 1.8.7.
43
+ - RVM and JRuby users should check the [Known Issues](https://github.com/chadrem/officer/wiki/Known-Issues) wiki page.
44
+ - UNIX domain sockets are supported (example: officer start -- -o UNIX -p /tmp)
29
45
 
30
46
  ## Ruby Client
31
47
 
@@ -40,6 +56,8 @@ Options:
40
56
 
41
57
  - :host => Hostname or IP address of the server to bind to (default: 0.0.0.0).
42
58
  - :port => TCP Port to listen on (default: 11500).
59
+ - :socket_type => TCP or UNIX (default: TCP).
60
+ - :socket_file => Full path to the server's UNIX domain socket file (default: /tmp/officer.sock). This option is only used when the socket type is UNIX.
43
61
 
44
62
 
45
63
  ### Lock
@@ -81,6 +99,13 @@ Options:
81
99
  - Useful if you use Officer with Phusion Passenger and smart spawning. See [Passenger's documentation](http://www.modrails.com/documentation/Users%20guide%20Apache.html#_smart_spawning_gotcha_1_unintential_file_descriptor_sharing) for more information.
82
100
 
83
101
 
102
+ ### Disconnect
103
+
104
+ client.disconnect
105
+
106
+ - Close the connection to the server.
107
+
108
+
84
109
  ### Show locks
85
110
 
86
111
  client.locks
@@ -100,6 +125,17 @@ Options:
100
125
  client.my_locks
101
126
 
102
127
 
128
+ ## Contributing to Officer
129
+
130
+ 1. Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
131
+ 2. Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
132
+ 3. Fork the project.
133
+ 4. Start a feature/bugfix branch.
134
+ 5. Commit and push until you are happy with your contribution.
135
+ 6. Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
136
+ 7. Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
137
+
138
+
103
139
  ## Copyright
104
140
 
105
- Copyright (c) 2010 Chad Remesch. See LICENSE for details.
141
+ Copyright (c) 2010 - 2012 Chad Remesch. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.6
1
+ 0.9.0
data/bin/officer CHANGED
@@ -3,51 +3,4 @@
3
3
  require 'rubygems'
4
4
  require 'officer'
5
5
 
6
- daemon_options = {
7
- :dir_mode => :system,
8
- :multiple => false,
9
- :monitor => true,
10
- :log_output => true
11
- }
12
-
13
- def parse_command_line
14
- if ARGV.include? '--'
15
- ARGV.slice! 0..ARGV.index('--')
16
- end
17
-
18
- Choice.options do
19
- option :host do
20
- short '-h'
21
- long '--host=HOST'
22
- desc 'The hostname or IP to bind to (default: 0.0.0.0)'
23
- end
24
-
25
- option :port do
26
- short '-p'
27
- long '--port=PORT'
28
- desc 'The port to listen on (default: 11500)'
29
- cast Integer
30
- end
31
-
32
- option :log_level do
33
- short '-l'
34
- long '--log-level'
35
- desc 'Set the log level to debug, info, or error (default: error)'
36
- end
37
-
38
- option :stats do
39
- short '-s'
40
- long '--stats'
41
- desc 'Log stats every 5 seconds (default: off, required log level: info)'
42
- end
43
-
44
- option :help do
45
- long '--help'
46
- end
47
- end
48
- end
49
-
50
- Daemons.run_proc('officer', daemon_options) do
51
- parse_command_line
52
- Officer::Server.new(Choice.choices).run
53
- end
6
+ Officer::Runner.new.run
@@ -15,6 +15,8 @@ module Officer
15
15
 
16
16
  class Client
17
17
  def initialize options={}
18
+ @socket_type = options[:socket_type] || 'TCP'
19
+ @socket_file = options[:socket_file] || '/tmp/officer.sock'
18
20
  @host = options[:host] || 'localhost'
19
21
  @port = options[:port] || 11500
20
22
  @namespace = options[:namespace]
@@ -27,6 +29,11 @@ module Officer
27
29
  connect
28
30
  end
29
31
 
32
+ def disconnect
33
+ @socket.close if @socket
34
+ @socket = nil
35
+ end
36
+
30
37
  def lock name, options={}
31
38
  result = execute :command => 'lock', :name => name_with_ns(name),
32
39
  :timeout => options[:timeout], :queue_max => options[:queue_max]
@@ -42,7 +49,7 @@ module Officer
42
49
  response = lock name, options
43
50
  result = response['result']
44
51
  queue = (response['queue'] || []).join ','
45
-
52
+
46
53
  raise LockTimeoutError.new("queue=#{queue}") if result == 'timed_out'
47
54
  raise LockQueuedMaxError.new("queue=#{queue}") if result == 'queue_maxed'
48
55
  raise LockError unless %w(acquired already_acquired).include?(result)
@@ -80,13 +87,16 @@ module Officer
80
87
  def connect
81
88
  raise AlreadyConnectedError if @socket
82
89
 
83
- @socket = TCPSocket.new @host, @port.to_i
84
- @socket.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
85
- end
90
+ case @socket_type
91
+ when 'TCP'
92
+ @socket = TCPSocket.new @host, @port.to_i
93
+ when 'UNIX'
94
+ @socket = UNIXSocket.new @socket_file
95
+ else
96
+ raise "Invalid socket type: #{@socket_type}"
97
+ end
86
98
 
87
- def disconnect
88
- @socket.close if @socket
89
- @socket = nil
99
+ @socket.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
90
100
  end
91
101
 
92
102
  def execute command
@@ -92,7 +92,12 @@ module Officer
92
92
  include LockStoreCallbacks
93
93
 
94
94
  def to_host_s
95
- @to_host_s ||= non_cached_to_host_s
95
+ begin
96
+ @to_host_s ||= non_cached_to_host_s
97
+ rescue ArgumentError
98
+ # we assume unix socket, so no ip/port info
99
+ @to_host_s ||= 'UNIX socket client'
100
+ end
96
101
  end
97
102
 
98
103
  private
@@ -118,4 +123,4 @@ module Officer
118
123
  end
119
124
 
120
125
  end
121
- end
126
+ end
@@ -0,0 +1,101 @@
1
+ module Officer
2
+
3
+ class Runner
4
+ def run
5
+ hack_argv
6
+ set_choices
7
+ unhack_argv
8
+ set_daemon_options
9
+ run_daemon
10
+ end
11
+
12
+ # HACK: Both the Choice and Daemons gems will parse ARGV so I
13
+ # modify ARGV for Choice and then restore it for Daemons.
14
+ def hack_argv
15
+ if ARGV.include? '--'
16
+ @saved_args = ARGV.slice! 0..ARGV.index('--')
17
+ end
18
+ end
19
+
20
+ def unhack_argv
21
+ ARGV.unshift(*@saved_args) if @saved_args
22
+ end
23
+
24
+ def set_choices
25
+
26
+ Choice.options do
27
+ option :host do
28
+ short '-h'
29
+ long '--host=HOST'
30
+ desc 'The hostname or IP to bind to (default: 0.0.0.0)'
31
+ end
32
+
33
+ option :socket_type do
34
+ short '-o'
35
+ long '--socket-type=OPTION'
36
+ desc 'TCP or UNIX (default: TCP)'
37
+ default 'TCP'
38
+ validate /^(TCP|UNIX)$/
39
+ end
40
+
41
+ option :socket_file do
42
+ short '-f'
43
+ long '--socket-file=FILE'
44
+ desc "Full path and name to the UNIX domain socket file (only used with '-o UNIX', default: /tmp/officer.sock)"
45
+ end
46
+
47
+ option :port do
48
+ short '-p'
49
+ long '--port=PORT'
50
+ desc 'The port to listen on (default: 11500)'
51
+ cast Integer
52
+ end
53
+
54
+ option :log_level do
55
+ short '-l'
56
+ long '--log-level'
57
+ desc 'Set the log level to debug, info, or error (default: error)'
58
+ end
59
+
60
+ option :stats do
61
+ short '-s'
62
+ long '--stats'
63
+ desc 'Log stats every 5 seconds (default: off, required log level: info)'
64
+ end
65
+
66
+ option :pid_dir do
67
+ short '-d'
68
+ long '--pid-dir'
69
+ desc "Set directory where pid file will be saved (default: operating system's run directory)"
70
+ end
71
+
72
+ option :help do
73
+ long '--help'
74
+ end
75
+ end
76
+
77
+ @choices = Choice.choices
78
+ end
79
+
80
+ def set_daemon_options
81
+ @daemon_options = {
82
+ :dir_mode => :system,
83
+ :multiple => false,
84
+ :monitor => true,
85
+ :log_output => true
86
+ }
87
+
88
+ if @choices[:pid_dir]
89
+ @daemon_options[:dir_mode] = :normal
90
+ @daemon_options[:dir] = @choices[:pid_dir]
91
+ end
92
+ end
93
+
94
+ def run_daemon
95
+ Daemons.run_proc('officer', @daemon_options) do
96
+ Officer::Server.new(@choices).run
97
+ end
98
+ end
99
+ end
100
+
101
+ end
@@ -1,11 +1,22 @@
1
1
  module Officer
2
2
 
3
3
  class Server
4
+ class ShutdownConnection < EventMachine::Connection
5
+ def unbind
6
+ EM::stop_event_loop
7
+ end
8
+ end
9
+
4
10
  def initialize params={}
11
+ @semaphore = Mutex.new
12
+ set_running(false)
13
+
5
14
  @params = params
6
15
 
16
+ params[:socket_type] ||= 'TCP'
7
17
  params[:port] ||= 11500
8
18
  params[:host] ||= '0.0.0.0'
19
+ params[:socket_file] ||= '/tmp/officer.sock'
9
20
  params[:stats] ||= false
10
21
  params[:log_level] ||= 'error'
11
22
 
@@ -15,14 +26,39 @@ module Officer
15
26
  def run
16
27
  EM.error_handler {|e| Officer::Log.error e}
17
28
 
29
+ EM.kqueue = true if EM.kqueue?
30
+ EM.epoll = true if EM.epoll?
31
+
18
32
  EM::run do
19
33
  if @params[:stats]
20
34
  EM::PeriodicTimer.new(5) {Officer::LockStore.instance.log_state}
21
35
  end
22
36
 
23
- EM::start_server @params[:host], @params[:port], Connection::Connection
37
+ if @enable_shutdown_port
38
+ EM::start_server '127.0.0.1', 11501, ShutdownConnection
39
+ end
40
+
41
+ if @params[:socket_type] == 'TCP'
42
+ EM::start_server @params[:host], @params[:port], Connection::Connection
43
+ else
44
+ EM::start_unix_domain_server @params[:socket_file], Connection::Connection
45
+ end
46
+
47
+ set_running(true)
24
48
  end
49
+
50
+ set_running(false)
51
+ end
52
+
53
+ def running?
54
+ @semaphore.synchronize {@running}
55
+ end
56
+
57
+ private
58
+
59
+ def set_running value
60
+ @semaphore.synchronize {@running = value}
25
61
  end
26
62
  end
27
63
 
28
- end
64
+ end
data/lib/officer.rb CHANGED
@@ -3,6 +3,7 @@ require 'singleton'
3
3
  require 'set'
4
4
  require 'logger'
5
5
  require 'delegate'
6
+ require 'thread'
6
7
 
7
8
  # Gems.
8
9
  require 'rubygems'
@@ -17,5 +18,6 @@ require 'officer/log'
17
18
  require 'officer/commands'
18
19
  require 'officer/connection'
19
20
  require 'officer/lock_store'
21
+ require 'officer/runner'
20
22
  require 'officer/server'
21
23
  require 'officer/client'
data/officer.gemspec CHANGED
@@ -4,15 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{officer}
8
- s.version = "0.8.6"
7
+ s.name = "officer"
8
+ s.version = "0.9.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chad Remesch"]
12
- s.date = %q{2011-01-22}
13
- s.default_executable = %q{officer}
14
- s.description = %q{Officer is designed to help you coordinate distributed processes and avoid race conditions.}
15
- s.email = %q{chad@remesch.com}
12
+ s.date = "2012-03-16"
13
+ s.description = "Officer is designed to help you coordinate distributed processes and avoid race conditions."
14
+ s.email = "chad@remesch.com"
16
15
  s.executables = ["officer"]
17
16
  s.extra_rdoc_files = [
18
17
  "LICENSE",
@@ -22,6 +21,7 @@ Gem::Specification.new do |s|
22
21
  ".autotest",
23
22
  ".document",
24
23
  ".rspec",
24
+ "CONTRIBUTORS",
25
25
  "LICENSE",
26
26
  "README.markdown",
27
27
  "Rakefile",
@@ -33,23 +33,19 @@ Gem::Specification.new do |s|
33
33
  "lib/officer/connection.rb",
34
34
  "lib/officer/lock_store.rb",
35
35
  "lib/officer/log.rb",
36
+ "lib/officer/runner.rb",
36
37
  "lib/officer/server.rb",
37
38
  "officer.gemspec",
38
39
  "spec/integration/officer_spec.rb",
39
40
  "spec/spec_helper.rb"
40
41
  ]
41
- s.homepage = %q{http://github.com/chadrem/officer}
42
+ s.homepage = "http://github.com/chadrem/officer"
42
43
  s.licenses = ["MIT"]
43
44
  s.require_paths = ["lib"]
44
- s.rubygems_version = %q{1.3.7}
45
- s.summary = %q{Ruby lock server and client built on EventMachine.}
46
- s.test_files = [
47
- "spec/integration/officer_spec.rb",
48
- "spec/spec_helper.rb"
49
- ]
45
+ s.rubygems_version = "1.8.17"
46
+ s.summary = "Ruby lock server and client built on EventMachine."
50
47
 
51
48
  if s.respond_to? :specification_version then
52
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
49
  s.specification_version = 3
54
50
 
55
51
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -1,15 +1,17 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- require "benchmark"
3
2
 
4
3
  describe Officer do
5
4
  before do
6
- @server_thread = Thread.new do
7
- Officer::Server.new.run
8
- end
5
+ @server = Officer::Server.new :stats => true # :log_level => "debug"
6
+ @server.instance_variable_set("@enable_shutdown_port", true)
7
+ @server_thread = Thread.new {@server.run}
8
+ while !@server.running?; end
9
9
  end
10
10
 
11
11
  after do
12
- @server_thread.terminate
12
+ shutdown_socket = TCPSocket.new("127.0.0.1", 11501)
13
+ shutdown_socket.close
14
+ while @server.running?; end
13
15
  end
14
16
 
15
17
  describe "COMMAND: with_lock" do
@@ -42,7 +44,11 @@ describe Officer do
42
44
  it "should allow a client to reset all of its locks (release them all)" do
43
45
  @client.lock("testlock1")
44
46
  @client.lock("testlock2")
45
- @client.my_locks.should eq({"value"=>["testlock1", "testlock2"], "result"=>"my_locks"})
47
+ actual = @client.my_locks
48
+ expected = {"value"=>["testlock1", "testlock2"], "result"=>"my_locks"}
49
+ actual.class.should eq(Hash)
50
+ actual["value"].sort.should eq(expected["value"].sort)
51
+ actual["result"].should eq(expected["result"])
46
52
  @client.reset
47
53
  @client.my_locks.should eq({"value"=>[], "result"=>"my_locks"})
48
54
  end
@@ -90,8 +96,8 @@ describe Officer do
90
96
  it "should allow a client to see all the connections to a server" do
91
97
  connections = @client2.connections
92
98
 
93
- connections["value"]["127.0.0.1:#{@client1_src_port}"].should eq(["client1_testlock1", "client1_testlock2"])
94
- connections["value"]["127.0.0.1:#{@client2_src_port}"].should eq(["client2_testlock1", "client2_testlock2"])
99
+ connections["value"]["127.0.0.1:#{@client1_src_port}"].sort.should eq(["client1_testlock1", "client1_testlock2"].sort)
100
+ connections["value"]["127.0.0.1:#{@client2_src_port}"].sort.should eq(["client2_testlock1", "client2_testlock2"].sort)
95
101
  connections["value"].keys.length.should eq(2)
96
102
  connections["result"].should eq("connections")
97
103
  end
@@ -163,6 +169,15 @@ describe Officer do
163
169
  @client.unlock("testlock")
164
170
  @client.my_locks.should eq({"value"=>[], "result"=>"my_locks"})
165
171
  end
172
+
173
+ it "should inform the client they already have a lock if they previously locked it" do
174
+ @client.lock("testlock")
175
+ @client.lock("testlock").should eq({"result" => "already_acquired", "name" => "testlock"})
176
+ end
177
+
178
+ it "should inform the client they don't have a lock if they try to unlock a lock that they don't have" do
179
+ @client.unlock("testlock").should eq({"result" => "release_failed", "name" => "testlock"})
180
+ end
166
181
  end
167
182
 
168
183
  describe "locking options" do
@@ -190,6 +205,12 @@ describe Officer do
190
205
  )
191
206
  end
192
207
 
208
+ it "should allow a client to set an instant timeout when obtaining a lock (block syntax)" do
209
+ lambda {
210
+ @client2.with_lock("testlock", :timeout => 0){}
211
+ }.should raise_error(Officer::LockTimeoutError, "queue=127.0.0.1:#{@client1_src_port}")
212
+ end
213
+
193
214
  it "should allow a client to set a positive integer timeout when obtaining a lock" do
194
215
  time = Benchmark.realtime do
195
216
  @client2.lock("testlock", :timeout => 1).should eq(
@@ -203,43 +224,47 @@ describe Officer do
203
224
 
204
225
  describe "OPTION: queue_max" do
205
226
  before do
206
- end
207
-
208
- after do
209
- @thread1.terminate
210
- @thread2.terminate
211
- end
212
-
213
- it "should allow a client to abort when obtaining a lock if too many other clients are waiting for the same lock" do
214
227
  @client1 = Officer::Client.new
215
228
  @client1.lock("testlock")
216
229
 
217
230
  @thread1 = Thread.new {
218
231
  @client2 = Officer::Client.new
219
232
  @client2.lock("testlock")
220
- raise "This should never execute since the lock request should block"
221
233
  }
222
234
 
223
235
  @thread2 = Thread.new {
224
236
  @client3 = Officer::Client.new
225
237
  @client3.lock("testlock")
226
- raise "This should never execute since the lock request should block"
227
238
  }
228
239
 
229
- sleep(0.25) # Allow thread 1 & 2 time to run.
240
+ @client4 = Officer::Client.new
241
+ while @client4.locks["value"]["testlock"].count != 3; end
230
242
 
231
- @thread1.status.should eq("sleep")
232
- @thread2.status.should eq("sleep")
243
+ @client1_src_port = @client1.instance_variable_get('@socket').addr[1]
244
+ @client2_src_port = @client2.instance_variable_get('@socket').addr[1]
245
+ @client3_src_port = @client3.instance_variable_get('@socket').addr[1]
246
+ end
233
247
 
234
- client1_src_port = @client1.instance_variable_get('@socket').addr[1]
235
- client2_src_port = @client2.instance_variable_get('@socket').addr[1]
236
- client3_src_port = @client3.instance_variable_get('@socket').addr[1]
248
+ after do
249
+ end
237
250
 
238
- @client4 = Officer::Client.new
239
- @client4.lock("testlock", :queue_max => 3).should eq(
240
- {"result" => "queue_maxed", "name" => "testlock", "queue" =>
241
- ["127.0.0.1:#{client1_src_port}", "127.0.0.1:#{client2_src_port}", "127.0.0.1:#{client3_src_port}"]}
242
- )
251
+ it "should allow a client to abort lock acquisition if the wait queue is too long" do
252
+ actual = @client4.lock("testlock", :queue_max => 3)
253
+ expected = {
254
+ "result" => "queue_maxed",
255
+ "name" => "testlock",
256
+ "queue" => ["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}", "127.0.0.1:#{@client3_src_port}"]
257
+ }
258
+ actual.class.should eq(Hash)
259
+ actual["result"].should eq(expected["result"])
260
+ actual["name"].should eq(expected["name"])
261
+ actual["queue"].sort.should eq(expected["queue"].sort)
262
+ end
263
+
264
+ it "should allow a client to abort lock acquisition if the wait queue is too long (block syntax)" do
265
+ lambda {
266
+ @client4.with_lock("testlock", :queue_max => 3) {}
267
+ }.should raise_error(Officer::LockQueuedMaxError)
243
268
  end
244
269
  end
245
270
 
@@ -260,5 +285,29 @@ describe Officer do
260
285
  end
261
286
  end
262
287
  end
288
+
289
+ describe "EXPERIMENTAL: server support for non-blocking clients attempting to release a queued lock request" do
290
+ before do
291
+ @client = Officer::Client.new
292
+
293
+ @socket = TCPSocket.new "127.0.0.1", 11500
294
+ @socket = TCPSocket.new "127.0.0.1", 11500
295
+ end
296
+
297
+ after do
298
+ @client.send("disconnect")
299
+ @client = nil
300
+
301
+ @socket.close
302
+ @socket = nil
303
+ end
304
+
305
+ it "should inform the client that their request has been de-queued" do
306
+ @client.lock("testlock")
307
+ @socket.write("{\"command\":\"lock\",\"name\":\"testlock\"}\n")
308
+ @socket.write("{\"command\":\"unlock\",\"name\":\"testlock\"}\n")
309
+ JSON.parse(@socket.gets("\n").chomp).should eq({"result" => "released", "name" => "testlock"})
310
+ end
311
+ end
263
312
  end
264
313
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
  require 'rspec'
4
+ require "benchmark"
5
+ require "json"
4
6
  require 'officer'
5
7
 
6
8
  # Requires supporting files with custom matchers and macros, etc,
@@ -8,5 +10,4 @@ require 'officer'
8
10
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
11
 
10
12
  RSpec.configure do |config|
11
-
12
13
  end
metadata CHANGED
@@ -1,108 +1,85 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: officer
3
- version: !ruby/object:Gem::Version
4
- hash: 51
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 8
9
- - 6
10
- version: 0.8.6
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Chad Remesch
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-01-22 00:00:00 -05:00
19
- default_executable: officer
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-03-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: rspec
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70245430020780 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - "="
28
- - !ruby/object:Gem::Version
29
- hash: 31
30
- segments:
31
- - 2
32
- - 4
33
- - 0
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
34
21
  version: 2.4.0
35
22
  type: :development
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: eventmachine
39
23
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70245430020780
25
+ - !ruby/object:Gem::Dependency
26
+ name: eventmachine
27
+ requirement: &70245430020300 !ruby/object:Gem::Requirement
41
28
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- hash: 3
46
- segments:
47
- - 0
48
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
49
33
  type: :runtime
50
- version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: json
53
34
  prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *70245430020300
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ requirement: &70245430019820 !ruby/object:Gem::Requirement
55
39
  none: false
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- hash: 3
60
- segments:
61
- - 0
62
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
63
44
  type: :runtime
64
- version_requirements: *id003
65
- - !ruby/object:Gem::Dependency
66
- name: daemons
67
45
  prerelease: false
68
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *70245430019820
47
+ - !ruby/object:Gem::Dependency
48
+ name: daemons
49
+ requirement: &70245430019320 !ruby/object:Gem::Requirement
69
50
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
77
55
  type: :runtime
78
- version_requirements: *id004
79
- - !ruby/object:Gem::Dependency
80
- name: choice
81
56
  prerelease: false
82
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *70245430019320
58
+ - !ruby/object:Gem::Dependency
59
+ name: choice
60
+ requirement: &70245430018760 !ruby/object:Gem::Requirement
83
61
  none: false
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- hash: 3
88
- segments:
89
- - 0
90
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
91
66
  type: :runtime
92
- version_requirements: *id005
93
- description: Officer is designed to help you coordinate distributed processes and avoid race conditions.
67
+ prerelease: false
68
+ version_requirements: *70245430018760
69
+ description: Officer is designed to help you coordinate distributed processes and
70
+ avoid race conditions.
94
71
  email: chad@remesch.com
95
- executables:
72
+ executables:
96
73
  - officer
97
74
  extensions: []
98
-
99
- extra_rdoc_files:
75
+ extra_rdoc_files:
100
76
  - LICENSE
101
77
  - README.markdown
102
- files:
78
+ files:
103
79
  - .autotest
104
80
  - .document
105
81
  - .rspec
82
+ - CONTRIBUTORS
106
83
  - LICENSE
107
84
  - README.markdown
108
85
  - Rakefile
@@ -114,44 +91,34 @@ files:
114
91
  - lib/officer/connection.rb
115
92
  - lib/officer/lock_store.rb
116
93
  - lib/officer/log.rb
94
+ - lib/officer/runner.rb
117
95
  - lib/officer/server.rb
118
96
  - officer.gemspec
119
97
  - spec/integration/officer_spec.rb
120
98
  - spec/spec_helper.rb
121
- has_rdoc: true
122
99
  homepage: http://github.com/chadrem/officer
123
- licenses:
100
+ licenses:
124
101
  - MIT
125
102
  post_install_message:
126
103
  rdoc_options: []
127
-
128
- require_paths:
104
+ require_paths:
129
105
  - lib
130
- required_ruby_version: !ruby/object:Gem::Requirement
106
+ required_ruby_version: !ruby/object:Gem::Requirement
131
107
  none: false
132
- requirements:
133
- - - ">="
134
- - !ruby/object:Gem::Version
135
- hash: 3
136
- segments:
137
- - 0
138
- version: "0"
139
- required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
113
  none: false
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- hash: 3
145
- segments:
146
- - 0
147
- version: "0"
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
148
118
  requirements: []
149
-
150
119
  rubyforge_project:
151
- rubygems_version: 1.3.7
120
+ rubygems_version: 1.8.17
152
121
  signing_key:
153
122
  specification_version: 3
154
123
  summary: Ruby lock server and client built on EventMachine.
155
- test_files:
156
- - spec/integration/officer_spec.rb
157
- - spec/spec_helper.rb
124
+ test_files: []