skvs 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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/skvs +5 -0
- data/lib/skvs/version.rb +3 -0
- data/lib/skvs.rb +73 -0
- data/lib/views/index.erb +173 -0
- data/readme.markdown +34 -0
- data/skvs.gemspec +24 -0
- metadata +101 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/skvs
ADDED
data/lib/skvs/version.rb
ADDED
data/lib/skvs.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "skvs/version"
|
2
|
+
require "sinatra"
|
3
|
+
require "redis"
|
4
|
+
require "cgi"
|
5
|
+
|
6
|
+
get "/" do
|
7
|
+
keys = redis.keys("skvs:#{params[:q]}*").sort
|
8
|
+
@count = keys.count
|
9
|
+
keys = keys.first(load_count + 1)
|
10
|
+
|
11
|
+
if keys.size > load_count
|
12
|
+
keys.pop
|
13
|
+
@oversized = true
|
14
|
+
end
|
15
|
+
|
16
|
+
if keys.any?
|
17
|
+
values = redis.mget(*keys)
|
18
|
+
@data = keys.map{|k| k.split(":", 2).last }.zip(values)
|
19
|
+
else
|
20
|
+
@data = []
|
21
|
+
end
|
22
|
+
|
23
|
+
erb :index
|
24
|
+
end
|
25
|
+
|
26
|
+
get "/*" do
|
27
|
+
content_type "text/plain"
|
28
|
+
redis_get params[:splat]
|
29
|
+
end
|
30
|
+
|
31
|
+
put "/*" do
|
32
|
+
redis_set params[:splat], request.body.read
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
post "/_set" do
|
37
|
+
redis_set params[:key], params[:value]
|
38
|
+
redirect url("/")
|
39
|
+
end
|
40
|
+
|
41
|
+
helpers do
|
42
|
+
def load_count
|
43
|
+
[params[:load_count].to_i, 5].max
|
44
|
+
end
|
45
|
+
|
46
|
+
def h(v)
|
47
|
+
CGI.escapeHTML(v)
|
48
|
+
end
|
49
|
+
|
50
|
+
def hbr(v)
|
51
|
+
CGI.escapeHTML(v).gsub("\n", "\n<br/>")
|
52
|
+
end
|
53
|
+
|
54
|
+
def key(*k)
|
55
|
+
"skvs:#{k.flatten.join("/")}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def redis
|
59
|
+
@redis ||= Redis.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def redis_set(k, v)
|
63
|
+
if v.to_s == ""
|
64
|
+
redis.del key(*k)
|
65
|
+
else
|
66
|
+
redis.set key(*k), v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def redis_get(k)
|
71
|
+
redis.get key(k)
|
72
|
+
end
|
73
|
+
end
|
data/lib/views/index.erb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
table{
|
5
|
+
border-collapse:separate;
|
6
|
+
border-spacing: 0 20px;
|
7
|
+
|
8
|
+
width: 100%;
|
9
|
+
}
|
10
|
+
|
11
|
+
table td, table th{
|
12
|
+
width: 50%;
|
13
|
+
padding: 0 10px;
|
14
|
+
}
|
15
|
+
|
16
|
+
table tfoot a{
|
17
|
+
text-decoration: underline;
|
18
|
+
}
|
19
|
+
|
20
|
+
table td{
|
21
|
+
border-left: 1px solid #eee;
|
22
|
+
}
|
23
|
+
|
24
|
+
table th{
|
25
|
+
text-align: right;
|
26
|
+
font-weight: bold;
|
27
|
+
border-right: 1px solid #fff;
|
28
|
+
}
|
29
|
+
|
30
|
+
table tr:hover th{
|
31
|
+
border-right: 1px solid #eee;
|
32
|
+
}
|
33
|
+
|
34
|
+
* {
|
35
|
+
font-weight: inherit;
|
36
|
+
text-decoration: inherit;
|
37
|
+
color: inherit;
|
38
|
+
}
|
39
|
+
strong {
|
40
|
+
font-weight: bold;
|
41
|
+
}
|
42
|
+
|
43
|
+
body {
|
44
|
+
font-family: "helvetica neue", arial, sans-serif;
|
45
|
+
font-weight: 200;
|
46
|
+
}
|
47
|
+
|
48
|
+
#search, tfoot {
|
49
|
+
text-align: center;
|
50
|
+
}
|
51
|
+
|
52
|
+
h1, td *, th *, th, td{
|
53
|
+
vertical-align: top;
|
54
|
+
}
|
55
|
+
|
56
|
+
h1 {
|
57
|
+
text-align: center;
|
58
|
+
font-weight: bold;
|
59
|
+
}
|
60
|
+
textarea {
|
61
|
+
padding: 2px;
|
62
|
+
}
|
63
|
+
|
64
|
+
</style>
|
65
|
+
<title>Simple Key/Value Store (SKVS)</title>
|
66
|
+
</head>
|
67
|
+
<body>
|
68
|
+
<h1>Simple Key/Value Store (SKVS)</h1>
|
69
|
+
|
70
|
+
<form id="search" method="get" action="<%= url("/") %>">
|
71
|
+
<input type='search' placeholder='search' name='q' value="<%=h params[:q].to_s %>" />
|
72
|
+
</form>
|
73
|
+
|
74
|
+
<form id="main" method="post" action="<%= url("/_set") %>">
|
75
|
+
|
76
|
+
<table id="data">
|
77
|
+
<thead>
|
78
|
+
<tr>
|
79
|
+
<th>
|
80
|
+
<input form="main" name="key" placeholder="key" required />
|
81
|
+
</th>
|
82
|
+
<td>
|
83
|
+
<textarea form="main" name="value" placeholder="value" ></textarea>
|
84
|
+
<input form="main" type="submit" value="set" />
|
85
|
+
</td>
|
86
|
+
</tr>
|
87
|
+
</thead>
|
88
|
+
<tfoot>
|
89
|
+
<tr>
|
90
|
+
<td colspan="2">
|
91
|
+
<% if @count > load_count %>
|
92
|
+
Showing <strong><%= load_count %></strong> of <strong><%= @count %></strong>,
|
93
|
+
<a href="<%= url("/?load_count=#{load_count*2}&q=#{URI.encode(params[:q].to_s)}") %>">
|
94
|
+
load more?
|
95
|
+
</a>
|
96
|
+
<% else %>
|
97
|
+
Showing all <strong><%= @count %></strong>.
|
98
|
+
<% end %>
|
99
|
+
</td>
|
100
|
+
</tr>
|
101
|
+
</tfoot>
|
102
|
+
<tbody>
|
103
|
+
<% @data.each do |k,v| %>
|
104
|
+
<tr>
|
105
|
+
<th><a href="<%= url("/#{k}") %>"><%=h k %></a></th>
|
106
|
+
<% if v.length < 100 %>
|
107
|
+
<td><%=hbr v %></td>
|
108
|
+
<% else %>
|
109
|
+
<td class="truncated"><%=hbr v[0..100] %>...</td>
|
110
|
+
<% end %>
|
111
|
+
</tr>
|
112
|
+
<% end %>
|
113
|
+
</tbody>
|
114
|
+
</table>
|
115
|
+
|
116
|
+
</form>
|
117
|
+
|
118
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
119
|
+
|
120
|
+
<script type="text/javascript" charset="utf-8">
|
121
|
+
$(function(){
|
122
|
+
$("input[name=key]").focus();
|
123
|
+
$("tbody").on("click", "td", function(){
|
124
|
+
var $t = $(this),
|
125
|
+
key = $t.parent("tr").find("th").text();
|
126
|
+
|
127
|
+
if($t.hasClass("truncated")){
|
128
|
+
$.get(<%= url("/").inspect %>+key, function(value){
|
129
|
+
$("input[name=key]").val(key);
|
130
|
+
$("textarea[name=value]").val(value).focus().select();
|
131
|
+
});
|
132
|
+
}else{
|
133
|
+
$("input[name=key]").val(key);
|
134
|
+
$("textarea[name=value]").val($t.text()).focus().select();
|
135
|
+
}
|
136
|
+
});
|
137
|
+
|
138
|
+
|
139
|
+
var update = function(url){
|
140
|
+
$.get(url, function(data){
|
141
|
+
var doc = $(data);
|
142
|
+
$("#data tbody").replaceWith(doc.find("#data tbody"));
|
143
|
+
$("#data tfoot").replaceWith(doc.find("#data tfoot"));
|
144
|
+
$("#search input").val(doc.find("input[type=search]").val());
|
145
|
+
});
|
146
|
+
}
|
147
|
+
|
148
|
+
|
149
|
+
var oldUrl = null;
|
150
|
+
var seachCallback = function(el){
|
151
|
+
var url = "/?q="+encodeURIComponent($(this).val());
|
152
|
+
if(oldUrl === url){
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
oldUrl = url;
|
156
|
+
window.history.pushState({}, "", url);
|
157
|
+
update(url);
|
158
|
+
};
|
159
|
+
|
160
|
+
$(window).on("popstate", function(){
|
161
|
+
update(document.location);
|
162
|
+
})
|
163
|
+
|
164
|
+
$("#search input")
|
165
|
+
.on("keyup", seachCallback)
|
166
|
+
.on("change", seachCallback)
|
167
|
+
.on("search", seachCallback);
|
168
|
+
|
169
|
+
});
|
170
|
+
</script>
|
171
|
+
|
172
|
+
</body>
|
173
|
+
</html>
|
data/readme.markdown
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
SKVS - Simple Key/Value Store
|
2
|
+
=============================
|
3
|
+
|
4
|
+
A dirt simple HTTP key value store, with a fancy UI.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
1. Install the gem:
|
9
|
+
|
10
|
+
$ gem install skvs
|
11
|
+
|
12
|
+
2. Start the server:
|
13
|
+
|
14
|
+
$ skvs
|
15
|
+
|
16
|
+
3. Navigate the the UI:
|
17
|
+
|
18
|
+
[http://localhost:7654/](http://localhost:7654/)
|
19
|
+
|
20
|
+
|
21
|
+
Example Bash Client
|
22
|
+
-------------------
|
23
|
+
|
24
|
+
function skvs_pull(){
|
25
|
+
echo $(curl -s -o - "http://localhost:7654/$1")
|
26
|
+
}
|
27
|
+
|
28
|
+
function skvs_push(){
|
29
|
+
local key
|
30
|
+
key=$1
|
31
|
+
shift
|
32
|
+
curl -XPUT --data-binary "$*" -s -o /dev/null "http://localhost:7654/$key"
|
33
|
+
}
|
34
|
+
|
data/skvs.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "skvs/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "skvs"
|
7
|
+
s.version = Skvs::VERSION
|
8
|
+
s.authors = ["Tom Lea"]
|
9
|
+
s.email = ["commit@tomlea.co.uk"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Simple Key/Value Store}
|
12
|
+
s.description = %q{A dirt simple HTTP key value store, with a fancy UI.
|
13
|
+
}
|
14
|
+
|
15
|
+
s.rubyforge_project = "skvs"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency "redis"
|
23
|
+
s.add_runtime_dependency "sinatra"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skvs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tom Lea
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-09 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: sinatra
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: "A dirt simple HTTP key value store, with a fancy UI.\n "
|
49
|
+
email:
|
50
|
+
- commit@tomlea.co.uk
|
51
|
+
executables:
|
52
|
+
- skvs
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- Rakefile
|
61
|
+
- bin/skvs
|
62
|
+
- lib/skvs.rb
|
63
|
+
- lib/skvs/version.rb
|
64
|
+
- lib/views/index.erb
|
65
|
+
- readme.markdown
|
66
|
+
- skvs.gemspec
|
67
|
+
homepage: ""
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: skvs
|
96
|
+
rubygems_version: 1.8.13
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Simple Key/Value Store
|
100
|
+
test_files: []
|
101
|
+
|