maestro_metrics 0.0.7
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 +7 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/README.md +4 -0
- data/Rakefile +17 -0
- data/lib/maestro_metrics.rb +215 -0
- data/lib/maestro_metrics/version.rb +5 -0
- data/maestro_metrics.gemspec +24 -0
- data/spec/maestro_metrics_spec.rb +161 -0
- data/spec/spec_helper.rb +11 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f5ed4252a33b5cf79e7fcc8a1d52392702fc2c49
|
4
|
+
data.tar.gz: c74d4a556e30e7b139961b22540d0f4a2426999e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd35d8cb55675d2d91dadbfee1e1768dbeb0c128834f14f9f69237753611afa4880fca5df80545179dbdfdd88390baed065d91eba3197e37665eefc4196b6920
|
7
|
+
data.tar.gz: 85102a9e0f5f91ecedd4b7337ccce9487f8ccbeb79e92b070a40820f7fef96a5f8274c7253535fb4074639504d6ab9ba5e56053b3775e8cd0860e8880ac7b5db
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.4
|
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rake/clean'
|
4
|
+
|
5
|
+
#require File.expand_path('../lib/maestro_plugin/version', __FILE__)
|
6
|
+
|
7
|
+
CLEAN.include('pkg')
|
8
|
+
CLOBBER.include('.bundle', '.config', 'coverage', 'InstalledFiles', 'spec/reports', 'rdoc', 'test', 'tmp')
|
9
|
+
|
10
|
+
task :default => [:clean, :bundle, :spec, :build]
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new
|
13
|
+
|
14
|
+
desc 'Get dependencies with Bundler'
|
15
|
+
task :bundle do
|
16
|
+
sh %{bundle install}
|
17
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'maestro_metrics/version'
|
2
|
+
require 'singleton'
|
3
|
+
require 'mongo'
|
4
|
+
require 'statsd'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Maestro
|
8
|
+
module Metrics
|
9
|
+
|
10
|
+
@mocking = false
|
11
|
+
@config = {}
|
12
|
+
|
13
|
+
def Metrics.mock!
|
14
|
+
@mocking = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def Metrics.unmock!
|
18
|
+
@mocking = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def Metrics.mock?
|
22
|
+
@mocking
|
23
|
+
end
|
24
|
+
|
25
|
+
def Metrics.mocking?
|
26
|
+
@mocking
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure(config={})
|
30
|
+
@config = config unless config.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def Metrics.count(metric, value, sample_rate=1)
|
34
|
+
logger.count(metric, value, sample_rate)
|
35
|
+
end
|
36
|
+
|
37
|
+
def Metrics.timing(metric, value, sample_rate=1)
|
38
|
+
logger.timing(metric, value, sample_rate)
|
39
|
+
end
|
40
|
+
|
41
|
+
def Metrics.increment(metric, sample_rate=1)
|
42
|
+
logger.increment(metric, sample_rate)
|
43
|
+
end
|
44
|
+
|
45
|
+
def Metrics.decrement(metric, sample_rate=1)
|
46
|
+
logger.decrement(metric, sample_rate)
|
47
|
+
end
|
48
|
+
|
49
|
+
def Metrics.time(metric, sample_rate=1, &block)
|
50
|
+
logger.time(metric, sample_rate, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def Metrics.log(collection, metrics)
|
54
|
+
logger.log(collection, metrics)
|
55
|
+
end
|
56
|
+
|
57
|
+
def Metrics.aggregate(collection, pipeline=nil)
|
58
|
+
logger.aggregate(collection, pipeline)
|
59
|
+
end
|
60
|
+
|
61
|
+
def Metrics.find(collection, selector={}, opts={})
|
62
|
+
logger.find(collection, selector, opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def Metrics.to_mongo(value)
|
66
|
+
if value.nil? || value == ''
|
67
|
+
nil
|
68
|
+
else
|
69
|
+
time = nil
|
70
|
+
|
71
|
+
if value.is_a?(::Date)
|
72
|
+
time = value.to_time
|
73
|
+
elsif value.is_a?(::Time)
|
74
|
+
time = value
|
75
|
+
else
|
76
|
+
time = ::Time.parse(value.to_s)
|
77
|
+
end
|
78
|
+
|
79
|
+
time.utc
|
80
|
+
end
|
81
|
+
rescue
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def Metrics.collection_names
|
86
|
+
logger.collection_names
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def self.config
|
92
|
+
@config
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def self.logger
|
98
|
+
self.mocking? ? Mock.instance : Real.instance
|
99
|
+
end
|
100
|
+
|
101
|
+
class Real
|
102
|
+
|
103
|
+
include Mongo
|
104
|
+
include Singleton
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
@mongo_host = Metrics.config[:mongo_host] || 'localhost'
|
108
|
+
@mongo_port = Metrics.config[:mongo_port] || 27017
|
109
|
+
@statsd_host = Metrics.config[:statsd_host] || 'localhost'
|
110
|
+
@statsd_port = Metrics.config[:statsd_port] || 8125
|
111
|
+
end
|
112
|
+
|
113
|
+
def count(metric, value, sample_rate=1)
|
114
|
+
statsd.count(metric, value, sample_rate)
|
115
|
+
end
|
116
|
+
|
117
|
+
def timing(metric, value, sample_rate=1)
|
118
|
+
statsd.timing(metric, value, sample_rate)
|
119
|
+
end
|
120
|
+
|
121
|
+
def increment(metric, sample_rate=1)
|
122
|
+
statsd.increment(metric, sample_rate)
|
123
|
+
end
|
124
|
+
|
125
|
+
def decrement(metric, sample_rate=1)
|
126
|
+
statsd.decrement(metric, sample_rate)
|
127
|
+
end
|
128
|
+
|
129
|
+
def time(metric, sample_rate=1, &block)
|
130
|
+
statsd.time(metric, sample_rate, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
def log(collection, metrics)
|
134
|
+
mongo_collection(collection).save(metrics)
|
135
|
+
end
|
136
|
+
|
137
|
+
def aggregate(collection, pipeline=nil)
|
138
|
+
mongo_collection(collection).aggregate(pipeline)
|
139
|
+
end
|
140
|
+
|
141
|
+
def find(collection, selector={}, opts={})
|
142
|
+
mongo_collection(collection).find(selector, opts)
|
143
|
+
end
|
144
|
+
|
145
|
+
def collection_names
|
146
|
+
mongo_db.collection_names
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def mongo_client
|
152
|
+
@mongo_client ||= MongoClient.new(@mongo_host, @mongo_port)
|
153
|
+
end
|
154
|
+
|
155
|
+
def mongo_db
|
156
|
+
@mongo_db ||= mongo_client['maestro']
|
157
|
+
end
|
158
|
+
|
159
|
+
def mongo_collection(name)
|
160
|
+
mongo_db[name]
|
161
|
+
end
|
162
|
+
|
163
|
+
def statsd
|
164
|
+
@statsd ||= Statsd.new(@statsd_host, @statsd_port)
|
165
|
+
@statsd.namespace= 'maestro'
|
166
|
+
@statsd
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
class Mock
|
174
|
+
|
175
|
+
include Singleton
|
176
|
+
|
177
|
+
def count(metric, value, sample_rate=1)
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
def timing(metric, value, sample_rate=1)
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
def increment(metric, sample_rate=1)
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
def decrement(metric, sample_rate=1)
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
def time(metric, sample_rate=1, &block)
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
def log(collection, metrics)
|
198
|
+
-1
|
199
|
+
end
|
200
|
+
|
201
|
+
def aggregate(collection, pipeline=nil)
|
202
|
+
Array.new
|
203
|
+
end
|
204
|
+
|
205
|
+
def find(collection, selector={}, opts={})
|
206
|
+
Array.new
|
207
|
+
end
|
208
|
+
|
209
|
+
def collection_names
|
210
|
+
Array.new
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/maestro_metrics/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Etienne Pelletier"]
|
6
|
+
gem.email = ["epelletier@maestrodev.com"]
|
7
|
+
gem.description = "A gem used to log application metrics"
|
8
|
+
gem.summary = "Use this gem to record application run time metrics."
|
9
|
+
gem.homepage = "https://github.com/kellyp/maestro-metrics"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "maestro_metrics"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Maestro::Metrics::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency('mongo')
|
19
|
+
gem.add_dependency('statsd')
|
20
|
+
gem.add_dependency('statsd-ruby')
|
21
|
+
gem.add_development_dependency('rake')
|
22
|
+
gem.add_development_dependency('rspec')
|
23
|
+
gem.add_development_dependency('mongo_mapper')
|
24
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'maestro_metrics'
|
3
|
+
|
4
|
+
include Maestro
|
5
|
+
|
6
|
+
describe Maestro::Metrics do
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
Metrics.configure( { :mongo_host => '192.168.56.10', :statsd_host => '192.168.56.10' } )
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
Metrics.unmock!
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'Stats logging' do
|
17
|
+
|
18
|
+
@metric = 'test'
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
|
22
|
+
@statsd = double('statsd')
|
23
|
+
Metrics::Real.instance.stub(:statsd => @statsd)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should log counts of things' do
|
27
|
+
value = 8
|
28
|
+
@statsd.should_receive(:count).with(@metric, value, 1)
|
29
|
+
Metrics.count(@metric, value)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should increment a counter' do
|
34
|
+
@statsd.should_receive(:increment).with(@metric, 1)
|
35
|
+
Metrics.increment(@metric)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should decrement a counter' do
|
39
|
+
@statsd.should_receive(:decrement).with(@metric, 1)
|
40
|
+
Metrics.decrement(@metric)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it 'should log timing stats' do
|
45
|
+
@statsd.should_receive(:timing).with(@metric, 1, 1)
|
46
|
+
Metrics.timing(@metric, 1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should time the execution of a block of code' do
|
50
|
+
ran = false
|
51
|
+
|
52
|
+
proc = Proc.new do
|
53
|
+
ran = true
|
54
|
+
end
|
55
|
+
|
56
|
+
@statsd.should_receive(:time).with(@metric, 1, &proc)
|
57
|
+
Metrics.time(@metric,1, &proc)
|
58
|
+
ran.should be_true
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should do nothing when using a mock' do
|
63
|
+
Metrics.mock!
|
64
|
+
value = 8
|
65
|
+
@statsd.should_not_receive(:count)
|
66
|
+
Metrics.count(@metric, value)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'raw metrics logging' do
|
72
|
+
|
73
|
+
it 'should have a list of collections' do
|
74
|
+
|
75
|
+
|
76
|
+
collection_names = ["system.indexes", "runs", "counters.login.admin_10"]
|
77
|
+
Metrics::Real.instance.stub(:collection_names => collection_names)
|
78
|
+
|
79
|
+
names = Metrics.collection_names
|
80
|
+
names.should_not be_nil
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should log a hash containing raw metrics' do
|
85
|
+
|
86
|
+
@collection = double('collection')
|
87
|
+
Metrics::Real.instance.stub(:mongo_collection => @collection)
|
88
|
+
|
89
|
+
doc = { :name => 'testdoc' }
|
90
|
+
@collection.stub(:save => 1)
|
91
|
+
@collection.should_receive(:save).with(doc)
|
92
|
+
|
93
|
+
Metrics.log("test", doc).should eq 1
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should log a complex document' do
|
98
|
+
Metrics.mock!
|
99
|
+
|
100
|
+
require 'mongo_mapper'
|
101
|
+
|
102
|
+
doc = {
|
103
|
+
:id => 1,
|
104
|
+
:composition_id => 1,
|
105
|
+
:start_time => Time.to_mongo(Time.new),
|
106
|
+
:user=> 'maestro',
|
107
|
+
:trigger_type => 'manual',
|
108
|
+
:run_time => 1234,
|
109
|
+
:wait_time => 0,
|
110
|
+
:success => true,
|
111
|
+
:agent_name => 'some-agent',
|
112
|
+
:agent_host => 'agenthost.example.com',
|
113
|
+
:tasks => [
|
114
|
+
{
|
115
|
+
:task_id => 1,
|
116
|
+
:start_time => Time.to_mongo(Time.new),
|
117
|
+
:run_time => 1234,
|
118
|
+
:wait_time => 0,
|
119
|
+
:success => true
|
120
|
+
}
|
121
|
+
]
|
122
|
+
}
|
123
|
+
|
124
|
+
Metrics.log('runs', doc)
|
125
|
+
results = Metrics.aggregate('runs',
|
126
|
+
[ { '$group' => { '_id' => '$composition_id',
|
127
|
+
'numRuns' => { '$sum' => 1 },
|
128
|
+
'numSuccessfulRuns' => { '$sum' => { '$cond' => [ '$success', 1, 0 ] } },
|
129
|
+
'avgRunTime' => { '$avg' => '$run_time' },
|
130
|
+
'maxRunTime' => { '$max' => '$run_time' },
|
131
|
+
'minRunTime' => { '$min' => '$run_time' }
|
132
|
+
}
|
133
|
+
} ] )
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should correctly convert date to mongo' do
|
138
|
+
d = Date.new(2001, 2, 3)
|
139
|
+
u = d.to_time.utc
|
140
|
+
m = Maestro::Metrics.to_mongo(d)
|
141
|
+
|
142
|
+
m.should == u
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should correctly convert time to mongo' do
|
146
|
+
t = Time.parse("2001-02-03 04:05:06 -0800")
|
147
|
+
u = t.utc
|
148
|
+
m = Maestro::Metrics.to_mongo(t)
|
149
|
+
|
150
|
+
m.should == u
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should correctly convert string datetime to mongo' do
|
154
|
+
s = "2001-02-03 04:05:06"
|
155
|
+
u = Time.parse(s).utc
|
156
|
+
m = Maestro::Metrics.to_mongo(s)
|
157
|
+
|
158
|
+
m.should == u
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maestro_metrics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Etienne Pelletier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mongo
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
prerelease: false
|
26
|
+
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: statsd
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
prerelease: false
|
40
|
+
type: :runtime
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: statsd-ruby
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
prerelease: false
|
54
|
+
type: :runtime
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
prerelease: false
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
prerelease: false
|
82
|
+
type: :development
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mongo_mapper
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
prerelease: false
|
96
|
+
type: :development
|
97
|
+
description: A gem used to log application metrics
|
98
|
+
email:
|
99
|
+
- epelletier@maestrodev.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- .ruby-version
|
106
|
+
- Gemfile
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/maestro_metrics.rb
|
110
|
+
- lib/maestro_metrics/version.rb
|
111
|
+
- maestro_metrics.gemspec
|
112
|
+
- spec/maestro_metrics_spec.rb
|
113
|
+
- spec/spec_helper.rb
|
114
|
+
homepage: https://github.com/kellyp/maestro-metrics
|
115
|
+
licenses: []
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.0.3
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: Use this gem to record application run time metrics.
|
137
|
+
test_files:
|
138
|
+
- spec/maestro_metrics_spec.rb
|
139
|
+
- spec/spec_helper.rb
|