autoload-checker 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 +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +13 -0
- data/README.md +35 -0
- data/bin/autoload_checker.rb +33 -0
- data/lib/autoload_checker.rb +36 -0
- data/lib/namespace/corrector.rb +18 -0
- data/lib/namespace/fix.rb +18 -0
- data/lib/namespace/parser.rb +51 -0
- data/lib/namespace/validator.rb +70 -0
- data/lib/namespace.rb +42 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6e5e30b050ff00a10be2fbb62a51bb5b57f7a7cfe7f612fd7afdf2a99e2c4028
|
4
|
+
data.tar.gz: 1f16534a3b8a2965c4e08b1d1e35bab5fa43d0b421cd697d013fa761888183c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fc1269943f368984f3c3bebe7ab836a61bd57b4e985409b76f6443c0644097da00df194a96592951675a0decf1e438fb7a73034c25b9b3d7e2f668a0dd3d98d8
|
7
|
+
data.tar.gz: a611efd324d67b824680ceb70aead2410574ca03e51256e19e9dc668d0b185cbf26603682333c473c66b8683639207c16f2752920c867a97629089fd63f09a3e
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.5.0)
|
5
|
+
rspec (3.11.0)
|
6
|
+
rspec-core (~> 3.11.0)
|
7
|
+
rspec-expectations (~> 3.11.0)
|
8
|
+
rspec-mocks (~> 3.11.0)
|
9
|
+
rspec-core (3.11.0)
|
10
|
+
rspec-support (~> 3.11.0)
|
11
|
+
rspec-expectations (3.11.0)
|
12
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
13
|
+
rspec-support (~> 3.11.0)
|
14
|
+
rspec-mocks (3.11.1)
|
15
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
16
|
+
rspec-support (~> 3.11.0)
|
17
|
+
rspec-support (3.11.0)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
x86_64-darwin-21
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
rspec (~> 3.11)
|
24
|
+
|
25
|
+
BUNDLED WITH
|
26
|
+
2.3.11
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2022 cyberfined
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# autoload_checker
|
2
|
+
|
3
|
+
Checks for conflicts in class/module definitions and corrects them. For instance, you have
|
4
|
+
a file `foo/bar/baz.rb` with following content:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
module Foo
|
8
|
+
module Bar
|
9
|
+
class Baz
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
And a file `foo/bar.rb` with following content:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
module Foo
|
19
|
+
class Bar
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
So, when you start your app, "Bar is not a class" exception will be thrown. autoload_checker
|
25
|
+
detects conflict definitions and fix them by replacing `module Bar` with `class Bar`
|
26
|
+
in `foo/bar/baz.rb`.
|
27
|
+
|
28
|
+
# Usage
|
29
|
+
|
30
|
+
```
|
31
|
+
Usage: ./autoload_checker.rb [options]
|
32
|
+
-p, --path FILE [Mandatory] file or directory to check
|
33
|
+
-c, --correct Enable errors correction
|
34
|
+
-h, --help Prints this help
|
35
|
+
```
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
$:.unshift Pathname.new(File.expand_path(__dir__)).parent.join('lib')
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'namespace'
|
8
|
+
require 'autoload_checker'
|
9
|
+
|
10
|
+
Options = Struct.new(:dir, :correct)
|
11
|
+
|
12
|
+
options = Options.new(nil, false)
|
13
|
+
opt_parser = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: #{$0} [options]"
|
15
|
+
opts.on('-p FILE', '--path', String, '[Mandatory] file or directory to check') do |v|
|
16
|
+
options.dir = v
|
17
|
+
end
|
18
|
+
opts.on('-c', '--correct', TrueClass, 'Enable errors correction') { |v| options.correct = v }
|
19
|
+
opts.on('-h', '--help', 'Prints this help') do
|
20
|
+
puts opts
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
end
|
24
|
+
opt_parser.parse!
|
25
|
+
|
26
|
+
unless options.dir
|
27
|
+
puts opt_parser.help
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
autoload_checker = AutoloadChecker.new(path: options.dir, correct: options.correct)
|
32
|
+
autoload_checker.call
|
33
|
+
exit(1) if autoload_checker.fixes.any? && !options.correct
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AutoloadChecker
|
4
|
+
def initialize(path:, correct:, output: $stderr)
|
5
|
+
@path = path
|
6
|
+
@correct = correct
|
7
|
+
@output = output
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
validator.call
|
12
|
+
return if fixes.empty?
|
13
|
+
|
14
|
+
if correct?
|
15
|
+
fixes.each { |file, fixes| Namespace::Corrector.new(file: file, fixes: fixes).call }
|
16
|
+
else
|
17
|
+
validator.fixes.each do |_file, fixes|
|
18
|
+
fixes.each { |fix| @output.puts(fix.pretty_error) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fixes
|
24
|
+
validator.fixes
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def validator
|
30
|
+
@validator ||= Namespace::Validator.new(@path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def correct?
|
34
|
+
@correct
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal
|
2
|
+
|
3
|
+
class Namespace
|
4
|
+
class Corrector
|
5
|
+
def initialize(file:, fixes:)
|
6
|
+
@file = file
|
7
|
+
@fixes = fixes
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
content = File.read(@file)
|
12
|
+
@fixes.each do |fix|
|
13
|
+
content.gsub!(/#{fix.from} #{fix.const_name}/, "#{fix.to} #{fix.const_name}")
|
14
|
+
end
|
15
|
+
File.write(@file, content)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Namespace
|
4
|
+
class Fix
|
5
|
+
attr_reader :file, :const_name, :from, :to
|
6
|
+
|
7
|
+
def initialize(file:, const_name:, from:, to:)
|
8
|
+
@file = file
|
9
|
+
@const_name = const_name
|
10
|
+
@from = from
|
11
|
+
@to = to
|
12
|
+
end
|
13
|
+
|
14
|
+
def pretty_error
|
15
|
+
"#{@file}: #{const_name} must be a #{@to} not a #{@from}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ripper'
|
4
|
+
require 'namespace'
|
5
|
+
|
6
|
+
class Namespace
|
7
|
+
class NonRootAstError < StandardError; end
|
8
|
+
class ConstRefExpectedError < StandardError; end
|
9
|
+
|
10
|
+
class Parser
|
11
|
+
def initialize(file)
|
12
|
+
@file = file
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
@ast = File.open(@file) { |fd| Ripper.sexp(fd, @file) }
|
17
|
+
raise NonRootAstError, @file unless @ast[0] == :program
|
18
|
+
|
19
|
+
module_or_class_defs(@ast[1])
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def module_or_class_defs(ast)
|
25
|
+
ast.each_with_object({}) do |stmt, namespaces|
|
26
|
+
next if !stmt.is_a?(Array) || !%i[module class].include?(stmt[0])
|
27
|
+
|
28
|
+
type = stmt[0]
|
29
|
+
const_ref = stmt[1]
|
30
|
+
raise ConstRefExpectedError unless %i[const_ref const_path_ref].include?(const_ref[0])
|
31
|
+
|
32
|
+
next if const_ref[0] == :const_path_ref
|
33
|
+
|
34
|
+
name = const_ref[1][1]
|
35
|
+
pos = const_ref[1][2]
|
36
|
+
line = pos[0]
|
37
|
+
column = pos[1]
|
38
|
+
|
39
|
+
children = module_or_class_defs(module_or_class_body(stmt))
|
40
|
+
namespaces[name] = Namespace.new(
|
41
|
+
type: type, name: name, children: children, file: @file, line: line, column: column
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def module_or_class_body(ast)
|
47
|
+
index = ast.find_index { |stmt| stmt.is_a?(Array) && stmt[0] == :bodystmt }
|
48
|
+
ast[index][1]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'namespace/fix'
|
5
|
+
require 'namespace/parser'
|
6
|
+
|
7
|
+
class Namespace
|
8
|
+
class Validator
|
9
|
+
attr_reader :fixes, :namespaces
|
10
|
+
|
11
|
+
def initialize(root_dir)
|
12
|
+
@root_dir = root_dir
|
13
|
+
@namespaces = {}
|
14
|
+
@fixes = Hash.new { |hsh, k| hsh[k] = [] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
directories = [@root_dir]
|
19
|
+
new_directories = []
|
20
|
+
|
21
|
+
while directories.any?
|
22
|
+
dir = directories.pop
|
23
|
+
Pathname.glob(File.join(dir, '*')) do |file|
|
24
|
+
if file.directory?
|
25
|
+
new_directories << file
|
26
|
+
elsif file.extname == '.rb'
|
27
|
+
namespace = Namespace::Parser.new(file.to_s).call
|
28
|
+
add_namespace(namespace)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if directories.empty?
|
33
|
+
directories = new_directories
|
34
|
+
new_directories = []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def add_namespace(namespace)
|
42
|
+
merge_children(@namespaces, namespace)
|
43
|
+
end
|
44
|
+
|
45
|
+
def merge_children(children1, children2)
|
46
|
+
children2.each do |k, space2|
|
47
|
+
space1 = children1[k]
|
48
|
+
if !space1
|
49
|
+
if space2.class? && !space2.has_standalone_file? && space2.children.any?
|
50
|
+
@fixes[space2.file] << Fix.new(
|
51
|
+
file: space2.file, const_name: k, from: :class, to: :module
|
52
|
+
)
|
53
|
+
space2.type = :module
|
54
|
+
end
|
55
|
+
|
56
|
+
children1[k] = space2
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
if space1.type != space2.type
|
61
|
+
@fixes[space2.file] << Fix.new(
|
62
|
+
file: space2.file, const_name: k, from: space2.type, to: space1.type
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
merge_children(space1.children, space2.children)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/namespace.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'namespace/corrector'
|
5
|
+
require 'namespace/fix'
|
6
|
+
require 'namespace/parser'
|
7
|
+
require 'namespace/validator'
|
8
|
+
|
9
|
+
class Namespace
|
10
|
+
attr_reader :name, :children, :file, :line, :column
|
11
|
+
attr_accessor :type
|
12
|
+
|
13
|
+
def initialize(type:, name:, children:, file:, line:, column:)
|
14
|
+
@type = type
|
15
|
+
@name = name
|
16
|
+
@children = children
|
17
|
+
@file = file
|
18
|
+
@line = line
|
19
|
+
@column = column
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_standalone_file?
|
23
|
+
Pathname.new(@file).basename.to_s == to_snake_case(@name) + ".rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
def class?
|
27
|
+
@type == :class
|
28
|
+
end
|
29
|
+
|
30
|
+
def module?
|
31
|
+
@type == :module
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def to_snake_case(str)
|
37
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
38
|
+
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
39
|
+
.tr('-', '_')
|
40
|
+
.downcase
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: autoload-checker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- cyberfined
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
description: Checks for conflicts in class/module definitions and corrects them.
|
28
|
+
email:
|
29
|
+
- cyberfined@protonmail.com
|
30
|
+
executables:
|
31
|
+
- autoload_checker.rb
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- LICENSE
|
38
|
+
- README.md
|
39
|
+
- bin/autoload_checker.rb
|
40
|
+
- lib/autoload_checker.rb
|
41
|
+
- lib/namespace.rb
|
42
|
+
- lib/namespace/corrector.rb
|
43
|
+
- lib/namespace/fix.rb
|
44
|
+
- lib/namespace/parser.rb
|
45
|
+
- lib/namespace/validator.rb
|
46
|
+
homepage: https://github.com/cyberfined/autoload-checker
|
47
|
+
licenses:
|
48
|
+
- WTFPL
|
49
|
+
metadata: {}
|
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.5'
|
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.3.11
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: Checks for conflicts in class/module definitions and corrects them.
|
69
|
+
test_files: []
|