flamingo 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +15 -21
- data/examples/flamingo.yml +3 -0
- data/lib/flamingo.rb +61 -52
- data/lib/flamingo/daemon/child_process.rb +10 -2
- data/lib/flamingo/daemon/dispatcher_process.rb +2 -2
- data/lib/flamingo/daemon/flamingod.rb +19 -0
- data/lib/flamingo/dispatch_event.rb +12 -2
- data/lib/flamingo/meta.rb +71 -0
- data/lib/flamingo/stream.rb +14 -4
- data/lib/flamingo/version.rb +1 -1
- data/lib/flamingo/wader.rb +0 -1
- data/lib/flamingo/web/server.rb +5 -1
- metadata +133 -40
- data/lib/flamingo/dispatch_error.rb +0 -11
- data/lib/flamingo/stats/time_series.rb +0 -134
data/README.md
CHANGED
@@ -12,24 +12,10 @@ a try if you have the need.
|
|
12
12
|
|
13
13
|
Dependencies
|
14
14
|
------------
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
* yajl-ruby
|
20
|
-
* active_support
|
21
|
-
* redis-namespace
|
22
|
-
|
23
|
-
By default, the `resque` gem installs the latest 2.x `redis` gem, so if
|
24
|
-
you are using Redis 1.x, you may want to swap it out.
|
25
|
-
|
26
|
-
$ gem list | grep redis
|
27
|
-
redis (2.0.3)
|
28
|
-
$ gem remove redis --version=2.0.3 -V
|
29
|
-
|
30
|
-
$ gem install redis --version=1.0.7
|
31
|
-
$ gem list | grep redis
|
32
|
-
redis (1.0.7)
|
15
|
+
Check flamingo.gemspec for all the requirements. Currently there are quite a
|
16
|
+
few dependencies and they are very specific. We plan to have fewer dependencies
|
17
|
+
and be more liberal with versions soon. Right now these gems and versions are
|
18
|
+
what is working well in production for us.
|
33
19
|
|
34
20
|
Getting Started
|
35
21
|
---------------
|
@@ -118,8 +104,12 @@ commandline (see below)
|
|
118
104
|
Two things should now happen:
|
119
105
|
* The pent-up jobs from the EXAMPLE queue should spray across your console
|
120
106
|
* The resque dashboard should show the queue being emptied as a result
|
107
|
+
|
108
|
+
10. Interact with your running flamingod instance via the REST API (by default it is on port 4711)
|
121
109
|
|
122
|
-
|
110
|
+
$ curl http://0.0.0.0:4711/
|
111
|
+
|
112
|
+
That will show you available resources. Also, take a look at `lib/web/server.rb` for more.
|
123
113
|
|
124
114
|
Overview
|
125
115
|
--------
|
@@ -157,8 +147,12 @@ code.
|
|
157
147
|
|
158
148
|
TODO
|
159
149
|
-----
|
160
|
-
*
|
161
|
-
|
150
|
+
* A proper REST API client
|
151
|
+
* Remove resque dependency
|
152
|
+
* More liberal gem dependencies
|
153
|
+
* Redis 2.x
|
154
|
+
* ActiveSupport 3.x
|
155
|
+
* OAuth support
|
162
156
|
|
163
157
|
Flamingo
|
164
158
|
--------
|
data/examples/flamingo.yml
CHANGED
@@ -13,8 +13,11 @@ logging:
|
|
13
13
|
level: DEBUG
|
14
14
|
|
15
15
|
# Where is the redis server the flamingod processes should connect to?
|
16
|
+
# By default, all keys are namespaced wih "flamingo". May be changed
|
17
|
+
# to run multiple flamingods in the same redis DB.
|
16
18
|
redis:
|
17
19
|
host: 0.0.0.0:6379
|
20
|
+
namespace: flamingo
|
18
21
|
|
19
22
|
# What port and interface should the flamingod web_server listen on?
|
20
23
|
# use 0.0.0.0 for all interfaces, 127.0.0.1 to listen on only localhost
|
data/lib/flamingo.rb
CHANGED
@@ -11,9 +11,8 @@ require 'sinatra/base'
|
|
11
11
|
|
12
12
|
require 'flamingo/version'
|
13
13
|
require 'flamingo/config'
|
14
|
-
require 'flamingo/
|
14
|
+
require 'flamingo/meta'
|
15
15
|
require 'flamingo/dispatch_event'
|
16
|
-
require 'flamingo/dispatch_error'
|
17
16
|
require 'flamingo/stream_params'
|
18
17
|
require 'flamingo/stream'
|
19
18
|
require 'flamingo/subscription'
|
@@ -28,47 +27,48 @@ require 'flamingo/daemon/flamingod'
|
|
28
27
|
require 'flamingo/logging/formatter'
|
29
28
|
require 'flamingo/web/server'
|
30
29
|
|
31
|
-
|
32
30
|
module Flamingo
|
33
31
|
|
34
32
|
class << self
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
# Configures flamingo. This must be called prior to using any flamingo
|
35
|
+
# classes.
|
36
|
+
#
|
37
|
+
# The config argument may be one of:
|
38
|
+
# 1) nil: Try to locate a config file in ./flamingo.yml, ~/flamingo.yml
|
39
|
+
# 2) String: A config file name (preferred)
|
40
|
+
# 3) Flamingo::Config: Used as the configuration directly
|
41
|
+
# 4) Hash: Converted to a Flamingo::Config and used as the configuration
|
42
|
+
def configure!(cfg_info=nil,validate=true)
|
43
|
+
if cfg_info.nil? || cfg_info.kind_of?(String)
|
44
|
+
config_file = find_config_file(cfg_info)
|
45
|
+
@config = Flamingo::Config.load(config_file)
|
46
|
+
logger.info "Loaded config file from #{config_file}"
|
47
|
+
elsif cfg_info.kind_of?(Flamingo::Config)
|
48
|
+
@config = cfg_info
|
49
|
+
elsif cfg_info.kind_of?(Hash)
|
50
|
+
@config = Flamingo::Config.new(cfg_info)
|
51
|
+
end
|
39
52
|
validate_config!
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
def config=(config)
|
44
|
-
@config = config
|
53
|
+
# Ensure redis gets loaded
|
54
|
+
redis
|
45
55
|
end
|
46
56
|
|
47
57
|
def config
|
48
58
|
@config
|
49
59
|
end
|
50
60
|
|
51
|
-
# PHD:
|
61
|
+
# PHD: Partially borrowed from resque
|
52
62
|
|
53
|
-
#
|
54
|
-
# 1. A 'hostname:port' string
|
55
|
-
# 2. A 'hostname:port:db' string (to select the Redis db)
|
56
|
-
# 3. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
57
|
-
# or `Redis::Namespace`.
|
63
|
+
# server must be a "hostname:port[:db]" string
|
58
64
|
def redis=(server)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@redis = Redis::Namespace.new(:flamingo, :redis => server)
|
67
|
-
when Redis::Namespace
|
68
|
-
@redis = server
|
69
|
-
else
|
70
|
-
raise "Invalid redis configuration: #{server.inspect}"
|
71
|
-
end
|
65
|
+
host, port, db = server.split(':')
|
66
|
+
redis = Redis.new(:host => host, :port => port,
|
67
|
+
:thread_safe => true, :db => db)
|
68
|
+
@redis = Redis::Namespace.new(namespace, :redis => redis)
|
69
|
+
|
70
|
+
# Ensure resque is configured to use this redis as well
|
71
|
+
Resque.redis = server
|
72
72
|
end
|
73
73
|
|
74
74
|
# Returns the current Redis connection. If none has been created, will
|
@@ -79,28 +79,6 @@ module Flamingo
|
|
79
79
|
self.redis
|
80
80
|
end
|
81
81
|
|
82
|
-
def new_logger
|
83
|
-
# determine log file location (default is root_dir/log/flamingo.log)
|
84
|
-
if valid_logging_dest?(config.logging.dest(nil))
|
85
|
-
log_dest = config.logging.dest
|
86
|
-
else
|
87
|
-
log_dest = File.join(root_dir,'log','flamingo.log')
|
88
|
-
end
|
89
|
-
|
90
|
-
# determine logging level (default is Logger::INFO)
|
91
|
-
begin
|
92
|
-
log_level = Logger.const_get(config.logging.level.upcase)
|
93
|
-
rescue
|
94
|
-
log_level = Logger::INFO
|
95
|
-
end
|
96
|
-
|
97
|
-
# create logger facility
|
98
|
-
logger = Logger.new(log_dest)
|
99
|
-
logger.level = log_level
|
100
|
-
logger.formatter = Flamingo::Logging::Formatter.new
|
101
|
-
logger
|
102
|
-
end
|
103
|
-
|
104
82
|
def logger
|
105
83
|
@logger ||= new_logger
|
106
84
|
end
|
@@ -109,7 +87,38 @@ module Flamingo
|
|
109
87
|
@logger = logger
|
110
88
|
end
|
111
89
|
|
90
|
+
def namespace
|
91
|
+
config.redis.namespace(:flamingo)
|
92
|
+
end
|
93
|
+
|
94
|
+
def dispatch_queue
|
95
|
+
@dispatch_queue ||= "#{namespace}:dispatch"
|
96
|
+
end
|
97
|
+
|
98
|
+
def meta
|
99
|
+
@meta ||= Flamingo::Meta.new(redis)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Intended to be called after a fork so that we don't have
|
103
|
+
# issues with shared file descriptors, sockets, etc
|
104
|
+
def reconnect!
|
105
|
+
reconnect_redis_client(@redis)
|
106
|
+
reconnect_redis_client(Resque.redis)
|
107
|
+
# Reload logger
|
108
|
+
logger.close
|
109
|
+
self.logger = new_logger
|
110
|
+
end
|
111
|
+
|
112
112
|
private
|
113
|
+
def reconnect_redis_client(client)
|
114
|
+
# Unfortunately older versions of the Redis client don't make these
|
115
|
+
# methods public so we have to use send. Later versions have made
|
116
|
+
# these public.
|
117
|
+
if client && (client.send(:connected?) rescue true)
|
118
|
+
client.send(:reconnect)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
113
122
|
def root_dir
|
114
123
|
File.expand_path(File.dirname(__FILE__)+'/..')
|
115
124
|
end
|
@@ -2,7 +2,7 @@ module Flamingo
|
|
2
2
|
module Daemon
|
3
3
|
class DispatcherProcess < ChildProcess
|
4
4
|
def run
|
5
|
-
worker = Resque::Worker.new(
|
5
|
+
worker = Resque::Worker.new(Flamingo.dispatch_queue)
|
6
6
|
def worker.procline(value)
|
7
7
|
# Hack to get around resque insisting on setting the proces name
|
8
8
|
$0 = "flamingod-dispatcher"
|
@@ -10,6 +10,6 @@ module Flamingo
|
|
10
10
|
Flamingo.logger.info "Starting dispatcher on pid=#{Process.pid} under pid=#{Process.ppid}"
|
11
11
|
worker.work(1) # Wait 1s between jobs
|
12
12
|
end
|
13
|
-
end
|
13
|
+
end
|
14
14
|
end
|
15
15
|
end
|
@@ -143,17 +143,36 @@ module Flamingo
|
|
143
143
|
io.reopen '/dev/null' rescue nil
|
144
144
|
end
|
145
145
|
run
|
146
|
+
clear_process_meta_data
|
146
147
|
pid_file.delete
|
147
148
|
end
|
148
149
|
Process.detach(pid)
|
149
150
|
pid
|
150
151
|
end
|
152
|
+
|
153
|
+
def set_process_meta_data
|
154
|
+
meta = Flamingo.meta
|
155
|
+
meta[:start_time] = Time.now.utc.to_i
|
156
|
+
meta[:host] = `hostname`.chomp rescue nil
|
157
|
+
meta[:pid] = Process.pid
|
158
|
+
meta[:running] = true
|
159
|
+
end
|
160
|
+
|
161
|
+
def clear_process_meta_data
|
162
|
+
meta = Flamingo.meta
|
163
|
+
meta.delete(:start_time)
|
164
|
+
meta.delete(:host)
|
165
|
+
meta.delete(:pid)
|
166
|
+
meta[:running] = false
|
167
|
+
end
|
151
168
|
|
152
169
|
def run
|
153
170
|
$0 = 'flamingod'
|
171
|
+
set_process_meta_data
|
154
172
|
trap_signals
|
155
173
|
start_children
|
156
174
|
wait_on_children
|
175
|
+
clear_process_meta_data
|
157
176
|
end
|
158
177
|
end
|
159
178
|
end
|
@@ -1,10 +1,17 @@
|
|
1
1
|
module Flamingo
|
2
2
|
class DispatchEvent
|
3
3
|
|
4
|
-
@queue = :flamingo
|
5
4
|
@parser = Yajl::Parser.new(:symbolize_keys => true)
|
6
5
|
|
7
6
|
class << self
|
7
|
+
|
8
|
+
def queue
|
9
|
+
Flamingo.dispatch_queue
|
10
|
+
end
|
11
|
+
|
12
|
+
def meta
|
13
|
+
Flamingo.meta
|
14
|
+
end
|
8
15
|
|
9
16
|
#
|
10
17
|
# TODO Track stats including: tweets per second and last tweet time
|
@@ -15,10 +22,13 @@ module Flamingo
|
|
15
22
|
# dispatching to improve in-order delivery (helps with "k-sorted")
|
16
23
|
#
|
17
24
|
def perform(event_json)
|
25
|
+
meta.incr("events:all_count")
|
26
|
+
meta.set("events:last_time",Time.now.utc.to_i)
|
18
27
|
type, event = typed_event(parse(event_json))
|
28
|
+
meta.incr("events:#{type}_count")
|
19
29
|
Subscription.all.each do |sub|
|
20
30
|
Resque::Job.create(sub.name, "HandleFlamingoEvent", type, event)
|
21
|
-
Flamingo.logger.debug "Put job on subscription queue #{sub.name}
|
31
|
+
Flamingo.logger.debug "Put job on subscription queue #{sub.name}\n#{event_json}"
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Flamingo
|
2
|
+
|
3
|
+
class Meta
|
4
|
+
|
5
|
+
attr_accessor :redis
|
6
|
+
|
7
|
+
def initialize(redis)
|
8
|
+
self.redis = redis
|
9
|
+
end
|
10
|
+
|
11
|
+
def incr(name,amt=1)
|
12
|
+
redis.incrby(key(name),amt)
|
13
|
+
end
|
14
|
+
|
15
|
+
def set(name,value)
|
16
|
+
redis.set(key(name),value)
|
17
|
+
end
|
18
|
+
alias_method :[]=,:set
|
19
|
+
|
20
|
+
def get(name)
|
21
|
+
norm_value(redis.get(key(name)))
|
22
|
+
end
|
23
|
+
alias_method :[],:get
|
24
|
+
|
25
|
+
def delete(name)
|
26
|
+
redis.del(key(name))
|
27
|
+
end
|
28
|
+
|
29
|
+
def all
|
30
|
+
redis.keys("#{namespace}*").map do |k|
|
31
|
+
[denamespace(k),norm_value(redis.get(k))]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear
|
36
|
+
all.each do |key,value|
|
37
|
+
delete(key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_h
|
42
|
+
all.inject({}) do |hash, (key,value)|
|
43
|
+
hash[key] = value
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def norm_value(value)
|
50
|
+
if value.kind_of?(String) && value =~ /^\d+$/
|
51
|
+
value.to_i
|
52
|
+
else
|
53
|
+
value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def denamespace(key)
|
58
|
+
key.gsub(namespace,'')
|
59
|
+
end
|
60
|
+
|
61
|
+
def namespace
|
62
|
+
"meta:"
|
63
|
+
end
|
64
|
+
|
65
|
+
def key(name)
|
66
|
+
"#{namespace}#{name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/flamingo/stream.rb
CHANGED
@@ -10,6 +10,12 @@ module Flamingo
|
|
10
10
|
:retweet => "statuses/retweet",
|
11
11
|
:sample => "statuses/sample"
|
12
12
|
)
|
13
|
+
|
14
|
+
DEFAULT_CONNECTION_OPTIONS = {
|
15
|
+
:method =>"POST",
|
16
|
+
:ssl => false,
|
17
|
+
:user_agent => "Flamingo/#{Flamingo::VERSION}"
|
18
|
+
}
|
13
19
|
|
14
20
|
class << self
|
15
21
|
def get(name)
|
@@ -25,13 +31,17 @@ module Flamingo
|
|
25
31
|
end
|
26
32
|
|
27
33
|
def connect(options)
|
28
|
-
|
29
|
-
|
30
|
-
|
34
|
+
Twitter::JSONStream.connect(connection_options(options))
|
35
|
+
end
|
36
|
+
|
37
|
+
def connection_options(overrides={})
|
38
|
+
DEFAULT_CONNECTION_OPTIONS.
|
39
|
+
merge(overrides).
|
40
|
+
merge(:path=>path,:content=>query)
|
31
41
|
end
|
32
42
|
|
33
43
|
def path
|
34
|
-
"/#{VERSION}/#{resource}.json
|
44
|
+
"/#{VERSION}/#{resource}.json"
|
35
45
|
end
|
36
46
|
|
37
47
|
def resource
|
data/lib/flamingo/version.rb
CHANGED
data/lib/flamingo/wader.rb
CHANGED
data/lib/flamingo/web/server.rb
CHANGED
@@ -9,11 +9,15 @@ module Flamingo
|
|
9
9
|
get '/' do
|
10
10
|
content_type 'text/plain'
|
11
11
|
api = self.methods.select do |method|
|
12
|
-
(method =~ /^(GET|POST) /) && !(method =~ /png$/)
|
12
|
+
(method =~ /^(GET|POST|PUT|DELETE) /) && !(method =~ /png$/)
|
13
13
|
end
|
14
14
|
api.sort.join("\n")
|
15
15
|
end
|
16
16
|
|
17
|
+
get '/meta.json' do
|
18
|
+
to_json(Flamingo.meta.to_h)
|
19
|
+
end
|
20
|
+
|
17
21
|
get '/streams/:name.json' do
|
18
22
|
stream = Stream.get(params[:name])
|
19
23
|
to_json(
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flamingo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 1
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Hayes Davis
|
@@ -16,55 +16,63 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-11-26 00:00:00 -08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
|
-
name:
|
23
|
+
name: activesupport
|
24
24
|
prerelease: false
|
25
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
26
|
none: false
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 11
|
31
31
|
segments:
|
32
|
+
- 2
|
32
33
|
- 1
|
33
34
|
- 0
|
34
|
-
|
35
|
-
|
35
|
+
version: 2.1.0
|
36
|
+
- - <=
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
hash: 9
|
39
|
+
segments:
|
40
|
+
- 2
|
41
|
+
- 3
|
42
|
+
- 5
|
43
|
+
version: 2.3.5
|
36
44
|
type: :runtime
|
37
45
|
version_requirements: *id001
|
38
46
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
47
|
+
name: eventmachine
|
40
48
|
prerelease: false
|
41
49
|
requirement: &id002 !ruby/object:Gem::Requirement
|
42
50
|
none: false
|
43
51
|
requirements:
|
44
|
-
- - "
|
52
|
+
- - "="
|
45
53
|
- !ruby/object:Gem::Version
|
46
|
-
hash:
|
54
|
+
hash: 59
|
47
55
|
segments:
|
48
56
|
- 0
|
49
|
-
-
|
50
|
-
-
|
51
|
-
version: 0.
|
57
|
+
- 12
|
58
|
+
- 10
|
59
|
+
version: 0.12.10
|
52
60
|
type: :runtime
|
53
61
|
version_requirements: *id002
|
54
62
|
- !ruby/object:Gem::Dependency
|
55
|
-
name:
|
63
|
+
name: rack
|
56
64
|
prerelease: false
|
57
65
|
requirement: &id003 !ruby/object:Gem::Requirement
|
58
66
|
none: false
|
59
67
|
requirements:
|
60
|
-
- - "
|
68
|
+
- - "="
|
61
69
|
- !ruby/object:Gem::Version
|
62
|
-
hash:
|
70
|
+
hash: 19
|
63
71
|
segments:
|
64
72
|
- 1
|
65
|
-
-
|
66
|
-
-
|
67
|
-
version: 1.
|
73
|
+
- 1
|
74
|
+
- 0
|
75
|
+
version: 1.1.0
|
68
76
|
type: :runtime
|
69
77
|
version_requirements: *id003
|
70
78
|
- !ruby/object:Gem::Dependency
|
@@ -81,60 +89,146 @@ dependencies:
|
|
81
89
|
- 9
|
82
90
|
- 2
|
83
91
|
version: 0.9.2
|
92
|
+
- - <=
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 15
|
95
|
+
segments:
|
96
|
+
- 1
|
97
|
+
- 0
|
98
|
+
version: "1.0"
|
84
99
|
type: :runtime
|
85
100
|
version_requirements: *id004
|
86
101
|
- !ruby/object:Gem::Dependency
|
87
|
-
name:
|
102
|
+
name: redis
|
88
103
|
prerelease: false
|
89
104
|
requirement: &id005 !ruby/object:Gem::Requirement
|
90
105
|
none: false
|
91
106
|
requirements:
|
92
107
|
- - ">="
|
93
108
|
- !ruby/object:Gem::Version
|
94
|
-
hash:
|
109
|
+
hash: 25
|
95
110
|
segments:
|
96
|
-
- 0
|
97
111
|
- 1
|
98
|
-
-
|
99
|
-
|
112
|
+
- 0
|
113
|
+
- 7
|
114
|
+
version: 1.0.7
|
115
|
+
- - <
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 15
|
118
|
+
segments:
|
119
|
+
- 2
|
120
|
+
- 0
|
121
|
+
- 0
|
122
|
+
version: 2.0.0
|
100
123
|
type: :runtime
|
101
124
|
version_requirements: *id005
|
102
125
|
- !ruby/object:Gem::Dependency
|
103
|
-
name:
|
126
|
+
name: redis-namespace
|
104
127
|
prerelease: false
|
105
128
|
requirement: &id006 !ruby/object:Gem::Requirement
|
106
129
|
none: false
|
107
130
|
requirements:
|
108
|
-
- - "
|
131
|
+
- - "="
|
109
132
|
- !ruby/object:Gem::Version
|
110
|
-
hash:
|
133
|
+
hash: 3
|
111
134
|
segments:
|
112
135
|
- 0
|
113
|
-
- 6
|
114
136
|
- 7
|
115
|
-
|
137
|
+
- 0
|
138
|
+
version: 0.7.0
|
116
139
|
type: :runtime
|
117
140
|
version_requirements: *id006
|
118
141
|
- !ruby/object:Gem::Dependency
|
119
|
-
name:
|
142
|
+
name: resque
|
120
143
|
prerelease: false
|
121
144
|
requirement: &id007 !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - "="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
hash: 61
|
150
|
+
segments:
|
151
|
+
- 1
|
152
|
+
- 9
|
153
|
+
- 7
|
154
|
+
version: 1.9.7
|
155
|
+
type: :runtime
|
156
|
+
version_requirements: *id007
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: twitter-stream
|
159
|
+
prerelease: false
|
160
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
122
161
|
none: false
|
123
162
|
requirements:
|
124
163
|
- - ">="
|
125
164
|
- !ruby/object:Gem::Version
|
126
|
-
hash:
|
165
|
+
hash: 19
|
127
166
|
segments:
|
128
|
-
-
|
167
|
+
- 0
|
129
168
|
- 1
|
169
|
+
- 4
|
170
|
+
version: 0.1.4
|
171
|
+
- - <=
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
hash: 23
|
174
|
+
segments:
|
130
175
|
- 0
|
131
|
-
|
176
|
+
- 1
|
177
|
+
- 6
|
178
|
+
version: 0.1.6
|
132
179
|
type: :runtime
|
133
|
-
version_requirements: *
|
180
|
+
version_requirements: *id008
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: yajl-ruby
|
183
|
+
prerelease: false
|
184
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - "="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
hash: 15
|
190
|
+
segments:
|
191
|
+
- 0
|
192
|
+
- 7
|
193
|
+
- 6
|
194
|
+
version: 0.7.6
|
195
|
+
type: :runtime
|
196
|
+
version_requirements: *id009
|
197
|
+
- !ruby/object:Gem::Dependency
|
198
|
+
name: SystemTimer
|
199
|
+
prerelease: false
|
200
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ~>
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
hash: 11
|
206
|
+
segments:
|
207
|
+
- 1
|
208
|
+
- 2
|
209
|
+
version: "1.2"
|
210
|
+
type: :runtime
|
211
|
+
version_requirements: *id010
|
212
|
+
- !ruby/object:Gem::Dependency
|
213
|
+
name: mocha
|
214
|
+
prerelease: false
|
215
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ">="
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
hash: 43
|
221
|
+
segments:
|
222
|
+
- 0
|
223
|
+
- 9
|
224
|
+
- 8
|
225
|
+
version: 0.9.8
|
226
|
+
type: :development
|
227
|
+
version_requirements: *id011
|
134
228
|
- !ruby/object:Gem::Dependency
|
135
229
|
name: mockingbird
|
136
230
|
prerelease: false
|
137
|
-
requirement: &
|
231
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
138
232
|
none: false
|
139
233
|
requirements:
|
140
234
|
- - ">="
|
@@ -146,8 +240,8 @@ dependencies:
|
|
146
240
|
- 0
|
147
241
|
version: 0.1.0
|
148
242
|
type: :development
|
149
|
-
version_requirements: *
|
150
|
-
description: " Flamingo makes it easy to wade through the Twitter Streaming API by \n handling all connectivity and resource management for you. You just tell \n it what to track and consume the information in a resque queue. \n\n Flamingo isn't a traditional ruby gem. You don't require it into your code.\n Instead, it's designed to run as a daemon like redis or mysql. It provides \n a REST interface to change the parameters sent to the Twitter Streaming \n resource. All events from the streaming API are placed on a resque job \n queue where your application can process them.\n\n"
|
243
|
+
version_requirements: *id012
|
244
|
+
description: " Flamingo makes it easy to wade through the Twitter Streaming API by \n handling all connectivity and resource management for you. You just tell \n it what to track and consume the information in a resque queue. \n\n Flamingo isn't a traditional ruby gem. You don't require it into your code.\n Instead, it's designed to run as a daemon like redis or mysql. It provides \n a REST interface to change the parameters sent to the Twitter Streaming \n resource. All events from the streaming API are placed on a resque job \n queue where your application can process them.\n \n CAVEAT EMPTOR: This gem is alpha code so act accordingly.\n\n"
|
151
245
|
email: hayes@appozite.com
|
152
246
|
executables:
|
153
247
|
- flamingo
|
@@ -168,10 +262,9 @@ files:
|
|
168
262
|
- lib/flamingo/daemon/trap_keeper.rb
|
169
263
|
- lib/flamingo/daemon/wader_process.rb
|
170
264
|
- lib/flamingo/daemon/web_server_process.rb
|
171
|
-
- lib/flamingo/dispatch_error.rb
|
172
265
|
- lib/flamingo/dispatch_event.rb
|
173
266
|
- lib/flamingo/logging/formatter.rb
|
174
|
-
- lib/flamingo/
|
267
|
+
- lib/flamingo/meta.rb
|
175
268
|
- lib/flamingo/stream.rb
|
176
269
|
- lib/flamingo/stream_params.rb
|
177
270
|
- lib/flamingo/subscription.rb
|
@@ -1,134 +0,0 @@
|
|
1
|
-
module Flamingo
|
2
|
-
module Stats
|
3
|
-
class TimeSeries
|
4
|
-
|
5
|
-
attr_accessor :name, :seconds_per_tick, :total_ticks
|
6
|
-
|
7
|
-
class << self
|
8
|
-
def create(name,seconds_per_tick,total_ticks)
|
9
|
-
redis.set("stats:#{name}:schema","#{seconds_per_tick},#{total_ticks}")
|
10
|
-
new(name,seconds_per_tick,total_ticks)
|
11
|
-
end
|
12
|
-
|
13
|
-
def get(name)
|
14
|
-
schema_value = redis.get("stats:#{name}:schema")
|
15
|
-
raise "No schema specified" unless schema_value
|
16
|
-
seconds_per_tick, total_ticks = schema_value.split(",").map{|v| v.to_i}
|
17
|
-
new(name,seconds_per_tick,total_ticks)
|
18
|
-
end
|
19
|
-
|
20
|
-
def redis
|
21
|
-
Flamingo.redis
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize(name,seconds_per_tick,total_ticks)
|
27
|
-
self.name = name
|
28
|
-
self.seconds_per_tick = seconds_per_tick
|
29
|
-
self.total_ticks = total_ticks
|
30
|
-
end
|
31
|
-
|
32
|
-
def data(ticks=total_ticks,time=Time.now.utc)
|
33
|
-
range = tick_range(ticks,time)
|
34
|
-
keys = range.map {|t| value_key(t) }
|
35
|
-
values = redis.mget(keys).map {|v| (v || 0).to_f }
|
36
|
-
range.map {|t| t * seconds_per_tick }.zip(values)
|
37
|
-
end
|
38
|
-
|
39
|
-
def each_datum(ticks=total_ticks,time=Time.now.utc)
|
40
|
-
tick_range(ticks,time).each do |tick|
|
41
|
-
yield(tick*seconds_per_tick,value(tick))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def tick_range(ticks=total_ticks,time=Time.now.utc)
|
46
|
-
end_tick = to_tick(time)
|
47
|
-
start_tick = (end_tick - ticks) + 1
|
48
|
-
(start_tick..end_tick)
|
49
|
-
end
|
50
|
-
|
51
|
-
def average(ticks=total_ticks,time=Time.now.utc)
|
52
|
-
count = 0
|
53
|
-
sum = 0
|
54
|
-
each_datum do |ts, value|
|
55
|
-
count += 1
|
56
|
-
sum += value
|
57
|
-
end
|
58
|
-
if count == 0
|
59
|
-
nil
|
60
|
-
else
|
61
|
-
sum / count
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def increment(val=1,time=Time.now.utc)
|
66
|
-
modify_value_at(time) do |val_key|
|
67
|
-
redis.incrby(val_key,val)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def set(val,time=Time.now.utc)
|
72
|
-
modify_value_at(time) do |val_key|
|
73
|
-
redis.set(val_key,val)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
def modify_value_at(time=Time.now.utc)
|
79
|
-
t = to_tick(time)
|
80
|
-
return false unless valid?(t)
|
81
|
-
val_key = value_key(t)
|
82
|
-
yield(val_key)
|
83
|
-
ver_key = version_key(t)
|
84
|
-
prev_tick = redis.getset(ver_key,t)
|
85
|
-
if prev_tick && prev_tick.to_i < t
|
86
|
-
redis.del(value_key(prev_tick.to_i))
|
87
|
-
end
|
88
|
-
true
|
89
|
-
end
|
90
|
-
|
91
|
-
def value(tick)
|
92
|
-
val = redis.get(value_key(tick))
|
93
|
-
val ? val.to_f : 0
|
94
|
-
end
|
95
|
-
|
96
|
-
def to_tick(time=Time.now.utc)
|
97
|
-
(time.to_i / seconds_per_tick)
|
98
|
-
end
|
99
|
-
|
100
|
-
def now_tick
|
101
|
-
to_tick(Time.now.utc)
|
102
|
-
end
|
103
|
-
|
104
|
-
def valid?(tick)
|
105
|
-
nt = now_tick
|
106
|
-
(nt - total_ticks) <= tick && tick <= nt
|
107
|
-
end
|
108
|
-
|
109
|
-
def tick_series(tick)
|
110
|
-
tick / total_ticks
|
111
|
-
end
|
112
|
-
|
113
|
-
def tick_pos(tick)
|
114
|
-
tick % total_ticks
|
115
|
-
end
|
116
|
-
|
117
|
-
def value_key(tick)
|
118
|
-
s = tick_series(tick)
|
119
|
-
pos = tick_pos(tick)
|
120
|
-
"stats:#{name}:#{s}:#{pos}"
|
121
|
-
end
|
122
|
-
|
123
|
-
def version_key(tick)
|
124
|
-
pos = tick_pos(tick)
|
125
|
-
"stats:#{name}:v:#{pos}"
|
126
|
-
end
|
127
|
-
|
128
|
-
def redis
|
129
|
-
self.class.redis
|
130
|
-
end
|
131
|
-
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|