kjess 1.1.0 → 1.2.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.
@@ -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