is_a_collection¶ ↑
API breaking change¶ ↑
As of version 0.1.0 an error gets raise on primary key collision. For the reasons read the new Indices section below. As of version 0.1.1 the error is IsACollection::DuplicateKeyError, not IsACollection::DuplicateKey any more.
Basics¶ ↑
A small gem that adds #find and #all to a class to keep track of its instances.
module AE::Assert alias :should :assert end require 'is_a_collection' class A is_a_collection def initialize(*args) super end def id object_id end end a = A.new A.all.assert == [a] b = A.new A.all.assert == [a,b] A.find(a.id) a.destroy A.all.should == [b]
As you can see: By default is_a_collection looks for an #id method and uses that as primary key for its index. But you can tell what method to use, #name in this example.
class B attr_reader :name is_a_collection :name def initialize(name) @name = name add_to_collection end def destroy remove_from_collection end end a = B.new('foo') B.all.assert == [a] b = B.new('bar') B.all.assert == [a,b] B.find('foo') a.destroy B.all.assert == [b]
is_a_collection hooks itself into #initialize and #destroy and calls its own #add_to_collection respectivly #remove_from_collection there. This means, if you define #initialize or #destroy in your class (and not in one of its superclasses), you either have to explicitly call #super (to give is_a_collection a chance to do its work) or call #add_to_collection and #remove_from_collection yourself.
Note to calling #super rubies other than 1.9.1 or 1.9.2.¶ ↑
If Object is the superclass of the Class you’re using is_a_collection in, be careful that Object#initialize has the arity 0. So make sure you call super without any arguments. As super implicitly passes all arguments, you may need to call it like this: super *[]. Directly using #add_to_collection may be more… intuitive.
See www.ruby-forum.com/topic/330466 and redmine.ruby-lang.org/issues/show/2451 for more information.
Indices¶ ↑
As of the latest version is_a_collection supports arbitrary indices.
class Song attr_reader :title, :genre is_a_collection :title, :index => :genre def initialize(title, genre) @title, @genre = title, genre add_to_collection end def destroy remove_from_collection end end
With this genre index defined, all instances of Song get added to a reverse lookup index. You can get songs with the #find_by_genre instance method.
s1 = Song.new 'foobar', 'Rock' s2 = Song.new 'foobaz', 'Rock' s3 = Song.new 'fuu', 'Pop' Song.find_by_genre('Rock').to_a.assert == [s1,s2]
The breaking change¶ ↑
Before the index capabilities got added, a new instance with a primary key that was already present would just overwrite the other instance in the index. Now with the new index stuff, an instance can be references by the primary index and one or more other indices. If the primary index entry would be overridden as before, the other indices would still reference the first instance, but it would be hard to delete it afterwards. As this would lead to an object leak I decided the best solution is to raise an error on duplicate key entry.
It doesn’t work?¶ ↑
Did you call super in #initialize? Yes? I’m glad to help: Issues
QED¶ ↑
You can run this documentation with QED