repository 0.0.1
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/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +76 -0
- data/Rakefile +11 -0
- data/lib/repository.rb +78 -0
- data/lib/repository/criterion.rb +192 -0
- data/lib/repository/repository.rb +126 -0
- data/lib/repository/stash.rb +59 -0
- data/lib/repository/stash_storage.rb +34 -0
- data/lib/repository/storage.rb +78 -0
- data/repository.gemspec +22 -0
- data/spec/domain.rb +21 -0
- data/spec/helper.rb +51 -0
- data/spec/lib/criterion_conjunction_spec.rb +58 -0
- data/spec/lib/criterion_contains_spec.rb +48 -0
- data/spec/lib/criterion_equals_spec.rb +79 -0
- data/spec/lib/criterion_factory_spec.rb +69 -0
- data/spec/lib/criterion_join_spec.rb +42 -0
- data/spec/lib/criterion_key_spec.rb +64 -0
- data/spec/lib/criterion_refers_to_spec.rb +12 -0
- data/spec/lib/criterion_spec.rb +120 -0
- data/spec/lib/repository_spec.rb +32 -0
- data/spec/lib/stash_spec.rb +185 -0
- metadata +135 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Criterion::Factory do
|
6
|
+
it "builds with equals" do
|
7
|
+
criterion = Criterion::Factory.equals('active', true)
|
8
|
+
criterion.should == Criterion::Equals.new(:subject => 'active', :value => true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "builds with key and one argument" do
|
12
|
+
criterion = Criterion::Factory.key(99)
|
13
|
+
criterion.should == Criterion::Key.new(:subject => 'key', :value => 99)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "builds with key and two arguments" do
|
17
|
+
criterion = Criterion::Factory.key('address_id', 99)
|
18
|
+
criterion.should == Criterion::Key.new(:subject => 'address_id', :value => 99)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "converts a symbol into a string for the subject field name" do
|
22
|
+
criterion = Criterion::Factory.key(:address_id, 99)
|
23
|
+
criterion.should == Criterion::Key.new(:subject => 'address_id', :value => 99)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
describe "individual criteria" do
|
28
|
+
before do
|
29
|
+
@repository = Repository.new(User)
|
30
|
+
end
|
31
|
+
|
32
|
+
def crit(&block)
|
33
|
+
@repository.criterion_from &block
|
34
|
+
end
|
35
|
+
|
36
|
+
it "equals" do
|
37
|
+
crit do |user|
|
38
|
+
user.equals('name', 'alice')
|
39
|
+
end.should == Criterion::Equals.new(:subject => "name", :value => "alice")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "key" do
|
43
|
+
pending
|
44
|
+
crit do |user|
|
45
|
+
user.key(99)
|
46
|
+
end.should == Criterion::Key.new(:value => 99)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "contains" do
|
50
|
+
crit do |user|
|
51
|
+
user.contains("name", "foo")
|
52
|
+
end.should == Criterion::Contains.new(:subject => "name", :value => "foo")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "joins"
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "conjunctions" do
|
59
|
+
|
60
|
+
it "and"
|
61
|
+
|
62
|
+
it "or"
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Criterion::Join do
|
6
|
+
before do
|
7
|
+
[::Repository[User], ::Repository[Address]].each do |repository|
|
8
|
+
repository.clear
|
9
|
+
repository.storage.clear
|
10
|
+
end
|
11
|
+
|
12
|
+
::Repository[Address].size.should == 0
|
13
|
+
::Repository[Address].storage.size.should == 0
|
14
|
+
|
15
|
+
@alice = User.new(id: 1, :name => "Alice")
|
16
|
+
@bob = User.new(id: 2, :name => "Bob")
|
17
|
+
@charlie = User.new(id: 3, :name => "Charlie")
|
18
|
+
::Repository[User].store [@alice, @bob, @charlie]
|
19
|
+
|
20
|
+
@chez_alice = Address.new(id: 111, :user_id => @alice.id, :street => "12 Apple St.")
|
21
|
+
@chez_alice2 = Address.new(id: 222, :user_id => @alice.id, :street => "8 Artichoke Ave.")
|
22
|
+
@chez_bob = Address.new(id: 333, :user_id => @bob.id, :street => "28 Banana St.")
|
23
|
+
@chez_charlie = Address.new(id: 444, :user_id => @charlie.id, :street => "42 Cherry St.")
|
24
|
+
::Repository[Address].store [@chez_alice, @chez_alice2, @chez_bob, @chez_charlie]
|
25
|
+
|
26
|
+
@nested_criterion = Criterion::Contains.new(:subject => "name", :value => "a")
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'the nested criterion' do
|
30
|
+
it "should find only 2 out of the 3 users" do
|
31
|
+
::Repository[User].search(@nested_criterion).should include_only(@alice, @charlie)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can be used as the target of an Equals criterion" do
|
36
|
+
straight_id_criterion = Criterion::Equals.new(:subject => :user_id, :value => [@alice.id, @charlie.id])
|
37
|
+
addresses = ::Repository[Address].search(straight_id_criterion)
|
38
|
+
addresses.should == [@chez_alice, @chez_alice2, @chez_charlie]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Criterion do
|
6
|
+
before do
|
7
|
+
@alice = User.new(:name => "Alice", :id => 1)
|
8
|
+
@bob = User.new(:name => "Bob", :id => 2)
|
9
|
+
@charlie = User.new(:name => "Charlie", :id => 3)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Criterion::Key do
|
13
|
+
before do
|
14
|
+
@c = Criterion::Key.new(:value => "1")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "uses 'id' as its subject" do
|
18
|
+
@c.subject.should == "id"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has a swell descriptor" do
|
22
|
+
Criterion::Key.new({}).descriptor.should == "#"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "converts its value to an integer" do
|
26
|
+
@c.value.should == [1]
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#match' do
|
30
|
+
it "matches an id" do
|
31
|
+
@c.match?(@alice).should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "fails to match an id" do
|
35
|
+
@c.match?(@bob).should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "a set of ints" do
|
39
|
+
before do
|
40
|
+
@c = Criterion::Key.new(:value => [1,2])
|
41
|
+
end
|
42
|
+
|
43
|
+
it "matches" do
|
44
|
+
@c.should be_match @alice
|
45
|
+
@c.should be_match @bob
|
46
|
+
end
|
47
|
+
|
48
|
+
it "fails to match an int" do
|
49
|
+
@c.should_not be_match @charlie
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "a string value in the criterion" do
|
55
|
+
it "should match an int value in the object" do
|
56
|
+
@c = Criterion::Key.new(:value => "1")
|
57
|
+
@c.should be_match @alice
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Criterion do
|
6
|
+
before do
|
7
|
+
@alice = User.new(:name => "Alice", :id => 1)
|
8
|
+
@bob = User.new(:name => "Bob", :id => 2)
|
9
|
+
@charlie = User.new(:name => "Charlie", :id => 3)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Criterion do
|
6
|
+
before do
|
7
|
+
@alice = User.new(:name => "Alice", :id => 1)
|
8
|
+
@bob = User.new(:name => "Bob", :id => 2)
|
9
|
+
@charlie = User.new(:name => "Charlie", :id => 3)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has some fields" do
|
13
|
+
c = Criterion.new(:subject => "name", :descriptor => "is named", :value => "alex", :property_name => "nombre")
|
14
|
+
c.subject.should == "name"
|
15
|
+
c.descriptor.should == "is named"
|
16
|
+
c.value.should == "alex"
|
17
|
+
c.property_name.should == "nombre"
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should be equal if the instance variables are equivalent' do
|
21
|
+
c1 = Criterion.new(:subject => "name", :descriptor => "is named", :value => "alex", :property_name => "nombre")
|
22
|
+
c2 = Criterion.new(:subject => "name", :descriptor => "is named", :value => "alex", :property_name => "nombre")
|
23
|
+
c1.should == c2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has some default values" do
|
27
|
+
c = Criterion.new(:value => "123")
|
28
|
+
c.subject.should == "id"
|
29
|
+
c.descriptor.should == "id criterion"
|
30
|
+
c.value.should == "123"
|
31
|
+
c.property_name.should == "id"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "converts a missing value into 'nil'" do
|
35
|
+
c = Criterion.new({})
|
36
|
+
c.value.should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it "converts a blank value into 'nil'" do
|
40
|
+
c = Criterion.new({:value => ""})
|
41
|
+
c.value.should be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "uses the subject as the property name by default" do
|
45
|
+
c = Criterion.new(:subject => "name", :value => "alex")
|
46
|
+
c.property_name.should == "name"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "converts the subject to a string" do
|
50
|
+
c = Criterion.new(:subject => :name, :value => "alex")
|
51
|
+
c.subject.should == "name"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "makes a nice description" do
|
55
|
+
c = Criterion.new(:subject => "name", :descriptor => "is named", :value => "alex")
|
56
|
+
c.description.should == "is named alex"
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "operator overloading" do
|
60
|
+
before do
|
61
|
+
@c1 = Criterion::Equals.new(:subject => "name", :value => "alex")
|
62
|
+
@c2 = Criterion::Equals.new(:subject => "name", :value => "kane")
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'generates an And criterion when used with +' do
|
66
|
+
c3 = @c1 + @c2
|
67
|
+
c3.class.should == Criterion::And
|
68
|
+
c3.criteria.should == [@c1, @c2]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'generates an And criterion when used with &' do
|
72
|
+
c3 = @c1 & @c2
|
73
|
+
c3.class.should == Criterion::And
|
74
|
+
c3.criteria.should == [@c1, @c2]
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'generates an Or criterion when used with |' do
|
78
|
+
c3 = @c1 | @c2
|
79
|
+
c3.class.should == Criterion::Or
|
80
|
+
c3.criteria.should == [@c1, @c2]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#match' do
|
85
|
+
it "matches nothing (since it's a base class)" do
|
86
|
+
c = Criterion.new(:subject => "name", :value => "alex")
|
87
|
+
c.match?(User.new(:name => "alex")).should be_false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#described_value' do
|
92
|
+
it "describes its value" do
|
93
|
+
c = Criterion.new(:value => "123")
|
94
|
+
c.described_value.should == "123"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns 'any' if the criterion is not set" do
|
98
|
+
Criterion.new(:value => nil).described_value.should == 'any'
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns 'any' if the criterion is blank" do
|
102
|
+
Criterion.new(:value => "").described_value.should == 'any'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns 'none' if the criterion is zero" do
|
106
|
+
Criterion.new(:value => 0).described_value.should == 'none'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#find_in' do
|
111
|
+
it "calls the storage" do
|
112
|
+
c = Criterion.new(:value => nil)
|
113
|
+
storage = Object.new
|
114
|
+
storage.should_receive(:find).with(c)
|
115
|
+
c.find_in(storage)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe "Repository" do
|
6
|
+
before(:each) do
|
7
|
+
#FIXME
|
8
|
+
::Repository[User].clear
|
9
|
+
::Repository[User].storage.clear
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns the repository for a class via []" do
|
13
|
+
r1 = ::Repository[User]
|
14
|
+
r1.should be_a(Repository)
|
15
|
+
::Repository[User].should == r1
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can clear all" do
|
19
|
+
::Repository[User].store([User.new(id: 1), User.new(id: 2)])
|
20
|
+
::Repository[User].size.should == 2
|
21
|
+
::Repository.clear_all
|
22
|
+
::Repository[User].size.should == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can replace the repository for a given class" do
|
26
|
+
new_repository = Repository.new(User)
|
27
|
+
::Repository[User] = new_repository
|
28
|
+
::Repository[User].should == new_repository
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Repository
|
5
|
+
describe Stash do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@frank = User.new(:name => "Frankenstein", :id => 1)
|
9
|
+
@igor = User.new(:name => "Igor", :id => 2)
|
10
|
+
@castle = Address.new(:name => "find a brain", :creator => @frank, :id => 1)
|
11
|
+
@village = Address.new(:name => "buy lunch", :creator => @igor, :id => 2)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :stash
|
15
|
+
|
16
|
+
before do
|
17
|
+
@stash = Stash.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "is empty when created" do
|
21
|
+
stash.size.should == 0
|
22
|
+
end
|
23
|
+
|
24
|
+
it "fails if you try to put an object without a id" do
|
25
|
+
lambda { stash.put(User.new) }.should raise_error(Storage::Unidentified)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has a size after an object's been put into it" do
|
29
|
+
stash.put(@frank)
|
30
|
+
stash.size.should == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#clear" do
|
34
|
+
it "clears" do
|
35
|
+
stash.put(@frank)
|
36
|
+
stash.clear
|
37
|
+
stash.size.should == 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#get" do
|
42
|
+
it "returns an object by id" do
|
43
|
+
stash.put(@frank)
|
44
|
+
stash.get(@frank.id).should == @frank
|
45
|
+
stash[@frank.id].should == @frank
|
46
|
+
end
|
47
|
+
|
48
|
+
# it "returns several objects" do
|
49
|
+
# stash.put(@frank)
|
50
|
+
# stash.put(igor)
|
51
|
+
# stash.get([@frank.id, igor.id]).should == [@frank, igor]
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# it "returns them in the presented order" do
|
55
|
+
# stash.put(@frank)
|
56
|
+
# stash.put(igor)
|
57
|
+
# stash.get([igor.id, @frank.id]).should == [igor, @frank]
|
58
|
+
# end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#put" do
|
63
|
+
it "accepts an array of objects" do
|
64
|
+
stash.put([@frank, @igor])
|
65
|
+
stash.size.should == 2
|
66
|
+
stash[@frank.id].should == @frank
|
67
|
+
stash[@igor.id].should == @igor
|
68
|
+
end
|
69
|
+
#
|
70
|
+
# it "freaks if you try to put an object of the wrong type" do
|
71
|
+
# lambda {
|
72
|
+
# stash.put(brain)
|
73
|
+
# }.should raise_error(ArgumentError)
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# class Animal
|
77
|
+
# def id; 1; end
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# class Dog < Animal
|
81
|
+
# def id; 2; end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# class Cat < Animal
|
85
|
+
# def id; 3; end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# it "doesn't freak if you put an object that's a subclass of the stash's type" do
|
89
|
+
# lambda {
|
90
|
+
# stash = Stash.new(Animal)
|
91
|
+
# stash.put(Dog.new)
|
92
|
+
# stash.put(Cat.new)
|
93
|
+
# }.should_not raise_error(ArgumentError)
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# class Dummy
|
97
|
+
# attr_reader :entered
|
98
|
+
# def id
|
99
|
+
# 1
|
100
|
+
# end
|
101
|
+
# def entered_stash
|
102
|
+
# @entered = true
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# it "calls #entered_stash after put" do
|
107
|
+
# repo = Stash.new(Dummy)
|
108
|
+
# dummy = Dummy.new
|
109
|
+
# repo.put(dummy)
|
110
|
+
# dummy.entered.should be_true
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#find" do
|
116
|
+
before do
|
117
|
+
stash.put(@frank)
|
118
|
+
stash.put(@igor)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "finds an object by an array of keys" do
|
122
|
+
stash.find([@frank.id, @igor.id]).should include_only [@frank, @igor]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "finds an object by a criterion on id" do
|
126
|
+
stash.find(Criterion::Equals.new(:value => @frank.id)).should == [@frank]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "finds an object by a criterion on a value" do
|
130
|
+
stash.find(Criterion::Contains.new(:subject => "name", :value => "gor")).should == [@igor]
|
131
|
+
end
|
132
|
+
|
133
|
+
# it "finds a bunch of objects and puts them immediately" do
|
134
|
+
# stash.find([@frank.id, igor.id]).should == [@frank, igor]
|
135
|
+
# stash.size.should == 2
|
136
|
+
# stash[@frank.id].should == @frank
|
137
|
+
# stash[igor.id].should == igor
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# it "returns them in the presented order" do
|
141
|
+
# stash.find([@frank.id, igor.id]).should == [@frank, igor]
|
142
|
+
# stash.find([igor.id, @frank.id]).should == [igor, @frank]
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# it "works ok even if there are duplicate keys" do
|
146
|
+
# stash.find([@frank.id, igor.id, @frank.id, @frank.id, igor.id]).should == [@frank, igor, @frank, @frank, igor]
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
end
|
150
|
+
#
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
# describe "class" do
|
154
|
+
# class Thing
|
155
|
+
# def id
|
156
|
+
# object_id
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# it "returns the stash for a class via []" do
|
161
|
+
# r1 = Stash[Thing]
|
162
|
+
# r1.should be_a(Stash)
|
163
|
+
# Stash[Thing].should == r1
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# it "stores itself in a Thread Local" do
|
167
|
+
# @r1 = nil
|
168
|
+
# @r2 = nil
|
169
|
+
# Thread.new {@r1 = Stash[Thing]}
|
170
|
+
# Thread.new {@r2 = Stash[Thing]}
|
171
|
+
# @r1.should_not be_nil
|
172
|
+
# @r2.should_not be_nil
|
173
|
+
# @r2.should_not == @r1
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# it "can clear all" do
|
177
|
+
# Stash[Thing].put([Thing.new, Thing.new])
|
178
|
+
# Stash[Thing].size.should == 2
|
179
|
+
# Stash.clear_all
|
180
|
+
# Stash[Thing].size.should == 0
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|