redis-protocol 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3
@@ -1,10 +1,8 @@
1
1
  # start with *, $, alpha-numeric, \r
2
2
 
3
3
  module RedisProtocol
4
- class Response
5
-
6
- end
7
4
  end
8
5
 
6
+ require 'redis-protocol/unified_protocol'
9
7
  require 'redis-protocol/request'
10
- require 'redis-protocol/static_request'
8
+ require 'redis-protocol/response'
@@ -1,134 +1,38 @@
1
+ require_relative 'unified_protocol'
2
+
1
3
  class RedisProtocol::Request
2
- attr_reader :raw_data, :type, :component_count, :components
4
+ attr_reader :raw_data, :type, :components
3
5
 
4
6
  def initialize(data)
5
7
  @raw_data = data
6
- @type, @components, @component_count, @current = nil, [], 0, ''
8
+ @type, @components= nil, []
7
9
  @next_length = 0
8
10
  parse
9
11
  end
10
12
 
13
+ def component_count
14
+ @components.length
15
+ end
16
+
11
17
  private
12
18
 
13
19
  def parse
14
- @raw_data.chars.each do |c|
15
- @type ||= c.eql?('*') ? :standard : :inline
20
+ @type ||= @raw_data[0].eql?('*') ? :standard : :inline
16
21
 
17
- send(@type, c)
18
- end
19
-
20
- complete_token
21
- @component_count += @components.count if @type == :inline
22
+ @components = send(@type)
22
23
  end
23
24
  =begin
24
25
  Inline Parser
25
26
  =end
26
- def inline(char)
27
- c = char.strip
28
-
29
- if c.empty? and not @current.empty?
30
- complete_token
31
- else
32
- @current += c
33
- end
27
+ def inline
28
+ @raw_data.split
34
29
  end
35
30
 
36
31
  =begin
37
32
  Standard Parser
38
33
  =end
39
34
 
40
- def standard(char)
41
- @token_type ||= detect_token_type char
42
- @doubt_str = ''
43
-
44
- send(@token_type, char)
45
- end
46
-
47
- def args_counts(char)
48
- return if char.eql? '*'
49
-
50
- tokenize char do
51
- @component_count = @current.to_i
52
- clean_context
53
- end
54
- end
55
-
56
- def length(char)
57
- return if char.eql? '$'
58
-
59
- tokenize char do
60
- @next_length = @current.to_i
61
- clean_context
62
- end
63
- end
64
-
65
- def payload(char)
66
- return if char.eql? '$'
67
-
68
- @next_length = 0 if @current.length == @next_length
69
-
70
- tokenize char do
71
- @next_length = 0
72
- complete_token
73
- end
74
- end
75
-
76
- def tokenize(char, &block)
77
- reach_delimiter = consume_delimiter(char)
78
-
79
- if reach_delimiter
80
- if @doubt_str.empty?
81
- block.call
82
- end
83
- return
84
- end
85
-
86
- @current += char
87
- end
88
-
89
- =begin
90
- Common Utilities
91
- =end
92
-
93
- def complete_token
94
- @components << @current
95
- clean_context
96
- end
97
-
98
- def clean_context
99
- @current = ''
100
- @token_type = nil
101
- end
102
-
103
- def consume_delimiter(char)
104
- return false if @next_length != 0
105
-
106
- if @doubt and char.eql? "\n"
107
- @doubt_str = ''
108
- @doubt = false
109
- return true
110
- end
111
-
112
- if not @doubt and char.eql? "\r"
113
- @doubt_str = "\r"
114
- @doubt = true
115
- return true
116
- end
117
-
118
- @doubt = false
119
- @current += @doubt_str unless @doubt_str.empty?
120
- @doubt_str = ''
121
- return false
122
- end
123
-
124
- def detect_token_type(char)
125
- case char
126
- when '*'
127
- :args_counts
128
- when '$'
129
- :length
130
- else
131
- :payload
132
- end
35
+ def standard
36
+ RedisProtocol::UnifiedProtocol.parse @raw_data
133
37
  end
134
38
  end
@@ -0,0 +1,22 @@
1
+ require_relative 'unified_protocol'
2
+
3
+ class RedisProtocol::Response
4
+ attr_reader :result, :raw_data
5
+
6
+ def <<(value)
7
+ @result += value.result
8
+ unless value.result.empty?
9
+ start = @raw_data.index('*') + 1
10
+ finish = @raw_data.index(RedisProtocol::UnifiedProtocol::DELIMITER)
11
+ @raw_data[start...finish] = @result.length.to_s
12
+
13
+ @raw_data += value.raw_data[(value.raw_data.index(RedisProtocol::UnifiedProtocol::DELIMITER) + RedisProtocol::UnifiedProtocol::DELIMITER.length)..-1]
14
+ end
15
+ end
16
+
17
+ def initialize(data)
18
+ @raw_data = data
19
+ @raw_data += RedisProtocol::UnifiedProtocol::DELIMITER unless @raw_data.end_with? RedisProtocol::UnifiedProtocol::DELIMITER
20
+ @result = RedisProtocol::UnifiedProtocol.parse @raw_data
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ class RedisProtocol::UnifiedProtocol
2
+ DELIMITER="\r\n"
3
+ class << self
4
+ def parse(data)
5
+ processed, index = 0, data.index(DELIMITER)
6
+ index ||= data.length
7
+ result = case data[processed]
8
+ when '*'
9
+ parse_multi_chunked data
10
+ when '$'
11
+ parse_chunked data
12
+ when '+'
13
+ parse_status data
14
+ when '-'
15
+ parse_error data
16
+ when ':'
17
+ parse_integer data
18
+ end
19
+ end
20
+
21
+ def parse_multi_chunked(data)
22
+ index = data.index DELIMITER
23
+ count = data[1...index].to_i
24
+ result = []
25
+ start = index + DELIMITER.length
26
+ 1.upto(count) do |_|
27
+ chunk, length = parse_chunked data, start
28
+ start = length + DELIMITER.length
29
+ result += chunk
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ def parse_chunked(data, start=0)
36
+ index = data.index DELIMITER, start
37
+ length = data[(start+1)...index].to_i
38
+ return nil if length == -1
39
+ result = [data[index+DELIMITER.length, length]]
40
+
41
+ start == 0 ? result : [result, index+DELIMITER.length + length]
42
+ end
43
+
44
+ def parse_status(data)
45
+ [true, data[1..-1]]
46
+ end
47
+
48
+ def parse_error(data)
49
+ [false, data[1..-1]]
50
+ end
51
+
52
+ def parse_integer(data)
53
+ [data[1..-1].to_i]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RedisProtocol::Response do
4
+ it 'should be parse response' do
5
+ result = RedisProtocol::Response.new "*3\r\n$3\r\nset\r\n$4\r\ntest\r\n$5\r\nhello\r\n"
6
+ result.result[0].should eql 'set'
7
+ result.result[1].should eql 'test'
8
+ result.result[2].should eql 'hello'
9
+ end
10
+
11
+ it 'should be parse aggregated response' do
12
+ result = RedisProtocol::Response.new "*3\r\n$3\r\nset\r\n$4\r\ntest\r\n$5\r\nhello\r\n"
13
+ additional = RedisProtocol::Response.new "*2\r\n$5\r\nworld\r\n$6\r\nturtle\r\n"
14
+ result << additional
15
+ result.result[0].should eql 'set'
16
+ result.result[1].should eql 'test'
17
+ result.result[2].should eql 'hello'
18
+ result.result[3].should eql 'world'
19
+ result.result[4].should eql 'turtle'
20
+ result.raw_data.should eql "*5\r\n$3\r\nset\r\n$4\r\ntest\r\n$5\r\nhello\r\n$5\r\nworld\r\n$6\r\nturtle\r\n"
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RedisProtocol::UnifiedProtocol do
4
+ it 'should parse a redis unified protocol based message' do
5
+ result = RedisProtocol::UnifiedProtocol.parse "$4\r\nping"
6
+ result[0].should eql 'ping'
7
+ end
8
+
9
+ it 'should parse multi-chunked format' do
10
+ result = RedisProtocol::UnifiedProtocol.parse "*1\r\n$4\r\nping"
11
+ result[0].should eql 'ping'
12
+ result = RedisProtocol::UnifiedProtocol.parse "*2\r\n$4\r\nkeys\r\n$1\r\n*\r\n"
13
+ result[0].should eql 'keys'
14
+ result[1].should eql '*'
15
+ end
16
+
17
+ it 'should parse status ok' do
18
+ result = RedisProtocol::UnifiedProtocol.parse '+ok'
19
+ result[0].should be true
20
+ result[1].should eql 'ok'
21
+ end
22
+
23
+ it 'should parse error with message' do
24
+ result = RedisProtocol::UnifiedProtocol.parse "-ERR unknown command 'foobar'"
25
+ result[0].should be false
26
+ result[1].should eql "ERR unknown command 'foobar'"
27
+ end
28
+
29
+ it 'should parse integer only return' do
30
+ result = RedisProtocol::UnifiedProtocol.parse ":1000\r\n"
31
+ result[0].should be 1000
32
+ RedisProtocol::UnifiedProtocol.parse(':-30')[0].should be -30
33
+ end
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-protocol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-12 00:00:00.000000000 Z
12
+ date: 2013-05-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -127,9 +127,11 @@ files:
127
127
  - VERSION
128
128
  - lib/redis-protocol.rb
129
129
  - lib/redis-protocol/request.rb
130
- - lib/redis-protocol/static_request.rb
130
+ - lib/redis-protocol/response.rb
131
+ - lib/redis-protocol/unified_protocol.rb
131
132
  - spec/redis-protocol/request_spec.rb
132
- - spec/redis-protocol/static_request_spec.rb
133
+ - spec/redis-protocol/response_spec.rb
134
+ - spec/redis-protocol/unified_protocol_spec.rb
133
135
  - spec/redis-protocol_spec.rb
134
136
  - spec/spec_helper.rb
135
137
  homepage: http://github.com/astral1/redis-protocol
@@ -147,7 +149,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
149
  version: '0'
148
150
  segments:
149
151
  - 0
150
- hash: 3700473079050196087
152
+ hash: -1569199417732497117
151
153
  required_rubygems_version: !ruby/object:Gem::Requirement
152
154
  none: false
153
155
  requirements:
@@ -1,77 +0,0 @@
1
- class RedisProtocol::StaticRequest
2
- attr_reader :raw_data, :type, :components, :component_count
3
- OP_DELIMITER = "\r\n"
4
-
5
- def initialize(data)
6
- @raw_data = data
7
- parse_request
8
- end
9
-
10
- def parse_request
11
- @type = check_request_type
12
-
13
- send @type
14
- end
15
-
16
- def self.recognize(data)
17
- (check_request_type == :inline) ? data.split[0] : data.split(OP_DELIMITER)[2]
18
- end
19
-
20
- def check_request_type
21
- (@raw_data.start_with? '*') ? :standard : :inline
22
- end
23
-
24
- def standard
25
- components = []
26
- data = @raw_data.dup
27
- operand_length, payload = operand_length_for data.split(OP_DELIMITER)
28
- 1.upto(operand_length).each do |_|
29
- data, payload = unpack_payload(payload)
30
- components << data
31
- end
32
- @component_count = components.count
33
- @components = components
34
- end
35
-
36
- def operand_length_for(data)
37
- operand_length, payload = next_token data
38
- raise "invalid packet : #{operand_length}" unless operand_length.start_with? '*'
39
- operand_length = operand_length[1..-1].to_i
40
-
41
- [operand_length, payload]
42
- end
43
-
44
- def unpack_payload(payload)
45
- field_length, payload = next_token payload
46
- raise "invalid length format : #{field_length}" unless field_length.start_with? '$'
47
- field_length = field_length[1..-1].to_i
48
- next_token payload, field_length
49
- end
50
-
51
- def inline
52
- @components = @raw_data.split
53
- @component_count = @components.count
54
- @components
55
- end
56
-
57
- def next_token(data, length = 0)
58
- if data[0].length == length || length == 0
59
- [data[0], data[1..-1]]
60
- else
61
- multiline_token(data, length)
62
- end
63
- end
64
-
65
- def multiline_token(data, length)
66
- val, index = '', 0
67
- data.each do |token|
68
- val.empty? ? val = token : val += "\r\n#{token}"
69
- index += 1
70
-
71
- break if val.length == length
72
- end
73
- [val, data[index..-1]]
74
- end
75
-
76
- private :next_token, :standard, :inline, :operand_length_for, :unpack_payload, :multiline_token
77
- end
@@ -1,54 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe RedisProtocol::StaticRequest do
4
- it 'initialize with data' do
5
- RedisProtocol::StaticRequest.new 'ping'
6
- end
7
-
8
- it 'should get raw data as original' do
9
- request = RedisProtocol::StaticRequest.new 'ping'
10
- request.raw_data.should eql 'ping'
11
- end
12
-
13
- it 'should parse inline command' do
14
- request = RedisProtocol::StaticRequest.new 'ping'
15
- request.components[0].should eql 'ping'
16
- request.component_count.should be 1
17
- end
18
-
19
- it 'should parse inline command with parameter' do
20
- request = RedisProtocol::StaticRequest.new 'keys *'
21
- request.components[0].should eql 'keys'
22
- request.component_count.should be 2
23
- end
24
-
25
- it 'should parse inline command with multi whitespaces' do
26
- request = RedisProtocol::StaticRequest.new 'keys *'
27
- request.components[0].should eql 'keys'
28
- request.component_count.should be 2
29
- end
30
-
31
- it 'should parse standard command' do
32
- request = RedisProtocol::StaticRequest.new "*1\r\n$4\r\nping"
33
- request.components[0].should eql 'ping'
34
- request.component_count.should be 1
35
- end
36
-
37
- it 'should parse standard command with parameter' do
38
- request = RedisProtocol::StaticRequest.new "*2\r\n$4\r\nkeys\r\n$1\r\n*"
39
- request.components[0].should eql 'keys'
40
- request.component_count.should be 2
41
- end
42
-
43
- it 'should parse standard command with multiple parameters' do
44
- request = RedisProtocol::StaticRequest.new "*3\r\n$3\r\nset\r\n$4\r\ntest\r\n$5\r\nhello"
45
- request.components[0].should eql 'set'
46
- request.component_count.should be 3
47
- end
48
-
49
- it 'should parse standard command containing delimiter' do
50
- request = RedisProtocol::StaticRequest.new "*3\r\n$3\r\nset\r\n$4\r\ntest\r\n$12\r\nhello\r\nworld"
51
- request.components[2].should eql "hello\r\nworld"
52
- request.component_count.should be 3
53
- end
54
- end