diff --git a/lib/rails_admin/adapters/active_record/object_extension.rb b/lib/rails_admin/adapters/active_record/object_extension.rb index c036774885..5e0f42ee07 100644 --- a/lib/rails_admin/adapters/active_record/object_extension.rb +++ b/lib/rails_admin/adapters/active_record/object_extension.rb @@ -2,6 +2,24 @@ module RailsAdmin module Adapters module ActiveRecord module ObjectExtension + def self.extended(object) + object.class.reflect_on_all_associations.each do |association| + association = Association.new(association, object.class) + case association.type + when :has_one + object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{association.name}_id + self.#{association.name}&.id + end + + def #{association.name}_id=(item_id) + self.#{association.name} = #{association.klass}.find_by_id(item_id) + end + RUBY + end + end + end + def assign_attributes(attributes) super if attributes end diff --git a/lib/rails_admin/adapters/mongoid/object_extension.rb b/lib/rails_admin/adapters/mongoid/object_extension.rb index 4fcd11ea14..ad94804b54 100644 --- a/lib/rails_admin/adapters/mongoid/object_extension.rb +++ b/lib/rails_admin/adapters/mongoid/object_extension.rb @@ -18,6 +18,11 @@ def self.extended(object) send(name)&.save end end + object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name}_id=(item_id) + self.#{name} = (#{association.klass}.find(item_id) rescue nil) + end + RUBY end end end diff --git a/lib/rails_admin/config/fields/types/has_one_association.rb b/lib/rails_admin/config/fields/types/has_one_association.rb index b1112035fe..cb44b92db9 100644 --- a/lib/rails_admin/config/fields/types/has_one_association.rb +++ b/lib/rails_admin/config/fields/types/has_one_association.rb @@ -17,10 +17,6 @@ class HasOneAssociation < RailsAdmin::Config::Fields::Association (o = value) && o.send(associated_model_config.object_label_method) end - def editable? - (nested_form || abstract_model.model.new.respond_to?("#{name}_id=")) && super - end - def selected_id value.try :id end diff --git a/spec/dummy_app/app/active_record/player.rb b/spec/dummy_app/app/active_record/player.rb index 0fe74796a1..5b2924ce2d 100644 --- a/spec/dummy_app/app/active_record/player.rb +++ b/spec/dummy_app/app/active_record/player.rb @@ -17,12 +17,4 @@ class Player < ActiveRecord::Base scope :rails_admin_search, ->(query) { where(name: query.reverse) } def destroy_hook; end - - def draft_id - draft.try :id - end - - def draft_id=(id) - self.draft = Draft.find_by_id(id) - end end diff --git a/spec/dummy_app/app/mongoid/player.rb b/spec/dummy_app/app/mongoid/player.rb index 618014139b..f9c40d25b6 100644 --- a/spec/dummy_app/app/mongoid/player.rb +++ b/spec/dummy_app/app/mongoid/player.rb @@ -31,12 +31,4 @@ class Player scope :rails_admin_search, ->(query) { where(name: query.reverse) } def destroy_hook; end - - def draft_id - draft.try :id - end - - def draft_id=(id) - self.draft = Draft.where(_id: id).first - end end diff --git a/spec/rails_admin/adapters/active_record/object_extension_spec.rb b/spec/rails_admin/adapters/active_record/object_extension_spec.rb index fded94c908..b06f69d801 100644 --- a/spec/rails_admin/adapters/active_record/object_extension_spec.rb +++ b/spec/rails_admin/adapters/active_record/object_extension_spec.rb @@ -9,4 +9,52 @@ expect(object.assign_attributes(nil)).to be nil end end + + describe 'has_one association' do + let(:draft) { FactoryBot.create(:draft) } + let(:player) { FactoryBot.build(:player).extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension) } + before do + class PlayerWithAutoSave < Player + has_one :draft, inverse_of: :player, foreign_key: :player_id, autosave: true + end + end + + it 'provides id getter' do + player.draft = draft + expect(player.draft_id).to eq draft.id + end + + context 'on create' do + before do + player.draft_id = draft.id + expect(player.draft).to receive(:save).once.and_call_original + player.save + end + + it 'persists associated documents changes on save' do + expect(player.reload.draft).to eq draft + end + end + + context 'on update' do + let(:player) { FactoryBot.create(:player).extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension) } + before do + player.draft_id = draft.id + end + + it 'persists associated documents changes on assignment' do + expect(player.reload.draft).to eq draft + end + end + + context 'with explicit id setter' do + let(:user) { ManagingUser.create(FactoryBot.attributes_for(:user)) } + let(:team) { ManagedTeam.create(FactoryBot.attributes_for(:team)) } + + it 'works without issues' do + user.team_id = team.id + expect(user.reload.team).to eq team + end + end + end end diff --git a/spec/rails_admin/adapters/mongoid/object_extension_spec.rb b/spec/rails_admin/adapters/mongoid/object_extension_spec.rb index dee40b28ce..7ccece93e6 100644 --- a/spec/rails_admin/adapters/mongoid/object_extension_spec.rb +++ b/spec/rails_admin/adapters/mongoid/object_extension_spec.rb @@ -110,5 +110,15 @@ class PlayerWithAutoSave < Player end end end + + context 'with explicit id setter' do + let(:user) { ManagingUser.create(FactoryBot.attributes_for(:user)) } + let(:team) { ManagedTeam.create(FactoryBot.attributes_for(:team)) } + + it 'works without issues' do + user.team_id = team.id + expect(user.reload.team).to eq team + end + end end end