Skip to content

Instantly share code, notes, and snippets.

@vjt
Created January 28, 2009 11:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vjt/53913 to your computer and use it in GitHub Desktop.
Save vjt/53913 to your computer and use it in GitHub Desktop.
#
# Implements the ArrayWithFinders class, that adds
# finders with names similar to ActiveRecord's ones:
#
# * find_by_XYZ -> calls the `XYZ` method on the array items,
# returing the first one on which the method
# didn't return nil/false
# * find_all_by_XYZ -> calls the `XYZ` method on the array items,
# returing all the ones on whose the method
# didn't return nil/false
# * remove_by_XXX -> removes *all* the elements whose XXX method
# returns true. Caveat: this finder is aliased
# as remove_all_by_XXX
#
# (C) 2008 ~ Marcello Barnaba <vjt@openssl.it>.
# Released under the terms of the Ruby License.
#
class ArrayWithFinders < Array
def method_missing(meth, *args, &block)
if meth.to_s =~ /^(find|remove)_(all_)?by_(\w+)/
if args.size != 1
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
end
if $1 == 'find'
meth = $2.nil? ? :find : :select
else
meth = :reject!
end
attr = $3.to_sym
key = args.first
self.send(meth) { |b| b.send(attr) == key }
else
super(meth, *args, &block)
end
end
end
if $0 == __FILE__
require 'test/unit'
class ArrayTest < Test::Unit::TestCase
def test_finders
populate
index = rand Size
assert_equal(Item.new(index), @ary.find_by_foo(index))
end
def test_exception
populate
assert_raise(NoMethodError) { @ary.find_by_bar(42) }
assert_raise(ArgumentError) { @ary.find_by_foo }
assert_raise(ArgumentError) { @ary.find_by_foo(1,2,3) }
end
private
Item = Struct.new(:foo)
Size = 10
def populate
@ary = ArrayWithFinders.new((0..Size).map { |i| Item.new(i) })
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment