plutonium 0.45.0 → 0.45.1

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: 1073fa6b607e230a752fac717f223a0b641179b3c2b93b67d941d40b6dea07e2
4
- data.tar.gz: 28da869f69c58a437c102be436ddb31f4017795dba17b244a8f2db15a15c7bea
3
+ metadata.gz: 2ec1a124615fb8539ddbdb8385100efb6ccfb4d2ff300dc2aef1610291bb8033
4
+ data.tar.gz: 5ff37e475beac544e0cfc892890799f2528bd110010bd807796e48bff493a1c5
5
5
  SHA512:
6
- metadata.gz: 11b347e6f98eaeb7a3cc93c75d3c67483107ddc3d822780ce9bcb12901af3b3d9c7fe7996b7bacffc3ea32528ebbab5a9affb54d9bfb56dbeec49cb40da92f46
7
- data.tar.gz: 5c0a5d6f4988980b8195cdd4d27d1c28189a8cc6d34c889136b73f512f6cd88ecaf2851af52ef666e68f1c5b66802fac570831ce6608c195d707871b5b35d899
6
+ metadata.gz: 7684effe6531a638833ad9c19166c080a61de1b3dd5b9d4a8e9c7189b5201bb67c7d48c25f05dca5cf974fa9351866d4ce6877dd0c470d97cf3e6154a5a59a1b
7
+ data.tar.gz: 1cbcf77b215cc0f19f1c15738ad5c6add48cabc6f1b687ac960379062fab3b49376956d2ef72edc758168259ab7e821709c8bd69b033009c95c8f06d56ec4f17
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.45.1] - 2026-04-02
2
+
3
+ ### 🐛 Bug Fixes
4
+
5
+ - *(routing)* Use named route helpers for top-level resource URLs
1
6
  ## [0.45.0] - 2026-04-01
2
7
 
3
8
  ### 🚀 Features
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.44.1)
4
+ plutonium (0.45.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 43.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.44.1)
4
+ plutonium (0.45.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 43.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.44.1)
4
+ plutonium (0.45.0)
5
5
  action_policy (~> 0.7.0)
6
6
  listen (~> 3.8)
7
7
  pagy (~> 43.0)
@@ -88,14 +88,16 @@ module Plutonium
88
88
  # @return [Hash] args to pass to `url_for`
89
89
  #
90
90
  def resource_url_args_for(*args, action: nil, parent: nil, association: nil, package: nil, **kwargs)
91
- target_package = package || current_package
91
+ element = args.first
92
+
93
+ raise ArgumentError, "parent is required when using symbol association name" if element.is_a?(Symbol) && parent.nil?
92
94
 
93
95
  # For nested resources, use named route helpers to avoid Rails param recall ambiguity
94
96
  if parent.present?
95
- assoc_name = if args.first.is_a?(Symbol)
96
- args.first
97
+ assoc_name = if element.is_a?(Symbol)
98
+ element
97
99
  else
98
- association || resolve_association(args.first, parent)
100
+ association || resolve_association(element, parent)
99
101
  end
100
102
 
101
103
  nested_route_key = "#{parent.class.model_name.plural}/#{assoc_name}"
@@ -103,7 +105,7 @@ module Plutonium
103
105
 
104
106
  if route_config
105
107
  return build_nested_resource_url_args(
106
- args.first,
108
+ element,
107
109
  parent: parent,
108
110
  association_name: assoc_name,
109
111
  route_config: route_config,
@@ -113,8 +115,8 @@ module Plutonium
113
115
  end
114
116
  end
115
117
 
116
- # Top-level resource: build controller/action hash for url_for
117
- build_top_level_resource_url_args(*args, action: action, parent: parent, association: association, package: target_package, **kwargs)
118
+ # Top-level resource: use named route helpers
119
+ build_top_level_resource_url_args(element, action: action, **kwargs)
118
120
  end
119
121
 
120
122
  def resource_url_for(*args, package: nil, **kwargs)
@@ -202,61 +204,73 @@ module Plutonium
202
204
  {_named_route: helper_name, _args: helper_args, _options: url_options}
203
205
  end
204
206
 
205
- def build_top_level_resource_url_args(*args, action: nil, parent: nil, association: nil, package: nil, **kwargs)
206
- url_args = {**kwargs, action: action}.compact
207
-
208
- controller_chain = [package&.to_s].compact
209
- [*args].compact.each_with_index do |element, index|
210
- if element.is_a?(Symbol)
211
- raise ArgumentError, "parent is required when using symbol association name" unless parent
212
-
213
- assoc = parent.class.reflect_on_association(element)
214
- raise ArgumentError, "Unknown association :#{element} on #{parent.class}" unless assoc
215
-
216
- controller_chain << assoc.klass.to_s.pluralize
217
- url_args[:action] ||= :index if index == args.length - 1
218
- elsif element.is_a?(Class)
219
- controller_chain << element.to_s.pluralize
220
- if index == args.length - 1
221
- # Singular resources have no index, default to show
222
- is_singular = current_engine.routes.singular_resource_route?(element.model_name.plural)
223
- url_args[:action] ||= is_singular ? :show : :index
224
- end
225
- else
226
- model_class = element.class
227
- if model_class.respond_to?(:base_class) && model_class != model_class.base_class
228
- route_configs = current_engine.routes.resource_route_config_for(model_class.model_name.plural)
229
- model_class = model_class.base_class if route_configs.empty?
230
- end
231
-
232
- controller_chain << model_class.to_s.pluralize
233
- if index == args.length - 1
234
- route_key = if parent.present?
235
- assoc_name = association || resolve_association(element, parent)
236
- "#{parent.class.model_name.plural}/#{assoc_name}"
237
- else
238
- model_class.model_name.plural
239
- end
240
- is_singular = current_engine.routes.singular_resource_route?(route_key)
241
- url_args[:id] = element.to_param unless is_singular
242
- url_args[:action] ||= :show
243
- else
244
- url_args[model_class.to_s.underscore.singularize.to_sym] = element.to_param
245
- end
207
+ # Build URL args for top-level resources using named route helpers.
208
+ # This avoids Rails url_for ambiguity when multiple routes map to the same controller
209
+ # (e.g., both /widgets/:id and /organization/nested_widgets/:id resolve to widgets#show).
210
+ def build_top_level_resource_url_args(element, action: nil, **kwargs)
211
+ # Resolve the model class for the target resource
212
+ if element.is_a?(Class)
213
+ model_class = element
214
+ elsif element
215
+ model_class = element.class
216
+ if model_class.respond_to?(:base_class) && model_class != model_class.base_class
217
+ route_configs = current_engine.routes.resource_route_config_for(model_class.model_name.plural)
218
+ model_class = model_class.base_class if route_configs.empty?
246
219
  end
247
220
  end
248
- url_args[:controller] = "/#{controller_chain.join("::").underscore}"
249
221
 
250
- url_args[:"#{parent.model_name.singular}_id"] = parent.to_param if parent.present?
251
- if scoped_to_entity? && scoped_entity_strategy == :path
252
- url_args[scoped_entity_param_key] = current_scoped_entity.to_param
222
+ route_key = model_class.model_name.plural
223
+ is_singular = current_engine.routes.singular_resource_route?(route_key)
224
+ no_record = element.is_a?(Class) || element.nil?
225
+
226
+ # Default action based on context
227
+ action ||= if no_record
228
+ is_singular ? :show : :index
229
+ else
230
+ :show
231
+ end
232
+
233
+ # Build named route helper, mirroring the pattern used by build_nested_resource_url_args.
234
+ # e.g., "organization_scope_widgets" (collection), "organization_scope_widget" (member),
235
+ # "edit_organization_scope_widget" (edit action)
236
+ is_collection_action = action == :index || action == :create || (no_record && action != :new)
237
+ helper_base = if is_singular || is_collection_action
238
+ model_class.model_name.plural
239
+ else
240
+ model_class.model_name.singular
253
241
  end
254
242
 
255
- if !url_args.key?(:format) && request.present? && request.format.present? && !request.format.symbol.in?([:html, :turbo_stream])
256
- url_args[:format] = request.format.symbol
243
+ # For uncountable model names (plural == singular), Rails adds _index suffix
244
+ # to collection route names to disambiguate from member routes.
245
+ uncountable_index_suffix = if is_collection_action && !is_singular && model_class.model_name.plural == model_class.model_name.singular
246
+ "_index"
257
247
  end
258
248
 
259
- url_args
249
+ helper_suffix = case action
250
+ when :show, :index, :create, :update then ""
251
+ else "#{action}_"
252
+ end
253
+
254
+ entity_prefix = if scoped_to_entity? && scoped_entity_strategy == :path
255
+ "#{scoped_entity_param_key}_"
256
+ end
257
+
258
+ helper_name = :"#{helper_suffix}#{entity_prefix}#{helper_base}#{uncountable_index_suffix}_path"
259
+
260
+ # Build the positional arguments for the helper
261
+ helper_args = []
262
+ helper_args << current_scoped_entity.to_param if entity_prefix
263
+ unless is_singular || no_record || is_collection_action
264
+ helper_args << element.to_param
265
+ end
266
+
267
+ # Build URL options
268
+ url_options = kwargs.dup
269
+ if !url_options.key?(:format) && request.present? && request.format.present? && !request.format.symbol.in?([:html, :turbo_stream])
270
+ url_options[:format] = request.format.symbol
271
+ end
272
+
273
+ {_named_route: helper_name, _args: helper_args, _options: url_options}
260
274
  end
261
275
 
262
276
  def root_path(*)
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.45.0"
2
+ VERSION = "0.45.1"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.45.0",
3
+ "version": "0.45.1",
4
4
  "description": "Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plutonium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.45.0
4
+ version: 0.45.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-04-01 00:00:00.000000000 Z
10
+ date: 2026-04-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk