ruby-memcached 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,3 @@
1
+ require_relative './ruby-memcached/Memcached.rb'
2
+ require_relative './ruby-memcached/Server.rb'
3
+ require_relative './ruby-memcached/Client.rb'
@@ -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,5 @@
1
+ require_relative './constants/Commands.rb'
2
+ require_relative './constants/CommandsRegex.rb'
3
+ require_relative './constants/Errors.rb'
4
+ require_relative './constants/Responses.rb'
5
+ require_relative './constants/ResponsesRegex.rb'
@@ -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,11 @@
1
+ require 'ruby-enum'
2
+
3
+ module RubyMemcached
4
+ class Errors
5
+ include Ruby::Enum
6
+
7
+ define :error, "ERROR\r\n"
8
+ define :client_error, "CLIENT_ERROR %s\r\n"
9
+ define :server_error, "SERVER_ERROR %s\r\n"
10
+ end
11
+ 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: []