sparkplug 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/VERSION +1 -1
- data/demos/redis/Rakefile +10 -0
- data/demos/redis/lib/redis_demo/handler.rb +27 -0
- data/demos/redis/lib/redis_demo/spark_list.rb +111 -0
- data/demos/redis/sparkplug_redis.rb +74 -0
- data/demos/redis/test/redis_demo_test.rb +93 -0
- data/demos/redis/views/show.erb +35 -0
- data/demos/simple/sparkplug_demo.rb +0 -2
- data/lib/sparkplug.rb +12 -0
- data/lib/sparkplug/cachers/abstract.rb +0 -1
- data/lib/sparkplug/cachers/filesystem.rb +0 -1
- data/lib/sparkplug/cachers/memory.rb +0 -1
- data/lib/sparkplug/handlers/abstract_data.rb +0 -2
- data/lib/sparkplug/handlers/csv_data.rb +0 -2
- data/lib/sparkplug/handlers/stubbed_data.rb +0 -2
- data/sparkplug.gemspec +15 -3
- data/test/sparkplug_test.rb +0 -4
- metadata +11 -3
data/.gitignore
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.1.0
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedisDemo
|
2
|
+
class Handler < Sparkplug::Handlers::AbstractData
|
3
|
+
attr_reader :list
|
4
|
+
|
5
|
+
def initialize(list)
|
6
|
+
@list = list
|
7
|
+
end
|
8
|
+
|
9
|
+
def data_path=(v)
|
10
|
+
v.sub!(/^\//, '')
|
11
|
+
@data_path = v
|
12
|
+
@plug = @list.find(@data_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def exists?
|
16
|
+
@plug.exists?
|
17
|
+
end
|
18
|
+
|
19
|
+
def updated_at
|
20
|
+
@plug.updated_at
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch
|
24
|
+
yield @plug.datapoints
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module RedisDemo
|
2
|
+
# Persists sparkline data to Redis. Data is pushed and popped from a Redis list.
|
3
|
+
class SparkList
|
4
|
+
attr_reader :redis, :plug_list
|
5
|
+
attr_accessor :prefix, :limit
|
6
|
+
|
7
|
+
# SparkList options
|
8
|
+
# :prefix => 'plug'
|
9
|
+
# :limit => 50
|
10
|
+
# :redis => {} # passed to Redis
|
11
|
+
def initialize(options = {})
|
12
|
+
@prefix = options[:prefix] || 'plug'
|
13
|
+
@limit = options[:limit] || 50
|
14
|
+
@index_key = "#{@prefix}:index"
|
15
|
+
@redis = Redis.new(options[:redis] || {})
|
16
|
+
@plug_list = PlugList.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(name)
|
20
|
+
Plug.new(self, name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class PlugList
|
25
|
+
def initialize(list)
|
26
|
+
@list = list
|
27
|
+
@redis = @list.redis
|
28
|
+
@list_key = "#{@prefix}:sparkplug:list"
|
29
|
+
end
|
30
|
+
|
31
|
+
def add(plug)
|
32
|
+
@redis.sadd @list_key, plug.name
|
33
|
+
@redis.sort @list_key, :order => "ALPHA"
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(plug)
|
37
|
+
@redis.srem @list_key, plug.name
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
@redis.smembers(@list_key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# represents a list of datapoints
|
46
|
+
class Plug
|
47
|
+
attr_reader :name
|
48
|
+
|
49
|
+
def initialize(list, name)
|
50
|
+
@list = list
|
51
|
+
@redis = @list.redis
|
52
|
+
@name = name
|
53
|
+
@name_key = @updated_key = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def add(value)
|
57
|
+
points = count
|
58
|
+
@redis.rpush(name_key, value.to_i)
|
59
|
+
if points.zero?
|
60
|
+
@list.plug_list.add(self)
|
61
|
+
end
|
62
|
+
excess = points + 1 - @list.limit
|
63
|
+
if excess > 0
|
64
|
+
excess.times { @redis.lpop(name_key) }
|
65
|
+
end
|
66
|
+
self.updated_at = Time.now.utc
|
67
|
+
end
|
68
|
+
|
69
|
+
def datapoints
|
70
|
+
@redis.lrange(name_key, 0, @list.limit-1).map! { |p| p.to_i }
|
71
|
+
end
|
72
|
+
|
73
|
+
def updated_at
|
74
|
+
value = @redis.get(updated_key)
|
75
|
+
value.nil? ? Time.now.utc : Time.parse(value)
|
76
|
+
end
|
77
|
+
|
78
|
+
def updated_at=(v)
|
79
|
+
@redis.set(updated_key, v.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete
|
83
|
+
@list.plug_list.delete(self)
|
84
|
+
@redis.delete(name_key)
|
85
|
+
@redis.delete(updated_key)
|
86
|
+
end
|
87
|
+
|
88
|
+
def exists?
|
89
|
+
count > 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def count
|
93
|
+
@redis.llen(name_key)
|
94
|
+
end
|
95
|
+
|
96
|
+
def name_key
|
97
|
+
@name_key ||= plug_name(@name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def updated_key
|
101
|
+
@updated_key ||= plug_name(@name, :updated)
|
102
|
+
end
|
103
|
+
|
104
|
+
# convert a simple plug name to a full plug name
|
105
|
+
def plug_name(plug, *extras)
|
106
|
+
s = "#{@list.prefix}:#{plug}"
|
107
|
+
extras.each { |e| s << ':' << e.to_s }
|
108
|
+
s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..', 'lib')
|
2
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
3
|
+
require 'rubygems'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'redis'
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
require 'sparkplug'
|
9
|
+
require 'redis_demo/spark_list'
|
10
|
+
require 'redis_demo/handler'
|
11
|
+
|
12
|
+
config =
|
13
|
+
if ENV['CONFIG']
|
14
|
+
if File.exist?(ENV['CONFIG'])
|
15
|
+
require 'yaml'
|
16
|
+
YAML.load_file(ENV['CONFIG'])
|
17
|
+
else
|
18
|
+
puts "Config file #{ENV['CONFIG'].inspect} not found."
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
else
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
|
25
|
+
$redis_list = RedisDemo::SparkList.new(config)
|
26
|
+
$handler = RedisDemo::Handler.new($redis_list)
|
27
|
+
pub_dir = File.expand_path(File.join(File.dirname(__FILE__), 'public'))
|
28
|
+
use Sparkplug, :prefix => 'sparks',
|
29
|
+
:handler => $handler,
|
30
|
+
:cacher => Sparkplug::Cachers::Filesystem.new(File.join(pub_dir, 'sparks'))
|
31
|
+
|
32
|
+
get '/' do
|
33
|
+
@plug_list = $redis_list.plug_list
|
34
|
+
erb :show
|
35
|
+
end
|
36
|
+
|
37
|
+
post '/modify' do
|
38
|
+
case params[:commit]
|
39
|
+
when /delete/i
|
40
|
+
$redis_list.find(params[:plug]).delete
|
41
|
+
redirect '/'
|
42
|
+
else
|
43
|
+
plug = $redis_list.find(params[:plug])
|
44
|
+
plug.add(params[:value])
|
45
|
+
redirect "/#{params[:plug]}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
post '/:plug/:value' do
|
50
|
+
plug = $redis_list.find(params[:plug])
|
51
|
+
plug.add(params[:value])
|
52
|
+
'ok'
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/:plug.json' do
|
56
|
+
plug = $redis_list.find(params[:plug])
|
57
|
+
"[#{plug.datapoints * ","}]"
|
58
|
+
end
|
59
|
+
|
60
|
+
get '/:plug' do
|
61
|
+
@plug_list = $redis_list.plug_list
|
62
|
+
@plug = $redis_list.find(params[:plug])
|
63
|
+
erb :show
|
64
|
+
end
|
65
|
+
|
66
|
+
delete '/:plug' do
|
67
|
+
plug = $redis_list.find(params[:plug])
|
68
|
+
plug.delete
|
69
|
+
'deleted'
|
70
|
+
end
|
71
|
+
|
72
|
+
def h(s)
|
73
|
+
Rack::Utils.escape(s)
|
74
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rack'
|
4
|
+
require 'rack/test'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
|
7
|
+
require 'sparkplug_redis'
|
8
|
+
|
9
|
+
$redis_list.redis.flushall
|
10
|
+
$redis_list.prefix = 'test'
|
11
|
+
$redis_list.limit = 5
|
12
|
+
sake = $redis_list.find('sake')
|
13
|
+
unagi = $redis_list.find('unagi')
|
14
|
+
sake.add 1
|
15
|
+
sake.add 2
|
16
|
+
sake.add 3
|
17
|
+
unagi.add 1
|
18
|
+
|
19
|
+
class RedisDemoTest < Test::Unit::TestCase
|
20
|
+
include Rack::Test::Methods
|
21
|
+
|
22
|
+
def app
|
23
|
+
Sinatra::Application
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_root_url
|
27
|
+
get '/'
|
28
|
+
assert last_response.ok?
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_adding_a_datapoint_for_new_plug
|
32
|
+
get '/koi.json'
|
33
|
+
assert_equal '[]', last_response.body
|
34
|
+
|
35
|
+
post '/koi/5'
|
36
|
+
assert last_response.ok?
|
37
|
+
|
38
|
+
get '/koi.json'
|
39
|
+
assert_equal '[5]', last_response.body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_datapoint_limit
|
43
|
+
10.times do |i|
|
44
|
+
post "/shiira/#{i + 5}"
|
45
|
+
end
|
46
|
+
get "/shiira.json"
|
47
|
+
assert_equal "[10,11,12,13,14]", last_response.body
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_fetching_csv_data_for_existing_slug
|
51
|
+
get '/sake.json'
|
52
|
+
assert_equal '[1,2,3]', last_response.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_deletes_plug
|
56
|
+
get '/unagi.json'
|
57
|
+
assert_equal '[1]', last_response.body
|
58
|
+
|
59
|
+
delete '/unagi'
|
60
|
+
|
61
|
+
get '/unagi.json'
|
62
|
+
assert_equal '[]', last_response.body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class RedisDemoPlugTest < Test::Unit::TestCase
|
67
|
+
def test_plug_list_stores_existing_plugs
|
68
|
+
assert $redis_list.plug_list.all.include?('sake')
|
69
|
+
assert !$redis_list.plug_list.all.include?('flounder')
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_adding_plug_adds_to_datapoint
|
73
|
+
assert !$redis_list.plug_list.all.include?('tamago')
|
74
|
+
$redis_list.find('tamago').add 5
|
75
|
+
assert $redis_list.plug_list.all.include?('tamago')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_removing_plug_clears_from_datapoint
|
79
|
+
$redis_list.find('tamago').add 5
|
80
|
+
assert $redis_list.plug_list.all.include?('tamago')
|
81
|
+
$redis_list.find('tamago').delete
|
82
|
+
assert !$redis_list.plug_list.all.include?('tamago')
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_adding_datapoint_sets_updated_at
|
86
|
+
plug = $redis_list.find('shiira')
|
87
|
+
plug.add 1
|
88
|
+
date = plug.updated_at
|
89
|
+
sleep 1
|
90
|
+
plug.add 5
|
91
|
+
assert_not_equal date, plug.updated_at
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
4
|
+
<title>Sparkplug - Ruby Rack module for generating sparkline graphs on the fly</title>
|
5
|
+
<style type="text/css" media="screen">
|
6
|
+
h1, div, p {
|
7
|
+
font-family: verdana;
|
8
|
+
}
|
9
|
+
</style>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<h1>Sparkplug Redis Demo<% if @plug %>: <%=h @plug.name %><% end %></h1>
|
13
|
+
<% if @plug %>
|
14
|
+
<div>
|
15
|
+
<img src="/sparks/<%= @plug.name %>.png" />
|
16
|
+
</div>
|
17
|
+
<p>(if you can see this, the rack module works!)</p>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<p><strong>Existing:</strong> <%= @plug_list.all.map { |name| %(<a href="/#{h(name)}">#{h(name)}</a>) }.join(", ") %></p>
|
21
|
+
<% if @plug %>
|
22
|
+
<p><a href="/">Back</a></p>
|
23
|
+
<% end %>
|
24
|
+
<form action="/modify" method="post">
|
25
|
+
<div>
|
26
|
+
<label>Name: <input type="text" name="plug" value="<%= @plug.name if @plug %>" /></label><br />
|
27
|
+
<label>Value: <input type="text" name="value" value="" size="5" /></label><br />
|
28
|
+
<input type="submit" name="commit" value="Save">
|
29
|
+
<% if @plug %>
|
30
|
+
<input type="submit" name="commit" value="Delete">
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
</form>
|
34
|
+
</body>
|
35
|
+
</html>
|
data/lib/sparkplug.rb
CHANGED
@@ -3,6 +3,18 @@ require 'spark_pr'
|
|
3
3
|
# Render sparkline graphs dynamically from datapoints in a matching CSV file
|
4
4
|
# (or anything that there is a Handler for).
|
5
5
|
class Sparkplug
|
6
|
+
module Cachers
|
7
|
+
autoload :Abstract, 'sparkplug/cachers/abstract'
|
8
|
+
autoload :Filesystem, 'sparkplug/cachers/filesystem'
|
9
|
+
autoload :Memory, 'sparkplug/cachers/memory'
|
10
|
+
end
|
11
|
+
|
12
|
+
module Handlers
|
13
|
+
autoload :AbstractData, 'sparkplug/handlers/abstract_data'
|
14
|
+
autoload :CsvData, 'sparkplug/handlers/csv_data'
|
15
|
+
autoload :StubbedData, 'sparkplug/handlers/stubbed_data'
|
16
|
+
end
|
17
|
+
|
6
18
|
DEFAULT_SPARK_OPTIONS = {:has_min => true, :has_max => true, :height => 40, :step => 10}
|
7
19
|
|
8
20
|
# Options:
|
data/sparkplug.gemspec
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{sparkplug}
|
5
|
-
s.version = "2.
|
8
|
+
s.version = "2.1.0"
|
6
9
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
11
|
s.authors = ["rick"]
|
9
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-12-29}
|
10
13
|
s.email = %q{technoweenie@gmail.com}
|
11
14
|
s.extra_rdoc_files = [
|
12
15
|
"LICENSE",
|
@@ -19,6 +22,14 @@ Gem::Specification.new do |s|
|
|
19
22
|
"README.rdoc",
|
20
23
|
"Rakefile",
|
21
24
|
"VERSION",
|
25
|
+
"demos/redis/Rakefile",
|
26
|
+
"demos/redis/lib/redis_demo/handler.rb",
|
27
|
+
"demos/redis/lib/redis_demo/spark_list.rb",
|
28
|
+
"demos/redis/public/sparks/foo.png",
|
29
|
+
"demos/redis/public/sparks/technoweenie.png",
|
30
|
+
"demos/redis/sparkplug_redis.rb",
|
31
|
+
"demos/redis/test/redis_demo_test.rb",
|
32
|
+
"demos/redis/views/show.erb",
|
22
33
|
"demos/simple/public/temps/portland/2007.csv",
|
23
34
|
"demos/simple/sparkplug_demo.rb",
|
24
35
|
"demos/simple/views/readme.erb",
|
@@ -36,7 +47,7 @@ Gem::Specification.new do |s|
|
|
36
47
|
s.homepage = %q{http://github.com/technoweenie/sparkplug}
|
37
48
|
s.rdoc_options = ["--charset=UTF-8"]
|
38
49
|
s.require_paths = ["lib"]
|
39
|
-
s.rubygems_version = %q{1.3.
|
50
|
+
s.rubygems_version = %q{1.3.5}
|
40
51
|
s.summary = %q{Rack module that dynamically generates sparkline graphs from a set of numbers.}
|
41
52
|
s.test_files = [
|
42
53
|
"test/sparkplug_test.rb"
|
@@ -52,3 +63,4 @@ Gem::Specification.new do |s|
|
|
52
63
|
else
|
53
64
|
end
|
54
65
|
end
|
66
|
+
|
data/test/sparkplug_test.rb
CHANGED
@@ -6,10 +6,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
6
6
|
require 'rack'
|
7
7
|
require 'rack/test'
|
8
8
|
require 'sparkplug'
|
9
|
-
require 'sparkplug/handlers/stubbed_data'
|
10
|
-
require 'sparkplug/handlers/csv_data'
|
11
|
-
require 'sparkplug/cachers/filesystem'
|
12
|
-
require 'sparkplug/cachers/memory'
|
13
9
|
|
14
10
|
class SparkplugTest < Test::Unit::TestCase
|
15
11
|
include Rack::Test::Methods
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sparkplug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- rick
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-29 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,6 +29,14 @@ files:
|
|
29
29
|
- README.rdoc
|
30
30
|
- Rakefile
|
31
31
|
- VERSION
|
32
|
+
- demos/redis/Rakefile
|
33
|
+
- demos/redis/lib/redis_demo/handler.rb
|
34
|
+
- demos/redis/lib/redis_demo/spark_list.rb
|
35
|
+
- demos/redis/public/sparks/foo.png
|
36
|
+
- demos/redis/public/sparks/technoweenie.png
|
37
|
+
- demos/redis/sparkplug_redis.rb
|
38
|
+
- demos/redis/test/redis_demo_test.rb
|
39
|
+
- demos/redis/views/show.erb
|
32
40
|
- demos/simple/public/temps/portland/2007.csv
|
33
41
|
- demos/simple/sparkplug_demo.rb
|
34
42
|
- demos/simple/views/readme.erb
|
@@ -66,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
74
|
requirements: []
|
67
75
|
|
68
76
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.3.
|
77
|
+
rubygems_version: 1.3.5
|
70
78
|
signing_key:
|
71
79
|
specification_version: 3
|
72
80
|
summary: Rack module that dynamically generates sparkline graphs from a set of numbers.
|