Reputation: 37
In a Rails project because of some history reasons, a piece of codes in Tiger
and Elephant
are same.
I don't like the repetition, but if I create a new method in AnimalController
class and move these codes into it, I can't return
the walk
or running
method from the new method.
I think return from another method may not a good practice, but I really hate the duplications, can someone help me refactoring?
class AnimalController
# I want create a new method here
#def all_in
#end
end
class TigerController < AnimalController
def running # This is an Action
some_different_codes...
if arm.blank?
render_not_found
return # <- how can I return `running` from the new method?
end
if lag.nil?
invalid_id
return # <-
end
some_different_codes...
end
end
class ElephantController < AnimalController
def walk # This is an Action
some_different_codes...
if arm.blank?
render_not_found
return
end
if lag.nil?
invalid_id
return
end
some_different_codes...
end
end
Upvotes: 2
Views: 2214
Reputation: 76784
Might be a good turn to look up about callbacks and superclassing:
Callbacks basically allow you to run login in code based on the response of another function.
Since we have them all over Rails, not many people actually appreciate what they do. If you've ever implemented them in JS, you'll know all about them!
-
Superclassing is where you inherit from an existing class - allowing you to use (& extend) the functions the class has. This is where the super command comes from.
I'd do this (looks like Sergio
's answer actually, which is reassuring):
#app/controllers/animals_controller.rb
class AnimalsController < ApplicationController
private
def all_in?
if invalid_id
return false
end
if lag.nil?
invalid_id
return false
end
true #-> ruby automatically returns the last line
end
end
The above is what you'd call a callback -- you'll be able to call all_in
(in an instance) and receive a response
of either true
or false
.
This will give you the ability to call that method (if you're superclassing
, the method will be available down the chain:
#app/controllers/elephants_controller.rb
class ElephantController < AnimalController
def walk # This is an Action
some_different_codes...
if all_in?
some_different_codes...
end
end
end
Now, there's something you must be aware of.
This type of behavior should not be put in the controller of your app - it should be in your models:
#app/models/animal.rb
class Animal < ActiveRecord::Base
def walk
end
end
#app/models/animals/elephant.rb
class Elephant < Animal
def walk
super ...
end
end
#app/models/animals/tiger.rb
class Tiger < Animal
end
The above is known as STI (Single Table Inheritance). It's basically a way to subclass against a main model with other "dependent" models & their methods.
Since ruby is object orientated, you should be calling object-specific methods on the objects themselves;
#config/routes.rb
resources :tigers, :elephants, controller: :animals
#url.com/tigers/
#url.com/elephants/
#app/controllers/animals_controller.rb
class AnimalsController < ApplicationController
def show
@tiger = Tiger.find params[:id]
@tiger.walk
end
end
This is more or less epitomized with a state machine
:
class Vehicle
attr_accessor :seatbelt_on, :time_used, :auto_shop_busy
state_machine :state, :initial => :parked do
before_transition :parked => any - :parked, :do => :put_on_seatbelt
after_transition :on => :crash, :do => :tow
after_transition :on => :repair, :do => :fix
after_transition any => :parked do |vehicle, transition|
vehicle.seatbelt_on = false
end
...
Upvotes: 0
Reputation: 230531
A method can't make its caller return, if it doesn't want to. So this new method will perform checks (while rendering something) and it will return result of the checks. Caller method analyzes the return value and decides what to do. Something along these lines:
class AnimalController
def all_in
if invalid_id
render_not_found
return false
end
if lag.nil?
invalid_id
return false
end
true
end
end
class TigerController < AnimalController
def running # This is an Action
some_different_codes...
return unless all_in
some_different_codes...
end
end
Upvotes: 2