Refactor code in api::controller

In my api::controller i have few methods which follow the same type of
structure .
like :-

def index
render json:{:status => false, :message => ‘qw’} if
params[:type].nil?
@stats =
ChildStat.get_child_stat(@child_profile.id,@profile.id,params[:type])
render json:{:status => false, :message => ‘Child stats not found’}
if
@stats.empty?
end

def child_vaccines
render json:{:status => false, :message => "Please specify type:
weekly, monthly in parameters "} if params[:type].nil?
@vaccines =
ChildStat.get_child_vaccine(@child_profile.id,@parent_profile.id,params[:type])
render json:{:status => false, :message => "Child Vaccines not found
"}
if @vaccines.empty?
end

def child_meals
render json:{:status => false, :message => "Please specify type:
weekly, monthly in parameters "} if params[:type].nil?
@meals =
ChildStat.get_child_meals(@child_profile.id,@profile.id,params[:type])
render json:{:status => false, :message => "Child meals not found "}
if
@meals.empty?
end

So the above three methods follow the same structure . I am thinking to
refactor or else write them in more compact form .
I needs your help or suggestion for this .

thanks.

On Mon, Feb 3, 2014 at 5:52 AM, sanjee [email protected] wrote:

In my api::controller i have few methods which follow the same type of
structure .

I am thinking to refactor or else write them in more compact form .
I needs your help or suggestion for this .

If they’re really that few and that short, I wouldn’t bother. But
assuming they’re really more numerous or much longer, and to show the
general principle, here’s how you would do that:

First, notice exactly what changes about them, versus what stays the
same. The first line of each is almost identical; the only variation
is the message (more on that later). The second line varies in what
var is set, what method is called on ChildStat, and that in the first
case, that method takes two parameters, while the others take three,
inserting the second. There’s also that that second parameter is
different between the two. The third line varies in the message
rendered if the var set in the second line is empty (and of course
what var that is).

So, substituting VAR#, where # is an increasing number, for each thing
that varies, we have:

render json:{:status => false, :message => VAR1} if params[:type].nil?
@VAR2 = ChildStat.VAR3(VAR4)
render json:{:status => false, :message => ‘VAR5’} if @VAR2.empty?

“Wait a second, VAR4 is just one thing, and those methods each took
multiple arguments!”, I hear some of you say. That’s right… but you
can substitute an array for a sequence of things. To demonstrate,
let’s define a method that simply adds two numbers:

def add(a, b)
a + b
end

add(1, 2) # => 3

So far so good, just basic Ruby method definition and calling. But
now let’s try calling it with an array:

arr = [1, 2]
add(arr) # => ArgumentError: wrong number of arguments (1 for 2)

Oops! What happened? We’re passing it one thing. We need to find a
way to tell it to split this thing apart into its subpart. The *
character does exactly that, using the array just as if you had
literally typed its elements:

add(*arr) # => 3

And yes, if arr has the wrong number of things in it, add will complain
again.

arr = [1]
add(*arr) # => ArgumentError: wrong number of arguments (1 for 2)
arr = [1, 2, 3]
add(*arr) # => ArgumentError: wrong number of arguments (3 for 2)

The next most obvious shocker is VAR3. “Why, surely we can’t just
pass something in and tack it onto an object, expecting it to turn
into a method call!”

Well, no. But we can do something very close: send it a message.
“What, you want to set up some kind of interprocess communication
system, create a process, or at least a thread (and we all know how
well standard Ruby threads!), and send it a message? That sounds like
horrible overkill!”

Yes, you’re right, that would be. But that’s not what I mean. Cast
your memory back to the 1960s, when Alan Kay was inventing Object
Orientation. He thought of an OO system as composed of objects
“sending messages” to each others. Indeed, many OO languages (and
programmers) speak of “sending messages” when others would say
“calling methods”. So what I mean is to give it some kind of “thing”
that says what to do. So how do we do that in Ruby?

Enter send. These methods take a symbol, look up the method on their
object whose name is the symbol, and call that method, feeding it the
rest of their arguments. Suppose we have this class:

class Foo
def foo
puts ‘foo’
end
def bar(howmany)
puts ‘bar’ * howmany
end
private
def baz(rows,cols)
rows.times do
puts ‘baz’ * cols
end
end
end

Then we can make a Foo (by doing f = Foo.new), and send f symbols:

irb> f.send :foo
foo
irb> f.send :bar
ArgumentError: wrong number of arguments (0 for 1)
irb> f.send :bar, 3
barbarbar
irb> f.send :baz, 3, 4
bazbazbazbaz
bazbazbazbaz
bazbazbazbaz

Also worth of attention is its pickier but lesser-known brother,
public_send, which is much safer to use in most circumstances. This
one will only call public methods.

irb> f.public_send :foo
foo
irb> f.public_send :bar
ArgumentError: wrong number of arguments (0 for 1)
irb> f.public_send :bar, 3
barbarbar
irb> f.public_send :baz, 3, 4
NoMethodError: private method `baz’ called for #Foo:0x007f9d4c886260

The main remaining challenge is the @-var, VAR2. We could probably
hand the method some var to use, but frankly I don’t think it would be
worth the bother. The important thing is that when we’re all done
with our method call and whatever else we do with it, the right @-var
is set to the right value. So make it the return from our master
consolidated method, and assign it to the var you need, using an
ordinary temp var inside.

So, we have in the end:

def check_child_stats(nil_msg, method_symbol, method_args, empty_msg)
render json:{:status => false, :message => nil_msg} if
params[:type].nil?
results = ChildStat.send(method, *method_args)
render json:{:status => false, :message => empty_msg} if
results.empty?
results
end

def index
@stats = check_child_stats(‘qw’, :get_child_stat,
[@child_profile.id, @profile.id,params[:type]],
‘Child stats not found’)
end

def child_vaccines
@vaccines = check_child_stats("Please specify type: weekly,
monthly in parameters ",
:get_child_vaccine, [@child_profile.id,@parent_profile.id,
params[:type]],
‘Child Vaccines not found’)
end

def child_meals
@meals = check_child_stats("Please specify type: weekly, monthly
in parameters ",
:get_child_meals, [@child_profile.id,@profile.id,
params[:type]],
‘Child meals not found’)
end

Was that the sort of thing you were looking for?

If you find that many positional arguments hard to keep straight, you
can even name them (using Ruby 2+), or make them hash options.

Lastly, two of them have a message that is the same. You could make
them reference the same string, stored in the controller or some other
appropriate class.


Dave A., the T. Rex of Codosaurus LLC (codosaur.us),
freelance software developer, and creator of these sites:
PullRequestRoulette.com, blog.codosaur.us, & Dare2XL.com.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs