rubocop-migration 0.1.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +58 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +11 -1
  6. data/Gemfile.lock +89 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +41 -21
  9. data/Rakefile +9 -3
  10. data/config/default.yml +151 -0
  11. data/data/reserved_words_mysql.txt +750 -0
  12. data/lib/rubocop/cop/migration/add_check_constraint.rb +111 -0
  13. data/lib/rubocop/cop/migration/add_column_with_default_value.rb +229 -0
  14. data/lib/rubocop/cop/migration/add_foreign_key.rb +166 -0
  15. data/lib/rubocop/cop/migration/add_index_columns_count.rb +114 -0
  16. data/lib/rubocop/cop/migration/add_index_concurrently.rb +164 -0
  17. data/lib/rubocop/cop/migration/add_index_duplicate.rb +183 -0
  18. data/lib/rubocop/cop/migration/batch_in_batches.rb +95 -0
  19. data/lib/rubocop/cop/migration/batch_in_transaction.rb +83 -0
  20. data/lib/rubocop/cop/migration/batch_with_throttling.rb +108 -0
  21. data/lib/rubocop/cop/migration/change_column.rb +113 -0
  22. data/lib/rubocop/cop/migration/change_column_null.rb +128 -0
  23. data/lib/rubocop/cop/migration/create_table_force.rb +89 -0
  24. data/lib/rubocop/cop/migration/jsonb.rb +131 -0
  25. data/lib/rubocop/cop/migration/remove_column.rb +246 -0
  26. data/lib/rubocop/cop/migration/rename_column.rb +81 -0
  27. data/lib/rubocop/cop/migration/rename_table.rb +79 -0
  28. data/lib/rubocop/cop/migration/reserved_word_mysql.rb +232 -0
  29. data/lib/rubocop/migration/config_loader.rb +51 -0
  30. data/lib/rubocop/migration/cop_concerns/batch_processing.rb +34 -0
  31. data/lib/rubocop/migration/cop_concerns/column_type_method.rb +28 -0
  32. data/lib/rubocop/migration/cop_concerns/disable_ddl_transaction.rb +51 -0
  33. data/lib/rubocop/migration/cop_concerns.rb +11 -0
  34. data/lib/rubocop/migration/rubocop_extension.rb +15 -0
  35. data/lib/rubocop/migration/version.rb +4 -2
  36. data/lib/rubocop/migration.rb +23 -0
  37. data/rubocop-migration.gemspec +25 -29
  38. metadata +50 -108
  39. data/.gitignore +0 -15
  40. data/bin/console +0 -14
  41. data/bin/setup +0 -8
  42. data/circle.yml +0 -6
  43. data/lib/rubocop/cop/migration/add_index_non_concurrently.rb +0 -66
  44. data/lib/rubocop-migration.rb +0 -11
  45. data/vendor/.gitkeep +0 -0
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Migration
6
+ # Avoid changing column type that is in use.
7
+ #
8
+ # Changing the type of a column causes the entire table to be rewritten.
9
+ # During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
10
+ #
11
+ # Some changes don’t require a table rewrite and are safe in PostgreSQL:
12
+ #
13
+ # Type | Safe Changes
14
+ # --- | ---
15
+ # `cidr` | Changing to `inet`
16
+ # `citext` | Changing to `text` if not indexed, changing to `string` with no `:limit` if not indexed
17
+ # `datetime` | Increasing or removing `:precision`, changing to `timestamptz` when session time zone is UTC in Postgres 12+
18
+ # `decimal` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
19
+ # `interval` | Increasing or removing `:precision`
20
+ # `numeric` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
21
+ # `string` | Increasing or removing `:limit`, changing to `text`, changing `citext` if not indexed
22
+ # `text` | Changing to `string` with no `:limit`, changing to `citext` if not indexed
23
+ # `time` | Increasing or removing `:precision`
24
+ # `timestamptz` | Increasing or removing `:limit`, changing to `datetime` when session time zone is UTC in Postgres 12+
25
+ #
26
+ # And some in MySQL and MariaDB:
27
+ #
28
+ # Type | Safe Changes
29
+ # --- | ---
30
+ # `string` | Increasing `:limit` from under 255 up to 255, increasing `:limit` from over 255 to the max
31
+ #
32
+ # A safer approach is to:
33
+ #
34
+ # 1. Create a new column
35
+ # 2. Write to both columns
36
+ # 3. Backfill data from the old column to the new column
37
+ # 4. Move reads from the old column to the new column
38
+ # 5. Stop writing to the old column
39
+ # 6. Drop the old column
40
+ #
41
+ # @safety
42
+ # Only meaningful if the table is in use and the type change is really unsafe as described above.
43
+ #
44
+ # @example
45
+ # # bad
46
+ # class ChangeUsersSomeColumnType < ActiveRecord::Migration[7.0]
47
+ # def change
48
+ # change_column :users, :some_column, :new_type
49
+ # end
50
+ # end
51
+ #
52
+ # # good
53
+ # class AddUsersAnotherColumn < ActiveRecord::Migration[7.0]
54
+ # def change
55
+ # add_column :users, :another_column, :new_type
56
+ # end
57
+ # end
58
+ #
59
+ # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
60
+ # def change
61
+ # remove_column :users, :some_column
62
+ # end
63
+ # end
64
+ class ChangeColumn < RuboCop::Cop::Base
65
+ MSG = 'Avoid changing column type that is in use.'
66
+
67
+ RESTRICT_ON_SEND = %i[
68
+ change
69
+ change_column
70
+ ].freeze
71
+
72
+ # @param node [RuboCop::AST::SendNode]
73
+ # @return [void]
74
+ def on_send(node)
75
+ return unless bad?(node)
76
+
77
+ add_offense(node)
78
+ end
79
+
80
+ private
81
+
82
+ # @!method change?(node)
83
+ # @param node [RuboCop::AST::SendNode]
84
+ # @return [Boolean]
85
+ def_node_matcher :change?, <<~PATTERN
86
+ (send
87
+ lvar
88
+ :change
89
+ ...
90
+ )
91
+ PATTERN
92
+
93
+ # @!method change_column?(node)
94
+ # @param node [RuboCop::AST::SendNode]
95
+ # @return [Boolean]
96
+ def_node_matcher :change_column?, <<~PATTERN
97
+ (send
98
+ nil?
99
+ :change_column
100
+ ...
101
+ )
102
+ PATTERN
103
+
104
+ # @param node [RuboCop::AST::SendNode]
105
+ # @return [Boolean]
106
+ def bad?(node)
107
+ change?(node) ||
108
+ change_column?(node)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Migration
6
+ # Avoid simply setting `NOT NULL` constraint on an existing column in PostgreSQL.
7
+ #
8
+ # It blocks reads and writes while every row is checked.
9
+ # In PostgreSQL 12+, you can safely set `NOT NULL` constraint if corresponding check constraint exists.
10
+ #
11
+ # @safety
12
+ # Only meaningful in PostgreSQL 12+.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # class SetNotNullColumnConstraintToUsersName < ActiveRecord::Migration[7.0]
17
+ # def change
18
+ # change_column_null :users, :name, false
19
+ # end
20
+ # end
21
+ #
22
+ # # good
23
+ # class SetNotNullCheckConstraintToUsersName < ActiveRecord::Migration[7.0]
24
+ # def change
25
+ # add_check_constraint :users, 'name IS NOT NULL', name: 'users_name_is_not_null', validate: false
26
+ # end
27
+ # end
28
+ #
29
+ # class ReplaceNotNullConstraintOnUsersName < ActiveRecord::Migration[7.0]
30
+ # def change
31
+ # validate_constraint :users, name: 'users_name_is_not_null'
32
+ # change_column_null :users, :name, false
33
+ # remove_check_constraint :users, name: 'users_name_is_not_null'
34
+ # end
35
+ # end
36
+ class ChangeColumnNull < RuboCop::Cop::Base
37
+ extend AutoCorrector
38
+
39
+ MSG = 'Avoid simply setting `NOT NULL` constraint on an existing column in PostgreSQL.'
40
+
41
+ RESTRICT_ON_SEND = %i[
42
+ change_column_null
43
+ ].freeze
44
+
45
+ # @param node [RuboCop::AST::SendNode]
46
+ # @return [void]
47
+ def on_send(node)
48
+ return if in_second_migration?(node)
49
+
50
+ add_offense(node) do |corrector|
51
+ autocorrect(corrector, node)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # @!method parse_table_name_and_column_name(node)
58
+ # @param node [RuboCop::AST::SendNode]
59
+ # @return [Array<Symbol>, nil]
60
+ def_node_matcher :parse_table_name_and_column_name, <<~PATTERN
61
+ (send
62
+ nil?
63
+ _
64
+ ({str sym} $_)
65
+ ({str sym} $_)
66
+ ...
67
+ )
68
+ PATTERN
69
+
70
+ # @!method remove_check_constraint?(node)
71
+ # @param node [RuboCop::AST::SendNode]
72
+ # @return [Boolean]
73
+ def_node_matcher :remove_check_constraint?, <<~PATTERN
74
+ (send nil? :remove_check_constraint ...)
75
+ PATTERN
76
+
77
+ # @!method validate_constraint?(node)
78
+ # @param node [RuboCop::AST::SendNode]
79
+ # @return [Boolean]
80
+ def_node_matcher :validate_constraint?, <<~PATTERN
81
+ (send nil? :validate_constraint ...)
82
+ PATTERN
83
+
84
+ # @param corrector [RuboCop::Cop::Corrector]
85
+ # @param node [RuboCop::AST::SendNode]
86
+ # @return [void]
87
+ def autocorrect(
88
+ corrector,
89
+ node
90
+ )
91
+ table_name, column_name = parse_table_name_and_column_name(node)
92
+ corrector.replace(
93
+ node,
94
+ format(
95
+ "add_check_constraint :%<table>s, '%<column>s IS NOT NULL', name: '%<constraint>s', validate: false",
96
+ column: column_name,
97
+ constraint: "#{table_name}_#{column_name}_is_not_null",
98
+ table: table_name
99
+ )
100
+ )
101
+ end
102
+
103
+ # @param node [RuboCop::AST::SendNode]
104
+ # @return [Boolean]
105
+ def called_after_validate_constraint?(node)
106
+ node.left_siblings.any? do |sibling|
107
+ validate_constraint?(sibling)
108
+ end
109
+ end
110
+
111
+ # @param node [RuboCop::AST::SendNode]
112
+ # @return [Boolean]
113
+ def called_before_remove_check_constraint?(node)
114
+ node.right_siblings.any? do |sibling|
115
+ remove_check_constraint?(sibling)
116
+ end
117
+ end
118
+
119
+ # @param node [RuboCop::AST::SendNode]
120
+ # @return [Boolean]
121
+ def in_second_migration?(node)
122
+ called_after_validate_constraint?(node) ||
123
+ called_before_remove_check_constraint?(node)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Migration
6
+ # Create tables without `force: true` option.
7
+ #
8
+ # The `force: true` option can drop an existing table.
9
+ # If you indend to drop an existing table, explicitly call `drop_table` first.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # class CreateUsers < ActiveRecord::Migration[7.0]
14
+ # def change
15
+ # create_table :users, force: true
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # class CreateUsers < ActiveRecord::Migration[7.0]
21
+ # def change
22
+ # create_table :users
23
+ # end
24
+ # end
25
+ class CreateTableForce < RuboCop::Cop::Base
26
+ extend AutoCorrector
27
+
28
+ include RangeHelp
29
+
30
+ MSG = 'Create tables without `force: true` option.'
31
+
32
+ RESTRICT_ON_SEND = %i[
33
+ create_table
34
+ ].freeze
35
+
36
+ # @param node [RuboCop::AST::SendNode]
37
+ # @return [void]
38
+ def on_send(node)
39
+ option_node = option_force_true_from_create_table(node)
40
+ return unless option_node
41
+
42
+ add_offense(option_node) do |corrector|
43
+ autocorrect(corrector, option_node)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # @!method option_force_true_from_create_table(node)
50
+ # @param node [RuboCop::AST::SendNode]
51
+ # @return [RuboCop::AST::PairNode, nil]
52
+ def_node_matcher :option_force_true_from_create_table, <<~PATTERN
53
+ (send
54
+ nil?
55
+ :create_table
56
+ _
57
+ (hash
58
+ <
59
+ $(pair
60
+ (sym :force)
61
+ true
62
+ )
63
+ ...
64
+ >
65
+ )
66
+ )
67
+ PATTERN
68
+
69
+ # @param corrector [RuboCop::Cop::Corrector]
70
+ # @param node [RuboCop::AST::PairNode]
71
+ # @return [void]
72
+ def autocorrect(
73
+ corrector,
74
+ node
75
+ )
76
+ corrector.remove(
77
+ range_with_surrounding_comma(
78
+ range_with_surrounding_space(
79
+ node.location.expression,
80
+ side: :left
81
+ ),
82
+ :left
83
+ )
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Migration
6
+ # Prefer `jsonb` to `json`.
7
+ #
8
+ # In PostgreSQL, there is no equality operator for the json column type,
9
+ # which can cause errors for existing `SELECT DISTINCT` queries in your application.
10
+ #
11
+ # @safety
12
+ # Only meaningful in PostgreSQL.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # add_column :users, :properties, :json
17
+ #
18
+ # # good
19
+ # add_column :users, :properties, :jsonb
20
+ class Jsonb < RuboCop::Cop::Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Prefer `jsonb` to `json`.'
24
+
25
+ RESTRICT_ON_SEND = %i[
26
+ add_column
27
+ change
28
+ change_column
29
+ json
30
+ ].freeze
31
+
32
+ # @param node [RuboCop::AST::SendNode]
33
+ # @return [void]
34
+ def on_send(node)
35
+ json_range = json_range_from_target_send_node(node)
36
+ return unless json_range
37
+
38
+ add_offense(json_range) do |corrector|
39
+ corrector.replace(json_range, 'jsonb')
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ # @!method json_type_node_from_add_column(node)
46
+ # @param node [RuboCop::AST::SendNode]
47
+ # @return [RuboCop::AST::SymNode, nil]
48
+ def_node_matcher :json_type_node_from_add_column, <<~PATTERN
49
+ (send
50
+ nil?
51
+ _
52
+ _
53
+ _
54
+ $(sym :json)
55
+ )
56
+ PATTERN
57
+ alias json_type_node_from_change_column json_type_node_from_add_column
58
+
59
+ # @!method json_type_node_from_change(node)
60
+ # @param node [RuboCop::AST::SendNode]
61
+ # @return [RuboCop::AST::SymNode, nil]
62
+ def_node_matcher :json_type_node_from_change, <<~PATTERN
63
+ (send
64
+ lvar
65
+ _
66
+ _
67
+ $(sym :json)
68
+ )
69
+ PATTERN
70
+
71
+ # @!method json_type_node_from_json(node)
72
+ # @param node [RuboCop::AST::SendNode]
73
+ # @return [RuboCop::AST::SendNode, nil]
74
+ def_node_matcher :json_type_node_from_json, <<~PATTERN
75
+ $(send
76
+ lvar
77
+ _
78
+ ...
79
+ )
80
+ PATTERN
81
+
82
+ # @param corrector [RuboCop::Cop::Corrector]
83
+ # @param node [RuboCop::AST::SendNode, RuboCop::AST::SymNode]
84
+ # @return [void]
85
+ def autocorrect(
86
+ corrector,
87
+ node
88
+ )
89
+ corrector.replace(node, 'jsonb')
90
+ end
91
+
92
+ # @param node [RuboCop::AST::SendNode]
93
+ # @return [RuboCop::AST::SymNode, nil]
94
+ def json_node_from_target_send_node(node)
95
+ case node.method_name
96
+ when :add_column
97
+ json_type_node_from_add_column(node)
98
+ when :change
99
+ json_type_node_from_change(node)
100
+ when :change_column
101
+ json_type_node_from_change_column(node)
102
+ when :json
103
+ json_type_node_from_json(node)
104
+ end
105
+ end
106
+
107
+ # @param node [RuboCop::AST::SendNode, RuboCop::AST::SymNode]
108
+ # @return [Parser::Source::Range]
109
+ def json_range_from_json_node(node)
110
+ case node.type
111
+ when :send
112
+ node.location.selector
113
+ when :sym
114
+ node.location.expression.with(
115
+ begin_pos: node.location.expression.begin_pos + 1
116
+ )
117
+ end
118
+ end
119
+
120
+ # @param node [RuboCop::AST::SendNode]
121
+ # @return [Parser::Source::Range]
122
+ def json_range_from_target_send_node(node)
123
+ json_node = json_node_from_target_send_node(node)
124
+ return unless json_node
125
+
126
+ json_range_from_json_node(json_node)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/inflector'
4
+ require 'pathname'
5
+
6
+ module RuboCop
7
+ module Cop
8
+ module Migration
9
+ # Make sure the column is already ignored by the running app before removing it.
10
+ #
11
+ # Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
12
+ #
13
+ # @safety
14
+ # The logic to check if it is included in `ignored_columns` may fail.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # class User < ApplicationRecord
19
+ # end
20
+ #
21
+ # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
22
+ # def change
23
+ # remove_column :users, :some_column
24
+ # end
25
+ # end
26
+ #
27
+ # # good
28
+ # class User < ApplicationRecord
29
+ # self.ignored_columns += %w[some_column]
30
+ # end
31
+ #
32
+ # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
33
+ # def change
34
+ # remove_column :users, :some_column
35
+ # end
36
+ # end
37
+ class RemoveColumn < RuboCop::Cop::Base
38
+ MSG = 'Make sure the column is already ignored by the running app before removing it.'
39
+
40
+ RESTRICT_ON_SEND = %i[
41
+ remove_column
42
+ ].freeze
43
+
44
+ # @param node [RuboCop::AST::SendNode]
45
+ # @return [void]
46
+ def on_send(node)
47
+ return unless bad?(node)
48
+
49
+ add_offense(node)
50
+ end
51
+
52
+ private
53
+
54
+ # @!method ignored_columns?(node)
55
+ # @param node [RuboCop::AST::Node]
56
+ # @return [Boolean]
57
+ def_node_matcher :ignored_columns?, <<~PATTERN
58
+ (send
59
+ self
60
+ :ignored_columns
61
+ ...
62
+ )
63
+ PATTERN
64
+
65
+ # @!method remove_column?(node)
66
+ # @param node [RuboCop::AST::SendNode]
67
+ # @return [Boolean]
68
+ def_node_matcher :remove_column?, <<~PATTERN
69
+ (send
70
+ nil?
71
+ :remove_column
72
+ ...
73
+ )
74
+ PATTERN
75
+
76
+ # @!method ignored_column_nodes_from(node)
77
+ # @param node [RuboCop::AST::Node]
78
+ # @return [Array<String, Symbol>, nil]
79
+ def_node_matcher :ignored_column_nodes_from, <<~PATTERN
80
+ `{
81
+ (op_asgn
82
+ (send
83
+ self
84
+ :ignored_columns
85
+ ...
86
+ )
87
+ :+
88
+ (array
89
+ ({str sym} $_)*
90
+ )
91
+ )
92
+
93
+ (send
94
+ self
95
+ :ignored_columns=
96
+ (array
97
+ ({str sym} $_)*
98
+ )
99
+ )
100
+ }
101
+ PATTERN
102
+
103
+ # @param node [RuboCop::AST::SendNode]
104
+ # @return [Boolean]
105
+ def bad?(node)
106
+ remove_column?(node) &&
107
+ !ignored?(node)
108
+ end
109
+
110
+ # @param node [RuboCop::AST::SendNode]
111
+ # @return [String, nil]
112
+ def find_column_name_from(node)
113
+ node.arguments[1].value&.to_s
114
+ end
115
+
116
+ # @param table_name [String]
117
+ # @return [Array<String>]
118
+ def find_ignored_column_names_from(table_name)
119
+ pathname = model_pathname_from(
120
+ singularize(table_name)
121
+ )
122
+ return [] unless pathname.exist?
123
+
124
+ ignored_column_nodes = ignored_column_nodes_from(
125
+ parse(
126
+ content: pathname.read,
127
+ path: pathname.to_s
128
+ )
129
+ )
130
+ return [] unless ignored_column_nodes
131
+
132
+ ignored_column_nodes.map(&:to_s)
133
+ end
134
+
135
+ # @param node [RuboCop::AST::SendNode]
136
+ # @return [String, nil]
137
+ def find_table_name_from(node)
138
+ node.arguments[0].value&.to_s
139
+ end
140
+
141
+ # @param node [RuboCop::AST::SendNode]
142
+ # @return [Boolean]
143
+ def ignored?(node)
144
+ find_ignored_column_names_from(
145
+ find_table_name_from(node)
146
+ ).include?(
147
+ find_column_name_from(node)
148
+ )
149
+ end
150
+
151
+ # @param snake_cased_model_name [String]
152
+ # @return [Pathname]
153
+ def model_pathname_from(snake_cased_model_name)
154
+ ::Pathname.new("app/models/#{snake_cased_model_name}.rb")
155
+ end
156
+
157
+ # @param content [String]
158
+ # @param path [String]
159
+ # @return [RuboCop::AST::Node]
160
+ def parse(
161
+ content:,
162
+ path:
163
+ )
164
+ Parser.call(
165
+ content: content,
166
+ path: path,
167
+ target_ruby_version: target_ruby_version
168
+ )
169
+ end
170
+
171
+ # @param plural [String]
172
+ # @return [String]
173
+ def singularize(plural)
174
+ ::ActiveSupport::Inflector.singularize(plural)
175
+ end
176
+
177
+ class Parser
178
+ class << self
179
+ # @param content [String]
180
+ # @param path [String]
181
+ # @param target_ruby_version [#to_s]
182
+ # @return [RuboCop::AST::Node]
183
+ def call(
184
+ content:,
185
+ path:,
186
+ target_ruby_version:
187
+ )
188
+ new(
189
+ content: content,
190
+ path: path,
191
+ target_ruby_version: target_ruby_version
192
+ ).call
193
+ end
194
+ end
195
+
196
+ # @param content [String]
197
+ # @param path [String]
198
+ # @param target_ruby_version [#to_s]
199
+ def initialize(
200
+ content:,
201
+ path:,
202
+ target_ruby_version:
203
+ )
204
+ @content = content
205
+ @path = path
206
+ @target_ruby_version = target_ruby_version
207
+ end
208
+
209
+ # @return [RuboCop::AST::Node]
210
+ def call
211
+ parser.parse(buffer)
212
+ end
213
+
214
+ private
215
+
216
+ # @return [Parser::Source::Buffer]
217
+ def buffer
218
+ ::Parser::Source::Buffer.new(
219
+ @path,
220
+ source: @content
221
+ )
222
+ end
223
+
224
+ # @return [RuboCop::AST::Builder]
225
+ def builder
226
+ ::RuboCop::AST::Builder.new
227
+ end
228
+
229
+ def parser
230
+ parser_class.new(builder)
231
+ end
232
+
233
+ # @return [Class]
234
+ def parser_class
235
+ ::Parser.const_get(parser_class_name)
236
+ end
237
+
238
+ # @return [String]
239
+ def parser_class_name
240
+ "Ruby#{@target_ruby_version.to_s.delete('.')}"
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end