From 208a24fd92ec002a33f4f78d335fb5743e7d3b44 Mon Sep 17 00:00:00 2001 From: Chris Gunther Date: Thu, 6 Mar 2025 21:37:00 -0500 Subject: [PATCH] Support `through_association` with `singleton`. Given a load declaration like: ```ruby load_resource :order load_resource through: :address, through_association: :custom_address, singleton: true ``` The `:through_assocation` option wasn't being respected in the `:singleton` branch, thus falling back to calling `find`, but odds are you wouldn't have an ID given it was a singleton, thus leading to an error. Now it uses the `:through_association` before falling back to the `name`. Fixes #604. --- CHANGELOG.md | 5 +++++ lib/cancan/controller_resource_finder.rb | 5 +++-- spec/cancan/controller_resource_spec.rb | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc34c1981..7008d2c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +* [#878](https://github.com/CanCanCommunity/cancancan/pull/878): Support `through_association` with `singleton`. ([@cgunther][]) + ## 3.6.0 * [#849](https://github.com/CanCanCommunity/cancancan/pull/849): Update tests matrix. ([@coorasse][]) @@ -716,3 +720,4 @@ Please read the [guide on migrating from CanCanCan 2.x to 3.0](https://github.co [@MrChoclate]: https://github.com/MrChoclate [@pandermatt]: https://github.com/pandermatt [@kalsan]: https://github.com/kalsan +[@cgunther]: https://github.com/cgunther diff --git a/lib/cancan/controller_resource_finder.rb b/lib/cancan/controller_resource_finder.rb index 742c88324..45205f057 100644 --- a/lib/cancan/controller_resource_finder.rb +++ b/lib/cancan/controller_resource_finder.rb @@ -5,8 +5,9 @@ module ControllerResourceFinder protected def find_resource - if @options[:singleton] && parent_resource.respond_to?(name) - parent_resource.send(name) + singleton_association_name = @options[:through_association] || name + if @options[:singleton] && parent_resource.respond_to?(singleton_association_name) + parent_resource.send(singleton_association_name) elsif @options[:find_by] find_resource_using_find_by else diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb index f66eb0461..4b534d2a5 100644 --- a/spec/cancan/controller_resource_spec.rb +++ b/spec/cancan/controller_resource_spec.rb @@ -430,6 +430,24 @@ class Dashboard; end expect(controller.instance_variable_get(:@model)).to eq(model) end + it 'finds record through has_one association with :through_association and :singleton options' do + params[:id] = nil + + model = Model.new + + category = double(custom_model: model) + controller.instance_variable_set(:@category, category) + + resource = CanCan::ControllerResource.new( + controller, + through: :category, + through_association: :custom_model, + singleton: true, + ) + resource.load_resource + expect(controller.instance_variable_get(:@model)).to eq(model) + end + it 'loads the model using a custom class' do model = Model.new allow(Model).to receive(:find).with('123') { model }