Skip to content

Commit

Permalink
restructure handler classes so that instance objects are passed to th…
Browse files Browse the repository at this point in the history
…e rack app. this results in fewer global config vars
  • Loading branch information
technoweenie committed Oct 10, 2009
1 parent faef72d commit 3b3f770
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 50 deletions.
8 changes: 4 additions & 4 deletions lib/rack-sparklines.rb
Expand Up @@ -12,7 +12,7 @@ class Sparklines
# :prefix - URL prefix for handled requests. Setting it to "/sparks"
# treats requests like "/sparks/stats.csv" as dynamic sparklines.
# :directory - local directory that cached PNG files are stored.
# :handler - Handler classes know how to fetch data and pass them
# :handler - Handler instances know how to fetch data and pass them
# to the Sparklines library.
def initialize(app, options = {})
@app, @options = app, options
Expand All @@ -29,13 +29,13 @@ def _call(env)
@data_path.sub! /\.png$/, ''
@png_path = @data_path + ".png"
@cache_file = ::File.join(@options[:directory], @png_path)
@handler = @options[:handler].new(@data_path)
if !@handler.data_exists?
@handler = @options[:handler].set(@data_path)
if !@handler.exists?
return @app.call(env)
end
if !@handler.already_cached?(@cache_file)
@handler.fetch do |data|
::File.open(@cache_file, 'wb' ) do |png|
::File.open(@cache_file, 'wb') do |png|
png << Spark.plot(data, @options[:spark])
end
end
Expand Down
16 changes: 11 additions & 5 deletions lib/rack-sparklines/handlers/abstract_data.rb
Expand Up @@ -5,21 +5,27 @@ module Handlers
# Abstract class for retrieving the data and determining whether the cache
# needs to be refreshed.
class AbstractData
def initialize(data_path)
@data_path = data_path
attr_accessor :data_path

# Setting the data_path returns a duplicate of this object that has any
# custom instance variables (configuration settings, for example).
def set(data_path)
data = dup
data.data_path = data_path
data
end

def already_cached?(cache_file)
if cache_time = File.file?(cache_file) && File.mtime(cache_file)
cache_time > data_updated_at
cache_time > updated_at
end
end

def data_exists?
def exists?
false
end

def data_updated_at
def updated_at
raise NotImplementedError
end

Expand Down
16 changes: 9 additions & 7 deletions lib/rack-sparklines/handlers/csv_data.rb
Expand Up @@ -4,20 +4,22 @@ module Rack::Sparklines::Handlers
# Reads sparkline data from CSV files. Only the first line of numbers are
# read. Requests for "/sparks/stats.csv" will pass a data_path of "stats.csv"
class CsvData < AbstractData
class << self
attr_accessor :directory
attr_accessor :directory

def initialize(directory)
@directory = directory
end

def initialize(data_path)
@data_path = File.join(self.class.directory, data_path)
def data_path=(s)
@data_path = s ? File.join(@directory, s) : nil
end

def data_exists?
def exists?
File.exist?(@data_path)
end

def data_updated_at
File.mtime @data_path
def updated_at
File.mtime(@data_path)
end

def fetch
Expand Down
29 changes: 15 additions & 14 deletions lib/rack-sparklines/handlers/stubbed_data.rb
Expand Up @@ -4,27 +4,28 @@ module Rack::Sparklines::Handlers
# Allows you to stub sparkline data in a global hash. Requests for
# "/sparks/stats.csv" will pass a data_path of "stats.csv"
class StubbedData < AbstractData
class << self
# Data is a hash of hashes. The key is the filename, which points to
# a hash with :updated and :contents keys
#
# StubbedData.data['stats.csv'] = {
# :updated => Time.utc(2009, 10, 1),
# :contents => [1, 2, 3, 4, 5]}
attr_accessor :data
# A hash of hashes where the key is the filename. The key points to
# a hash with :updated and :contents keys
#
# StubbedData.datasets['stats.csv'] = {
# :updated => Time.utc(2009, 10, 1),
# :contents => [1, 2, 3, 4, 5]}
attr_accessor :datasets

def initialize(datasets = {})
@datasets = datasets
end
self.data = {}

def initialize(data_path)
super
@data = self.class.data[data_path]
def data_path=(s)
@data = @datasets[s]
@data_path = s
end

def data_exists?
def exists?
@data
end

def data_updated_at
def updated_at
@data[:updated]
end

Expand Down
37 changes: 17 additions & 20 deletions test/rack-sparklines_test.rb
Expand Up @@ -15,46 +15,45 @@ class SparklinesTest < Test::Unit::TestCase
$data_dir = File.join(File.dirname(__FILE__), 'data')
FileUtils.rm_rf $data_dir
FileUtils.mkdir_p $data_dir
File.open File.join($data_dir, 'missing.csv'), 'wb' do |csv|
File.open File.join($data_dir, 'stats.csv'), 'wb' do |csv|
csv << "47,43,24,47,16,28,38,57,50,76,42,20,98,34,53,1,55,74,63,38,31,98,89"
end
sleep 1
Rack::Sparklines::Handlers::StubbedData.data['missing.csv'] = \
{:updated => Time.utc(2009, 1, 1), :contents => [47, 43, 24, 47, 16, 28, 38, 57, 50, 76, 42, 20, 98, 34, 53, 1, 55, 74, 63, 38, 31, 98, 89]}

def app
stubbed_data = {:updated => Time.utc(2009, 1, 1), :contents => [47, 43, 24, 47, 16, 28, 38, 57, 50, 76, 42, 20, 98, 34, 53, 1, 55, 74, 63, 38, 31, 98, 89]}
Rack::Sparklines.new \
Proc.new {|env| [200, {"Content-Type" => "text/html"}, "booya"] },
:handler => Rack::Sparklines::Handlers::StubbedData,
:handler => Rack::Sparklines::Handlers::StubbedData.new('stats.csv' => stubbed_data),
:prefix => '/sparks',
:directory => $data_dir
end

def setup
@missing_png = File.join($data_dir, 'missing.csv.png')
FileUtils.rm_rf @missing_png
@stats_png = File.join($data_dir, 'stats.csv.png')
FileUtils.rm_rf @stats_png
end

def test_creates_png_from_csv_request
assert !File.exist?(@missing_png)
get "/sparks/missing.csv.png"
assert File.exist?(@missing_png)
assert File.size(@missing_png) > 0
assert_equal IO.read(@missing_png), last_response.body
assert !File.exist?(@stats_png)
get "/sparks/stats.csv.png"
assert File.exist?(@stats_png)
assert File.size(@stats_png) > 0
assert_equal IO.read(@stats_png), last_response.body
end

def test_leaves_recent_cached_png
FileUtils.touch(@missing_png)
get "/sparks/missing.csv.png"
FileUtils.touch(@stats_png)
get "/sparks/stats.csv.png"
assert_equal '', last_response.body
assert_equal 0, File.size(@missing_png)
assert_equal 0, File.size(@stats_png)
end

def test_lets_other_requests_fallthrough
assert !File.exist?(@missing_png)
get "/spark/missing.csv.png"
assert !File.exist?(@stats_png)
get "/spark/stats.csv.png"
assert_equal 'booya', last_response.body
assert !File.exist?(@missing_png)
assert !File.exist?(@stats_png)
end

def test_passes_missing_data_requests_through
Expand All @@ -64,12 +63,10 @@ def test_passes_missing_data_requests_through
end

class SparklinesCSVTest < SparklinesTest
Rack::Sparklines::Handlers::CsvData.directory = $data_dir

def app
Rack::Sparklines.new \
Proc.new {|env| [200, {"Content-Type" => "text/html"}, "booya"] },
:handler => Rack::Sparklines::Handlers::CsvData,
:handler => Rack::Sparklines::Handlers::CsvData.new($data_dir),
:prefix => '/sparks',
:directory => $data_dir
end
Expand Down

0 comments on commit 3b3f770

Please sign in to comment.