开发者

How to detect an array- or set-like value while avoiding type checks

开发者 https://www.devze.com 2023-03-22 00:28 出处:网络
I have a method which accepts an argument which can be an Array/Set-like object, or a Hash. The gist of the method is something like:

I have a method which accepts an argument which can be an Array/Set-like object, or a Hash. The gist of the method is something like:

def find(query = {})
  if Array === query or Set === query
    query = {:_id => {'$in' => query.to_a}}
  end
  mongo_collection.find(query)
end

The method will accept a set of ID objects and turn it into a hash condition for MongoDB.

Two problems with above code:

  1. It will fail if 'set' is not required from standard library. I don't want to require the dependency just to perform a check.
  2. I don't want to do strict type comparisons. I want to accept any array- or set-like value and cast it to an array of values with to_a.

How would you perform this check? Some considerations to have in mind:

  1. I could check for to_ary method, but Set doesn't respond to to_ary. Objects that implement this method should fundamentally be arrays, and I agree that Set isn't fundamentally an array. See Consequences of implementing to_int and to_str in Ruby
  2. I can't check for to_a since Hash responds to it
  3. Methods that are common to Array and Set, but not to Hash are:

    [:&, :+, :-, :<<, :collect!, :flatten!, :map!, :|]
    

I decided to go with something like this:

开发者_如何转开发
query = {:_id => {'$in' => query.to_a}} if query.respond_to? :&

since intersection is likely an operator a set-like object would have. But I'm not sure about this.


Here's my take:

if not Hash === query and query.respond_to? :to_a

I'm just checking for to_a, which is the only method I'm interested in, but also ensuring that it's not a Hash object. I'm using strict type checking for Hash, but only because this is the least likely object to be passed as a completely separate class that's fundamentally a hash.


How about trying to find out if the query is Hash like?

def find(query = {})
  query = {:_id => {'$in' => query.to_a}} unless query.respond_to?(:has_key?)
  mongo_collection.find(query)
end

It is reasonable to expect that the object will be a Hash or Hash like if it responds to has_key?.


Checking to see if Set is defined would solve your first issue. For the second, you could possibly check the ancestors of the class of query to see if Array is in them, but that probably won't catch all "array-like" objects. I probably wouldn't check for the existence of methods to test for arrayness, as you are testing names, not behavior. Arel in particular responds to (or did before it was deprecated) &, but this type of object wouldn't work like you wanted it to.


Personally I'm thinking...

def find(query = {})      
  mongo_collection.find(query_formatter(query))
end

def query_formatter(query)
  if query.respond_to?(:to_a) && !query.kind_of?(Hash)
    {:_id => {'$in' => query.to_a}}
  else
    query
  end
end
0

精彩评论

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

关注公众号