繁体   English   中英

Ruby 上轨,Rubocop 重构

[英]Ruby on rails, Rubocop refactoring

罪行:

app/controllers/tasks_controller.rb:12:3: C: Metrics/AbcSize: 用于创建的分配分支条件大小太高。 [<3, 19, 1> 19.26/17] 定义创建...

app/controllers/tasks_controller.rb:24:3: C: Metrics/AbcSize: 更新的分配分支条件大小太高。 [<4, 20, 1> 20.42/17] 定义更新...

检查了 27 个文件,发现了 2 个违规行为

  def create
    @task = current_user.projects.find(params[:project_id]).tasks.new
    @task.title = task_params[:title]

    @task.save ? (redirect_to root_url) : (flash[:error] = @task.errors.full_messages) | (render 
    :new)
  end

  def update
    @task = current_user.tasks.find(params[:id])
    @task.title = task_params[:title]
    @task.deadline = task_params[:deadline]

    @task.save ? (redirect_to root_url) : (flash[:error] = @task.errors.full_messages) | (render 
    :edit)
  end

我如何重构它以解决 rubocop 的罪行?

通过创建私有方法解决了问题:

  def create
    tasknew
    tasktitle

    @task.save ? (redirect_to root_url) : (flash[:error] = @task.errors.full_messages) | (render :new)
  end

  def update
    taskfind
    tasktitle
    @task.deadline = task_params[:deadline]

    @task.save ? (redirect_to root_url) : (flash[:error] = @task.errors.full_messages) | (render :edit)
  end

private

  def tasktitle
    @task.title = task_params[:title]
  end

  def taskfind
    @task = current_user.tasks.find(params[:id])
  end

  def tasknew
    @task = current_user.projects.find(params[:project_id]).tasks.new
  end

您已经提取了代码中最简单的部分并留下了最复杂的部分。 ABC 指标的重点是指出代码的复杂部分,以便您可以简化它们,并可能发现错误。 如果你只是提取最琐碎的部分来满足警察,你还不如禁用它。

@task.save? (redirect_to root_url): (flash[:error] = @task.errors.full_messages) | (render:edit) @task.save? (redirect_to root_url): (flash[:error] = @task.errors.full_messages) | (render:edit)显然是这些方法中最复杂的部分。 我很难理解它。 为什么有按位或? 它是重复的,除了要渲染的内容。

让我们把它分开。 首先,将三元运算符转换为 if/else。

if @task.save
  redirect_to root_url
else
  (flash[:error] = @task.errors.full_messages) | (render :edit)
end

我不这么认为| 实际上是在做任何事情。 这只是一种将两个语句塞进一个语句的方法。

if @task.save
  redirect_to root_url
else
  flash[:error] = @task.errors.full_messages
  render :edit
end

这更容易理解,并且稍微减少了 ABC。

接下来是将查找代码放入自己的私有方法中。 只是一个简单的提取方法。

# Note I left it at just finding the associated project.
# I left the .tasks.new off because that's very specific.
private def current_project
  current_user.projects.find(params[:project_id])
end

private def current_task
  current_user.tasks.find(params[:id])
end

def create
  @task = current_project.tasks.new
  @task.title = task_params[:title]

  if @task.save
    redirect_to root_url
  else
    flash[:error] = @task.errors.full_messages
    render :edit
  end
end

def update
  @task = current_task
  @task.title = task_params[:title]
  @task.deadline = task_params[:deadline]

  if @task.save
    redirect_to root_url
  else
    flash[:error] = @task.errors.full_messages
    render :edit
  end
end

这样就可以了。 您可以更进一步,提取保存逻辑,传入渲染位置和任何其他可能更改的细节。

# Note the use of defaults.
private def save_task(render:, redirect_to: root_url)
  if @task.save
    redirect_to(redirect_to)
  else
    flash[:error] = @task.errors.full_messages
    render(render)
  end
end

def create
  @task = current_project.tasks.new
  @task.title = task_params[:title]

  save_task(render: :new)
end

def update
  @task = current_task
  @task.title = task_params[:title]
  @task.deadline = task_params[:deadline]

  save_task(render: :edit)
end

它揭示了一些可能的问题。

createupdate使用不同的方法来查找任务。 好像很腥您的tasknewtaskfind隐藏了这种差异,使代码更难理解。

update是设置最后期限,但create不是。 这表明从 task_params 设置 @task 属性应该提取到一个方法中。


请注意,我避免将分配放入私有方法中。 尽管这在技术上减少了 ABC,但它使代码更难理解,因为方法调用有副作用 它改变了 object 的 state。 副作用必须有目的地进行。

例如,可以将current_taskcurrent_project转换为具有默认值的记忆访问器。

private def current_project
  @current_project ||= current_user.projects.find(params[:project_id])
end

现在它只会加载 object 一次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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