Skip to content

Commit 91737ab

Browse files
committed
Handle has_one assignment on the field level, making patched has_one getter/setter unnecessary
1 parent d62f604 commit 91737ab

File tree

8 files changed

+32
-116
lines changed

8 files changed

+32
-116
lines changed

lib/rails_admin/adapters/active_record/object_extension.rb

-18
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,6 @@ module RailsAdmin
44
module Adapters
55
module ActiveRecord
66
module ObjectExtension
7-
def self.extended(object)
8-
object.class.reflect_on_all_associations.each do |association|
9-
association = Association.new(association, object.class)
10-
case association.type
11-
when :has_one
12-
object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
13-
def #{association.name}_id
14-
self.#{association.name}&.id
15-
end
16-
17-
def #{association.name}_id=(item_id)
18-
self.#{association.name} = (#{association.klass}.find(item_id) rescue nil)
19-
end
20-
RUBY
21-
end
22-
end
23-
end
24-
257
def assign_attributes(attributes)
268
super if attributes
279
end

lib/rails_admin/adapters/mongoid/object_extension.rb

-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ def self.extended(object)
2020
send(name)&.save
2121
end
2222
end
23-
object.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
24-
def #{name}_id=(item_id)
25-
self.#{name} = (#{association.klass}.find(item_id) rescue nil)
26-
end
27-
RUBY
2823
end
2924
end
3025
end

lib/rails_admin/config/fields/types/has_one_association.rb

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class HasOneAssociation < RailsAdmin::Config::Fields::Association
2323
(o = value) && o.send(associated_model_config.object_label_method)
2424
end
2525

26+
register_instance_option :allowed_methods do
27+
nested_form ? [method_name] : [name]
28+
end
29+
2630
def selected_id
2731
value.try(:id).try(:to_s)
2832
end
@@ -38,6 +42,13 @@ def multiple?
3842
def associated_prepopulate_params
3943
{associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
4044
end
45+
46+
def parse_input(params)
47+
return if nested_form
48+
49+
id = params.delete(method_name)
50+
params[name] = associated_model_config.abstract_model.get(id) if id
51+
end
4152
end
4253
end
4354
end

spec/dummy_app/app/active_record/managing_user.rb

-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,4 @@
33
class ManagingUser < User
44
has_one :team, class_name: 'ManagedTeam', foreign_key: :manager, primary_key: :email, inverse_of: :user
55
has_many :teams, class_name: 'ManagedTeam', foreign_key: :manager, primary_key: :email, inverse_of: :user
6-
7-
def team_id=(id)
8-
self.team = ManagedTeam.find_by_id(id)
9-
end
106
end

spec/dummy_app/app/mongoid/managing_user.rb

-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@ class ManagingUser < User
55
has_many :teams, class_name: 'ManagedTeam', foreign_key: :manager, primary_key: :email, inverse_of: :user
66
has_and_belongs_to_many :players, foreign_key: :player_names, primary_key: :name, inverse_of: :nil
77
has_and_belongs_to_many :balls, primary_key: :color, inverse_of: :nil
8-
9-
def team_id=(id)
10-
self.team = ManagedTeam.where(_id: id).first
11-
end
128
end

spec/integration/fields/has_one_association_spec.rb

+19-14
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
context 'on create' do
1515
before do
1616
@draft = FactoryBot.create :draft
17+
visit new_path(model_name: 'player')
1718
end
1819

1920
it 'shows selects' do
20-
visit new_path(model_name: 'player')
2121
is_expected.to have_selector('select#player_draft_id')
2222
end
2323

2424
it 'creates an object with correct associations' do
25-
post new_path(model_name: 'player', player: FactoryBot.attributes_for(:player).merge(name: 'Jackie Robinson', draft_id: @draft.id))
25+
fill_in 'Name', with: 'Jackie Robinson'
26+
fill_in 'Number', with: @draft.player.number + 1
27+
select("Draft ##{@draft.id}", from: 'Draft')
28+
click_button 'Save'
29+
is_expected.to have_content 'Player successfully created'
2630
@player = Player.where(name: 'Jackie Robinson').first
2731
@draft.reload
2832
expect(@player.draft).to eq(@draft)
@@ -31,22 +35,23 @@
3135

3236
context 'on update' do
3337
before do
34-
@player = FactoryBot.create :player
35-
@draft = FactoryBot.create :draft
36-
@number = @draft.player.number + 1 # to avoid collision
37-
put edit_path(model_name: 'player', id: @player.id, player: {name: 'Jackie Robinson', draft_id: @draft.id, number: @number, position: 'Second baseman'})
38-
@player.reload
38+
@drafts = FactoryBot.create_list :draft, 2
39+
@player = FactoryBot.create :player, draft: @drafts[0]
40+
visit edit_path(model_name: 'player', id: @player.id)
3941
end
4042

41-
it 'updates an object with correct attributes' do
42-
expect(@player.name).to eq('Jackie Robinson')
43-
expect(@player.number).to eq(@number)
44-
expect(@player.position).to eq('Second baseman')
43+
it 'updates an object with correct associations' do
44+
select("Draft ##{@drafts[1].id}", from: 'Draft')
45+
click_button 'Save'
46+
@player.reload
47+
expect(@player.draft).to eq(@drafts[1])
4548
end
4649

47-
it 'updates an object with correct associations' do
48-
@draft.reload
49-
expect(@player.draft).to eq(@draft)
50+
it 'clears the current selection' do
51+
select('', from: 'Draft')
52+
click_button 'Save'
53+
@player.reload
54+
expect(@player.draft).to be nil
5055
end
5156
end
5257

spec/rails_admin/adapters/active_record/object_extension_spec.rb

-59
Original file line numberDiff line numberDiff line change
@@ -11,63 +11,4 @@
1111
expect(object.assign_attributes(nil)).to be nil
1212
end
1313
end
14-
15-
describe 'has_one association' do
16-
let(:draft) { FactoryBot.create(:draft) }
17-
let(:player) { FactoryBot.build(:player).extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension) }
18-
before do
19-
class PlayerWithAutoSave < Player
20-
has_one :draft, inverse_of: :player, foreign_key: :player_id, autosave: true
21-
end
22-
end
23-
24-
it 'provides id getter' do
25-
player.draft = draft
26-
expect(player.draft_id).to eq draft.id
27-
end
28-
29-
context 'on create' do
30-
before do
31-
player.draft_id = draft.id
32-
expect(player.draft).to receive(:save).once.and_call_original
33-
player.save
34-
end
35-
36-
it 'persists associated documents changes on save' do
37-
expect(player.reload.draft).to eq draft
38-
end
39-
end
40-
41-
context 'on update' do
42-
let(:player) { FactoryBot.create(:player).extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension) }
43-
before do
44-
player.draft_id = draft.id
45-
end
46-
47-
it 'persists associated documents changes on assignment' do
48-
expect(player.reload.draft).to eq draft
49-
end
50-
end
51-
52-
context 'with explicit id setter' do
53-
let(:user) { ManagingUser.create(FactoryBot.attributes_for(:user)) }
54-
let(:team) { ManagedTeam.create(FactoryBot.attributes_for(:team)) }
55-
56-
it 'works without issues' do
57-
user.team_id = team.id
58-
expect(user.reload.team).to eq team
59-
end
60-
end
61-
62-
context 'when associated class has custom primary key' do
63-
let(:league) { FactoryBot.build(:league).extend(RailsAdmin::Adapters::ActiveRecord::ObjectExtension) }
64-
let(:division) { FactoryBot.create :division }
65-
66-
it 'does not break' do
67-
league.division_id = division.id
68-
league.save!
69-
expect(league.reload.division).to eq division
70-
end
71-
end
72-
end
7314
end

spec/rails_admin/adapters/mongoid/object_extension_spec.rb

+2-12
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class PlayerWithAutoSave < Player
6969

7070
context 'on create' do
7171
before do
72-
player.draft_id = draft.id
72+
player.draft = draft
7373
expect(player.draft._target).to receive(:save).once.and_call_original
7474
player.save
7575
end
@@ -93,7 +93,7 @@ class PlayerWithAutoSave < Player
9393

9494
context 'on update' do
9595
before do
96-
player.draft_id = draft.id
96+
player.draft = draft
9797
end
9898

9999
context 'with autosave: false' do
@@ -112,15 +112,5 @@ class PlayerWithAutoSave < Player
112112
end
113113
end
114114
end
115-
116-
context 'with explicit id setter' do
117-
let(:user) { ManagingUser.create(FactoryBot.attributes_for(:user)) }
118-
let(:team) { ManagedTeam.create(FactoryBot.attributes_for(:team)) }
119-
120-
it 'works without issues' do
121-
user.team_id = team.id
122-
expect(user.reload.team).to eq team
123-
end
124-
end
125115
end
126116
end

0 commit comments

Comments
 (0)