1
\$\begingroup\$

I have a photo_shouts_controller.rb and text_shouts_controller.rb which both have almost similar implementations, and I would like to refactor it.

I have implemented a refactoring but I am not too sure if that is the best way to do it.

Current implementation

class PhotoShoutsController < ApplicationController
  def create
    content = build_content
    shout = current_user.shouts.build(content: content)

    if shout.save
      redirect_to dashboard_path
    else
      redirect_to dashboard_path, notice: 'not created!'
    end
  end

  private

  def build_content
    PhotoShout.new(photo_shouts_parameters)
  end

  def photo_shouts_parameters
    params.require(:photo_shout).permit(:image)
  end
end

class TextShoutsController < ApplicationController
  def create
    content = build_content
    shout = current_user.shouts.build(content: content)

    if shout.save
      redirect_to dashboard_path
    else
      redirect_to dashboard_path, notice: 'not created!'
    end
  end

  private

  def build_content
    TextShout.new(text_shouts_parameters)
  end

  def text_shouts_parameters
    params.require(:text_shout).permit(:body)
  end
end

My new implementation

ShoutBuild.rb

class ShoutBuild
  def initialize(current_user, shout_parameters, shout_class)
    @shout_parameters = shout_parameters
    @shout_class = shout_class
    @user = current_user
  end

  def build
    content = build_content
    @user.shouts.build(content: content)
  end

  private

  def build_content
    @shout_class.new(@shout_parameters)
  end
end

Controllers

class TextShoutsController < ApplicationController
  def create
    shout = ShoutBuild.new(current_user, text_shouts_parameters, TextShout).build

    if shout.save
      redirect_to dashboard_path
    else
      redirect_to dashboard_path, notice: 'not created'
    end
  end

  private

  def text_shouts_parameters
    params.require(:text_shout).permit(:body)
  end
end

class PhotoShoutsController < ApplicationController
  def create
    shout = ShoutBuild.new(current_user, photo_shouts_parameters, PhotoShout).build

    if shout.save
      redirect_to dashboard_path
    else
      redirect_to dashboard_path, notice: 'not created!'
    end
  end

  private

  def photo_shouts_parameters
    params.require(:photo_shout).permit(:image)
  end
end
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

By moving the build statement into a service you actually aren't saving yourself much, even after the refactor you still have a bunch of code that isn't DRY. Depending on how big your application is I would move pretty much all of the logic for your create actions into either ApplicationController, or, more likely a module. The module could look like this:

module ShoutController
  def create
    shout = current_user.shouts.build(content: build_content)
    redirect_params = shout.save ? {} : {notice: 'not created'}
    redirect_to dashboard_path, redirect_params
  end
end

Then your controllers would simply be:

class PhotoShoutsController < ApplicationController
  include ShoutController

  private

  def build_content
    PhotoShout.new(params.require(:photo_shout).permit(:image))
  end
end

And

class TextShoutsController < ApplicationController
  include ShoutController

  private

  def build_content
    TextShout.new(params.require(:text_shout).permit(:body))
  end
end
\$\endgroup\$
1
  • \$\begingroup\$ Very clean solution, but it really feels like inheritance and override the build_content method is the more appropriate solution, since the controllers are related (they process shouts). \$\endgroup\$ May 24, 2016 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.