metric_system 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []