简体   繁体   中英

"Assignment Branch Condition size for call is too high" in rails query object

I have a query object in a rails project which searches a resource with multiple filters (fees, name, specialty, years of experience).

class SearchDoctors
  attr_accessor :initial_scope, :search_params

  def self.call(initial_scope, search_params)
    new(initial_scope, search_params).call
  end

  def initialize(initial_scope, search_params)
    @initial_scope = initial_scope
    @search_params = search_params
  end


  # Assignment branch condition on call method
  def call
    scoped = filter_by_speciality(initial_scope, search_params[:speciality])
    scoped = filter_by_name(scoped, search_params[:name])
    scoped = filter_by_fees(scoped,
                            search_params[:fees_from],
                            search_params[:fees_to])
    filter_by_years_of_experience(scoped,
                                  search_params[:experience_from],
                                  search_params[:experience_to])
  end
end

The filter methods are private methods removed for brevity.

The call method gives "assignment branch condition is too high" rubocop warning, which makes sense because it does much. How I can refactor it to bypass the rubocop warning?

I saw a few similar questions but none of them solved my problem.

There are plenty of ways to construct scopes in Rails without using reassignment which just the lazy way to do it.

You could make chainable scopes on the the model itself:

class Doctor < ApplicationRecord
  def self.filter_by_speciality(speciality)
    speciality.present ? self.where(speciality: speciality) : self     
  end

  def self.filter_by_name(name)
    name.present ? self.where(name: name) : self     
  end
end

That will let you call:

Doctor.filter_by_speciality(params[:speciality])
      .filter_by_name(params[:name])
      # etc

Always returning self or another scope will prevent nil errors.

You can also use .merge to combine scopes.

Doctor.where(name: 'John').merge(
  Doctor.where(specialty: 'pediatrician')
)

So if you start by refactoring the scoped argument out of those methods you can compose an array of scopes and merge them together:

def call
  # not going to list them all. 
  scopes = [filter_by_speciality(search_params[:speciality]), filter_by_name(search_params[:name])]
  scopes.compact.each_with_object(initial_scope) do |filter, memo|
     memo.merge(filter)
   end
end

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