speed_gun 0.0.4 → 1.0.0.rc1

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/Rakefile +6 -1
  6. data/lib/speed_gun/app/views/meter.html.slim +4 -9
  7. data/lib/speed_gun/config.rb +33 -42
  8. data/lib/speed_gun/event.rb +72 -0
  9. data/lib/speed_gun/middleware.rb +43 -43
  10. data/lib/speed_gun/profile.rb +102 -0
  11. data/lib/speed_gun/profiler/action_controller_profiler.rb +14 -0
  12. data/lib/speed_gun/profiler/action_view_profiler.rb +11 -0
  13. data/lib/speed_gun/profiler/active_record_profiler.rb +11 -0
  14. data/lib/speed_gun/profiler/active_support_notifications_profiler.rb +29 -0
  15. data/lib/speed_gun/profiler/rack_profiler.rb +7 -0
  16. data/lib/speed_gun/profiler.rb +11 -118
  17. data/lib/speed_gun/railtie.rb +7 -16
  18. data/lib/speed_gun/store/elastic_search_store.rb +64 -0
  19. data/lib/speed_gun/store/fluent_logger_store.rb +29 -0
  20. data/lib/speed_gun/store/memcache_store.rb +40 -0
  21. data/lib/speed_gun/store/memory_store.rb +23 -0
  22. data/lib/speed_gun/store/multiple_store.rb +23 -0
  23. data/lib/speed_gun/store/redis_store.rb +41 -0
  24. data/lib/speed_gun/store.rb +7 -3
  25. data/lib/speed_gun/template.rb +1 -1
  26. data/lib/speed_gun/version.rb +1 -1
  27. data/lib/speed_gun.rb +30 -39
  28. data/spec/lib/speed_gun/config_spec.rb +37 -0
  29. data/spec/lib/speed_gun/event_spec.rb +70 -0
  30. data/spec/lib/speed_gun/middleware_spec.rb +65 -0
  31. data/spec/lib/speed_gun/profile_spec.rb +41 -0
  32. data/spec/lib/speed_gun_spec.rb +52 -0
  33. data/spec/spec_helper.rb +9 -0
  34. data/spec/support/simplecov.rb +12 -0
  35. data/speed_gun.gemspec +10 -7
  36. metadata +102 -56
  37. data/app/assets/javascripts/browser.js +0 -83
  38. data/app/assets/javascripts/profiler.js +0 -45
  39. data/app/views/speed_gun/_meter.html.slim +0 -9
  40. data/lib/speed_gun/app/public/browser.js +0 -83
  41. data/lib/speed_gun/app/public/jquery-1.10.2.min.js +0 -6
  42. data/lib/speed_gun/app/public/profile.js +0 -5
  43. data/lib/speed_gun/app/public/profiler.js +0 -45
  44. data/lib/speed_gun/app/public/style.css +0 -170
  45. data/lib/speed_gun/app/views/profile.slim +0 -97
  46. data/lib/speed_gun/app.rb +0 -58
  47. data/lib/speed_gun/browser/navigation.rb +0 -23
  48. data/lib/speed_gun/browser/timing.rb +0 -92
  49. data/lib/speed_gun/browser.rb +0 -22
  50. data/lib/speed_gun/hook.rb +0 -25
  51. data/lib/speed_gun/profiler/action_controller.rb +0 -12
  52. data/lib/speed_gun/profiler/action_view.rb +0 -12
  53. data/lib/speed_gun/profiler/active_record.rb +0 -16
  54. data/lib/speed_gun/profiler/base.rb +0 -139
  55. data/lib/speed_gun/profiler/js.rb +0 -17
  56. data/lib/speed_gun/profiler/manual.rb +0 -14
  57. data/lib/speed_gun/profiler/rack.rb +0 -7
  58. data/lib/speed_gun/store/base.rb +0 -9
  59. data/lib/speed_gun/store/file.rb +0 -62
  60. data/lib/speed_gun/store/memcache.rb +0 -27
  61. data/lib/speed_gun/store/memory.rb +0 -22
  62. data/lib/speed_gun/store/redis.rb +0 -28
@@ -1,33 +1,24 @@
1
1
  require 'speed_gun'
2
- require 'speed_gun/store/file'
3
2
 
4
3
  class SpeedGun::Railtie < ::Rails::Railtie
5
4
  initializer 'speed_gun' do |app|
6
5
  app.middleware.insert(0, SpeedGun::Middleware)
7
6
 
8
- SpeedGun.config[:enable_if] = -> { Rails.env.development? }
9
- SpeedGun.config[:backtrace_remove] = Rails.root.to_s + '/'
10
- SpeedGun.config[:backtrace_includes] = [/^(app|config|lib|test|spec)/]
11
- SpeedGun.config[:authorize_proc] = ->(request) { Rails.env.development? }
12
- SpeedGun.config.skip_paths << /^#{Regexp.escape(app.config.assets.prefix)}/
13
- SpeedGun.config[:store] =
14
- SpeedGun::Store::File.new(path: Rails.root.join('tmp/speed_gun'))
7
+ SpeedGun.config.logger = Rails.logger
8
+ SpeedGun.config.skip_paths.push(
9
+ /^#{Regexp.escape(app.config.assets.prefix)}/
10
+ )
15
11
 
16
12
  ActiveSupport.on_load(:action_controller) do
17
- require 'speed_gun/profiler/action_controller'
13
+ require 'speed_gun/profiler/action_controller_profiler'
18
14
  end
19
15
 
20
16
  ActiveSupport.on_load(:action_view) do
21
- require 'speed_gun/profiler/action_view'
17
+ require 'speed_gun/profiler/action_view_profiler'
22
18
  end
23
19
 
24
20
  ActiveSupport.on_load(:active_record) do
25
- require 'speed_gun/profiler/active_record'
26
-
27
- SpeedGun::Profiler::ActiveRecord.hook_method(
28
- ActiveRecord::Base.connection.class,
29
- :execute
30
- )
21
+ require 'speed_gun/profiler/active_record_profiler'
31
22
  end
32
23
  end
33
24
  end
@@ -0,0 +1,64 @@
1
+ require 'speed_gun/store'
2
+
3
+ class SpeedGun::Store::ElasticSearchStore < SpeedGun::Store
4
+ DEFAULT_INDEX = 'speed_gun'
5
+
6
+ def initialize(options = {})
7
+ @index = options[:index] || DEFAULT_INDEX
8
+ @async = options.fetch(:async, true)
9
+ @client = options[:client] || default_clinet(options)
10
+ end
11
+
12
+ def save(object)
13
+ @async ? save_with_async(object) : save_without_async(object)
14
+ end
15
+
16
+ def load(klass, id)
17
+ hit = @client.search(
18
+ index: @index,
19
+ body: {
20
+ query: {
21
+ match: {
22
+ "_id" => id,
23
+ "_type" => underscore(klass.name)
24
+ }
25
+ }
26
+ }
27
+ )['hits']['hits'].first['_source']
28
+
29
+ klass.from_hash(id, hit)
30
+ end
31
+
32
+ private
33
+
34
+ def save_with_async(object)
35
+ Thread.new(object) { |object| save_without_async(object) }
36
+ end
37
+
38
+ def save_without_async(object)
39
+ @client.index(
40
+ index: @index,
41
+ type: underscore(object.class.name),
42
+ id: object.id,
43
+ body: object.to_hash.merge(
44
+ '@timestamp' => Time.now
45
+ )
46
+ )
47
+ end
48
+
49
+ def index(klass)
50
+ [@prefix, underscore(klass.name)].join('-')
51
+ end
52
+
53
+ def underscore(name)
54
+ name = name
55
+ name.sub!(/^[A-Z]/) { |c| c.downcase }
56
+ name.gsub!(/[A-Z]/) { |c| "_#{c.downcase}" }
57
+ name.gsub!('::', '')
58
+ end
59
+
60
+ def default_clinet(options)
61
+ require 'elasticsearch' unless defined?(Elasticsearch)
62
+ Elasticsearch::Client.new(options)
63
+ end
64
+ end
@@ -0,0 +1,29 @@
1
+ require 'speed_gun/store'
2
+
3
+ class SpeedGun::Store::FluentLoggerStore < SpeedGun::Store
4
+ DEFAULT_PREFIX = 'speed_gun'
5
+
6
+ def initialize(options = {})
7
+ @prefix = options[:prefix] || DEFAULT_PREFIX
8
+ @logger = options[:logger] || default_logger(options)
9
+ end
10
+
11
+ def save(object)
12
+ @logger.post(tag(object), object.to_hash.merge(id: object.id))
13
+ end
14
+
15
+ def load(klass, id)
16
+ nil
17
+ end
18
+
19
+ private
20
+
21
+ def tag(object)
22
+ object.class.name.sub(/.*::/, '').downcase
23
+ end
24
+
25
+ def default_logger(options)
26
+ require 'fluent-logger' unless defined?(Fluent::Logger)
27
+ Fluent::Logger::FluentLogger.new(@prefix, options)
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ require 'speed_gun/store'
2
+
3
+ class SpeedGun::Store::MemcacheStroe < SpeedGun::Store
4
+ DEFAULT_PREFIX = 'speed-gun'
5
+ DEFAULT_EXPIRES_IN_SECONDS = 60 * 60 * 24
6
+
7
+ def initialize(options = {})
8
+ @prefix = options[:prefix] || DEFAULT_PREFIX
9
+ @client = options[:client] || default_client(options)
10
+ @expires = (options[:expires] || DEFAULT_EXPIRES_IN_SECONDS).to_i
11
+ end
12
+
13
+ def save(object)
14
+ @client.set(
15
+ key(object.class, object.id),
16
+ object.to_hash.to_msgpack,
17
+ @expires
18
+ )
19
+ end
20
+
21
+ def load(klass, id)
22
+ klass.from_hash(id, MessagePack.unpack(@client.get(key(klass, id))))
23
+ end
24
+
25
+ private
26
+
27
+ def key(klass, id)
28
+ klass_name = klass.name
29
+ klass_name.gsub!(/([a-z])([A-Z])/) { |c| "#{$1.to_s}_#{$2.to_s.downcase}" }
30
+ klass_name.gsub!(/[A-Z]/) { |c| "#{c.downcase}" }
31
+ klass_name.gsub!('::', '-')
32
+
33
+ [@prefix, klass_name, id].join('-')
34
+ end
35
+
36
+ def default_client(options)
37
+ require 'dalli' unless defined?(Dalli)
38
+ Dalli.new(options)
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require 'speed_gun/store'
2
+
3
+ class SpeedGun::Store::MemoryStore < SpeedGun::Store
4
+ DEFAULT_MAX_ENTRIES = 1000
5
+
6
+ def initialize(options = {})
7
+ @max_entries = options[:max_entries] || DEFAULT_MAX_ENTRIES
8
+ @store = {}
9
+ @stored_list = []
10
+ end
11
+
12
+ def save(object)
13
+ id = "#{object.class.name}-#{object.id}"
14
+ @store[id] = object.to_hash
15
+ @stored_list.push(id)
16
+
17
+ @store.delete(@stored_list.shift) while @stored_list.length > @max_entries
18
+ end
19
+
20
+ def load(klass, id)
21
+ klass.from_hash(id, @store["#{klass.name}-#{id}"])
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ require 'speed_gun/store'
2
+
3
+ class SpeedGun::Store::MultipleStore < SpeedGun::Store
4
+ def initialize(stores = [])
5
+ @stores = stores
6
+ end
7
+
8
+ def save(object)
9
+ @stores.each do |store|
10
+ store.save(object)
11
+ end
12
+ end
13
+
14
+ def load(klass, id)
15
+ @stores.each do |store|
16
+ ret = store.load(klass, id)
17
+
18
+ return ret if ret
19
+ end
20
+
21
+ nil
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ require 'msgpack'
2
+ require 'speed_gun/store'
3
+
4
+ class SpeedGun::Store::RedisStore < SpeedGun::Store
5
+ DEFAULT_PREFIX = 'speed-gun'
6
+ DEFAULT_EXPIRES_IN_SECONDS = 60 * 60 * 24
7
+
8
+ def initialize(options = {})
9
+ @prefix = options[:prefix] || DEFAULT_PREFIX
10
+ @client = options[:client] || default_client(options)
11
+ @expires = (options[:expires] || DEFAULT_EXPIRES_IN_SECONDS).to_i
12
+ end
13
+
14
+ def save(object)
15
+ @client.setex(
16
+ key(object.class, object.id),
17
+ @expires,
18
+ object.to_hash.to_msgpack
19
+ )
20
+ end
21
+
22
+ def load(klass, id)
23
+ klass.from_hash(id, MessagePack.unpack(@client.get(key(klass, id))))
24
+ end
25
+
26
+ private
27
+
28
+ def key(klass, id)
29
+ klass_name = klass.name
30
+ klass_name.gsub!(/([a-z])([A-Z])/) { |c| "#{$1.to_s}_#{$2.to_s.downcase}" }
31
+ klass_name.gsub!(/[A-Z]/) { |c| "#{c.downcase}" }
32
+ klass_name.gsub!('::', '-')
33
+
34
+ [@prefix, klass_name, id].join('-')
35
+ end
36
+
37
+ def default_client(options)
38
+ require 'redis' unless defined? Redis
39
+ Redis.new(options)
40
+ end
41
+ end
@@ -1,6 +1,10 @@
1
1
  require 'speed_gun'
2
2
 
3
- module SpeedGun::Store
4
- end
3
+ # @abstract
4
+ class SpeedGun::Store
5
+ def save(object)
6
+ end
5
7
 
6
- require 'speed_gun/store/memory'
8
+ def load(klass, id)
9
+ end
10
+ end
@@ -6,7 +6,7 @@ class SpeedGun::Template < Slim::Template
6
6
  File.join(File.dirname(__FILE__), 'app/views/meter.html.slim')
7
7
 
8
8
  def self.render
9
- new.render(SpeedGun.current)
9
+ new.render(SpeedGun.current_profile)
10
10
  end
11
11
 
12
12
  def initialize
@@ -1,3 +1,3 @@
1
1
  module SpeedGun
2
- VERSION = '0.0.4'
2
+ VERSION = '1.0.0.rc1'.freeze
3
3
  end
data/lib/speed_gun.rb CHANGED
@@ -1,52 +1,43 @@
1
+ require 'forwardable'
1
2
  require 'thread'
3
+ require 'speed_gun/version'
4
+ require 'speed_gun/config'
5
+ require 'speed_gun/profile'
6
+ require 'speed_gun/middleware'
2
7
 
3
8
  module SpeedGun
4
9
  class << self
5
- attr_writer :config
6
- end
7
-
8
- def self.current
9
- Thread.current[:speed_gun_current]
10
- end
11
-
12
- def self.current=(profiler)
13
- Thread.current[:speed_gun_current] = profiler
14
- end
15
-
16
- def self.config
17
- @config ||= SpeedGun::Config.new
18
- end
19
-
20
- def self.active?
21
- current && current.active?
22
- end
23
-
24
- def self.activate!
25
- current && current.activate!
26
- end
10
+ # @return [SpeedGun::Config] the config of speed gun
11
+ def config
12
+ @config ||= Config.new
13
+ end
27
14
 
28
- def self.deactivate!
29
- current && current.deactivate!
30
- end
15
+ # @return [SpeedGun::Profile, nil] the profile of a current thread
16
+ def current_profile
17
+ Thread.current[:speed_gun_current_profile]
18
+ end
31
19
 
32
- def self.enable?
33
- config.enable?
34
- end
20
+ # Set the profile of a current thread
21
+ #
22
+ # @param profile [SpeedGun::Profile] the profile
23
+ # @return [SpeedGun::Profile] the profile of a current thread
24
+ def current_profile=(profile)
25
+ Thread.current[:speed_gun_current_profile] = profile
26
+ end
35
27
 
36
- def self.store
37
- config.store
38
- end
28
+ # Discard the profile of a current thread
29
+ #
30
+ # @return [nil]
31
+ def discard_profile!
32
+ self.current_profile = nil
33
+ end
39
34
 
40
- def self.profile(title, *args, &block)
41
- if title.kind_of?(String)
42
- current && current.profile(:manual, title, &block)
43
- else
44
- current && current.profile(title, *args, &block)
35
+ # @see SpeedGun::Config#enabled?
36
+ # @return [Boolean] true if enabled speed gun
37
+ def enabled?
38
+ config.enabled?
45
39
  end
46
40
  end
47
41
  end
48
42
 
49
- require 'speed_gun/version'
50
- require 'speed_gun/config'
51
- require 'speed_gun/middleware'
52
43
  require 'speed_gun/railtie' if defined?(Rails)
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpeedGun::Config do
4
+ subject(:config) { described_class.new }
5
+
6
+ describe '#enable!' do
7
+ before do
8
+ config.disable!
9
+ end
10
+
11
+ it 'enables the config' do
12
+ expect(config).to_not be_enabled
13
+ config.enable!
14
+ expect(config).to be_enabled
15
+ end
16
+ end
17
+
18
+ describe '#disable!' do
19
+ it 'disables the config' do
20
+ expect(config).to_not be_disabled
21
+ config.disable!
22
+ expect(config).to be_disabled
23
+ end
24
+ end
25
+
26
+ describe '#enabled?' do
27
+ it 'defaults to true' do
28
+ expect(config.enabled?).to be_true
29
+ end
30
+ end
31
+
32
+ describe '#disabled?' do
33
+ it 'defaults to false' do
34
+ expect(config.disabled?).to be_false
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpeedGun::Event do
4
+ let(:event_name) { 'spec.test' }
5
+
6
+ subject(:event) { described_class.new(event_name) }
7
+
8
+ describe '#id' do
9
+ subject { event.id }
10
+
11
+ it { should be_kind_of(String) }
12
+ end
13
+
14
+ describe '#name' do
15
+ subject { event.name }
16
+
17
+ it { should be_kind_of(String) }
18
+ it { should eq(event_name) }
19
+ end
20
+
21
+ describe '#started_at' do
22
+ subject { event.started_at }
23
+
24
+ it { should be_kind_of(Time) }
25
+ end
26
+
27
+ describe '#finished_at' do
28
+ subject { event.finished_at }
29
+
30
+ context 'when finished event' do
31
+ before { event.finish! }
32
+
33
+ it { should be_kind_of(Time) }
34
+ end
35
+
36
+ context 'when continues event' do
37
+ it { should be_nil }
38
+ end
39
+ end
40
+
41
+ describe '#finish!' do
42
+ it 'finishes the event' do
43
+ expect(event).to_not be_finished
44
+ event.finish!
45
+ expect(event).to be_finished
46
+ end
47
+ end
48
+
49
+ describe '#duration' do
50
+ subject(:duration) { event.duration }
51
+
52
+ context 'when continues event' do
53
+ it { should eq(-1) }
54
+ end
55
+
56
+ context 'when finished event' do
57
+ before { event.finish! }
58
+
59
+ it { should be_kind_of(Float) }
60
+ end
61
+ end
62
+
63
+ describe '#to_hash' do
64
+ it 'valid serialize' do
65
+ expect(
66
+ SpeedGun::Event.from_hash(event.id, event.to_hash).to_hash
67
+ ).to eq(event.to_hash)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+
4
+ describe SpeedGun::Middleware do
5
+ include Rack::Test::Methods
6
+
7
+ before(:all) do
8
+ SpeedGun.config.skip_paths << '/skip'
9
+ end
10
+
11
+ let(:app) do
12
+ builder = Rack::Builder.new do
13
+ use SpeedGun::Middleware
14
+
15
+ map '/skip' do
16
+ process = lambda do |env|
17
+ [
18
+ 200,
19
+ { 'Content-Type' => 'text/html' },
20
+ "<html><BODY><h1>Skip</h1></BODY>\n \t</html>"
21
+ ]
22
+ end
23
+
24
+ run process
25
+ end
26
+
27
+ map '/html' do
28
+ process = lambda do |env|
29
+ [
30
+ 200,
31
+ { 'Content-Type' => 'text/html' },
32
+ "<html><BODY><h1>Hi</h1></BODY>\n \t</html>"
33
+ ]
34
+ end
35
+
36
+ run process
37
+ end
38
+ end
39
+ builder.to_app
40
+ end
41
+
42
+ describe 'GET /skip' do
43
+ subject(:response) { get '/skip' }
44
+
45
+ it { should be_ok }
46
+
47
+ describe '#headers' do
48
+ subject { response.headers }
49
+
50
+ it { should_not be_has_key('X-SpeedGun-Profile-Id') }
51
+ end
52
+ end
53
+
54
+ describe 'GET /html' do
55
+ subject(:response) { get '/html' }
56
+
57
+ it { should be_ok }
58
+
59
+ describe '#headers' do
60
+ subject { response.headers }
61
+
62
+ it { should be_has_key('X-SpeedGun-Profile-Id') }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpeedGun::Profile do
4
+ subject(:profile) { described_class.new }
5
+
6
+ describe '#id' do
7
+ subject { profile.id }
8
+
9
+ it { should be_kind_of(String) }
10
+ end
11
+
12
+ describe '#events' do
13
+ subject { profile.events }
14
+
15
+ it { should be_kind_of(Array) }
16
+ end
17
+
18
+ describe '#record!' do
19
+ let(:logger) { double(debug: nil) }
20
+ let(:event) { SpeedGun::Event.new('spec.test') }
21
+
22
+ before { profile.config.logger = logger }
23
+
24
+ it 'records event' do
25
+ expect(profile.record!(event)).to eq(profile.events)
26
+ expect(profile.events).to include(event)
27
+ end
28
+ end
29
+
30
+ describe '#to_hash' do
31
+ let(:event) { SpeedGun::Event.new('spec.test') }
32
+
33
+ before { profile.record!(event) }
34
+
35
+ it 'valid serialize' do
36
+ expect(
37
+ SpeedGun::Profile.from_hash(profile.id, profile.to_hash).to_hash
38
+ ).to eq(profile.to_hash)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpeedGun do
4
+ subject { described_class }
5
+
6
+ describe '#config' do
7
+ subject { described_class.config }
8
+
9
+ it { should be_kind_of(SpeedGun::Config) }
10
+ end
11
+
12
+ describe '#current_profile' do
13
+ let(:profile) { double }
14
+ subject(:current_profile) { described_class.current_profile }
15
+
16
+ it 'defaults to be nil' do
17
+ expect(current_profile).to be_nil
18
+ end
19
+
20
+ it 'thread localy' do
21
+ described_class.current_profile = profile
22
+ expect(current_profile).to eq(profile)
23
+
24
+ thread = Thread.new { expect(described_class.current_profile).to be_nil }
25
+ thread.join
26
+ end
27
+ end
28
+
29
+ describe '#discard_profile!' do
30
+ let(:profile) { double }
31
+
32
+ it 'discards current profile' do
33
+ described_class.current_profile = profile
34
+ described_class.discard_profile!
35
+ expect(described_class.current_profile).to be_nil
36
+ end
37
+ end
38
+
39
+ describe '#enabled?' do
40
+ context 'when enabled' do
41
+ before { described_class.config.stub(enabled?: true) }
42
+
43
+ it { should be_enabled }
44
+ end
45
+
46
+ context 'when disabled' do
47
+ before { described_class.config.stub(enabled?: false) }
48
+
49
+ it { should_not be_enabled }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,9 @@
1
+ require 'support/simplecov'
2
+
3
+ require 'speed_gun'
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+ end
@@ -0,0 +1,12 @@
1
+ require 'simplecov'
2
+ require 'simplecov-console'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ SimpleCov::Formatter::Console,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+ SimpleCov.start do
11
+ add_filter('spec')
12
+ end