redistat 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ module Redistat
2
+ class Event
3
+ include Database
4
+
5
+ attr_reader :id
6
+ attr_reader :key
7
+
8
+ attr_accessor :stats
9
+ attr_accessor :meta
10
+ attr_accessor :options
11
+
12
+ def initialize(scope, label = nil, date = nil, stats = {}, options = {}, meta = {}, is_new = true)
13
+ @options = default_options.merge(options)
14
+ @key = Key.new(scope, label, date, @options)
15
+ @stats = stats ||= {}
16
+ @meta = meta ||= {}
17
+ @new = is_new
18
+ end
19
+
20
+ def default_options
21
+ { :depth => :hour, :store_event => false }
22
+ end
23
+
24
+ def new?
25
+ @new
26
+ end
27
+
28
+ def date
29
+ @key.date
30
+ end
31
+
32
+ def date=(input)
33
+ @key.date = input
34
+ end
35
+
36
+ def scope
37
+ @key.scope
38
+ end
39
+
40
+ def scope=(input)
41
+ @key.scope = input
42
+ end
43
+
44
+ def label
45
+ @key.label
46
+ end
47
+
48
+ def label_hash
49
+ @key.label_hash
50
+ end
51
+
52
+ def label=(input)
53
+ @key.label = input
54
+ end
55
+
56
+ def next_id
57
+ db.incr("#{self.scope}#{KEY_NEXT_ID}")
58
+ end
59
+
60
+ def save
61
+ return false if !self.new?
62
+ Summary.update_all(@key, @stats, depth_limit)
63
+ if @options[:store_event]
64
+ @id = self.next_id
65
+ db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
66
+ "scope", self.scope,
67
+ "label", self.label,
68
+ "date", self.date.to_time.to_s,
69
+ "stats", self.stats.to_json,
70
+ "meta", self.meta.to_json,
71
+ "options", self.options.to_json)
72
+ db.sadd("#{self.scope}#{KEY_EVENT_IDS}", @id)
73
+ end
74
+ @new = false
75
+ self
76
+ end
77
+
78
+ def depth_limit
79
+ @options[:depth] ||= @key.depth
80
+ end
81
+
82
+ def self.create(*args)
83
+ self.new(*args).save
84
+ end
85
+
86
+ def self.find(scope, id)
87
+ event = db.hgetall "#{scope}#{KEY_EVENT}#{id}"
88
+ return nil if event.size == 0
89
+ self.new( event["scope"], event["label"], event["date"], JSON.parse(event["stats"]),
90
+ JSON.parse(event["meta"]), JSON.parse(event["options"]), false )
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,165 @@
1
+ module Redistat
2
+ class Finder
3
+ include Database
4
+
5
+ attr_reader :options
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ end
10
+
11
+ def valid_options?
12
+ return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
13
+ false
14
+ end
15
+
16
+ def find(options = {})
17
+ @options.merge!(options)
18
+ raise InvalidOptions.new if !valid_options?
19
+ if @options[:interval].nil? || !@options[:interval]
20
+ find_by_magic
21
+ else
22
+ find_by_interval
23
+ end
24
+ end
25
+
26
+ def find_by_interval(options = {})
27
+ @options.merge!(options)
28
+ raise InvalidOptions.new if !valid_options?
29
+ key = build_key
30
+ col = Collection.new(@options)
31
+ col.total = Result.new(@options)
32
+ build_date_sets.each do |set|
33
+ set[:add].each do |date|
34
+ result = Result.new
35
+ result.date = Date.new(date).to_time
36
+ db.hgetall("#{key.prefix}#{date}").each do |k, v|
37
+ result[k] = v
38
+ col.total.set_or_incr(k, v.to_i)
39
+ end
40
+ col << result
41
+ end
42
+ end
43
+ col
44
+ end
45
+
46
+ def find_by_magic(options = {})
47
+ @options.merge!(options)
48
+ raise InvalidOptions.new if !valid_options?
49
+ key = Key.new(@options[:scope], @options[:label])
50
+ col = Collection.new(@options)
51
+ col.total = Result.new(@options)
52
+ col << col.total
53
+ build_date_sets.each do |set|
54
+ sum = Result.new
55
+ sum = summarize_add_keys(set[:add], key, sum)
56
+ sum = summarize_rem_keys(set[:rem], key, sum)
57
+ sum.each do |k, v|
58
+ col.total.set_or_incr(k, v.to_i)
59
+ end
60
+ end
61
+ col
62
+ end
63
+
64
+ def build_date_sets
65
+ Finder::DateSet.new(@options[:from], @options[:till], @options[:depth], @options[:interval])
66
+ end
67
+
68
+ def build_key
69
+ Key.new(@options[:scope], @options[:label])
70
+ end
71
+
72
+ def summarize_add_keys(sets, key, sum)
73
+ sets.each do |date|
74
+ db.hgetall("#{key.prefix}#{date}").each do |k, v|
75
+ sum.set_or_incr(k, v.to_i)
76
+ end
77
+ end
78
+ sum
79
+ end
80
+
81
+ def summarize_rem_keys(sets, key, sum)
82
+ sets.each do |date|
83
+ db.hgetall("#{key.prefix}#{date}").each do |k, v|
84
+ sum.set_or_incr(k, -v.to_i)
85
+ end
86
+ end
87
+ sum
88
+ end
89
+
90
+ class << self
91
+
92
+ def find(*args)
93
+ new.find(*args)
94
+ end
95
+
96
+ def scope(scope)
97
+ new.scope(scope)
98
+ end
99
+
100
+ def label(label)
101
+ new.label(label)
102
+ end
103
+
104
+ def dates(from, till)
105
+ new.dates(from, till)
106
+ end
107
+ alias :date :dates
108
+
109
+ def from(date)
110
+ new.from(date)
111
+ end
112
+
113
+ def till(date)
114
+ new.till(date)
115
+ end
116
+ alias :untill :till
117
+
118
+ def depth(unit)
119
+ new.depth(unit)
120
+ end
121
+
122
+ def interval(unit)
123
+ new.interval(unit)
124
+ end
125
+
126
+ end
127
+
128
+ def scope(scope)
129
+ @options[:scope] = scope
130
+ self
131
+ end
132
+
133
+ def label(label)
134
+ @options[:label] = label
135
+ self
136
+ end
137
+
138
+ def dates(from, till)
139
+ from(from).till(till)
140
+ end
141
+ alias :date :dates
142
+
143
+ def from(date)
144
+ @options[:from] = date
145
+ self
146
+ end
147
+
148
+ def till(date)
149
+ @options[:till] = date
150
+ self
151
+ end
152
+ alias :until :till
153
+
154
+ def depth(unit)
155
+ @options[:depth] = unit
156
+ self
157
+ end
158
+
159
+ def interval(unit)
160
+ @options[:interval] = unit
161
+ self
162
+ end
163
+
164
+ end
165
+ end
@@ -0,0 +1,95 @@
1
+ module Redistat
2
+ class Finder
3
+ class DateSet < Array
4
+
5
+ def initialize(start_date = nil, end_date = nil, depth = nil, interval = false)
6
+ if !start_date.nil? && !end_date.nil?
7
+ find_date_sets(start_date, end_date, depth, interval)
8
+ end
9
+ end
10
+
11
+ def find_date_sets(start_date, end_date, depth = nil, interval = false)
12
+ start_date = start_date.to_time if start_date.is_a?(::Date)
13
+ end_date = end_date.to_time if end_date.is_a?(::Date)
14
+ if !interval
15
+ find_date_sets_by_magic(start_date, end_date, depth)
16
+ else
17
+ find_date_sets_by_interval(start_date, end_date, depth)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def find_date_sets_by_magic(start_date, end_date, depth = nil)
24
+ depth ||= :hour
25
+ depths = Date::DEPTHS[Date::DEPTHS.index(:year), Date::DEPTHS.index(depth)+1].reverse
26
+ depths.each_with_index do |d, i|
27
+ sets = [find_start_keys_for(d, start_date, end_date, (i == 0))]
28
+ sets << find_end_keys_for(d, start_date, end_date, (i == 0))
29
+ sets.each do |set|
30
+ self << set if set != { :add => [], :rem => [] }
31
+ end
32
+ end
33
+ self
34
+ end
35
+
36
+ def find_date_sets_by_interval(start_date, end_date, depth, inclusive = true)
37
+ depth ||= :hour
38
+ self << { :add => start_date.map_beginning_of_each(depth, :include_start => inclusive, :include_end => inclusive).until(end_date) { |t| t.to_rs.to_s(depth) }, :rem => [] }
39
+ end
40
+
41
+ def find_start_keys_for(unit, start_date, end_date, lowest_depth = false)
42
+ return find_start_year_for(start_date, end_date, lowest_depth) if unit == :year
43
+ index = Date::DEPTHS.index(unit)
44
+ nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
45
+ if start_date < start_date.round(nunit) || start_date.next(nunit).beginning_of(nunit) > end_date.beginning_of(nunit)
46
+ add = []
47
+ start_date.beginning_of_each(unit, :include_start => lowest_depth).until(start_date.end_of(nunit)) do |t|
48
+ add << t.to_rs.to_s(unit) if t < end_date.beginning_of(unit)
49
+ end
50
+ { :add => add, :rem => [] }
51
+ else
52
+ { :add => [start_date.beginning_of(nunit).to_rs.to_s(nunit)],
53
+ :rem => start_date.beginning_of(nunit).map_beginning_of_each(unit, :include_start => true, :include_end => !lowest_depth).until(start_date) { |t| t.to_rs.to_s(unit) } }
54
+ end
55
+ end
56
+
57
+ def find_end_keys_for(unit, start_date, end_date, lowest_depth = false)
58
+ return find_end_year_for(start_date, end_date, lowest_depth) if unit == :year
59
+ index = Date::DEPTHS.index(unit)
60
+ nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
61
+ has_nunit = end_date.prev(nunit).beginning_of(nunit) >= start_date.beginning_of(nunit)
62
+ nearest_nunit = end_date.round(nunit)
63
+ if end_date >= nearest_nunit && has_nunit
64
+ add = []
65
+ end_date.beginning_of(nunit).beginning_of_each(unit, :include_start => true, :include_end => lowest_depth).until(end_date) do |t|
66
+ add << t.to_rs.to_s(unit) if t > start_date.beginning_of(unit)
67
+ end
68
+ { :add => add, :rem => [] }
69
+ elsif has_nunit
70
+ { :add => [end_date.beginning_of(nunit).to_rs.to_s(nunit)],
71
+ :rem => end_date.map_beginning_of_each(unit, :include_start => !lowest_depth).until(end_date.end_of(nunit)) { |t| t.to_rs.to_s(unit) } }
72
+ else
73
+ { :add => [], :rem => [] }
74
+ end
75
+ end
76
+
77
+ def find_start_year_for(start_date, end_date, lowest_depth = false)
78
+ if start_date.years_since(1).beginning_of_year < end_date.beginning_of_year
79
+ { :add => start_date.map_beginning_of_each_year(:include_end => lowest_depth).until(end_date) { |t| t.to_rs.to_s(:year) }, :rem => [] }
80
+ else
81
+ { :add => [], :rem => [] }
82
+ end
83
+ end
84
+
85
+ def find_end_year_for(start_date, end_date, lowest_depth = false)
86
+ if lowest_depth
87
+ { :add => [end_date.beginning_of_year.to_rs.to_s(:year)], :rem => [] }
88
+ else
89
+ { :add => [], :rem => [] }
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,54 @@
1
+ module Redistat
2
+ class Key
3
+
4
+ attr_accessor :scope
5
+ attr_accessor :date
6
+ attr_accessor :options
7
+
8
+ def initialize(scope, label = nil, date = nil, options = {})
9
+ @scope = scope
10
+ self.label = label if !label.nil?
11
+ self.date = date ||= Time.now
12
+ @options = default_options.merge(options ||= {})
13
+ end
14
+
15
+ def default_options
16
+ { :depth => :day }
17
+ end
18
+
19
+ def prefix
20
+ key = "#{@scope}"
21
+ key << "/" + ((@options[:label_hash].nil? || @options[:label_hash] == true) ? @label.hash : @label.name) if !label.nil?
22
+ key << ":"
23
+ key
24
+ end
25
+
26
+ def date=(input)
27
+ @date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date
28
+ end
29
+
30
+ def depth
31
+ @options[:depth]
32
+ end
33
+
34
+ def label
35
+ @label.name
36
+ end
37
+
38
+ def label_hash
39
+ @label.hash
40
+ end
41
+
42
+ def label=(input)
43
+ @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input)
44
+ end
45
+
46
+ def to_s(depth = nil)
47
+ depth ||= @options[:depth]
48
+ key = self.prefix
49
+ key << @date.to_s(depth)
50
+ key
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ module Redistat
2
+ class Label
3
+ include Database
4
+
5
+ attr_reader :name
6
+ attr_reader :hash
7
+
8
+ def initialize(str)
9
+ @name = str.to_s
10
+ @hash = Digest::SHA1.hexdigest(@name)
11
+ end
12
+
13
+ def save
14
+ @saved = (db.set("#{KEY_LEBELS}#{@hash}", @name) == "OK")
15
+ self
16
+ end
17
+
18
+ def saved?
19
+ @saved ||= false
20
+ end
21
+
22
+ def self.create(name)
23
+ self.new(name).save
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ module Redistat
2
+ module Model
3
+
4
+ def self.included(base)
5
+ base.extend(self)
6
+ end
7
+
8
+ def store(label, stats = {}, date = nil, meta = {}, opts = {})
9
+ Event.new(name, label, date, stats, options.merge(opts), meta).save
10
+ end
11
+ alias :event :store
12
+
13
+ def fetch(label, from, till, opts = {})
14
+ Finder.find({
15
+ :scope => name,
16
+ :label => label,
17
+ :from => from,
18
+ :till => till
19
+ }.merge(options.merge(opts)))
20
+ end
21
+ alias :find :fetch
22
+
23
+ def depth(depth = nil)
24
+ if !depth.nil?
25
+ options[:depth] = depth
26
+ else
27
+ options[:depth] || nil
28
+ end
29
+ end
30
+
31
+ def store_event(boolean = nil)
32
+ if !boolean.nil?
33
+ options[:store_event] = boolean
34
+ else
35
+ options[:store_event] || nil
36
+ end
37
+ end
38
+
39
+ def options
40
+ @options ||= {}
41
+ end
42
+
43
+ private
44
+
45
+ def name
46
+ @name ||= self.to_s
47
+ end
48
+
49
+ end
50
+ end