patty 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/.travis.yml +6 -0
- data/Gemfile +5 -0
- data/README.md +5 -0
- data/Rakefile +8 -0
- data/lib/patty/aggregated_storage.rb +47 -0
- data/lib/patty/base.rb +72 -0
- data/lib/patty/storages/riak.rb +32 -0
- data/lib/patty/time_signature.rb +117 -0
- data/lib/patty/version.rb +5 -0
- data/lib/patty.rb +9 -0
- data/patty.gemspec +20 -0
- data/test/test_time_signature.rb +67 -0
- metadata +78 -0
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Patty
|
4
|
+
class AggregatedStorage
|
5
|
+
|
6
|
+
def initialize(storage = nil)
|
7
|
+
@storage = storage
|
8
|
+
end
|
9
|
+
|
10
|
+
def put(signature = nil, value = nil, marker = 'main')
|
11
|
+
validate_signature signature
|
12
|
+
signature.parents.each{ |p| del p }
|
13
|
+
@storage.put key(signature, marker), encode(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(signature = nil, marker = 'main')
|
17
|
+
validate_signature signature
|
18
|
+
decode @storage.get(key(signature, marker))
|
19
|
+
end
|
20
|
+
|
21
|
+
def del(signature = nil, marker = 'main')
|
22
|
+
validate_signature signature
|
23
|
+
@storage.del key(signature, marker)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def encode(data = nil)
|
29
|
+
Yajl::Encoder.encode data
|
30
|
+
end
|
31
|
+
|
32
|
+
def decode(data = '')
|
33
|
+
Yajl::Parser.parse data
|
34
|
+
end
|
35
|
+
|
36
|
+
def key(signature = nil, marker = 'main')
|
37
|
+
"#{marker}|#{signature}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_signature(signature = nil)
|
41
|
+
if signature.nil? || !signature.is_a?(Patty::TimeSignature)
|
42
|
+
raise "key validation fail for #{signature}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/patty/base.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'waffle'
|
3
|
+
require 'patty/time_signature'
|
4
|
+
|
5
|
+
module Patty
|
6
|
+
class Base
|
7
|
+
|
8
|
+
def initialize(storage = nil)
|
9
|
+
@storage = storage
|
10
|
+
end
|
11
|
+
|
12
|
+
def flow
|
13
|
+
'events'
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_signature(datetime_string = '')
|
17
|
+
Patty::TimeSignature.new(datetime_string[0, 16]).align
|
18
|
+
end
|
19
|
+
|
20
|
+
def emit(signature = nil, value = nil, marker = nil)
|
21
|
+
if signature.present?
|
22
|
+
signature = Patty::TimeSignature.from_datetime.align
|
23
|
+
end
|
24
|
+
|
25
|
+
current_value = fetch signature, marker
|
26
|
+
|
27
|
+
unless current_value.present?
|
28
|
+
current_value = value
|
29
|
+
else
|
30
|
+
current_value = reduce [current_value, value]
|
31
|
+
end
|
32
|
+
|
33
|
+
@storage.put signature, current_value, marker
|
34
|
+
end
|
35
|
+
|
36
|
+
def map(flow_title = '', signature = nil, record = nil)
|
37
|
+
emit(signature, value, record['marker'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def reduce(data = [])
|
41
|
+
data.inject(0){ |acc, item| acc += item }
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch(signature = nil, marker = nil)
|
45
|
+
current_value = @storage.get signature, marker
|
46
|
+
|
47
|
+
unless current_value.present?
|
48
|
+
data = signature.children.inject([]) do |acc, child|
|
49
|
+
child_value = fetch(child, marker)
|
50
|
+
acc << child_value unless child_value.nil?
|
51
|
+
acc
|
52
|
+
end
|
53
|
+
|
54
|
+
if data.size > 0
|
55
|
+
current_value = reduce data
|
56
|
+
@storage.put signature, current_value, marker
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
current_value
|
61
|
+
end
|
62
|
+
|
63
|
+
def run
|
64
|
+
transport = Waffle::Base.new eval("Waffle::Transports::#{Waffle::Config.transport.capitalize}").new
|
65
|
+
|
66
|
+
transport.subscribe flow do |flow_title, event|
|
67
|
+
map flow_title, build_signature(event['occured_at']), event
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Patty
|
4
|
+
module Storages
|
5
|
+
class Riak
|
6
|
+
|
7
|
+
def initialize(bucket_name = 'events')
|
8
|
+
@client = ::Riak::Client.new
|
9
|
+
@bucket = @client.bucket bucket_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def put(key = nil, value = nil)
|
13
|
+
record = @bucket.get_or_new key
|
14
|
+
record.data = value
|
15
|
+
record.store
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(key = nil)
|
19
|
+
if @bucket.exists? key
|
20
|
+
@bucket.get(key).data
|
21
|
+
else
|
22
|
+
''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def del(key = nil)
|
27
|
+
@bucket.delete key
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Patty
|
4
|
+
class TimeSignature
|
5
|
+
|
6
|
+
MINUTES = ['00', '10', '20', '30', '40', '50']
|
7
|
+
HOURS = (0..23).map { |h| "%02d" % h }
|
8
|
+
MONTHS = (1..12).map { |m| "%02d" % m }
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def from_datetime(datetime = lambda{ DateTime.now })
|
12
|
+
if datetime.is_a?(Proc)
|
13
|
+
datetime = datetime.call
|
14
|
+
end
|
15
|
+
self.new(datetime.strftime("%Y-%m-%d %H:%M")).align
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :signature
|
20
|
+
|
21
|
+
def initialize(signature = '')
|
22
|
+
if signature.empty?
|
23
|
+
raise ArgumentError, 'signature can not be empty'
|
24
|
+
end
|
25
|
+
@signature = signature
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
signature == other.signature
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
signature
|
34
|
+
end
|
35
|
+
|
36
|
+
def kind
|
37
|
+
case signature.size
|
38
|
+
when 16
|
39
|
+
:minute
|
40
|
+
when 13
|
41
|
+
:hour
|
42
|
+
when 10
|
43
|
+
:day
|
44
|
+
when 7
|
45
|
+
:month
|
46
|
+
when 4
|
47
|
+
:year
|
48
|
+
else
|
49
|
+
raise 'broken signature'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def align
|
54
|
+
self.class.new "%04d-%02d-%02d %02d:%02d" % [year, month, day, hour, aligned_minute]
|
55
|
+
end
|
56
|
+
|
57
|
+
def cut(from = 0, length = 2)
|
58
|
+
signature.size > from ? signature[from, length] : ''
|
59
|
+
end
|
60
|
+
|
61
|
+
def crop(to = 13)
|
62
|
+
signature.size > to ? signature[0, to] : signature
|
63
|
+
end
|
64
|
+
|
65
|
+
def year
|
66
|
+
cut(0, 4).to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def month
|
70
|
+
cut(5).to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
def day
|
74
|
+
cut(8).to_i
|
75
|
+
end
|
76
|
+
|
77
|
+
def hour
|
78
|
+
cut(11).to_i
|
79
|
+
end
|
80
|
+
|
81
|
+
def minute
|
82
|
+
cut(14).to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def aligned_minute
|
86
|
+
if kind == :minute
|
87
|
+
"%-1d0" % (minute / 10)
|
88
|
+
else
|
89
|
+
minute
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def days_count
|
94
|
+
Date.new(year, month, -1).day
|
95
|
+
end
|
96
|
+
|
97
|
+
def parents
|
98
|
+
[crop(13), crop(10), crop(7), crop(4)].delete_if{ |p| p.empty? }.map{ |p| self.class.new p }
|
99
|
+
end
|
100
|
+
|
101
|
+
def children
|
102
|
+
case kind
|
103
|
+
when :hour
|
104
|
+
MINUTES.map{ |m| self.class.new "#{signature}:#{m}" }
|
105
|
+
when :day
|
106
|
+
HOURS.map{ |h| self.class.new "#{signature} #{h}" }
|
107
|
+
when :month
|
108
|
+
(1..days_count).map{ |d| self.class.new "#{signature}-" + '%02d' % d }
|
109
|
+
when :year
|
110
|
+
MONTHS.map{ |m| self.class.new "#{signature}-#{m}" }
|
111
|
+
else
|
112
|
+
[]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
data/lib/patty.rb
ADDED
data/patty.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "patty/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'patty'
|
7
|
+
s.version = Patty::VERSION
|
8
|
+
|
9
|
+
s.homepage = 'http://github.com/peanut/patty'
|
10
|
+
s.authors = ['Alexander Lomakin']
|
11
|
+
s.email = 'alexander.lomakin@gmail.com'
|
12
|
+
|
13
|
+
s.summary = 'Framework for creating Waffle events handlers'
|
14
|
+
s.description = 'Server part of Patty statistics server'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'yajl-ruby'
|
19
|
+
s.add_runtime_dependency 'riak-client'
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'patty/time_signature'
|
3
|
+
|
4
|
+
class TimeSignatureTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@time_signature = Patty::TimeSignature.new('2005-10-10 18:55')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_to_s
|
11
|
+
assert_equal '2005-10-10 18:55', @time_signature.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_kind
|
15
|
+
assert_equal :minute, @time_signature.kind
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_cut
|
19
|
+
assert_equal '10', @time_signature.cut(5, 2)
|
20
|
+
assert_equal '', @time_signature.cut(1000, 2)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_crop
|
24
|
+
assert_equal '2005-10-10', @time_signature.crop(10)
|
25
|
+
assert_equal '2005-10-10 18:55', @time_signature.crop(100)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_parents
|
29
|
+
expected_parents = [
|
30
|
+
Patty::TimeSignature.new('2005-10-10 18'),
|
31
|
+
Patty::TimeSignature.new('2005-10-10'),
|
32
|
+
Patty::TimeSignature.new('2005-10'),
|
33
|
+
Patty::TimeSignature.new('2005')
|
34
|
+
]
|
35
|
+
assert_equal expected_parents, @time_signature.parents
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_children_for_minute
|
39
|
+
assert_equal 0, @time_signature.children.size
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_children_for_hour
|
43
|
+
@time_signature = Patty::TimeSignature.new('2005-10-10 18')
|
44
|
+
assert_equal 6, @time_signature.children.size
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_children_for_day
|
48
|
+
@time_signature = Patty::TimeSignature.new('2005-10-10')
|
49
|
+
assert_equal 24, @time_signature.children.size
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_children_for_month
|
53
|
+
@time_signature = Patty::TimeSignature.new('2012-02')
|
54
|
+
assert_equal 29, @time_signature.children.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_children_for_year
|
58
|
+
@time_signature = Patty::TimeSignature.new('2005')
|
59
|
+
assert_equal 12, @time_signature.children.size
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_from_datetime
|
63
|
+
@time_signature = Patty::TimeSignature.from_datetime DateTime.new(2012, 02, 29, 12, 35)
|
64
|
+
assert_equal '2012-02-29 12:30', @time_signature.signature
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: patty
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alexander Lomakin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-02 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yajl-ruby
|
16
|
+
requirement: &70183040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70183040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: riak-client
|
27
|
+
requirement: &70182340 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70182340
|
36
|
+
description: Server part of Patty statistics server
|
37
|
+
email: alexander.lomakin@gmail.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- .travis.yml
|
43
|
+
- Gemfile
|
44
|
+
- README.md
|
45
|
+
- Rakefile
|
46
|
+
- lib/patty.rb
|
47
|
+
- lib/patty/aggregated_storage.rb
|
48
|
+
- lib/patty/base.rb
|
49
|
+
- lib/patty/storages/riak.rb
|
50
|
+
- lib/patty/time_signature.rb
|
51
|
+
- lib/patty/version.rb
|
52
|
+
- patty.gemspec
|
53
|
+
- test/test_time_signature.rb
|
54
|
+
homepage: http://github.com/peanut/patty
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.10
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Framework for creating Waffle events handlers
|
78
|
+
test_files: []
|