ActiveRecord suggestion: Object.in? to complement Enumerable.include?

Hello,

This is a joint patch from rubyduo (at gmail) and myself. Some of the following is extracted from our discussion on Lighthouse (https:// rails.lighthouseapp.com/projects/8994/tickets/6321-add-improved- version-of-enumerableinclude). It was suggested we bring it up here to try and garner some support for the contribution rather than let the ticket languish in Lighthouse.

Sometimes you want to ask a question like

  "Is x 1 or 2?"

Normally you write this as:

  [1,2].include?(x)

which seems backwards to us.

We have created a patch to ActiveSupport, with tests, that allows you instead to write:

  x.in?([1,2])

Thanks to duck typing, this extension works with any type that implements include?:

  # Array   a = [1,2,3]   3.in?(a) # => true   4.in?(a) # => false

  # Hash   h = { "a" => 100, "b" => 200 }   "a".in?(h) # => true   "z".in?(h) # => false

  # String   "lo".in?("hello") # => true   "ol".in?("hello") # => false   ?h.in?("hello") # => true

  # Range   25.in?(1..50) # => true   75.in?(1..50) # => false

  # Set   require 'set'   s = Set.new([1,2])   1.in?(s) # => true   3.in?(s) # => false

  # Even Module   module A   end   class B     include A   end   class C < B   end   A.in?(B) # => true   A.in?(C) # => true   A.in?(A) # => false

The patch is based on rubyduo's proposal and on my in_enumerable gem, which has been in use since late 2009 and is solid. The patch includes a number of tests.

In Lighthouse we also discussed a couple of other options but decided against them. Here's a quick summary, and you can read more about them in the ticket:

1. Rubyduo's original suggestion was that in? would take a list of parameters instead of a single parameter. We agreed in the end that a single parameter is better because of the added flexibility of working with any type that implements include?.

2. We discussed having the best of both worlds: allowing a single parameter or a list of parameters. But in this case, that would lead to ambiguity and hard-to-track-down bugs so we agreed it's not the right course.

I would be happy to deprecate in_enumerable if this change is put into ActiveSupport. See the following if you want more info on in_enumerable: - Original announcement: http://ilikestuffblog.com/2009/12/24/now-you-can-use-obj-inarray-instead-of-array-includeobj/ - Source code: https://github.com/BMorearty/in_enumerable

Thanks so much. Any ideas or feedback would be appreciated.

Brian Morearty and rubyduo

P.S. In an odd bit of timing, rubyduo and I didn't know each other before but the stars aligned and we had the same idea *on the same day* to suggest this patch to ActiveRecord.

That would be a useful addition, the method was in Facets library for ages I think https://github.com/rubyworks/facets/blame/master/lib/core/facets/kernel/in.rb

But I guess to get it into the ActiveSupport you would also need to have (examples at least) of using it throughout Rails code base.

Anyway, I'm +1 on it.

Hey,

FWIW, there was a pull request for something similar fairly recently, but it didn't really go anywhere: https://github.com/rails/rails/pull/138

Jon

Hi Jon,

Thanks for pointing me to the recent pull request. That proposal does indeed have some similarity of this one but there are a couple of differences:

1. The other pull request uses the name Object#avoids?, which is awkward. I agree with DHH's comment on that one: "If you're going for the positive, I much prefer Object#in? I don't like included_in and excluded_from at all." (Um, was he reading my mind?)

2. That pull request started out being primarily a negative test, not a positive one. Hence the name "avoids?". When it was suggested that the positive might be better, the name "matches?" was proposed. Again, "in?" seems clearer.

I'll post a comment on that thread just in case. :slight_smile:

Brian