清理(​​DRY)这个控制器的好方法是什么?

What's a good approach to clean up (DRY) this controller?

我在我的控制器上的每个创建、更新和 update_status 方法上都执行了一个操作,但我觉得我在重复自己,非常感谢您提供一些帮助,以更好的方法来编写它。

我已经将更新逻辑抽象为一个服务,但是参数在我的控制器的每个方法上都是不同的。

def create
    @story = Story.new(story_params)
    @story.creator = current_user

    if @story.save

      next_state = StoryStateService.new(@story, current_user, nil).call

      if next_state 
        @story.update_column(:status_id, next_state) 
      end 

      redirect_to stories_path
    else
      render 'stories/new'
    end
  end

  def update
    @story = Story.find(params[:id])   

    if @story.update(story_params)

      next_state = StoryStateService.new(@story, current_user, nil).call

      if next_state 
        @story.update_column(:status_id, next_state) 
      end  

      redirect_to stories_path
    else
      render 'edit'
    end
  end

  def update_story_status_event
    story = Story.find(params['story_id'])
    sub_action = params['sub_action']

    next_state = StoryStateService.new(story, current_user, sub_action).call

    if next_state 
      story.update_column(:status_id, next_state) 
    end

    redirect_to stories_path
  end

如你所见,我有

   next_state = StoryStateService.new(story, current_user, sub_action).call

    if next_state 
      story.update_column(:status_id, next_state) 
    end

在三种方法上重复,但在常规创建和更新时,我不需要发送 sub_action 参数(字符串)。

我不确定StoryStateService在做什么,但是如果是处理更新一些模型,那么你也可以制作一个模块或class并将它放在lib目录。

另一种选择是(这可能更适合 Story.find())是使用 before_action 回调。 (https://apidock.com/rails/v4.0.2/AbstractController/Callbacks/ClassMethods/before_action)

例如

当您在 updateupdate_story_status_event 中使用 Story.find() 时,您可以执行以下操作:

before_action :find_story, :only => [:update, :update_story_status_event]

   def find_story
     @story = Story.find(params[:id])
   rescue ActiveRecord::RecordNotFound
     # handle error here
   end  

我只会提取故事的进展,考虑到其他一切的简单性,任何超出此范围的内容对我来说都太过分了。

def create
  @story = Story.new(story_params)
  @story.creator = current_user

  if @story.save
    advance_story @story

    redirect_to stories_path
  else
    render 'stories/new'
  end
end

def update
  @story = Story.find(params[:id])

  if @story.update(story_params)
    advance_story @story

    redirect_to stories_path
  else
    render 'edit'
  end
end

def update_story_status_event
  story = Story.find(params['story_id'])
  sub_action = params['sub_action']

  advance_story story, sub_action

  redirect_to stories_path
end

private 

def advance_story(story, sub_action = nil)
  next_state = StoryStateService.new(story, current_user, sub_action).call

  if next_state
    story.update_column(:status_id, next_state)
  end
end