stal 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gems +2 -0
- data/LICENSE +19 -0
- data/README.md +99 -0
- data/lib/stal.rb +69 -0
- data/makefile +4 -0
- data/stal.gemspec +15 -0
- data/test/all.rb +37 -0
- data/test/helper.rb +1 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 01f32692fdb539b8eebbc7d2964ba275aecef27b
|
4
|
+
data.tar.gz: a971d29478038e640c0e94fd2636e0a57edd807f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dc8a947141e7a9d84d8c78ba58097d5cdb002b2493fdbd5a9752cb98306cd661c4f49e6c2b353c4371bb28ff45f30368b255f200d56a467bfe265c69fe620bfd
|
7
|
+
data.tar.gz: 47e6bb79573cde80bce0ed2554ef135de38f52c45ce5303761886d41f2b14b4bb4994632d45007d342685f21c530241d111dc8abe7a2984ef2beebc4361b082d
|
data/.gems
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Michel Martens
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
Stal
|
2
|
+
====
|
3
|
+
|
4
|
+
Set algebra solver for Redis.
|
5
|
+
|
6
|
+
Description
|
7
|
+
-----------
|
8
|
+
|
9
|
+
`Stal` receives an array with an s-expression composed of commands
|
10
|
+
and key names and resolves the set operations in [Redis][redis].
|
11
|
+
|
12
|
+
Community
|
13
|
+
---------
|
14
|
+
|
15
|
+
Meet us on IRC: [#lesscode](irc://chat.freenode.net/#lesscode) on
|
16
|
+
[freenode.net](http://freenode.net/).
|
17
|
+
|
18
|
+
Getting started
|
19
|
+
---------------
|
20
|
+
|
21
|
+
Install [Redis][redis]. On most platforms it's as easy as grabbing
|
22
|
+
the sources, running make and then putting the `redis-server` binary
|
23
|
+
in the PATH.
|
24
|
+
|
25
|
+
Once you have it installed, you can execute `redis-server` and it
|
26
|
+
will run on `localhost:6379` by default. Check the `redis.conf`
|
27
|
+
file that comes with the sources if you want to change some settings.
|
28
|
+
|
29
|
+
Usage
|
30
|
+
-----
|
31
|
+
|
32
|
+
`Stal` requires a [Redic][redic] compatible client. To make things
|
33
|
+
easier, `redic` is listed as a runtime dependency so the examples
|
34
|
+
in this document will work.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require "stal"
|
38
|
+
|
39
|
+
# Connect the client to the default host
|
40
|
+
redis = Redic.new
|
41
|
+
|
42
|
+
# Use the Redis client to populate some sets
|
43
|
+
redis.call("SADD", "foo", "a", "b", "c")
|
44
|
+
redis.call("SADD", "bar", "b", "c", "d")
|
45
|
+
redis.call("SADD", "baz", "c", "d", "e")
|
46
|
+
redis.call("SADD", "qux", "x", "y", "z")
|
47
|
+
```
|
48
|
+
|
49
|
+
Now we can perform some set operations with `Stal`:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
expr = [:union, "qux", [:diff, [:inter, "foo", "bar"], "baz"]]
|
53
|
+
|
54
|
+
Stal.solve(redis, expr)
|
55
|
+
#=> ["b", "x", "y", "z"]
|
56
|
+
```
|
57
|
+
|
58
|
+
`Stal` translates the shortcuts `:union`, `:diff` and `:inter` into
|
59
|
+
`SDIFFSTORE`, `SINTERSTORE` and `SUNIONSTORE` to perform the
|
60
|
+
underlying operations. You can also use the explicit command
|
61
|
+
(lowercase works too).
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
expr = [:SUNIONSTORE, "qux", [:SDIFFSTORE, [:SINTERSTORE, "foo", "bar"], "baz"]]
|
65
|
+
|
66
|
+
Stal.solve(redis, expr)
|
67
|
+
#=> ["b", "x", "y", "z"]
|
68
|
+
```
|
69
|
+
|
70
|
+
If you want to preview the commands `Stal` will send to generate
|
71
|
+
the results, you can use `Stal.compile`:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
Stal.compile([:inter, [:union, "foo", "bar"], "baz"])
|
75
|
+
# [[:SUNIONSTORE, "stal:55f631dc-...", "foo", "bar"],
|
76
|
+
# [:SINTERSTORE,
|
77
|
+
# "stal:fe5aaec9-...",
|
78
|
+
# "stal:55f631dc-...",
|
79
|
+
# "baz"],
|
80
|
+
# [:SMEMBERS, "stal:fe5aaec9-..."],
|
81
|
+
# [:DEL,
|
82
|
+
# "stal:fe5aaec9-...",
|
83
|
+
# "stal:55f631dc-..."]]
|
84
|
+
```
|
85
|
+
|
86
|
+
All commands are pipelined and wrapped in a `MULTI/EXEC` transaction.
|
87
|
+
The temporary keys, which have been shortened in the example, are
|
88
|
+
deleted immediately.
|
89
|
+
|
90
|
+
|
91
|
+
Installation
|
92
|
+
------------
|
93
|
+
|
94
|
+
```
|
95
|
+
$ gem install stal
|
96
|
+
```
|
97
|
+
|
98
|
+
[redis]: http://redis.io
|
99
|
+
[redic]: https://github.com/amakawa/redic
|
data/lib/stal.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "redic"
|
4
|
+
require "securerandom"
|
5
|
+
|
6
|
+
module Stal
|
7
|
+
ALIASES = {
|
8
|
+
:diff => "SDIFFSTORE",
|
9
|
+
:inter => "SINTERSTORE",
|
10
|
+
:union => "SUNIONSTORE",
|
11
|
+
}
|
12
|
+
|
13
|
+
def self.tr(term)
|
14
|
+
ALIASES.fetch(term, term)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.compute(expr, ids, acc)
|
18
|
+
id = sprintf("stal:%s", SecureRandom.uuid)
|
19
|
+
|
20
|
+
# Keys we need to clean up later
|
21
|
+
ids.push(id)
|
22
|
+
|
23
|
+
# Add command with destination key
|
24
|
+
cmd = [tr(expr[0]), id]
|
25
|
+
|
26
|
+
expr[1..-1].each do |item|
|
27
|
+
if Array === item
|
28
|
+
cmd.push(compute(item, ids, acc))
|
29
|
+
else
|
30
|
+
cmd.push(item)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
acc.push(cmd)
|
35
|
+
|
36
|
+
return id
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.compile(expr)
|
40
|
+
|
41
|
+
# Commands to process
|
42
|
+
acc = []
|
43
|
+
|
44
|
+
# Keys to cleanup
|
45
|
+
ids = []
|
46
|
+
|
47
|
+
id = compute(expr, ids, acc)
|
48
|
+
|
49
|
+
acc.push([:SMEMBERS, id])
|
50
|
+
acc.push([:DEL, *ids])
|
51
|
+
acc
|
52
|
+
end
|
53
|
+
|
54
|
+
# Evaluate expression `expr` in the Redis client `c`.
|
55
|
+
def self.solve(c, expr)
|
56
|
+
operations = compile(expr)
|
57
|
+
|
58
|
+
c.queue("MULTI")
|
59
|
+
|
60
|
+
operations.each do |op|
|
61
|
+
c.queue(*op)
|
62
|
+
end
|
63
|
+
|
64
|
+
c.queue("EXEC")
|
65
|
+
|
66
|
+
# Return the result of SMEMBERS
|
67
|
+
c.commit[-1][-2]
|
68
|
+
end
|
69
|
+
end
|
data/makefile
ADDED
data/stal.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "stal"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = %{Set algebra solver for Redis}
|
5
|
+
s.description = %Q{Stal receives s-expressions and resolves the set operations in Redis}
|
6
|
+
s.authors = ["Michel Martens"]
|
7
|
+
s.email = ["michel@soveran.com"]
|
8
|
+
s.homepage = "https://github.com/soveran/stal"
|
9
|
+
s.license = "MIT"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
|
13
|
+
s.add_dependency "redic"
|
14
|
+
s.add_development_dependency "cutest"
|
15
|
+
end
|
data/test/all.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Redic.new.tap do |c|
|
5
|
+
c.call("FLUSHDB")
|
6
|
+
c.call("SADD", "foo", "a", "b", "c")
|
7
|
+
c.call("SADD", "bar", "b", "c", "d")
|
8
|
+
c.call("SADD", "baz", "c", "d", "e")
|
9
|
+
c.call("SADD", "qux", "x", "y", "z")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
test do |c|
|
14
|
+
|
15
|
+
# Shortcut syntax
|
16
|
+
expr = [:union, "qux", [:diff, [:inter, "foo", "bar"], "baz"]]
|
17
|
+
|
18
|
+
# Explicit syntax
|
19
|
+
expr = [:SUNIONSTORE, "qux", [:SDIFFSTORE, [:SINTERSTORE, "foo", "bar"], "baz"]]
|
20
|
+
|
21
|
+
assert_equal ["b", "x", "y", "z"], Stal.solve(c, expr).sort
|
22
|
+
|
23
|
+
# Explicit syntax with strings
|
24
|
+
expr = ["SUNIONSTORE", "qux", ["SDIFFSTORE", ["SINTERSTORE", "foo", "bar"], "baz"]]
|
25
|
+
|
26
|
+
assert_equal ["b", "x", "y", "z"], Stal.solve(c, expr).sort
|
27
|
+
|
28
|
+
# Explicit syntax with lowercase strings
|
29
|
+
expr = ["sunionstore", "qux", ["sdiffstore", ["sinterstore", "foo", "bar"], "baz"]]
|
30
|
+
|
31
|
+
assert_equal ["b", "x", "y", "z"], Stal.solve(c, expr).sort
|
32
|
+
|
33
|
+
assert_equal ["b", "x", "y", "z"], Stal.solve(c, expr).sort
|
34
|
+
|
35
|
+
# Verify there's no pollution
|
36
|
+
assert_equal ["bar", "baz", "foo", "qux"], c.call("KEYS", "*").sort
|
37
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "../lib/stal"
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michel Martens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redic
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cutest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Stal receives s-expressions and resolves the set operations in Redis
|
42
|
+
email:
|
43
|
+
- michel@soveran.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gems
|
49
|
+
- LICENSE
|
50
|
+
- README.md
|
51
|
+
- lib/stal.rb
|
52
|
+
- makefile
|
53
|
+
- stal.gemspec
|
54
|
+
- test/all.rb
|
55
|
+
- test/helper.rb
|
56
|
+
homepage: https://github.com/soveran/stal
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.0.14
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Set algebra solver for Redis
|
80
|
+
test_files: []
|