zombie_scout 0.0.2 → 0.0.3

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: cc91be8df398812d74758f0f52b4fb5314debcbe
4
- data.tar.gz: 908249e99886cd2546e40a59d6a68cbe379ed2df
3
+ metadata.gz: c1d48e23ce3d846d2a1bb64d2c5a698386a7ccbb
4
+ data.tar.gz: dec98dc4fcf229576c71c080c00e4a24e1afa7d1
5
5
  SHA512:
6
- metadata.gz: a2854512c5126c80f7c33dd118ec0577adfab4adeba60c823eaa47602933eee4fc81c3c91a6b9483f927a262de8cae58524e5a7d4138475c5f718e0552310b97
7
- data.tar.gz: ad6e1a064157c087b9b081c0aa889af10f85f337d47eb9beab4c52a141f9143265f0a406338cae3c6c50a0cbf9c5055750625f991e00a9fd256465eae54d51d1
6
+ metadata.gz: 989aedb17a61b6aa6dc899a3b9426117dc771fece220a5fb1505d5b21b36e40dde5a06af2e17a424e438fffb212b765e4500aa227f3bec11c9350081ab6e1d91
7
+ data.tar.gz: 973b0f339bbfef34d128924df0d66902be6c929ac96c617516f230c28c7b2ebce8761026376a11cb6b5efcf104a22a914574bbf62e24fb5c4c4c03d9ebf810e5
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  gem 'parser', '~> 2.1'
4
+ gem 'flog', '~> 4.2'
4
5
  gem 'thor', '~> 0.18'
5
6
 
6
7
  group :test do
data/Gemfile.lock CHANGED
@@ -4,6 +4,9 @@ GEM
4
4
  ast (1.1.0)
5
5
  diff-lcs (1.1.3)
6
6
  fakefs (0.5.0)
7
+ flog (4.2.0)
8
+ ruby_parser (~> 3.1, > 3.1.0)
9
+ sexp_processor (~> 4.4)
7
10
  parser (2.1.4)
8
11
  ast (~> 1.1)
9
12
  slop (~> 3.4, >= 3.4.5)
@@ -16,6 +19,9 @@ GEM
16
19
  rspec-expectations (2.11.3)
17
20
  diff-lcs (~> 1.1.3)
18
21
  rspec-mocks (2.11.3)
22
+ ruby_parser (3.4.1)
23
+ sexp_processor (~> 4.1)
24
+ sexp_processor (4.4.1)
19
25
  slop (3.4.7)
20
26
  thor (0.18.1)
21
27
 
@@ -24,6 +30,7 @@ PLATFORMS
24
30
 
25
31
  DEPENDENCIES
26
32
  fakefs
33
+ flog (~> 4.2)
27
34
  parser (~> 2.1)
28
35
  rake
29
36
  rspec
data/README.md CHANGED
@@ -14,7 +14,26 @@ parses your code to find method declarations, and then greps through your
14
14
  project's source, looking for each method. If Zombie Scout can't find any
15
15
  calls to a method, it presumes the method is dead, and reports back to you.
16
16
 
17
- ### Fair Warning
17
+ ### How Does It Work?
18
+
19
+ #### Phase 1: Parse
20
+
21
+ Zombie Scout parses your ruby files, and remembers all the methods it sees
22
+ defined.
23
+
24
+ #### Phase 2: Grep
25
+
26
+ Then it greps your whole project - currently *.rb and *.erb files - for
27
+ whole-word occurrances of any of the methods it found. The whole-word grep
28
+ (`grep -w`) means that searching for `dead_method` won't count `dead_methods`
29
+ as a match, and accidentally think it's live.
30
+
31
+ So, if you have 1,000 methods, you have to run 1,000 greps? That's slow, right?
32
+ Good news: back in Phase 1, when Zombie Scout parsed your code, it also
33
+ remembered all the method CALLS it saw, and it automatically counts those
34
+ methods as not-zombies, so it saves a bunch of time by not grepping for them.
35
+
36
+ #### Fair Warning
18
37
 
19
38
  Zombie Scout isn't exhaustive or thorough - it's a scout, not a spy. (That
20
39
  could be another project, though - a Zombie Spy.)
@@ -57,23 +76,69 @@ Or, add this to your Gemfile:
57
76
 
58
77
  ## Usage
59
78
 
79
+ ### From the Command Line
80
+
60
81
  You can run it on a whole folder:
61
82
 
62
83
  dan@aleph:~/projects/zombie_scout$ zombie_scout scout
63
- Scouting out /home/dan/projects/zombie_scout!
64
- lib/zombie_scout/method_finder.rb:23 on_def
65
- lib/zombie_scout/method_finder.rb:29 on_defs
66
- lib/zombie_scout/method_finder.rb:35 on_send
67
- lib/zombie_scout/method_finder.rb:77 on_sym
68
- Scouted 23 methods in 8 files. Found 4 potential zombies.
84
+ Scouted 43 methods in 9 files, in 1.096459054 seconds.
85
+ Found 11 potential zombies, with a combined flog score of 51.5.
86
+
87
+ lib/zombie_scout/parser.rb:29 on_send 8.3
88
+ lib/zombie_scout/parser.rb:23 on_defs 5.9
89
+ lib/zombie_scout/parser.rb:65 handle_def_delegators 5.8
90
+ lib/zombie_scout/parser.rb:56 handle_attr_accessor 5.6
91
+ lib/zombie_scout/parser.rb:17 on_def 4.9
92
+ lib/zombie_scout/parser.rb:48 handle_attr_writer 4.3
93
+ lib/zombie_scout/parser.rb:40 handle_attr_reader 4.3
94
+ lib/zombie_scout/parser.rb:73 handle_def_delegator 3.8
95
+ lib/zombie_scout/parser.rb:79 handle_scope 3.8
96
+ lib/zombie_scout/parser.rb:100 on_sym 2.4
97
+ lib/zombie_scout/mission.rb:34 zombie_count 2.4
69
98
 
70
99
  (See what I meant about callbacks and false-positives?)
71
100
 
72
101
  Or you can run it on a given file or glob:
73
102
 
74
- dan@aleph:~/projects/zombie_scout$ zombie_scout scout lib/zombie_scout.rb
75
- Scouting out /home/dan/projects/zombie_scout!
76
- Scouted 0 methods in 1 files. Found 0 potential zombies.
103
+ dan@aleph:~/projects/zombie_scout$ zombie_scout scout lib/app.rb
104
+ Scouted 1 methods in 1 files, in 0.041942649 seconds.
105
+ Found 0 potential zombies, with a combined flog score of 0.0.
106
+
107
+ ZombieScout will also report in CSV, if you like:
108
+
109
+ dan@aleph:~/projects/zombie_scout$ zombie_scout scout --format csv
110
+ location,name,flog_score
111
+ lib/zombie_scout/parser.rb:29,on_send,8.3
112
+ lib/zombie_scout/parser.rb:23,on_defs,5.9
113
+ lib/zombie_scout/parser.rb:65,handle_def_delegators,5.8
114
+ lib/zombie_scout/parser.rb:56,handle_attr_accessor,5.6
115
+ lib/zombie_scout/parser.rb:17,on_def,4.9
116
+ lib/zombie_scout/parser.rb:48,handle_attr_writer,4.3
117
+ lib/zombie_scout/parser.rb:40,handle_attr_reader,4.3
118
+ lib/zombie_scout/parser.rb:73,handle_def_delegator,3.8
119
+ lib/zombie_scout/parser.rb:79,handle_scope,3.8
120
+ lib/zombie_scout/parser.rb:100,on_sym,2.4
121
+ lib/zombie_scout/mission.rb:34,zombie_count,2.4
122
+
123
+ ### In Ruby
124
+
125
+ You can also embed ZombieScout in your own code, if you need that kind of
126
+ thing:
127
+
128
+ irb> require 'zombie_scout'
129
+ => true
130
+ irb> require 'pp'
131
+ => true
132
+ irb> > pp ZombieScout::Mission.new('.').scout
133
+ [{:location=>"./lib/zombie_scout/parser.rb:17",
134
+ :name=>:on_def,
135
+ :flog_score=>4.9},
136
+ {:location=>"./lib/zombie_scout/parser.rb:23",
137
+ :name=>:on_defs,
138
+ :flog_score=>5.9},
139
+ {:location=>"./lib/zombie_scout/parser.rb:29",
140
+ :name=>:on_send,
141
+ :flog_score=>8.3}, ...]
77
142
 
78
143
  ## Code Status
79
144
 
@@ -84,10 +149,11 @@ Or you can run it on a given file or glob:
84
149
  * [x] parse for attr_reader/writer/accessors, & forwardables, & rails scopes
85
150
  * [ ] parse for rails delegators
86
151
  * [ ] let users configure: files to search for methods, files to search for calls...probably in `.zombie_scout`.
152
+ * [x] option for CSV output
87
153
 
88
154
  ToThinkAbouts:
89
- * [ ] extract a hash-y report structure that can be used by whatever, from the default report
90
- * [ ] pass a method name, or a ruby class
155
+ * [x] extract a hash-y report structure that can be used by whatever, from the default report
156
+ * [x] pass a ruby file, or a glob
91
157
  * [ ] rspec/mini-test drop-in tests that can be added easily, to fail your
92
158
  build if the scout or spy finds dead code. (This is probably a bad idea.)
93
159
 
data/Rakefile CHANGED
@@ -4,3 +4,7 @@ require 'rspec/core/rake_task'
4
4
  RSpec::Core::RakeTask.new
5
5
 
6
6
  task :default => :spec
7
+
8
+ task :console do
9
+ sh('irb -I lib -r zombie_scout')
10
+ end
data/bin/zombie_scout CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'zombie_scout'
4
4
 
@@ -4,8 +4,30 @@ require 'zombie_scout/mission'
4
4
  module ZombieScout
5
5
  class App < Thor
6
6
  desc "scout", "scout for zombie code in current directory"
7
+ option :format, default: 'report'
7
8
  def scout(*globs)
8
- Mission.new(globs).scout
9
+ mission = Mission.new(globs)
10
+ report = mission.scout.sort_by { |z| -z[:flog_score] }
11
+
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 #{report.size} 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[: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[:name], zombie[:flog_score]]
28
+ end
29
+ end
30
+ end
9
31
  end
10
32
  end
11
33
  end
@@ -0,0 +1,27 @@
1
+ require 'flog'
2
+
3
+ module ZombieScout
4
+ class FlogScorer
5
+ def initialize(zombie_location)
6
+ @zombie_location = zombie_location
7
+ end
8
+
9
+ def score
10
+ raw_score.round(1)
11
+ end
12
+
13
+ private
14
+
15
+ def raw_score
16
+ flog = Flog.new(methods: true, quiet: true, score:false)
17
+ 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
+ flog.score_method(scores)
21
+ end
22
+
23
+ def zombie_path
24
+ @zombie_location.sub(/\:\d+$/, '')
25
+ end
26
+ end
27
+ end
@@ -1,38 +1,74 @@
1
1
  require 'zombie_scout/ruby_project'
2
- require 'zombie_scout/method_finder'
2
+ require 'zombie_scout/parser'
3
3
  require 'zombie_scout/method_call_finder'
4
+ require 'zombie_scout/flog_scorer'
4
5
 
5
6
  module ZombieScout
6
7
  class Mission
8
+ attr_reader :defined_method_count
9
+
7
10
  def initialize(globs)
8
- puts "Scouting out #{Dir.pwd}!"
9
11
  @ruby_project = RubyProject.new(*globs)
10
12
  end
11
13
 
12
14
  def scout
13
- zombies.each do |zombie|
14
- puts [zombie.location, zombie.name] * "\t"
15
- end
15
+ @start_time = Time.now
16
+ zombies.map { |zombie|
17
+ { location: zombie.location,
18
+ name: zombie.name,
19
+ flog_score: flog_score(zombie.location)
20
+ }
21
+ }.tap {
22
+ @end_time = Time.now
23
+ }
24
+ end
25
+
26
+ def duration
27
+ @end_time - @start_time
28
+ end
16
29
 
17
- puts "Scouted #{methods.size} methods in #{sources.size} files. Found #{zombies.size} potential zombies."
30
+ def source_count
31
+ sources.size
32
+ end
33
+
34
+ def zombie_count
35
+ zombies.size
18
36
  end
19
37
 
20
38
  private
21
39
 
40
+ def sources
41
+ @sources ||= @ruby_project.ruby_sources
42
+ end
43
+
44
+ def flog_score(zombie_location)
45
+ ZombieScout::FlogScorer.new(zombie_location).score
46
+ end
47
+
22
48
  def zombies
23
- @zombies ||= methods.select { |method|
49
+ return @zombies unless @zombies.nil?
50
+
51
+ scout!
52
+ @zombies ||= @defined_methods.select { |method|
24
53
  might_be_dead?(method)
25
54
  }
26
55
  end
27
56
 
28
- def methods
29
- @methods ||= sources.map { |ruby_source|
30
- MethodFinder.new(ruby_source).find_methods
31
- }.flatten
32
- end
57
+ def scout!
58
+ @defined_methods, @called_methods = [], []
33
59
 
34
- def sources
35
- @sources ||= @ruby_project.ruby_sources
60
+ sources.each do |ruby_source|
61
+ parser = ZombieScout::Parser.new(ruby_source)
62
+ @defined_methods.concat(parser.defined_methods)
63
+ @called_methods.concat(parser.called_methods)
64
+ end
65
+
66
+ @defined_method_count = @defined_methods.size
67
+
68
+ @called_methods.uniq!
69
+ @defined_methods.reject! do |method|
70
+ @called_methods.include?(method.name)
71
+ end
36
72
  end
37
73
 
38
74
  def might_be_dead?(method)
@@ -3,21 +3,15 @@ require 'parser/current'
3
3
  module ZombieScout
4
4
  Method = Class.new(Struct.new(:name, :location))
5
5
 
6
- class MethodFinder < Parser::AST::Processor
6
+ class Parser < Parser::AST::Processor
7
+ attr_reader :defined_methods, :called_methods
8
+
7
9
  def initialize(ruby_source)
8
10
  @ruby_source = ruby_source
9
- end
10
-
11
- def find_methods
12
- @methods = []
13
- @private_method_calls = []
14
-
15
- node = Parser::CurrentRuby.parse(@ruby_source.source)
11
+ @defined_methods = []
12
+ @called_methods = []
13
+ node = ::Parser::CurrentRuby.parse(@ruby_source.source)
16
14
  process(node)
17
-
18
- @methods.reject { |method|
19
- @private_method_calls.include?(method.name)
20
- }
21
15
  end
22
16
 
23
17
  def on_def(node)
@@ -36,10 +30,9 @@ module ZombieScout
36
30
  receiver, method_name, *args = *node
37
31
  if respond_to?(:"handle_#{method_name}", true)
38
32
  send(:"handle_#{method_name}", args, node)
39
- elsif receiver.nil? # Then it's a private method call
40
- @private_method_calls << method_name
41
- process_all(args)
42
33
  end
34
+ @called_methods << method_name
35
+ process_all(args)
43
36
  end
44
37
 
45
38
  private
@@ -99,11 +92,11 @@ module ZombieScout
99
92
  def stash_method(method_name, node)
100
93
  line_number = node.location.line
101
94
  location = [@ruby_source.path, line_number].join(":")
102
- @methods << Method.new(method_name, location)
95
+ @defined_methods << Method.new(method_name, location)
103
96
  end
104
97
  end
105
98
 
106
- class SymbolExtracter < Parser::AST::Processor
99
+ class SymbolExtracter < ::Parser::AST::Processor
107
100
  def on_sym(node)
108
101
  node.to_a[0]
109
102
  end
@@ -1,3 +1,3 @@
1
1
  module ZombieScout
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+ require 'zombie_scout/parser'
3
+
4
+ describe ZombieScout::Parser do
5
+ let(:ruby_source) {
6
+ double(:ruby_source, path: 'lib/fizzbuzz.rb', source: ruby_code)
7
+ }
8
+
9
+ describe '#called_methods' do
10
+ let(:called_methods) {
11
+ ZombieScout::Parser.new(ruby_source).called_methods
12
+ }
13
+ context 'when a ruby file has code that calls methods' do
14
+ let(:ruby_code) {
15
+ "class RockBand
16
+ def rock_out
17
+ turn_up_amps
18
+ play_tunes
19
+ end
20
+ end"
21
+ }
22
+ it 'can find the called methods' do
23
+ expect(called_methods).to match_array %i(play_tunes turn_up_amps)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#defined_methods' do
29
+ let(:defined_methods) {
30
+ ZombieScout::Parser.new(ruby_source).defined_methods.sort_by(&:name)
31
+ }
32
+ context 'when a ruby file has instance or class methods' do
33
+ let(:ruby_code) {
34
+ "class FizzBuzz
35
+ def fizz
36
+ 'plop plop'
37
+ end
38
+
39
+ def self.buzz
40
+ 'bzz bzz bzz'
41
+ end
42
+ end"
43
+ }
44
+
45
+ it 'can find the methods' do
46
+ expect(defined_methods[0].name).to eq :buzz
47
+ expect(defined_methods[0].location).to eq 'lib/fizzbuzz.rb:6'
48
+
49
+ expect(defined_methods[1].name).to eq :fizz
50
+ expect(defined_methods[1].location).to eq 'lib/fizzbuzz.rb:2'
51
+ end
52
+ end
53
+
54
+ context 'when a ruby file has attr_readers' do
55
+ context "when they're declared with symbols, the normal way" do
56
+ let(:ruby_code) {
57
+ "class Book
58
+ attr_reader :title, :author
59
+ end"
60
+ }
61
+ it 'can find attr_readers' do
62
+ expect(defined_methods.map(&:name)).to match_array(%i[author title])
63
+ end
64
+ end
65
+ context "when they're declare with a splat from an array of symbols" do
66
+ let(:ruby_code) {
67
+ "class Book
68
+ attributes = %i(title author)
69
+ attr_reader *attributes
70
+ end"
71
+ }
72
+ it 'will ignore them' do
73
+ expect(defined_methods).to be_empty
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'when a ruby file has attr_writers' do
79
+ context "when they're declared with symbols, the normal way" do
80
+ let(:ruby_code) {
81
+ "class Book
82
+ attr_writer :title, :author
83
+ end"
84
+ }
85
+ it 'can find attr_writers' do
86
+ expect(defined_methods.map(&:name)).to match_array(%i[author= title=])
87
+ end
88
+ end
89
+ context "when they're declare with a splat from an array of symbols" do
90
+ let(:ruby_code) {
91
+ "class Book
92
+ attributes = %i(title author)
93
+ attr_reader *attributes
94
+ end"
95
+ }
96
+ it 'will ignore them' do
97
+ expect(defined_methods).to be_empty
98
+ end
99
+ end
100
+ end
101
+
102
+ context 'when a ruby file has attr_accessors' do
103
+ context "when they're declared with symbols, the normal way" do
104
+ let(:ruby_code) {
105
+ "class Book
106
+ attr_accessor :title, :author
107
+ end"
108
+ }
109
+ it 'can find attr_accessors' do
110
+ expect(defined_methods.map(&:name)).to match_array(%i[author author= title title=])
111
+ end
112
+ end
113
+ context "when they're declare with a splat from an array of symbols" do
114
+ let(:ruby_code) {
115
+ "class Book
116
+ attributes = %i(title author)
117
+ attr_accessor *attributes
118
+ end"
119
+ }
120
+ it 'will ignore them' do
121
+ expect(defined_methods).to be_empty
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'when a ruby file uses Forwardable::def_delegator' do
127
+ let(:ruby_code) {
128
+ "class RecordCollection
129
+ extend Forwardable
130
+ def_delegator :@records, :[], :record_number
131
+ end"
132
+ }
133
+ it 'can find methods created by def_delegator' do
134
+ expect(defined_methods.map(&:name)).to match_array([:record_number])
135
+ end
136
+ end
137
+
138
+ context 'when a ruby file uses Forwardable::def_delegators' do
139
+ let(:ruby_code) {
140
+ "class RecordCollection
141
+ extend Forwardable
142
+ def_delegators :@records, :size, :<<, :map
143
+ end"
144
+ }
145
+ it 'can find methods created by def_delegators' do
146
+ expect(defined_methods.map(&:name)).to match_array(%i[<< map size])
147
+ end
148
+ end
149
+
150
+ context 'when a rails model has scopes' do
151
+ let(:ruby_code) {
152
+ "class Post
153
+ scope :published, -> { where(published: true) }
154
+ scope :draft, -> { where(published: true) }
155
+ end"
156
+ }
157
+ it 'can find scopes' do
158
+ expect(defined_methods.map(&:name)).to match_array(%i[draft published])
159
+ end
160
+ end
161
+
162
+ context 'when a ruby file has private methods' do
163
+ let(:ruby_code) {
164
+ "class FizzBuzz
165
+ def fizz
166
+ magick_helper
167
+ end
168
+
169
+ private
170
+ def magick_helper
171
+ 'magick sauce'
172
+ end
173
+
174
+ def other_helper
175
+ 'boo'
176
+ end
177
+ end"
178
+ }
179
+ it 'excludes private method calls, since we KNOW they are called' do
180
+ expect(defined_methods.map(&:name)).to match_array([:fizz, :magick_helper, :other_helper])
181
+ end
182
+ end
183
+ end
184
+ end
data/zombie_scout.gemspec CHANGED
@@ -10,12 +10,12 @@ Gem::Specification.new do |s|
10
10
 
11
11
  s.summary = "Find dead methods in your Ruby app"
12
12
  s.description = "
13
- zombie_scout finds methods in classes in your ruby project,
14
- and then searches for calls to them, and tells you which ones
13
+ zombie_scout finds methods in classes in your ruby project,
14
+ and then searches for calls to them, and tells you which ones
15
15
  are never called.".strip.gsub(/^\s*/, '')
16
16
  s.homepage = 'https://github.com/danbernier/zombie_scout'
17
17
  s.license = 'ASL2'
18
-
18
+
19
19
  s.authors = ["Dan Bernier"]
20
20
  s.email = ['danbernier@gmail.com']
21
21
 
@@ -26,5 +26,6 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_dependency('parser', '~> 2.1')
28
28
  s.add_dependency('thor', '~> 0.18')
29
+ s.add_dependency('flog', '~> 4.2')
29
30
  s.add_development_dependency "bundler", "~> 1.5"
30
31
  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.2
4
+ version: 0.0.3
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-02-28 00:00:00.000000000 Z
11
+ date: 2014-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.18'
41
+ - !ruby/object:Gem::Dependency
42
+ name: flog
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.2'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,8 +66,10 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '1.5'
55
- description: "zombie_scout finds methods in classes in your ruby project, \nand then
56
- searches for calls to them, and tells you which ones \nare never called."
69
+ description: |-
70
+ zombie_scout finds methods in classes in your ruby project,
71
+ and then searches for calls to them, and tells you which ones
72
+ are never called.
57
73
  email:
58
74
  - danbernier@gmail.com
59
75
  executables:
@@ -74,14 +90,15 @@ files:
74
90
  - bin/zombie_scout
75
91
  - lib/zombie_scout.rb
76
92
  - lib/zombie_scout/app.rb
93
+ - lib/zombie_scout/flog_scorer.rb
77
94
  - lib/zombie_scout/method_call_finder.rb
78
- - lib/zombie_scout/method_finder.rb
79
95
  - lib/zombie_scout/mission.rb
96
+ - lib/zombie_scout/parser.rb
80
97
  - lib/zombie_scout/ruby_project.rb
81
98
  - lib/zombie_scout/ruby_source.rb
82
99
  - lib/zombie_scout/version.rb
83
100
  - spec/spec_helper.rb
84
- - spec/zombie_scout/method_finder_spec.rb
101
+ - spec/zombie_scout/parser_spec.rb
85
102
  - spec/zombie_scout/ruby_project_spec.rb
86
103
  - spec/zombie_scout/ruby_source_spec.rb
87
104
  - zombie_scout.gemspec
@@ -111,6 +128,6 @@ specification_version: 4
111
128
  summary: Find dead methods in your Ruby app
112
129
  test_files:
113
130
  - spec/spec_helper.rb
114
- - spec/zombie_scout/method_finder_spec.rb
131
+ - spec/zombie_scout/parser_spec.rb
115
132
  - spec/zombie_scout/ruby_project_spec.rb
116
133
  - spec/zombie_scout/ruby_source_spec.rb
@@ -1,160 +0,0 @@
1
- require 'spec_helper'
2
- require 'zombie_scout/method_finder'
3
-
4
- describe ZombieScout::MethodFinder, '#find_methods' do
5
- let(:ruby_source) {
6
- double(:ruby_source, path: 'lib/fizzbuzz.rb', source: ruby_code)
7
- }
8
- let(:zombies) {
9
- ZombieScout::MethodFinder.new(ruby_source).find_methods.sort_by(&:name)
10
- }
11
-
12
- context 'when a ruby file has instance or class methods' do
13
- let(:ruby_code) {
14
- "class FizzBuzz
15
- def fizz
16
- 'plop plop'
17
- end
18
-
19
- def self.buzz
20
- 'bzz bzz bzz'
21
- end
22
- end"
23
- }
24
-
25
- it 'can find the methods' do
26
- expect(zombies[0].name).to eq :buzz
27
- expect(zombies[0].location).to eq 'lib/fizzbuzz.rb:6'
28
-
29
- expect(zombies[1].name).to eq :fizz
30
- expect(zombies[1].location).to eq 'lib/fizzbuzz.rb:2'
31
- end
32
- end
33
-
34
- context 'when a ruby file has attr_readers' do
35
- context "when they're declared with symbols, the normal way" do
36
- let(:ruby_code) {
37
- "class Book
38
- attr_reader :title, :author
39
- end"
40
- }
41
- it 'can find attr_readers' do
42
- expect(zombies.map(&:name)).to eq(%i[author title])
43
- end
44
- end
45
- context "when they're declare with a splat from an array of symbols" do
46
- let(:ruby_code) {
47
- "class Book
48
- attributes = %i(title author)
49
- attr_reader *attributes
50
- end"
51
- }
52
- it 'will ignore them' do
53
- expect(zombies).to be_empty
54
- end
55
- end
56
- end
57
-
58
- context 'when a ruby file has attr_writers' do
59
- context "when they're declared with symbols, the normal way" do
60
- let(:ruby_code) {
61
- "class Book
62
- attr_writer :title, :author
63
- end"
64
- }
65
- it 'can find attr_writers' do
66
- expect(zombies.map(&:name)).to eq(%i[author= title=])
67
- end
68
- end
69
- context "when they're declare with a splat from an array of symbols" do
70
- let(:ruby_code) {
71
- "class Book
72
- attributes = %i(title author)
73
- attr_reader *attributes
74
- end"
75
- }
76
- it 'will ignore them' do
77
- expect(zombies).to be_empty
78
- end
79
- end
80
- end
81
-
82
- context 'when a ruby file has attr_accessors' do
83
- context "when they're declared with symbols, the normal way" do
84
- let(:ruby_code) {
85
- "class Book
86
- attr_accessor :title, :author
87
- end"
88
- }
89
- it 'can find attr_accessors' do
90
- expect(zombies.map(&:name)).to eq(%i[author author= title title=])
91
- end
92
- end
93
- context "when they're declare with a splat from an array of symbols" do
94
- let(:ruby_code) {
95
- "class Book
96
- attributes = %i(title author)
97
- attr_accessor *attributes
98
- end"
99
- }
100
- it 'will ignore them' do
101
- expect(zombies).to be_empty
102
- end
103
- end
104
- end
105
-
106
- context 'when a ruby file uses Forwardable::def_delegator' do
107
- let(:ruby_code) {
108
- "class RecordCollection
109
- extend Forwardable
110
- def_delegator :@records, :[], :record_number
111
- end"
112
- }
113
- it 'can find methods created by def_delegator' do
114
- expect(zombies.map(&:name)).to match_array([:record_number])
115
- end
116
- end
117
-
118
- context 'when a ruby file uses Forwardable::def_delegators' do
119
- let(:ruby_code) {
120
- "class RecordCollection
121
- extend Forwardable
122
- def_delegators :@records, :size, :<<, :map
123
- end"
124
- }
125
- it 'can find methods created by def_delegators' do
126
- expect(zombies.map(&:name)).to eq(%i[<< map size])
127
- end
128
- end
129
-
130
- context 'when a rails model has scopes' do
131
- let(:ruby_code) {
132
- "class Post
133
- scope :published, -> { where(published: true) }
134
- scope :draft, -> { where(published: true) }
135
- end"
136
- }
137
- it 'can find scopes' do
138
- expect(zombies.map(&:name)).to eq(%i[draft published])
139
- end
140
- end
141
-
142
- context 'when a ruby file has private methods' do
143
- let(:ruby_code) {
144
- "class FizzBuzz
145
- def fizz
146
- magick_helper
147
- end
148
-
149
- private
150
- def magick_helper
151
- 'magick sauce'
152
- end
153
- end"
154
- }
155
- it 'excludes private method calls, since we KNOW they are called' do
156
- expect(zombies.map(&:name)).to match_array([:fizz])
157
- expect(zombies.map(&:name)).not_to include :magick_helper
158
- end
159
- end
160
- end