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.
- data/lib/is_a_collection.rb +38 -3
- data/spec/is_a_collection_spec.rb +45 -0
- metadata +4 -16
data/lib/is_a_collection.rb
CHANGED
@@ -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
|
-
|
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:
|
5
|
-
|
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:
|
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.
|
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."
|