redislike 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b32d60e77c8f6e9fc89a7df48aa4f8229564c66e
4
+ data.tar.gz: ce5b4dcdafb32682ac78350970cbbedd6ccca032
5
+ SHA512:
6
+ metadata.gz: ae5c0c559385cf9e46394631629f942b2bd4c5da4b9b9208d3d5ab13ad030b55fc65325b847cadb24cdf74d3bbc48bfbda23555dcb933e9addfbd468174b3b9a
7
+ data.tar.gz: 9e0bade973bb81c47130e8ba41975b4798d9ece654d338dda1c627ef23c071aa5e946842e59776797fabc5242c51ca3b266b8ee385f54609f3ba2b18902d385e
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle/
2
+ coverage/
3
+ vendor/
4
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Chris Olstrom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ redislike
2
+ =========
3
+
4
+ For when we want Redis, but can't have nice things.
5
+
6
+ redislike adds backend-independent support for redis-like list operations to any Moneta datastore.
7
+
8
+ Supported Operations
9
+ --------------------
10
+ * LINDEX
11
+ * LINSERT
12
+ * LLEN
13
+ * LPOP
14
+ * LPUSH
15
+ * LPUSHX
16
+ * LRANGE
17
+ * LREM
18
+ * LSET
19
+ * LTRIM
20
+ * RPOP
21
+ * RPOPLPUSH
22
+ * RPUSH
23
+ * RPUSHX
24
+
25
+ Missing
26
+ -------
27
+ * BLPOP
28
+ * BRPOP
29
+ * BRPOPLPUSH
30
+
31
+ Other Supported Operations
32
+ --------------------------
33
+ * EXISTS
34
+
35
+ Installation
36
+ ------------
37
+
38
+ ```gem install redislike```
39
+
40
+ or in your Gemfile
41
+
42
+ ```gem 'redislike'```
43
+
44
+ Example
45
+ -----
46
+
47
+ ```
48
+ require 'redislike'
49
+ datastore = Moneta.new :Memory, expires: true
50
+
51
+ datastore.lpush 'pending', 'foo'
52
+ datastore.rpoplpush 'pending', 'active'
53
+ puts datastore.lrange 'active', 0, -1
54
+ ```
55
+
56
+ Should return ```['foo']```
57
+
58
+ Motivation
59
+ ----------
60
+
61
+ Redis may be my favourite data store, for a number of reasons that have nothing to do with this gem. It's also not a silver bullet. Like many tools, used in the right context, it excels. Out of its element, it adds complexity and overhead (when compared to using a tool better suited to that problem domain).
62
+
63
+ Then there's Moneta, a lovely gem that provides a unified API to an impressive range of backends for key/value storage. This allows low-effort integration with whatever data store you already have. The consistency also enables trivial swapping of backends as the evolution of an application guides requirements, without rewriting much of anything.
64
+
65
+ To achive this consistency, Moneta omits support for backend-specfic features. One of those that I often want is the (B)RPOPLPUSH list operation from Redis. This takes an item from the tail of one list, and puts it at the head of another list.
66
+
67
+ Rather than depending on a Redis backend for Moneta, and passing these operations through to it, I built redis-like queue operations on top of Moneta. Once those were in place, it seemed reasonable to continue and build the rest.
68
+
69
+ At this point, some sort of scope began to take shape. The methods were restructured to align with those provided by Redis, and tests were written to guide this process.
70
+
71
+ **TL;DR: I wanted RPOPLPUSH in Moneta, didn't define scope boundaries and accidentally reimplemented (some of) Redis.**
72
+
73
+ Dependencies
74
+ ------------
75
+ * Moneta
76
+
77
+ What's up with the tests?
78
+ -------------------------
79
+
80
+ Alright, so the tests are a bit unconventional, in the sense that you need a working redis server to actually run the tests for a gem with the stated intent of not requiring a redis server.
81
+
82
+ These tests that redislike is in fact, redis-like. These work by executing the same instructions against redis, and an in-memory instance of redislike, and asserting that the results should be the same.
data/Rakefile ADDED
@@ -0,0 +1,64 @@
1
+ # require 'simplecov'
2
+
3
+ require 'rubocop/rake_task'
4
+ require 'reek/rake/task'
5
+ require 'roodi_task'
6
+
7
+ task default: [:test]
8
+
9
+ task test: [:kintama]
10
+
11
+ desc 'Run Test Suite'
12
+ task :kintama do
13
+ sh 'ruby tests/redislike.rb'
14
+ end
15
+
16
+ task audit: [:style, :complexity, :duplication, :design, :documentation]
17
+
18
+ task style: [:rubocop]
19
+
20
+ desc 'Enforce Style Conformance with RuboCop'
21
+ RuboCop::RakeTask.new(:rubocop) do |task|
22
+ task.patterns = ['lib/**/*.rb']
23
+ task.fail_on_error = false
24
+ end
25
+
26
+ task complexity: [:flog]
27
+
28
+ desc 'Assess Complexity with Flog'
29
+ # FlogTask.new :flog, 9000, ['lib']
30
+ task :flog do
31
+ sh 'flog lib/**/*.rb'
32
+ end
33
+
34
+ task duplication: [:flay]
35
+
36
+ task :flay do
37
+ sh 'flay'
38
+ end
39
+
40
+ task design: [:roodi, :reek]
41
+
42
+ desc 'Find Code Smells with Reek'
43
+ Reek::Rake::Task.new(:reek) do |task|
44
+ task.fail_on_error = false
45
+ end
46
+
47
+ task :rework do
48
+ sh 'churn'
49
+ end
50
+
51
+ task :documentation do
52
+ sh 'inch'
53
+ end
54
+
55
+ task generate: [:documents, :graphs]
56
+
57
+ task :documents do
58
+ sh 'yard'
59
+ end
60
+
61
+ task :graphs do
62
+ sh 'bundle viz'
63
+ sh 'yard graph | dot -Tpng > lib_graph.png'
64
+ end
@@ -0,0 +1,40 @@
1
+ module RedisLike
2
+ module Helpers
3
+ module Lists
4
+ def enqueue(list, item, to:, allow_create: true)
5
+ operation = to == :head ? :unshift : :push
6
+ items = fetch list, []
7
+ items.method(operation).call(item)
8
+ store list, items if allow_create || exists(list)
9
+ llen list
10
+ end
11
+
12
+ def dequeue(list, from:)
13
+ operation = from == :head ? :shift : :pop
14
+ current = fetch list, []
15
+ item = current.method(operation).call
16
+ store list, current
17
+ item
18
+ end
19
+
20
+ def remove_count(list, count, item, from:)
21
+ operation = from == :tail ? :rindex : :index
22
+ items = fetch list, []
23
+ removed = count.times.map do
24
+ found = items.method(operation).call(item)
25
+ items.delete_at found if found
26
+ end
27
+ store list, items
28
+ removed.compact.length
29
+ end
30
+
31
+ def remove_all(list, item)
32
+ items = fetch list, []
33
+ remove_count list, items.count(item), item, from: :head
34
+ # remaining = items.reject { |i| i == item }
35
+ # store list, remaining
36
+ # items.length - remaining.length
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ require 'moneta'
2
+
3
+ module RedisLike
4
+ module Keys
5
+ def exists(list)
6
+ key? list
7
+ end
8
+ end
9
+ end
10
+
11
+ module Moneta
12
+ module Defaults
13
+ include RedisLike::Keys
14
+ end
15
+ end
@@ -0,0 +1,91 @@
1
+ require 'moneta'
2
+ require_relative 'helpers/lists'
3
+
4
+ # Module containing mixins for redis-like operations.
5
+ module RedisLike
6
+ # Mixin containing redis-like list operations for Moneta stores.
7
+ module Lists
8
+ include RedisLike::Keys
9
+ include RedisLike::Helpers::Lists
10
+
11
+ def lpush(list, item)
12
+ enqueue list, item, to: :head
13
+ end
14
+
15
+ def lpushx(list, item)
16
+ enqueue list, item, to: :head, allow_create: false
17
+ end
18
+
19
+ def rpush(list, item)
20
+ enqueue list, item, to: :tail
21
+ end
22
+
23
+ def rpushx(list, item)
24
+ enqueue list, item, to: :tail, allow_create: false
25
+ end
26
+
27
+ def lpop(list)
28
+ dequeue list, from: :head
29
+ end
30
+
31
+ def rpop(list)
32
+ dequeue list, from: :tail
33
+ end
34
+
35
+ def rpoplpush(source, destination)
36
+ item = rpop source
37
+ lpush destination, item
38
+ item
39
+ end
40
+
41
+ def lindex(list, item)
42
+ fetch(list, []).at item
43
+ end
44
+
45
+ def linsert(list, location, pivot, item)
46
+ return 0 unless exists list
47
+ return -1 unless lrange(list, 0, -1).include? pivot
48
+ items = fetch list, []
49
+ index = items.index pivot
50
+ index += 1 if location == :before
51
+ items.insert(index, item)
52
+ store list, items
53
+ llen list
54
+ end
55
+
56
+ def llen(list)
57
+ fetch(list, []).length
58
+ end
59
+
60
+ def lrange(list, start, stop)
61
+ fetch(list, [])[(start..stop)] || []
62
+ end
63
+
64
+ def lrem(list, count, item)
65
+ case
66
+ when count > 0
67
+ remove_count list, count, item, from: :head
68
+ when count < 0
69
+ remove_count list, count.abs, item, from: :tail
70
+ when count == 0
71
+ remove_all list, item
72
+ end
73
+ end
74
+
75
+ def lset(list, index, item)
76
+ items = fetch list, []
77
+ items[index] = item
78
+ 'OK' if store list, items
79
+ end
80
+
81
+ def ltrim(list, start, stop)
82
+ 'OK' if store list, lrange(list, start, stop)
83
+ end
84
+ end
85
+ end
86
+
87
+ module Moneta
88
+ module Defaults
89
+ include RedisLike::Lists
90
+ end
91
+ end
data/lib/redislike.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative 'redislike/keys'
2
+ require_relative 'redislike/lists'
data/redislike.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'redislike'
3
+ gem.version = '0.1.0'
4
+ gem.licenses = 'MIT'
5
+ gem.authors = ['Chris Olstrom']
6
+ gem.email = 'chris@olstrom.com'
7
+ gem.homepage = 'http://colstrom.github.io/redislike/'
8
+ gem.summary = %Q{For when we want Redis, but can't have nice things.}
9
+ gem.description = %Q{redislike adds backend-independent support for redis-like list operations to any Moneta datastore.}
10
+
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
14
+ gem.require_paths = ['lib']
15
+
16
+ gem.add_runtime_dependency 'moneta', '~> 0.8'
17
+
18
+ gem.add_development_dependency 'kintama', '~> 0.1'
19
+ gem.add_development_dependency 'simplecov', '~> 0.8'
20
+ gem.add_development_dependency 'redis', '~> 3.2'
21
+ end
@@ -0,0 +1,255 @@
1
+ context 'List Operations' do
2
+ def mirror(method, *arguments)
3
+ original = @original.method(method).call(*arguments)
4
+ compatible = @compatible.method(method).call(*arguments)
5
+ [original, compatible]
6
+ end
7
+
8
+ def assert_compatible(method, *arguments)
9
+ assert_equal(*mirror(method, *arguments))
10
+ end
11
+
12
+ setup do
13
+ @original = Redis.new
14
+ # @compatible = Moneta.new :Daybreak, file: '.test.db', expires: true
15
+ @compatible = Moneta.new :Memory
16
+
17
+ @original.del 'test', 'test:a', 'test:b', 'test:nx'
18
+ end
19
+
20
+ teardown do
21
+ @original.disconnect!
22
+ @compatible.close
23
+ end
24
+
25
+ context 'lindex' do
26
+ setup do
27
+ mirror :lpush, 'test', 'foo'
28
+ mirror :lpush, 'test', 'bar'
29
+ end
30
+
31
+ test 'when list does not exist' do
32
+ assert_compatible :lindex, 'test:nx', 1
33
+ end
34
+
35
+ test 'in range' do
36
+ assert_compatible :lindex, 'test', 1
37
+ end
38
+
39
+ test 'out of range' do
40
+ assert_compatible :lindex, 'test', 999
41
+ end
42
+ end
43
+
44
+ context 'linsert' do
45
+ setup do
46
+ mirror :lpush, 'test', 'foo'
47
+ end
48
+
49
+ test 'when list does not exist' do
50
+ assert_compatible :linsert, 'test:nx', :before, 'foo', 'bar'
51
+ assert_compatible :linsert, 'test:nx', :after, 'foo', 'bar'
52
+ end
53
+
54
+ test 'when list contains pivot value' do
55
+ assert_compatible :linsert, 'test', :before, 'foo', 'bar'
56
+ assert_compatible :linsert, 'test', :after, 'foo', 'bar'
57
+ end
58
+
59
+ test 'when list does not contain pivot value' do
60
+ assert_compatible :linsert, 'test', :before, 'baz', 'bar'
61
+ assert_compatible :linsert, 'test', :after, 'baz', 'bar'
62
+ end
63
+ end
64
+
65
+ context 'llen' do
66
+ test 'when list does not exist' do
67
+ assert_compatible :llen, 'test:nx'
68
+ end
69
+
70
+ test 'when list exists' do
71
+ mirror :lpush, 'test', 'foo'
72
+ assert_compatible :llen, 'test'
73
+ end
74
+ end
75
+
76
+ context 'lpop' do
77
+ test 'when list does not exist' do
78
+ assert_compatible :lpop, 'test:nx'
79
+ end
80
+
81
+ test 'when list exists' do
82
+ mirror :lpush, 'test', 'foo'
83
+ assert_compatible :lpop, 'test:nx'
84
+ end
85
+ end
86
+
87
+ context 'lpush' do
88
+ test 'when list does not exist' do
89
+ assert_compatible :lpush, 'test:nx', 'foo'
90
+ end
91
+
92
+ test 'when list has items already' do
93
+ mirror :lpush, 'test', 'foo'
94
+ assert_compatible :lpush, 'test', 'foo'
95
+ end
96
+ end
97
+
98
+ context 'lpushx' do
99
+ test 'when list does not exist' do
100
+ assert_compatible :lpushx, 'test:nx', 'foo'
101
+ end
102
+
103
+ test 'when list has items already' do
104
+ mirror :lpush, 'test', 'foo'
105
+ assert_compatible :lpushx, 'test', 'foo'
106
+ end
107
+ end
108
+
109
+ context 'lrange' do
110
+ test 'when list does not exist' do
111
+ assert_compatible :lrange, 'test:nx', 0, -1
112
+ end
113
+
114
+ test 'when list exists' do
115
+ mirror :lpush, 'test', 'foo'
116
+ assert_compatible :lrange, 'test', 0, -1
117
+ end
118
+
119
+ test 'stop beyond range' do
120
+ mirror :lpush, 'test', 'foo'
121
+ assert_compatible :lrange, 'test', 0, 9
122
+ end
123
+
124
+ test 'stop before start' do
125
+ mirror :lpush, 'test', 'foo'
126
+ assert_compatible :lrange, 'test', -1, 1
127
+ end
128
+ end
129
+
130
+ context 'lrem' do
131
+ setup do
132
+ mirror :lpush, 'test', 'foo'
133
+ mirror :lpush, 'test', 'foo'
134
+ mirror :lpush, 'test', 'foo'
135
+ end
136
+
137
+ test 'when list does not exist' do
138
+ assert_compatible :lrem, 'test:nx', 0, 'foo'
139
+ end
140
+
141
+ test 'all items (present)' do
142
+ assert_compatible :lrem, 'test', 0, 'foo'
143
+ end
144
+
145
+ test 'all items (when not present)' do
146
+ assert_compatible :lrem, 'test', 0, 'bar'
147
+ end
148
+
149
+ test 'one from head (present)' do
150
+ assert_compatible :lrem, 'test', 1, 'foo'
151
+ end
152
+
153
+ test 'one from head (when not present)' do
154
+ assert_compatible :lrem, 'test', 1, 'bar'
155
+ end
156
+
157
+ test 'more than exists from head (present)' do
158
+ assert_compatible :lrem, 'test', 9, 'foo'
159
+ end
160
+
161
+ test 'more than exists from head (when not present)' do
162
+ assert_compatible :lrem, 'test', 9, 'bar'
163
+ end
164
+
165
+ test 'one from tail (present)' do
166
+ assert_compatible :lrem, 'test', -1, 'foo'
167
+ end
168
+
169
+ test 'one from tail (when not present)' do
170
+ assert_compatible :lrem, 'test', -1, 'bar'
171
+ end
172
+
173
+ test 'more than exists from tail (present)' do
174
+ assert_compatible :lrem, 'test', -9, 'foo'
175
+ end
176
+
177
+ test 'more than exists from tail (when not present)' do
178
+ assert_compatible :lrem, 'test', -9, 'bar'
179
+ end
180
+ end
181
+
182
+ context 'lset' do
183
+ setup do
184
+ mirror :lpush, 'test', 'foo'
185
+ mirror :lpush, 'test', 'foo'
186
+ mirror :lpush, 'test', 'foo'
187
+ end
188
+
189
+ test 'in range' do
190
+ assert_compatible :lset, 'test', 1, 'bar'
191
+ assert_equal mirror(:lindex, 'test', 1).uniq, ['bar']
192
+ end
193
+ end
194
+
195
+ context 'ltrim' do
196
+ setup do
197
+ 100.times do
198
+ mirror :lpush, 'test', 'foo'
199
+ end
200
+ end
201
+
202
+ test 'when list does not exist' do
203
+ assert_compatible :ltrim, 'test:nx', 0, 10
204
+ end
205
+
206
+ test 'when range is larger than list length' do
207
+ assert_compatible :ltrim, 'test', 0, 200
208
+ assert_equal mirror(:lrange, 'test', 0, -1).uniq.flatten.size, 100
209
+ end
210
+ end
211
+
212
+ context 'rpop' do
213
+ test 'when list does not exist' do
214
+ assert_compatible :rpop, 'test:nx'
215
+ end
216
+
217
+ test 'when list exists' do
218
+ mirror :rpush, 'test', 'foo'
219
+ assert_compatible :rpop, 'test:nx'
220
+ end
221
+ end
222
+
223
+ context 'rpoplpush' do
224
+ test 'when source list does not exist' do
225
+ assert_compatible :rpoplpush, 'test:nx', 'test:b'
226
+ end
227
+
228
+ test 'when list exists' do
229
+ 3.times { mirror :lpush, 'test:a', 'foo' }
230
+ assert_compatible :rpoplpush, 'test:a', 'test:b'
231
+ end
232
+ end
233
+
234
+ context 'rpush' do
235
+ test 'when list does not exist' do
236
+ assert_compatible :rpush, 'test:nx', 'foo'
237
+ end
238
+
239
+ test 'when list has items already' do
240
+ mirror :rpush, 'test', 'foo'
241
+ assert_compatible :rpush, 'test', 'foo'
242
+ end
243
+ end
244
+
245
+ context 'rpushx' do
246
+ test 'when list does not exist' do
247
+ assert_compatible :rpushx, 'test:nx', 'foo'
248
+ end
249
+
250
+ test 'when list has items already' do
251
+ mirror :rpush, 'test', 'foo'
252
+ assert_compatible :rpushx, 'test', 'foo'
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require 'bundler/setup'
3
+ require 'simplecov'
4
+ SimpleCov.command_name 'Kintama'
5
+ SimpleCov.start { add_filter '/vendor/' }
6
+ require 'redis'
7
+ require 'kintama'
8
+ require 'moneta'
9
+
10
+ require_relative '../lib/redislike'
11
+ require_relative 'redislike/lists'
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redislike
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Olstrom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: moneta
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kintama
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redis
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ description: redislike adds backend-independent support for redis-like list operations
70
+ to any Moneta datastore.
71
+ email: chris@olstrom.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - lib/redislike.rb
82
+ - lib/redislike/helpers/lists.rb
83
+ - lib/redislike/keys.rb
84
+ - lib/redislike/lists.rb
85
+ - redislike.gemspec
86
+ - tests/redislike.rb
87
+ - tests/redislike/lists.rb
88
+ homepage: http://colstrom.github.io/redislike/
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: For when we want Redis, but can't have nice things.
112
+ test_files: []
113
+ has_rdoc: