alba_migration 0.0.2 → 0.0.4
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/CHANGELOG.md +8 -0
- data/lib/alba_migration/cli.rb +1 -4
- data/lib/alba_migration/snippet/attribute_rewritable.rb +334 -0
- data/lib/alba_migration/snippet/superclass_rewritable.rb +52 -0
- data/lib/alba_migration/snippet.rb +12 -239
- data/lib/alba_migration/version.rb +1 -1
- data/sig/generated/alba_migration/snippet/attribute_rewritable.rbs +10 -0
- data/sig/generated/alba_migration/snippet/superclass_rewritable.rbs +10 -0
- data/sig/generated/alba_migration/snippet.rbs +8 -6
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d8b22bb77ee9707b3787fbef95b63722b8303ccc02044e905174b673b024517
|
4
|
+
data.tar.gz: f9f5d8c943160355a7fbd9e38308347929df48afc6062751cd8a5f38ae0f1984
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61967197eadc4abb078a2563d0cf8b118872f61373ef964e35267e2d819d6e7a94ab3fbf720a32bb8c75f94ff943cee2c01f7d8a38bb971c9c25062f6da70887
|
7
|
+
data.tar.gz: 732ffb30e8a855349c4a7d8602deec121ee51ce4d0dc864d877da6ef62eb3444a5fcab14c8818b1219a8391192dcb0df290cbd47537c7f0668da8bc9454cc2a5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# GHANGELOG
|
2
2
|
|
3
|
+
## 0.0.4 (2024-06-08)
|
4
|
+
|
5
|
+
- Extend support for attribute rewriting
|
6
|
+
|
7
|
+
## 0.0.3 (2024-06-07)
|
8
|
+
|
9
|
+
- Add gem dependency for easier installation
|
10
|
+
|
3
11
|
## 0.0.2 (2024-05-19)
|
4
12
|
|
5
13
|
- Add attribute method rewriter for better AMS to Alba conversion
|
data/lib/alba_migration/cli.rb
CHANGED
@@ -28,10 +28,7 @@ module AlbaMigration
|
|
28
28
|
exit(1)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
snippets.each do |rewriter|
|
33
|
-
rewriter.process
|
34
|
-
end
|
31
|
+
AlbaMigration::Snippet.process_snippets(migrate_file_path: matched_files)
|
35
32
|
end
|
36
33
|
end
|
37
34
|
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
require "synvert/core"
|
6
|
+
require "standard"
|
7
|
+
|
8
|
+
module AlbaMigration
|
9
|
+
class Snippet
|
10
|
+
module AttributeRewritable
|
11
|
+
# @rbs return: Synvert::Core::Rewriter
|
12
|
+
def attribute_method_rewriter
|
13
|
+
migrate_file_path = @migrate_file_path
|
14
|
+
|
15
|
+
Synvert::Core::Rewriter.new "alba_migration", "rewrite_attribute_method" do
|
16
|
+
description <<~EOS
|
17
|
+
It migrates ActiveModelSerializers attribute syntax to Alba attribute syntax.
|
18
|
+
|
19
|
+
Example:
|
20
|
+
|
21
|
+
# 1. Simple attribute methods grouped
|
22
|
+
```ruby
|
23
|
+
class AttributeResource
|
24
|
+
attribute(:object)
|
25
|
+
attribute(:user_id)
|
26
|
+
attribute :user_name
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
=>
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class AttributeResource
|
34
|
+
attributes :object, :user_id, :user_name
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
# 2. attribute with block
|
39
|
+
```ruby
|
40
|
+
class AttributeResource
|
41
|
+
attribute(:object) { 'access_token' }
|
42
|
+
attribute(:user_id) { object.user_id }
|
43
|
+
attribute :user_name
|
44
|
+
def user_name
|
45
|
+
object.user.name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
=>
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class AttributeResource
|
54
|
+
attribute :object do
|
55
|
+
'access_token'
|
56
|
+
end
|
57
|
+
attribute :user_id do |object|
|
58
|
+
object.user_id
|
59
|
+
end
|
60
|
+
attribute :user_name do |object|
|
61
|
+
object.user.name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
# 3. attribute :name + def name ... end
|
67
|
+
```ruby
|
68
|
+
class AttributeResource
|
69
|
+
attribute :user_name
|
70
|
+
def user_name
|
71
|
+
object.user.name
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
=>
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class AttributeResource
|
80
|
+
attribute :user_name do |object|
|
81
|
+
object.user.name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
# 4. attributes :a, :b, ... + def a ... end
|
87
|
+
```ruby
|
88
|
+
class AttributeResource
|
89
|
+
attributes :user_name, :user_id
|
90
|
+
def user_name
|
91
|
+
object.user.name
|
92
|
+
end
|
93
|
+
def user_id
|
94
|
+
object.user.id
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
=>
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class AttributeResource
|
103
|
+
attribute :user_name do |object|
|
104
|
+
object.user.name
|
105
|
+
end
|
106
|
+
attribute :user_id do |object|
|
107
|
+
object.user.id
|
108
|
+
end
|
109
|
+
end
|
110
|
+
```
|
111
|
+
EOS
|
112
|
+
|
113
|
+
configure(parser: "parser")
|
114
|
+
|
115
|
+
within_files migrate_file_path do
|
116
|
+
# Rewrite the entire class at once
|
117
|
+
with_node type: "class" do
|
118
|
+
class_name = node.children[0].loc.expression.source
|
119
|
+
superclass_node = node.children[1]
|
120
|
+
superclass_src = superclass_node ? " < #{superclass_node.loc.expression.source}" : ""
|
121
|
+
class_body = node.children[2]
|
122
|
+
# If class_body is a :begin node, loop through its children. Otherwise, treat it as a single node in an array.
|
123
|
+
body_nodes = if class_body && class_body.type == :begin
|
124
|
+
class_body.children
|
125
|
+
elsif class_body
|
126
|
+
[class_body]
|
127
|
+
else
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Detect include Alba::Resource
|
132
|
+
include_line = nil
|
133
|
+
body_nodes.each do |child|
|
134
|
+
next unless child.is_a?(Parser::AST::Node)
|
135
|
+
if child.type == :send && child.children[1] == :include
|
136
|
+
include_line = " " + child.loc.expression.source
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# 1-1. Collect attribute(:xxx) { ... } style
|
141
|
+
attribute_blocks = []
|
142
|
+
body_nodes.each do |child|
|
143
|
+
next unless child.is_a?(Parser::AST::Node)
|
144
|
+
if child.type == :block &&
|
145
|
+
child.children[0] && child.children[0].is_a?(Parser::AST::Node) && child.children[0].type == :send &&
|
146
|
+
child.children[0].children[1] == :attribute
|
147
|
+
|
148
|
+
# Process arguments
|
149
|
+
arg_node = child.children[0].children[2]
|
150
|
+
arg = if arg_node.is_a?(Parser::AST::Node) && arg_node.type == :sym
|
151
|
+
":#{arg_node.children[0]}"
|
152
|
+
else
|
153
|
+
((arg_node.respond_to?(:loc) && arg_node.loc.respond_to?(:expression)) ? arg_node.loc.expression.source : arg_node.to_s)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Process block parameters
|
157
|
+
block_params = child.children[1] ? child.children[1].children.map { |c| c.children[0].to_s }.join(", ") : ""
|
158
|
+
|
159
|
+
# Process block body
|
160
|
+
block_body = child.children[2] ? child.children[2].loc.expression.source : ""
|
161
|
+
|
162
|
+
# Add 'object' to parameters if used in block body
|
163
|
+
if block_params.strip.empty? && block_body.include?("object")
|
164
|
+
block_params = "object"
|
165
|
+
end
|
166
|
+
|
167
|
+
# Generate new code (no indentation added)
|
168
|
+
attribute_blocks << if block_params.strip.empty?
|
169
|
+
"attribute #{arg} do\n#{block_body}\nend"
|
170
|
+
else
|
171
|
+
"attribute #{arg} do |#{block_params.strip}|\n#{block_body}\nend"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# 1-2. Collect attribute :name + def name ... end style
|
177
|
+
attr_names = {}
|
178
|
+
other_methods = []
|
179
|
+
|
180
|
+
# First, collect attribute :name
|
181
|
+
body_nodes.each do |child|
|
182
|
+
next unless child.is_a?(Parser::AST::Node)
|
183
|
+
if child.type == :send &&
|
184
|
+
child.children[1] == :attribute &&
|
185
|
+
child.children.length == 3 &&
|
186
|
+
child.children[2].is_a?(Parser::AST::Node) &&
|
187
|
+
child.children[2].type == :sym
|
188
|
+
|
189
|
+
attr_name = child.children[2].children[0].to_s
|
190
|
+
attr_names[attr_name] = nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Extract attribute names to be grouped as attributes
|
195
|
+
attributes_group = []
|
196
|
+
attributes_methods = {}
|
197
|
+
body_nodes.each do |child|
|
198
|
+
next unless child.is_a?(Parser::AST::Node)
|
199
|
+
# attribute :xxx
|
200
|
+
if child.type == :send && child.children[1] == :attribute && child.children.length == 3 && child.children[2].is_a?(Parser::AST::Node) && child.children[2].type == :sym
|
201
|
+
method_name = child.children[2].children[0].to_s
|
202
|
+
unless body_nodes.any? { |c| c.is_a?(Parser::AST::Node) && c.type == :def && c.children[0].to_s == method_name }
|
203
|
+
attributes_group << ":#{method_name}"
|
204
|
+
end
|
205
|
+
# attributes :xxx, :yyy, ...
|
206
|
+
elsif child.type == :send && child.children[1] == :attributes
|
207
|
+
# attributes [:a, :b] or attributes %i[a b] style
|
208
|
+
if child.children.length == 3 && child.children[2].is_a?(Parser::AST::Node)
|
209
|
+
arg_node = child.children[2]
|
210
|
+
if arg_node.type == :array
|
211
|
+
syms = arg_node.children.select { |n| n.is_a?(Parser::AST::Node) && n.type == :sym }
|
212
|
+
syms.each { |sym_node| attributes_group << ":#{sym_node.children[0]}" }
|
213
|
+
elsif arg_node.type == :sym_array
|
214
|
+
# For %i[a b] style
|
215
|
+
arg_node.children.each do |sym_lit|
|
216
|
+
attributes_group << ":#{sym_lit}" # sym_lit is a Symbol
|
217
|
+
end
|
218
|
+
end
|
219
|
+
else
|
220
|
+
child.children[2..].each do |a|
|
221
|
+
if a.is_a?(Parser::AST::Node) && a.type == :sym
|
222
|
+
method_name = a.children[0].to_s
|
223
|
+
def_node = body_nodes.find { |c| c.is_a?(Parser::AST::Node) && c.type == :def && c.children[0].to_s == method_name }
|
224
|
+
if def_node
|
225
|
+
method_body = def_node.children[2]
|
226
|
+
method_src = method_body ? method_body.loc.expression.source : ""
|
227
|
+
attributes_methods[method_name] = "attribute :#{method_name} do |object|\n#{method_src}\nend"
|
228
|
+
else
|
229
|
+
attributes_group << ":#{method_name}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# 2. Generate new code for the entire class
|
238
|
+
new_lines = ["class #{class_name}#{superclass_src}"]
|
239
|
+
|
240
|
+
# Add include Alba::Resource first if present
|
241
|
+
if include_line
|
242
|
+
new_lines << include_line
|
243
|
+
new_lines << ""
|
244
|
+
end
|
245
|
+
|
246
|
+
# Add attributes group after include
|
247
|
+
unless attributes_group.empty?
|
248
|
+
new_lines << " attributes #{attributes_group.join(", ")}"
|
249
|
+
new_lines << ""
|
250
|
+
end
|
251
|
+
|
252
|
+
# Add attribute blocks (no extra blank lines)
|
253
|
+
new_lines.concat(attribute_blocks)
|
254
|
+
|
255
|
+
# Convert attribute :name + def name ... end pairs
|
256
|
+
attributeized_method_names = []
|
257
|
+
body_nodes.each do |child|
|
258
|
+
next unless child.is_a?(Parser::AST::Node)
|
259
|
+
next unless child.type == :def
|
260
|
+
method_name = child.children[0].to_s
|
261
|
+
method_body = child.children[2]
|
262
|
+
method_src = method_body ? method_body.loc.expression.source : ""
|
263
|
+
# attribute :name + def name ... end
|
264
|
+
if attr_names.key?(method_name)
|
265
|
+
attr_names[method_name] = "attribute :#{method_name} do |object|\n#{method_src}\nend"
|
266
|
+
attributeized_method_names << method_name
|
267
|
+
attributes_group.delete(":#{method_name}")
|
268
|
+
next
|
269
|
+
end
|
270
|
+
# attributes :name + def name ... end
|
271
|
+
if attributes_methods.key?(method_name)
|
272
|
+
new_lines << attributes_methods[method_name]
|
273
|
+
attributeized_method_names << method_name
|
274
|
+
attributes_group.delete(":#{method_name}")
|
275
|
+
next
|
276
|
+
end
|
277
|
+
# def name ... object.xxx ... end
|
278
|
+
if method_src.strip.start_with?("object.")
|
279
|
+
new_lines << "attribute :#{method_name} do |object|\n#{method_src}\nend"
|
280
|
+
attributeized_method_names << method_name
|
281
|
+
attributes_group.delete(":#{method_name}")
|
282
|
+
next
|
283
|
+
end
|
284
|
+
# def accounts ... object.account_id ... end (plural method names are converted as-is to attribute)
|
285
|
+
if method_name.end_with?("s") && method_body
|
286
|
+
new_lines << "attribute :#{method_name} do |object|\n#{method_src}\nend"
|
287
|
+
attributeized_method_names << method_name
|
288
|
+
attributes_group.delete(":#{method_name}")
|
289
|
+
next
|
290
|
+
end
|
291
|
+
# All other methods go to other_methods (excluding those already converted to attribute)
|
292
|
+
unless attributeized_method_names.include?(method_name)
|
293
|
+
method_src_full = child.loc.expression.source
|
294
|
+
other_methods << method_src_full
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Add conversion results of attribute :name + def name ... end (no extra blank lines)
|
299
|
+
attr_blocks = []
|
300
|
+
attr_names.each do |k, _|
|
301
|
+
def_node = body_nodes.find { |c| c.is_a?(Parser::AST::Node) && c.type == :def && c.children[0].to_s == k }
|
302
|
+
if def_node && def_node.children[2] && def_node.children[2].loc.expression.source.strip.start_with?("object.")
|
303
|
+
method_src = def_node.children[2].loc.expression.source
|
304
|
+
attr_blocks << "attribute :#{k} do |object|\n#{method_src}\nend"
|
305
|
+
# Also remove from attributes_group
|
306
|
+
attributes_group.delete(":#{k}")
|
307
|
+
end
|
308
|
+
end
|
309
|
+
new_lines.concat(attr_blocks)
|
310
|
+
# Add other methods (def not corresponding to attribute) (no extra blank lines)
|
311
|
+
filtered_other_methods = other_methods.reject do |src|
|
312
|
+
attributeized_method_names.any? { |name| src.match?(/def\s+#{name}\b/) }
|
313
|
+
end
|
314
|
+
new_lines.concat(filtered_other_methods)
|
315
|
+
|
316
|
+
new_lines << "end"
|
317
|
+
|
318
|
+
# Remove consecutive blank lines except immediately after attributes or include Alba::Resource
|
319
|
+
final_code = new_lines.each_with_object([]) do |line, arr|
|
320
|
+
if line.strip.empty? && arr.last&.strip&.empty?
|
321
|
+
next
|
322
|
+
end
|
323
|
+
arr << line
|
324
|
+
end.join("\n")
|
325
|
+
replace_with final_code
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
Standard::Cli.new(["--fix", Array(migrate_file_path).join(" ")]).run
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
require "synvert/core"
|
6
|
+
require "standard"
|
7
|
+
|
8
|
+
module AlbaMigration
|
9
|
+
class Snippet
|
10
|
+
module SuperclassRewritable
|
11
|
+
# @rbs return: Synvert::Core::Rewriter
|
12
|
+
def superclass_rewriter
|
13
|
+
migrate_file_path = @migrate_file_path
|
14
|
+
|
15
|
+
Synvert::Core::Rewriter.new "alba_migration", "rewrite_superclass" do
|
16
|
+
description <<~EOS
|
17
|
+
It migrates ActiveModelSerializers syntax to Alba syntax.
|
18
|
+
|
19
|
+
Example:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class AttributesResource < ActiveModel::Serializer
|
23
|
+
attributes :id, :name
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
=>
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class AttributesResource
|
31
|
+
include Alba::Resource
|
32
|
+
|
33
|
+
attributes :id, :name
|
34
|
+
end
|
35
|
+
```
|
36
|
+
EOS
|
37
|
+
|
38
|
+
configure(parser: "prism")
|
39
|
+
|
40
|
+
within_files migrate_file_path do
|
41
|
+
with_node node_type: "class_node", superclass: "ActiveModel::Serializer" do
|
42
|
+
body_src = node.body ? node.body.to_source : ""
|
43
|
+
replace_with "class #{node.constant_path.to_source}\ninclude Alba::Resource\n\n#{body_src}\nend"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Standard::Cli.new(["--fix", Array(migrate_file_path).join(" ")]).run
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
# rbs_inline: enabled
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
require_relative "snippet/superclass_rewritable"
|
6
|
+
require_relative "snippet/attribute_rewritable"
|
7
7
|
|
8
8
|
module AlbaMigration
|
9
9
|
class Snippet
|
10
|
+
include SuperclassRewritable
|
11
|
+
include AttributeRewritable
|
12
|
+
|
10
13
|
# @rbs migrate_file_path: String
|
11
14
|
# @rbs return: Array[Synvert::Core::Rewriter]
|
12
15
|
def self.snippets(migrate_file_path:)
|
@@ -19,245 +22,15 @@ module AlbaMigration
|
|
19
22
|
|
20
23
|
# @rbs migrate_file_path: String
|
21
24
|
# @rbs return: void
|
22
|
-
def
|
23
|
-
|
25
|
+
def self.process_snippets(migrate_file_path:)
|
26
|
+
snippets = snippets(migrate_file_path:)
|
27
|
+
snippets.each(&:process)
|
24
28
|
end
|
25
29
|
|
26
|
-
# @rbs
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
Synvert::Core::Rewriter.new "alba_migration", "rewrite_superclass" do
|
31
|
-
description <<~EOS
|
32
|
-
It migrates ActiveModelSerializers syntax to Alba syntax.
|
33
|
-
|
34
|
-
Example:
|
35
|
-
|
36
|
-
```ruby
|
37
|
-
class AttributesResource < ActiveModel::Serializer
|
38
|
-
attributes :id, :name
|
39
|
-
end
|
40
|
-
```
|
41
|
-
|
42
|
-
=>
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
class AttributesResource
|
46
|
-
include Alba::Resource
|
47
|
-
|
48
|
-
attributes :id, :name
|
49
|
-
end
|
50
|
-
```
|
51
|
-
EOS
|
52
|
-
|
53
|
-
configure(parser: "prism")
|
54
|
-
|
55
|
-
within_files migrate_file_path do
|
56
|
-
with_node node_type: "class_node", superclass: "ActiveModel::Serializer" do
|
57
|
-
body_src = node.body ? node.body.to_source : ""
|
58
|
-
replace_with "class #{node.constant_path.to_source}\ninclude Alba::Resource\n\n#{body_src}\nend"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
Standard::Cli.new(["--fix", Array(migrate_file_path).join(" ")]).run
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# @rbs return: Synvert::Core::Rewriter
|
67
|
-
def attribute_method_rewriter
|
68
|
-
migrate_file_path = @migrate_file_path
|
69
|
-
|
70
|
-
Synvert::Core::Rewriter.new "alba_migration", "rewrite_attribute_method" do
|
71
|
-
description <<~EOS
|
72
|
-
It migrates ActiveModelSerializers attribute syntax to Alba attribute syntax.
|
73
|
-
|
74
|
-
Example:
|
75
|
-
|
76
|
-
```ruby
|
77
|
-
class AttributeResource
|
78
|
-
attribute(:object)
|
79
|
-
attribute(:user_id)
|
80
|
-
attribute :user_name
|
81
|
-
end
|
82
|
-
```
|
83
|
-
|
84
|
-
=>
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
class AttributeResource
|
88
|
-
attributes :object, :user_id, :user_name
|
89
|
-
end
|
90
|
-
```
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
class AttributeResource
|
94
|
-
attribute(:object) { 'access_token' }
|
95
|
-
attribute(:user_id) { object.user_id }
|
96
|
-
attribute :user_name
|
97
|
-
def user_name
|
98
|
-
object.user.name
|
99
|
-
end
|
100
|
-
end
|
101
|
-
```
|
102
|
-
|
103
|
-
=>
|
104
|
-
|
105
|
-
```ruby
|
106
|
-
class AttributeResource
|
107
|
-
attribute :object do
|
108
|
-
'access_token'
|
109
|
-
end
|
110
|
-
attribute :user_id do |object|
|
111
|
-
object.user_id
|
112
|
-
end
|
113
|
-
attribute :user_name do |object|
|
114
|
-
object.user.name
|
115
|
-
end
|
116
|
-
end
|
117
|
-
```
|
118
|
-
EOS
|
119
|
-
|
120
|
-
configure(parser: "parser")
|
121
|
-
|
122
|
-
within_files migrate_file_path do
|
123
|
-
# Rewrite the entire class at once
|
124
|
-
with_node type: "class" do
|
125
|
-
class_name = node.children[0].loc.expression.source
|
126
|
-
class_body = node.children[2]
|
127
|
-
next unless class_body
|
128
|
-
|
129
|
-
# 1. Collect attribute blocks
|
130
|
-
attribute_blocks = []
|
131
|
-
other_lines = []
|
132
|
-
|
133
|
-
# Collect other lines in the class
|
134
|
-
class_body.children.each do |child|
|
135
|
-
if child && child.type == :send
|
136
|
-
if child.children[1] == :include || child.children[1] == :attributes
|
137
|
-
other_lines << " " + child.loc.expression.source
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# 1-1. Collect attribute(:xxx) { ... } style
|
143
|
-
class_body.children.each do |child|
|
144
|
-
if child && child.type == :block &&
|
145
|
-
child.children[0] && child.children[0].type == :send &&
|
146
|
-
child.children[0].children[1] == :attribute
|
147
|
-
|
148
|
-
# Process arguments
|
149
|
-
arg_node = child.children[0].children[2]
|
150
|
-
arg = (arg_node.type == :sym) ? ":#{arg_node.children[0]}" : arg_node.loc.expression.source
|
151
|
-
|
152
|
-
# Process block parameters
|
153
|
-
block_params = child.children[1] ? child.children[1].children.map { |c| c.children[0].to_s }.join(", ") : ""
|
154
|
-
|
155
|
-
# Process block body
|
156
|
-
block_body = child.children[2] ? child.children[2].loc.expression.source : ""
|
157
|
-
|
158
|
-
# Add 'object' to parameters if used in block body
|
159
|
-
if block_params.strip.empty? && block_body.include?("object")
|
160
|
-
block_params = "object"
|
161
|
-
end
|
162
|
-
|
163
|
-
# Generate new code(インデント付与はしない)
|
164
|
-
attribute_blocks << if block_params.strip.empty?
|
165
|
-
"attribute #{arg} do\n#{block_body}\nend"
|
166
|
-
else
|
167
|
-
"attribute #{arg} do |#{block_params.strip}|\n#{block_body}\nend"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
# 1-2. Collect attribute :name + def name ... end style
|
173
|
-
attr_names = {}
|
174
|
-
other_methods = []
|
175
|
-
|
176
|
-
# First, collect attribute :name
|
177
|
-
class_body.children.each do |child|
|
178
|
-
if child && child.type == :send &&
|
179
|
-
child.children[1] == :attribute &&
|
180
|
-
child.children.length == 3 &&
|
181
|
-
child.children[2].type == :sym
|
182
|
-
|
183
|
-
attr_name = child.children[2].children[0].to_s
|
184
|
-
attr_names[attr_name] = nil
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
# Extract attribute names to be grouped as attributes
|
189
|
-
attributes_group = []
|
190
|
-
class_body.children.each do |child|
|
191
|
-
if child && child.type == :send &&
|
192
|
-
child.children[1] == :attribute &&
|
193
|
-
child.children.length == 3 &&
|
194
|
-
child.children[2].type == :sym
|
195
|
-
method_name = child.children[2].children[0].to_s
|
196
|
-
# Only group as attributes if there is no corresponding def method
|
197
|
-
unless class_body.children.any? { |c| c && c.type == :def && c.children[0].to_s == method_name }
|
198
|
-
attributes_group << ":#{method_name}"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# 2. Generate new code for the entire class
|
204
|
-
new_lines = ["class #{class_name}"]
|
205
|
-
|
206
|
-
# Add attributes group at the top
|
207
|
-
unless attributes_group.empty?
|
208
|
-
new_lines << " attributes #{attributes_group.join(", ")}"
|
209
|
-
end
|
210
|
-
|
211
|
-
# Add include and attributes lines
|
212
|
-
if !other_lines.empty?
|
213
|
-
include_line = other_lines.find { |line| line.include?("include") }
|
214
|
-
attribute_lines = other_lines.select { |line| !line.include?("include") }
|
215
|
-
|
216
|
-
# Add include first
|
217
|
-
new_lines << include_line if include_line
|
218
|
-
|
219
|
-
# Add a blank line
|
220
|
-
new_lines << ""
|
221
|
-
|
222
|
-
# Add attributes lines
|
223
|
-
new_lines.concat(attribute_lines) unless attribute_lines.empty?
|
224
|
-
end
|
225
|
-
|
226
|
-
# 2-1. Add attribute blocks (no extra blank lines)
|
227
|
-
new_lines.concat(attribute_blocks)
|
228
|
-
|
229
|
-
# Convert attribute :name + def name ... end pairs
|
230
|
-
class_body.children.each do |child|
|
231
|
-
if child && child.type == :def
|
232
|
-
method_name = child.children[0].to_s
|
233
|
-
if attr_names.key?(method_name)
|
234
|
-
# Method corresponding to attribute (to be converted)
|
235
|
-
method_body = child.children[2]
|
236
|
-
method_src = method_body ? method_body.loc.expression.source : ""
|
237
|
-
# No indent
|
238
|
-
attr_names[method_name] = "attribute :#{method_name} do |object|\n#{method_src}\nend"
|
239
|
-
elsif child.respond_to?(:loc) && child.loc.respond_to?(:expression)
|
240
|
-
# Methods not corresponding to attribute (preserved)
|
241
|
-
method_src = child.loc.expression.source
|
242
|
-
other_methods << method_src
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
# 2-2. Add conversion results of attribute :name + def name ... end (no extra blank lines)
|
248
|
-
attr_blocks = attr_names.values.compact
|
249
|
-
new_lines.concat(attr_blocks)
|
250
|
-
# 2-3. Add other methods (def not corresponding to attribute) (no extra blank lines)
|
251
|
-
new_lines.concat(other_methods)
|
252
|
-
|
253
|
-
new_lines << "end"
|
254
|
-
|
255
|
-
replace_with new_lines.join("\n")
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
Standard::Cli.new(["--fix", Array(migrate_file_path).join(" ")]).run
|
260
|
-
end
|
30
|
+
# @rbs migrate_file_path: String
|
31
|
+
# @rbs return: void
|
32
|
+
def initialize(migrate_file_path:)
|
33
|
+
@migrate_file_path = migrate_file_path
|
261
34
|
end
|
262
35
|
end
|
263
36
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Generated from lib/alba_migration/snippet/attribute_rewritable.rb with RBS::Inline
|
2
|
+
|
3
|
+
module AlbaMigration
|
4
|
+
class Snippet
|
5
|
+
module AttributeRewritable
|
6
|
+
# @rbs return: Synvert::Core::Rewriter
|
7
|
+
def attribute_method_rewriter: () -> Synvert::Core::Rewriter
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Generated from lib/alba_migration/snippet/superclass_rewritable.rb with RBS::Inline
|
2
|
+
|
3
|
+
module AlbaMigration
|
4
|
+
class Snippet
|
5
|
+
module SuperclassRewritable
|
6
|
+
# @rbs return: Synvert::Core::Rewriter
|
7
|
+
def superclass_rewriter: () -> Synvert::Core::Rewriter
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -2,18 +2,20 @@
|
|
2
2
|
|
3
3
|
module AlbaMigration
|
4
4
|
class Snippet
|
5
|
+
include SuperclassRewritable
|
6
|
+
|
7
|
+
include AttributeRewritable
|
8
|
+
|
5
9
|
# @rbs migrate_file_path: String
|
6
10
|
# @rbs return: Array[Synvert::Core::Rewriter]
|
7
11
|
def self.snippets: (migrate_file_path: String) -> Array[Synvert::Core::Rewriter]
|
8
12
|
|
9
13
|
# @rbs migrate_file_path: String
|
10
14
|
# @rbs return: void
|
11
|
-
def
|
12
|
-
|
13
|
-
# @rbs return: Synvert::Core::Rewriter
|
14
|
-
def superclass_rewriter: () -> Synvert::Core::Rewriter
|
15
|
+
def self.process_snippets: (migrate_file_path: String) -> void
|
15
16
|
|
16
|
-
# @rbs
|
17
|
-
|
17
|
+
# @rbs migrate_file_path: String
|
18
|
+
# @rbs return: void
|
19
|
+
def initialize: (migrate_file_path: String) -> void
|
18
20
|
end
|
19
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alba_migration
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ShoheiMitani
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: synvert-core
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: standard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: AlbaMigration is a CLI tool and library to help you convert your Ruby
|
28
42
|
code from ActiveModelSerializers (AMS) to Alba syntax. It rewrites AMS serializer
|
29
43
|
classes to Alba resource classes, supporting the migration of class definitions
|
@@ -54,10 +68,14 @@ files:
|
|
54
68
|
- lib/alba_migration.rb
|
55
69
|
- lib/alba_migration/cli.rb
|
56
70
|
- lib/alba_migration/snippet.rb
|
71
|
+
- lib/alba_migration/snippet/attribute_rewritable.rb
|
72
|
+
- lib/alba_migration/snippet/superclass_rewritable.rb
|
57
73
|
- lib/alba_migration/version.rb
|
58
74
|
- sig/generated/alba_migration.rbs
|
59
75
|
- sig/generated/alba_migration/cli.rbs
|
60
76
|
- sig/generated/alba_migration/snippet.rbs
|
77
|
+
- sig/generated/alba_migration/snippet/attribute_rewritable.rbs
|
78
|
+
- sig/generated/alba_migration/snippet/superclass_rewritable.rbs
|
61
79
|
- sig/generated/alba_migration/version.rbs
|
62
80
|
homepage: https://github.com/ShoheiMitani/alba_migration
|
63
81
|
licenses:
|