diff --git a/Gemfile b/Gemfile index 1604589..5fc5ebb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,5 @@ source "http://rubygems.org" -# Add dependencies required to use your gem here. -# Example: -# gem "activesupport", ">= 2.3.5" -# Add dependencies to develop your gem here. -# Include everything needed to run rake, tests, features, etc. group :development do gem "minitest", ">= 0" -# gem "bundler", "~> 1.0.0" -# gem "jeweler", "~> 1.5.2" -# gem "rcov", ">= 0" -# gem "rocco" end diff --git a/lib/stathat.rb b/lib/stathat.rb index 711fb32..5a12351 100644 --- a/lib/stathat.rb +++ b/lib/stathat.rb @@ -6,196 +6,179 @@ require 'singleton' module StatHat - class Common - CLASSIC_VALUE_URL = "https://api.stathat.com/v" - CLASSIC_COUNT_URL = "https://api.stathat.com/c" - EZ_URL = "https://api.stathat.com/ez" - - class << self - def send_to_stathat(url, args) - uri = URI.parse(url) - - begin - uri.query = URI.encode_www_form(args) - rescue NoMethodError => e - # backwards compatability for pre 1.9.x - uri.query = args.map { |arg, val| arg.to_s + "=" + CGI::escape(val.to_s) }.join('&') - end - - resp = Net::HTTP.get(uri) - return Response.new(resp) - end - end - end - - class SyncAPI - class << self - def ez_post_value(stat_name, ezkey, value, timestamp=nil) - args = { :stat => stat_name, - :ezkey => ezkey, - :value => value } - args[:t] = timestamp unless timestamp.nil? - Common::send_to_stathat(Common::EZ_URL, args) - end - - def ez_post_count(stat_name, ezkey, count, timestamp=nil) - args = { :stat => stat_name, - :ezkey => ezkey, - :count => count } - args[:t] = timestamp unless timestamp.nil? - Common::send_to_stathat(Common::EZ_URL, args) - end - - def post_count(stat_key, user_key, count, timestamp=nil) - args = { :key => stat_key, - :ukey => user_key, - :count => count } - args[:t] = timestamp unless timestamp.nil? - Common::send_to_stathat(Common::CLASSIC_COUNT_URL, args) - end - - def post_value(stat_key, user_key, value, timestamp=nil) - args = { :key => stat_key, - :ukey => user_key, - :value => value } - args[:t] = timestamp unless timestamp.nil? - Common::send_to_stathat(Common::CLASSIC_VALUE_URL, args) - end - end - end - - class API - class << self - def ez_post_value(stat_name, ezkey, value, timestamp=nil, &block) - Reporter.instance.ez_post_value(stat_name, ezkey, value, timestamp, block) - end - - def ez_post_count(stat_name, ezkey, count, timestamp=nil, &block) - Reporter.instance.ez_post_count(stat_name, ezkey, count, timestamp, block) - end - - def post_count(stat_key, user_key, count, timestamp=nil, &block) - Reporter.instance.post_count(stat_key, user_key, count, timestamp, block) - end - - def post_value(stat_key, user_key, value, timestamp=nil, &block) - Reporter.instance.post_value(stat_key, user_key, value, timestamp, block) - end - end - end - - class Reporter - include Singleton - - def initialize - @que = Queue.new - @runlock = Mutex.new - run_pool() - end - - def finish() - stop_pool - # XXX serialize queue? - end - - def post_value(stat_key, user_key, value, timestamp, cb) - args = { :key => stat_key, - :ukey => user_key, - :value => value } - args[:t] = timestamp unless timestamp.nil? - enqueue(Common::CLASSIC_VALUE_URL, args, cb) - end - - def post_count(stat_key, user_key, count, timestamp, cb) - args = { :key => stat_key, - :ukey => user_key, - :count => count } - args[:t] = timestamp unless timestamp.nil? - enqueue(Common::CLASSIC_COUNT_URL, args, cb) - end - - def ez_post_value(stat_name, ezkey, value, timestamp, cb) - args = { :stat => stat_name, - :ezkey => ezkey, - :value => value } - args[:t] = timestamp unless timestamp.nil? - enqueue(Common::EZ_URL, args, cb) - end - - def ez_post_count(stat_name, ezkey, count, timestamp, cb) - args = { :stat => stat_name, - :ezkey => ezkey, - :count => count } - args[:t] = timestamp unless timestamp.nil? - enqueue(Common::EZ_URL, args, cb) - end - - private - def run_pool - @runlock.synchronize { @running = true } - @pool = [] - 5.times do |i| - @pool[i] = Thread.new do - while true do - point = @que.pop - # XXX check for error? - begin - resp = Common::send_to_stathat(point[:url], point[:args]) - if point[:cb] - point[:cb].call(resp) - end - rescue - pp $! - end - @runlock.synchronize { - break unless @running - } - end - end - end - end - - def stop_pool() - @runlock.synchronize { - @running = false - } - @pool.each do |th| - th.join if th && th.alive? - end - end - - def enqueue(url, args, cb=nil) - return false unless @running - point = {:url => url, :args => args, :cb => cb} - @que << point - true - end - end - class Response - def initialize(body) - @body = body - @parsed = nil - end - - def valid? - return status == 200 - end - - def status - parse - return @parsed['status'] - end - - def msg - parse - return @parsed['msg'] - end - - private - def parse - return unless @parsed.nil? - @parsed = JSON.parse(@body) - end + class Common + CLASSIC_VALUE_URL = "https://api.stathat.com/v" + CLASSIC_COUNT_URL = "https://api.stathat.com/c" + EZ_URL = "https://api.stathat.com/ez" + + class << self + def send_to_stathat(url, args) + uri = URI.parse(url) + uri.query = URI.encode_www_form(args) + resp = Net::HTTP.get_response(uri) + Response.new(resp.body, resp.code.to_i) + end + end + end + + class SyncAPI + class << self + def ez_post_value(stat_name, ezkey, value, timestamp=nil) + args = { stat: stat_name, ezkey: ezkey, value: value } + args[:t] = timestamp unless timestamp.nil? + Common::send_to_stathat(Common::EZ_URL, args) + end + + def ez_post_count(stat_name, ezkey, count, timestamp=nil) + args = { stat: stat_name, ezkey: ezkey, count: count } + args[:t] = timestamp unless timestamp.nil? + Common::send_to_stathat(Common::EZ_URL, args) + end + + def post_count(stat_key, user_key, count, timestamp=nil) + args = { key: stat_key, ukey: user_key, count: count } + args[:t] = timestamp unless timestamp.nil? + Common::send_to_stathat(Common::CLASSIC_COUNT_URL, args) + end + + def post_value(stat_key, user_key, value, timestamp=nil) + args = { key: stat_key, ukey: user_key, value: value } + args[:t] = timestamp unless timestamp.nil? + Common::send_to_stathat(Common::CLASSIC_VALUE_URL, args) + end + end + end + + class API + class << self + def ez_post_value(stat_name, ezkey, value, timestamp=nil, &block) + Reporter.instance.ez_post_value(stat_name, ezkey, value, timestamp, block) + end + + def ez_post_count(stat_name, ezkey, count, timestamp=nil, &block) + Reporter.instance.ez_post_count(stat_name, ezkey, count, timestamp, block) + end + + def post_count(stat_key, user_key, count, timestamp=nil, &block) + Reporter.instance.post_count(stat_key, user_key, count, timestamp, block) + end + + def post_value(stat_key, user_key, value, timestamp=nil, &block) + Reporter.instance.post_value(stat_key, user_key, value, timestamp, block) + end + end + end + + class Reporter + include Singleton + + def initialize + @que = Queue.new + @runlock = Mutex.new + run_pool + end + + def finish + stop_pool + # XXX serialize queue? + end + + def post_value(stat_key, user_key, value, timestamp, cb) + args = { key: stat_key, ukey: user_key, value: value } + args[:t] = timestamp unless timestamp.nil? + enqueue(Common::CLASSIC_VALUE_URL, args, cb) + end + + def post_count(stat_key, user_key, count, timestamp, cb) + args = { key: stat_key, ukey: user_key, count: count } + args[:t] = timestamp unless timestamp.nil? + enqueue(Common::CLASSIC_COUNT_URL, args, cb) + end + + def ez_post_value(stat_name, ezkey, value, timestamp, cb) + args = { stat: stat_name, ezkey: ezkey, value: value } + args[:t] = timestamp unless timestamp.nil? + enqueue(Common::EZ_URL, args, cb) + end + + def ez_post_count(stat_name, ezkey, count, timestamp, cb) + args = { stat: stat_name, ezkey: ezkey, count: count } + args[:t] = timestamp unless timestamp.nil? + enqueue(Common::EZ_URL, args, cb) + end + + private + + def run_pool + @runlock.synchronize { @running = true } + @pool = [] + 5.times do |i| + @pool[i] = Thread.new do + while true do + point = @que.pop + # XXX check for error? + begin + resp = Common::send_to_stathat(point[:url], point[:args]) + if point[:cb] + point[:cb].call(resp) + end + rescue + pp $! + end + @runlock.synchronize do + break unless @running + end + end end + end + end + + def stop_pool + @runlock.synchronize { @running = false } + @pool.each do |th| + th.join if th && th.alive? + end + end + + def enqueue(url, args, cb=nil) + return false unless @running + point = { url: url, args: args, cb: cb } + @que << point + true + end + end + + class Response + def initialize(body, http_status) + @body = body + @http_status = http_status + @parsed = nil + end + + def valid? + (200..299).cover? status + end + + def status + if @body + parse + @parsed['status'] + else + @http_status + end + end + + def msg + parse + @parsed['msg'] + end + + private + + def parse + return unless @parsed.nil? + @parsed = JSON.parse(@body) + end + end end diff --git a/test/helper.rb b/test/helper.rb index be4465f..473ab92 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,12 +1,4 @@ require 'rubygems' -#require 'bundler' -#begin -# Bundler.setup(:default, :development) -#rescue Bundler::BundlerError => e -# $stderr.puts e.message -# $stderr.puts "Run `bundle install` to install missing gems" -# exit e.status_code -#end require 'minitest/unit' $LOAD_PATH.unshift(File.dirname(__FILE__)) diff --git a/test/test_stathat.rb b/test/test_stathat.rb index 76140d9..401d7b6 100644 --- a/test/test_stathat.rb +++ b/test/test_stathat.rb @@ -3,67 +3,65 @@ class TestStathat < MiniTest::Unit::TestCase - def test_ez_value - StatHat::API.ez_post_value("test ez value stat", "test@stathat.com", 0.92) do |resp| - assert(resp.valid?, "response was invalid") - assert_equal(resp.msg, "ok", "message should be 'ok'") - assert_equal(resp.status, 200, "status should be 200") - end - sleep(1) - end + def test_ez_value + StatHat::API.ez_post_value("test ez value stat", "test@stathat.com", 0.92) do |resp| + assert(resp.valid?, "response was invalid") + assert_equal(resp.msg, "ok", "message should be 'ok'") + assert_equal(resp.status, 200, "status should be 200") + end + sleep(1) + end - def test_ez_count - StatHat::API.ez_post_value("test ez count stat", "test@stathat.com", 12) do |r| - assert(r.valid?, "response was invalid") - assert_equal(r.msg, "ok", "message should be 'ok'") - assert_equal(r.status, 200, "status should be 200") - end - sleep(1) - end + def test_ez_count + StatHat::API.ez_post_value("test ez count stat", "test@stathat.com", 12) do |r| + assert(r.valid?, "response was invalid") + assert_equal(r.msg, "ok", "message should be 'ok'") + assert_equal(r.status, 200, "status should be 200") + end + sleep(1) + end - def test_classic_count_bad_keys - StatHat::API.post_count("XXXXXXXX", "YYYYYYYY", 12) do |r| - assert_equal(r.valid?, false, "response was valid") - assert_equal(r.msg, "invalid keys", "incorrect error message") - assert_equal(r.status, 500, "incorrect status code") - end - sleep(1) - end + def test_classic_count_bad_keys + StatHat::API.post_count("XXXXXXXX", "YYYYYYYY", 12) do |r| + assert_equal(r.valid?, false, "response was valid") + assert_equal(r.msg, "invalid keys", "incorrect error message") + assert_equal(r.status, 500, "incorrect status code") + end + sleep(1) + end - def test_classic_value_bad_keys - StatHat::API.post_value("ZZZZZZZZ", "YYYYYYYYY", 0.92) do |r| - assert_equal(r.valid?, false, "response was valid") - assert_equal(r.msg, "invalid keys", "incorrect error message") - assert_equal(r.status, 500, "incorrect status code") - end - sleep(1) - end + def test_classic_value_bad_keys + StatHat::API.post_value("ZZZZZZZZ", "YYYYYYYYY", 0.92) do |r| + assert_equal(r.valid?, false, "response was valid") + assert_equal(r.msg, "invalid keys", "incorrect error message") + assert_equal(r.status, 500, "incorrect status code") + end + sleep(1) + end - def test_ez_value_sync - resp = StatHat::SyncAPI.ez_post_value("test ez value stat", "test@stathat.com", 0.92) - assert(resp.valid?, "response was invalid") - assert_equal(resp.msg, "ok", "message should be 'ok'") - assert_equal(resp.status, 200, "status should be 200") - end + def test_ez_value_sync + resp = StatHat::SyncAPI.ez_post_value("test ez value stat", "test@stathat.com", 0.92) + assert(resp.valid?, "response was invalid") + assert_equal(resp.status, 204, "status should be 200") + end - def test_ez_count_sync - resp = StatHat::SyncAPI.ez_post_value("test ez count stat", "test@stathat.com", 12) - assert(resp.valid?, "response was invalid") - assert_equal(resp.msg, "ok", "message should be 'ok'") - assert_equal(resp.status, 200, "status should be 200") - end + def test_ez_count_sync + resp = StatHat::SyncAPI.ez_post_value("test ez count stat", "test@stathat.com", 12) + assert(resp.valid?, "response was invalid") + assert_equal(resp.status, 204, "status should be 200") + end - def test_classic_count_bad_keys_sync - r = StatHat::SyncAPI.post_count("XXXXXXXX", "YYYYYYYY", 12) - assert_equal(r.valid?, false, "response was valid") - assert_equal(r.msg, "invalid keys", "incorrect error message") - assert_equal(r.status, 500, "incorrect status code") - end + def test_classic_count_bad_keys_sync + r = StatHat::SyncAPI.post_count("XXXXXXXX", "YYYYYYYY", 12) + assert_equal(r.valid?, false, "response was valid") + assert_equal(r.msg, "invalid keys", "incorrect error message") + assert_equal(r.status, 500, "incorrect status code") + end - def test_classic_value_bad_keys_sync - r = StatHat::SyncAPI.post_value("ZZZZZZZZ", "YYYYYYYYY", 0.92) - assert_equal(r.valid?, false, "response was valid") - assert_equal(r.msg, "invalid keys", "incorrect error message") - assert_equal(r.status, 500, "incorrect status code") - end + def test_classic_value_bad_keys_sync + r = StatHat::SyncAPI.post_value("ZZZZZZZZ", "YYYYYYYYY", 0.92) + assert_equal(r.valid?, false, "response was valid") + assert_equal(r.msg, "invalid keys", "incorrect error message") + assert_equal(r.status, 500, "incorrect status code") + end end