索引的圈复杂度太高

Cyclomatic complexity for index is too high

我的控制器中有索引操作:

def index
  authenticate_admin!
  @users = User.paginate(per_page: 25, page: params[:page])
  if params[:list_type].to_i == 2 # => Pending mentors
    @users = @users.where(documents: { is_verified: 0 }, user_type: 0, profile_status: 1)
  elsif params[:list_type].to_i == 1 # => approved listing
    @users = @users.where(documents: { is_verified: 1 }, user_type: 0) if params[:user_type].to_i.zero? # => Mentor
    @users = @users.where(user_type: 1) if params[:user_type].to_i == 1 # => mentee
  end
  @users = @users.where('full_name LIKE ?', "%#{params[:search]}%") if params[:search].present?
  @users = if params[:sort].to_i == 1
    @users.order(full_name: :asc)
  else
    @users.order(id: :desc)
  end

  @users = @users.profession.where(id: params[:profession_ids]) if params[:profession_ids].present?
  @users = @users.call('age >= ?', params[:from_date]) if params[:from_date].present?
  @users = @users.call('age <= ?', params[:to_date]) if params[:to_date].present?
  @users = @users.where(gender: params[:gender]) if params[:gender].present?
  @users = @users.includes(:document)
end

但是当我 运行 Rubocop 检查我的代码是否有任何 offense 时。 然后 returns 我犯了两个错误。

app/controllers/users_controller.rb:5:3: C: Metrics/CyclomaticComplexity: Cyclomatic complexity for index is too high. [11/6]
  def index ...
  ^^^^^^^^^
app/controllers/users_controller.rb:5:3: C: Metrics/PerceivedComplexity: Perceived complexity for index is too high. [13/7]
  def index ...
  ^^^^^^^^^

因此,我将索引操作分为多个操作,例如:

def index
  authenticate_admin!
  @users = User.paginate(per_page: 25, page: params[:page])
  if params[:list_type].to_i == 2 # => Pending mentors
    @users = @users.where(documents: { is_verified: 0 }, user_type: 0, profile_status: 1)
  elsif params[:list_type].to_i == 1 # => approved listing
    @users = @users.where(documents: { is_verified: 1 }, user_type: 0) if params[:user_type].to_i.zero? # => Mentor
    @users = @users.where(user_type: 1) if params[:user_type].to_i == 1 # => mentee
  end

  search
end

def search
  @users = @users.where('full_name LIKE ?', "%#{params[:search]}%") if params[:search].present?
  @users = if params[:sort].to_i == 1
    @users.order(full_name: :asc)
  else
    @users.order(id: :desc)
  end

  apply_filter
end

def apply_filter
  @users = @users.profession.where(id: params[:profession_ids]) if params[:profession_ids].present?
  @users = @users.call('age >= ?', params[:from_date]) if params[:from_date].present?
  @users = @users.call('age <= ?', params[:to_date]) if params[:to_date].present?
  @users = @users.where(gender: params[:gender]) if params[:gender].present?
  @users = @users.includes(:document)
end

这是实现这样的代码的正确方法吗?或者我还能做些什么来改进我的编码结构?

Is this the right way to implement code like this

只要你觉得代码合理清晰就可以了。如果 index 方法是控制器中唯一的方法,那么通过用户搜索的逻辑进行导航不会有问题。

is there anything else i can do to refine my coding structure

其中一个选项是将这两种方法(或更多逻辑)提取到单独的 class/file 中。例如,类似于:

控制器

def index
  authenticate_admin!
  @users = User.paginate(per_page: 25, page: params[:page])
  if params[:list_type].to_i == 2 # => Pending mentors
    @users = @users.where(documents: { is_verified: 0 }, user_type: 0, profile_status: 1)
  elsif params[:list_type].to_i == 1 # => approved listing
    @users = @users.where(documents: { is_verified: 1 }, user_type: 0) if params[:user_type].to_i.zero? # => Mentor
    @users = @users.where(user_type: 1) if params[:user_type].to_i == 1 # => mentee
  end

  @users = Users::Search.new(@users).call
end

服务

class Users::Search
  attr_reader :params, :users

  def initialize(users, params)
    @users = users
    @params = params
  end

  def search
    users = @users.where('full_name LIKE ?', "%#{params[:search]}%") if params[:search].present?
    users = if params[:sort].to_i == 1
      users.order(full_name: :asc)
    else
      users.order(id: :desc)
    end

    apply_filter(users)
  end

  def apply_filter(users)
    users = users.profession.where(id: params[:profession_ids]) if params[:profession_ids].present?
    users = users.call('age >= ?', params[:from_date]) if params[:from_date].present?
    users = users.call('age <= ?', params[:to_date]) if params[:to_date].present?
    users = users.where(gender: params[:gender]) if params[:gender].present?
    users.includes(:document)
  end
end

并且提取的优点之一是这样 class 比控制器方法更容易测试,控制器方法需要请求并为每个测试用例解析响应。