skvs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|