Search code examples
ruby-on-railsrubydesign-patternsdry

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


I have an action being performed on every create, update and update_status methods on my controller, But I feel that I am repeating myself, and would really appreciate some help on a better approach to write this.

I've abstracted the update logic to a service, but the parameters are different, on every method of my controller.

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

As you can see, I have

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

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

being repeated on three methods, but on regular create and update, I dont need to send a sub_action param (string).


Solution

  • I would just extract the advance of the story, anything more than that would be overkill to me, given the simplicity of everything else.

    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