private_detective 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3b2a4fd2e7baae3f735e10f71e48d5b9536d54831244e36283afaacc028919c5
4
+ data.tar.gz: 104bfcd621e4fb437c5956f6ac9793ab7116ef8a26ae2927f78deec9ec668d80
5
+ SHA512:
6
+ metadata.gz: 60038d8884c2bec910a34ebc060265beb5c1068d91b5ae23fc4365dabd0ec766e06e10239021dcb107bd60a7836fe4eaa5cdd33acbd6421b5a28f7258a919859
7
+ data.tar.gz: 6ee76e06401e130ebde370d1389ff714cabc9a20707bcdb8385d86e73134eea2d7d8159d9e39b0c59371db2a30d6c3b666ad68f13d55a89a78e38490c28064c6
data/.idea/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
data/.idea/misc.xml ADDED
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="rbenv: 2.7.3" project-jdk-type="RUBY_SDK" />
4
+ </project>
data/.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/private_detective.iml" filepath="$PROJECT_DIR$/.idea/private_detective.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="RUBY_MODULE" version="4">
3
+ <component name="ModuleRunConfigurationManager">
4
+ <shared />
5
+ </component>
6
+ <component name="NewModuleRootManager">
7
+ <content url="file://$MODULE_DIR$">
8
+ <sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
9
+ <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
10
+ <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
11
+ </content>
12
+ <orderEntry type="inheritedJdk" />
13
+ <orderEntry type="sourceFolder" forTests="false" />
14
+ <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, rbenv: 2.7.3) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="byebug (v11.1.3, rbenv: 2.7.3) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="coderay (v1.1.3, rbenv: 2.7.3) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="colorize (v0.8.1, rbenv: 2.7.3) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, rbenv: 2.7.3) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="language_server-protocol (v3.17.0.3, rbenv: 2.7.3) [gem]" level="application" />
20
+ <orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, rbenv: 2.7.3) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="parallel (v1.23.0, rbenv: 2.7.3) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="parser (v3.2.2.4, rbenv: 2.7.3) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="pry (v0.14.2, rbenv: 2.7.3) [gem]" level="application" />
24
+ <orderEntry type="library" scope="PROVIDED" name="pry-byebug (v3.10.1, rbenv: 2.7.3) [gem]" level="application" />
25
+ <orderEntry type="library" scope="PROVIDED" name="racc (v1.7.3, rbenv: 2.7.3) [gem]" level="application" />
26
+ <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, rbenv: 2.7.3) [gem]" level="application" />
27
+ <orderEntry type="library" scope="PROVIDED" name="rake (v13.1.0, rbenv: 2.7.3) [gem]" level="application" />
28
+ <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.6, rbenv: 2.7.3) [gem]" level="application" />
29
+ <orderEntry type="library" scope="PROVIDED" name="rspec (v3.12.0, rbenv: 2.7.3) [gem]" level="application" />
30
+ <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.12.2, rbenv: 2.7.3) [gem]" level="application" />
31
+ <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.12.3, rbenv: 2.7.3) [gem]" level="application" />
32
+ <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.12.6, rbenv: 2.7.3) [gem]" level="application" />
33
+ <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.12.1, rbenv: 2.7.3) [gem]" level="application" />
34
+ <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.30.0, rbenv: 2.7.3) [gem]" level="application" />
35
+ <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.13.0, rbenv: 2.7.3) [gem]" level="application" />
36
+ <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.5.0, rbenv: 2.7.3) [gem]" level="application" />
37
+ </component>
38
+ <component name="RakeTasksCache">
39
+ <option name="myRootTask">
40
+ <RakeTaskImpl id="rake" />
41
+ </option>
42
+ </component>
43
+ </module>
data/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-12-01
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in private_detective.gemspec
6
+ gemspec
7
+
8
+ gem "colorize"
9
+ gem "parser"
10
+ gem "pry-byebug"
11
+ gem "rake", "~> 13.0"
12
+ gem "rspec", "~> 3.0"
13
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ private_detective (0.1.0)
5
+ colorize (~> 0.8.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ byebug (11.1.3)
12
+ coderay (1.1.3)
13
+ colorize (0.8.1)
14
+ diff-lcs (1.5.0)
15
+ json (2.7.1)
16
+ language_server-protocol (3.17.0.3)
17
+ method_source (1.0.0)
18
+ parallel (1.23.0)
19
+ parser (3.2.2.4)
20
+ ast (~> 2.4.1)
21
+ racc
22
+ pry (0.14.2)
23
+ coderay (~> 1.1)
24
+ method_source (~> 1.0)
25
+ pry-byebug (3.10.1)
26
+ byebug (~> 11.0)
27
+ pry (>= 0.13, < 0.15)
28
+ racc (1.7.3)
29
+ rainbow (3.1.1)
30
+ rake (13.1.0)
31
+ regexp_parser (2.8.3)
32
+ rexml (3.2.6)
33
+ rspec (3.12.0)
34
+ rspec-core (~> 3.12.0)
35
+ rspec-expectations (~> 3.12.0)
36
+ rspec-mocks (~> 3.12.0)
37
+ rspec-core (3.12.2)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-expectations (3.12.3)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.12.0)
42
+ rspec-mocks (3.12.6)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.12.0)
45
+ rspec-support (3.12.1)
46
+ rubocop (1.58.0)
47
+ json (~> 2.3)
48
+ language_server-protocol (>= 3.17.0)
49
+ parallel (~> 1.10)
50
+ parser (>= 3.2.2.4)
51
+ rainbow (>= 2.2.2, < 4.0)
52
+ regexp_parser (>= 1.8, < 3.0)
53
+ rexml (>= 3.2.5, < 4.0)
54
+ rubocop-ast (>= 1.30.0, < 2.0)
55
+ ruby-progressbar (~> 1.7)
56
+ unicode-display_width (>= 2.4.0, < 3.0)
57
+ rubocop-ast (1.30.0)
58
+ parser (>= 3.2.1.0)
59
+ ruby-progressbar (1.13.0)
60
+ unicode-display_width (2.5.0)
61
+
62
+ PLATFORMS
63
+ x86_64-darwin-23
64
+
65
+ DEPENDENCIES
66
+ colorize
67
+ parser
68
+ private_detective!
69
+ pry-byebug
70
+ rake (~> 13.0)
71
+ rspec (~> 3.0)
72
+ rubocop (~> 1.21)
73
+
74
+ BUNDLED WITH
75
+ 2.4.10
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Ross Buddie
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Private Detective 🕵️
2
+ ## A Ruby gem to analyze method visibility in your Ruby project
3
+
4
+ ## Overview
5
+
6
+ Private Detective was created to address a common issue in Ruby projects where determining the appropriate visibility for class methods is sometimes overlooked. Often, it is not immediately clear whether a method should be private, protected or public. Private Detective simplifies this process by providing tools to analyze Ruby source code files, helping developers identify and refine the visibility of their methods.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'private_detective'
14
+ ```
15
+
16
+ and then execute:
17
+
18
+ ```bash
19
+ $ private_detective
20
+ ```
21
+
22
+ ## Why did I build this gem?
23
+
24
+ In real-world scenarios, it is common for public methods to inadvertently expose internal details, or for private methods to be unnecessarily restrictive. This can lead to issues with maintainability, code readability, and overall project health. Private Detective was developed to streamline the identification and rectification of such visibility issues, offering a clearer understanding of method visibility within Ruby projects.
25
+
26
+ More importantly, it was also a good excuse to look at the inner workings of the Rubocop gem and build a gem of my own exploring the Ruby file parser and to traverse AST nodes.
27
+
28
+ ## How It Works
29
+
30
+ The Private Detective gem employs the following process to analyze method visibility in your Ruby project:
31
+
32
+ 1. **Iteration Over Project Files:**
33
+ - The gem iterates over your project files (currently app/models only), inspecting the Ruby source code.
34
+
35
+ 2. **Parsing to AST Nodes:**
36
+ - Each file is parsed into an Abstract Syntax Tree (AST) node using the `parser` gem.
37
+
38
+ 3. **Traversing the AST:**
39
+ - The gem traverses the AST nodes to identify class methods and their visibility modifiers: Private, Protected and the default Public.
40
+
41
+ 4. **Visibility Analysis:**
42
+ - Based on the identified methods and modifiers, Private Detective provides insights into potential issues related to method visibility.
43
+
44
+ ## Example response
45
+
46
+ ```response
47
+ Private Detective found the following Class method information in your project:
48
+
49
+ Class: Client
50
+ Method: test_method_rubocop_client, visibility: public
51
+ Method contents:
52
+ models/user.rb:10:4 User#test_method_rubocop private [Correctable]
53
+ models/user.rb:11:4 User#test_private_method_rubocop protected [Correctable]
54
+ Method: test_additional_method_example, visibility: private
55
+ Method: test_additional_method_example_2, visibility: private
56
+
57
+ Class: Prompt
58
+ Method: message_content, visibility: public
59
+
60
+ Class: User
61
+ Method: test_method_rubocop, visibility: private
62
+ Method: test_private_method_rubocop, visibility: protected
63
+
64
+ End of report
65
+ ```
66
+
67
+ ## Contributing
68
+
69
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rossme/private_detective.
70
+
71
+ ## License
72
+
73
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
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
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "parser/current"
6
+ require "colorize"
7
+ require "private_detective"
8
+
9
+ files = if ARGV.empty?
10
+ Dir.glob("#{Dir.pwd}/**/app/models/*.rb")
11
+ else
12
+ ARGV
13
+ end
14
+
15
+ private_detective = PrivateDetective::AnalyzeProject.new(files: files)
16
+ private_detective&.analyze_project
17
+
18
+ require "irb"
19
+ IRB.start(__FILE__)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrivateDetective
4
+ class AnalyzeFile
5
+
6
+ attr_reader :file_path, :report, :node
7
+
8
+ # @param [String] file_path, [Hash] report
9
+ def initialize(file_path:, report:)
10
+ @file_path = file_path
11
+ @report = report
12
+ end
13
+
14
+ # @return [Hash] report
15
+ def analyze_file
16
+ @node = Parser::CurrentRuby.parse(File.read(file_path))
17
+ return unless node.is_a?(Parser::AST::Node)
18
+
19
+ analyze_node
20
+ end
21
+
22
+ private
23
+
24
+ # @return [Hash] report
25
+ def analyze_node
26
+ analyze_node = PrivateDetective::AnalyzeNode.new(node: node, file_path: file_path)
27
+ analyze_node.process_node
28
+
29
+ report.merge!(analyze_node.report)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrivateDetective
4
+ class AnalyzeNode
5
+
6
+ ACCESS_CONTROL_KEYWORDS = %i[public private protected].freeze
7
+
8
+ attr_reader :class_details, :super_class, :class_body, :file_path, :report
9
+
10
+ # @param [Parser::AST::Node] node, [String] file_path
11
+ def initialize(node:, file_path: nil)
12
+ @class_details, @super_class, @class_body = *node
13
+ @file_path = file_path
14
+ @report = { class_name => [] }
15
+ end
16
+
17
+ # @return [Hash] report
18
+ def process_node
19
+ return unless class_pattern?
20
+
21
+ create_report
22
+ end
23
+
24
+ private
25
+
26
+ # @return [Hash] report
27
+ def create_report
28
+ class_body.children.each do |child_node|
29
+ next unless def_method?(child_node)
30
+
31
+ report[class_name] << build_method_hash(child_node)
32
+ end
33
+ end
34
+
35
+ # @return [Boolean]
36
+ def class_pattern?
37
+ class_name && super_class&.children[1] == :ApplicationRecord && class_body&.type == :begin
38
+ end
39
+
40
+ # @return [Boolean]
41
+ def def_method?(child_node)
42
+ child_node&.type == :def
43
+ end
44
+
45
+ # @return [Hash] method name, class name, visibility, on_send, location
46
+ def build_method_hash(child_node)
47
+ {
48
+ file_path: shortened_file_path,
49
+ class: class_name,
50
+ method: child_node.children[0],
51
+ visibility: method_visibility(child_node),
52
+ on_send: build_send_hash(child_node),
53
+ location: {
54
+ location: child_node.location,
55
+ line: child_node.location.line,
56
+ column: child_node.location.column,
57
+ source: child_node.location.expression.source
58
+ }
59
+ }
60
+ end
61
+
62
+ # @return [Hash] send_method_hash(send_node)
63
+ # @example { 0 => { method: :my_user_method, class: :user } }
64
+ def build_send_hash(child_node)
65
+ send_hash = {}
66
+ child_node.children[2].children.each_with_index do |send_node, index|
67
+ next unless valid_send_node?(send_node)
68
+
69
+ send_hash.merge!(index => send_method_hash(send_node))
70
+ end
71
+ send_hash
72
+ end
73
+
74
+ # @return [Boolean]
75
+ def valid_send_node?(send_node)
76
+ return false unless send_node.is_a?(Parser::AST::Node)
77
+
78
+ send_node.type == :send && send_node.children&.first&.type == :const
79
+ end
80
+
81
+ # @return [Hash] method name and class name
82
+ def send_method_hash(send_node)
83
+ {
84
+ method: send_node.children[1],
85
+ class: send_node.children[0].children[1],
86
+ location: {
87
+ location: send_node.location,
88
+ line: send_node.location.line,
89
+ column: send_node.location.column,
90
+ source: send_node.location.expression.source
91
+ }
92
+ }
93
+ end
94
+
95
+ # @return [Symbol] visibility
96
+ # @example :public, :private, :protected
97
+ def method_visibility(child_node)
98
+ visibility = :public
99
+
100
+ while child_node
101
+ if child_node.type == :send && ACCESS_CONTROL_KEYWORDS.include?(child_node.children[1])
102
+ visibility = child_node.children[1]
103
+ break
104
+ end
105
+ child_node = sibling_index(child_node).zero? ? nil : sibling_node(child_node)
106
+ end
107
+
108
+ visibility
109
+ end
110
+
111
+ # @return [Integer] index of child_node in parent_node
112
+ def sibling_index(child_node)
113
+ class_body&.children&.index { |sibling| sibling.equal?(child_node) }
114
+ end
115
+
116
+ # @return [Parser::AST::Node] sibling node
117
+ def sibling_node(child_node)
118
+ class_body.children[sibling_index(child_node) - 1]
119
+ end
120
+
121
+ # @return [Symbol] class name
122
+ def class_name
123
+ @class_name ||= class_details&.children&.last
124
+ end
125
+
126
+ def shortened_file_path
127
+ @shortened_file_path ||= file_path&.split('/').last(2).join('/')
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrivateDetective
4
+
5
+ class AnalyzeProject
6
+
7
+ attr_accessor :files, :report
8
+
9
+ # @param [Array] files
10
+ def initialize(files:)
11
+ @files = files
12
+ @report = {}
13
+ end
14
+
15
+ def analyze_project
16
+ analyze_files
17
+ filter_report
18
+ show_report
19
+
20
+ # Exit the IRB session after the report is shown
21
+ exit
22
+ end
23
+
24
+ private
25
+
26
+ def analyze_files
27
+ files.each do |file_path|
28
+ analyze_file(file_path)
29
+ end
30
+ end
31
+
32
+ # @return [Hash] report with empty classes and methods removed
33
+ def filter_report
34
+ report.delete_if { |_class_name, methods| methods.empty? }
35
+ end
36
+
37
+ # @param [String] file_path
38
+ def analyze_file(file_path)
39
+ investigation = PrivateDetective::AnalyzeFile.new(file_path: file_path, report: report)
40
+ investigation.analyze_file
41
+ end
42
+
43
+ def show_report
44
+ return puts "No class methods found".colorize(:red) if report.empty?
45
+
46
+ PrivateDetective::Report.generate(report: report)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrivateDetective
4
+ class Report
5
+
6
+ class << self
7
+
8
+ def generate(report:)
9
+ return puts 'No class methods found'.colorize(:red) if report.empty?
10
+
11
+ puts "Private Detective found the following Class method information in your project:\n".colorize(:yellow)
12
+
13
+ report.each do |class_name, methods|
14
+ puts "Class: #{class_name}".colorize(:cyan)
15
+ methods.each do |method_info|
16
+ method_name = method_info[:method]
17
+
18
+ puts " Method: #{method_name}, visibility: #{format_visibility(method_info[:visibility])}"
19
+ on_send_values = method_info[:on_send]
20
+ next if on_send_values.empty?
21
+
22
+ puts "\tMethod contents:"
23
+ handle_on_send_values(report, method_info)
24
+ end
25
+ puts "\n"
26
+ end
27
+ puts 'End of report'.colorize(:cyan)
28
+ end
29
+
30
+ def handle_on_send_values(report, method_info)
31
+ values = method_info[:on_send]
32
+
33
+ values.each do |_k, v|
34
+ method = v[:method]
35
+ klass = v[:class]
36
+
37
+ # find the parent method info
38
+ method_info = report[klass].find { |m| m[:method] == method }
39
+ next unless method_info
40
+
41
+ puts "\t\t#{method_info[:file_path]}:#{v.dig(:location, :line)}:#{v.dig(:location, :column)} #{klass.to_s.colorize(:cyan)}##{method.to_s.colorize(:cyan)} #{format_visibility(method_info[:visibility], true)}"
42
+ end
43
+ end
44
+
45
+ def format_visibility(visibility, on_send = false)
46
+ case visibility
47
+ when :private
48
+ visibility.to_s.colorize(:red) + (on_send ? ' [Correctable]'.colorize(:green) : '')
49
+ when :protected
50
+ visibility.to_s.colorize(:yellow) + (on_send ? ' [Correctable]'.colorize(:green) : '')
51
+ else # :public
52
+ visibility.to_s.colorize(:green)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrivateDetective
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,17 @@
1
+ # lib/private_detective.rb
2
+
3
+ # @example Usage in a Rails project
4
+ # private_detective = PrivateDetective::AnalyzeProject.new
5
+ # private_detective.analyze_project
6
+
7
+ module PrivateDetective
8
+ require "parser/current"
9
+ require "colorize"
10
+ require_relative "./private_detective/analyze_project"
11
+ require_relative "./private_detective/analyze_file"
12
+ require_relative "./private_detective/analyze_node"
13
+ require_relative "./private_detective/report"
14
+ require_relative "./private_detective/version"
15
+
16
+ class PrivateDetectiveError < StandardError; end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/private_detective/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "private_detective"
7
+ spec.version = PrivateDetective::VERSION
8
+ spec.authors = ["rossme"]
9
+ spec.email = ["r.buddie@gmail.com"]
10
+
11
+ spec.summary = "Private Detective is a Ruby parser gem that helps you check method visibility."
12
+ spec.description = "Method visibility is an important part of Ruby's object model. Private Detective helps you check method visibility in your Ruby code."
13
+ spec.homepage = "https://github.com/rossme/private_detective"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/rossme/private_detective"
19
+ spec.metadata["changelog_uri"] = "https://github.com/rossme/private_detective/blob/master/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
26
+ end
27
+ end
28
+ spec.bindir = "bin"
29
+ spec.executables = ["private_detective"]
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+
35
+ spec.add_dependency "colorize", "~> 0.8.1"
36
+
37
+ # For more information and examples about making a new gem, check out our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
39
+ end
@@ -0,0 +1,4 @@
1
+ module PrivateDetective
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: private_detective
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - rossme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.1
27
+ description: Method visibility is an important part of Ruby's object model. Private
28
+ Detective helps you check method visibility in your Ruby code.
29
+ email:
30
+ - r.buddie@gmail.com
31
+ executables:
32
+ - private_detective
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".idea/.gitignore"
37
+ - ".idea/misc.xml"
38
+ - ".idea/modules.xml"
39
+ - ".idea/private_detective.iml"
40
+ - ".idea/vcs.xml"
41
+ - ".rspec"
42
+ - ".rubocop.yml"
43
+ - CHANGELOG.md
44
+ - Gemfile
45
+ - Gemfile.lock
46
+ - LICENSE.txt
47
+ - README.md
48
+ - Rakefile
49
+ - bin/private_detective
50
+ - lib/private_detective.rb
51
+ - lib/private_detective/analyze_file.rb
52
+ - lib/private_detective/analyze_node.rb
53
+ - lib/private_detective/analyze_project.rb
54
+ - lib/private_detective/report.rb
55
+ - lib/private_detective/version.rb
56
+ - private_detective.gemspec
57
+ - sig/private_detective.rbs
58
+ homepage: https://github.com/rossme/private_detective
59
+ licenses:
60
+ - MIT
61
+ metadata:
62
+ homepage_uri: https://github.com/rossme/private_detective
63
+ source_code_uri: https://github.com/rossme/private_detective
64
+ changelog_uri: https://github.com/rossme/private_detective/blob/master/CHANGELOG.md
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 2.6.0
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.4.10
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Private Detective is a Ruby parser gem that helps you check method visibility.
84
+ test_files: []