zombie_scout 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3017624103d8c386a942a3653769d1e62b93175e
4
- data.tar.gz: 4051fd1c51e9626ff04846945c7d9d22a58b4788
3
+ metadata.gz: 24fef05906d9243f5b7ccc8f5d527162f5a38c5f
4
+ data.tar.gz: a56de0fbd3d1e9cb5369a2b24bcbd76b5c728931
5
5
  SHA512:
6
- metadata.gz: 0bee3360ff18f1c4363d605612a0b48f574b8390607ee975f51a84cab76f9544bf6334429d6133f770ef97ea179ee5349760edbb3d55e0a6adb66f2260283354
7
- data.tar.gz: 49dec6d09b9d3f62538a93072bd6a6cf3619a2c1366b778154a2a0b4a20f68fab4557854a966c6cb895bbfb632e2b6cb177de168fe14caaf52caa4abff8e70c1
6
+ metadata.gz: e5111aa9eff93beb078907b74c24242c053227b71a5b50b8f4a3cc21835519bf58f845111907b8e0d76de39a2419bac3a077a81d76fc2f4bb1f4c6f2d61850ac
7
+ data.tar.gz: 2c19bbd09b45ae27af15d44f2bfd05795365d063cf42dbfb9732c5fc70c0101c1baf97caa2118584e7bb82229904ff4fcf0f77bb9fe08b111a0d0ce18d97a43e
data/.gitignore CHANGED
@@ -2,6 +2,8 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .zombie_scout
6
+ .zombie_scout_whitelist
5
7
  coverage
6
8
  InstalledFiles
7
9
  lib/bundler/man
data/README.md CHANGED
@@ -153,6 +153,7 @@ thing:
153
153
  ## Code Status
154
154
 
155
155
  * [![Build Status](https://travis-ci.org/danbernier/zombie_scout.png?branch=master)](https://travis-ci.org/danbernier/zombie_scout)
156
+ * [![Code Climate](https://codeclimate.com/github/danbernier/zombie_scout.png)](https://codeclimate.com/github/danbernier/zombie_scout)
156
157
 
157
158
  ## TODOs
158
159
 
@@ -161,6 +162,7 @@ thing:
161
162
  * [ ] let users configure: files to search for methods, files to search for calls...probably in `.zombie_scout`.
162
163
  * [x] option for CSV output
163
164
  * [ ] if 2 classes have a method w/ the same name, you can't tell (right now, easily) whether it's dead - so don't grep for it.
165
+ * [ ] look for db/schema.rb & db/structure.sql, parse out columns, & look for those.
164
166
 
165
167
  ToThinkAbouts:
166
168
  * [x] extract a hash-y report structure that can be used by whatever, from the default report
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require 'zombie_scout/mission'
3
+ require 'zombie_scout/formatter'
3
4
 
4
5
  module ZombieScout
5
6
  class App < Thor
@@ -9,25 +10,7 @@ module ZombieScout
9
10
  mission = Mission.new(globs)
10
11
  report = mission.scout.sort_by { |z| [z[:file_path], -z[:flog_score]] }
11
12
 
12
- if options[:format] == 'report'
13
- total_flog_score = report.map { |z| z[:flog_score] }.reduce(0, :+)
14
-
15
- puts "Scouted #{mission.defined_method_count} methods in #{mission.source_count} files, in #{mission.duration} seconds."
16
- puts "Found #{mission.zombie_count} potential zombies, with a combined flog score of #{total_flog_score.round(1)}."
17
- puts
18
-
19
- report.each do |zombie|
20
- puts [zombie[:location], zombie[:full_name], zombie[:flog_score]] * "\t"
21
- end
22
- elsif options[:format] == 'csv'
23
- require 'csv'
24
- CSV do |csv|
25
- csv << %w(location name flog_score)
26
- report.each do |zombie|
27
- csv << [zombie[:location], zombie[:full_name], zombie[:flog_score]]
28
- end
29
- end
30
- end
13
+ puts ZombieScout::Formatter.format(options[:format], mission, report)
31
14
  end
32
15
  end
33
16
  end
@@ -2,8 +2,9 @@ require 'flog'
2
2
 
3
3
  module ZombieScout
4
4
  class FlogScorer
5
- def initialize(zombie_location)
6
- @zombie_location = zombie_location
5
+ def initialize(zombie, flog=nil)
6
+ @zombie = zombie
7
+ @flog = flog || Flog.new(methode: true, quiet: true, score: false)
7
8
  end
8
9
 
9
10
  def score
@@ -12,16 +13,19 @@ module ZombieScout
12
13
 
13
14
  private
14
15
 
16
+ attr_reader :flog
17
+
15
18
  def raw_score
16
- flog = Flog.new(methods: true, quiet: true, score:false)
17
19
  all_scores = flog.flog(zombie_path)
18
- method_locations = flog.instance_variable_get(:@method_locations)
19
- scores = all_scores.fetch(method_locations.invert.fetch(@zombie_location, {}), {}) # default to {} in case there is no score. (it's a 0)
20
+
21
+ # default to {} in case there is no score. (it's a 0)
22
+ scores = all_scores.fetch(@zombie.full_name, {})
23
+
20
24
  flog.score_method(scores)
21
25
  end
22
26
 
23
27
  def zombie_path
24
- @zombie_location.sub(/\:\d+$/, '')
28
+ @zombie.location.sub(/\:\d+$/, '')
25
29
  end
26
30
  end
27
31
  end
@@ -0,0 +1,46 @@
1
+ module ZombieScout
2
+ module Formatter
3
+ def self.format(format, mission, report)
4
+ case format
5
+ when 'report'
6
+ ReportFormatter.new(mission, report).to_s
7
+ when 'csv'
8
+ CsvFormatter.new(mission, report).to_s
9
+ end
10
+ end
11
+
12
+ class BaseFormatter
13
+ attr_reader :mission, :report
14
+ def initialize(mission, report)
15
+ @mission, @report = mission, report
16
+ end
17
+ end
18
+
19
+ class ReportFormatter < BaseFormatter
20
+ def to_s
21
+ one = "Scouted #{mission.defined_method_count} methods in #{mission.source_count} files, in #{mission.duration} seconds."
22
+ two = "Found #{mission.zombie_count} potential zombies, with a combined flog score of #{total_flog_score.round(1)}."
23
+
24
+ ([one, two, "\n"] + report.map { |zombie|
25
+ [zombie[:location], zombie[:full_name], zombie[:flog_score]] * "\t"
26
+ }) * "\n"
27
+ end
28
+
29
+ def total_flog_score
30
+ report.map { |z| z[:flog_score] }.reduce(0, :+)
31
+ end
32
+ end
33
+
34
+ class CsvFormatter < BaseFormatter
35
+ def to_s
36
+ require 'csv'
37
+ CSV.generate do |csv|
38
+ csv << %w(location name flog_score)
39
+ report.each do |zombie|
40
+ csv << [zombie[:location], zombie[:full_name], zombie[:flog_score]]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -7,7 +7,10 @@ module ZombieScout
7
7
 
8
8
  def count_calls(method_name)
9
9
  method_name = method_name.to_s
10
- method_name.sub!(/=$/, ' *=')
10
+
11
+ # zero-or-more spaces, =, and NOT a > (so we don't match hashrockets)
12
+ method_name.sub!(/=$/, ' *=[^>]')
13
+
11
14
  find_occurrances(method_name).size
12
15
  end
13
16
 
@@ -15,13 +18,17 @@ module ZombieScout
15
18
 
16
19
  def find_occurrances(method_name)
17
20
  # TODO somehow expose some of this config for end-users
18
- command = "grep -rnw --include=\"*.rb\" --include=\"*.erb\" --binary-files=without-match \"#{method_name}\" #{files_to_search} "
19
- # grep -r --include="*.rb" --include="*.erb" -nw PATTERN app lib
20
-
21
+ command = "grep -rnw #{includes} --binary-files=without-match \"#{method_name}\" #{files_to_search} Rakefile"
21
22
  grep_lines = `#{command}`
22
23
  grep_lines.split("\n")
23
24
  end
24
25
 
26
+ def includes
27
+ %w(*.rb *.erb *.rake Rakefile).map { |inc|
28
+ "--include=\"#{inc}\""
29
+ }.join(' ')
30
+ end
31
+
25
32
  def files_to_search
26
33
  @ruby_project.folders.join(' ')
27
34
  end
@@ -1,4 +1,5 @@
1
1
  require 'zombie_scout/ruby_project'
2
+ require 'zombie_scout/whitelist'
2
3
  require 'zombie_scout/parser'
3
4
  require 'zombie_scout/method_call_finder'
4
5
  require 'zombie_scout/flog_scorer'
@@ -9,6 +10,7 @@ module ZombieScout
9
10
 
10
11
  def initialize(globs)
11
12
  @ruby_project = RubyProject.new(*globs)
13
+ @whitelist = Whitelist.new
12
14
  end
13
15
 
14
16
  def scout
@@ -18,7 +20,7 @@ module ZombieScout
18
20
  file_path: zombie.file_path,
19
21
  name: zombie.name,
20
22
  full_name: zombie.full_name,
21
- flog_score: flog_score(zombie.location)
23
+ flog_score: flog_score(zombie)
22
24
  }
23
25
  }.tap {
24
26
  @end_time = Time.now
@@ -43,15 +45,15 @@ module ZombieScout
43
45
  @sources ||= @ruby_project.ruby_sources
44
46
  end
45
47
 
46
- def flog_score(zombie_location)
47
- ZombieScout::FlogScorer.new(zombie_location).score
48
+ def flog_score(zombie)
49
+ ZombieScout::FlogScorer.new(zombie).score
48
50
  end
49
51
 
50
52
  def zombies
51
53
  return @zombies unless @zombies.nil?
52
54
 
53
55
  scout!
54
- @zombies ||= @defined_methods.select { |method|
56
+ @zombies = @defined_methods.select { |method|
55
57
  might_be_dead?(method)
56
58
  }
57
59
  end
@@ -74,8 +76,14 @@ module ZombieScout
74
76
  end
75
77
 
76
78
  def might_be_dead?(method)
77
- @method_call_counter ||= MethodCallFinder.new(@ruby_project)
78
- @method_call_counter.count_calls(method.name) < 2
79
+ unless whitelisted?(method)
80
+ @method_call_counter ||= MethodCallFinder.new(@ruby_project)
81
+ @method_call_counter.count_calls(method.name) < 2
82
+ end
83
+ end
84
+
85
+ def whitelisted?(method)
86
+ @whitelist.include?(method.full_name)
79
87
  end
80
88
  end
81
89
  end
@@ -1,3 +1,3 @@
1
1
  module ZombieScout
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,15 @@
1
+ module ZombieScout
2
+ class Whitelist
3
+ def initialize
4
+ @whitelist = if File.exist?('.zombie_scout_whitelist')
5
+ File.read('.zombie_scout_whitelist')
6
+ else
7
+ []
8
+ end
9
+ end
10
+
11
+ def include?(zombie_full_name)
12
+ @whitelist.include?(zombie_full_name)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'zombie_scout/flog_scorer'
3
+
4
+ describe ZombieScout::FlogScorer do
5
+ let(:full_name) { 'ClassName#method_name' }
6
+ let(:method_score) { :whatever }
7
+ let(:class_scores) {
8
+ {full_name => method_score}
9
+ }
10
+
11
+ let(:zombie_path) { 'lib/foo.rb' }
12
+
13
+ let(:flog) {
14
+ flog = double(:flog)
15
+ flog.stub(:flog).with(zombie_path).and_return(class_scores)
16
+ flog.stub(:score_method).with(method_score).and_return(38.293)
17
+ flog
18
+ }
19
+ let(:zombie) {
20
+ double(:zombie, location: "#{zombie_path}:42", full_name: full_name)
21
+ }
22
+
23
+ it 'should call some methods on Flog, and round the result' do
24
+ scorer = ZombieScout::FlogScorer.new(zombie, flog)
25
+ expect(scorer.score).to eq(38.3)
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'zombie_scout/formatter'
3
+
4
+ describe ZombieScout::Formatter do
5
+ let(:mission) {
6
+ double(:mission, defined_method_count: 123,
7
+ source_count: 97,
8
+ zombie_count: 42,
9
+ duration: 55)
10
+ }
11
+
12
+ let(:report) {
13
+ [
14
+ { flog_score: 23.4,
15
+ location: 'foo.rb:42',
16
+ full_name: 'Foo#bar' },
17
+ { flog_score: 43.2,
18
+ location: 'bar.rb:99',
19
+ full_name: 'Bar#none' }
20
+ ]
21
+ }
22
+
23
+ context 'for "report" format' do
24
+ it 'formats it correctly' do
25
+ output = ZombieScout::Formatter.format('report', mission, report)
26
+
27
+ expect(output).to include "Scouted 123 methods in 97 files"
28
+ expect(output).to include "in 55 seconds"
29
+ expect(output).to include "Found 42 potential zombies"
30
+ expect(output).to include "combined flog score of 66.6"
31
+ expect(output).to include "foo.rb:42\tFoo#bar\t23.4"
32
+ expect(output).to include "bar.rb:99\tBar#none\t43.2"
33
+ end
34
+ end
35
+
36
+ context 'for "csv" format' do
37
+ it 'formats it correctly' do
38
+ output = ZombieScout::Formatter.format('csv', mission, report)
39
+
40
+ lines = output.split("\n").map(&:chomp)
41
+ expect(lines).to eq([
42
+ 'location,name,flog_score',
43
+ 'foo.rb:42,Foo#bar,23.4',
44
+ 'bar.rb:99,Bar#none,43.2'
45
+ ])
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'zombie_scout/whitelist'
3
+
4
+ describe ZombieScout::Whitelist do
5
+ include FakeFS::SpecHelpers
6
+
7
+ def clear_whitelist!
8
+ if File.exist?('.zombie_scout_whitelist')
9
+ File.delete('.zombie_scout_whitelist')
10
+ end
11
+ end
12
+
13
+ def whitelist!(*names)
14
+ File.open('.zombie_scout_whitelist', 'w') do |f|
15
+ f.puts names.join("\n")
16
+ end
17
+ end
18
+
19
+ let(:whitelist) { ZombieScout::Whitelist.new }
20
+
21
+ describe '#include?' do
22
+ it 'only includes methods listed in .zombie_scout_whitelist' do
23
+ whitelist! 'Foo#bar', 'Iron#man'
24
+ expect(whitelist).to include('Foo#bar')
25
+ expect(whitelist).to include('Iron#man')
26
+ expect(whitelist).not_to include('Cran#berry')
27
+ expect(whitelist).not_to include('Boot#laces')
28
+ end
29
+
30
+ it 'includes nothing when .zombie_scout_whitelist does not exist' do
31
+ clear_whitelist!
32
+ expect(whitelist).not_to include('Foo#bar')
33
+ expect(whitelist).not_to include('Iron#man')
34
+ expect(whitelist).not_to include('Cran#berry')
35
+ expect(whitelist).not_to include('Boot#laces')
36
+ end
37
+ end
38
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zombie_scout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Bernier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-11 00:00:00.000000000 Z
11
+ date: 2014-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -91,6 +91,7 @@ files:
91
91
  - lib/zombie_scout.rb
92
92
  - lib/zombie_scout/app.rb
93
93
  - lib/zombie_scout/flog_scorer.rb
94
+ - lib/zombie_scout/formatter.rb
94
95
  - lib/zombie_scout/method.rb
95
96
  - lib/zombie_scout/method_call_finder.rb
96
97
  - lib/zombie_scout/mission.rb
@@ -98,10 +99,14 @@ files:
98
99
  - lib/zombie_scout/ruby_project.rb
99
100
  - lib/zombie_scout/ruby_source.rb
100
101
  - lib/zombie_scout/version.rb
102
+ - lib/zombie_scout/whitelist.rb
101
103
  - spec/spec_helper.rb
104
+ - spec/zombie_scout/flog_scorer_spec.rb
105
+ - spec/zombie_scout/formatter_spec.rb
102
106
  - spec/zombie_scout/parser_spec.rb
103
107
  - spec/zombie_scout/ruby_project_spec.rb
104
108
  - spec/zombie_scout/ruby_source_spec.rb
109
+ - spec/zombie_scout/whitelist_spec.rb
105
110
  - zombie_scout.gemspec
106
111
  homepage: https://github.com/danbernier/zombie_scout
107
112
  licenses:
@@ -129,6 +134,9 @@ specification_version: 4
129
134
  summary: Find dead methods in your Ruby app
130
135
  test_files:
131
136
  - spec/spec_helper.rb
137
+ - spec/zombie_scout/flog_scorer_spec.rb
138
+ - spec/zombie_scout/formatter_spec.rb
132
139
  - spec/zombie_scout/parser_spec.rb
133
140
  - spec/zombie_scout/ruby_project_spec.rb
134
141
  - spec/zombie_scout/ruby_source_spec.rb
142
+ - spec/zombie_scout/whitelist_spec.rb