tabstabs 2.0.0

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +421 -0
  8. data/Rakefile +5 -0
  9. data/lib/tabs_tabs.rb +26 -0
  10. data/lib/tabs_tabs/config.rb +65 -0
  11. data/lib/tabs_tabs/helpers.rb +27 -0
  12. data/lib/tabs_tabs/metrics/counter.rb +69 -0
  13. data/lib/tabs_tabs/metrics/counter/stats.rb +51 -0
  14. data/lib/tabs_tabs/metrics/task.rb +72 -0
  15. data/lib/tabs_tabs/metrics/task/token.rb +89 -0
  16. data/lib/tabs_tabs/metrics/value.rb +91 -0
  17. data/lib/tabs_tabs/metrics/value/stats.rb +55 -0
  18. data/lib/tabs_tabs/resolution.rb +65 -0
  19. data/lib/tabs_tabs/resolutionable.rb +48 -0
  20. data/lib/tabs_tabs/resolutions/day.rb +40 -0
  21. data/lib/tabs_tabs/resolutions/hour.rb +40 -0
  22. data/lib/tabs_tabs/resolutions/minute.rb +40 -0
  23. data/lib/tabs_tabs/resolutions/month.rb +40 -0
  24. data/lib/tabs_tabs/resolutions/week.rb +40 -0
  25. data/lib/tabs_tabs/resolutions/year.rb +40 -0
  26. data/lib/tabs_tabs/storage.rb +105 -0
  27. data/lib/tabs_tabs/tabs_tabs.rb +117 -0
  28. data/lib/tabs_tabs/version.rb +3 -0
  29. data/spec/lib/tabs_tabs/config_spec.rb +60 -0
  30. data/spec/lib/tabs_tabs/metrics/counter/stats_spec.rb +42 -0
  31. data/spec/lib/tabs_tabs/metrics/counter_spec.rb +196 -0
  32. data/spec/lib/tabs_tabs/metrics/task/token_spec.rb +18 -0
  33. data/spec/lib/tabs_tabs/metrics/task_spec.rb +103 -0
  34. data/spec/lib/tabs_tabs/metrics/value/stats_spec.rb +61 -0
  35. data/spec/lib/tabs_tabs/metrics/value_spec.rb +160 -0
  36. data/spec/lib/tabs_tabs/resolution_spec.rb +52 -0
  37. data/spec/lib/tabs_tabs/resolutionable_spec.rb +53 -0
  38. data/spec/lib/tabs_tabs/resolutions/day_spec.rb +23 -0
  39. data/spec/lib/tabs_tabs/resolutions/hour_spec.rb +23 -0
  40. data/spec/lib/tabs_tabs/resolutions/minute_spec.rb +23 -0
  41. data/spec/lib/tabs_tabs/resolutions/month_spec.rb +23 -0
  42. data/spec/lib/tabs_tabs/resolutions/week_spec.rb +24 -0
  43. data/spec/lib/tabs_tabs/resolutions/year_spec.rb +23 -0
  44. data/spec/lib/tabs_tabs/storage_spec.rb +138 -0
  45. data/spec/lib/tabs_tabs_spec.rb +223 -0
  46. data/spec/spec_helper.rb +17 -0
  47. data/spec/support/custom_resolutions.rb +40 -0
  48. data/tabs_tabs.gemspec +31 -0
  49. metadata +213 -0
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe TabsTabs::Resolutions::Year do
4
+ let(:timestamp){ Time.utc(2000, 2) }
5
+
6
+ context "#normalize" do
7
+ it "should normalize the date to year, month" do
8
+ expect(subject.normalize(timestamp)).to eq(timestamp.change(month: 1))
9
+ end
10
+ end
11
+
12
+ context "#serialize" do
13
+ it "should return YYYY" do
14
+ expect(subject.serialize(timestamp)).to eq("2000")
15
+ end
16
+ end
17
+
18
+ context "#deserialize" do
19
+ it "should convert string into date" do
20
+ expect(subject.deserialize("2000")).to eq(timestamp.change(month: 1))
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,138 @@
1
+ require "spec_helper"
2
+
3
+ describe TabsTabs::Storage do
4
+ context "#redis" do
5
+ it "should return the configured Redis" do
6
+ expect(subject.redis).to eq(TabsTabs::Config.redis)
7
+ end
8
+ end
9
+
10
+ context "#tabs_key" do
11
+ after do
12
+ TabsTabs::Config.prefix = nil
13
+ end
14
+
15
+ it "should add prefix if configued" do
16
+ TabsTabs::Config.prefix = "myapp"
17
+ expect(subject.tabs_key("key")).to eq("tabstabs:myapp:key")
18
+ end
19
+
20
+ it "should not add prefix if not there" do
21
+ expect(subject.tabs_key("key")).to eq("tabstabs:key")
22
+ end
23
+ end
24
+
25
+ context "with stubbed redis" do
26
+
27
+ let(:stubbed_redis) { double("redis").as_null_object }
28
+
29
+ before do
30
+ allow(subject).to receive(:redis).and_return(stubbed_redis)
31
+ allow(subject).to receive(:tabs_key).at_least(:once).and_call_original
32
+ end
33
+
34
+ it "#exists calls exists with the expected key" do
35
+ subject.exists("foo")
36
+ expect(stubbed_redis).to have_received(:exists).with("tabstabs:foo")
37
+ end
38
+
39
+ it "#expireat calls expireat with expected key and timestamp" do
40
+ subject.expireat("foo", 1234)
41
+ expect(stubbed_redis).to have_received(:expireat).with("tabstabs:foo", 1234)
42
+ end
43
+
44
+ it "#ttl calls ttl with expected key" do
45
+ subject.ttl("foo")
46
+ expect(stubbed_redis).to have_received(:ttl).with("tabstabs:foo")
47
+ end
48
+
49
+ it "#get calls get with expected key" do
50
+ subject.get("foo")
51
+ expect(stubbed_redis).to have_received(:get).with("tabstabs:foo")
52
+ end
53
+
54
+ it "#mget receives prefixed keys" do
55
+ subject.mget("foo", "bar")
56
+ expect(stubbed_redis).to have_received(:mget).with("tabstabs:foo", "tabstabs:bar")
57
+ end
58
+
59
+ it "#set calls set with the expected key and arg" do
60
+ subject.set("foo", "bar")
61
+ expect(stubbed_redis).to have_received(:set).with("tabstabs:foo", "bar")
62
+ end
63
+
64
+ it "#del" do
65
+ subject.del("foo")
66
+ expect(stubbed_redis).to have_received(:del).with("tabstabs:foo")
67
+ end
68
+
69
+ it "#del_by_prefix" do
70
+ allow(stubbed_redis).to receive(:keys).and_return(["foo:a", "foo:b"])
71
+ subject.del_by_prefix("foo")
72
+ expect(stubbed_redis).to have_received(:del).with("foo:a", "foo:b")
73
+ end
74
+
75
+ it "#incr" do
76
+ subject.incr("foo")
77
+ expect(stubbed_redis).to have_received(:incr).with("tabstabs:foo")
78
+ end
79
+
80
+ it "#rpush" do
81
+ subject.rpush("foo", "bar")
82
+ expect(stubbed_redis).to have_received(:rpush).with("tabstabs:foo", "bar")
83
+ end
84
+
85
+ it "#sadd" do
86
+ subject.sadd("foo", "bar", "baz")
87
+ expect(stubbed_redis).to have_received(:sadd).with("tabstabs:foo", "bar", "baz")
88
+ end
89
+
90
+ it "#smembers" do
91
+ subject.smembers("foo")
92
+ expect(stubbed_redis).to have_received(:smembers).with("tabstabs:foo")
93
+ end
94
+
95
+ it "#smembers_all" do
96
+ allow(stubbed_redis).to receive(:pipelined).and_yield
97
+ subject.smembers_all("foo", "bar")
98
+ expect(stubbed_redis).to have_received(:smembers).with("tabstabs:foo")
99
+ expect(stubbed_redis).to have_received(:smembers).with("tabstabs:bar")
100
+ end
101
+
102
+ it "#sismember" do
103
+ subject.sismember("foo", "bar")
104
+ expect(stubbed_redis).to have_received(:sismember).with("tabstabs:foo", "bar")
105
+ end
106
+
107
+ it "#hget" do
108
+ subject.hget("foo", "bar")
109
+ expect(stubbed_redis).to have_received(:hget).with("tabstabs:foo", "bar")
110
+ end
111
+
112
+ it "#hset" do
113
+ subject.hset("foo", "bar", "baz")
114
+ expect(stubbed_redis).to have_received(:hset).with("tabstabs:foo", "bar", "baz")
115
+ end
116
+
117
+ it "#hdel" do
118
+ subject.hdel("foo", "bar")
119
+ expect(stubbed_redis).to have_received(:hdel).with("tabstabs:foo", "bar")
120
+ end
121
+
122
+ it "#hkeys" do
123
+ subject.hkeys("foo")
124
+ expect(stubbed_redis).to have_received(:hkeys).with("tabstabs:foo")
125
+ end
126
+
127
+ it "#hincrby" do
128
+ subject.hincrby("foo", "bar", 42)
129
+ expect(stubbed_redis).to have_received(:hincrby).with("tabstabs:foo", "bar", 42)
130
+ end
131
+
132
+ it "#hgetall" do
133
+ subject.hgetall("foo")
134
+ expect(stubbed_redis).to have_received(:hgetall).with("tabstabs:foo")
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,223 @@
1
+ require "spec_helper"
2
+
3
+ describe TabsTabs do
4
+ include TabsTabs::Storage
5
+
6
+ describe ".create_metric" do
7
+
8
+ it "raises an error if the type is invalid" do
9
+ expect { TabsTabs.create_metric("foo", "foobar") }.to raise_error(TabsTabs::UnknownTypeError)
10
+ end
11
+
12
+ it "raises an error if the metric already exists" do
13
+ TabsTabs.create_metric("foo", "counter")
14
+ expect { TabsTabs.create_metric("foo", "counter") }.to raise_error(TabsTabs::DuplicateMetricError)
15
+ end
16
+
17
+ it "returns a Counter metric if 'counter' was the specified type" do
18
+ expect(TabsTabs.create_metric("foo", "counter")).to be_a_kind_of(TabsTabs::Metrics::Counter)
19
+ end
20
+
21
+ it "returns a Value metric if 'value' was the specified type" do
22
+ expect(TabsTabs.create_metric("foo", "value")).to be_a_kind_of(TabsTabs::Metrics::Value)
23
+ end
24
+
25
+ it "adds the metric's key to the list_metrics" do
26
+ TabsTabs.create_metric("foo", "value")
27
+ TabsTabs.create_metric("bar", "counter")
28
+ TabsTabs.create_metric("baz", "task")
29
+ expect(TabsTabs.list_metrics).to include("foo")
30
+ expect(TabsTabs.list_metrics).to include("bar")
31
+ expect(TabsTabs.list_metrics).to include("baz")
32
+ end
33
+
34
+ end
35
+
36
+ describe ".counter_total" do
37
+
38
+ it "returns the total for a counter metric" do
39
+ TabsTabs.increment_counter("foo")
40
+ expect(TabsTabs.counter_total("foo")).to eq 1
41
+ end
42
+
43
+ it "returns the value of the block if given and the metric doesn't exist" do
44
+ expect(TabsTabs.counter_total("foo") { 42 }).to eq 42
45
+ end
46
+
47
+ it "raises an UnknownMetricError if no block is given and the metric does not exist" do
48
+ expect { TabsTabs.counter_total("foo") }.to raise_error TabsTabs::UnknownMetricError
49
+ end
50
+
51
+ end
52
+
53
+ describe ".get_metric" do
54
+
55
+ it "returns the expected metric object" do
56
+ TabsTabs.create_metric("foo", "counter")
57
+ expect(TabsTabs.get_metric("foo")).to be_a_kind_of(TabsTabs::Metrics::Counter)
58
+ end
59
+
60
+ end
61
+
62
+ describe ".list_metrics" do
63
+
64
+ it "returns the list_metrics of metric names" do
65
+ TabsTabs.create_metric("foo", "counter")
66
+ TabsTabs.create_metric("bar", "value")
67
+ expect(TabsTabs.list_metrics).to eq(["foo", "bar"])
68
+ end
69
+
70
+ end
71
+
72
+ describe ".metric_exists?" do
73
+
74
+ it "returns true if the metric exists" do
75
+ TabsTabs.create_metric("foo", "counter")
76
+ expect(TabsTabs.metric_exists?("foo")).to be_truthy
77
+ end
78
+
79
+ it "returns false if the metric does not exist" do
80
+ expect(TabsTabs.metric_exists?("foo")).to be_falsey
81
+ end
82
+
83
+ end
84
+
85
+ describe ".drop_metric" do
86
+
87
+ before do
88
+ TabsTabs.create_metric("foo", "counter")
89
+ end
90
+
91
+ it "removes the metric from the list_metrics" do
92
+ TabsTabs.drop_metric!("foo")
93
+ expect(TabsTabs.list_metrics).to_not include("foo")
94
+ end
95
+
96
+ it "results in metric_exists? returning false" do
97
+ TabsTabs.drop_metric!("foo")
98
+ expect(TabsTabs.metric_exists?("foo")).to be_falsey
99
+ end
100
+
101
+ it "calls drop! on the metric" do
102
+ metric = double(:metric)
103
+ allow(TabsTabs).to receive(:get_metric).and_return(metric)
104
+ expect(metric).to receive(:drop!)
105
+ TabsTabs.drop_metric!("foo")
106
+ end
107
+
108
+ end
109
+
110
+ describe ".drop_all_metrics" do
111
+
112
+ it "drops all metrics" do
113
+ TabsTabs.create_metric("foo", "counter")
114
+ TabsTabs.create_metric("bar", "value")
115
+ TabsTabs.drop_all_metrics!
116
+ expect(TabsTabs.metric_exists?("foo")).to be_falsey
117
+ expect(TabsTabs.metric_exists?("bar")).to be_falsey
118
+ end
119
+
120
+ end
121
+
122
+ describe ".increment_counter" do
123
+
124
+ it "raises a Tabs::MetricTypeMismatchError if the metric is the wrong type" do
125
+ TabsTabs.create_metric("foo", "value")
126
+ expect { TabsTabs.increment_counter("foo") }.to raise_error(TabsTabs::MetricTypeMismatchError)
127
+ end
128
+
129
+ it "creates the metric if it doesn't exist" do
130
+ expect(TabsTabs.metric_exists?("foo")).to be_falsey
131
+ expect { TabsTabs.increment_counter("foo") }.to_not raise_error
132
+ expect(TabsTabs.metric_exists?("foo")).to be_truthy
133
+ end
134
+
135
+ it "calls increment on the metric" do
136
+ metric = TabsTabs.create_metric("foo", "counter")
137
+ allow(TabsTabs).to receive(:get_metric).and_return(metric)
138
+ expect(metric).to receive(:increment)
139
+ TabsTabs.increment_counter("foo")
140
+ end
141
+
142
+ end
143
+
144
+ describe ".record_value" do
145
+
146
+ it "creates the metric if it doesn't exist" do
147
+ expect(TabsTabs.metric_exists?("foo")).to be_falsey
148
+ expect { TabsTabs.record_value("foo", 38) }.not_to raise_error
149
+ expect(TabsTabs.metric_exists?("foo")).to be_truthy
150
+ end
151
+
152
+ it "raises a Tabs::MetricTypeMismatchError if the metric is the wrong type" do
153
+ TabsTabs.create_metric("foo", "counter")
154
+ expect { TabsTabs.record_value("foo", 27) }.to raise_error(TabsTabs::MetricTypeMismatchError)
155
+ end
156
+
157
+ it "calls record on the metric" do
158
+ Timecop.freeze(Time.now.utc)
159
+ metric = TabsTabs.create_metric("foo", "value")
160
+ allow(TabsTabs).to receive(:get_metric).and_return(metric)
161
+ allow(metric).to receive(:record).with(42, Time.now.utc)
162
+ TabsTabs.record_value("foo", 42)
163
+ end
164
+
165
+ end
166
+
167
+ describe ".list_metrics" do
168
+
169
+ it "lists all metrics that are defined" do
170
+ TabsTabs.create_metric("foo", "counter")
171
+ TabsTabs.create_metric("bar", "counter")
172
+ TabsTabs.create_metric("baz", "counter")
173
+ expect(TabsTabs.list_metrics).to eq(["foo", "bar", "baz"])
174
+ end
175
+
176
+ end
177
+
178
+ describe ".metric_type" do
179
+
180
+ it "returns the type of a counter metric" do
181
+ TabsTabs.create_metric("foo", "counter")
182
+ expect(TabsTabs.metric_type("foo")).to eq("counter")
183
+ end
184
+
185
+ it "returns the type of a value metric" do
186
+ TabsTabs.create_metric("bar", "value")
187
+ expect(TabsTabs.metric_type("bar")).to eq("value")
188
+ end
189
+
190
+ it "returns the type of a task metric" do
191
+ TabsTabs.create_metric("baz", "task")
192
+ expect(TabsTabs.metric_type("baz")).to eq("task")
193
+ end
194
+
195
+ end
196
+
197
+ describe ".drop_resolution_for_metric!" do
198
+ it "raises unknown metric error if metric does not exist" do
199
+ expect{ TabsTabs.drop_resolution_for_metric!(:invalid, :minute) }.to raise_error(TabsTabs::UnknownMetricError)
200
+ end
201
+
202
+ it "raises resolution missing error if resolution not registered" do
203
+ TabsTabs.create_metric("baz", "value")
204
+ expect{ TabsTabs.drop_resolution_for_metric!("baz", :invalid) }.to raise_error(TabsTabs::ResolutionMissingError)
205
+ end
206
+
207
+ it "does not allow you to call drop_by_resolution if task metric" do
208
+ metric = TabsTabs.create_metric("baz", "task")
209
+ expect(metric).not_to receive(:drop_by_resolution!)
210
+ TabsTabs.drop_resolution_for_metric!("baz", :minute)
211
+ end
212
+
213
+ it "drops the metric by resolution" do
214
+ now = Time.utc(2000,1,1)
215
+ metric = TabsTabs.create_metric("baz", "value")
216
+ metric.record(42, now)
217
+ TabsTabs.drop_resolution_for_metric!("baz", :minute)
218
+ minute_key = TabsTabs::Metrics::Value.new("baz").storage_key(:minute, now)
219
+ expect(TabsTabs::Storage.exists(minute_key)).to be_falsey
220
+ end
221
+ end
222
+
223
+ end
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "tabs_tabs"
3
+ require "pry"
4
+ require "timecop"
5
+
6
+ RSpec.configure do |config|
7
+ config.mock_with :rspec
8
+
9
+ config.before(:each) do
10
+ TabsTabs::Resolution.reset_default_resolutions
11
+ TabsTabs::Storage.del_by_prefix("")
12
+ end
13
+
14
+ config.after(:each) do
15
+ Timecop.return
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ module BadlyFormedResolution
2
+ include TabsTabs::Resolutionable
3
+ extend self
4
+ end
5
+
6
+ module WellFormedResolution
7
+ include TabsTabs::Resolutionable
8
+ extend self
9
+
10
+ PATTERN = "%Y-%m-%d-%H-%M-%S"
11
+
12
+ def name
13
+ :seconds
14
+ end
15
+
16
+ def serialize(timestamp)
17
+ timestamp.strftime(PATTERN)
18
+ end
19
+
20
+ def deserialize(str)
21
+ dt = DateTime.strptime(str, PATTERN)
22
+ self.normalize(dt)
23
+ end
24
+
25
+ def from_seconds(s)
26
+ s / 1
27
+ end
28
+
29
+ def to_seconds
30
+ 1
31
+ end
32
+
33
+ def add(ts, num)
34
+ ts + num.seconds
35
+ end
36
+
37
+ def normalize(ts)
38
+ Time.utc(ts.year, ts.month, ts.day, ts.hour, ts.min, ts.sec)
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tabs_tabs/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.name = "tabstabs"
9
+ gem.version = TabsTabs::VERSION
10
+ gem.authors = ["Max Kießling", "Michael Prilop", "JC Grubbs"]
11
+ gem.email = ['max@kopfueber.org', 'michael.prilop@imw.fraunhofer.de']
12
+ gem.description = %q{A redis-backed metrics tracker for keeping tabstabs on pretty much anything. Fork of Tabs}
13
+ gem.summary = %q{A redis-backed metrics tracker for keeping tabstabs on pretty much anything ;)}
14
+ gem.homepage = "https://github.com/FHG-IMW/tabstabs"
15
+ gem.license = "MIT"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.add_dependency "activesupport", ">= 3.2"
23
+ gem.add_dependency "redis", ">= 3.0.0"
24
+
25
+ gem.add_development_dependency "pry"
26
+ gem.add_development_dependency "pry-nav"
27
+ gem.add_development_dependency "rake"
28
+ gem.add_development_dependency "rspec"
29
+ gem.add_development_dependency "timecop"
30
+
31
+ end