kjess 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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