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 CHANGED
@@ -4,4 +4,5 @@ coverage
4
4
  rdoc
5
5
  pkg
6
6
  test/data
7
- demos/simple/public/sparks/*
7
+ dump.rdb
8
+ demos/**/public/sparks/*
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.1.0
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/*_test.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -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>
@@ -3,8 +3,6 @@ require 'rubygems'
3
3
  require 'sinatra'
4
4
 
5
5
  require 'sparkplug'
6
- require 'sparkplug/handlers/csv_data'
7
- require 'sparkplug/cachers/filesystem'
8
6
 
9
7
  pub_dir = File.expand_path(File.join(File.dirname(__FILE__), 'public'))
10
8
  use Sparkplug, :prefix => 'sparks',
@@ -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:
@@ -1,4 +1,3 @@
1
- require 'sparkplug'
2
1
  require 'time'
3
2
 
4
3
  class Sparkplug
@@ -1,4 +1,3 @@
1
- require 'sparkplug/cachers/abstract'
2
1
  require 'fileutils'
3
2
 
4
3
  class Sparkplug
@@ -1,4 +1,3 @@
1
- require 'sparkplug/cachers/abstract'
2
1
  require 'fileutils'
3
2
 
4
3
  class Sparkplug
@@ -1,5 +1,3 @@
1
- require 'sparkplug'
2
-
3
1
  class Sparkplug
4
2
  module Handlers
5
3
  # Abstract class for retrieving the data and determining whether the cache
@@ -1,5 +1,3 @@
1
- require 'sparkplug/handlers/abstract_data'
2
-
3
1
  module Sparkplug::Handlers
4
2
  # Reads sparkline data from CSV files. Only the first line of numbers are
5
3
  # read. Requests for "/sparks/stats.csv" will pass a data_path of "stats.csv"
@@ -1,5 +1,3 @@
1
- require 'sparkplug/handlers/abstract_data'
2
-
3
1
  module Sparkplug::Handlers
4
2
  # Allows you to stub sparkline data in a global hash. Requests for
5
3
  # "/sparks/stats.csv" will pass a data_path of "stats.csv"
@@ -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.0.0"
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-11-01}
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.4}
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
+
@@ -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.0.0
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-11-01 00:00:00 -07:00
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.4
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.