@@ -11,10 +11,6 @@ module Rails
11
11
# This cop works best when used together with
12
12
# `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
13
13
#
14
- # @safety
15
- # This cop is unsafe for autocorrection because `Dir`'s `children`, `each_child`, `entries`, and `glob`
16
- # methods return string element, but these methods of `Pathname` return `Pathname` element.
17
- #
18
14
# @example
19
15
# # bad
20
16
# File.open(Rails.root.join('db', 'schema.rb'))
@@ -32,17 +28,20 @@ module Rails
32
28
# Rails.root.join('db', 'schema.rb').write(content)
33
29
# Rails.root.join('db', 'schema.rb').binwrite(content)
34
30
#
35
- class RootPathnameMethods < Base
31
+ class RootPathnameMethods < Base # rubocop:disable Metrics/ClassLength
36
32
extend AutoCorrector
37
33
include RangeHelp
38
34
39
35
MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
40
36
41
- DIR_METHODS = %i[ children delete each_child empty? entries exist? glob mkdir open rmdir unlink ] . to_set . freeze
37
+ DIR_NON_PATHNAMES_RETURNED_METHODS = %i[ delete empty? exist? mkdir open rmdir unlink ] . to_set . freeze
38
+
39
+ DIR_PATHNAMES_RETURNED_METHODS = %i[ children each_child entries glob ] . to_set . freeze
42
40
43
- FILE_METHODS = %i[
41
+ DIR_METHODS = ( DIR_PATHNAMES_RETURNED_METHODS + DIR_NON_PATHNAMES_RETURNED_METHODS ) . freeze
42
+
43
+ FILE_NON_PATHNAME_RETURNED_METHODS = %i[
44
44
atime
45
- basename
46
45
binread
47
46
binwrite
48
47
birthtime
@@ -53,19 +52,16 @@ class RootPathnameMethods < Base
53
52
ctime
54
53
delete
55
54
directory?
56
- dirname
57
55
empty?
58
56
executable?
59
57
executable_real?
60
58
exist?
61
- expand_path
62
59
extname
63
60
file?
64
61
fnmatch
65
62
fnmatch?
66
63
ftype
67
64
grpowned?
68
- join
69
65
lchmod
70
66
lchown
71
67
lstat
@@ -77,9 +73,6 @@ class RootPathnameMethods < Base
77
73
readable?
78
74
readable_real?
79
75
readlines
80
- readlink
81
- realdirpath
82
- realpath
83
76
rename
84
77
setgid?
85
78
setuid?
@@ -102,6 +95,18 @@ class RootPathnameMethods < Base
102
95
zero?
103
96
] . to_set . freeze
104
97
98
+ FILE_PATHNAME_RETURNED_METHODS = %i[
99
+ basename
100
+ dirname
101
+ expand_path
102
+ join
103
+ readlink
104
+ realdirpath
105
+ realpath
106
+ ] . to_set . freeze
107
+
108
+ FILE_METHODS = ( FILE_PATHNAME_RETURNED_METHODS + FILE_NON_PATHNAME_RETURNED_METHODS ) . freeze
109
+
105
110
FILE_TEST_METHODS = %i[
106
111
blockdev?
107
112
chardev?
@@ -160,13 +165,24 @@ class RootPathnameMethods < Base
160
165
(send (const {nil? cbase} :Rails) {:root :public_path})
161
166
PATTERN
162
167
168
+ # @!method dir_pathnames_returned_method?(node)
169
+ def_node_matcher :dir_pathnames_returned_method? , <<~PATTERN
170
+ (send (const {nil? cbase} :Dir) DIR_PATHNAMES_RETURNED_METHODS ...)
171
+ PATTERN
172
+
173
+ # @!method file_pathname_returned_method?(node)
174
+ def_node_matcher :file_pathname_returned_method? , <<~PATTERN
175
+ (send (const {nil? cbase} {:IO :File}) FILE_PATHNAME_RETURNED_METHODS ...)
176
+ PATTERN
177
+
163
178
def on_send ( node )
164
179
evidence ( node ) do |method , path , args , rails_root |
165
180
add_offense ( node , message : format ( MSG , method : method , rails_root : rails_root . source ) ) do |corrector |
181
+ suffix = build_replacement_suffix ( node )
166
182
replacement = if dir_glob? ( node )
167
- build_path_glob_replacement ( path , method )
183
+ build_path_glob_replacement ( path , method , suffix )
168
184
else
169
- build_path_replacement ( path , method , args )
185
+ build_path_replacement ( path , method , args , suffix )
170
186
end
171
187
172
188
corrector . replace ( node , replacement )
@@ -183,15 +199,15 @@ def evidence(node)
183
199
yield ( method , path , args , rails_root )
184
200
end
185
201
186
- def build_path_glob_replacement ( path , method )
202
+ def build_path_glob_replacement ( path , method , suffix )
187
203
receiver = range_between ( path . source_range . begin_pos , path . children . first . loc . selector . end_pos ) . source
188
204
189
205
argument = path . arguments . one? ? path . first_argument . source : join_arguments ( path . arguments )
190
206
191
- "#{ receiver } .#{ method } (#{ argument } )"
207
+ "#{ receiver } .#{ method } (#{ argument } )#{ suffix } "
192
208
end
193
209
194
- def build_path_replacement ( path , method , args )
210
+ def build_path_replacement ( path , method , args , suffix )
195
211
path_replacement = path . source
196
212
if path . arguments? && !path . parenthesized_call?
197
213
path_replacement [ ' ' ] = '('
@@ -200,9 +216,20 @@ def build_path_replacement(path, method, args)
200
216
201
217
replacement = "#{ path_replacement } .#{ method } "
202
218
replacement += "(#{ args . map ( &:source ) . join ( ', ' ) } )" unless args . empty?
219
+ replacement += suffix
203
220
replacement
204
221
end
205
222
223
+ def build_replacement_suffix ( node )
224
+ if dir_pathnames_returned_method? ( node )
225
+ '.map(&:to_s)'
226
+ elsif file_pathname_returned_method? ( node )
227
+ '.to_s'
228
+ else
229
+ ''
230
+ end
231
+ end
232
+
206
233
def include_interpolation? ( arguments )
207
234
arguments . any? do |argument |
208
235
argument . children . any? { |child | child . respond_to? ( :begin_type? ) && child . begin_type? }
0 commit comments