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 ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake'
4
+ gem 'mocha'
5
+ gem 'patty', :git => 'git://github.com/peanut/patty.git'
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Patty
2
+
3
+ Framework for creating Waffle events handlers.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/peanut/patty.png?branch=master)](http://travis-ci.org/peanut/patty)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -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
@@ -0,0 +1,5 @@
1
+ module Patty
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ end
data/lib/patty.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'patty/version'
2
+ require 'patty/base'
3
+ require 'patty/aggregated_storage'
4
+
5
+ module Patty
6
+ module Storages
7
+ autoload :Riak, 'patty/storages/riak'
8
+ end
9
+ end
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: []