zombie_scout 0.0.4 → 0.0.5

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 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