索引的圈复杂度太高

时间:2018-07-30 07:15:48

标签: ruby-on-rails ruby rubocop

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

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时。 然后返回两个错误。

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

这是实现这样的代码的正确方法,还是我可以做些其他事情来完善我的编码结构?

1 个答案:

答案 0 :(得分:3)

  

这是实现这样的代码的正确方法

只要您发现代码合理清晰即可。如果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

  @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

提取的优点之一是,此类类比控制器方法更容易测试,而控制器方法需要针对每个测试用例进行请求并解析响应。