kjess 1.0.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.
Files changed (54) hide show
  1. data/CONTRIBUTING.md +45 -0
  2. data/HISTORY.rdoc +5 -0
  3. data/LICENSE +16 -0
  4. data/Manifest.txt +53 -0
  5. data/README.rdoc +44 -0
  6. data/Rakefile +337 -0
  7. data/example/client_test.rb +73 -0
  8. data/lib/kjess.rb +11 -0
  9. data/lib/kjess/client.rb +289 -0
  10. data/lib/kjess/connection.rb +119 -0
  11. data/lib/kjess/error.rb +5 -0
  12. data/lib/kjess/protocol.rb +76 -0
  13. data/lib/kjess/request.rb +31 -0
  14. data/lib/kjess/request/delete.rb +11 -0
  15. data/lib/kjess/request/dump_stats.rb +7 -0
  16. data/lib/kjess/request/flush.rb +11 -0
  17. data/lib/kjess/request/flush_all.rb +7 -0
  18. data/lib/kjess/request/get.rb +21 -0
  19. data/lib/kjess/request/quit.rb +7 -0
  20. data/lib/kjess/request/reload.rb +7 -0
  21. data/lib/kjess/request/set.rb +19 -0
  22. data/lib/kjess/request/shutdown.rb +7 -0
  23. data/lib/kjess/request/stats.rb +7 -0
  24. data/lib/kjess/request/status.rb +8 -0
  25. data/lib/kjess/request/version.rb +8 -0
  26. data/lib/kjess/response.rb +76 -0
  27. data/lib/kjess/response/client_error.rb +19 -0
  28. data/lib/kjess/response/deleted.rb +5 -0
  29. data/lib/kjess/response/dumped_stats.rb +48 -0
  30. data/lib/kjess/response/end.rb +5 -0
  31. data/lib/kjess/response/eof.rb +5 -0
  32. data/lib/kjess/response/error.rb +13 -0
  33. data/lib/kjess/response/flushed_all_queues.rb +6 -0
  34. data/lib/kjess/response/not_found.rb +5 -0
  35. data/lib/kjess/response/not_stored.rb +5 -0
  36. data/lib/kjess/response/reloaded_config.rb +6 -0
  37. data/lib/kjess/response/server_error.rb +18 -0
  38. data/lib/kjess/response/stats.rb +60 -0
  39. data/lib/kjess/response/stored.rb +5 -0
  40. data/lib/kjess/response/unknown.rb +3 -0
  41. data/lib/kjess/response/value.rb +26 -0
  42. data/lib/kjess/response/version.rb +10 -0
  43. data/lib/kjess/stats_cache.rb +31 -0
  44. data/spec/client_spec.rb +265 -0
  45. data/spec/kestrel_server.rb +137 -0
  46. data/spec/request/set_spec.rb +13 -0
  47. data/spec/request/version_spec.rb +17 -0
  48. data/spec/request_spec.rb +30 -0
  49. data/spec/response/client_error_spec.rb +17 -0
  50. data/spec/spec_helper.rb +12 -0
  51. data/spec/utils.rb +18 -0
  52. data/spec/version_spec.rb +9 -0
  53. data/tasks/kestrel.rake +70 -0
  54. metadata +193 -0
@@ -0,0 +1,5 @@
1
+ module KJess
2
+ class Error < ::StandardError; end
3
+ class ClientError < Error; end
4
+ class ServerError < Error; end
5
+ end
@@ -0,0 +1,76 @@
1
+ module KJess
2
+ # Protocl is the base class that all Kestrel requests and responses are
3
+ # developed on. it defines the DSL for creating the Request and Response
4
+ # objects that make up the Protocol.
5
+ #
6
+ class Protocol
7
+
8
+ CRLF = "\r\n"
9
+
10
+ class << self
11
+ # Internal: The keyword that starts this protocol message
12
+ #
13
+ # name - the keyword to define this portion of the protocol
14
+ #
15
+ # Returns the name
16
+ def keyword( name = nil )
17
+ if name then
18
+ register( name )
19
+ @keyword = name
20
+ end
21
+ @keyword
22
+ end
23
+
24
+ # Internal: define or return the arity of this protocol item
25
+ #
26
+ # arity - the number of args this protocol item has
27
+ #
28
+ # Returns the arity
29
+ def arity( a = nil )
30
+ @arity = a if a
31
+ @arity
32
+ end
33
+
34
+ # Internal: register this protocol item with its registry
35
+ #
36
+ # name - the name under which to register the protocol
37
+ #
38
+ # Returns nothing
39
+ def register( name )
40
+ registry[name] ||= self
41
+ end
42
+ end
43
+
44
+ attr_reader :args
45
+ attr_reader :raw_args
46
+
47
+ def initialize( opts = {} )
48
+ @raw_args = opts
49
+ @args = parse_options_to_args( opts ) || []
50
+ end
51
+
52
+ # Internal: callback that child classes may use to further parse the
53
+ # initialization arguments
54
+ #
55
+ # Returns Array
56
+ def parse_options_to_args( opts ); end
57
+
58
+ # Internal: Convert the object to its protocol serialized format.
59
+ #
60
+ # This may be overridden in child classes
61
+ #
62
+ # Return a String
63
+ def to_protocol
64
+ s = keyword
65
+ s += " #{args.join(' ')}" unless args.empty?
66
+ s += CRLF
67
+ end
68
+
69
+ # Internal: return the keyword
70
+ #
71
+ # Returns a String
72
+ def keyword
73
+ self.class.keyword
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,31 @@
1
+ module KJess
2
+ # Request is the base Request Protocol. All Requests made to the Kestrel
3
+ # server are decendants of this class.
4
+ #
5
+ # The Request class holds the registry of all the Request decendent classes.
6
+ class Request < Protocol
7
+ Registry = Hash.new
8
+
9
+ def self.registry
10
+ Registry
11
+ end
12
+
13
+ def self.valid_responses( list = nil )
14
+ @valid_responses = [ list ].flatten if list
15
+ @valid_responses
16
+ end
17
+ end
18
+ end
19
+ require 'kjess/response'
20
+ require 'kjess/request/flush'
21
+ require 'kjess/request/flush_all'
22
+ require 'kjess/request/delete'
23
+ require 'kjess/request/dump_stats'
24
+ require 'kjess/request/get'
25
+ require 'kjess/request/quit'
26
+ require 'kjess/request/reload'
27
+ require 'kjess/request/set'
28
+ require 'kjess/request/shutdown'
29
+ require 'kjess/request/stats'
30
+ require 'kjess/request/status'
31
+ require 'kjess/request/version'
@@ -0,0 +1,11 @@
1
+ class KJess::Request
2
+ class Delete < KJess::Request
3
+ keyword 'DELETE'
4
+ arity 1
5
+ valid_responses [ KJess::Response::Deleted, KJess::Response::NotFound ]
6
+
7
+ def parse_options_to_args( opts )
8
+ [ opts[:queue_name] ]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class DumpStats < KJess::Request
3
+ keyword 'DUMP_STATS'
4
+ arity 0
5
+ valid_responses [ KJess::Response::DumpedStats ]
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ class KJess::Request
2
+ class Flush < KJess::Request
3
+ keyword 'FLUSH'
4
+ arity 1
5
+ valid_responses [ KJess::Response::End ]
6
+
7
+ def parse_options_to_args( opts )
8
+ [ opts[:queue_name] ]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class FlushAll < KJess::Request
3
+ keyword 'FLUSH_ALL'
4
+ arity 0
5
+ valid_responses [ KJess::Response::FlushedAllQueues ]
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ class KJess::Request
2
+ class Get < KJess::Request
3
+ keyword 'GET'
4
+ arity 1
5
+ valid_responses [ KJess::Response::Value ]
6
+
7
+ def parse_options_to_args( opts )
8
+ a = [ opts[:queue_name] ]
9
+
10
+ a << "t=#{opts[:wait_for]}" if opts[:wait_for]
11
+
12
+ [ :open, :close, :abort, :peek ].each do |o|
13
+ a << o.to_s if opts[o]
14
+ end
15
+
16
+ [ a.join("/") ]
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class Quit < KJess::Request
3
+ keyword 'QUIT'
4
+ arity 0
5
+ valid_responses [ KJess::Response::Eof ]
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class Reload < KJess::Request
3
+ keyword 'RELOAD'
4
+ arity 0
5
+ valid_responses [ KJess::Response::ReloadedConfig ]
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ class KJess::Request
2
+ class Set < KJess::Request
3
+ keyword 'SET'
4
+ arity 4
5
+ valid_responses [ KJess::Response::Stored, KJess::Response::NotStored ]
6
+
7
+ attr_reader :data
8
+
9
+ def parse_options_to_args( opts )
10
+ @data = opts[:data].to_s
11
+ [ opts[:queue_name], 0, opts[:expiration] || 0 , data.bytesize ]
12
+ end
13
+
14
+ def to_protocol
15
+ s = super
16
+ s += "#{data}#{CRLF}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class Shutdown < KJess::Request
3
+ keyword 'SHUTDOWN'
4
+ arity 0
5
+ valid_responses [ KJess::Response::Eof ]
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class KJess::Request
2
+ class Stats < KJess::Request
3
+ keyword 'STATS'
4
+ arity 0
5
+ valid_responses [ KJess::Response::Stats ]
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ class KJess::Request
2
+ # This is not yet in a released version of Kestrel
3
+ class Status < KJess::Request
4
+ keyword 'STATUS'
5
+ arity 1
6
+ #valid_responses [ KJess::Response::Eof ]
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require 'kjess/response'
2
+ class KJess::Request
3
+ class Version < KJess::Request
4
+ keyword 'VERSION'
5
+ arity 0
6
+ valid_responses KJess::Response::Version
7
+ end
8
+ end
@@ -0,0 +1,76 @@
1
+ module KJess
2
+ # Response is the parent class of all Response derived objects that come back
3
+ # from the Kestrel server. It holds the registry of all the Response objects
4
+ # and is responsible for parsing the initial line from the Kestrel server and
5
+ # determinig which Response child object to instantiate.
6
+ class Response < Protocol
7
+ arity 0
8
+
9
+ Registry = Hash.new
10
+ def self.registry
11
+ Registry
12
+ end
13
+
14
+ # Internal: parse the string and create the appropriate Response child
15
+ # object.
16
+ #
17
+ # str - a String from the Kestrel server
18
+ #
19
+ # Returns a new Response child object
20
+ def self.parse( str )
21
+ keyword, *args = str.strip.split
22
+ klass = Registry.fetch( keyword, KJess::Response::Unknown )
23
+ klass.new( args )
24
+ end
25
+
26
+ # Internal: callback to create the @args member
27
+ #
28
+ # opts - the opts that were passed to initialize
29
+ #
30
+ # Returns an Array
31
+ def parse_options_to_args( opts )
32
+ [ opts ].flatten
33
+ end
34
+
35
+ # Internal: create the human readable version of this response
36
+ #
37
+ # Returns a String
38
+ def message
39
+ [ keyword, raw_args ].flatten.join(' ')
40
+ end
41
+
42
+ # Internal: callback that is used by some Responses that have more complex
43
+ # response creation.
44
+ #
45
+ # connection - the KJess::Connection object to continue to read from
46
+ #
47
+ # Returns nothing
48
+ def read_more( connection ); end
49
+
50
+ # Internal: is this Response object an error object.
51
+ #
52
+ # This is overwritte in those objects that create Exceptions
53
+ #
54
+ # Returns false
55
+ def error?
56
+ false
57
+ end
58
+ end
59
+ end
60
+
61
+ require 'kjess/response/client_error'
62
+ require 'kjess/response/deleted'
63
+ require 'kjess/response/dumped_stats'
64
+ require 'kjess/response/end'
65
+ require 'kjess/response/eof'
66
+ require 'kjess/response/error'
67
+ require 'kjess/response/flushed_all_queues'
68
+ require 'kjess/response/not_found'
69
+ require 'kjess/response/not_stored'
70
+ require 'kjess/response/reloaded_config'
71
+ require 'kjess/response/server_error'
72
+ require 'kjess/response/stats'
73
+ require 'kjess/response/stored'
74
+ require 'kjess/response/unknown'
75
+ require 'kjess/response/value'
76
+ require 'kjess/response/version'
@@ -0,0 +1,19 @@
1
+ require 'kjess/error'
2
+ class KJess::Response
3
+ class ClientError < KJess::Response
4
+ keyword 'CLIENT_ERROR'
5
+ arity 1
6
+
7
+ def message
8
+ args.join(' ')
9
+ end
10
+
11
+ def error?
12
+ true
13
+ end
14
+
15
+ def exception
16
+ KJess::ClientError.new( message )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ class KJess::Response
2
+ class Deleted < KJess::Response
3
+ keyword 'DELETED'
4
+ end
5
+ end
@@ -0,0 +1,48 @@
1
+ class KJess::Response
2
+ class DumpedStats < KJess::Response
3
+ keyword 'queue'
4
+ arity 2
5
+
6
+ attr_accessor :data
7
+
8
+ # Internal: Read the extra data from the value
9
+ #
10
+ # Read the datablock that is after the value and then the final END marker.
11
+ #
12
+ # Returns nothing
13
+ def read_more( connection )
14
+ queue_line_re = /\Aqueue\s+'(\S+)' \{\Z/
15
+ stat_line_re = /\A(\w+)=(\S+)\Z/
16
+ stats = Hash.new
17
+ line = message.strip
18
+ current_queue = nil
19
+
20
+ begin
21
+ line.strip!
22
+ if md = stat_line_re.match( line ) then
23
+ stats[current_queue][md.captures[0]] = convert_value( md.captures[1] )
24
+ elsif md = queue_line_re.match( line ) then
25
+ current_queue = md.captures.first
26
+ stats[current_queue] = Hash.new
27
+ elsif line == "}" then
28
+ current_queue = nil
29
+ elsif line == "END" then
30
+ break
31
+ else
32
+ # do nothing -- empty line
33
+ end
34
+ end while line = connection.readline
35
+ @data = stats
36
+ end
37
+
38
+ def convert_value( value )
39
+ if value =~ /\A\d+\Z/ then
40
+ Float( value ).to_i
41
+ elsif value =~ /\A\d+\.\d+\Z/
42
+ Float( value )
43
+ else
44
+ value
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ class KJess::Response
2
+ class End < KJess::Response
3
+ keyword 'END'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class KJess::Response
2
+ class Eof < KJess::Response
3
+ keyword 'EOF'
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ class KJess::Response
2
+ class Error < KJess::Response
3
+ keyword 'ERROR'
4
+
5
+ def error?
6
+ true
7
+ end
8
+
9
+ def exception
10
+ raise KJess::Error
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ class KJess::Response
2
+ class FlushedAllQueues < KJess::Response
3
+ keyword 'Flushed'
4
+ arity 2 # 'all queues.'
5
+ end
6
+ end