Reputation: 896
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
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
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
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
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