简体   繁体   中英

Rails NoMethodError (undefined method `<<' for #<ActiveRecord::Relation []>):

The Back Story

I am currently changing my Ruby on Rails app to a multi-database configuration. Main reason for the switch was to put my Member(User) and Profile tables in a separate database, which could be accessed from another RoR app; thus allowing me to have a single sign-on capability aside from using OAuth and Doorkeeper. This has been a many many hours project with many crazy hurdles. Finally tonight it seemed that everything was working, until I ran my spec tests, and one of my work arounds I did today is throwing flags.

Here is the appropriate code for the problem, simplified for brevity sake.

Any help would be greatly appreciated. Thank you.

Models

app/models/members_record.rb

class MembersRecord < ApplicationRecord
    self.abstract_class = true
  
    connects_to database: { writing: :members, reading: :members }
end

app/models/member.rb

class Member < MembersRecord
    devise  :database_authenticatable, :registerable,
            :recoverable, :rememberable, :trackable, 
            :validatable, :confirmable
  
    has_one :member_map,    dependent: :destroy
    has_one :profile,       dependent: :destroy
    has_one :site_profile,  dependent: :destroy
    
    has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
    has_many :groups, through: :approved_group_members, class_name: "Group", source: :group

app/models/group.rb

class Group < ApplicationRecord
    belongs_to :owner, class_name: 'Member'
    belongs_to :organization, optional: true
  
    has_many :group_members, dependent: :destroy
  
    # Many of the lines below no longer work with Multiple DBs, as we can not INNER JOIN across DBs.
    # They have been commented out and methods built in their place.
  
    has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
    # has_many :members, through: :approved_group_members, class_name: "Member", source: :member
    def members
      Member.where(id: self.approved_group_members.pluck(:member_id))
    end
  
    has_many :invited_group_members, -> { invited }, class_name: "GroupMember"
    # has_many :invited_members, through: :invited_group_members, class_name: "Member", source: :member
    def invited_members
      Member.where(id: self.invited_group_members.pluck(:member_id))
    end

app/models/group_member.rb

class GroupMember < ApplicationRecord
  belongs_to :group
  belongs_to :member

  enum status: { approved: 0, invited: 1, pending_approval: 2, banned: 3}
end

The Workarounds

Now, looking at the above code, you should see a few of the workarounds in question within the group model. The original code was commented out with the workaround beneath it. Here is just that code:

# Many of the lines below no longer work with Multiple DBs, as we can not INNER JOIN across DBs.
# They have been commented out and methods built in their place.

has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
# has_many :members, through: :approved_group_members, class_name: "Member", source: :member
def members
  Member.where(id: self.approved_group_members.pluck(:member_id))
end

has_many :invited_group_members, -> { invited }, class_name: "GroupMember"
# has_many :invited_members, through: :invited_group_members, class_name: "Member", source: :member
def invited_members
  Member.where(id: self.invited_group_members.pluck(:member_id))
end

The reason for these workarounds is because the has_many relationship was not working with the class Member once that class was referencing data on a different database. The SQL queries being built were failing. So, I made a method instead and called a where on the Member model. That gave me the list of members that I was looking for. I had to do this on 9 different occurrences.

Now the problem that I am having

Within my code in over 100 different spots I add a member, or other object, into a given list, say of 'invited members' with this type of code. (This code specifically is within one of my specs now failing)

it 'will set the GroupMember to approved if the group is invite only and member was invited' do
  group_invitation
  group.invited_members << member
  expect { service.call! }.to change{GroupMember.count}.by(0)
  expect(service.success?).to be_truthy
  expect(service.success_message).to eq("You have successfully joined this group.")
  expect(service.error).to eq("")
end

The second line reads group.invited_members << member and then I get an error that states:

NoMethodError (undefined method `<<' for #<ActiveRecord::Relation []>):

My Question

I completely understand what the error is stating, but am at a loss for how to correct it. Is there a different way that I could do the workaround in order to allow the << to remain? I'd very much prefer to only have to change 9 different times I did the workaround instead of rewriting over 100 different instances where << is being used to add the member, or whatever the object is, to the list being referenced.

Please let me know if you need any further details. Thank you again!

This is supported natively on Rails 7 upwards. (See https://edgeguides.rubyonrails.org/active_record_multiple_databases.html#handling-associations-with-joins-across-databases , https://github.blog/2021-07-12-adding-support-cross-cluster-associations-rails-7/ and https://github.com/rails/rails/pull/41937 )

If you really need this, I'd probably recommend moving to rails master, and use the native way:

has_many :members, 
         through: :approved_group_members, 
         class_name: "Member", 
         source: :member, 
         disable_joins: true
  1. I would like to go with rewritten you can go with master of rails.

  2. you can try to convert the object from activerecord relation to active record collection proxy object and there we << this method.Which i think is kind of not a good approach

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM