mudguard 0.1.3 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.rubocop.yml +3 -1
- data/Gemfile.lock +25 -23
- data/Guardfile +2 -0
- data/exe/mudguard +11 -3
- data/lib/mudguard.rb +2 -17
- data/lib/mudguard/application/application.rb +33 -0
- data/lib/mudguard/domain/const_visitor.rb +20 -0
- data/lib/mudguard/domain/consts.rb +81 -0
- data/lib/mudguard/domain/dependencies.rb +47 -0
- data/lib/mudguard/domain/dependency.rb +28 -0
- data/lib/mudguard/domain/dependency_visitor.rb +32 -0
- data/lib/mudguard/{error.rb → domain/error.rb} +3 -1
- data/lib/mudguard/domain/policies.rb +49 -0
- data/lib/mudguard/domain/source.rb +79 -0
- data/lib/mudguard/domain/source_policies.rb +8 -0
- data/lib/mudguard/domain/source_processor.rb +92 -0
- data/lib/mudguard/domain/texts.rb +42 -0
- data/lib/mudguard/infrastructure/cli/controller.rb +78 -0
- data/lib/mudguard/infrastructure/cli/notification_adapter.rb +26 -0
- data/lib/mudguard/infrastructure/cli/view.rb +14 -0
- data/lib/mudguard/infrastructure/persistence/policy_file.rb +45 -0
- data/lib/mudguard/infrastructure/persistence/project_repository.rb +31 -0
- data/lib/mudguard/infrastructure/persistence/ruby_files.rb +39 -0
- data/lib/mudguard/infrastructure/rake/task.rb +28 -0
- data/lib/mudguard/version.rb +1 -1
- data/mudguard.gemspec +2 -1
- metadata +38 -11
- data/lib/mudguard/policies.rb +0 -23
- data/lib/mudguard/policy_file.rb +0 -17
- data/lib/mudguard/ruby_analyser.rb +0 -74
- data/lib/mudguard/ruby_files.rb +0 -17
- data/lib/tasks/gem.rake +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6db3d7377ad8b0b1d13e59c306bd937bcb39a2b68bee5d0539a4dff17d460243
|
4
|
+
data.tar.gz: b46919284fea9ad09e6db895025c50eeff3577fc0fe32bbc3b5ad7aec0e4833f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68ed243c036a7bdbe9a5e3e5e5996c76006cbc7057f4f5aad78d43c011038a6de16496c61ce5bd9c88a63c678f6d501f53af17e18e931abb20d0bce351ce0585
|
7
|
+
data.tar.gz: 050fdc731cc7297f986f10ed1b39e95aa6e82bf2f36959d7fe6e322c9681b5322a8a0390b177ced26ecc5c09bea737e86b26645966e4d1b2612eee25d370a735
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mudguard (0.1.
|
4
|
+
mudguard (0.1.8)
|
5
5
|
parser (~> 2.7)
|
6
|
+
rake (~> 13.0)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
11
|
ast (2.4.0)
|
11
|
-
byebug (11.
|
12
|
+
byebug (11.1.3)
|
12
13
|
coderay (1.1.2)
|
13
14
|
diff-lcs (1.3)
|
14
15
|
ffi (1.12.2)
|
15
16
|
formatador (0.2.5)
|
16
|
-
guard (2.16.
|
17
|
+
guard (2.16.2)
|
17
18
|
formatador (>= 0.2.4)
|
18
19
|
listen (>= 2.7, < 4.0)
|
19
20
|
lumberjack (>= 1.0.12, < 2.0)
|
@@ -27,31 +28,30 @@ GEM
|
|
27
28
|
guard (~> 2.1)
|
28
29
|
guard-compat (~> 1.1)
|
29
30
|
rspec (>= 2.99.0, < 4.0)
|
30
|
-
jaro_winkler (1.5.4)
|
31
31
|
listen (3.2.1)
|
32
32
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
33
33
|
rb-inotify (~> 0.9, >= 0.9.10)
|
34
|
-
lumberjack (1.2.
|
35
|
-
method_source (0.
|
34
|
+
lumberjack (1.2.5)
|
35
|
+
method_source (1.0.0)
|
36
36
|
nenv (0.3.0)
|
37
37
|
notiffany (0.1.3)
|
38
38
|
nenv (~> 0.1)
|
39
39
|
shellany (~> 0.0)
|
40
40
|
parallel (1.19.1)
|
41
|
-
parser (2.7.
|
41
|
+
parser (2.7.1.3)
|
42
42
|
ast (~> 2.4.0)
|
43
|
-
pry (0.
|
44
|
-
coderay (~> 1.1
|
45
|
-
method_source (~>
|
46
|
-
pry-byebug (3.
|
43
|
+
pry (0.13.1)
|
44
|
+
coderay (~> 1.1)
|
45
|
+
method_source (~> 1.0)
|
46
|
+
pry-byebug (3.9.0)
|
47
47
|
byebug (~> 11.0)
|
48
|
-
pry (~> 0.
|
49
|
-
pry-doc (1.
|
48
|
+
pry (~> 0.13.0)
|
49
|
+
pry-doc (1.1.0)
|
50
50
|
pry (~> 0.11)
|
51
51
|
yard (~> 0.9.11)
|
52
52
|
rainbow (3.0.0)
|
53
53
|
rake (13.0.1)
|
54
|
-
rb-fsevent (0.10.
|
54
|
+
rb-fsevent (0.10.4)
|
55
55
|
rb-inotify (0.10.1)
|
56
56
|
ffi (~> 1.0)
|
57
57
|
rexml (3.2.4)
|
@@ -59,28 +59,30 @@ GEM
|
|
59
59
|
rspec-core (~> 3.9.0)
|
60
60
|
rspec-expectations (~> 3.9.0)
|
61
61
|
rspec-mocks (~> 3.9.0)
|
62
|
-
rspec-core (3.9.
|
63
|
-
rspec-support (~> 3.9.
|
64
|
-
rspec-expectations (3.9.
|
62
|
+
rspec-core (3.9.2)
|
63
|
+
rspec-support (~> 3.9.3)
|
64
|
+
rspec-expectations (3.9.2)
|
65
65
|
diff-lcs (>= 1.2.0, < 2.0)
|
66
66
|
rspec-support (~> 3.9.0)
|
67
67
|
rspec-mocks (3.9.1)
|
68
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
69
69
|
rspec-support (~> 3.9.0)
|
70
|
-
rspec-support (3.9.
|
71
|
-
rubocop (0.
|
72
|
-
jaro_winkler (~> 1.5.1)
|
70
|
+
rspec-support (3.9.3)
|
71
|
+
rubocop (0.84.0)
|
73
72
|
parallel (~> 1.10)
|
74
73
|
parser (>= 2.7.0.1)
|
75
74
|
rainbow (>= 2.2.2, < 4.0)
|
76
75
|
rexml
|
76
|
+
rubocop-ast (>= 0.0.3)
|
77
77
|
ruby-progressbar (~> 1.7)
|
78
|
-
unicode-display_width (>= 1.4.0, <
|
78
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
79
|
+
rubocop-ast (0.0.3)
|
80
|
+
parser (>= 2.7.0.1)
|
79
81
|
ruby-progressbar (1.10.1)
|
80
82
|
shellany (0.0.1)
|
81
83
|
thor (1.0.1)
|
82
|
-
unicode-display_width (1.
|
83
|
-
yard (0.9.
|
84
|
+
unicode-display_width (1.7.0)
|
85
|
+
yard (0.9.25)
|
84
86
|
|
85
87
|
PLATFORMS
|
86
88
|
ruby
|
data/Guardfile
CHANGED
@@ -8,7 +8,9 @@ rspec_options = {
|
|
8
8
|
|
9
9
|
guard "rspec", rspec_options do
|
10
10
|
watch(%r{^spec/.+_spec\.rb$})
|
11
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
11
12
|
watch(%r{^lib/mudguard/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
12
13
|
watch(%r{^lib/mudguard/(.+)\.rb$}) { |m| Dir.glob("spec/lib/#{m[1]}_*.rb") }
|
13
14
|
watch("spec/spec_helper.rb") { "spec" }
|
15
|
+
watch(%r{^exe\/mudguard$}) { |_| "spec/exe/mudguard_spec.rb" }
|
14
16
|
end
|
data/exe/mudguard
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require "mudguard/infrastructure/cli/view"
|
5
|
+
require "mudguard/infrastructure/cli/controller"
|
6
|
+
|
7
|
+
# Checks the dependencies of a ruby project
|
8
|
+
module Mudguard
|
9
|
+
view = Mudguard::Infrastructure::Cli::View.new
|
10
|
+
parser = Mudguard::Infrastructure::Cli::Controller.new(view: view)
|
11
|
+
ok = parser.parse!(ARGV)
|
12
|
+
exit(ok ? 0 : 1)
|
13
|
+
end
|
data/lib/mudguard.rb
CHANGED
@@ -1,19 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require_relative "mudguard/
|
5
|
-
require_relative "mudguard/ruby_files"
|
6
|
-
|
7
|
-
# Contains methods to check if your project is a bit muddy
|
8
|
-
module Mudguard
|
9
|
-
class << self
|
10
|
-
def check(project_path)
|
11
|
-
policy_file_path = File.expand_path("MudguardFile", project_path)
|
12
|
-
policies = PolicyFile.read(policy_file_path)
|
13
|
-
|
14
|
-
files = RubyFiles.all(project_path).map { |f| File.read(f) }
|
15
|
-
|
16
|
-
policies.check(files)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
3
|
+
require_relative "./mudguard/version"
|
4
|
+
require_relative "./mudguard/application/application"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../infrastructure/persistence/project_repository"
|
4
|
+
require_relative "../domain/policies"
|
5
|
+
|
6
|
+
# Contains methods to check if your project is a bit muddy
|
7
|
+
module Mudguard
|
8
|
+
# API to mudguard
|
9
|
+
module Application
|
10
|
+
class << self
|
11
|
+
def check(project_path, notification)
|
12
|
+
create_policies(project_path) do |policies|
|
13
|
+
policies.check(notification)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def print_allowed_dependencies(project_path, notification)
|
18
|
+
create_policies(project_path) do |policies|
|
19
|
+
policies.print_allowed_dependencies(notification)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def create_policies(project_path)
|
26
|
+
repo = Infrastructure::Persistence::ProjectRepository
|
27
|
+
source_policies = repo.load_source_policies(project_path)
|
28
|
+
policies = Domain::Policies.new(source_policies: source_policies)
|
29
|
+
yield policies
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Domain
|
5
|
+
# Transforms AST-Nodes into Strings denoting consts
|
6
|
+
class ConstVisitor
|
7
|
+
def initialize
|
8
|
+
@consts = []
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :consts
|
12
|
+
|
13
|
+
def visit_dependency(_, __, ___); end # rubocop:disable Naming/MethodParameterName
|
14
|
+
|
15
|
+
def visit_const_declaration(_location, const_name, module_name)
|
16
|
+
@consts << "#{module_name}#{const_name}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "error"
|
4
|
+
|
5
|
+
module Mudguard
|
6
|
+
module Domain
|
7
|
+
# Knows all constants of the project
|
8
|
+
class Consts
|
9
|
+
def initialize(sources:)
|
10
|
+
@consts = sources
|
11
|
+
.flat_map(&:find_consts)
|
12
|
+
.each_with_object(Hash.new { |h, k| h[k] = {} }) do |c, a|
|
13
|
+
path = split_hierarchy(c)
|
14
|
+
const_name = path.last
|
15
|
+
module_names = path.take(path.count - 1)
|
16
|
+
sub_module = module_names.reduce(a) { |h, m| h[m] }
|
17
|
+
sub_module[const_name] = {} unless sub_module.key?(const_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def resolve(module_name, const_name)
|
22
|
+
raise Error, "const_name is undefined" if const_name.empty?
|
23
|
+
|
24
|
+
path = split_hierarchy(module_name)
|
25
|
+
if module_name.empty?
|
26
|
+
# not in a module therefor const can only be defined in the root module (::)
|
27
|
+
qualified_path(const_name)
|
28
|
+
else
|
29
|
+
# analyse module hierarchy to find fully qualified const name
|
30
|
+
# resolve_in_modules(const_name, path)
|
31
|
+
const_path = const_name.split(SEPARATOR).drop(1)
|
32
|
+
find_const_deeper("", path, @consts, const_path) || qualified_path(const_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
SEPARATOR = "::"
|
39
|
+
|
40
|
+
def find_const_deeper(current_module, remaining_modules, consts, const_path)
|
41
|
+
return if consts.nil?
|
42
|
+
|
43
|
+
if remaining_modules.any?
|
44
|
+
# Move deeper toward target module
|
45
|
+
next_module = remaining_modules.first
|
46
|
+
next_remaining = remaining_modules.drop(1)
|
47
|
+
next_consts = consts[next_module]
|
48
|
+
found_const = find_const_deeper(next_module, next_remaining, next_consts, const_path)
|
49
|
+
return join_path(current_module, found_const) if found_const
|
50
|
+
end
|
51
|
+
|
52
|
+
find_const(current_module, consts, const_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_const(current_module, consts, const_path)
|
56
|
+
const_name = const_path.first
|
57
|
+
if const_path.length == 1 && consts.key?(const_name)
|
58
|
+
# const defined in current_module
|
59
|
+
return join_path(current_module, const_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
# backward search (along const_path only)
|
63
|
+
next_path = const_path.drop(1)
|
64
|
+
found_const = find_const_deeper(const_name, next_path, consts[const_name], next_path)
|
65
|
+
found_const ? join_path(current_module, found_const) : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def join_path(module_name, const_name)
|
69
|
+
"#{module_name}#{SEPARATOR}#{const_name}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def qualified_path(const_name)
|
73
|
+
const_name =~ /^#{SEPARATOR}/ ? const_name : "#{SEPARATOR}#{const_name}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def split_hierarchy(module_name)
|
77
|
+
module_name.split(SEPARATOR).drop(1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "texts"
|
4
|
+
|
5
|
+
module Mudguard
|
6
|
+
module Domain
|
7
|
+
# Executes operation on a set of dependencies
|
8
|
+
class Dependencies
|
9
|
+
include Texts
|
10
|
+
|
11
|
+
def initialize(policies:, notification:)
|
12
|
+
@policies = policies.map { |p| /^#{p}/x }
|
13
|
+
@notification = notification
|
14
|
+
end
|
15
|
+
|
16
|
+
def check(dependencies)
|
17
|
+
select_dependencies(dependencies) do |dependency, is_allowed|
|
18
|
+
add_message(dependency, dependency_not_allowed(dependency.dependency)) unless is_allowed
|
19
|
+
!is_allowed
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def print_allowed(dependencies)
|
24
|
+
select_dependencies(dependencies) do |dependency, is_allowed|
|
25
|
+
add_message(dependency, dependency_allowed(dependency.dependency)) if is_allowed
|
26
|
+
is_allowed
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def add_message(dependency, message)
|
33
|
+
@notification.add(dependency.location, message)
|
34
|
+
end
|
35
|
+
|
36
|
+
def select_dependencies(dependencies)
|
37
|
+
dependencies.select do |dependency|
|
38
|
+
yield dependency, dependency_allowed?(dependency)
|
39
|
+
end.count
|
40
|
+
end
|
41
|
+
|
42
|
+
def dependency_allowed?(dependency)
|
43
|
+
@policies.any? { |p| dependency.match(p) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Domain
|
5
|
+
# A Dependency between Modules
|
6
|
+
class Dependency
|
7
|
+
attr_reader :location, :dependency
|
8
|
+
|
9
|
+
def initialize(location: nil, dependency:)
|
10
|
+
@location = location
|
11
|
+
@dependency = dependency
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"{#{@location}, #{@dependency}}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def match(policy)
|
19
|
+
@dependency.match(policy)
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
@location == other.instance_eval { @location } &&
|
24
|
+
@dependency == other.instance_eval { @dependency }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Domain
|
5
|
+
# Transforms AST-Nodes into Dependencies
|
6
|
+
class DependencyVisitor
|
7
|
+
def initialize(consts:)
|
8
|
+
@consts = consts
|
9
|
+
@dependencies = []
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :dependencies
|
13
|
+
|
14
|
+
def visit_dependency(location, const_name, module_name)
|
15
|
+
qualified_const_name = @consts.resolve(module_name, const_name)
|
16
|
+
return [] unless qualified_const_name&.include?("::")
|
17
|
+
|
18
|
+
dependency = if module_name.empty?
|
19
|
+
"->#{qualified_const_name}"
|
20
|
+
else
|
21
|
+
"#{module_name}->#{qualified_const_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
@dependencies << Dependency.new(location: location, dependency: dependency)
|
25
|
+
end
|
26
|
+
|
27
|
+
# rubocop:disable Naming/MethodParameterName
|
28
|
+
def visit_const_declaration(_, __, ___); end
|
29
|
+
# rubocop:enable Naming/MethodParameterName
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "dependencies"
|
4
|
+
require_relative "texts"
|
5
|
+
require_relative "consts"
|
6
|
+
|
7
|
+
module Mudguard
|
8
|
+
module Domain
|
9
|
+
# Contains the policies to be enforced
|
10
|
+
class Policies
|
11
|
+
include Texts
|
12
|
+
|
13
|
+
def initialize(source_policies: [])
|
14
|
+
@source_policies = source_policies
|
15
|
+
end
|
16
|
+
|
17
|
+
def check(notification)
|
18
|
+
result = analyse(:check, notification)
|
19
|
+
|
20
|
+
count = result[:sources_count]
|
21
|
+
violations = result[:analyser_count]
|
22
|
+
|
23
|
+
notification.add(nil, summary(count, violations))
|
24
|
+
violations.zero?
|
25
|
+
end
|
26
|
+
|
27
|
+
def print_allowed_dependencies(notification)
|
28
|
+
result = analyse(:print_allowed, notification)
|
29
|
+
|
30
|
+
count = result[:sources_count]
|
31
|
+
violations = result[:analyser_count]
|
32
|
+
|
33
|
+
notification.add(nil, dependency_summary(count, violations))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def analyse(method, notification)
|
39
|
+
consts = Consts.new(sources: @source_policies.map(&:source))
|
40
|
+
@source_policies.each_with_object(sources_count: 0, analyser_count: 0) do |sp, result|
|
41
|
+
analyser = Dependencies.new(policies: sp.policies, notification: notification)
|
42
|
+
dependencies = sp.source.find_mod_dependencies(consts)
|
43
|
+
result[:sources_count] += 1
|
44
|
+
result[:analyser_count] += analyser.send(method, dependencies)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "parser/current"
|
4
|
+
require_relative "dependency"
|
5
|
+
require_relative "dependency_visitor"
|
6
|
+
require_relative "const_visitor"
|
7
|
+
require_relative "error"
|
8
|
+
require_relative "source_processor"
|
9
|
+
|
10
|
+
module Mudguard
|
11
|
+
module Domain
|
12
|
+
# Represents a Ruby source file
|
13
|
+
class Source
|
14
|
+
def initialize(location:, code_loader: -> { "" })
|
15
|
+
@code_loader = code_loader
|
16
|
+
@location = location
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
@location == other.instance_eval { @location }
|
21
|
+
end
|
22
|
+
|
23
|
+
def hash
|
24
|
+
@location.hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def eql?(other)
|
28
|
+
@location.eql?(other.instance_eval { @location })
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
@location
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_mod_dependencies(consts)
|
36
|
+
visitor = DependencyVisitor.new(consts: consts)
|
37
|
+
visit_ast(visitor)
|
38
|
+
visitor.dependencies
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_consts
|
42
|
+
visitor = ConstVisitor.new
|
43
|
+
visit_ast(visitor)
|
44
|
+
visitor.consts
|
45
|
+
end
|
46
|
+
|
47
|
+
def location?(glob)
|
48
|
+
@location == glob
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
SYNTAX_ERROR = "error"
|
54
|
+
|
55
|
+
def ast
|
56
|
+
@ast ||= create_ast
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_ast
|
60
|
+
begin
|
61
|
+
root = Parser::CurrentRuby.parse(code)
|
62
|
+
rescue Parser::SyntaxError
|
63
|
+
return SYNTAX_ERROR
|
64
|
+
end
|
65
|
+
root.nil? ? SYNTAX_ERROR : root
|
66
|
+
end
|
67
|
+
|
68
|
+
def code
|
69
|
+
@code ||= @code_loader.call
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_ast(visitor)
|
73
|
+
return if ast == SYNTAX_ERROR
|
74
|
+
|
75
|
+
SourceProcessor.new(location: @location).process(ast, visitor)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Domain
|
5
|
+
# Processes the interesting parts of the Ast and forwards selected nodes to the visitors
|
6
|
+
class SourceProcessor
|
7
|
+
def initialize(location:)
|
8
|
+
@location = location
|
9
|
+
end
|
10
|
+
|
11
|
+
def process(node, visitor)
|
12
|
+
process_node(node, visitor, "")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def process_node(node, visitor, module_name) # rubocop:disable Metrics/MethodLength
|
18
|
+
case node
|
19
|
+
when type?(:module)
|
20
|
+
process_module(node, visitor, module_name)
|
21
|
+
when type?(:class)
|
22
|
+
process_module(node, visitor, module_name)
|
23
|
+
when type?(:const)
|
24
|
+
process_const(node, visitor, module_name)
|
25
|
+
when type?(:casgn)
|
26
|
+
process_const_assignment(node, visitor, module_name)
|
27
|
+
else
|
28
|
+
ignore_and_continue(node, visitor, module_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_const_assignment(node, visitor, module_name)
|
33
|
+
is_explicit, is_static, const_name = find_const_name(node.children)
|
34
|
+
return unless is_static
|
35
|
+
|
36
|
+
visitor.visit_const_declaration(describe_location(node), const_name,
|
37
|
+
is_explicit ? "" : module_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_const(node, visitor, module_name)
|
41
|
+
is_explicit, is_static, const_name = find_const_name(node.children)
|
42
|
+
return unless is_static
|
43
|
+
|
44
|
+
visitor.visit_dependency(describe_location(node), const_name,
|
45
|
+
is_explicit ? "" : module_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def describe_location(node)
|
49
|
+
"#{@location}:#{node.location.line}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def ignore_and_continue(node, visitor, module_name)
|
53
|
+
return unless node.respond_to?(:children)
|
54
|
+
|
55
|
+
node.children.flat_map { |c| process_node(c, visitor, module_name) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def process_module(node, visitor, module_name)
|
59
|
+
is_explicit, is_static, const_name = find_const_name(node.children[0].children)
|
60
|
+
return unless is_static
|
61
|
+
|
62
|
+
visitor.visit_const_declaration(describe_location(node), const_name, module_name)
|
63
|
+
|
64
|
+
module_name = "#{is_explicit ? '' : module_name}#{const_name}"
|
65
|
+
node.children.drop(1).reject(&:nil?).each do |child_node|
|
66
|
+
process_node(child_node, visitor, module_name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_const_name(children)
|
71
|
+
return [false, nil] if children.nil? || children.empty?
|
72
|
+
|
73
|
+
first_child = children[0]
|
74
|
+
is_explicit, is_static = find_const_type(first_child)
|
75
|
+
first_child_children = first_child.respond_to?(:children) ? first_child.children : nil
|
76
|
+
_, __, module_name = find_const_name(first_child_children)
|
77
|
+
const_name = children[1].to_s
|
78
|
+
[is_explicit, is_static, "#{module_name}::#{const_name}"]
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_const_type(first_child)
|
82
|
+
is_explicit = type?(:cbase).call(first_child)
|
83
|
+
is_static = is_explicit || first_child.nil? || type?(:const).call(first_child)
|
84
|
+
[is_explicit, is_static]
|
85
|
+
end
|
86
|
+
|
87
|
+
def type?(type)
|
88
|
+
->(n) { n.respond_to?(:type) && n.type == type }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Domain
|
5
|
+
# Builds texts to be displayed to a user
|
6
|
+
module Texts
|
7
|
+
def summary(file_count, violation_count)
|
8
|
+
files = pluralize("file", file_count)
|
9
|
+
"#{file_count} #{files} inspected, #{count(violation_count)}\
|
10
|
+
bad #{dependency(violation_count)} detected"
|
11
|
+
end
|
12
|
+
|
13
|
+
def dependency_not_allowed(dependency)
|
14
|
+
"#{dependency} not allowed"
|
15
|
+
end
|
16
|
+
|
17
|
+
def dependency_summary(file_count, dependency_count)
|
18
|
+
files = pluralize("file", file_count)
|
19
|
+
"#{file_count} #{files} inspected, #{count(dependency_count)}\
|
20
|
+
good #{dependency(dependency_count)} detected"
|
21
|
+
end
|
22
|
+
|
23
|
+
def dependency_allowed(dependency)
|
24
|
+
dependency.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def dependency(count)
|
30
|
+
count <= 1 ? "dependency" : "dependencies"
|
31
|
+
end
|
32
|
+
|
33
|
+
def count(count)
|
34
|
+
count.zero? ? "no" : count.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def pluralize(word, count)
|
38
|
+
count == 1 ? word : "#{word}s"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "mudguard/application/application"
|
5
|
+
require_relative "notification_adapter"
|
6
|
+
|
7
|
+
module Mudguard
|
8
|
+
module Infrastructure
|
9
|
+
module Cli
|
10
|
+
# Parses the cli arguments
|
11
|
+
class Controller
|
12
|
+
def initialize(view:) # rubocop:disable Metrics/MethodLength
|
13
|
+
@cmd = :analyse
|
14
|
+
@view = view
|
15
|
+
@display_opts = {
|
16
|
+
view: view,
|
17
|
+
compressed: false
|
18
|
+
}
|
19
|
+
@parser = ::OptionParser.new do |opts|
|
20
|
+
opts.banner = "Usage: mudguard [options] [directory]"
|
21
|
+
opts.on("-h", "--help", "Prints this help") do
|
22
|
+
@cmd = :help
|
23
|
+
end
|
24
|
+
opts.on("-p", "--print", "Prints all allowed dependencies") do
|
25
|
+
@cmd = :print_allowed
|
26
|
+
end
|
27
|
+
opts.on("-c", "--compressed", "Omits printing the same dependency more than once") do
|
28
|
+
@display_opts[:compressed] = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse!(argv) # rubocop:disable Metrics/MethodLength
|
34
|
+
directories = @parser.parse!(argv)
|
35
|
+
|
36
|
+
case @cmd
|
37
|
+
when :print_allowed
|
38
|
+
print_allowed_dependencies(directories)
|
39
|
+
when :help
|
40
|
+
help
|
41
|
+
when :analyse
|
42
|
+
check_dependencies(directories)
|
43
|
+
else
|
44
|
+
raise StandardError, "unknown command #{@cmd}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def help
|
51
|
+
@view.print(@parser.to_s)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_dependencies(directories)
|
56
|
+
yield_directories(directories) do |directory, notification|
|
57
|
+
Mudguard::Application.check(directory, notification)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def print_allowed_dependencies(directories)
|
62
|
+
yield_directories(directories) do |directory, notification|
|
63
|
+
Mudguard::Application.print_allowed_dependencies(directory, notification)
|
64
|
+
true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def yield_directories(directories)
|
69
|
+
notification = NotificationAdapter.new(**@display_opts)
|
70
|
+
directories = [Dir.pwd] if directories.empty?
|
71
|
+
directories.all? do |directory|
|
72
|
+
yield directory, notification
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mudguard
|
4
|
+
module Infrastructure
|
5
|
+
module Cli
|
6
|
+
# Forwards Notification to the view for printing
|
7
|
+
class NotificationAdapter
|
8
|
+
def initialize(view:, compressed: false)
|
9
|
+
@view = view
|
10
|
+
@compressed = compressed
|
11
|
+
@printed_texts = Set.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(location, text)
|
15
|
+
text = if location.nil? || @compressed
|
16
|
+
text
|
17
|
+
else
|
18
|
+
"#{location} #{text}"
|
19
|
+
end
|
20
|
+
@view.print(text) unless @printed_texts.include?(text)
|
21
|
+
@printed_texts << text
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require_relative "../../domain/policies"
|
5
|
+
require_relative "../../domain/error"
|
6
|
+
|
7
|
+
module Mudguard
|
8
|
+
module Infrastructure
|
9
|
+
module Persistence
|
10
|
+
# A file containing the Mudguard-Policies
|
11
|
+
class PolicyFile
|
12
|
+
class << self
|
13
|
+
def read(project_path)
|
14
|
+
policy_file = File.join(project_path, ".mudguard.yml")
|
15
|
+
policy_exists = File.exist?(policy_file)
|
16
|
+
|
17
|
+
unless policy_exists
|
18
|
+
raise Mudguard::Domain::Error, "expected policy file #{policy_file} doesn't exists"
|
19
|
+
end
|
20
|
+
|
21
|
+
read_yml(policy_file)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def read_yml(policy_file)
|
27
|
+
yaml_file = File.read(policy_file)
|
28
|
+
yaml = YAML.safe_load(yaml_file, [Symbol], [], policy_file) || {}
|
29
|
+
yaml.transform_values { |value| (value || []).map(&method(:unsymbolize)) }
|
30
|
+
rescue Psych::SyntaxError => e
|
31
|
+
raise Mudguard::Domain::Error, "#{policy_file} is invalid (#{e.message})"
|
32
|
+
end
|
33
|
+
|
34
|
+
def unsymbolize(dependency)
|
35
|
+
if dependency.is_a?(Symbol)
|
36
|
+
":#{dependency}"
|
37
|
+
else
|
38
|
+
dependency
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "policy_file"
|
4
|
+
require_relative "ruby_files"
|
5
|
+
require_relative "../../domain/source_policies"
|
6
|
+
|
7
|
+
module Mudguard
|
8
|
+
module Infrastructure
|
9
|
+
module Persistence
|
10
|
+
# Provides access to the persisted source and policies
|
11
|
+
class ProjectRepository
|
12
|
+
class << self
|
13
|
+
def load_source_policies(project_path)
|
14
|
+
file = PolicyFile.read(project_path)
|
15
|
+
scopes = file.flat_map do |patterns, policies|
|
16
|
+
sources = RubyFiles.select(project_path, patterns: [patterns])
|
17
|
+
sources.flat_map { |s| { source: s, policies: policies } }
|
18
|
+
end
|
19
|
+
|
20
|
+
sources = scopes.group_by { |e| e[:source] }
|
21
|
+
|
22
|
+
sources.map do |source, group|
|
23
|
+
policies = group.flat_map { |r| r[:policies] }.uniq
|
24
|
+
Domain::SourcePolicies.new(source: source, policies: policies)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "mudguard/domain/source"
|
5
|
+
|
6
|
+
module Mudguard
|
7
|
+
module Infrastructure
|
8
|
+
module Persistence
|
9
|
+
# Provides access to all ruby-files of a project
|
10
|
+
class RubyFiles
|
11
|
+
class << self
|
12
|
+
def select(project_path, patterns: nil)
|
13
|
+
project_exists = Dir.exist?(project_path)
|
14
|
+
|
15
|
+
unless project_exists
|
16
|
+
raise Mudguard::Domain::Error, "expected project #{project_path} doesn't exists"
|
17
|
+
end
|
18
|
+
|
19
|
+
patterns = [File.join("**", "*.rb")] if patterns.nil?
|
20
|
+
enumerate_files(project_path, patterns)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def enumerate_files(project_path, patterns)
|
26
|
+
project_path_name = Pathname.new(project_path)
|
27
|
+
ruby_files = patterns.map { |p| File.join(project_path, p) }
|
28
|
+
Dir.glob(ruby_files).map do |f|
|
29
|
+
file_path_name = Pathname.new(f)
|
30
|
+
diff_path = file_path_name.relative_path_from(project_path_name).to_s
|
31
|
+
Mudguard::Domain::Source.new(location: File.join("./", diff_path),
|
32
|
+
code_loader: -> { File.read(f) })
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rake"
|
4
|
+
require "rake/tasklib"
|
5
|
+
require_relative "../../application/application"
|
6
|
+
require_relative "../../infrastructure/cli/notification_adapter"
|
7
|
+
require_relative "../../infrastructure/cli/view"
|
8
|
+
|
9
|
+
module Mudguard
|
10
|
+
module Infrastructure
|
11
|
+
module Rake
|
12
|
+
# Provides Mudguard Rake Tasks
|
13
|
+
class Task < ::Rake::TaskLib
|
14
|
+
def initialize(project_dir: Dir.pwd)
|
15
|
+
@project_dir = project_dir
|
16
|
+
|
17
|
+
desc "Run Mudguard"
|
18
|
+
task(:mudguard) do
|
19
|
+
view = Mudguard::Infrastructure::Cli::View.new
|
20
|
+
notification = Mudguard::Infrastructure::Cli::NotificationAdapter.new(view: view)
|
21
|
+
ok = Application.check(@project_dir, notification)
|
22
|
+
abort unless ok
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/mudguard/version.rb
CHANGED
data/mudguard.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
|
13
13
|
spec.summary = "mudguard helps your ruby project not becoming a "\
|
14
14
|
"'Big ball of mud'"
|
15
|
-
spec.homepage = "https://
|
15
|
+
spec.homepage = "https://github.com/Enceradeira/mudguard"
|
16
16
|
spec.license = "MIT"
|
17
17
|
|
18
18
|
spec.metadata["homepage_uri"] = spec.homepage
|
@@ -40,4 +40,5 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.add_development_dependency "rubocop", "~>0.80"
|
41
41
|
|
42
42
|
spec.add_dependency "parser", "~>2.7"
|
43
|
+
spec.add_dependency "rake", "~> 13.0"
|
43
44
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mudguard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jorg Jenni
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '2.7'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rake
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '13.0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '13.0'
|
153
167
|
description:
|
154
168
|
email:
|
155
169
|
- jorg.jenni@jennius.co.uk
|
@@ -171,23 +185,36 @@ files:
|
|
171
185
|
- Rakefile
|
172
186
|
- exe/mudguard
|
173
187
|
- lib/mudguard.rb
|
174
|
-
- lib/mudguard/
|
175
|
-
- lib/mudguard/
|
176
|
-
- lib/mudguard/
|
177
|
-
- lib/mudguard/
|
178
|
-
- lib/mudguard/
|
188
|
+
- lib/mudguard/application/application.rb
|
189
|
+
- lib/mudguard/domain/const_visitor.rb
|
190
|
+
- lib/mudguard/domain/consts.rb
|
191
|
+
- lib/mudguard/domain/dependencies.rb
|
192
|
+
- lib/mudguard/domain/dependency.rb
|
193
|
+
- lib/mudguard/domain/dependency_visitor.rb
|
194
|
+
- lib/mudguard/domain/error.rb
|
195
|
+
- lib/mudguard/domain/policies.rb
|
196
|
+
- lib/mudguard/domain/source.rb
|
197
|
+
- lib/mudguard/domain/source_policies.rb
|
198
|
+
- lib/mudguard/domain/source_processor.rb
|
199
|
+
- lib/mudguard/domain/texts.rb
|
200
|
+
- lib/mudguard/infrastructure/cli/controller.rb
|
201
|
+
- lib/mudguard/infrastructure/cli/notification_adapter.rb
|
202
|
+
- lib/mudguard/infrastructure/cli/view.rb
|
203
|
+
- lib/mudguard/infrastructure/persistence/policy_file.rb
|
204
|
+
- lib/mudguard/infrastructure/persistence/project_repository.rb
|
205
|
+
- lib/mudguard/infrastructure/persistence/ruby_files.rb
|
206
|
+
- lib/mudguard/infrastructure/rake/task.rb
|
179
207
|
- lib/mudguard/version.rb
|
180
|
-
- lib/tasks/gem.rake
|
181
208
|
- lib/tasks/rubocop.rake
|
182
209
|
- lib/tasks/test.rake
|
183
210
|
- mudguard.gemspec
|
184
211
|
- pkg/mudguard-0.1.0.gem
|
185
|
-
homepage: https://
|
212
|
+
homepage: https://github.com/Enceradeira/mudguard
|
186
213
|
licenses:
|
187
214
|
- MIT
|
188
215
|
metadata:
|
189
|
-
homepage_uri: https://
|
190
|
-
source_code_uri: https://
|
216
|
+
homepage_uri: https://github.com/Enceradeira/mudguard
|
217
|
+
source_code_uri: https://github.com/Enceradeira/mudguard
|
191
218
|
post_install_message:
|
192
219
|
rdoc_options: []
|
193
220
|
require_paths:
|
data/lib/mudguard/policies.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "ruby_analyser"
|
4
|
-
|
5
|
-
module Mudguard
|
6
|
-
# Contains the policies to be enforced
|
7
|
-
class Policies
|
8
|
-
def initialize(policies: [])
|
9
|
-
@policies = policies.map { |l| l.gsub(/\s/, "") }.map { |p| /^#{p}/ }
|
10
|
-
end
|
11
|
-
|
12
|
-
def check(sources)
|
13
|
-
sources.all? do |source|
|
14
|
-
dependencies = RubyAnalyser.find_mod_dependencies(source)
|
15
|
-
dependencies.all? do |d|
|
16
|
-
@policies.any? do |p|
|
17
|
-
d.match?(p)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/mudguard/policy_file.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "policies"
|
4
|
-
require_relative "error"
|
5
|
-
|
6
|
-
module Mudguard
|
7
|
-
# A file containing the Mudguard-Policies
|
8
|
-
class PolicyFile
|
9
|
-
def self.read(policy_file)
|
10
|
-
policy_exists = File.exist?(policy_file)
|
11
|
-
|
12
|
-
raise Error, "expected policy file #{policy_file} doesn't exists" unless policy_exists
|
13
|
-
|
14
|
-
Policies.new(policies: File.readlines(policy_file))
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "parser/current"
|
4
|
-
|
5
|
-
module Mudguard
|
6
|
-
# Analyses Ruby-Source and returns extracted information
|
7
|
-
class RubyAnalyser
|
8
|
-
class << self
|
9
|
-
def find_mod_dependencies(source)
|
10
|
-
begin
|
11
|
-
root = Parser::CurrentRuby.parse(source)
|
12
|
-
rescue Parser::SyntaxError
|
13
|
-
return []
|
14
|
-
end
|
15
|
-
return [] if root.nil?
|
16
|
-
|
17
|
-
process(root)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def process(node, module_name = "")
|
23
|
-
case node
|
24
|
-
when type?(:module)
|
25
|
-
process_module(node.children)
|
26
|
-
when type?(:class)
|
27
|
-
process_class(node.children, module_name)
|
28
|
-
when type?(:const)
|
29
|
-
["#{module_name}->#{find_const_name(node.children)}"]
|
30
|
-
else
|
31
|
-
ignore_and_continue(node, module_name)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def ignore_and_continue(node, module_name)
|
36
|
-
case node
|
37
|
-
when children?
|
38
|
-
node.children.flat_map { |c| process(c, module_name) }
|
39
|
-
else
|
40
|
-
[]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def process_module(children)
|
45
|
-
module_name = find_const_name(children[0].children)
|
46
|
-
process(children[1], module_name)
|
47
|
-
end
|
48
|
-
|
49
|
-
def process_class(children, module_name)
|
50
|
-
process(children[1], module_name)
|
51
|
-
end
|
52
|
-
|
53
|
-
def find_const_name(children)
|
54
|
-
return nil if children.nil?
|
55
|
-
|
56
|
-
module_name = find_const_name(children[0]&.children)
|
57
|
-
const_name = children[1].to_s
|
58
|
-
if module_name.nil?
|
59
|
-
const_name
|
60
|
-
else
|
61
|
-
"#{module_name}::#{const_name}"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def children?
|
66
|
-
->(n) { n.respond_to?(:children) }
|
67
|
-
end
|
68
|
-
|
69
|
-
def type?(type)
|
70
|
-
->(n) { n.respond_to?(:type) && n.type == type }
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/mudguard/ruby_files.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mudguard
|
4
|
-
# Provides access to all ruby-files of a project
|
5
|
-
class RubyFiles
|
6
|
-
class << self
|
7
|
-
def all(project_path)
|
8
|
-
project_exists = Dir.exist?(project_path)
|
9
|
-
|
10
|
-
raise Error, "expected project #{project_path} doesn't exists" unless project_exists
|
11
|
-
|
12
|
-
ruby_files = File.join(project_path, "**", "*.rb")
|
13
|
-
Dir.glob(ruby_files).lazy
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/tasks/gem.rake
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rspec/core/rake_task"
|
4
|
-
require "mudguard/version"
|
5
|
-
|
6
|
-
namespace :gem do
|
7
|
-
desc "Publishes the gem on rubygems.org"
|
8
|
-
task publish: "gem:build" do
|
9
|
-
exec("gem push mudguard-#{Mudguard::VERSION}.gem")
|
10
|
-
end
|
11
|
-
desc "Builds the gem"
|
12
|
-
task :build do
|
13
|
-
exec("gem build mudguard.gemspec")
|
14
|
-
end
|
15
|
-
end
|