rails-route-checker 0.1.1 → 0.2.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 +4 -4
- data/lib/rails-route-checker.rb +6 -5
- data/lib/rails-route-checker/app_interface.rb +62 -50
- data/lib/rails-route-checker/parsers/erb_parser.rb +52 -0
- data/lib/rails-route-checker/parsers/haml_parser.rb +28 -0
- data/lib/rails-route-checker/parsers/haml_parser/document.rb +47 -0
- data/lib/rails-route-checker/parsers/haml_parser/ruby_extractor.rb +151 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/filter_node.rb +13 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/node.rb +126 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/root_node.rb +13 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/script_node.rb +13 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/silent_script_node.rb +13 -0
- data/lib/rails-route-checker/parsers/haml_parser/tree/tag_node.rb +56 -0
- data/lib/rails-route-checker/parsers/loader.rb +55 -0
- data/lib/rails-route-checker/parsers/ruby_parser.rb +57 -0
- data/lib/rails-route-checker/version.rb +1 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b24a961c0772ee84e1c259c0a0ec3ae58e604a00
|
4
|
+
data.tar.gz: 0cc8d360ec7a9b00ba983438777c84b3e380e506
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94985375668b8d095725af978851124f9212b05a84cca9ef754ac8b1a9b3b62a33ce9f61a898f252392d5220b3f9ea4a313a83cba0bf129ef49431ba38b54768
|
7
|
+
data.tar.gz: 802d00447e68e0ea6f7a302f2228de146e4612d57c251c067cf5857d9e5895a2abfd33bbe8cc392a14dab85cb7cf8a1df60dc9ebd84ce3a9d4183029bc566482
|
data/lib/rails-route-checker.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require_relative 'rails-route-checker/app_interface'
|
2
|
+
require_relative 'rails-route-checker/config_file'
|
3
|
+
require_relative 'rails-route-checker/loaded_app'
|
4
|
+
require_relative 'rails-route-checker/runner'
|
5
|
+
require_relative 'rails-route-checker/parsers/loader'
|
6
|
+
require_relative 'rails-route-checker/version'
|
6
7
|
|
7
8
|
module RailsRouteChecker; end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RailsRouteChecker
|
2
2
|
class AppInterface
|
3
3
|
def initialize(**opts)
|
4
|
-
@options = opts
|
4
|
+
@options = { ignored_controllers: [], ignored_paths: [], ignored_path_whitelist: {} }.merge(opts)
|
5
5
|
end
|
6
6
|
|
7
7
|
def routes_without_actions
|
@@ -38,77 +38,89 @@ module RailsRouteChecker
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def generate_undef_view_path_calls
|
41
|
-
|
41
|
+
generate_undef_view_path_calls_erb + generate_undef_view_path_calls_haml
|
42
|
+
end
|
43
|
+
|
44
|
+
def generate_undef_view_path_calls_erb
|
45
|
+
files = `find app -type f -iregex '.*\\.erb'`.split("\n")
|
46
|
+
return [] if files.none?
|
47
|
+
|
48
|
+
RailsRouteChecker::Parsers::Loader.load_parser(:erb)
|
49
|
+
|
42
50
|
files.map do |filename|
|
43
51
|
controller = controller_from_view_file(filename)
|
44
52
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
skip_first = false
|
50
|
-
if line =~ /^\s*-/
|
51
|
-
line_match = line.match(/^\s*-\s*([a-zA-Z0-9_]+_(?:path|url))\s*=/)
|
52
|
-
defined_variables << line_match[1] if line_match
|
53
|
-
skip_first = true
|
54
|
-
end
|
55
|
-
|
56
|
-
matches = line.scan(/(([a-zA-Z][a-zA-Z0-9_]*)_(?:path|url))[^a-z0-9_]/)
|
57
|
-
matches.shift if skip_first
|
58
|
-
ignores = line.scan(/(([a-zA-Z][a-zA-Z0-9_]*)_(?:path|url))(?: =|[!:])/).map(&:first)
|
59
|
-
ignores += line.scan(/[.@:_'"]([a-zA-Z][a-zA-Z0-9_]+_(?:path|url))[^a-z0-9_]/).map(&:first)
|
60
|
-
|
61
|
-
matches.reject! { |match| ignores.include?(match[0]) }
|
62
|
-
|
63
|
-
matches.map do |match|
|
64
|
-
next if match_in_whitelist?(filename, match)
|
65
|
-
next if match_defined_in_view?(controller, defined_variables, match)
|
66
|
-
{ file: filename, line: line_num + 1, method: match[0] }
|
67
|
-
end
|
53
|
+
filter = lambda do |path_or_url|
|
54
|
+
return false if match_in_whitelist?(filename, path_or_url)
|
55
|
+
return false if match_defined_in_view?(controller, path_or_url)
|
56
|
+
true
|
68
57
|
end
|
58
|
+
|
59
|
+
RailsRouteChecker::Parsers::ErbParser.run(filename, filter: filter)
|
69
60
|
end.flatten.compact
|
70
61
|
end
|
71
62
|
|
72
|
-
def
|
73
|
-
`find app
|
74
|
-
|
63
|
+
def generate_undef_view_path_calls_haml
|
64
|
+
files = `find app -type f -iregex '.*\\.haml'`.split("\n")
|
65
|
+
return [] if files.none?
|
66
|
+
|
67
|
+
unless RailsRouteChecker::Parsers::Loader.haml_available?
|
68
|
+
puts 'WARNING: There are Haml files in your codebase, ' \
|
69
|
+
"but the Haml parser for rails-route-checker couldn't load!"
|
70
|
+
return []
|
71
|
+
end
|
72
|
+
|
73
|
+
RailsRouteChecker::Parsers::Loader.load_parser(:haml)
|
74
|
+
|
75
|
+
files.map do |filename|
|
76
|
+
controller = controller_from_view_file(filename)
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
filter = lambda do |path_or_url|
|
79
|
+
return false if match_in_whitelist?(filename, path_or_url)
|
80
|
+
return false if match_defined_in_view?(controller, path_or_url)
|
81
|
+
true
|
82
|
+
end
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
84
|
+
RailsRouteChecker::Parsers::HamlParser.run(filename, filter: filter)
|
85
|
+
end.flatten.compact
|
86
|
+
end
|
83
87
|
|
84
|
-
|
88
|
+
def generate_undef_controller_path_calls
|
89
|
+
files = `find app/controllers -type f -iregex '.*\\.rb'`.split("\n")
|
90
|
+
return [] if files.none?
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
92
|
+
RailsRouteChecker::Parsers::Loader.load_parser(:ruby)
|
93
|
+
|
94
|
+
files.map do |filename|
|
95
|
+
controller = controller_from_ruby_file(filename)
|
96
|
+
next unless controller # controller will be nil if it's an ignored controller
|
97
|
+
|
98
|
+
filter = lambda do |path_or_url|
|
99
|
+
return false if match_in_whitelist?(filename, path_or_url)
|
100
|
+
return false if match_defined_in_ruby?(controller, path_or_url)
|
101
|
+
return true
|
91
102
|
end
|
103
|
+
|
104
|
+
RailsRouteChecker::Parsers::RubyParser.run(filename, filter: filter)
|
92
105
|
end.flatten.compact
|
93
106
|
end
|
94
107
|
|
95
|
-
def match_in_whitelist?(filename,
|
96
|
-
|
108
|
+
def match_in_whitelist?(filename, path_or_url)
|
109
|
+
possible_route_name = path_or_url.sub(/_(?:url|path)$/, '')
|
97
110
|
return true if options[:ignored_paths].include?(possible_route_name)
|
98
|
-
(options[:ignored_path_whitelist][filename] || []).include?(
|
111
|
+
(options[:ignored_path_whitelist][filename] || []).include?(path_or_url)
|
99
112
|
end
|
100
113
|
|
101
|
-
def match_defined_in_view?(controller,
|
102
|
-
|
114
|
+
def match_defined_in_view?(controller, path_or_url)
|
115
|
+
possible_route_name = path_or_url.sub(/_(?:url|path)$/, '')
|
103
116
|
return true if loaded_app.all_route_names.include?(possible_route_name)
|
104
|
-
|
105
|
-
controller && controller[:helpers].include?(full_match)
|
117
|
+
controller && controller[:helpers].include?(path_or_url)
|
106
118
|
end
|
107
119
|
|
108
|
-
def match_defined_in_ruby?(controller,
|
109
|
-
|
120
|
+
def match_defined_in_ruby?(controller, path_or_url)
|
121
|
+
possible_route_name = path_or_url.sub(/_(?:url|path)$/, '')
|
110
122
|
return true if loaded_app.all_route_names.include?(possible_route_name)
|
111
|
-
controller && controller[:instance_methods].include?(
|
123
|
+
controller && controller[:instance_methods].include?(path_or_url)
|
112
124
|
end
|
113
125
|
|
114
126
|
def controller_from_view_file(filename)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module ErbParser
|
4
|
+
class << self
|
5
|
+
def run(filename, **opts)
|
6
|
+
file_source = opts[:source] || File.read(filename)
|
7
|
+
|
8
|
+
next_ruby_source_line_num = 1
|
9
|
+
ruby_source = ''
|
10
|
+
source_map = {}
|
11
|
+
|
12
|
+
file_source.split("\n").each_with_index do |line, line_num|
|
13
|
+
ruby_lines = process_line(line)
|
14
|
+
next unless ruby_lines.any?
|
15
|
+
|
16
|
+
ruby_source += ruby_lines.join("\n") + "\n"
|
17
|
+
ruby_lines.length.times do |i|
|
18
|
+
source_map[next_ruby_source_line_num + i] = line_num + 1
|
19
|
+
end
|
20
|
+
next_ruby_source_line_num += ruby_lines.length
|
21
|
+
end
|
22
|
+
|
23
|
+
opts[:source] = ruby_source
|
24
|
+
opts[:source_map] = source_map
|
25
|
+
|
26
|
+
RailsRouteChecker::Parsers::RubyParser.run(filename, **opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def process_line(line)
|
32
|
+
lookup_index = 0
|
33
|
+
ruby_lines = []
|
34
|
+
|
35
|
+
while lookup_index < line.length
|
36
|
+
opening = line.index('<%=', lookup_index)
|
37
|
+
is_write_opening = opening
|
38
|
+
opening ||= line.index('<%', lookup_index)
|
39
|
+
break unless opening
|
40
|
+
|
41
|
+
closing = line.index('%>', opening + 2)
|
42
|
+
break unless closing
|
43
|
+
|
44
|
+
ruby_lines << line[(opening + (is_write_opening ? 3 : 2))..(closing - 1)]
|
45
|
+
lookup_index = closing + 2
|
46
|
+
end
|
47
|
+
ruby_lines
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'haml_parser/document'
|
2
|
+
require_relative 'haml_parser/tree/node'
|
3
|
+
require_relative 'haml_parser/tree/filter_node'
|
4
|
+
require_relative 'haml_parser/tree/root_node'
|
5
|
+
require_relative 'haml_parser/tree/script_node'
|
6
|
+
require_relative 'haml_parser/tree/silent_script_node'
|
7
|
+
require_relative 'haml_parser/tree/tag_node'
|
8
|
+
require_relative 'haml_parser/ruby_extractor'
|
9
|
+
|
10
|
+
module RailsRouteChecker
|
11
|
+
module Parsers
|
12
|
+
module HamlParser
|
13
|
+
class << self
|
14
|
+
def run(filename, **opts)
|
15
|
+
file_source = opts[:source] || File.read(filename)
|
16
|
+
|
17
|
+
document = RailsRouteChecker::Parsers::HamlParser::Document.new(file_source)
|
18
|
+
extracted_ruby = RailsRouteChecker::Parsers::HamlParser::RubyExtractor.extract(document)
|
19
|
+
|
20
|
+
opts[:source] = extracted_ruby.source
|
21
|
+
opts[:source_map] = extracted_ruby.source_map
|
22
|
+
|
23
|
+
RailsRouteChecker::Parsers::RubyParser.run(filename, **opts)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module HamlParser
|
4
|
+
class Document
|
5
|
+
attr_reader :tree, :source, :source_lines
|
6
|
+
|
7
|
+
def initialize(source)
|
8
|
+
@source = source.force_encoding(Encoding::UTF_8)
|
9
|
+
@source_lines = @source.split(/\r\n|\r|\n/)
|
10
|
+
|
11
|
+
version = Gem::Version.new(Haml::VERSION).approximate_recommendation
|
12
|
+
original_tree = case version
|
13
|
+
when '~> 4.0', '~> 4.1' then Haml::Parser.new(@source, Haml::Options.new).parse
|
14
|
+
when '~> 5.0' then Haml::Parser.new(options).call(@source)
|
15
|
+
else raise "Cannot handle Haml version: #{version}"
|
16
|
+
end
|
17
|
+
|
18
|
+
@tree = process_tree(original_tree)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def process_tree(original_tree)
|
24
|
+
if Gem::Requirement.new('~> 4.0.0').satisfied_by?(Gem.loaded_specs['haml'].version)
|
25
|
+
original_tree.children.pop
|
26
|
+
end
|
27
|
+
|
28
|
+
convert_tree(original_tree)
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert_tree(haml_node, parent = nil)
|
32
|
+
node_class_name = "#{haml_node.type.to_s.split(/[-_ ]/).collect(&:capitalize).join}Node"
|
33
|
+
node_class_name = 'Node' unless RailsRouteChecker::Parsers::HamlParser::Tree.const_defined?(node_class_name)
|
34
|
+
|
35
|
+
new_node = RailsRouteChecker::Parsers::HamlParser::Tree.const_get(node_class_name).new(self, haml_node)
|
36
|
+
new_node.parent = parent
|
37
|
+
|
38
|
+
new_node.children = haml_node.children.map do |child|
|
39
|
+
convert_tree(child, new_node)
|
40
|
+
end
|
41
|
+
|
42
|
+
new_node
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module HamlParser
|
4
|
+
class RubyExtractor
|
5
|
+
RubySource = Struct.new(:source, :source_map)
|
6
|
+
|
7
|
+
def self.extract(document)
|
8
|
+
new(document).extract
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(document)
|
12
|
+
@document = document
|
13
|
+
end
|
14
|
+
|
15
|
+
def extract
|
16
|
+
@source_lines = []
|
17
|
+
@source_map = {}
|
18
|
+
@line_count = 0
|
19
|
+
@indent_level = 0
|
20
|
+
@output_count = 0
|
21
|
+
|
22
|
+
visit_children(document.tree)
|
23
|
+
|
24
|
+
RubySource.new(@source_lines.join("\n"), @source_map)
|
25
|
+
end
|
26
|
+
|
27
|
+
def visit_tag(node)
|
28
|
+
additional_attributes = node.dynamic_attributes_sources
|
29
|
+
|
30
|
+
additional_attributes.each do |attributes_code|
|
31
|
+
attributes_code = attributes_code.gsub(/\s*\n\s*/, ' ').strip
|
32
|
+
add_line("{}.merge(#{attributes_code.strip})", node)
|
33
|
+
end
|
34
|
+
|
35
|
+
if node.hash_attributes? && node.dynamic_attributes_sources.empty?
|
36
|
+
normalized_attr_source = node.dynamic_attributes_source[:hash].gsub(/\s*\n\s*/, ' ')
|
37
|
+
|
38
|
+
add_line(normalized_attr_source, node)
|
39
|
+
end
|
40
|
+
|
41
|
+
code = node.script.strip
|
42
|
+
add_line(code, node) unless code.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_script(node)
|
46
|
+
code = node.text
|
47
|
+
add_line(code.strip, node)
|
48
|
+
|
49
|
+
start_block = anonymous_block?(code) || start_block_keyword?(code)
|
50
|
+
|
51
|
+
@indent_level += 1 if start_block
|
52
|
+
|
53
|
+
yield
|
54
|
+
|
55
|
+
return unless start_block
|
56
|
+
@indent_level -= 1
|
57
|
+
add_line('end', node)
|
58
|
+
end
|
59
|
+
|
60
|
+
def visit_filter(node)
|
61
|
+
return unless node.filter_type == 'ruby'
|
62
|
+
|
63
|
+
node.text.split("\n").each_with_index do |line, index|
|
64
|
+
add_line(line, node.line + index + 1, false)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def visit(node)
|
69
|
+
block_called = false
|
70
|
+
|
71
|
+
block = lambda do |descend = :children|
|
72
|
+
block_called = true
|
73
|
+
visit_children(node) if descend == :children
|
74
|
+
end
|
75
|
+
|
76
|
+
case node.type
|
77
|
+
when :tag
|
78
|
+
visit_tag(node)
|
79
|
+
when :script, :silent_script
|
80
|
+
visit_script(node, &block)
|
81
|
+
when :filter
|
82
|
+
visit_filter(node)
|
83
|
+
end
|
84
|
+
|
85
|
+
visit_children(node) unless block_called
|
86
|
+
end
|
87
|
+
|
88
|
+
def visit_children(parent)
|
89
|
+
parent.children.each { |node| visit(node) }
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
attr_reader :document
|
95
|
+
|
96
|
+
def add_line(code, node_or_line, discard_blanks = true)
|
97
|
+
return if code.empty? && discard_blanks
|
98
|
+
|
99
|
+
indent_level = @indent_level
|
100
|
+
|
101
|
+
if node_or_line.respond_to?(:line)
|
102
|
+
indent_level -= 1 if mid_block_keyword?(code)
|
103
|
+
end
|
104
|
+
|
105
|
+
indent = (' ' * 2 * indent_level)
|
106
|
+
|
107
|
+
@source_lines << indent_code(code, indent)
|
108
|
+
|
109
|
+
original_line =
|
110
|
+
node_or_line.respond_to?(:line) ? node_or_line.line : node_or_line
|
111
|
+
|
112
|
+
(code.count("\n") + 1).times do
|
113
|
+
@line_count += 1
|
114
|
+
@source_map[@line_count] = original_line
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def indent_code(code, indent)
|
119
|
+
codes = code.split("\n")
|
120
|
+
codes.map { |c| indent + c }.join("\n")
|
121
|
+
end
|
122
|
+
|
123
|
+
def anonymous_block?(text)
|
124
|
+
text =~ /\bdo\s*(\|\s*[^\|]*\s*\|)?(\s*#.*)?\z/
|
125
|
+
end
|
126
|
+
|
127
|
+
START_BLOCK_KEYWORDS = %w[if unless case begin for until while].freeze
|
128
|
+
def start_block_keyword?(text)
|
129
|
+
START_BLOCK_KEYWORDS.include?(block_keyword(text))
|
130
|
+
end
|
131
|
+
|
132
|
+
MID_BLOCK_KEYWORDS = %w[else elsif when rescue ensure].freeze
|
133
|
+
def mid_block_keyword?(text)
|
134
|
+
MID_BLOCK_KEYWORDS.include?(block_keyword(text))
|
135
|
+
end
|
136
|
+
|
137
|
+
LOOP_KEYWORDS = %w[for until while].freeze
|
138
|
+
def block_keyword(text)
|
139
|
+
# Need to handle 'for'/'while' since regex stolen from HAML parser doesn't
|
140
|
+
keyword = text[/\A\s*([^\s]+)\s+/, 1]
|
141
|
+
return keyword if keyword && LOOP_KEYWORDS.include?(keyword)
|
142
|
+
|
143
|
+
keyword = text.scan(Haml::Parser::BLOCK_KEYWORD_REGEX)[0]
|
144
|
+
return unless keyword
|
145
|
+
|
146
|
+
keyword[0] || keyword[1]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module HamlParser
|
4
|
+
module Tree
|
5
|
+
class Node
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_accessor :children, :parent
|
9
|
+
attr_reader :line, :type
|
10
|
+
|
11
|
+
def initialize(document, parse_node)
|
12
|
+
@line = parse_node.line
|
13
|
+
@document = document
|
14
|
+
@value = parse_node.value
|
15
|
+
@type = parse_node.type
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return to_enum(__callee__) unless block_given?
|
20
|
+
|
21
|
+
node = self
|
22
|
+
loop do
|
23
|
+
yield node
|
24
|
+
break unless (node = node.next_node)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def directives
|
29
|
+
directives = []
|
30
|
+
directives << predecessor.directives if predecessor
|
31
|
+
directives.flatten
|
32
|
+
end
|
33
|
+
|
34
|
+
def source_code
|
35
|
+
next_node_line =
|
36
|
+
if next_node
|
37
|
+
next_node.line - 1
|
38
|
+
else
|
39
|
+
@document.source_lines.count + 1
|
40
|
+
end
|
41
|
+
|
42
|
+
@document.source_lines[@line - 1...next_node_line]
|
43
|
+
.join("\n")
|
44
|
+
.gsub(/^\s*\z/m, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#<#{self.class.name}>"
|
49
|
+
end
|
50
|
+
|
51
|
+
def lines
|
52
|
+
return [] unless @value && text
|
53
|
+
|
54
|
+
text.split(/\r\n|\r|\n/)
|
55
|
+
end
|
56
|
+
|
57
|
+
def line_numbers
|
58
|
+
return (line..line) unless @value && text
|
59
|
+
|
60
|
+
(line..line + lines.count)
|
61
|
+
end
|
62
|
+
|
63
|
+
def predecessor
|
64
|
+
siblings.previous(self) || parent
|
65
|
+
end
|
66
|
+
|
67
|
+
def successor
|
68
|
+
next_sibling = siblings.next(self)
|
69
|
+
return next_sibling if next_sibling
|
70
|
+
|
71
|
+
parent.successor if parent
|
72
|
+
end
|
73
|
+
|
74
|
+
def next_node
|
75
|
+
children.first || successor
|
76
|
+
end
|
77
|
+
|
78
|
+
def subsequents
|
79
|
+
siblings.subsequents(self)
|
80
|
+
end
|
81
|
+
|
82
|
+
def text
|
83
|
+
@value[:text].to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def siblings
|
89
|
+
@siblings ||= Siblings.new(parent ? parent.children : [self])
|
90
|
+
end
|
91
|
+
|
92
|
+
class Siblings < SimpleDelegator
|
93
|
+
def next(node)
|
94
|
+
subsequents(node).first
|
95
|
+
end
|
96
|
+
|
97
|
+
def previous(node)
|
98
|
+
priors(node).last
|
99
|
+
end
|
100
|
+
|
101
|
+
def priors(node)
|
102
|
+
position = position(node)
|
103
|
+
if position.zero?
|
104
|
+
[]
|
105
|
+
else
|
106
|
+
siblings[0..(position - 1)]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def subsequents(node)
|
111
|
+
siblings[(position(node) + 1)..-1]
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
alias siblings __getobj__
|
117
|
+
|
118
|
+
def position(node)
|
119
|
+
siblings.index(node)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module HamlParser
|
4
|
+
module Tree
|
5
|
+
class TagNode < Node
|
6
|
+
def dynamic_attributes_sources
|
7
|
+
@dynamic_attributes_sources ||=
|
8
|
+
if Gem::Version.new(Haml::VERSION) < Gem::Version.new('5')
|
9
|
+
@value[:attributes_hashes]
|
10
|
+
else
|
11
|
+
Array(@value[:dynamic_attributes].to_literal).reject(&:empty?)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def dynamic_attributes_source
|
16
|
+
@dynamic_attributes_source ||=
|
17
|
+
attributes_source.reject { |key| key == :static }
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes_source
|
21
|
+
@attr_source ||=
|
22
|
+
begin
|
23
|
+
_explicit_tag, static_attrs, rest =
|
24
|
+
source_code.scan(/\A\s*(%[-:\w]+)?([-:\w\.\#]*)(.*)/m)[0]
|
25
|
+
|
26
|
+
attr_types = {
|
27
|
+
'{' => [:hash, %w[{ }]],
|
28
|
+
'(' => [:html, %w[( )]],
|
29
|
+
'[' => [:object_ref, %w[[ ]]]
|
30
|
+
}
|
31
|
+
|
32
|
+
attr_source = { static: static_attrs }
|
33
|
+
while rest
|
34
|
+
type, chars = attr_types[rest[0]]
|
35
|
+
break unless type
|
36
|
+
break if attr_source[type]
|
37
|
+
|
38
|
+
attr_source[type], rest = Haml::Util.balance(rest, *chars)
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_source
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash_attributes?
|
46
|
+
!dynamic_attributes_source[:hash].nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
def script
|
50
|
+
(@value[:value] if @value[:parse]) || ''
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RailsRouteChecker
|
2
|
+
module Parsers
|
3
|
+
module Loader
|
4
|
+
class << self
|
5
|
+
def load_parser(type)
|
6
|
+
case type
|
7
|
+
when :ruby
|
8
|
+
load_basic_parser(:ruby)
|
9
|
+
when :erb
|
10
|
+
load_basic_parser(:ruby)
|
11
|
+
load_basic_parser(:erb)
|
12
|
+
when :haml
|
13
|
+
if haml_available?
|
14
|
+
load_basic_parser(:ruby)
|
15
|
+
load_haml_parser
|
16
|
+
end
|
17
|
+
else
|
18
|
+
raise "Unrecognised parser attempting to be loaded: #{type}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def haml_available?
|
23
|
+
return @haml_available if defined?(@haml_available)
|
24
|
+
@haml_available = gem_installed?('haml')
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def gem_installed?(name, version_requirement = nil)
|
30
|
+
Gem::Dependency.new(name, version_requirement).matching_specs.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_basic_parser(parser_name)
|
34
|
+
if_unloaded(parser_name) do
|
35
|
+
require_relative "#{parser_name}_parser"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_haml_parser
|
40
|
+
if_unloaded(:haml) do
|
41
|
+
require 'haml'
|
42
|
+
require_relative 'haml_parser'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def if_unloaded(parser)
|
47
|
+
@loaded_parsers ||= {}
|
48
|
+
return false if @loaded_parsers[parser]
|
49
|
+
yield
|
50
|
+
@loaded_parsers[parser] = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
|
3
|
+
module RailsRouteChecker
|
4
|
+
module Parsers
|
5
|
+
module RubyParser
|
6
|
+
class << self
|
7
|
+
def run(filename, **opts)
|
8
|
+
file_source = opts[:source] || File.read(filename)
|
9
|
+
|
10
|
+
items = []
|
11
|
+
|
12
|
+
deep_iterator(Ripper.sexp(file_source)) do |item, extra_data|
|
13
|
+
scope = extra_data[:scope]
|
14
|
+
next unless %i[vcall fcall].include?(scope[-2])
|
15
|
+
next unless scope[-1] == :@ident
|
16
|
+
next unless item.end_with?('_path', '_url')
|
17
|
+
|
18
|
+
next if opts[:filter].respond_to?(:call) && !opts[:filter].call(item)
|
19
|
+
|
20
|
+
line = extra_data[:position][0]
|
21
|
+
line = opts[:source_map][line] || 'unknown' if opts[:source_map]
|
22
|
+
|
23
|
+
items << { file: filename, line: line, method: item }
|
24
|
+
end
|
25
|
+
|
26
|
+
items
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def deep_iterator(list, current_scope = [], current_line_num = [], &block)
|
32
|
+
if list.is_a?(Array)
|
33
|
+
if list[0].is_a?(Symbol)
|
34
|
+
current_scope << list[0]
|
35
|
+
|
36
|
+
if list[-1].is_a?(Array) && list[-1].length == 2 && list[-1].all? { |item| item.is_a?(Integer) }
|
37
|
+
current_line_num = list[-1]
|
38
|
+
list = list[0..-2]
|
39
|
+
end
|
40
|
+
|
41
|
+
list[1..-1].each do |item|
|
42
|
+
deep_iterator(item, current_scope, current_line_num, &block)
|
43
|
+
end
|
44
|
+
current_scope.pop
|
45
|
+
else
|
46
|
+
list.each do |item|
|
47
|
+
deep_iterator(item, current_scope, current_line_num, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
elsif !list.nil?
|
51
|
+
yield(list, { scope: current_scope, position: current_line_num })
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-route-checker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Allie
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -68,6 +68,18 @@ files:
|
|
68
68
|
- lib/rails-route-checker/app_interface.rb
|
69
69
|
- lib/rails-route-checker/config_file.rb
|
70
70
|
- lib/rails-route-checker/loaded_app.rb
|
71
|
+
- lib/rails-route-checker/parsers/erb_parser.rb
|
72
|
+
- lib/rails-route-checker/parsers/haml_parser.rb
|
73
|
+
- lib/rails-route-checker/parsers/haml_parser/document.rb
|
74
|
+
- lib/rails-route-checker/parsers/haml_parser/ruby_extractor.rb
|
75
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/filter_node.rb
|
76
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/node.rb
|
77
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/root_node.rb
|
78
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/script_node.rb
|
79
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/silent_script_node.rb
|
80
|
+
- lib/rails-route-checker/parsers/haml_parser/tree/tag_node.rb
|
81
|
+
- lib/rails-route-checker/parsers/loader.rb
|
82
|
+
- lib/rails-route-checker/parsers/ruby_parser.rb
|
71
83
|
- lib/rails-route-checker/runner.rb
|
72
84
|
- lib/rails-route-checker/version.rb
|
73
85
|
- rails-route-checker.gemspec
|