帮我重构这个讨厌的Ruby if / else语句

时间:2010-05-08 11:54:49

标签: ruby-on-rails ruby

所以我在我的新闻稿发布申请中有这个大方法。方法是更新人造丝,我需要将用户分配给人造丝。我的关系是n:n到表colporteur_in_rayons,其中包含since_dateuntil_date属性。

我是初级程序员,我知道这段代码非常虚拟:) 我很欣赏每一个建议。

def update
  rayon = Rayon.find(params[:id])
  if rayon.update_attributes(params[:rayon])
    if params[:user_id] != ""
      unless rayon.users.empty?
        unless rayon.users.last.id.eql?(params[:user_id])
          rayon.colporteur_in_rayons.last.update_attributes(:until_date => Time.now)
          Rayon.assign_user(rayon.id,params[:user_id])
          flash[:success] = "Rayon #{rayon.name} has been succesuly assigned to #{rayon.actual_user.name}."
          return redirect_to rayons_path
        end
      else
         Rayon.assign_user(rayon.id,params[:user_id])
         flash[:success] = "Rayon #{rayon.name} has been successfully assigned to #{rayon.actual_user.name}."
         return redirect_to rayons_path
      end
    end
    flash[:success] = "Rayon has been successfully updated."
    return redirect_to rayons_path
  else
    flash[:error] = "Rayon has not been updated."
    return redirect_to :back
  end
end

4 个答案:

答案 0 :(得分:5)

def update
    rayon = Rayon.find(params[:id])

    unless rayon.update_attributes(params[:rayon])
        flash[:error] = "Rayon not updated."
        return redirect_to :back
    end

    puid = params[:user_id]
    empty = rayon.users.empty?

    if puid == "" or (not empty and rayon.users.last.id.eql?(puid))
        msg = "Rayon updated.",
    else
        msg = "Rayon #{rayon.name} assigned to #{rayon.actual_user.name}.",
        rayon.colporteur_in_rayons.last.update_attributes(
            :until_date => Time.now) unless empty
        Rayon.assign_user(rayon.id, puid)
    end

    flash[:success] = msg[msg_i]
    return redirect_to rayons_path
end

答案 1 :(得分:1)

有一些双重代码。所以删除它。但是,控制器中还有一些业务代码,不应该存在。所以我应该按如下方式重构控制器代码:

def update
  rayon = Rayon.find(params[:id])

  if rayon.update_attributes(params[:rayon])
    if params[:user_id] != ""
      rayon.handle_update_user(params[:user_id]
      flash[:success] = "Rayon #{rayon.name} has been succesuly assigned to #{rayon.actual_user.name}."
    else
      flash[:success] = "Rayon has been successfully updated."
    end
    return redirect_to rayons_path
  else
    flash[:error] = "Rayon has not been updated."
    return redirect_to :back
  end
end

控制器方法现在可以清楚地处理需要采取的操作,并在需要时相应地设置闪存方法。

在Rayon模型中,您可以编写代码,当用户完成更新时需要执行的操作:

class Rayon

  def handle_update_user(user_id)
    if (!users.empty? && users.last.id.eql?(params[:user_id]))
      # do nothing! 
    else 
      colporteur_in_rayons.last.update_attributes(:until_date => Time.now) unless users.empty?
      Rayon.assign_user(rayon.id,params[:user_id])
    end

  end

end

这清楚地表明了这些担忧。人造丝应该知道当用户更新它时会发生什么(函数的名称可以改进到你实际想要它的意思,因为这对我来说并不完全清楚)。 它可能更短,但我想明确地写下如果最后一个用户与当前用户相同则不需要做任何事情。否则,需要采取行动。如果我理解正确的话。

答案 2 :(得分:0)

这是我的看法。我把一堆评论内联描述了为什么我改变了我的所作所为。

def update
    rayon = Rayon.find(params[:id])
    if rayon.update_attributes(params[:rayon])
        user_id = params[:user_id] # using same value a couple times
        # if-else strongly preferred over unless-else...
        # so replace `unless foo.empty?` with `if foo.length > 0`
        if user_id.length > 0 # then string != ""
            # `if A && B` more readable than `unless X || Y` in my opinion
            if rayon.users.length > 0 && rayon.users.last.id != user_id
                rayon.colporteur_in_rayons.last.update_attributes(:until_date => Time.now)
            end 
            # same line in if-else; pull outside
            Rayon.assign_user(rayon.id, user_id)
            flash[:success] = "Rayon #{rayon.name} has been successfully assigned to #{rayon.actual_user.name}."
        else
            flash[:success] = "Rayon has been successfully updated."
        end

        # all branches in here return this value, pull outside
        return redirect_to rayons_path
    else
        flash[:error] = "Rayon has not been updated."
        return redirect_to :back
    end
end

我注意到你的代码中最大的问题是你的第一个if-block中有很多重复。真部分中的所有分支都达到了return redirect_to rayons_path,因此被拉到了块的末尾。你有两次相同的flash[:success] = ...,所以也被拉出来了。

if-elseunless-else更具可读性,因此请尽量避免使用后者。我不是一个粉丝,除非立即嵌套在另一个中,所以我将其改为复合if语句。

Rails中的一个主要习语是DRY(不要重复自己),所以尽量找出重复代码的地方。

(另请注意,我没有测试过这种重构 - 我很确定逻辑仍然相同,但我无法证明。)

答案 3 :(得分:0)

def update
  rayon = Rayon.find(params[:id])

  unless rayon.update_attributes(params[:rayon])
    flash[:error] = "Rayon has not been updated."
    return redirect_to :back
  end

  if params[:user_id].empty?
    msg = "Rayon has been successfully updated."
  else
    if rayon.users.empty?
      Rayon.assign_user(rayon.id,params[:user_id])
      msg = "Rayon #{rayon.name} has been successfully assigned to #{rayon.actual_user.name}."
    elsif not rayon.users.last.id.eql?(params[:user_id])
      rayon.colporteur_in_rayons.last.update_attributes(:until_date => Time.now)
      Rayon.assign_user(rayon.id,params[:user_id])
      msg = "Rayon #{rayon.name} has been succesuly assigned to #{rayon.actual_user.name}."
    end
  end
  flash[:success] = msg
  return redirect_to rayons_path
end