unused 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a86b4573cbfaaf695d099312598a519d43e77610e1bb49b2aa874754c6214824
4
+ data.tar.gz: 07b88a9cefe71a52b80f695b46ed4b03899d05bc5cb64ae52992f8a6c5b46313
5
+ SHA512:
6
+ metadata.gz: 26ae749dde1d7cf37e9ca7635be6657bf82fec11e1bce7dcaebdf199fdfc309c8a8e1401465b362b3dfe7097845efa6c00c8e09ebdbf72d71d838cb41823f3a3
7
+ data.tar.gz: bf32f9e9923aa82738157a55396a78028140e2596ff59a61e201cfe7078c15fe7ed532f5e8d563c7dd2839330642555631eb886fab2a6bdf8699e42b0c7b25e0
@@ -0,0 +1,58 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ ## Specific to RubyMotion:
20
+ .dat*
21
+ .repl_history
22
+ build/
23
+ *.bridgesupport
24
+ build-iPhoneOS/
25
+ build-iPhoneSimulator/
26
+
27
+ ## Specific to RubyMotion (use of CocoaPods):
28
+ #
29
+ # We recommend against adding the Pods directory to your .gitignore. However
30
+ # you should judge for yourself, the pros and cons are mentioned at:
31
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
+ #
33
+ # vendor/Pods/
34
+
35
+ ## Documentation cache and generated files:
36
+ /.yardoc/
37
+ /_yardoc/
38
+ /doc/
39
+ /rdoc/
40
+
41
+ ## Environment normalization:
42
+ /.bundle/
43
+ /vendor/bundle
44
+ /lib/bundler/man/
45
+
46
+ # for a library or gem, you might want to ignore these files since the code is
47
+ # intended to run in multiple environments; otherwise, check them in:
48
+ # Gemfile.lock
49
+ # .ruby-version
50
+ # .ruby-gemset
51
+
52
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
+ .rvmrc
54
+
55
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
+ # .rubocop-https?--*
57
+
58
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.0
6
+ before_install: gem install bundler -v 2.1.4
File without changes
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in unused.gemspec
6
+ gemspec
7
+
8
+ gem 'pry', group: :test
9
+ gem 'rake', '~> 12.0'
10
+ gem 'rspec'
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ unused (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.2)
10
+ diff-lcs (1.3)
11
+ method_source (0.9.2)
12
+ pry (0.12.2)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.9.0)
15
+ rake (12.3.3)
16
+ rspec (3.9.0)
17
+ rspec-core (~> 3.9.0)
18
+ rspec-expectations (~> 3.9.0)
19
+ rspec-mocks (~> 3.9.0)
20
+ rspec-core (3.9.1)
21
+ rspec-support (~> 3.9.1)
22
+ rspec-expectations (3.9.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.9.0)
25
+ rspec-mocks (3.9.1)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.9.0)
28
+ rspec-support (3.9.2)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ pry
35
+ rake (~> 12.0)
36
+ rspec
37
+ unused!
38
+
39
+ BUNDLED WITH
40
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Stephen Freund
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,117 @@
1
+ # Unused
2
+
3
+ Unused tracks the usage of all your application's methods during runtime and identifies methods that were never called.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'unused, require: false'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install unused
20
+
21
+ ## Basic Usage
22
+
23
+ Load and start Unused just before loading your application code. For instance, in a rails app, add the following lines
24
+ to `config/environment.rb`
25
+ ```
26
+ require_relative 'application'
27
+
28
+ # Add these two lines
29
+ require_relative "../../unused/lib/unused"
30
+ Unused.start
31
+
32
+ Rails.application.initialize!
33
+ ```
34
+
35
+ When the application finishes execution (when the server is stopped in the above example), Unused will report all of the
36
+ methods defined in your application and the usage.
37
+
38
+ ## Configuration
39
+
40
+ ### Path
41
+ Specifies where the code to track is located on the file system.
42
+
43
+ ```
44
+ # default
45
+ # The root of the project
46
+ Unused.configure do |config|
47
+ config.path = Dir.pwd
48
+ end
49
+
50
+ # Only track classes defined in app/models
51
+ Unused.configure do |config|
52
+ config.path = Pathname.new(Dir.pwd).join("app", "models").to_s
53
+ end
54
+ ```
55
+
56
+ ### Reporter
57
+ Specifies the method of reporting results
58
+ ```
59
+ # default
60
+ # a csv report listing all of the methods, their location, and the number of calls
61
+ # can specify output file via output_file configuration
62
+ Unused.configure do |config|
63
+ config.reporter = :csv
64
+ end
65
+
66
+ # Report results to stdout
67
+ # will only print methods with 0 calls
68
+ Unused.configure do |config|
69
+ config.reporter = :stdout
70
+ end
71
+ ```
72
+
73
+ ### Output File
74
+ Specifies the output for the csv reporter
75
+ ```
76
+ # default
77
+ # output report to unused_methods.csv in the project root
78
+ Unused.configure do |config|
79
+ config.output_file = "#{Dir.pwd}/unused_methods.csv"
80
+ end
81
+
82
+ # output report to a file appened with the day in the temp directory
83
+ Unused.configure do |config|
84
+ config.output_file = "#{Dir.pwd}/tmp/unused_methods_#{DateTime.now.strftime("%Y_%m_%d")}.csv"
85
+ end
86
+ ```
87
+
88
+ ### Report At Exit
89
+ Specifies whether or not to automatically report at exit of program.
90
+ ```
91
+ # default
92
+ # will automatically report at exit of program
93
+ Unused.configure do |config|
94
+ config.report_at_exit = true
95
+ end
96
+
97
+ # do not automatically produce report
98
+ # require explicit call to Unused.report
99
+ Unused.configure do |config|
100
+ config.report_at_exit = true
101
+ end
102
+ ```
103
+
104
+ ## Development
105
+
106
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
107
+
108
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
109
+
110
+ ## Contributing
111
+
112
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sdfreund10/unused.
113
+
114
+
115
+ ## License
116
+
117
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'unused'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'unused/version'
4
+ require_relative 'unused/registry'
5
+ require_relative 'unused/reporter'
6
+ require_relative 'unused/definition_listener'
7
+ require_relative 'unused/call_listener'
8
+ require_relative 'unused/method_alias_listener'
9
+ require_relative 'unused/configuration'
10
+
11
+ module Unused
12
+ def self.start
13
+ MethodAliasListener.instance.enable
14
+ DefinitionListener.instance.enable
15
+ CallListener.instance.enable
16
+ at_exit { report } if config.report_at_exit
17
+ end
18
+
19
+ def self.stop
20
+ MethodAliasListener.instance.disable
21
+ DefinitionListener.instance.disable
22
+ CallListener.instance.disable
23
+ end
24
+
25
+ def self.config
26
+ Configuration.instance
27
+ end
28
+
29
+ def self.configure
30
+ yield config
31
+ end
32
+
33
+ def self.report
34
+ Reporter.call
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative './registry'
5
+
6
+ module Unused
7
+ class CallListener
8
+ include Singleton
9
+
10
+ def initialize
11
+ @trace = define_tracepoint
12
+ end
13
+
14
+ def enable
15
+ @trace.enable
16
+ end
17
+
18
+ def disable
19
+ @trace.disable
20
+ end
21
+
22
+ private
23
+
24
+ def define_tracepoint
25
+ TracePoint.new(:call) do |tp|
26
+ next unless tp.path.start_with?(Unused.config.path)
27
+
28
+ tracked_objects = Registry.tracked_objects
29
+ method = tp.method_id
30
+ owner = tp.self._method_UNUSED_ALIAS_(method).owner
31
+ next unless tracked_objects.include? owner.object_id
32
+
33
+ if owner.singleton_class?
34
+ Registry.log_class_method(owner.object_id, method)
35
+ else
36
+ Registry.log_instance_method(owner.object_id, method)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Unused
6
+ class Configuration
7
+ include Singleton
8
+
9
+ attr_accessor :path, :output_file, :report_at_exit, :reporter
10
+
11
+ def initialize
12
+ @path = Dir.pwd
13
+ @reporter = :csv
14
+ @output_file = "#{Dir.pwd}/unused_methods.csv"
15
+ @report_at_exit = true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative './registry'
5
+
6
+ module Unused
7
+ class DefinitionListener
8
+ include Singleton
9
+
10
+ def initialize
11
+ @trace = define_tracepoint
12
+ end
13
+
14
+ def enable
15
+ @trace.enable
16
+ end
17
+
18
+ def disable
19
+ @trace.disable
20
+ end
21
+
22
+ private
23
+
24
+ def define_tracepoint
25
+ TracePoint.new(:end) do |tp|
26
+ next unless tp.path.start_with?(Unused.config.path)
27
+
28
+ Registry.register(tp.self)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Unused
6
+ class MethodAliasListener
7
+ include Singleton
8
+
9
+ ESSENTIAL_METHODS = %w[
10
+ object_id
11
+ methods
12
+ private_methods
13
+ singleton_class
14
+ method
15
+ ].freeze
16
+
17
+ ESSENTIAL_METHODS_TO_DEFINE = %w[
18
+ instance_methods
19
+ private_instance_methods
20
+ instance_method
21
+ ].freeze
22
+
23
+ def initialize
24
+ @trace = define_tracepoint
25
+ end
26
+
27
+ def enable
28
+ @trace.enable
29
+ end
30
+
31
+ def disable
32
+ @trace.disable
33
+ end
34
+
35
+ def self.alias_pattern(method_name)
36
+ "_#{method_name}_UNUSED_ALIAS_"
37
+ end
38
+
39
+ private
40
+
41
+ def define_tracepoint
42
+ TracePoint.new(:class) do |tp|
43
+ if tp.path.start_with?(Unused.config.path)
44
+ alias_essential_methods(tp.self)
45
+ end
46
+ end
47
+ end
48
+
49
+ def alias_essential_methods(defined_obj)
50
+ ESSENTIAL_METHODS.each do |method|
51
+ alias_name = self.class.alias_pattern(method)
52
+ defined_obj.alias_method(alias_name, method)
53
+ defined_obj.singleton_class.alias_method(alias_name, method)
54
+ end
55
+
56
+ ESSENTIAL_METHODS_TO_DEFINE.each do |method|
57
+ alias_name = self.class.alias_pattern(method)
58
+ defined_obj.define_method(method) { |*args| super(*args) }
59
+ defined_obj.singleton_class.define_method(method) { |*args| super(*args) }
60
+ defined_obj.alias_method(alias_name, method)
61
+ defined_obj.singleton_class.alias_method(alias_name, method)
62
+ defined_obj.remove_method(method)
63
+ defined_obj.singleton_class.remove_method(method)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative './reporter'
5
+
6
+ module Unused
7
+ class Registry
8
+ include Singleton
9
+ # delegate class method calls to instance
10
+ def self.register(defined_class)
11
+ instance.register(defined_class)
12
+ end
13
+
14
+ def self.log_instance_method(callee_id, method_id)
15
+ instance.log_instance_method(callee_id, method_id)
16
+ end
17
+
18
+ def self.log_class_method(callee_id, method_id)
19
+ instance.log_class_method(callee_id, method_id)
20
+ end
21
+
22
+ def self.tracked_objects
23
+ instance.tracked_objects
24
+ end
25
+
26
+ def self.class_method_calls
27
+ instance.class_method_calls
28
+ end
29
+
30
+ def self.instance_method_calls
31
+ instance.instance_method_calls
32
+ end
33
+
34
+ def self.class_map
35
+ instance.class_map
36
+ end
37
+
38
+ def initialize
39
+ @class_method_calls = {}
40
+ @instance_method_calls = {}
41
+ @class_id_map = {}
42
+ end
43
+
44
+ def reset
45
+ @class_method_calls = {}
46
+ @instance_method_calls = {}
47
+ @class_id_map = {}
48
+ end
49
+
50
+ def tracked_objects
51
+ @class_id_map.keys
52
+ end
53
+
54
+ def register(defined_class)
55
+ id = defined_class._object_id_UNUSED_ALIAS_
56
+ @class_id_map[id] = defined_class
57
+ instance_methods = defined_class._instance_methods_UNUSED_ALIAS_(false) +
58
+ defined_class._private_instance_methods_UNUSED_ALIAS_(false)
59
+
60
+ instance_methods.each do |method|
61
+ key = [id, method]
62
+ next if @instance_method_calls.key?(key)
63
+ next if method.to_s.include? '_UNUSED_ALIAS_'
64
+
65
+ source, = defined_class._instance_method_UNUSED_ALIAS_(method).source_location
66
+ unless source &&
67
+ File.expand_path(source).start_with?(Unused.config.path)
68
+ next
69
+ end
70
+
71
+ @instance_method_calls.store(key, 0)
72
+ end
73
+
74
+ register_class_methods(defined_class)
75
+ end
76
+
77
+ def log_class_method(callee_id, method)
78
+ hash_key = [callee_id, method]
79
+
80
+ # metaprogramming can sometime define methods not in the registry
81
+ # skip in this case
82
+ return unless @class_method_calls.key?(hash_key)
83
+
84
+ incrment_class_method_call(hash_key)
85
+ end
86
+
87
+ def log_instance_method(callee_class_id, method)
88
+ hash_key = [callee_class_id, method]
89
+
90
+ # metaprogramming can sometime define methods not in the registry
91
+ # skip in this case
92
+ return unless @instance_method_calls.key?(hash_key)
93
+
94
+ increment_instance_method_call(hash_key)
95
+ end
96
+
97
+ def instance_method_calls
98
+ # getter returns clone so as not to allow mutation
99
+ @instance_method_calls.clone
100
+ end
101
+
102
+ def class_method_calls
103
+ # getter returns clone so as not to allow mutation
104
+ @class_method_calls.clone
105
+ end
106
+
107
+ def class_map
108
+ # getter returns clone so as not to allow mutation
109
+ @class_id_map.clone
110
+ end
111
+
112
+ private
113
+
114
+ def register_class_methods(defined_class)
115
+ # Class methods are owned by a defined class's "singleton"
116
+ # Singleton classes are only created on class method definition
117
+ # or when calling Class#singleton_class
118
+ # Thus, to prevent a needless allocation, check if there is
119
+ # any need to register the singleton
120
+
121
+ # :inherited and :initialize defined on all classes
122
+ class_methods = defined_class._private_methods_UNUSED_ALIAS_(false) -
123
+ %i[inherited initialize]
124
+ class_methods += defined_class._methods_UNUSED_ALIAS_(false)
125
+ return if class_methods.empty?
126
+
127
+ singleton_id = defined_class._singleton_class_UNUSED_ALIAS_.object_id
128
+ @class_id_map[singleton_id] = defined_class
129
+ class_methods.each do |method|
130
+ key = [singleton_id, method]
131
+ next if @class_method_calls.key?(key)
132
+ next if method.to_s.include? '_UNUSED_ALIAS_'
133
+
134
+ source, = defined_class._method_UNUSED_ALIAS_(method).source_location
135
+ unless source &&
136
+ File.expand_path(source).start_with?(Unused.config.path)
137
+ next
138
+ end
139
+
140
+ @class_method_calls.store(key, 0)
141
+ end
142
+ end
143
+
144
+ def increment_instance_method_call(hash_key)
145
+ @instance_method_calls[hash_key] += 1
146
+ end
147
+
148
+ def incrment_class_method_call(hash_key)
149
+ @class_method_calls[hash_key] += 1
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'reporters/csv_reporter'
4
+ require_relative 'reporters/stdout_reporter'
5
+
6
+ module Unused
7
+ class Reporter
8
+ def self.call
9
+ reporter_class.new(
10
+ Registry.instance_method_calls,
11
+ Registry.class_method_calls,
12
+ Registry.class_map
13
+ ).report
14
+ end
15
+
16
+ def self.reporter_class
17
+ case Unused.config.reporter
18
+ when :csv
19
+ Reporters::CSVReporter
20
+ else
21
+ Reporters::StdoutReporter
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unused
4
+ module Reporters
5
+ class Base
6
+ private
7
+
8
+ def method_summary
9
+ method_summary = @instance_methods.map do |(object_id, method_id), calls|
10
+ owner = @map[object_id]
11
+ Method.new(method_id, owner, 'instance', calls)
12
+ end
13
+ @class_methods.each do |(object_id, method_id), calls|
14
+ owner = @map[object_id]
15
+ method_summary << Method.new(method_id, owner, 'class', calls)
16
+ end
17
+ method_summary
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'method'
4
+ require_relative 'base'
5
+ require 'csv'
6
+
7
+ module Unused
8
+ module Reporters
9
+ class CSVReporter < Base
10
+ HEADERS = %w[representation owner method type calls source].freeze
11
+ def initialize(instance_methods, class_methods, map)
12
+ @instance_methods = instance_methods
13
+ @class_methods = class_methods
14
+ @map = map
15
+ end
16
+
17
+ def report
18
+ CSV.open(
19
+ Unused.config.output_file,
20
+ 'w',
21
+ headers: HEADERS,
22
+ write_headers: true
23
+ ) do |csv|
24
+ ordered_methods.each do |method|
25
+ csv << [
26
+ method.representation,
27
+ method.owner,
28
+ method.id,
29
+ method.type,
30
+ method.calls,
31
+ method.source
32
+ ]
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def ordered_methods
40
+ method_summary.sort_by!(&:calls)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unused
4
+ module Reporters
5
+ class Method
6
+ attr_reader :id, :owner, :type, :calls
7
+
8
+ def initialize(id, owner, type, calls)
9
+ @id = id
10
+ @owner = owner
11
+ @type = type
12
+ @calls = calls
13
+ end
14
+
15
+ def representation
16
+ "#{@owner}#{representation_symbol}#{@id}"
17
+ end
18
+
19
+ def source
20
+ file, line = source_location
21
+ "#{file.sub(Unused.config.path, '')}:#{line}"
22
+ end
23
+
24
+ private
25
+
26
+ def representation_symbol
27
+ @type == 'instance' ? '#' : '::'
28
+ end
29
+
30
+ def source_location
31
+ if @type == 'instance'
32
+ @owner.instance_method(@id).source_location
33
+ else
34
+ @owner.method(@id).source_location
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'method'
4
+ require_relative 'base'
5
+
6
+ module Unused
7
+ module Reporters
8
+ class StdoutReporter < Base
9
+ HEADERS = %w[
10
+ Method
11
+ Calls
12
+ Source
13
+ ].freeze
14
+
15
+ def initialize(instance_methods, class_methods, map)
16
+ @instance_methods = instance_methods
17
+ @class_methods = class_methods
18
+ @map = map
19
+ end
20
+
21
+ def report
22
+ print(output)
23
+ end
24
+
25
+ def output
26
+ method_length, call_length, loc_length = column_lengths
27
+ data = output_data
28
+ output = ''
29
+ output += HEADERS[0].ljust(method_length, ' ') + "\t"
30
+ output += HEADERS[1].ljust(call_length, ' ') + "\t"
31
+ output += HEADERS[2].ljust(loc_length, ' ') + "\t"
32
+ output += "\n"
33
+
34
+ data.each do |method, calls, location|
35
+ output += method.ljust(method_length, ' ') + "\t"
36
+ output += calls.rjust(call_length, ' ') + "\t"
37
+ output += location.ljust(loc_length, ' ') + "\t"
38
+ output += "\n"
39
+ end
40
+
41
+ output
42
+ end
43
+
44
+ private
45
+
46
+ def output_data
47
+ unused_methods.map do |method|
48
+ [method.representation,
49
+ method.calls.to_s,
50
+ method.source]
51
+ end
52
+ end
53
+
54
+ def column_lengths
55
+ output_data.transpose.map.with_index do |values, index|
56
+ [
57
+ HEADERS[index].length,
58
+ values.map(&:length).max
59
+ ].max
60
+ end
61
+ end
62
+
63
+ def unused_methods
64
+ method_summary.select do |method|
65
+ method.calls.zero?
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unused
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/unused/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'unused'
7
+ spec.version = Unused::VERSION
8
+ spec.authors = ['Stephen Freund']
9
+ spec.email = ['sdfreund10@gmail.com']
10
+
11
+ spec.summary = 'Identification of unused ruby methods'
12
+ spec.homepage = 'https://github.com/sdfreund10/unused'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/sdfreund10/unused'
18
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unused
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephen Freund
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-02-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - sdfreund10@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".travis.yml"
23
+ - CHANGELOG.md
24
+ - Gemfile
25
+ - Gemfile.lock
26
+ - LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - bin/console
30
+ - bin/setup
31
+ - lib/unused.rb
32
+ - lib/unused/call_listener.rb
33
+ - lib/unused/configuration.rb
34
+ - lib/unused/definition_listener.rb
35
+ - lib/unused/method_alias_listener.rb
36
+ - lib/unused/registry.rb
37
+ - lib/unused/reporter.rb
38
+ - lib/unused/reporters/base.rb
39
+ - lib/unused/reporters/csv_reporter.rb
40
+ - lib/unused/reporters/method.rb
41
+ - lib/unused/reporters/stdout_reporter.rb
42
+ - lib/unused/version.rb
43
+ - unused.gemspec
44
+ homepage: https://github.com/sdfreund10/unused
45
+ licenses:
46
+ - MIT
47
+ metadata:
48
+ homepage_uri: https://github.com/sdfreund10/unused
49
+ source_code_uri: https://github.com/sdfreund10/unused
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.3.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.1.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Identification of unused ruby methods
69
+ test_files: []