Run
Run

Reputation: 896

Calling a method in another class?

I have two controllers in different namespace(a and b), like below:

class A::TechnologiesController < ApplicationController
  def index
    render json: Technology.all
  end
end

class B::TechnologiesController < ApplicationController
  def index
    render json: Technology.all
  end
end

The two actions execute the same logic, and I belive it is a repetition. I want to eliminate the repetition, so how can I borrow the code in namespace a like below?

class B::TechnologiesController < ApplicationController
  def index
    A::TechnologiesController.method(:index).call self
  end
end

Upvotes: 1

Views: 1207

Answers (4)

Mohammad Shahnawaz
Mohammad Shahnawaz

Reputation: 876

You can solve the above problem by making a super method in the ApplicationController and you need to add one more method in each controller to pass the values to super method you can not only used for Technology model you can also used it for any other model.

For sample example

In the application controller

    class ApplicationController < ActionController::Base
      def index
        @collection = model
      end
    end

In each controller we can call the above method like this

    class StudentsController < ApplicationController

      def index
        super
      end

      def model
        Student
      end
    end




    class JobsController < ApplicationController
      def index
        super
      end
      def model
        Job
      end
    end

The data you will recived in the @collection that can be used in the views for example

For the app/views/students/index.html.erb

    <% @collection.all.each do |student| %>
      <tr>
        <td><%= student.Name %></td>
        <td><%= student.Email %></td>
      </tr>
    <% end %>

For the app/views/jobs/index.html.erb

    <% @collection.all.each do |job| %>
      <tr>
        <td><%= job.id %></td>
        <td><%= job.exp %></td>
      </tr>
    <% end %>

Upvotes: 0

vladvel
vladvel

Reputation: 173

The ancestor and mixin answers are really good if you do want to DRY things up, but in my opinion you don't necessarily have to. The render json: bit belongs in the controller action. You expect your action to render something in both controllers.

The query is something that might change in the same way over time I guess Technology.all, so you could abstract that somewhere, but again, it might be a good idea to wait and see if that is the case.

If you do go with mixins or a base class, you will couple the two controllers, which might be fine. But again, you might just want to take this decision later on.

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Answering an implicit question stated in comments: there is an ability to borrow the method with UnboundMethod#bind if and only the object calling bind is_a?() instance of the class the method belongs to:

def index
  A::TechnologiesController.instance_method(:index).bind(self).()
end

but this is neither idiomatic nor readable. One should either use a mixin:

module Mixins::TechnologiesController
  def index
    render json: Technology.all
  end
end

class A::TechnologiesController < ApplicationController
  include Mixins::TechnologiesController
end
class B::TechnologiesController < ApplicationController
  include Mixins::TechnologiesController
end

or a common ancestor:

class Base::TechnologiesController < ApplicationController
  def index
    render json: Technology.all
  end
end
class A::TechnologiesController < Base::TechnologiesController; end
class B::TechnologiesController < Base::TechnologiesController; end

Bonus track: in Rails one might use Module#delegate monkeypatch.


Bonus track #2: the implementation on procs stored as constants:

class A::TechnologiesController < ApplicationController
  INDEX = -> { render json: Technology.all }
  def index
    INDEX.()
  end
end

class B::TechnologiesController < ApplicationController
  def index
    A::INDEX.()
  end
end

Upvotes: 3

Shimu
Shimu

Reputation: 1147

This is a good example of a mixin.
With this you can DRY up your code and you don't have to call the method of another controller.

Here is the module:

module CommonInterface
  def render_technology
    render :json, Technology.all
  end
end

And this would be your controller

class B::TechnologiesController < ApplicationController
  include CommonInterface
  def index
    render_technology
  end
end

Upvotes: 1

Related Questions