Severin
Severin

Reputation: 8588

Moving out instance variables from Controller to Service Class

I am at the point where the controller of my Rails 5 application start to get crowded, so I am thinking about moving as much as possible out to service classes. This works well for most of the business logic, but I can't seem to be able to move any instance variables out of the method. What I want to do is something that follows this pattern:

# users_controller.rb
class UsersController < ApplicationController
  def show
    UserService.new.set_user_detail_variables(params[:id])

    respond_to do |format|
      format.html
      format.json { render json: @user }
    end
  end
end

# user_service.rb
class UserService

  def set_user_detail_variables(user_id)
    @user = User.find(user_id)
    @cars = Car.where(brand: 'Volvo')
  end

end

Is it all possible to move instance variables out of the controller?

Upvotes: 2

Views: 1466

Answers (2)

VAD
VAD

Reputation: 2401

UPDATED according to your updated question

I believe you have to have at least one instance variable in your controller action to render its data or send it as json. And service object are not the one to do such things. Service objects designed to perform single huge or complicated operation.

What you could do is to use Presenter object (Presenter pattern as Rails community sees it). This pattern in Rails is useful to "hide" several instance variables inside the single one. So put your instance variables inside the single Presenter variable. Then pass presenter to the views or render its data as json.

Example

Before:

class UsersController < ApplicationController
  def show
    @user = User.find(user_id)
    @cars = Car.where(brand: 'Volvo')

    respond_to do |format|
      format.html
      format.json { render json: @user, @car }
    end
  end
end

After:

class UsersController < ApplicationController
  def show
    user = User.find(user_id)
    cars = Car.where(brand: 'Volvo')
    @show_presenter = ShowPresenter.new(user, cars)

    respond_to do |format|
      format.html
      format.json { render json: @show_presenter }
    end
  end
end

class ShowPresenter
  def initialize(user, cars)
    @user = user
    @cars = cars
  end
end

Upvotes: 1

Jagdeep Singh
Jagdeep Singh

Reputation: 4920

If you want to do some calculations in a separate method and assign some instance variables there, then use them in your view, you can move those methods to a mixin and include that mixin inside your controller.

# mixins/user_helper.rb
module UserHelper
  def set_user_detail_variables(user_id)
    @user = User.includes(:comments, :addresses, :posts).find(user_id)

    @user_comments  = @user.comments
    @user_addresses = @user.addresses
    @user_posts     = @user.posts
  end
end

# controllers/users_controller.rb
class UsersController < ApplicationController
  include UserHelper

  def show
    set_user_detail_variables(params[:id])

    respond_to do |format|
      format.html
      format.json { render json: @user }
    end
  end
end

This way your controller would not get lengthy and also you will be able to use your variables.

Upvotes: 2

Related Questions