typespec_from_serializers 0.5.3 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bc90948b5d93848663bab0d1162a1f26d49d832a05ef4c57eb5cb1e90f74ff3
4
- data.tar.gz: '008ddb8319166e72e2e75a30d840ad387a082df6e4d0846698e87f9b15ac1c05'
3
+ metadata.gz: e442eede2713d04dc58848c414f8c97fb3131ec16aff96c33b31fc2b9bfe248b
4
+ data.tar.gz: 88424e945a35538e356adceaf9abdcb2ceb5997469a94efe9d661c31b228050a
5
5
  SHA512:
6
- metadata.gz: 950c7c34c0d3766854fd6eb4b3aa958b7ed8a20040ad8a43c5573d52741b156441b43e0fbded753536624d368ce353959f5f9e13d311eddea05ba75b8a4e85df
7
- data.tar.gz: 6695700e9a0f73b03e97107d1844e2ef878dcf50217b45826418488827643923260f9b2465bd20fc4d03ef298ea2028f87132fa496abbb67f43ad635fda47562
6
+ metadata.gz: 960f08588e534643e0ecdb2cf4f3129d351995330f4474748c10ee8f7e844e202614c3e8ae391753c5b45b555831398aff2ffa3474af080ef9e5767b86f2f706
7
+ data.tar.gz: 32cd0ff2d5b1e339829a78f7150b3870a4ef954d61d12c8738b3ebe5f0d04b31ea7ba1988fc4efd7f5c46e436f56e96d52b2719f77b29a95231fdfaf13d07777
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # TypeSpec From Serializers Changelog
2
2
 
3
+ ## [0.5.4] - 2025-12-23
4
+
5
+ ### Fixed
6
+ - Transitive params extraction now follows method calls through intermediate methods
7
+ - Route-level `type:` declarations now only apply to actual path params (not inherited by collection routes)
8
+
3
9
  ## [0.5.3] - 2025-12-23
4
10
 
5
11
  ### Fixed
@@ -847,7 +847,10 @@ module TypeSpecFromSerializers
847
847
 
848
848
  # Internal: Extracts all parameter types from route metadata and controller DSL
849
849
  def extract_all_param_types(controller, route)
850
+ # Route-level types are for path params only - filter to actual path params
851
+ path_param_names = route[:path].scan(/:([a-zA-Z_][a-zA-Z0-9_]*)/).flatten
850
852
  route_param_types = route[:param_types]
853
+ .select { |k, _| path_param_names.include?(k.to_s) }
851
854
  .transform_keys(&:to_s)
852
855
  .transform_values { |v| map_type_class_to_typespec(v) }
853
856
 
@@ -1133,19 +1136,40 @@ module TypeSpecFromSerializers
1133
1136
  {}
1134
1137
  end
1135
1138
 
1136
- # Internal: Finds *_params methods that are called by a specific action method
1139
+ # Internal: Finds *_params methods that are called by a specific action method (transitively)
1137
1140
  def find_params_methods_called_by_action(controller_class, action)
1138
1141
  method_node = find_action_node(controller_class, action)
1139
1142
  return [] unless method_node
1140
1143
 
1144
+ method = controller_class.instance_method(action)
1145
+ file_path, = method.source_location
1141
1146
  suffix = config.param_method_suffix
1142
- find_method_calls(method_node)
1143
- .select { |name| name.end_with?(suffix) }
1144
- .map(&:to_sym)
1147
+
1148
+ find_params_methods_transitively(file_path, method_node, suffix)
1145
1149
  rescue
1146
1150
  []
1147
1151
  end
1148
1152
 
1153
+ # Internal: Recursively finds *_params methods through intermediate method calls
1154
+ def find_params_methods_transitively(file_path, node, suffix, visited = Set.new)
1155
+ calls = find_method_calls(node)
1156
+ params_methods = calls.select { |name| name.end_with?(suffix) }.map(&:to_sym)
1157
+
1158
+ # Follow non-params method calls defined in the same file
1159
+ calls.each do |call_name|
1160
+ next if call_name.end_with?(suffix)
1161
+ next if visited.include?(call_name)
1162
+
1163
+ visited << call_name
1164
+ called_node = find_def_node(file_path) { |n| n.name.to_s == call_name }
1165
+ next unless called_node
1166
+
1167
+ params_methods.concat(find_params_methods_transitively(file_path, called_node, suffix, visited))
1168
+ end
1169
+
1170
+ params_methods.uniq
1171
+ end
1172
+
1149
1173
  # Internal: Finds the AST node for a controller action method.
1150
1174
  def find_action_node(controller_class, action)
1151
1175
  return unless controller_class.method_defined?(action)
@@ -1154,7 +1178,7 @@ module TypeSpecFromSerializers
1154
1178
  file_path, line_number = method.source_location
1155
1179
  return unless file_path && File.exist?(file_path)
1156
1180
 
1157
- find_method_at_line(file_path, line_number)
1181
+ find_def_node(file_path) { |node| node.location.start_line == line_number }
1158
1182
  end
1159
1183
 
1160
1184
  # Internal: Parses a Ruby file and caches the result.
@@ -1166,28 +1190,29 @@ module TypeSpecFromSerializers
1166
1190
  end
1167
1191
  end
1168
1192
 
1169
- # Internal: Finds a DefNode at the exact line number.
1170
- def find_method_at_line(file_path, line_number)
1193
+ # Internal: Traverses all descendant nodes in BFS order.
1194
+ def each_descendant(node)
1195
+ return enum_for(:each_descendant, node) unless block_given?
1196
+ queue = [node]
1197
+ while (current = queue.shift)
1198
+ yield current
1199
+ queue.concat(current.compact_child_nodes)
1200
+ end
1201
+ end
1202
+
1203
+ # Internal: Finds a DefNode matching the given condition.
1204
+ def find_def_node(file_path)
1171
1205
  ast = parse_file_cached(file_path)
1172
1206
  return unless ast
1173
-
1174
- queue = [ast]
1175
- while (node = queue.shift)
1176
- return node if node.is_a?(Prism::DefNode) && node.location.start_line == line_number
1177
- queue.concat(node.compact_child_nodes)
1178
- end
1179
- nil
1207
+ each_descendant(ast).find { |node| node.is_a?(Prism::DefNode) && yield(node) }
1180
1208
  end
1181
1209
 
1182
1210
  # Internal: Finds all unqualified method call names in an AST node.
1183
1211
  def find_method_calls(node)
1184
- calls = []
1185
- queue = [node]
1186
- while (current = queue.shift)
1187
- calls << current.name.to_s if current.is_a?(Prism::CallNode) && current.receiver.nil?
1188
- queue.concat(current.compact_child_nodes)
1189
- end
1190
- calls.uniq
1212
+ each_descendant(node)
1213
+ .select { |n| n.is_a?(Prism::CallNode) && n.receiver.nil? }
1214
+ .map { |n| n.name.to_s }
1215
+ .uniq
1191
1216
  end
1192
1217
 
1193
1218
  # Internal: Extracts key-value types from Sorbet hash type
@@ -2,5 +2,5 @@
2
2
 
3
3
  module TypeSpecFromSerializers
4
4
  # Public: This library adheres to semantic versioning.
5
- VERSION = "0.5.3"
5
+ VERSION = "0.5.4"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typespec_from_serializers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danila Poyarkov