is_a_collection 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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."