kjess 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module KJess
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
4
4
 
5
5
  require 'kjess/connection'
@@ -65,7 +65,7 @@ module KJess
65
65
  v = KJess::Request::Version.new
66
66
  r = send_recv( v )
67
67
  return r.version if Response::Version === r
68
- raise KJess::Error, "Unexpected Response from VERSION command"
68
+ raise KJess::ProtocolError, "Unexpected Response from VERSION command"
69
69
  end
70
70
 
71
71
  # Public: Add an item to the given queue
@@ -98,7 +98,7 @@ module KJess
98
98
  g = KJess::Request::Get.new( opts )
99
99
 
100
100
  if opts[:wait_for]
101
- wait_for_in_seconds = opts[:wait_for] / 1000
101
+ wait_for_in_seconds = Float(opts[:wait_for]) / 1000.0
102
102
  else
103
103
  wait_for_in_seconds = 0.1
104
104
  end
@@ -236,7 +236,7 @@ module KJess
236
236
  # Returns a String.
237
237
  def status( update_to = nil )
238
238
  resp = send_recv( KJess::Request::Status.new( :update_to => update_to ) )
239
- raise KJess::Error, "Status command is not supported" if KJess::Response::ClientError === resp
239
+ raise KJess::ProtocolError, "Status command is not supported" if KJess::Response::ClientError === resp
240
240
  return resp.message
241
241
  end
242
242
 
@@ -256,7 +256,7 @@ module KJess
256
256
  # Returns a Hash
257
257
  def stats!
258
258
  stats = send_recv( KJess::Request::Stats.new )
259
- raise KJess::Error, "Problem receiving stats: #{stats.inspect}" unless KJess::Response::Stats === stats
259
+ raise KJess::ProtocolError, "Problem receiving stats: #{stats.inspect}" unless KJess::Response::Stats === stats
260
260
 
261
261
  h = stats.data
262
262
  dump_stats = send_recv( KJess::Request::DumpStats.new )
@@ -7,16 +7,39 @@ require 'kjess/socket'
7
7
  module KJess
8
8
  # Connection
9
9
  class Connection
10
- class Error < KJess::Error; end
10
+ class Error < KJess::NetworkError; end
11
+
12
+ # Public: Set a socket factory
13
+ #
14
+ # factory - an object that responds to #call(options) where options is
15
+ # a Hash.
16
+ #
17
+ # returns nothing
18
+ def self.socket_factory=(factory)
19
+ @socket_factory = factory
20
+ end
21
+
22
+ # Public: Return the socket factory
23
+ #
24
+ def self.socket_factory
25
+ @socket_factory ||= nil
26
+ @socket_factory.respond_to?(:call) ? @socket_factory : default_socket_factory
27
+ end
28
+
29
+ # Internal: Returns the default socket factory
30
+ #
31
+ def self.default_socket_factory
32
+ lambda { |options| KJess::Socket.connect(options) }
33
+ end
11
34
 
12
35
  # Public: The hostname/ip address to connect to.
13
36
  def host
14
- socket.host
37
+ @options[:host]
15
38
  end
16
39
 
17
40
  # Public: The port number to connect to. Default 22133
18
41
  def port
19
- socket.port
42
+ @options[:port]
20
43
  end
21
44
 
22
45
  # Public: The timeout for connecting in seconds. Defaults to 2
@@ -95,10 +118,12 @@ module KJess
95
118
  def socket
96
119
  close if @pid && @pid != Process.pid
97
120
  return @socket if @socket and not @socket.closed?
98
- @socket = Socket.connect( @options )
121
+ @socket = self.class.socket_factory.call(@options)
99
122
  @pid = Process.pid
100
123
  @read_buffer = ''
101
124
  return @socket
125
+ rescue => e
126
+ raise Error, "Could not connect to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
102
127
  end
103
128
 
104
129
  # Internal: close the socket if it is not already closed
@@ -127,9 +152,12 @@ module KJess
127
152
  def write( msg )
128
153
  $stderr.puts "--> #{msg}" if $DEBUG
129
154
  socket.write( msg )
130
- rescue KJess::Error
155
+ rescue KJess::NetworkError
131
156
  close
132
157
  raise
158
+ rescue => e
159
+ close
160
+ raise Error, "Could not write to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
133
161
  end
134
162
 
135
163
  # Internal: read a single line from the socket
@@ -148,7 +176,7 @@ module KJess
148
176
  break unless line.strip.length == 0
149
177
  end
150
178
  return line
151
- rescue KJess::Error
179
+ rescue KJess::NetworkError
152
180
  close
153
181
  raise
154
182
  rescue EOFError
@@ -173,9 +201,12 @@ module KJess
173
201
 
174
202
  $stderr.puts "<-- #{result}" if $DEBUG
175
203
  return result
176
- rescue KJess::Error
204
+ rescue KJess::NetworkError
177
205
  close
178
206
  raise
207
+ rescue => e
208
+ close
209
+ raise Error, "Could not read from #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
179
210
  end
180
211
  end
181
212
  end
@@ -1,5 +1,8 @@
1
1
  module KJess
2
2
  class Error < ::StandardError; end
3
- class ClientError < Error; end
4
- class ServerError < Error; end
3
+ class NetworkError < Error; end
4
+
5
+ class ProtocolError < Error; end
6
+ class ClientError < ProtocolError; end
7
+ class ServerError < ProtocolError; end
5
8
  end
@@ -7,7 +7,7 @@ class KJess::Response
7
7
  end
8
8
 
9
9
  def exception
10
- raise KJess::Error
10
+ raise KJess::ClientError
11
11
  end
12
12
  end
13
13
  end
@@ -24,7 +24,7 @@ class KJess::Response
24
24
  when "END"
25
25
  break
26
26
  else
27
- raise KJess::Error, "Unknown line '#{line.strip}' from STAT command"
27
+ raise KJess::ClientError, "Unknown line '#{line.strip}' from STAT command"
28
28
  end
29
29
  end while line = connection.readline
30
30
 
@@ -3,7 +3,7 @@ require 'socket'
3
3
  module KJess
4
4
  # Socket: A specialized socket that has been configure
5
5
  class Socket
6
- class Error < KJess::Error; end
6
+ class Error < KJess::NetworkError; end
7
7
  class Timeout < Error; end
8
8
 
9
9
  # Internal:
@@ -21,7 +21,7 @@ module KJess
21
21
  # Internal:
22
22
  # The host this socket is connected to
23
23
  attr_reader :host
24
-
24
+
25
25
  # Internal:
26
26
  # The port this socket is connected to
27
27
  attr_reader :port
@@ -98,7 +98,7 @@ module KJess
98
98
  end
99
99
 
100
100
  # Internal: Low level socket allocation and option configuration
101
- #
101
+ #
102
102
  # Using the options from the initializer, a new ::Socket is created that
103
103
  # is:
104
104
  #
@@ -167,12 +167,12 @@ module KJess
167
167
  conn_error = lambda { raise errors.first }
168
168
  sock = nil
169
169
 
170
- addrs.find( conn_error ) do |addr|
170
+ addrs.find( conn_error ) do |addr|
171
171
  sock = connect_or_error( addr, deadline, errors )
172
172
  end
173
173
  return sock
174
174
  end
175
-
175
+
176
176
  # Internal: Connect to the destination or raise an error.
177
177
  #
178
178
  # Connect to the address or capture the error of the connection
@@ -208,7 +208,7 @@ module KJess
208
208
  sock.connect_nonblock( sockaddr )
209
209
  return sock
210
210
  rescue Errno::EINPROGRESS
211
- if IO.select(nil, [sock], nil, timeout).nil? then
211
+ if !wait_writable(timeout, sock)
212
212
  raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds"
213
213
  end
214
214
  return connect_nonblock_finalize( sock, sockaddr )
@@ -218,12 +218,13 @@ module KJess
218
218
 
219
219
 
220
220
  # Internal: Make sure that a non-blocking connect has truely connected.
221
- #
221
+ #
222
222
  # Ensure that the given socket is actually connected to the given adddress.
223
223
  #
224
224
  # Returning the socket if it is and raising an Error if it isn't.
225
225
  def connect_nonblock_finalize( sock, sockaddr )
226
226
  sock.connect_nonblock( sockaddr )
227
+ return sock
227
228
  rescue Errno::EISCONN
228
229
  return sock
229
230
  rescue => ex
@@ -260,7 +261,7 @@ module KJess
260
261
  def readpartial(maxlen, outbuf = nil)
261
262
  return socket.read_nonblock(maxlen, outbuf)
262
263
  rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET
263
- if IO.select([@socket], nil, nil, read_timeout)
264
+ if wait_readable(read_timeout)
264
265
  retry
265
266
  else
266
267
  raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds"
@@ -276,16 +277,24 @@ module KJess
276
277
  #
277
278
  # returns nothing
278
279
  def write( buf )
279
- until buf.length == 0
280
+ until buf.nil? or (buf.length == 0) do
280
281
  written = socket.write_nonblock(buf)
281
282
  buf = buf[written, buf.length]
282
283
  end
283
284
  rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET
284
- if IO.select(nil, [socket], nil, write_timeout)
285
+ if wait_writable(write_timeout)
285
286
  retry
286
287
  else
287
288
  raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds"
288
289
  end
289
290
  end
291
+
292
+ def wait_writable(timeout = nil, socket = @socket)
293
+ IO.select(nil, [socket], nil, timeout || write_timeout)
294
+ end
295
+
296
+ def wait_readable(timeout = nil, socket = @socket)
297
+ IO.select([socket], nil, nil, timeout || read_timeout)
298
+ end
290
299
  end
291
300
  end
@@ -128,7 +128,7 @@ describe KJess::Client do
128
128
  @client.set( "get_q", "get item 1" )
129
129
  @client.set( "get_q", "get item 2" )
130
130
  @client.reserve( "get_q" )
131
- lambda { @client.get( "get_q" ) }.must_raise KJess::Error
131
+ lambda { @client.get( "get_q" ) }.must_raise KJess::ClientError
132
132
  end
133
133
 
134
134
  end
@@ -292,14 +292,14 @@ describe KJess::Client do
292
292
  describe "connecting to a server on a port that isn't listening" do
293
293
  it "throws an exception" do
294
294
  c = KJess::Connection.new '127.0.0.1', 65521
295
- lambda { c.socket }.must_raise KJess::Socket::Error
295
+ lambda { c.socket }.must_raise KJess::Connection::Error
296
296
  end
297
297
  end
298
298
 
299
299
  describe "connecting to a server that isn't responding" do
300
300
  it "throws an exception" do
301
301
  c = KJess::Connection.new '127.1.1.1', 65521, :timeout => 0.5
302
- lambda { c.socket }.must_raise KJess::Socket::Timeout
302
+ lambda { c.socket }.must_raise KJess::Connection::Error
303
303
  end
304
304
  end
305
305
 
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe KJess::Connection do
4
+
5
+ it "Returns a callable for the factory" do
6
+ KJess::Connection.socket_factory.respond_to?(:call).must_equal true
7
+ end
8
+
9
+ it "Default Factory returns a KJess::Socket" do
10
+ factory = KJess::Connection.socket_factory
11
+ s = factory.call( :port => KJess::Spec.memcache_port, :host => 'localhost' )
12
+ s.instance_of?(KJess::Socket).must_equal true
13
+ end
14
+
15
+ end
@@ -0,0 +1,271 @@
1
+ # vim: syntax=ruby
2
+ require 'rake/clean'
3
+ #------------------------------------------------------------------------------
4
+ # If you want to Develop on this project just run 'rake develop' and you'll
5
+ # have all you need to get going. If you want to use bundler for development,
6
+ # then run 'rake develop:using_bundler'
7
+ #------------------------------------------------------------------------------
8
+ namespace :develop do
9
+
10
+ # Install all the development and runtime dependencies of this gem using the
11
+ # gemspec.
12
+ task :default do
13
+ require 'rubygems/dependency_installer'
14
+ installer = Gem::DependencyInstaller.new
15
+
16
+ This.set_coverage_gem
17
+
18
+ puts "Installing gem depedencies needed for development"
19
+ This.platform_gemspec.dependencies.each do |dep|
20
+ if dep.matching_specs.empty? then
21
+ puts "Installing : #{dep}"
22
+ installer.install dep
23
+ else
24
+ puts "Skipping : #{dep} -> already installed #{dep.matching_specs.first.full_name}"
25
+ end
26
+ end
27
+ puts "\n\nNow run 'rake test'"
28
+ end
29
+
30
+ # Create a Gemfile that just references the gemspec
31
+ file 'Gemfile' => :gemspec do
32
+ File.open( "Gemfile", "w+" ) do |f|
33
+ f.puts 'source :rubygems'
34
+ f.puts 'gemspec'
35
+ end
36
+ end
37
+
38
+ desc "Create a bundler Gemfile"
39
+ task :using_bundler => 'Gemfile' do
40
+ puts "Now you can 'bundle'"
41
+ end
42
+
43
+ # Gemfiles are build artifacts
44
+ CLOBBER << FileList['Gemfile*']
45
+ end
46
+ desc "Boostrap development"
47
+ task :develop => "develop:default"
48
+
49
+ #------------------------------------------------------------------------------
50
+ # Minitest - standard TestTask
51
+ #------------------------------------------------------------------------------
52
+ begin
53
+ require 'rake/testtask'
54
+ Rake::TestTask.new( :test ) do |t|
55
+ t.ruby_opts = %w[ -w -rubygems ]
56
+ t.libs = %w[ lib spec ]
57
+ t.pattern = "spec/**/*_spec.rb"
58
+ end
59
+
60
+ task :test_requirements
61
+ task :test => :test_requirements
62
+ task :default => :test
63
+ rescue LoadError
64
+ This.task_warning( 'test' )
65
+ end
66
+
67
+ #------------------------------------------------------------------------------
68
+ # RDoc - standard rdoc rake task, although we must make sure to use a more
69
+ # recent version of rdoc since it is the one that has 'tomdoc' markup
70
+ #------------------------------------------------------------------------------
71
+ begin
72
+ gem 'rdoc' # otherwise we get the wrong task from stdlib
73
+ require 'rdoc/task'
74
+ RDoc::Task.new do |t|
75
+ t.markup = 'tomdoc'
76
+ t.rdoc_dir = 'doc'
77
+ t.main = 'README.md'
78
+ t.title = "#{This.name} #{This.version}"
79
+ t.rdoc_files.include( FileList['*.{rdoc,md,txt}'], FileList['ext/**/*.c'],
80
+ FileList['lib/**/*.rb'] )
81
+ end
82
+ rescue StandardError, LoadError
83
+ This.task_warning( 'rdoc' )
84
+ end
85
+
86
+ #------------------------------------------------------------------------------
87
+ # Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so
88
+ # for the moment only rcov is listed.
89
+ #------------------------------------------------------------------------------
90
+ if RUBY_VERSION < "1.9.0"
91
+ begin
92
+ require 'rcov/rcovtask'
93
+ Rcov::RcovTask.new( 'coverage' ) do |t|
94
+ t.libs << 'spec'
95
+ t.pattern = 'spec/**/*_spec.rb'
96
+ t.verbose = true
97
+ t.rcov_opts << "-x ^/" # remove all the global files
98
+ t.rcov_opts << "--sort coverage" # so we see the worst files at the top
99
+ end
100
+ rescue LoadError
101
+ This.task_warning( 'rcov' )
102
+ end
103
+ else
104
+ begin
105
+ require 'simplecov'
106
+ desc 'Run tests with code coverage'
107
+ task :coverage do
108
+ ENV['COVERAGE'] = 'true'
109
+ Rake::Task[:test].execute
110
+ end
111
+ CLOBBER << FileList["coverage"]
112
+ rescue LoadError
113
+ This.task_warning( 'simplecov' )
114
+ end
115
+ end
116
+
117
+ #------------------------------------------------------------------------------
118
+ # Manifest - We want an explicit list of thos files that are to be packaged in
119
+ # the gem. Most of this is from Hoe.
120
+ #------------------------------------------------------------------------------
121
+ namespace 'manifest' do
122
+ desc "Check the manifest"
123
+ task :check => :clean do
124
+ files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort
125
+ files = files.select{ |f| File.file?( f ) }
126
+
127
+ tmp = "Manifest.tmp"
128
+ File.open( tmp, 'w' ) do |f|
129
+ f.puts files.join("\n")
130
+ end
131
+
132
+ begin
133
+ sh "diff -du Manifest.txt #{tmp}"
134
+ ensure
135
+ rm tmp
136
+ end
137
+ puts "Manifest looks good"
138
+ end
139
+
140
+ desc "Generate the manifest"
141
+ task :generate => :clean do
142
+ files = %x[ git ls-files ].split("\n").sort
143
+ files.reject! { |f| f =~ This.exclude_from_manifest }
144
+ File.open( "Manifest.txt", "w" ) do |f|
145
+ f.puts files.join("\n")
146
+ end
147
+ end
148
+ end
149
+
150
+ #------------------------------------------------------------------------------
151
+ # Fixme - look for fixmes and report them
152
+ #------------------------------------------------------------------------------
153
+ namespace :fixme do
154
+ task :default => 'manifest:check' do
155
+ This.manifest.each do |file|
156
+ next if file == __FILE__
157
+ next unless file =~ %r/(txt|rb|md|rdoc|css|html|xml|css)\Z/
158
+ puts "FIXME: Rename #{file}" if file =~ /fixme/i
159
+ IO.readlines( file ).each_with_index do |line, idx|
160
+ prefix = "FIXME: #{file}:#{idx+1}".ljust(42)
161
+ puts "#{prefix} => #{line.strip}" if line =~ /fixme/i
162
+ end
163
+ end
164
+ end
165
+
166
+ def fixme_project_root
167
+ This.project_path( '../fixme' )
168
+ end
169
+
170
+ def fixme_project_path( subtree )
171
+ fixme_project_root.join( subtree )
172
+ end
173
+
174
+ def local_fixme_files
175
+ This.manifest.select { |p| p =~ %r|^tasks/| }
176
+ end
177
+
178
+ def outdated_fixme_files
179
+ local_fixme_files.reject do |local|
180
+ upstream = fixme_project_path( local )
181
+ Digest::SHA256.file( local ) == Digest::SHA256.file( upstream )
182
+ end
183
+ end
184
+
185
+ def fixme_up_to_date?
186
+ outdated_fixme_files.empty?
187
+ end
188
+
189
+ desc "See if the fixme tools are outdated"
190
+ task :outdated => :release_check do
191
+ if fixme_up_to_date? then
192
+ puts "Fixme files are up to date."
193
+ else
194
+ outdated_fixme_files.each do |f|
195
+ puts "#{f} is outdated"
196
+ end
197
+ end
198
+ end
199
+
200
+ desc "Update outdated fixme files"
201
+ task :update => :release_check do
202
+ if fixme_up_to_date? then
203
+ puts "Fixme files are already up to date."
204
+ else
205
+ puts "Updating fixme files:"
206
+ outdated_fixme_files.each do |local|
207
+ upstream = fixme_project_path( local )
208
+ puts " * #{local}"
209
+ FileUtils.cp( upstream, local )
210
+ end
211
+ puts "Use your git commands as appropriate."
212
+ end
213
+ end
214
+ end
215
+ desc "Look for fixmes and report them"
216
+ task :fixme => "fixme:default"
217
+
218
+ #------------------------------------------------------------------------------
219
+ # Gem Specification
220
+ #------------------------------------------------------------------------------
221
+ # Really this is only here to support those who use bundler
222
+ desc "Build the #{This.name}.gemspec file"
223
+ task :gemspec do
224
+ File.open( This.gemspec_file, "wb+" ) do |f|
225
+ f.write This.platform_gemspec.to_ruby
226
+ end
227
+ end
228
+
229
+ # the gemspec is also a dev artifact and should not be kept around.
230
+ CLOBBER << This.gemspec_file.to_s
231
+
232
+ # .rbc files from ruby 2.0
233
+ CLOBBER << FileList["**/*.rbc"]
234
+
235
+ # The standard gem packaging task, everyone has it.
236
+ require 'rubygems/package_task'
237
+ Gem::PackageTask.new( This.platform_gemspec ) do
238
+ # nothing
239
+ end
240
+
241
+ #------------------------------------------------------------------------------
242
+ # Release - the steps we go through to do a final release, this is pulled from
243
+ # a compbination of mojombo's rakegem, hoe and hoe-git
244
+ #
245
+ # 1) make sure we are on the master branch
246
+ # 2) make sure there are no uncommitted items
247
+ # 3) check the manifest and make sure all looks good
248
+ # 4) build the gem
249
+ # 5) do an empty commit to have the commit message of the version
250
+ # 6) tag that commit as the version
251
+ # 7) push master
252
+ # 8) push the tag
253
+ # 7) pus the gem
254
+ #------------------------------------------------------------------------------
255
+ task :release_check do
256
+ unless `git branch` =~ /^\* master$/
257
+ abort "You must be on the master branch to release!"
258
+ end
259
+ unless `git status` =~ /^nothing to commit/m
260
+ abort "Nope, sorry, you have unfinished business"
261
+ end
262
+ end
263
+
264
+ desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org"
265
+ task :release => [ :release_check, 'manifest:check', :gem ] do
266
+ sh "git commit --allow-empty -a -m 'Release #{This.version}'"
267
+ sh "git tag -a -m 'v#{This.version}' v#{This.version}"
268
+ sh "git push origin master"
269
+ sh "git push origin v#{This.version}"
270
+ sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
271
+ end