nightfury 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +80 -0
- data/Gemfile +14 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/example.rb +49 -0
- data/lib/nightfury/identity.rb +42 -0
- data/lib/nightfury/metric/time_series.rb +91 -0
- data/lib/nightfury/metric/value.rb +28 -0
- data/lib/nightfury/metric.rb +24 -0
- data/lib/nightfury/version.rb +3 -0
- data/lib/nightfury.rb +43 -0
- data/nightfury.gemspec +18 -0
- data/spec/nightfury/identity_spec.rb +48 -0
- data/spec/nightfury/metric/time_series_spec.rb +141 -0
- data/spec/nightfury/metric/value_spec.rb +25 -0
- data/spec/nightfury/metric_spec.rb +29 -0
- data/spec/nightfury_spec.rb +29 -0
- data/spec/spec_helper.rb +35 -0
- metadata +73 -0
data/.gitignore
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
.sass-cache/
|
2
|
+
.bundle
|
3
|
+
public/javascripts/app_error_handler.js
|
4
|
+
supportbeeapp_production.sql
|
5
|
+
solr/data
|
6
|
+
db/*.qlite3
|
7
|
+
log/*.log
|
8
|
+
tmp/**/*
|
9
|
+
config/database.yml
|
10
|
+
config/sb_config.yml
|
11
|
+
config/secret_config.yml
|
12
|
+
tmp/*
|
13
|
+
public/javascripts/backbone/**/**/*.js
|
14
|
+
public/javascripts/backbone/*.js
|
15
|
+
public/javascripts/backbone/collections/
|
16
|
+
public/javascripts/backbone/models/
|
17
|
+
doc/api
|
18
|
+
doc/app
|
19
|
+
*.swp
|
20
|
+
*.swo
|
21
|
+
*.swm
|
22
|
+
*.un~
|
23
|
+
*˜
|
24
|
+
.DS_Store
|
25
|
+
schema.rb
|
26
|
+
.tags
|
27
|
+
emails/*
|
28
|
+
public/uploads/*
|
29
|
+
public/images/twitter_auth.png
|
30
|
+
public/assets
|
31
|
+
public/javascripts/backbone/libs/
|
32
|
+
public/javascripts/spin.js
|
33
|
+
public/javascripts/utility.js
|
34
|
+
public/javascripts/admin/
|
35
|
+
public/javascripts/backbone-rails.js
|
36
|
+
public/javascripts/payments.js
|
37
|
+
public/javascripts/widget/script_coffee.js
|
38
|
+
public/javascripts/app_error_handler.js
|
39
|
+
public/javascripts/sb.apps.js
|
40
|
+
public/stylesheets/admin/ie.css
|
41
|
+
public/stylesheets/admin/ie6.css
|
42
|
+
public/stylesheets/admin/print.css
|
43
|
+
public/stylesheets/admin/screen.css
|
44
|
+
public/stylesheets/admin/theme.css
|
45
|
+
public/stylesheets/app/ie.css
|
46
|
+
public/stylesheets/app/ie6.css
|
47
|
+
public/stylesheets/app/layout.css
|
48
|
+
public/stylesheets/app/screen.css
|
49
|
+
public/stylesheets/app/theme.css
|
50
|
+
public/stylesheets/app/account.css
|
51
|
+
public/stylesheets/app/screen_responsive.css
|
52
|
+
public/stylesheets/embed_form.css
|
53
|
+
public/stylesheets/formtastic_changes.css
|
54
|
+
public/stylesheets/app/email.css
|
55
|
+
public/stylesheets/common/marketing.css
|
56
|
+
public/stylesheets/common/sprite.css
|
57
|
+
public/stylesheets/common/blueprint_tabs.css
|
58
|
+
public/stylesheets/widget/
|
59
|
+
spec/javascripts/backbone/
|
60
|
+
spec/javascripts/admin
|
61
|
+
spec/javascripts/UtilitySpec.js
|
62
|
+
spec/javascripts/BackboneRailsSpec.js
|
63
|
+
spec/javascripts/helpers/SpecHelper.js
|
64
|
+
*.swn
|
65
|
+
uploads/
|
66
|
+
bin/
|
67
|
+
lib/daemons/log
|
68
|
+
log/
|
69
|
+
.sass-cache/
|
70
|
+
tags
|
71
|
+
.rvmrc
|
72
|
+
bundler_stubs/
|
73
|
+
*.swg
|
74
|
+
*.swh
|
75
|
+
*.swi
|
76
|
+
*.swj
|
77
|
+
*.swk
|
78
|
+
*.swl
|
79
|
+
public/stylesheets/common/tinymce.css
|
80
|
+
public/stylesheets/common/tinymce_ui.css
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Avinasha Shastry
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Nightfury
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'nightfury'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install nightfury
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/example.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'nightfury'
|
4
|
+
|
5
|
+
REDIS_CLIENT = Redis.new(:db => 15)
|
6
|
+
Redis.current = REDIS_CLIENT
|
7
|
+
|
8
|
+
# Defining Identity
|
9
|
+
class CompanyIdentity < Nightfury::Identity::Base
|
10
|
+
# Defining metric:
|
11
|
+
# #metric(name, type)
|
12
|
+
# type defaults to :value
|
13
|
+
metric :first_response_time, :time_series
|
14
|
+
metric :ticket_count
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
time_now = Time.now
|
19
|
+
|
20
|
+
# Identity object requires an unique ID
|
21
|
+
c = CompanyIdentity.new(1)
|
22
|
+
|
23
|
+
# Working with time series
|
24
|
+
# #set(value, timestamp)
|
25
|
+
# timestamp defaults to current time
|
26
|
+
c.first_response_time.set(1)
|
27
|
+
c.first_response_time.set(2,time_now - 10)
|
28
|
+
|
29
|
+
# #get([timestamp])
|
30
|
+
# without a timestamp get returns the latest value
|
31
|
+
# return value is a hash with a key value pair (unix_timestamp, value)
|
32
|
+
# all return values are strings regardless of their data type at the time of insertion
|
33
|
+
puts "#get; latest value: \n #{c.first_response_time.get}"
|
34
|
+
puts "#get(timestamp); value at the timestamp: \n #{c.first_response_time.get(time_now - 10)}"
|
35
|
+
puts "#get_range(start_time, end_time); returns values with condition start_time >= value <= end_time \n #{c.first_response_time.get_range(time_now - 15, time_now - 5)}"
|
36
|
+
puts "#get_all; returns all values \n #{c.first_response_time.get_all}"
|
37
|
+
|
38
|
+
|
39
|
+
# Working with values
|
40
|
+
c.ticket_count.set(0)
|
41
|
+
# increment by 1
|
42
|
+
c.ticket_count.incr
|
43
|
+
# increment by 10
|
44
|
+
c.ticket_count.incr(10)
|
45
|
+
# decrement by 1
|
46
|
+
c.ticket_count.decr
|
47
|
+
# decrement by 5
|
48
|
+
c.ticket_count.decr(5)
|
49
|
+
puts "Ticket Count; should be 5 \n #{c.ticket_count.get}"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Nightfury
|
2
|
+
module Identity
|
3
|
+
class Base
|
4
|
+
|
5
|
+
METRIC_MAPPINGS = {
|
6
|
+
:value => Nightfury::Metric::Value,
|
7
|
+
:time_series => Nightfury::Metric::TimeSeries
|
8
|
+
}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
attr_reader :metrics
|
13
|
+
|
14
|
+
def name
|
15
|
+
self.to_s.demodulize.underscore
|
16
|
+
end
|
17
|
+
|
18
|
+
def metric(name, type = :value)
|
19
|
+
@metrics ||= {}
|
20
|
+
@metrics[name] = {type: type}
|
21
|
+
|
22
|
+
class_eval <<-ENDOFMETHOD
|
23
|
+
def #{name}
|
24
|
+
@_#{name} ||= METRIC_MAPPINGS[:#{type}].new(:#{name}, redis_key_prefix: key_prefix)
|
25
|
+
end
|
26
|
+
ENDOFMETHOD
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :id
|
32
|
+
|
33
|
+
def initialize(id)
|
34
|
+
@id = id
|
35
|
+
end
|
36
|
+
|
37
|
+
def key_prefix
|
38
|
+
"#{self.class.name}:#{id}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Nightfury
|
2
|
+
module Metric
|
3
|
+
class TimeSeries < Base
|
4
|
+
|
5
|
+
def initialize(name, options={})
|
6
|
+
super(name, options={})
|
7
|
+
init_time_series unless redis.exists(redis_key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(value, time=Time.now)
|
11
|
+
value = before_set(value)
|
12
|
+
# make sure the time_series is initialized.
|
13
|
+
# It will not if the metric is removed and
|
14
|
+
# set is called on the smae object
|
15
|
+
init_time_series unless redis.exists(redis_key)
|
16
|
+
add_value_to_timeline(value, time)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(timestamp=nil)
|
20
|
+
return nil unless redis.exists(redis_key)
|
21
|
+
data_point = ''
|
22
|
+
if timestamp
|
23
|
+
timestamp = timestamp.to_i
|
24
|
+
data_point = redis.zrangebyscore(redis_key, timestamp, timestamp).first
|
25
|
+
else
|
26
|
+
data_point = redis.zrevrange(redis_key, 0, 0).first
|
27
|
+
end
|
28
|
+
|
29
|
+
time, data = decode_data_point(data_point)
|
30
|
+
{time => data}
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_range(start_time, end_time)
|
34
|
+
return nil unless redis.exists(redis_key)
|
35
|
+
start_time = start_time.to_i
|
36
|
+
end_time = end_time.to_i
|
37
|
+
data_points = redis.zrangebyscore(redis_key, start_time, end_time)
|
38
|
+
decode_many_data_points(data_points)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_all
|
42
|
+
return nil unless redis.exists(redis_key)
|
43
|
+
data_points = redis.zrange(redis_key,1,-1)
|
44
|
+
decode_many_data_points(data_points)
|
45
|
+
end
|
46
|
+
|
47
|
+
def meta
|
48
|
+
json = redis.zrange(redis_key, 0, 0).first
|
49
|
+
JSON.parse(json)
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_meta
|
53
|
+
{}
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def add_value_to_timeline(value, time)
|
59
|
+
time = time.to_i
|
60
|
+
value = "#{time}:#{value}"
|
61
|
+
redis.zadd redis_key, time, value
|
62
|
+
end
|
63
|
+
|
64
|
+
def decode_many_data_points(data_points)
|
65
|
+
result = {}
|
66
|
+
data_points.each do |data_point|
|
67
|
+
time, data = decode_data_point(data_point)
|
68
|
+
result[time] = data
|
69
|
+
end
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def decode_data_point(data_point)
|
74
|
+
colon_index = data_point.index(':')
|
75
|
+
|
76
|
+
[
|
77
|
+
data_point[0...colon_index],
|
78
|
+
data_point[colon_index+1..-1]
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
def before_set(value)
|
83
|
+
value
|
84
|
+
end
|
85
|
+
|
86
|
+
def init_time_series
|
87
|
+
redis.zadd redis_key, 0, default_meta.to_json
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Nightfury
|
2
|
+
module Metric
|
3
|
+
class Value < Base
|
4
|
+
def set(value)
|
5
|
+
value = before_set(value)
|
6
|
+
redis.set(redis_key, value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get
|
10
|
+
redis.get(redis_key)
|
11
|
+
end
|
12
|
+
|
13
|
+
def incr(step=1)
|
14
|
+
redis.incrby(redis_key, step)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decr(step=1)
|
18
|
+
redis.decrby(redis_key, step)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def before_set(value)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Nightfury
|
2
|
+
module Metric
|
3
|
+
class Base
|
4
|
+
attr_reader :name, :redis, :redis_key_prefix
|
5
|
+
|
6
|
+
def initialize(name, options={})
|
7
|
+
@name = name
|
8
|
+
@redis = Nightfury.redis
|
9
|
+
@redis_key_prefix = options[:redis_key_prefix]
|
10
|
+
end
|
11
|
+
|
12
|
+
def redis_key
|
13
|
+
prefix = redis_key_prefix.blank? ? '' : "#{redis_key_prefix}:"
|
14
|
+
"#{prefix}metric:#{name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete
|
18
|
+
redis.del redis_key
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
data/lib/nightfury.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'redis-namespace'
|
3
|
+
require 'json'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
require 'active_support/concern'
|
8
|
+
|
9
|
+
module Nightfury
|
10
|
+
class << self
|
11
|
+
attr_accessor :redis, :namespace
|
12
|
+
|
13
|
+
def redis
|
14
|
+
@redis ||= set_namespace(Redis.current)
|
15
|
+
end
|
16
|
+
|
17
|
+
def redis=(redis_instance)
|
18
|
+
@redis = set_namespace(redis_instance)
|
19
|
+
end
|
20
|
+
|
21
|
+
def namespace
|
22
|
+
@namespace ||= :nf
|
23
|
+
end
|
24
|
+
|
25
|
+
def namespace=(namespace_symbol)
|
26
|
+
@namespace = namespace_symbol
|
27
|
+
@redis = set_namespace(redis)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def set_namespace(redis_instance)
|
33
|
+
Redis::Namespace.new(namespace, redis: redis_instance)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require "nightfury/version"
|
39
|
+
require "nightfury/metric"
|
40
|
+
require "nightfury/metric/value"
|
41
|
+
require "nightfury/metric/time_series"
|
42
|
+
require "nightfury/identity"
|
43
|
+
|
data/nightfury.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/nightfury/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Avinasha Shastry", "Prateek Dayal"]
|
6
|
+
gem.email = ["me@avinasha.com", "prateek@supportbee.com"]
|
7
|
+
gem.description = %q{Nightfury is a reporting/analytics backend written on Redis}
|
8
|
+
gem.summary = %q{Nightfury is a reporting/analytics backend written on Redis}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "nightfury"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.requirements = ["redis, v3.0.0 or greater"]
|
17
|
+
gem.version = Nightfury::VERSION
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nightfury::Identity::Base do
|
4
|
+
|
5
|
+
class Dummy < Nightfury::Identity::Base
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "ClassMethods" do
|
9
|
+
it "should have a name" do
|
10
|
+
Dummy.name.should == 'dummy'
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "metric" do
|
14
|
+
it "should add to metrics" do
|
15
|
+
Dummy.metric(:count)
|
16
|
+
Dummy.metrics[:count].should == {type: :value}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should add a instance method" do
|
20
|
+
Dummy.metric(:another_count)
|
21
|
+
Dummy.instance_methods.should include(:another_count)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "InstanceMethods" do
|
27
|
+
it "should have an id" do
|
28
|
+
d = Dummy.new(1)
|
29
|
+
d.id.should == 1
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should generates a key prefix" do
|
33
|
+
d = Dummy.new(1)
|
34
|
+
d.key_prefix.should == 'dummy:1'
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Dynamically generated metric" do
|
38
|
+
it "should instantiate the right metric class" do
|
39
|
+
Dummy.metric(:third_count)
|
40
|
+
d = Dummy.new(1)
|
41
|
+
metric_object = d.third_count
|
42
|
+
metric_object.should be_kind_of(Nightfury::Metric::Value)
|
43
|
+
metric_object.name.should == :third_count
|
44
|
+
metric_object.redis_key_prefix.should == 'dummy:1'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nightfury::Metric::TimeSeries do
|
4
|
+
describe "Initialization" do
|
5
|
+
it "should initialize a time series (setup meta data)" do
|
6
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
7
|
+
ts_metric.meta.should == {}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "Getter" do
|
12
|
+
describe "#get" do
|
13
|
+
it "should retrun nil if metric key on redis is empty" do
|
14
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
15
|
+
# delete redis key
|
16
|
+
ts_metric.redis.del ts_metric.redis_key
|
17
|
+
ts_metric.get.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
context "without timestamp" do
|
21
|
+
it "should get the most recent data point" do
|
22
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
23
|
+
time_now = Time.now
|
24
|
+
time_later = time_now + 10
|
25
|
+
ts_metric.set(1, time_now)
|
26
|
+
ts_metric.set(2, time_later)
|
27
|
+
result = ts_metric.get
|
28
|
+
result[time_later.to_i.to_s].should == '2'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with timestamp" do
|
33
|
+
it "should get the data point at the time stamp" do
|
34
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
35
|
+
time_now = Time.now
|
36
|
+
time_later = time_now + 10
|
37
|
+
ts_metric.set(1, time_now)
|
38
|
+
ts_metric.set(2, time_later)
|
39
|
+
result = ts_metric.get(time_now)
|
40
|
+
result[time_now.to_i.to_s].should == '1'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#get_range" do
|
46
|
+
it "should retrun nil if metric key on redis is empty" do
|
47
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
48
|
+
# delete redis key
|
49
|
+
ts_metric.redis.del ts_metric.redis_key
|
50
|
+
ts_metric.get_range(Time.now, Time.now).should be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should get all data points between the specified ranges" do
|
54
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
55
|
+
time = Time.now
|
56
|
+
loop_time = time.dup
|
57
|
+
|
58
|
+
10.times do |i|
|
59
|
+
ts_metric.set(i, loop_time)
|
60
|
+
loop_time = loop_time + 1
|
61
|
+
end
|
62
|
+
|
63
|
+
start_time = time + 3
|
64
|
+
end_time = time + 5
|
65
|
+
|
66
|
+
result = ts_metric.get_range(start_time, end_time)
|
67
|
+
result[start_time.to_i.to_s].should == '3'
|
68
|
+
result[(start_time.to_i + 1).to_s].should == '4'
|
69
|
+
result[end_time.to_i.to_s].should == '5'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#get_all" do
|
74
|
+
it "should retrun nil if metric key on redis is empty" do
|
75
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
76
|
+
# delete redis key
|
77
|
+
ts_metric.redis.del ts_metric.redis_key
|
78
|
+
ts_metric.get_all.should be_nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should get all the data points in the series" do
|
82
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
83
|
+
time = Time.now
|
84
|
+
loop_time = time.dup
|
85
|
+
|
86
|
+
10.times do |i|
|
87
|
+
ts_metric.set(i, loop_time)
|
88
|
+
loop_time = loop_time + 1
|
89
|
+
end
|
90
|
+
|
91
|
+
result = ts_metric.get_all
|
92
|
+
result.length.should == 10
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "Setter" do
|
98
|
+
describe "add the value to timeline" do
|
99
|
+
it "should default time to current time" do
|
100
|
+
time_now = Time.now
|
101
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
102
|
+
|
103
|
+
flexmock(ts_metric.redis).should_receive(:zadd)
|
104
|
+
.with(ts_metric.redis_key,
|
105
|
+
time_now.to_i,
|
106
|
+
FlexMock.any)
|
107
|
+
.once
|
108
|
+
|
109
|
+
Timecop.freeze(time_now) do
|
110
|
+
ts_metric.set(1)
|
111
|
+
end
|
112
|
+
Timecop.return
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should add at specified time" do
|
116
|
+
time = Time.now - 60
|
117
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
118
|
+
|
119
|
+
flexmock(ts_metric.redis).should_receive(:zadd)
|
120
|
+
.with(ts_metric.redis_key,
|
121
|
+
time.to_i,
|
122
|
+
FlexMock.any)
|
123
|
+
.once
|
124
|
+
|
125
|
+
ts_metric.set(1, time)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should add the time to the value to avoid duplicate value in the set" do
|
129
|
+
time = Time.now
|
130
|
+
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
131
|
+
|
132
|
+
flexmock(ts_metric.redis).should_receive(:zadd)
|
133
|
+
.with(FlexMock.any,
|
134
|
+
FlexMock.any,
|
135
|
+
"#{time.to_i}:1")
|
136
|
+
.once
|
137
|
+
ts_metric.set(1, time)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe Nightfury::Metric::Value do
|
2
|
+
it "should get a value by delegating to redis" do
|
3
|
+
value_metric = Nightfury::Metric::Value.new(:tickets_count)
|
4
|
+
flexmock(value_metric.redis).should_receive(:get).with('metric:tickets_count').once
|
5
|
+
value_metric.get
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should set a value by delegating to redis" do
|
9
|
+
value_metric = Nightfury::Metric::Value.new(:tickets_count)
|
10
|
+
flexmock(value_metric.redis).should_receive(:set).with('metric:tickets_count', 1).once
|
11
|
+
value_metric.set(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should increment a value by delegating to redis" do
|
15
|
+
value_metric = Nightfury::Metric::Value.new(:tickets_count)
|
16
|
+
flexmock(value_metric.redis).should_receive(:incrby).with('metric:tickets_count', 1).once
|
17
|
+
value_metric.incr
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should decrement a value by delegating to redis" do
|
21
|
+
value_metric = Nightfury::Metric::Value.new(:tickets_count)
|
22
|
+
flexmock(value_metric.redis).should_receive(:decrby).with('metric:tickets_count', 2).once
|
23
|
+
value_metric.decr(2)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nightfury::Metric::Base do
|
4
|
+
it "should have a name" do
|
5
|
+
metric = Nightfury::Metric::Base.new(:tickets_count)
|
6
|
+
metric.name.should == :tickets_count
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have a redis key" do
|
10
|
+
metric = Nightfury::Metric::Base.new(:tickets_count)
|
11
|
+
metric.redis_key.should == "metric:tickets_count"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should accept a redis key prefix" do
|
15
|
+
metric = Nightfury::Metric::Base.new(:tickets_count, redis_key_prefix: 'prefix')
|
16
|
+
metric.redis_key.should == "prefix:metric:tickets_count"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have nightfury's redis connection" do
|
20
|
+
metric = Nightfury::Metric::Base.new(:tickets_count)
|
21
|
+
metric.redis.should == Nightfury.redis
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to remove itself" do
|
25
|
+
metric = Nightfury::Metric::Base.new(:tickets_count)
|
26
|
+
flexmock(metric.redis).should_receive(:del).with('metric:tickets_count').once
|
27
|
+
metric.delete
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nightfury do
|
4
|
+
describe "redis setup" do
|
5
|
+
it "should default redis connection to Redis.current" do
|
6
|
+
Nightfury.redis.client.host.should == "localhost"
|
7
|
+
Nightfury.redis.client.port.should == 9212
|
8
|
+
Nightfury.redis.client.db.should == 0
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a redis connection" do
|
12
|
+
redis = Redis.new
|
13
|
+
Nightfury.redis = redis
|
14
|
+
|
15
|
+
Nightfury.redis.client.host.should == "127.0.0.1"
|
16
|
+
Nightfury.redis.client.port.should == 6379
|
17
|
+
Nightfury.redis.client.db.should == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should default redis namespace to 'nf'" do
|
21
|
+
Nightfury.redis.namespace.should == :nf
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set redis namespace to the specified namespace" do
|
25
|
+
Nightfury.namespace = :new_nf
|
26
|
+
Nightfury.redis.namespace.should == :new_nf
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'nightfury'
|
5
|
+
require 'timecop'
|
6
|
+
|
7
|
+
# Start our own redis-server to avoid corrupting any others
|
8
|
+
REDIS_BIN = 'redis-server'
|
9
|
+
REDIS_PORT = ENV['REDIS_PORT'] || 9212
|
10
|
+
REDIS_HOST = ENV['REDIS_HOST'] || 'localhost'
|
11
|
+
REDIS_PID = File.expand_path 'redis.pid', File.dirname(__FILE__)
|
12
|
+
REDIS_DUMP = File.expand_path 'redis.rdb', File.dirname(__FILE__)
|
13
|
+
puts "=> Starting redis-server on #{REDIS_HOST}:#{REDIS_PORT}"
|
14
|
+
fork_pid = fork do
|
15
|
+
system "(echo port #{REDIS_PORT}; echo logfile /dev/null; echo daemonize yes; echo pidfile #{REDIS_PID}; echo dbfilename #{REDIS_DUMP}) | #{REDIS_BIN} -"
|
16
|
+
end
|
17
|
+
at_exit do
|
18
|
+
pid = File.read(REDIS_PID).to_i
|
19
|
+
puts "=> Killing #{REDIS_BIN} with pid #{pid}"
|
20
|
+
Process.kill "TERM", pid
|
21
|
+
Process.kill "KILL", pid
|
22
|
+
File.unlink REDIS_PID
|
23
|
+
File.unlink REDIS_DUMP if File.exists? REDIS_DUMP
|
24
|
+
end
|
25
|
+
|
26
|
+
REDIS_CLIENT = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
|
27
|
+
Redis.current = REDIS_CLIENT
|
28
|
+
|
29
|
+
RSpec.configure do |config|
|
30
|
+
config.mock_with :flexmock
|
31
|
+
|
32
|
+
config.before(:each) do
|
33
|
+
REDIS_CLIENT.flushdb
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nightfury
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Avinasha Shastry
|
9
|
+
- Prateek Dayal
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Nightfury is a reporting/analytics backend written on Redis
|
16
|
+
email:
|
17
|
+
- me@avinasha.com
|
18
|
+
- prateek@supportbee.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- example.rb
|
29
|
+
- lib/nightfury.rb
|
30
|
+
- lib/nightfury/identity.rb
|
31
|
+
- lib/nightfury/metric.rb
|
32
|
+
- lib/nightfury/metric/time_series.rb
|
33
|
+
- lib/nightfury/metric/value.rb
|
34
|
+
- lib/nightfury/version.rb
|
35
|
+
- nightfury.gemspec
|
36
|
+
- spec/nightfury/identity_spec.rb
|
37
|
+
- spec/nightfury/metric/time_series_spec.rb
|
38
|
+
- spec/nightfury/metric/value_spec.rb
|
39
|
+
- spec/nightfury/metric_spec.rb
|
40
|
+
- spec/nightfury_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
homepage: ''
|
43
|
+
licenses: []
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements:
|
61
|
+
- redis, v3.0.0 or greater
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.8.24
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Nightfury is a reporting/analytics backend written on Redis
|
67
|
+
test_files:
|
68
|
+
- spec/nightfury/identity_spec.rb
|
69
|
+
- spec/nightfury/metric/time_series_spec.rb
|
70
|
+
- spec/nightfury/metric/value_spec.rb
|
71
|
+
- spec/nightfury/metric_spec.rb
|
72
|
+
- spec/nightfury_spec.rb
|
73
|
+
- spec/spec_helper.rb
|