Grug
Grug

Reputation: 1890

Rails scope filter/search from association

I have a one-to-one association between PublicKey and PublicKeyRole. The PublicKey model has a column, role_id, and the PublicKeyRole model has role column which is a string, such as 'super'.

I want to be able to search by role string via a url query param, like; https://api.domain.com/public_keys?role=admin. I've tried this on the PublicKey model but I'm not sure where to pass the query in to:

scope :filter_by_role, lambda { |query|
  joins(:public_key_role).merge(PublicKeyRole.role)
}

Here are my models:

class PublicKey < ActiveRecord::Base
  belongs_to :user
  belongs_to :public_key_role, foreign_key: :role_id

  def self.search(params = {})
    public_keys = params[:public_key_ids].present? ? PublicKey.where(id: params[:public_key_ids]) : PublicKey.all
    public_keys = public_keys.filter_by_role(params[:role]) if params[:role]

    public_keys
  end
end

class PublicKeyRole < ActiveRecord::Base
  has_one :public_key
end

Also, here's my test:

describe '.filter_by_role' do
    before(:each) do
      @public_key1 = FactoryGirl.create :public_key, { role_id: 1 }
      @public_key2 = FactoryGirl.create :public_key, { role_id: 2 }
      @public_key3 = FactoryGirl.create :public_key, { role_id: 1 }
    end

    context "when a 'super' role is sent" do
      it 'returns the 2 matching public keys results' do
        expect(PublicKey.filter_by_role('super').size).to eq(2)
      end

      it 'returns the matching public keys' do
        expect(PublicKey.filter_by_role('super').sort).to match_array([@public_key1, @public_key3])
      end
    end
  end

Update

I was missing the following in my spec, @lcguida's answer works.

FactoryGirl.create :public_key_role, { id: 1, role: 'super' }

Upvotes: 0

Views: 1606

Answers (1)

lcguida
lcguida

Reputation: 3847

From the docs you have some examples. You can search in the relation passing the query to the PublicKeyRole relation:

scope :filter_by_role, lambda { |query|
  joins(:public_key_role).merge(PublicKeyRole.where(role: query))
}

If you want a string search (as LIKE):

scope :filter_by_role, lambda { |query|
  joins(:public_key_role).merge(PublicKeyRole.where(PublicKeyRole.arel_table[:role].matches(query)))
}

Upvotes: 1

Related Questions