redistat 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.
@@ -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