@@ -35,6 +35,8 @@ module Rails
35
35
# Rails.root.join('app', 'models', 'goober').to_s
36
36
#
37
37
class FilePath < Base
38
+ extend AutoCorrector
39
+
38
40
include ConfigurableEnforcedStyle
39
41
include RangeHelp
40
42
@@ -57,9 +59,9 @@ class FilePath < Base
57
59
def on_dstr ( node )
58
60
return unless rails_root_nodes? ( node )
59
61
return if dstr_separated_by_colon? ( node )
60
- return unless dstr_ending_with_file_extension? ( node ) || dstr_including_file_separator? ( node )
61
62
62
- register_offense ( node , require_to_s : false )
63
+ check_for_slash_after_rails_root_in_dstr ( node )
64
+ check_for_extension_after_rails_root_join_in_dstr ( node )
63
65
end
64
66
65
67
def on_send ( node )
@@ -70,11 +72,33 @@ def on_send(node)
70
72
71
73
private
72
74
75
+ def check_for_slash_after_rails_root_in_dstr ( node )
76
+ rails_root_index = find_rails_root_index ( node )
77
+ slash_node = node . children [ rails_root_index + 1 ]
78
+ return unless slash_node &.str_type? && slash_node . source . start_with? ( File ::SEPARATOR )
79
+
80
+ register_offense ( node , require_to_s : false ) do |corrector |
81
+ autocorrect_slash_after_rails_root_in_dstr ( corrector , node , rails_root_index )
82
+ end
83
+ end
84
+
85
+ def check_for_extension_after_rails_root_join_in_dstr ( node )
86
+ rails_root_index = find_rails_root_index ( node )
87
+ extension_node = node . children [ rails_root_index + 1 ]
88
+ return unless extension_node? ( extension_node )
89
+
90
+ register_offense ( node , require_to_s : false ) do |corrector |
91
+ autocorrect_extension_after_rails_root_join_in_dstr ( corrector , node , rails_root_index , extension_node )
92
+ end
93
+ end
94
+
73
95
def check_for_file_join_with_rails_root ( node )
74
96
return unless file_join_nodes? ( node )
75
97
return unless node . arguments . any? { |e | rails_root_nodes? ( e ) }
76
98
77
- register_offense ( node , require_to_s : true )
99
+ register_offense ( node , require_to_s : true ) do |corrector |
100
+ autocorrect_file_join ( corrector , node )
101
+ end
78
102
end
79
103
80
104
def check_for_rails_root_join_with_string_arguments ( node )
@@ -84,7 +108,9 @@ def check_for_rails_root_join_with_string_arguments(node)
84
108
return unless node . arguments . size > 1
85
109
return unless node . arguments . all? ( &:str_type? )
86
110
87
- register_offense ( node , require_to_s : false )
111
+ register_offense ( node , require_to_s : false ) do |corrector |
112
+ autocorrect_rails_root_join_with_string_arguments ( corrector , node )
113
+ end
88
114
end
89
115
90
116
def check_for_rails_root_join_with_slash_separated_path ( node )
@@ -93,20 +119,22 @@ def check_for_rails_root_join_with_slash_separated_path(node)
93
119
return unless rails_root_join_nodes? ( node )
94
120
return unless node . arguments . any? { |arg | string_with_slash? ( arg ) }
95
121
96
- register_offense ( node , require_to_s : false )
122
+ register_offense ( node , require_to_s : false ) do |corrector |
123
+ autocorrect_rails_root_join_with_slash_separated_path ( corrector , node )
124
+ end
97
125
end
98
126
99
127
def string_with_slash? ( node )
100
- node . str_type? && node . source . include? ( '/' )
128
+ node . str_type? && node . source . include? ( File :: SEPARATOR )
101
129
end
102
130
103
- def register_offense ( node , require_to_s :)
131
+ def register_offense ( node , require_to_s :, & block )
104
132
line_range = node . loc . column ...node . loc . last_column
105
133
source_range = source_range ( processed_source . buffer , node . first_line , line_range )
106
134
107
135
message = build_message ( require_to_s )
108
136
109
- add_offense ( source_range , message : message )
137
+ add_offense ( source_range , message : message , & block )
110
138
end
111
139
112
140
def build_message ( require_to_s )
@@ -116,21 +144,94 @@ def build_message(require_to_s)
116
144
format ( message_template , to_s : to_s )
117
145
end
118
146
119
- def dstr_ending_with_file_extension? ( node )
120
- node . children . last . str_type? && node . children . last . source . start_with? ( '.' )
147
+ def dstr_separated_by_colon? ( node )
148
+ node . children [ 1 ..] . any? do |child |
149
+ child . str_type? && child . source . start_with? ( ':' )
150
+ end
121
151
end
122
152
123
- def dstr_including_file_separator? ( node )
124
- node . children . any? do |child |
125
- child . str_type? && child . source . include? ( File ::SEPARATOR )
153
+ def autocorrect_slash_after_rails_root_in_dstr ( corrector , node , rails_root_index )
154
+ rails_root_node = node . children [ rails_root_index ] . children . first
155
+ argument_source = extract_rails_root_join_argument_source ( node , rails_root_index )
156
+ if rails_root_node . method? ( :join )
157
+ append_argument ( corrector , rails_root_node , argument_source )
158
+ else
159
+ replace_with_rails_root_join ( corrector , rails_root_node , argument_source )
126
160
end
161
+ node . children [ rails_root_index + 1 ..] . each { |child | corrector . remove ( child ) }
127
162
end
128
163
129
- def dstr_separated_by_colon? ( node )
130
- node . children [ 1 ..] . any? do |child |
131
- child . str_type? && child . source . start_with? ( ':' )
164
+ def autocorrect_extension_after_rails_root_join_in_dstr ( corrector , node , rails_root_index , extension_node )
165
+ rails_root_node = node . children [ rails_root_index ] . children . first
166
+ return unless rails_root_node . arguments . last . str_type?
167
+
168
+ corrector . insert_before ( rails_root_node . arguments . last . location . end , extension_node . source )
169
+ corrector . remove ( extension_node )
170
+ end
171
+
172
+ def autocorrect_file_join ( corrector , node )
173
+ corrector . replace ( node . receiver , 'Rails.root' )
174
+ corrector . remove (
175
+ range_with_surrounding_space (
176
+ range_with_surrounding_comma (
177
+ node . arguments . first . source_range ,
178
+ :right
179
+ ) ,
180
+ side : :right
181
+ )
182
+ )
183
+ corrector . insert_after ( node , '.to_s' )
184
+ end
185
+
186
+ def autocorrect_rails_root_join_with_string_arguments ( corrector , node )
187
+ corrector . replace ( node . arguments . first , %("#{ node . arguments . map ( &:value ) . join ( '/' ) } ") )
188
+ node . arguments [ 1 ..] . each do |argument |
189
+ corrector . remove (
190
+ range_with_surrounding_comma (
191
+ range_with_surrounding_space (
192
+ argument . source_range ,
193
+ side : :left
194
+ ) ,
195
+ :left
196
+ )
197
+ )
198
+ end
199
+ end
200
+
201
+ def autocorrect_rails_root_join_with_slash_separated_path ( corrector , node )
202
+ node . arguments . each do |argument |
203
+ next unless string_with_slash? ( argument )
204
+
205
+ index = argument . source . index ( File ::SEPARATOR )
206
+ rest = inner_range_of ( argument ) . adjust ( begin_pos : index - 1 )
207
+ corrector . remove ( rest )
208
+ corrector . insert_after ( argument , %(, "#{ rest . source . delete_prefix ( File ::SEPARATOR ) } ") )
132
209
end
133
210
end
211
+
212
+ def inner_range_of ( node )
213
+ node . location . end . with ( begin_pos : node . location . begin . end_pos ) . adjust ( end_pos : -1 )
214
+ end
215
+
216
+ def find_rails_root_index ( node )
217
+ node . children . index { |child | rails_root_nodes? ( child ) }
218
+ end
219
+
220
+ def append_argument ( corrector , node , argument_source )
221
+ corrector . insert_after ( node . arguments . last , %(, "#{ argument_source } ") )
222
+ end
223
+
224
+ def replace_with_rails_root_join ( corrector , node , argument_source )
225
+ corrector . replace ( node , %<Rails.root.join("#{ argument_source } ")> )
226
+ end
227
+
228
+ def extract_rails_root_join_argument_source ( node , rails_root_index )
229
+ node . children [ rails_root_index + 1 ..] . map ( &:source ) . join . delete_prefix ( File ::SEPARATOR )
230
+ end
231
+
232
+ def extension_node? ( node )
233
+ node &.str_type? && node . source . start_with? ( '.' )
234
+ end
134
235
end
135
236
end
136
237
end
0 commit comments