alba_migration 0.0.1 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d0d4f72523fe8a34222de3cfe8a36d66aac6c5435f5efa1838899d0c70dec70
4
- data.tar.gz: 1a1c8a0d42928db2f3bda02ad2f0202153f2706b95ef3c84d56348e47c575800
3
+ metadata.gz: 0f67906a1b1a0b5a723ed8c008a01ddc3175605b951545e5f9c63a6abea87720
4
+ data.tar.gz: bb47d0f21eb17c2ce923d3073426936fcfe30836c55010dd0eef6f99de664a61
5
5
  SHA512:
6
- metadata.gz: a6fb7aec6e909fdeeee455f04bf0a3fe8b4ae7596673d995f7288311fca94c19b5682260696d66b1b3a5b60ba2ca561d4b50eec30f6e59a01a0821a356629d79
7
- data.tar.gz: 1157bfbcf57f3ef83d22aa2b1896c125c550b3744cab656312f1220d2083303ff16f84191b067afe310eda19182c874d20d1a9ed6e389965893976c3068f5780
6
+ metadata.gz: 8bcfd3e77babad553ad41e7e6889448bc47c5da2fd7fa199238a04b547dfa7f3ed7c18c18fcc8eae37f01622aca87ccd4e4381346ab6c6509e6ce3faec63c428
7
+ data.tar.gz: a8e02d2763f1d6745ca9d5d7db8f079bc9988df7fda1229e7e9b6df7069560a7116a5db46e4574a4aa07535509ef49fbedefdbd117a6bf0d745757bf90a56a54
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # GHANGELOG
2
2
 
3
+ ## 0.0.3 (2024-06-07)
4
+
5
+ - Add gem dependency for easier installation
6
+
7
+ ## 0.0.2 (2024-05-19)
8
+
9
+ - Add attribute method rewriter for better AMS to Alba conversion
10
+ - Add tests for nested class conversion
11
+
3
12
  ## 0.0.1 (2025-05-19)
4
13
 
5
14
  - Initial release
data/README.md CHANGED
@@ -64,6 +64,7 @@ end
64
64
  Currently, AlbaMigration supports the following AMS syntax:
65
65
  - `class ... < ActiveModel::Serializer` with `attributes ...` inside the class body
66
66
  - Only the conversion of the class definition and `attributes` is supported at this time
67
+ - Attribute methods (e.g. `attribute :foo do ... end`) are now automatically converted
67
68
  - Other AMS features (e.g., associations, custom methods) are **not** yet supported
68
69
 
69
70
  ## Contributing
@@ -28,8 +28,10 @@ module AlbaMigration
28
28
  exit(1)
29
29
  end
30
30
 
31
- rewriter = AlbaMigration::Snippet.create(migrate_file_path: matched_files)
32
- rewriter.process
31
+ snippets = AlbaMigration::Snippet.snippets(migrate_file_path: matched_files)
32
+ snippets.each do |rewriter|
33
+ rewriter.process
34
+ end
33
35
  end
34
36
  end
35
37
  end
@@ -3,13 +3,31 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  require "synvert/core"
6
+ require "standard"
6
7
 
7
8
  module AlbaMigration
8
9
  class Snippet
9
- # @rbs file_patterns: Array[String]
10
+ # @rbs migrate_file_path: String
11
+ # @rbs return: Array[Synvert::Core::Rewriter]
12
+ def self.snippets(migrate_file_path:)
13
+ snippet_creator = new(migrate_file_path:)
14
+ [
15
+ snippet_creator.superclass_rewriter,
16
+ snippet_creator.attribute_method_rewriter
17
+ ]
18
+ end
19
+
20
+ # @rbs migrate_file_path: String
21
+ # @rbs return: void
22
+ def initialize(migrate_file_path:)
23
+ @migrate_file_path = migrate_file_path
24
+ end
25
+
10
26
  # @rbs return: Synvert::Core::Rewriter
11
- def self.create(migrate_file_path:)
12
- Synvert::Core::Rewriter.new "alba", "convert_ams_to_alba" do
27
+ def superclass_rewriter
28
+ migrate_file_path = @migrate_file_path
29
+
30
+ Synvert::Core::Rewriter.new "alba_migration", "rewrite_superclass" do
13
31
  description <<~EOS
14
32
  It migrates ActiveModelSerializers syntax to Alba syntax.
15
33
 
@@ -36,16 +54,209 @@ module AlbaMigration
36
54
 
37
55
  within_files migrate_file_path do
38
56
  with_node node_type: "class_node", superclass: "ActiveModel::Serializer" do
39
- body = node.body ? node.body.to_source : ""
40
- if body.strip.empty?
41
- indented_body = ""
42
- else
43
- min_indent = body.lines.reject { |l| l.strip.empty? }.map { |l| l[/^\s*/].size }.min || 0
44
- indented_body = body.lines.map { |l| " " + l[min_indent..] }.join
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
45
115
  end
46
- replace_with "class #{node.constant_path.to_source}\n include Alba::Resource\n\n#{indented_body}\nend"
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")
47
256
  end
48
257
  end
258
+
259
+ Standard::Cli.new(["--fix", Array(migrate_file_path).join(" ")]).run
49
260
  end
50
261
  end
51
262
  end
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  module AlbaMigration
6
- VERSION = "0.0.1"
6
+ VERSION = "0.0.3"
7
7
  end
@@ -2,8 +2,18 @@
2
2
 
3
3
  module AlbaMigration
4
4
  class Snippet
5
- # @rbs file_patterns: Array[String]
5
+ # @rbs migrate_file_path: String
6
+ # @rbs return: Array[Synvert::Core::Rewriter]
7
+ def self.snippets: (migrate_file_path: String) -> Array[Synvert::Core::Rewriter]
8
+
9
+ # @rbs migrate_file_path: String
10
+ # @rbs return: void
11
+ def initialize: (migrate_file_path: String) -> void
12
+
13
+ # @rbs return: Synvert::Core::Rewriter
14
+ def superclass_rewriter: () -> Synvert::Core::Rewriter
15
+
6
16
  # @rbs return: Synvert::Core::Rewriter
7
- def self.create: (migrate_file_path: untyped) -> Synvert::Core::Rewriter
17
+ def attribute_method_rewriter: () -> Synvert::Core::Rewriter
8
18
  end
9
19
  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.1
4
+ version: 0.0.3
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-19 00:00:00.000000000 Z
11
+ date: 2025-05-20 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