开发者

Refactoring respond_to? call in if-elsif-else condition

开发者 https://www.devze.com 2023-01-08 11:55 出处:网络
I have the following method and want to make it more readable: def value_format(value) if value.respond_to? :to_actor

I have the following method and want to make it more readable:

def value_format(value)
  if value.respond_to? :to_actor
    value.to_actor
  elsif value.respond_to? :to_subject
    value.to_subject
  elsif value.respond_to? :to_json
    value.to_json
  elsif value.respond_to? :to_hash
    value.to_hash
  else
    value.inspect
  end
end

This is my solution. What do you think?

def value_format(value)
  me开发者_开发技巧thods = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
  value.send(methods.find_all { |m| m if value.respond_to? m }.first)
end


Your solution looks fine, but you might as well use find instead of find_all:

METHODS = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
def value_format(value)
  value.send(METHODS.find { |m| value.respond_to? m })
end

Using a constant has the advantage of not creating a new array every time value_format is ran.


Seems there's a pretty simple optimization to your solution:

def value_format(value)
  methods = [:to_actor, :to_subject, :to_json, :to_hash]
  value.send(methods.find(:inspect) { |m| value.respond_to? m })
end


The facets gem provides an elegant solution (I think) to this problem. It combines the two steps of checking if an object responds to a method and actually calling that method into a single step.

So your example could be rewritten as this:

require 'facets/kernel/respond'

def value_format(v)
  v.respond.to_actor || v.respond.to_subject || v.respond.to_json || v.respond.to_hash || v.respond.inspect
end

Note that this method only works if it is safe to assume that none of these methods are going to return nil or false (because respond returns nil if the object doesn't respond, that is what allows us to chain it together with a bunch of ors).

Since all of the methods you listed should return strings, I believe this approach would work fine in your example.

Documentation:

  # Like #respond_to? but returns the result of the call
  # if it does indeed respond.
  #
  #   class RespondExample
  #     def f; "f"; end
  #   end
  #
  #   x = RespondExample.new
  #   x.respond(:f)  #=> "f"
  #   x.respond(:g)  #=> nil
  #
  # or
  #
  #   x.respond.f   #=> "f"
  #   x.respond.g   #=> nil
0

精彩评论

暂无评论...
验证码 换一张
取 消