Skip to content

niko/is_a_collection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

A small gem that adds #find and #all to a class to keep track of it's instances.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages