ruby-memcached 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/memcached-client.rb +29 -0
- data/bin/memcached-server.rb +22 -0
- data/lib/ruby-memcached.rb +3 -0
- data/lib/ruby-memcached/Client.rb +161 -0
- data/lib/ruby-memcached/Constants.rb +5 -0
- data/lib/ruby-memcached/Item.rb +38 -0
- data/lib/ruby-memcached/Memcached.rb +121 -0
- data/lib/ruby-memcached/Server.rb +194 -0
- data/lib/ruby-memcached/constants/Commands.rb +26 -0
- data/lib/ruby-memcached/constants/CommandsRegex.rb +26 -0
- data/lib/ruby-memcached/constants/Errors.rb +11 -0
- data/lib/ruby-memcached/constants/Responses.rb +18 -0
- data/lib/ruby-memcached/constants/ResponsesRegex.rb +12 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 11f93dfef10cbbbc92972e071a7662c78ada89bbc098fc59905ad386c5a62c25
|
4
|
+
data.tar.gz: e94b21ce9b83b03bd204114b2344ae398971428e1154f1508daeebbfb17ca7cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10d7e9ca7df90a8f07f66d5627cc4b9012b2e66762014800d796650424403a49ae2855bd20052982051094bd2f862e515ae84ef9e87c0ff43210a53075d3064f
|
7
|
+
data.tar.gz: 0a25744a8187a5d3f1d48a23e10ebe89d4ab6e830ff5f6ddb42ea7f881c0488ffd0aed2a5f025ac754832bdf618aa427ad0551ffe03f8d1cb62c6ace741a16f4
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require_relative '../lib/ruby-memcached.rb'
|
5
|
+
|
6
|
+
|
7
|
+
host = ARGV[0]
|
8
|
+
port = ARGV[1]
|
9
|
+
server = TCPSocket.new(host, port)
|
10
|
+
|
11
|
+
listener = Thread.new {
|
12
|
+
while response = server.gets()
|
13
|
+
puts("Server: " + response)
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
speaker = Thread.new {
|
18
|
+
while true
|
19
|
+
print("> ")
|
20
|
+
server.puts(STDIN.gets())
|
21
|
+
break if $_ =~ RubyMemcached::CommandsRegex.end
|
22
|
+
sleep(0.1)
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
listener.join()
|
27
|
+
speaker.join()
|
28
|
+
|
29
|
+
server.close()
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/ruby-memcached.rb'
|
4
|
+
|
5
|
+
puts('Creating server...')
|
6
|
+
host = ARGV[0]
|
7
|
+
port = ARGV[1]
|
8
|
+
gc_interval = ARGV[2].to_i()
|
9
|
+
|
10
|
+
server = RubyMemcached::Server.new(host, port)
|
11
|
+
|
12
|
+
main = Thread.new {
|
13
|
+
puts('Starting server...')
|
14
|
+
server.start()
|
15
|
+
}
|
16
|
+
|
17
|
+
gc = Thread.new {
|
18
|
+
server.start_gc(gc_interval) if gc_interval > 0
|
19
|
+
}
|
20
|
+
|
21
|
+
main.join()
|
22
|
+
puts('Server closed')
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require_relative './Memcached.rb'
|
4
|
+
require_relative './Constants.rb'
|
5
|
+
|
6
|
+
module RubyMemcached
|
7
|
+
class Client
|
8
|
+
|
9
|
+
attr_reader :host
|
10
|
+
attr_reader :port
|
11
|
+
|
12
|
+
def initialize(host, port)
|
13
|
+
@host = host
|
14
|
+
@port = port
|
15
|
+
@server = TCPSocket.new(host, port)
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(keys)
|
19
|
+
keys_string = keys.reduce { |ss, s| ss + ' ' + s }
|
20
|
+
@server.puts(Commands.get % keys_string)
|
21
|
+
|
22
|
+
items = []
|
23
|
+
|
24
|
+
loop do
|
25
|
+
case @server.gets()
|
26
|
+
when ResponsesRegex.get
|
27
|
+
key = $~['key']
|
28
|
+
flags = $~['flags'].to_i()
|
29
|
+
bytes = $~['bytes'].to_i()
|
30
|
+
data = @server.read(bytes + 1).chomp()
|
31
|
+
|
32
|
+
items << Memcached::Item.new(key, data, flags, 0, bytes, nil)
|
33
|
+
|
34
|
+
when ResponsesRegex.end
|
35
|
+
break
|
36
|
+
|
37
|
+
when /.*ERROR.*/
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
return items
|
43
|
+
end
|
44
|
+
|
45
|
+
def gets(keys)
|
46
|
+
keys_string = keys.reduce { |ss, s| ss + ' ' + s }
|
47
|
+
@server.puts(Commands.gets % keys_string)
|
48
|
+
|
49
|
+
items = []
|
50
|
+
|
51
|
+
loop do
|
52
|
+
case @server.gets()
|
53
|
+
when ResponsesRegex.gets
|
54
|
+
key = $~['key']
|
55
|
+
flags = $~['flags'].to_i()
|
56
|
+
bytes = $~['bytes'].to_i()
|
57
|
+
cas_id = $~['cas_id'].to_i()
|
58
|
+
data = @server.read(bytes + 1).chomp()
|
59
|
+
|
60
|
+
items << Memcached::Item.new(key, data, flags, 0, bytes, cas_id)
|
61
|
+
|
62
|
+
when ResponsesRegex.end
|
63
|
+
break
|
64
|
+
|
65
|
+
when /.*ERROR.*/
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
return items
|
71
|
+
end
|
72
|
+
|
73
|
+
def set(key, flags, exptime, bytes, data)
|
74
|
+
@server.puts(Commands.set % [
|
75
|
+
key,
|
76
|
+
flags,
|
77
|
+
exptime,
|
78
|
+
bytes,
|
79
|
+
'',
|
80
|
+
data
|
81
|
+
])
|
82
|
+
|
83
|
+
return Responses.to_h[Responses.key @server.gets()]
|
84
|
+
end
|
85
|
+
|
86
|
+
def add(key, flags, exptime, bytes, data)
|
87
|
+
@server.puts(Commands.add % [
|
88
|
+
key,
|
89
|
+
flags,
|
90
|
+
exptime,
|
91
|
+
bytes,
|
92
|
+
'',
|
93
|
+
data
|
94
|
+
])
|
95
|
+
|
96
|
+
return Responses.to_h[Responses.key @server.gets()]
|
97
|
+
end
|
98
|
+
|
99
|
+
def replace(key, flags, exptime, bytes, data)
|
100
|
+
@server.puts(Commands.replace % [
|
101
|
+
key,
|
102
|
+
flags,
|
103
|
+
exptime,
|
104
|
+
bytes,
|
105
|
+
'',
|
106
|
+
data
|
107
|
+
])
|
108
|
+
|
109
|
+
return Responses.to_h[Responses.key @server.gets()]
|
110
|
+
end
|
111
|
+
|
112
|
+
def append(key, bytes, data)
|
113
|
+
@server.puts(Commands.append % [
|
114
|
+
key,
|
115
|
+
bytes,
|
116
|
+
'',
|
117
|
+
data
|
118
|
+
])
|
119
|
+
|
120
|
+
return Responses.to_h[Responses.key @server.gets()]
|
121
|
+
end
|
122
|
+
|
123
|
+
def prepend(key, bytes, data)
|
124
|
+
@server.puts(Commands._prepend % [
|
125
|
+
key,
|
126
|
+
bytes,
|
127
|
+
'',
|
128
|
+
data
|
129
|
+
])
|
130
|
+
|
131
|
+
return Responses.to_h[Responses.key @server.gets()]
|
132
|
+
end
|
133
|
+
|
134
|
+
def cas(key, flags, exptime, bytes, cas_id, data)
|
135
|
+
@server.puts(Commands.cas % [
|
136
|
+
key,
|
137
|
+
flags,
|
138
|
+
exptime,
|
139
|
+
bytes,
|
140
|
+
cas_id,
|
141
|
+
'',
|
142
|
+
data
|
143
|
+
])
|
144
|
+
|
145
|
+
return Responses.to_h[Responses.key @server.gets()]
|
146
|
+
end
|
147
|
+
|
148
|
+
def delete(key)
|
149
|
+
@server.puts(Commands.delete % key)
|
150
|
+
return Responses.to_h[Responses.key @server.gets()]
|
151
|
+
end
|
152
|
+
|
153
|
+
def end()
|
154
|
+
@server.puts(Commands.end)
|
155
|
+
end
|
156
|
+
|
157
|
+
def reconnect()
|
158
|
+
@server = TCPSocket.new(@host, @port)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RubyMemcached
|
2
|
+
class Memcached::Item
|
3
|
+
|
4
|
+
attr_accessor :key
|
5
|
+
attr_accessor :data
|
6
|
+
attr_accessor :flags
|
7
|
+
attr_accessor :exptime
|
8
|
+
attr_accessor :bytes
|
9
|
+
attr_accessor :cas_id
|
10
|
+
|
11
|
+
attr_accessor :lock
|
12
|
+
|
13
|
+
def initialize(key, data, flags, exptime, bytes, cas_id)
|
14
|
+
@key = key
|
15
|
+
@data = data
|
16
|
+
@flags = flags
|
17
|
+
@bytes = bytes
|
18
|
+
@exptime = get_exptime(exptime.to_i)
|
19
|
+
@cas_id = cas_id
|
20
|
+
|
21
|
+
@lock = Mutex.new()
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s()
|
25
|
+
return @key
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_exptime(exptime)
|
31
|
+
if (exptime == 0)
|
32
|
+
return nil
|
33
|
+
elsif (exptime < 1592000)
|
34
|
+
return Time.now().getutc() + exptime
|
35
|
+
else
|
36
|
+
return Time.at(exptime)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
module RubyMemcached
|
4
|
+
class Memcached
|
5
|
+
|
6
|
+
attr_reader :storage
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@storage = Concurrent::Hash.new()
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(key)
|
13
|
+
self.check_exptime(key)
|
14
|
+
return @storage[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_multi(keys)
|
18
|
+
result = []
|
19
|
+
for key in keys do
|
20
|
+
item = self.get(key)
|
21
|
+
result.append(item) unless item.nil?
|
22
|
+
end
|
23
|
+
return result
|
24
|
+
end
|
25
|
+
|
26
|
+
def set(key, flags, exptime, bytes, data)
|
27
|
+
if(@storage.key?(key))
|
28
|
+
cas_id = @storage[key].cas_id + 1
|
29
|
+
else
|
30
|
+
cas_id = 0
|
31
|
+
end
|
32
|
+
new_item = Item.new(key, data, flags, exptime, bytes, cas_id)
|
33
|
+
@storage.store(key, new_item)
|
34
|
+
return Responses.stored
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(key, flags, exptime, bytes, data)
|
38
|
+
self.check_exptime(key)
|
39
|
+
|
40
|
+
return Responses.not_stored if @storage.key?(key)
|
41
|
+
new_item = Item.new(key, data, flags, exptime, bytes, 0)
|
42
|
+
@storage.store(key, new_item)
|
43
|
+
return Responses.stored
|
44
|
+
end
|
45
|
+
|
46
|
+
def replace(key, flags, exptime, bytes, data)
|
47
|
+
self.check_exptime(key)
|
48
|
+
|
49
|
+
return Responses.not_stored unless @storage.key?(key)
|
50
|
+
cas_id = @storage[key].cas_id + 1
|
51
|
+
new_item = Item.new(key, data, flags, exptime, bytes, cas_id)
|
52
|
+
@storage.store(key, new_item)
|
53
|
+
return Responses.stored
|
54
|
+
end
|
55
|
+
|
56
|
+
def append(key, bytes, data)
|
57
|
+
self.check_exptime(key)
|
58
|
+
|
59
|
+
return Responses.not_stored unless @storage.key?(key)
|
60
|
+
@storage[key].data.concat(data)
|
61
|
+
@storage[key].bytes += bytes
|
62
|
+
@storage[key].cas_id += 1
|
63
|
+
return Responses.stored
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepend(key, bytes, data)
|
67
|
+
self.check_exptime(key)
|
68
|
+
|
69
|
+
return Responses.not_stored unless @storage.key?(key)
|
70
|
+
@storage[key].data.prepend(data)
|
71
|
+
@storage[key].bytes += bytes
|
72
|
+
@storage[key].cas_id += 1
|
73
|
+
return Responses.stored
|
74
|
+
end
|
75
|
+
|
76
|
+
def cas(key, flags, exptime, bytes, cas_id, data)
|
77
|
+
self.check_exptime(key)
|
78
|
+
|
79
|
+
return Responses.not_found unless @storage.key?(key)
|
80
|
+
|
81
|
+
old_item = @storage[key]
|
82
|
+
old_item.lock.synchronize do
|
83
|
+
return Responses.exists if old_item.cas_id != cas_id
|
84
|
+
|
85
|
+
new_item = Item.new(key, data, flags, exptime, bytes, old_item.cas_id + 1)
|
86
|
+
@storage.store(key, new_item)
|
87
|
+
return Responses.stored
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete(key)
|
92
|
+
return Responses.not_found unless @storage.key?(key)
|
93
|
+
@storage.delete(key)
|
94
|
+
return Responses.deleted
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_exptime(key)
|
98
|
+
value = @storage[key]
|
99
|
+
return true if value.nil?() || value.exptime.nil?()
|
100
|
+
|
101
|
+
current_time = Time.now().getutc()
|
102
|
+
|
103
|
+
if (current_time > value.exptime)
|
104
|
+
@storage.delete(key)
|
105
|
+
return false
|
106
|
+
end
|
107
|
+
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_exptimes()
|
112
|
+
deleted = 0
|
113
|
+
@storage.each_key do | key |
|
114
|
+
delete += 1 unless self.check_exptime(key)
|
115
|
+
end
|
116
|
+
return deleted
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
require_relative './Item.rb'
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require_relative './/Memcached.rb'
|
4
|
+
require_relative './Constants.rb'
|
5
|
+
|
6
|
+
module RubyMemcached
|
7
|
+
class Server
|
8
|
+
|
9
|
+
attr_reader :host
|
10
|
+
attr_reader :port
|
11
|
+
attr_accessor :storage
|
12
|
+
|
13
|
+
def initialize(host, port)
|
14
|
+
@host = host
|
15
|
+
@port = port
|
16
|
+
@connection = TCPServer.new(host, port)
|
17
|
+
@memc = Memcached.new()
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
loop do
|
22
|
+
Thread.start(@connection.accept()) do | client |
|
23
|
+
|
24
|
+
puts('Opening connection to %s' % client.to_s)
|
25
|
+
|
26
|
+
continue_condition = true
|
27
|
+
|
28
|
+
while command = client.gets()
|
29
|
+
printf('%s: %s' % [client.to_s(), command])
|
30
|
+
begin
|
31
|
+
continue_condition = parse(command, client) unless command.nil?
|
32
|
+
rescue => e
|
33
|
+
client.puts(Errors.server_error % e.message)
|
34
|
+
puts(Errors.server_error % e.message)
|
35
|
+
end
|
36
|
+
break unless continue_condition
|
37
|
+
end
|
38
|
+
|
39
|
+
client.close()
|
40
|
+
puts('Closing connection to %s' % [client.to_s()])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse(command, client)
|
46
|
+
case command
|
47
|
+
when CommandsRegex.get
|
48
|
+
self.get(client, $~)
|
49
|
+
|
50
|
+
when CommandsRegex.gets
|
51
|
+
self.gets(client, $~)
|
52
|
+
|
53
|
+
when CommandsRegex.set
|
54
|
+
self.set(client, $~)
|
55
|
+
|
56
|
+
when CommandsRegex.add
|
57
|
+
self.add(client, $~)
|
58
|
+
|
59
|
+
when CommandsRegex.replace
|
60
|
+
self.replace(client, $~)
|
61
|
+
|
62
|
+
when CommandsRegex.append
|
63
|
+
self.append(client, $~)
|
64
|
+
|
65
|
+
when CommandsRegex.prepend
|
66
|
+
self.prepend(client, $~)
|
67
|
+
|
68
|
+
when CommandsRegex.cas
|
69
|
+
self.cas(client, $~)
|
70
|
+
|
71
|
+
when CommandsRegex.delete
|
72
|
+
self.delete(client, $~)
|
73
|
+
|
74
|
+
when CommandsRegex.end
|
75
|
+
return false;
|
76
|
+
else
|
77
|
+
client.puts(Errors.client_error % [": Invalid command"])
|
78
|
+
end
|
79
|
+
|
80
|
+
return true
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_data(client, bytes)
|
84
|
+
return client.read(bytes + 1).chomp()
|
85
|
+
end
|
86
|
+
|
87
|
+
def get(client, command)
|
88
|
+
keys = command['keys'].split(' ')
|
89
|
+
items = @memc.get_multi(keys)
|
90
|
+
|
91
|
+
for item in items
|
92
|
+
client.puts(Responses.get % [item.key, item.flags, item.bytes, item.data]) unless item.nil?()
|
93
|
+
end
|
94
|
+
client.puts(Responses.end)
|
95
|
+
end
|
96
|
+
|
97
|
+
def gets(client, command)
|
98
|
+
keys = command['keys'].split(' ')
|
99
|
+
items = @memc.get_multi(keys)
|
100
|
+
|
101
|
+
for item in items
|
102
|
+
client.puts(Responses.gets % [item.key, item.flags, item.bytes, item.cas_id, item.data]) unless item.nil?()
|
103
|
+
end
|
104
|
+
client.puts(Responses.end)
|
105
|
+
end
|
106
|
+
|
107
|
+
def set(client, command)
|
108
|
+
key = command['key']
|
109
|
+
flags = command['flags']
|
110
|
+
exptime = command['exptime']
|
111
|
+
bytes = command['bytes'].to_i()
|
112
|
+
data = self.get_data(client, bytes)
|
113
|
+
noreply = !command['noreply'].nil?
|
114
|
+
|
115
|
+
response = @memc.set(key, flags, exptime, bytes, data)
|
116
|
+
client.puts(response) unless noreply
|
117
|
+
end
|
118
|
+
|
119
|
+
def add(client, command)
|
120
|
+
key = command['key']
|
121
|
+
flags = command['flags']
|
122
|
+
exptime = command['exptime']
|
123
|
+
bytes = command['bytes'].to_i()
|
124
|
+
data = self.get_data(client, bytes)
|
125
|
+
noreply = !command['noreply'].nil?
|
126
|
+
|
127
|
+
response = @memc.add(key, flags, exptime, bytes, data)
|
128
|
+
client.puts(response) unless noreply
|
129
|
+
end
|
130
|
+
|
131
|
+
def replace(client, command)
|
132
|
+
key = command['key']
|
133
|
+
flags = command['flags']
|
134
|
+
exptime = command['exptime']
|
135
|
+
bytes = command['bytes'].to_i()
|
136
|
+
data = self.get_data(client, bytes)
|
137
|
+
noreply = !command['noreply'].nil?
|
138
|
+
|
139
|
+
response = @memc.replace(key, flags, exptime, bytes, data)
|
140
|
+
client.puts(response) unless noreply
|
141
|
+
end
|
142
|
+
|
143
|
+
def append(client, command)
|
144
|
+
key = command['key']
|
145
|
+
bytes = command['bytes'].to_i()
|
146
|
+
data = self.get_data(client, bytes)
|
147
|
+
noreply = !command['noreply'].nil?
|
148
|
+
|
149
|
+
response = @memc.append(key, bytes, data)
|
150
|
+
client.puts(response) unless noreply
|
151
|
+
end
|
152
|
+
|
153
|
+
def prepend(client, command)
|
154
|
+
key = command['key']
|
155
|
+
bytes = command['bytes'].to_i()
|
156
|
+
data = self.get_data(client, bytes)
|
157
|
+
noreply = !command['noreply'].nil?
|
158
|
+
|
159
|
+
response = @memc.prepend(key, bytes, data)
|
160
|
+
client.puts(response) unless noreply
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
def cas(client, command)
|
165
|
+
key = command['key']
|
166
|
+
flags = command['flags']
|
167
|
+
exptime = command['exptime']
|
168
|
+
bytes = command['bytes'].to_i()
|
169
|
+
data = self.get_data(client, bytes)
|
170
|
+
cas_id = command['cas_id'].to_i()
|
171
|
+
noreply = !command['noreply'].nil?
|
172
|
+
|
173
|
+
response = @memc.cas(key, flags, exptime, bytes, cas_id, data)
|
174
|
+
client.puts(response) unless noreply
|
175
|
+
end
|
176
|
+
|
177
|
+
def delete(client, command)
|
178
|
+
key = command['key']
|
179
|
+
noreply = !command['noreply'].nil?
|
180
|
+
|
181
|
+
response = @memc.delete(key)
|
182
|
+
client.puts(response) unless noreply
|
183
|
+
end
|
184
|
+
|
185
|
+
def start_gc(interval, loops = nil)
|
186
|
+
|
187
|
+
while loops.nil?() || loops > 0
|
188
|
+
sleep(interval)
|
189
|
+
puts('Garbage collector deleted: %d' % @memc.check_exptimes())
|
190
|
+
loops -= 1 unless loops.nil?()
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ruby-enum'
|
2
|
+
|
3
|
+
module RubyMemcached
|
4
|
+
class Commands
|
5
|
+
include Ruby::Enum
|
6
|
+
|
7
|
+
define :get, "get %s\n"
|
8
|
+
define :gets, "gets %s\n"
|
9
|
+
|
10
|
+
define :set, "set %s %d %d %d%s\n%s\n"
|
11
|
+
define :add, "add %s %d %d %d%s\n%s\n"
|
12
|
+
define :replace, "replace %s %d %d %d%s\n%s\n"
|
13
|
+
|
14
|
+
define :append, "append %s %d%s\n%s\n"
|
15
|
+
define :_prepend, "prepend %s %d%s\n%s\n"
|
16
|
+
|
17
|
+
define :cas, "cas %s %d %d %d %d%s\n%s\n"
|
18
|
+
|
19
|
+
define :delete, "delete %s%s\n"
|
20
|
+
|
21
|
+
define :incr, "incr %s %d%s\n%d\n"
|
22
|
+
define :decr, "decr %s %d%s\n%d\n"
|
23
|
+
|
24
|
+
define :end, "END\n"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ruby-enum'
|
2
|
+
|
3
|
+
module RubyMemcached
|
4
|
+
class CommandsRegex
|
5
|
+
include Ruby::Enum
|
6
|
+
|
7
|
+
define :get, /^get (?<keys>(\w|[ ])+)\n/
|
8
|
+
define :gets, /^gets (?<keys>(\w|[ ])+)\n/
|
9
|
+
|
10
|
+
define :set, /^set (?<key>(\w)+) (?<flags>[0-9]+) (?<exptime>[0-9]+) (?<bytes>[0-9]+)(?<noreply> noreply)?\n/
|
11
|
+
define :add, /^add (?<key>(\w)+) (?<flags>[0-9]+) (?<exptime>[0-9]+) (?<bytes>[0-9]+)(?<noreply> noreply)?\n/
|
12
|
+
define :replace, /^replace (?<key>(\w)+) (?<flags>[0-9]+) (?<exptime>[0-9]+) (?<bytes>[0-9]+)(?<noreply> noreply)?\n/
|
13
|
+
|
14
|
+
define :append, /^append (?<key>(\w)+) (?<bytes>[0-9]+)(?<noreply> noreply)?\n/
|
15
|
+
define :prepend, /^prepend (?<key>(\w)+) (?<bytes>[0-9]+)(?<noreply> noreply)?\n/
|
16
|
+
|
17
|
+
define :cas, /^cas (?<key>(\w)+) (?<flags>[0-9]+) (?<exptime>[0-9]+) (?<bytes>[0-9]+) (?<cas_id>[0-9]+)(?<noreply> noreply)?\n/
|
18
|
+
|
19
|
+
define :delete, /^delete (?<key>(\w)+)(?<noreply> noreply)?\n/
|
20
|
+
|
21
|
+
define :incr, /^incr (?<key>(\w)+) (?<value>[0-9]+)(?<noreply> noreply)?\n/
|
22
|
+
define :decr, /^decr (?<key>(\w)+) (?<value>[0-9]+)(?<noreply> noreply)?\n/
|
23
|
+
|
24
|
+
define :end, /END\n/
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'ruby-enum'
|
2
|
+
|
3
|
+
module RubyMemcached
|
4
|
+
class Responses
|
5
|
+
include Ruby::Enum
|
6
|
+
|
7
|
+
define :stored, "STORED\n"
|
8
|
+
define :not_stored, "NOT_STORED\n"
|
9
|
+
define :exists, "EXISTS\n"
|
10
|
+
define :not_found, "NOT_FOUND\n"
|
11
|
+
define :deleted, "DELETED\n"
|
12
|
+
|
13
|
+
define :get, "VALUE %s %d %d\n%s\n"
|
14
|
+
define :gets, "VALUE %s %d %d %d\n%s\n"
|
15
|
+
|
16
|
+
define :end, "END\n"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'ruby-enum'
|
2
|
+
|
3
|
+
module RubyMemcached
|
4
|
+
class ResponsesRegex
|
5
|
+
include Ruby::Enum
|
6
|
+
|
7
|
+
define :get, /VALUE (?<key>(\w)+) (?<flags>[0-9]+) (?<bytes>[0-9]+)/
|
8
|
+
define :gets, /VALUE (?<key>(\w)+) (?<flags>[0-9]+) (?<bytes>[0-9]+) (?<cas_id>[0-9]+)/
|
9
|
+
|
10
|
+
define :end, /END/
|
11
|
+
end
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-memcached
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Francisco Bongiovanni
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-12-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-enum
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.1.5
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.1'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.1.5
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '12.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '12.3'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.11'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.11'
|
75
|
+
description: A simple memcached implementation for Moove-It code challenge
|
76
|
+
email: Frantss.Bongiovanni@gmail.com
|
77
|
+
executables:
|
78
|
+
- memcached-client.rb
|
79
|
+
- memcached-server.rb
|
80
|
+
extensions: []
|
81
|
+
extra_rdoc_files: []
|
82
|
+
files:
|
83
|
+
- bin/memcached-client.rb
|
84
|
+
- bin/memcached-server.rb
|
85
|
+
- lib/ruby-memcached.rb
|
86
|
+
- lib/ruby-memcached/Client.rb
|
87
|
+
- lib/ruby-memcached/Constants.rb
|
88
|
+
- lib/ruby-memcached/Item.rb
|
89
|
+
- lib/ruby-memcached/Memcached.rb
|
90
|
+
- lib/ruby-memcached/Server.rb
|
91
|
+
- lib/ruby-memcached/constants/Commands.rb
|
92
|
+
- lib/ruby-memcached/constants/CommandsRegex.rb
|
93
|
+
- lib/ruby-memcached/constants/Errors.rb
|
94
|
+
- lib/ruby-memcached/constants/Responses.rb
|
95
|
+
- lib/ruby-memcached/constants/ResponsesRegex.rb
|
96
|
+
homepage: https://rubygems.org/gems/ruby-memcached
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
github_uri: https://github.com/Frantss/memcached-ruby
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.0.3
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Simple ruby memcached implementation
|
120
|
+
test_files: []
|