简体   繁体   English

Ruby 上轨,Rubocop 重构

[英]Ruby on rails, Rubocop refactoring

Offenses:罪行:

app/controllers/tasks_controller.rb:12:3: C: Metrics/AbcSize: Assignment Branch Condition size for create is too high. app/controllers/tasks_controller.rb:12:3: C: Metrics/AbcSize: 用于创建的分配分支条件大小太高。 [<3, 19, 1> 19.26/17] def create... [<3, 19, 1> 19.26/17] 定义创建...

app/controllers/tasks_controller.rb:24:3: C: Metrics/AbcSize: Assignment Branch Condition size for update is too high. app/controllers/tasks_controller.rb:24:3: C: Metrics/AbcSize: 更新的分配分支条件大小太高。 [<4, 20, 1> 20.42/17] def update... [<4, 20, 1> 20.42/17] 定义更新...

27 files inspected, 2 offenses detected检查了 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

How can i refactor it to resolve rubocop's offences?我如何重构它以解决 rubocop 的罪行?

Solved problem by creating private methods:通过创建私有方法解决了问题:

  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

You've extracted the simplest part of your code and left the most complex.您已经提取了代码中最简单的部分并留下了最复杂的部分。 The point of the ABC metric is to point out complex parts of your code so you can simplify them, and possibly catch bugs. ABC 指标的重点是指出代码的复杂部分,以便您可以简化它们,并可能发现错误。 If you just extract the most trivial parts to satisfy the cop, you might as well disable it.如果你只是提取最琐碎的部分来满足警察,你还不如禁用它。

@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) is obviously the most complex part of those methods. @task.save? (redirect_to root_url): (flash[:error] = @task.errors.full_messages) | (render:edit)显然是这些方法中最复杂的部分。 I'm having trouble understanding it.我很难理解它。 Why is there a bitwise or?为什么有按位或? And it's duplicated, except or what to render.它是重复的,除了要渲染的内容。

Let's pull this apart.让我们把它分开。 First, turn the ternary operator into an if/else.首先,将三元运算符转换为 if/else。

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

I don't think that |我不这么认为| is actually doing anything.实际上是在做任何事情。 It's just a way to cram two statements into one.这只是一种将两个语句塞进一个语句的方法。

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

That is much easier to understand, and it reduces the ABC a bit.这更容易理解,并且稍微减少了 ABC。

Next is to put the find code into their own private methods.接下来是将查找代码放入自己的私有方法中。 Just a simple extract method.只是一个简单的提取方法。

# 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

And that will do it.这样就可以了。 You could take this a step further and extract the save logic, passing in where to render and any other details that might change.您可以更进一步,提取保存逻辑,传入渲染位置和任何其他可能更改的细节。

# 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

It reveals some possible issues.它揭示了一些可能的问题。

create and update are using different methods of finding the task. createupdate使用不同的方法来查找任务。 Seems fishy.好像很腥Your tasknew and taskfind hide this difference making the code harder to understand.您的tasknewtaskfind隐藏了这种差异,使代码更难理解。

And update is setting the deadline, but create is not. update是设置最后期限,但create不是。 That suggests setting the @task attributes from task_params should be extracted into a single method.这表明从 task_params 设置 @task 属性应该提取到一个方法中。


Note that I avoided putting the assignment into the private methods.请注意,我避免将分配放入私有方法中。 Even though this technically reduces ABC, it makes the code harder to understand because the method call has a side effect ;尽管这在技术上减少了 ABC,但它使代码更难理解,因为方法调用有副作用 it changes the state of the object.它改变了 object 的 state。 Side effects have to be done with purpose.副作用必须有目的地进行。

For example, one could turn current_task and current_project into memoized accessors with defaults.例如,可以将current_taskcurrent_project转换为具有默认值的记忆访问器。

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

Now it will only load the object once.现在它只会加载 object 一次。

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

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