Skip to content

Commit 5a9ef5c

Browse files
committed
[rail_inspector] Prism Visitor::FrameworkDefault
Also apply tweaks to Configuration guide
1 parent 96f13d1 commit 5a9ef5c

File tree

9 files changed

+116
-127
lines changed

9 files changed

+116
-127
lines changed

Gemfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ gem "uri", ">= 0.13.1", require: false
4545

4646
gem "prism"
4747

48-
group :lint do
49-
gem "syntax_tree", "6.1.1", require: false
50-
end
51-
5248
group :rubocop do
5349
gem "rubocop", ">= 1.25.1", require: false
5450
gem "rubocop-minitest", require: false

Gemfile.lock

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,6 @@ GEM
423423
racc
424424
path_expander (1.1.3)
425425
pg (1.5.8)
426-
prettier_print (1.2.1)
427426
prism (1.2.0)
428427
propshaft (1.1.0)
429428
actionpack (>= 7.0.0)
@@ -617,8 +616,6 @@ GEM
617616
stringio (3.1.1)
618617
sucker_punch (3.2.0)
619618
concurrent-ruby (~> 1.0)
620-
syntax_tree (6.1.1)
621-
prettier_print (>= 1.2.0)
622619
tailwindcss-rails (3.0.0)
623620
railties (>= 7.0.0)
624621
tailwindcss-ruby
@@ -743,7 +740,6 @@ DEPENDENCIES
743740
stackprof
744741
stimulus-rails
745742
sucker_punch
746-
syntax_tree (= 6.1.1)
747743
tailwindcss-rails
748744
terser (>= 1.1.4)
749745
thruster

guides/source/configuring.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Below are the default values associated with each target version. In cases of co
7272

7373
- [`config.active_record.postgresql_adapter_decode_dates`](#config-active-record-postgresql-adapter-decode-dates): `true`
7474
- [`config.active_record.validate_migration_timestamps`](#config-active-record-validate-migration-timestamps): `true`
75-
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w[image/png image/jpeg image/gif image/webp]`
75+
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w( image/png image/jpeg image/gif image/webp )`
7676
- [`config.yjit`](#config-yjit): `true`
7777

7878
#### Default Values for Target Version 7.1

tools/rail_inspector/lib/rail_inspector/configuring.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def initialize
1212
end
1313

1414
def call(path)
15-
@cache[path] ||= SyntaxTree.parse(SyntaxTree.read(path))
15+
@cache[path] ||= Prism.parse_file(path.to_s).value
1616
end
1717
end
1818

tools/rail_inspector/lib/rail_inspector/configuring/check/general_configuration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def initialize(checker)
1414
def call
1515
visitor = Visitor::Attribute.new
1616
visitor.visit(app_config_tree)
17-
visitor.attribute_map[APP_CONFIG_CONST]["attr_accessor"]
17+
visitor.attribute_map[APP_CONFIG_CONST][:attr_accessor]
1818
end
1919

2020
private

tools/rail_inspector/lib/rail_inspector/visitor/framework_default.rb

Lines changed: 79 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,129 @@
11
# frozen_string_literal: true
22

3-
require "syntax_tree"
3+
require "prism"
44

55
require_relative "./hash_to_string"
6-
require_relative "./multiline_to_string"
76

87
module RailInspector
98
module Visitor
109
class FrameworkDefault
11-
TargetVersionCaseFinder =
12-
SyntaxTree::Search.new(
13-
->(node) do
14-
node in SyntaxTree::Case[
15-
value: SyntaxTree::CallNode[
16-
receiver: SyntaxTree::VarRef[
17-
value: SyntaxTree::Ident[value: "target_version"]
18-
]
19-
]
20-
]
21-
end
22-
)
23-
2410
attr_reader :config_map
2511

2612
def initialize
2713
@config_map = {}
2814
end
2915

3016
def visit(node)
31-
case_node, *others = TargetVersionCaseFinder.scan(node).to_a
32-
raise "#{others.length} other cases?" unless others.empty?
17+
target_version_case = node.breadth_first_search do |n|
18+
n in Prism::CaseNode[
19+
predicate: Prism::CallNode[receiver: Prism::LocalVariableReadNode[name: :target_version]]
20+
]
21+
end
3322

34-
visit_when(case_node.consequent)
23+
target_version_case.conditions.each { |cond| visit_when(cond) }
3524
end
3625

3726
private
3827
def visit_when(node)
39-
version = node.arguments.parts[0].parts[0].value
40-
41-
config_map[version] = VersionedConfig.new.config_for(node.statements)
28+
version = node.conditions[0].unescaped
4229

43-
visit_when(node.consequent) if node.consequent.is_a? SyntaxTree::When
30+
config_map[version] = VersionedConfig.new.tap { |v| v.visit(node.statements) }.configs
4431
end
4532

46-
class VersionedConfig < SyntaxTree::Visitor
33+
class VersionedConfig < Prism::Visitor
4734
attr_reader :configs
4835

4936
def initialize
5037
@configs = {}
5138
@framework_stack = []
5239
end
5340

54-
def config_for(node)
55-
visit(node)
56-
@configs
57-
end
41+
def visit_if_node(node)
42+
unless new_framework = respond_to_framework?(node.predicate)
43+
return visit_child_nodes(node)
44+
end
5845

59-
visit_methods do
60-
def visit_if(node)
61-
unless new_framework = respond_to_framework?(node.predicate)
62-
return super
63-
end
46+
if ENV["STRICT"] && current_framework
47+
raise "Potentially nested framework? Current: '#{current_framework}', found: '#{new_framework}'"
48+
end
6449

65-
if ENV["STRICT"] && current_framework
66-
raise "Potentially nested framework? Current: '#{current_framework}', found: '#{new_framework}'"
67-
end
50+
@framework_stack << new_framework
51+
visit_child_nodes(node)
52+
@framework_stack.pop
53+
end
54+
55+
def visit_call_node(node)
56+
name = node.name.to_s
6857

69-
@framework_stack << new_framework
70-
super
71-
@framework_stack.pop
58+
unless name.end_with? "="
59+
return super
7260
end
7361

74-
def visit_assign(node)
75-
assert_framework(node)
76-
77-
target = SyntaxTree::Formatter.format(nil, node.target)
78-
value =
79-
case node.value
80-
when SyntaxTree::HashLiteral
81-
HashToString.new.tap { |v| v.visit(node.value) }.to_s
82-
when SyntaxTree::StringConcat
83-
MultilineToString.new.tap { |v| v.visit(node.value) }.to_s
84-
else
85-
SyntaxTree::Formatter.format(nil, node.value)
86-
end
87-
@configs[target] = value
62+
handle_assignment(node, name[...-1], node.arguments.arguments[0])
63+
end
64+
65+
def visit_call_or_write_node(node)
66+
name = node.write_name.to_s
67+
68+
unless name.end_with? "="
69+
return super
8870
end
8971

90-
def visit_opassign(node)
91-
if node.operator.name == :"||="
92-
visit_assign(node)
93-
else
94-
super
72+
handle_assignment(node, node.read_name.to_s, node.value)
73+
end
74+
75+
def handle_assignment(node, name, value)
76+
prefix = case node.receiver
77+
in Prism::ConstantReadNode[name: constant_name]
78+
constant_name
79+
in Prism::SelfNode
80+
"self"
81+
in Prism::CallNode[receiver: nil, name: framework]
82+
framework_string = framework.to_s
83+
84+
unless current_framework == framework_string
85+
raise "expected: #{current_framework}, actual: #{framework_string}"
9586
end
87+
88+
framework_string
89+
else
90+
node.receiver.location.slice
9691
end
97-
end
9892

99-
private
100-
def assert_framework(node)
101-
framework =
102-
case node.target.parent
103-
in { value: SyntaxTree::Const } |
104-
{ value: SyntaxTree::Kw[value: "self"] }
105-
nil
106-
in receiver: { value: { value: framework } }
107-
framework
108-
in value: { value: framework }
109-
framework
110-
end
111-
112-
return if current_framework == framework
113-
114-
raise "Expected #{current_framework} to match #{framework}"
93+
target = "#{prefix}.#{name}"
94+
95+
string_value = case value
96+
in Prism::ConstantPathNode
97+
value.full_name
98+
in Prism::HashNode
99+
HashToString.new.tap { |v| v.visit(value) }.to_s
100+
in Prism::InterpolatedStringNode
101+
"\"#{value.parts.map(&:content).join("")}\""
102+
in Prism::FalseNode
103+
"false"
104+
in Prism::TrueNode
105+
"true"
106+
else
107+
value.location.slice
115108
end
116109

110+
@configs[target] = string_value
111+
end
112+
113+
private
117114
def current_framework
118115
@framework_stack.last
119116
end
120117

121118
def respond_to_framework?(node)
122-
if node in SyntaxTree::CallNode[
123-
receiver: nil,
124-
message: SyntaxTree::Ident[value: "respond_to?"],
125-
arguments: SyntaxTree::ArgParen[
126-
arguments: SyntaxTree::Args[
127-
parts: [
128-
SyntaxTree::SymbolLiteral[
129-
value: SyntaxTree::Ident[value: new_framework]
130-
]
131-
]
132-
]
133-
]
134-
]
119+
if node in Prism::CallNode[
120+
name: :respond_to?,
121+
arguments: Prism::ArgumentsNode[
122+
arguments: [
123+
Prism::SymbolNode[unescaped: new_framework]
124+
]
125+
]
126+
]
135127
new_framework
136128
end
137129
end

tools/rail_inspector/lib/rail_inspector/visitor/multiline_to_string.rb

Lines changed: 0 additions & 27 deletions
This file was deleted.

tools/rail_inspector/rail_inspector.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ Gem::Specification.new do |spec|
2929
spec.require_paths = ["lib"]
3030

3131
# Uncomment to register a new dependency of your gem
32-
spec.add_dependency "syntax_tree", "6.1.1"
32+
spec.add_dependency "prism", "~> 1.2"
3333
spec.add_dependency "thor", "~> 1.0"
3434
end

tools/rail_inspector/test/rail_inspector/visitor/framework_default_test.rb

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22

3+
require "test_helper"
34
require "rail_inspector/visitor/framework_default"
45

56
class FrameworkDefaultTest < Minitest::Test
@@ -81,6 +82,37 @@ def test_nested_frameworks_raise_when_strict
8182
ENV["STRICT"] = original_env
8283
end
8384

85+
def test_multiline_strings
86+
config = config_for_defaults <<~RUBY
87+
case target_version.to_s
88+
when "7.0"
89+
if respond_to?(:active_storage)
90+
active_storage.video_preview_arguments =
91+
"-vf 'select=eq(n\\,0)+eq(key\\,1)+gt(scene\\,0.015),loop=loop=-1:size=2,trim=start_frame=1'" \
92+
" -frames:v 1 -f image2"
93+
end
94+
end
95+
RUBY
96+
97+
assert_includes config, "7.0"
98+
assert_equal(
99+
"\"-vf 'select=eq(n\\,0)+eq(key\\,1)+gt(scene\\,0.015),loop=loop=-1:size=2,trim=start_frame=1' -frames:v 1 -f image2\"",
100+
config["7.0"]["active_storage.video_preview_arguments"],
101+
)
102+
end
103+
104+
def test_inline_condition
105+
config = config_for_defaults <<~RUBY
106+
case target_version.to_s
107+
when "8.0"
108+
Regexp.timeout ||= 1 if Regexp.respond_to?(:timeout=)
109+
end
110+
RUBY
111+
112+
assert_includes config, "8.0"
113+
assert_equal("1", config["8.0"]["Regexp.timeout"])
114+
end
115+
84116
private
85117
def wrapped_defaults(defaults)
86118
<<~RUBY
@@ -94,7 +126,7 @@ def load_defaults(target_version)
94126

95127
def config_for_defaults(defaults)
96128
full_class = wrapped_defaults(defaults)
97-
parsed = SyntaxTree.parse(full_class)
129+
parsed = Prism.parse(full_class).value
98130
visitor.visit(parsed)
99131
visitor.config_map
100132
end

0 commit comments

Comments
 (0)