metric_system 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c76a2843de05d5ccaaa70865f23e76b09b287dc3
4
+ data.tar.gz: 34156de4921e7fb09874a3d47bedc4174ca4985b
5
+ SHA512:
6
+ metadata.gz: 13f1207dee0f2cfb89850d1b637f4015abc0507231b332640848f88d6295e30ca222d59c8df2ae972139f01a184c04c1af997f4752151eb6a2cb709701e06cc9
7
+ data.tar.gz: 89948cf352c4e282b5e0bb7c91dc3f6bca221312767bec1a84f72c5254fa3aa7952d0f2abef10366750a101b2e1f0891526df258d4cb7f60c4f8bf6717090eeb
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # metric_system
2
+
3
+ A simple metric collector
4
+
5
+ ## Installation
6
+
7
+ gem install metric_system
8
+
9
+ ## Getting started
10
+
11
+ TBD
12
+
13
+ ## License
14
+
15
+ The metric_system gem is (c) radiospiel, 2012; it is distributed under the terms of the Modified BSD License, see LICENSE.BSD for details.
@@ -0,0 +1,11 @@
1
+ def benchmark(msg, &block)
2
+ starts = Time.now
3
+
4
+ yield
5
+
6
+ ensure
7
+ runtime = Time.now - starts
8
+ if runtime > 0.5
9
+ STDERR.puts "%s: %.3f secs" % [ msg, runtime = Time.now - starts ]
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ class Array
2
+ def by(key = nil, &block)
3
+ ary = []
4
+
5
+ if key
6
+ each do |rec|
7
+ ary << rec[key] << rec
8
+ end
9
+ else
10
+ each do |value|
11
+ ary << yield(value) << value
12
+ end
13
+ end
14
+
15
+ Hash[*ary]
16
+ end
17
+ end
@@ -0,0 +1,158 @@
1
+ require "sqlite3"
2
+
3
+ # manage SQLite3::Records
4
+ #
5
+ # The SQLite3::Record module is able to generate classes that are optimized
6
+ # for a specific set of columns. It is build on top of Struct, which is way
7
+ # faster than Hashes, for example.
8
+ module SQLite3::Record
9
+ module ClassMethods
10
+ attr :columns, true
11
+
12
+ private
13
+
14
+ def to_time(s)
15
+ case s
16
+ when String then Time.parse(s)
17
+ when Fixnum then Time.at(s)
18
+ else s
19
+ end
20
+ end
21
+
22
+ def to_date(s)
23
+ return unless time = to_time(s)
24
+ time.to_date
25
+ end
26
+
27
+ public
28
+
29
+ def build(*attrs)
30
+ attrs = columns.zip(attrs).map do |key, value|
31
+ case key
32
+ when /_at$/ then to_time(value)
33
+ when /_on$/ then to_date(value)
34
+ else value
35
+ end
36
+ end
37
+
38
+ new *attrs
39
+ end
40
+ end
41
+
42
+ def to_a
43
+ self.class.columns.map do |column| send(column) end
44
+ end
45
+
46
+ def self.for_columns(columns)
47
+ columns = columns.map(&:to_sym)
48
+
49
+ @@classes ||= {}
50
+ @@classes[columns] ||= begin
51
+ struct = Struct.new(*columns)
52
+ struct.extend SQLite3::Record::ClassMethods
53
+ struct.include SQLite3::Record
54
+
55
+ struct.columns = columns
56
+ struct
57
+ end
58
+ end
59
+ end
60
+
61
+ class SQLite3::Query
62
+ def initialize(sql, statement)
63
+ expect! statement => SQLite3::Statement
64
+
65
+ @sql, @statement = sql, statement
66
+ end
67
+
68
+ def run(*args)
69
+ # STDERR.puts "Q: #{@sql} #{args.map(&:inspect).join(", ")}"
70
+ @statement.execute *args
71
+ end
72
+
73
+ def select(*args)
74
+ @klass ||= SQLite3::Record.for_columns(@statement.columns)
75
+
76
+ run(*args).map do |rec|
77
+ @klass.build *rec
78
+ end
79
+ end
80
+
81
+ def ask(*args)
82
+ results = run(*args)
83
+ row = results.first
84
+ results.reset
85
+
86
+ if !row then nil
87
+ elsif row.length == 1 then row.first
88
+ else row
89
+ end
90
+ end
91
+ end
92
+
93
+ class SQLite3::Database
94
+ # execute multiple SQL statements at once.
95
+ def exec(sql, *args)
96
+ args = prepare_arguments(args)
97
+
98
+ while sql =~ /\S/ do
99
+ statement = prepare(sql)
100
+
101
+ sql = statement.remainder
102
+ if statement.active?
103
+ statement.execute!(*args)
104
+ end
105
+ end
106
+
107
+ rescue
108
+ STDERR.puts "#{sql}: #{$!}"
109
+ raise
110
+ end
111
+
112
+ # -- cached queries ---------------------------------------------------------
113
+
114
+ private
115
+
116
+ def query(sql)
117
+ @queries ||= {}
118
+ @queries[sql] ||= SQLite3::Query.new sql, prepare(sql)
119
+ end
120
+
121
+ def prepare_arguments(args)
122
+ args.map do |arg|
123
+ case arg
124
+ when Time then arg.to_i
125
+ when Date then arg.to_time.to_i
126
+ else arg
127
+ end
128
+ end
129
+ end
130
+
131
+ public
132
+
133
+ def run(sql, *args)
134
+ query(sql).run *prepare_arguments(args)
135
+ end
136
+
137
+ def ask(sql, *args)
138
+ query(sql).ask *prepare_arguments(args)
139
+ end
140
+
141
+ # run a select like query. Returns an array of records.
142
+ def select(sql, *args)
143
+ query(sql).select *prepare_arguments(args)
144
+ end
145
+
146
+ def print(sql, *args)
147
+ results = select sql, *args
148
+ log_sql = sql.gsub(/\n/, " ").gsub(/\s+/, " ")
149
+ puts "=" * log_sql.length
150
+ puts log_sql
151
+ puts "-" * log_sql.length
152
+
153
+ results.each do |result|
154
+ pp result.to_a
155
+ end
156
+ puts "=" * log_sql.length
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ class MetricSystem
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,168 @@
1
+ require "expectation"
2
+ require_relative "metric_system/core_extensions"
3
+ require_relative "metric_system/sqlite3_extensions"
4
+
5
+ # -- The MetricSystem module --------------------------------------------------
6
+
7
+ require "forwardable"
8
+ class MetricSystem
9
+ extend Forwardable
10
+ delegate [:exec, :select, :transaction, :rollback] => :@db
11
+
12
+ PERIODS = [
13
+ [ :year, 31536000, "strftime('%Y-01-01', starts_at, 'unixepoch')" ],
14
+ [ :month, 2592000, "strftime('%Y-%m-01', starts_at, 'unixepoch')" ],
15
+ [ :week, 604800, "strftime('%Y-%m-%d', starts_at, 'unixepoch', 'weekday 1', '-7 days')" ],
16
+ [ :day, 86400, "strftime('%Y-%m-%d', starts_at, 'unixepoch')" ],
17
+ [ :hour, 3600, "strftime('%Y-%m-%d %H:00:00', starts_at, 'unixepoch')" ],
18
+ [ :minute, 60, "strftime('%Y-%m-%d %H:%M:00', starts_at, 'unixepoch')" ],
19
+ [ :second, 1, "strftime('%Y-%m-%d %H:%M:%S', starts_at, 'unixepoch')" ],
20
+ ]
21
+
22
+ def initialize(path)
23
+ @db = SQLite3::Database.new(path)
24
+
25
+ [ :counters, :gauges ].each do |name|
26
+ exec <<-SQL
27
+ CREATE TABLE IF NOT EXISTS #{name}(
28
+ id INTEGER PRIMARY KEY,
29
+
30
+ name NOT NULL, -- the event name
31
+ value NOT NULL, -- the value
32
+ starts_at TIMESTAMP NOT NULL DEFAULT (strftime('%s','now')) -- the timestamp
33
+ );
34
+
35
+ CREATE INDEX IF NOT EXISTS #{name}_idx1 ON #{name}(name, starts_at);
36
+
37
+ CREATE TABLE IF NOT EXISTS aggregated_#{name}(
38
+ id INTEGER PRIMARY KEY,
39
+
40
+ name NOT NULL, -- the event name
41
+ starts_at TIMESTAMP NOT NULL, -- the start-at timestamp
42
+ duration NOT NULL, -- the duration (estimate, in secs.)
43
+ period, -- the name of the period (year, day, etc.)
44
+ sum, -- the sum of event values
45
+ count, -- the count of events
46
+ value -- the aggregated value
47
+ );
48
+
49
+ CREATE UNIQUE INDEX IF NOT EXISTS aggregated_#{name}_uidx1 ON aggregated_#{name}(name, starts_at, duration);
50
+ CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx2 ON aggregated_#{name}(starts_at);
51
+ CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx3 ON aggregated_#{name}(duration);
52
+ SQL
53
+ end
54
+
55
+ exec <<-SQL
56
+ PRAGMA synchronous = NORMAL;
57
+
58
+ CREATE VIEW IF NOT EXISTS aggregates AS
59
+ SELECT * FROM aggregated_gauges
60
+ UNION
61
+ SELECT * FROM aggregated_counters
62
+ SQL
63
+ end
64
+
65
+ def gauge(name, value, starts_at = nil)
66
+ add_event :gauges, name, value, starts_at
67
+ end
68
+
69
+ def count(name, value, starts_at = nil)
70
+ add_event :counters, name, value, starts_at
71
+ end
72
+
73
+ private
74
+
75
+ def add_event(table, name, value, starts_at)
76
+ # get names of all related events. An event "a.b.c" is actually
77
+ # 3 events: "a", "a.b", "a.b.c"
78
+ names = begin
79
+ parts = name.split(".")
80
+ parts.length.downto(1).map do |cnt|
81
+ parts[0,cnt].join(".")
82
+ end
83
+ end
84
+
85
+ if starts_at
86
+ starts_at = Time.parse(starts_at) if starts_at.is_a?(String)
87
+
88
+ names.each do |name|
89
+ @db.run "INSERT INTO #{table}(name, value, starts_at) VALUES(?, ?, ?)", name, value, starts_at.to_i
90
+ end
91
+ else
92
+ names.each do |name|
93
+ @db.run "INSERT INTO #{table}(name, value) VALUES(?, ?)", name, value
94
+ end
95
+ end
96
+ end
97
+
98
+ public
99
+
100
+ PERIODS_BY_KEY = PERIODS.by(&:first)
101
+
102
+ def aggregate(*keys)
103
+ if keys.empty?
104
+ keys = PERIODS.map(&:first)
105
+ end
106
+
107
+ transaction do
108
+ keys.each do |period|
109
+ aggregate_for_period :period => period, :source => :counters, :dest => :aggregated_counters, :aggregate => "sum"
110
+ end
111
+
112
+ @db.exec "DELETE FROM counters"
113
+ end
114
+
115
+ transaction do
116
+ keys.each do |period|
117
+ aggregate_for_period :period => period, :source => :gauges, :dest => :aggregated_gauges, :aggregate => "CAST(sum AS FLOAT) / count"
118
+ end
119
+
120
+ @db.exec "DELETE FROM gauges"
121
+ end
122
+ end
123
+
124
+ def aggregate_for_period(options)
125
+ expect! options => {
126
+ :period => PERIODS.map(&:first)
127
+ }
128
+ period, source, dest, aggregate = options.values_at :period, :source, :dest, :aggregate
129
+
130
+ _, duration, starts_at = PERIODS_BY_KEY[period]
131
+
132
+ # sql expression to calculate value from sum and count of event values
133
+ aggregate = source == :gauges ? "CAST(sum AS FLOAT) / count" : "sum"
134
+
135
+ @db.exec <<-SQL
136
+ CREATE TEMPORARY TABLE batch AS
137
+ SELECT name, starts_at, SUM(sum) AS sum, SUM(count) AS count FROM
138
+ (
139
+ SELECT name AS name,
140
+ #{starts_at} AS starts_at,
141
+ SUM(value) AS sum,
142
+ COUNT(value) AS count
143
+ FROM #{source}
144
+ GROUP BY name, starts_at
145
+
146
+ UNION
147
+
148
+ SELECT #{dest}.name AS name,
149
+ #{dest}.starts_at AS starts_at,
150
+ sum AS sum,
151
+ count AS count
152
+ FROM #{dest}
153
+ INNER JOIN #{source} ON #{dest}.name=#{source}.name
154
+ WHERE duration=#{duration}
155
+ AND #{dest}.starts_at >= (SELECT MIN(starts_at) FROM #{source})
156
+ )
157
+ GROUP BY name, starts_at;
158
+
159
+ INSERT OR REPLACE INTO #{dest}(name, starts_at, period, duration, sum, count, value)
160
+ SELECT name, starts_at, '#{period}', #{duration}, sum, count, #{aggregate}
161
+ FROM batch;
162
+ SQL
163
+
164
+ @db.exec <<-SQL
165
+ DROP TABLE batch;
166
+ SQL
167
+ end
168
+ end
@@ -0,0 +1,109 @@
1
+ $: << "#{File.dirname(__FILE__)}/../lib"
2
+ require "metric_system"
3
+ require "test/unit"
4
+
5
+ class MetricSystem::TestCounters < Test::Unit::TestCase
6
+ def db
7
+ @db ||= MetricSystem.new ":memory:"
8
+ end
9
+
10
+ def test_two_events
11
+ db.count "foo", 1, "2014-03-02 12:10:11"
12
+ db.count "foo", 1, "2014-03-02 14:10:11"
13
+ db.aggregate :minute, :hour, :day
14
+
15
+ r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name, starts_at")
16
+ r = r.map(&:to_a)
17
+
18
+ assert_equal(r, [
19
+ ["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
20
+ ["foo", 1, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
21
+ ["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
22
+ ["foo", 1, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
23
+ ["foo", 2, "day", Time.parse("2014-03-02 00:00:00 +0100")],
24
+ ])
25
+ end
26
+
27
+ def test_conversion
28
+ db.exec "CREATE TABLE tmp(value, starts_at, starts_on)"
29
+ now = Time.parse("2014-03-02 11:10:11 +0100")
30
+ day = Date.parse("2014-03-02")
31
+
32
+ db.exec "INSERT INTO tmp (value, starts_at, starts_on) VALUES(?, ?, ?)", "one", now, now
33
+ rows = db.select("SELECT value, starts_at, starts_on FROM tmp")
34
+ assert_equal rows.map(&:to_a), [
35
+ [ "one", now, day ]
36
+ ]
37
+ end
38
+
39
+ def test_single_aggregate
40
+ db.count "foo", 1, "2014-03-02 12:10:11"
41
+ db.aggregate
42
+
43
+ r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name")
44
+ assert_equal(r.map(&:to_a), [
45
+ ["foo", 1, "second", Time.parse("2014-03-02 11:10:11 +0100")],
46
+ ["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
47
+ ["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
48
+ ["foo", 1, "day", Time.parse("2014-03-02 00:00:00 +0100")],
49
+ ["foo", 1, "week", Time.parse("2014-02-24 00:00:00 +0100")],
50
+ ["foo", 1, "month", Time.parse("2014-03-01 00:00:00 +0100")],
51
+ ["foo", 1, "year", Time.parse("2014-01-01 00:00:00 +0100")]
52
+ ])
53
+ end
54
+
55
+ def test_store_combined_name
56
+ db.count "foo", 1, "2014-03-02 12:10:11"
57
+ db.count "foo.bar", 2, "2014-03-02 12:10:11"
58
+ r = db.select("SELECT name, value, starts_at FROM counters ORDER BY name")
59
+ assert_equal(r.map(&:to_a), [
60
+ ["foo" , 1, Time.parse("2014-03-02 12:10:11 +0100")],
61
+ ["foo" , 2, Time.parse("2014-03-02 12:10:11 +0100")],
62
+ ["foo.bar", 2, Time.parse("2014-03-02 12:10:11 +0100")]
63
+ ])
64
+ end
65
+
66
+ def test_combined_name
67
+ db.count "foo", 3, "2014-03-02 12:10:11"
68
+ db.count "foo.bar", 2, "2014-03-02 12:10:11"
69
+ db.aggregate :second, :minute, :hour
70
+
71
+ r = db.select <<-SQL
72
+ SELECT name, value, period, starts_at
73
+ FROM aggregates
74
+ ORDER BY duration, name
75
+ SQL
76
+
77
+ assert_equal(r.map(&:to_a), [
78
+ ["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
79
+ ["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
80
+ ["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
81
+ ["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
82
+ ["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
83
+ ["foo.bar", 2, "hour", Time.parse("2014-03-02 11:00:00 +0100")]
84
+ ])
85
+ end
86
+
87
+ def test_double_aggregate
88
+ db.count "foo", 3, "2014-03-02 12:10:11"
89
+ db.count "foo.bar", 2, "2014-03-02 12:10:11"
90
+ db.aggregate :second, :minute, :hour
91
+ db.aggregate :second, :minute, :hour
92
+
93
+ r = db.select <<-SQL
94
+ SELECT name, value, period, starts_at
95
+ FROM aggregates
96
+ ORDER BY duration, name
97
+ SQL
98
+
99
+ assert_equal(r.map(&:to_a), [
100
+ ["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
101
+ ["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
102
+ ["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
103
+ ["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
104
+ ["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
105
+ ["foo.bar", 2, "hour", Time.parse("2014-03-02 11:00:00 +0100")]
106
+ ])
107
+ end
108
+ end
109
+
@@ -0,0 +1,26 @@
1
+ $: << "#{File.dirname(__FILE__)}/../lib"
2
+ require "metric_system"
3
+ require "test/unit"
4
+
5
+ class MetricSystem::TestGauging < Test::Unit::TestCase
6
+ def db
7
+ @db ||= MetricSystem.new ":memory:"
8
+ end
9
+
10
+ def test_two_events
11
+ db.gauge "foo", 1, "2014-03-02 12:10:11"
12
+ db.gauge "foo", 2, "2014-03-02 14:10:11"
13
+ db.aggregate :minute, :hour, :day
14
+
15
+ r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name, starts_at")
16
+ r = r.map(&:to_a)
17
+
18
+ assert_equal(r, [
19
+ ["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
20
+ ["foo", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
21
+ ["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
22
+ ["foo", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
23
+ ["foo", 1.5, "day", Time.parse("2014-03-02 00:00:00 +0100")],
24
+ ])
25
+ end
26
+ end
@@ -0,0 +1,52 @@
1
+ $: << "#{File.dirname(__FILE__)}/../lib"
2
+ require "metric_system"
3
+ require "test/unit"
4
+
5
+ class MetricSystem::TestMixed < Test::Unit::TestCase
6
+ def db
7
+ @db ||= MetricSystem.new ":memory:"
8
+ end
9
+
10
+ def xtest_two_events
11
+ db.gauge "foo", 1, "2014-03-02 12:10:11"
12
+ db.gauge "foo", 2, "2014-03-02 14:10:11"
13
+ db.count "bar", 1, "2014-03-02 12:10:11"
14
+ db.count "bar", 2, "2014-03-02 14:10:11"
15
+ db.aggregate :minute, :hour, :day
16
+
17
+ r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY name, duration, starts_at")
18
+ r = r.map(&:to_a)
19
+
20
+ assert_equal(r, [
21
+ ["bar", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
22
+ ["bar", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
23
+ ["bar", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
24
+ ["bar", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
25
+ ["bar", 3, "day", Time.parse("2014-03-02 00:00:00 +0100")],
26
+ ["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
27
+ ["foo", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
28
+ ["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
29
+ ["foo", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
30
+ ["foo", 1.5, "day", Time.parse("2014-03-02 00:00:00 +0100")],
31
+ ])
32
+ end
33
+
34
+ def test_two_events_x
35
+ db.count "bar", 1, "2014-03-02 12:10:11"
36
+ db.count "bar", 2, "2014-03-02 14:10:11"
37
+ db.gauge "foo", 1, "2014-03-02 12:10:11"
38
+
39
+ db.aggregate :minute, :hour, :day
40
+
41
+ r = db.select("SELECT name, value, period, starts_at FROM aggregates WHERE name='bar' ORDER BY name, duration, starts_at")
42
+ r = r.map(&:to_a)
43
+
44
+ assert_equal(r, [
45
+ ["bar", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
46
+ ["bar", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
47
+ ["bar", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
48
+ ["bar", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
49
+ ["bar", 3, "day", Time.parse("2014-03-02 00:00:00 +0100")],
50
+ ])
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metric_system
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - radiospiel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: expectation
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A simple metrics aggregator
42
+ email: eno@radiospiel.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.md
48
+ - lib/metric_system.rb
49
+ - lib/metric_system/benchmark.rb
50
+ - lib/metric_system/core_extensions.rb
51
+ - lib/metric_system/sqlite3_extensions.rb
52
+ - lib/metric_system/version.rb
53
+ - test/counters_test.rb
54
+ - test/gauges_test.rb
55
+ - test/mixed_test.rb
56
+ homepage: http://github.com/radiospiel/metric_system
57
+ licenses: []
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.2.2
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: A simple metrics aggregator
79
+ test_files: []