is_a_collection 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,50 @@
1
+ require 'set'
2
+
3
+ class Object # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
4
+ def meta_def name, &blk
5
+ (class << self; self; end).instance_eval { define_method name, &blk }
6
+ end
7
+ end
8
+
1
9
  class Class
2
10
 
3
- def is_a_collection(id_method=:id)
11
+ def is_a_collection(id_method=:id, opts={})
4
12
  id_method = id_method.to_sym
13
+ indices = [opts[:index], opts[:indices]].flatten.compact
5
14
 
6
15
  include IsACollection::InstanceMethods
7
16
  extend IsACollection::ClassMethods
8
17
 
9
18
  define_method :add_to_collection do
10
- self.class.__collection[__send__ id_method] = self
19
+ primary_key = __send__(id_method)
20
+ raise IsACollection::DuplicateKey if self.class.__collection[primary_key]
21
+ self.class.__collection[primary_key] = self
22
+
23
+ indices.each do |index|
24
+ self.class.__indices[index][__send__ index].add(self)
25
+ end
11
26
  end
27
+
12
28
  define_method :remove_from_collection do
13
29
  self.class.__collection.delete(__send__ id_method)
30
+
31
+ indices.each do |index|
32
+ self.class.__indices[index][__send__ index].delete(self)
33
+ end
34
+ end
35
+
36
+ indices.each do |index|
37
+ meta_def :"find_by_#{index}" do |genre|
38
+ __indices[index][genre]
39
+ end
14
40
  end
41
+
15
42
  end
16
43
  end
17
44
 
18
45
  module IsACollection
46
+ class DuplicateKey < StandardError; end
47
+
19
48
  module InstanceMethods
20
49
  def initialize(*args, &block)
21
50
  super
@@ -37,6 +66,12 @@ module IsACollection
37
66
  def find(identifier)
38
67
  __collection[identifier]
39
68
  end
69
+ def clear_collection_indices
70
+ @__collection = {}
71
+ @__indices = Hash.new{ |hash,key| hash[key] = Hash.new{ |hash1,key1| hash1[key1] = Set.new } }
72
+ end
73
+ def __indices
74
+ @__indices ||= Hash.new{ |hash,key| hash[key] = Hash.new{ |hash1,key1| hash1[key1] = Set.new } }
75
+ end
40
76
  end
41
77
  end
42
-
@@ -28,6 +28,19 @@ class C
28
28
  is_a_collection
29
29
  end
30
30
 
31
+ class Song
32
+ attr_reader :title, :genre
33
+ is_a_collection :title, :index => :genre
34
+
35
+ def initialize(title, genre)
36
+ @title, @genre = title, genre
37
+ add_to_collection
38
+ end
39
+ def destroy
40
+ remove_from_collection
41
+ end
42
+ end
43
+
31
44
  describe "is_a_collection" do
32
45
  describe "#all" do
33
46
  describe "without any instances" do
@@ -43,6 +56,12 @@ describe "is_a_collection" do
43
56
  end
44
57
  end
45
58
  end
59
+ describe "on primary key collision" do
60
+ it "raise an error" do
61
+ b1 = B.new('a key')
62
+ lambda{ B.new('a key') }.should raise_error(IsACollection::DuplicateKey)
63
+ end
64
+ end
46
65
  describe "#find" do
47
66
  it "should return the instance by default identifier (#object_id)" do
48
67
  a = A.new
@@ -65,4 +84,30 @@ describe "is_a_collection" do
65
84
  B.find('doomed').should be_nil
66
85
  end
67
86
  end
87
+ describe "indices" do
88
+ after(:each) do
89
+ Song.clear_collection_indices
90
+ end
91
+ describe "on instance creation" do
92
+ it "adds instances to the index" do
93
+ s1 = Song.new 'some title', 'Rock'
94
+ s2 = Song.new 'some other title', 'Rock'
95
+ Song.find_by_genre('Rock').should include(s1)
96
+ Song.find_by_genre('Rock').should include(s2)
97
+ end
98
+ it "doesn't add instances to other indices" do
99
+ s1 = Song.new 'some title', 'Rock'
100
+ s2 = Song.new 'some other title', 'Pop'
101
+ Song.find_by_genre('Rock').should_not include(s2)
102
+ end
103
+ end
104
+ describe "on instance destruction" do
105
+ it "removes the instance from the index" do
106
+ s = Song.new 'some other title', 'Rock'
107
+ Song.find_by_genre('Rock').should include(s)
108
+ s.destroy
109
+ Song.find_by_genre('Rock').should_not include(s)
110
+ end
111
+ end
112
+ end
68
113
  end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: is_a_collection
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 5
9
- version: 0.0.5
4
+ prerelease:
5
+ version: 0.1.0
10
6
  platform: ruby
11
7
  authors:
12
8
  - Niko Dittmann
@@ -14,8 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-12-14 00:00:00 +01:00
18
- default_executable:
13
+ date: 2011-07-01 00:00:00 Z
19
14
  dependencies:
20
15
  - !ruby/object:Gem::Dependency
21
16
  name: rspec
@@ -25,8 +20,6 @@ dependencies:
25
20
  requirements:
26
21
  - - ">="
27
22
  - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
23
  version: "0"
31
24
  type: :development
32
25
  version_requirements: *id001
@@ -41,7 +34,6 @@ extra_rdoc_files: []
41
34
  files:
42
35
  - lib/is_a_collection.rb
43
36
  - spec/is_a_collection_spec.rb
44
- has_rdoc: true
45
37
  homepage: http://github.com/niko/is_a_collection
46
38
  licenses: []
47
39
 
@@ -55,21 +47,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
47
  requirements:
56
48
  - - ">="
57
49
  - !ruby/object:Gem::Version
58
- segments:
59
- - 0
60
50
  version: "0"
61
51
  required_rubygems_version: !ruby/object:Gem::Requirement
62
52
  none: false
63
53
  requirements:
64
54
  - - ">="
65
55
  - !ruby/object:Gem::Version
66
- segments:
67
- - 0
68
56
  version: "0"
69
57
  requirements: []
70
58
 
71
59
  rubyforge_project: nowarning
72
- rubygems_version: 1.3.7
60
+ rubygems_version: 1.7.2
73
61
  signing_key:
74
62
  specification_version: 3
75
63
  summary: "A small gem that adds #find, #all and #destroy to a class to keep track of its instances."