perpetuity 0.2 → 0.3
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/CHANGELOG.md +16 -0
- data/LICENSE +7 -0
- data/README.md +50 -8
- data/lib/perpetuity/attribute.rb +5 -1
- data/lib/perpetuity/attribute_set.rb +1 -1
- data/lib/perpetuity/mapper.rb +53 -71
- data/lib/perpetuity/mapper_registry.rb +17 -0
- data/lib/perpetuity/mongodb/index.rb +48 -0
- data/lib/perpetuity/mongodb/query_expression.rb +11 -0
- data/lib/perpetuity/mongodb/query_intersection.rb +16 -0
- data/lib/perpetuity/mongodb/query_union.rb +16 -0
- data/lib/perpetuity/mongodb.rb +44 -7
- data/lib/perpetuity/serializer.rb +75 -0
- data/lib/perpetuity/version.rb +1 -1
- data/lib/perpetuity.rb +2 -2
- data/perpetuity.gemspec +2 -3
- data/spec/perpetuity/mapper_registry_spec.rb +15 -0
- data/spec/perpetuity/mapper_spec.rb +17 -11
- data/spec/perpetuity/mongodb/index_spec.rb +44 -0
- data/spec/perpetuity/mongodb/query_expression_spec.rb +18 -0
- data/spec/perpetuity/mongodb/query_intersection_spec.rb +16 -0
- data/spec/perpetuity/mongodb/query_union_spec.rb +16 -0
- data/spec/perpetuity/mongodb_spec.rb +28 -1
- data/spec/perpetuity/serializer_spec.rb +6 -0
- data/spec/perpetuity_spec.rb +138 -59
- data/spec/test_classes.rb +26 -17
- metadata +22 -23
- data/.rspec +0 -1
- data/Guardfile +0 -24
@@ -2,30 +2,26 @@ require 'perpetuity/mapper'
|
|
2
2
|
|
3
3
|
module Perpetuity
|
4
4
|
describe Mapper do
|
5
|
-
let(:
|
6
|
-
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
5
|
+
let(:mapper_class) { Class.new(Mapper) }
|
6
|
+
let(:mapper) { mapper_class.new }
|
10
7
|
subject { mapper }
|
11
8
|
|
12
9
|
it { should be_a Mapper }
|
13
10
|
|
14
11
|
it 'has correct attributes' do
|
15
|
-
|
12
|
+
Class.new(Mapper) { attribute :name }.attributes.should eq [:name]
|
16
13
|
end
|
17
14
|
|
18
15
|
it 'returns an empty attribute list when no attributes have been assigned' do
|
19
|
-
Mapper.
|
16
|
+
Mapper.attributes.should be_empty
|
20
17
|
end
|
21
18
|
|
22
19
|
it 'can have embedded attributes' do
|
23
|
-
mapper_with_embedded_attrs =
|
20
|
+
mapper_with_embedded_attrs = Class.new(Mapper)
|
21
|
+
mapper_with_embedded_attrs.attribute :comments, embedded: true
|
24
22
|
mapper_with_embedded_attrs.attribute_set[:comments].should be_embedded
|
25
23
|
end
|
26
24
|
|
27
|
-
its(:mapped_class) { should eq Object }
|
28
|
-
|
29
25
|
context 'with unserializable embedded attributes' do
|
30
26
|
let(:unserializable_object) { 1.to_c }
|
31
27
|
let(:serialized_attrs) do
|
@@ -35,7 +31,8 @@ module Perpetuity
|
|
35
31
|
it 'serializes attributes' do
|
36
32
|
object = Object.new
|
37
33
|
object.stub(sub_objects: [unserializable_object])
|
38
|
-
|
34
|
+
mapper_class.attribute :sub_objects, embedded: true
|
35
|
+
mapper_class.map Object
|
39
36
|
data_source = double(:data_source)
|
40
37
|
mapper.stub(data_source: data_source)
|
41
38
|
data_source.should_receive(:can_serialize?).with(unserializable_object).and_return false
|
@@ -43,5 +40,14 @@ module Perpetuity
|
|
43
40
|
mapper.serialize(object)['sub_objects'].should eq serialized_attrs
|
44
41
|
end
|
45
42
|
end
|
43
|
+
|
44
|
+
describe 'subclassing Mapper' do
|
45
|
+
let!(:mapper_subclass) { Class.new(Mapper) { map String } }
|
46
|
+
let(:mapper) { mapper_subclass.new }
|
47
|
+
|
48
|
+
it 'can explicitly map a class' do
|
49
|
+
MapperRegistry[String].should be_instance_of mapper_subclass
|
50
|
+
end
|
51
|
+
end
|
46
52
|
end
|
47
53
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'perpetuity/mongodb/index'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
class MongoDB
|
5
|
+
describe Index do
|
6
|
+
let(:attribute) { double(name: 'name') }
|
7
|
+
let(:index) { Index.new(Object, attribute) }
|
8
|
+
|
9
|
+
it 'is not active by default' do
|
10
|
+
index.should_not be_active
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can be activated' do
|
14
|
+
index.activate!
|
15
|
+
index.should be_active
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can be unique' do
|
19
|
+
index = Index.new(Object, attribute, unique: true)
|
20
|
+
index.should be_unique
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is not unique by default' do
|
24
|
+
index.should_not be_unique
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'index ordering' do
|
28
|
+
it 'can be ordered in ascending order' do
|
29
|
+
index = Index.new(Object, attribute, order: :ascending)
|
30
|
+
index.order.should be :ascending
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'is ordered ascending by default' do
|
34
|
+
index.order.should be :ascending
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can be ordered in descending order' do
|
38
|
+
index = Index.new(Object, attribute, order: :descending)
|
39
|
+
index.order.should be :descending
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -45,5 +45,23 @@ module Perpetuity
|
|
45
45
|
expression.to_db.should == { attribute: :value }
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
describe 'unions' do
|
50
|
+
let(:lhs) { MongoDB::QueryExpression.new :first, :equals, :one }
|
51
|
+
let(:rhs) { MongoDB::QueryExpression.new :second, :equals, :two }
|
52
|
+
|
53
|
+
it 'converts | to an $or query' do
|
54
|
+
(lhs | rhs).to_db.should == { '$or' => [{first: :one}, {second: :two}] }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'intersections' do
|
59
|
+
let(:lhs) { MongoDB::QueryExpression.new :first, :equals, :one }
|
60
|
+
let(:rhs) { MongoDB::QueryExpression.new :second, :equals, :two }
|
61
|
+
|
62
|
+
it 'converts & to an $and query' do
|
63
|
+
(lhs & rhs).to_db.should == { '$and' => [{first: :one}, {second: :two}] }
|
64
|
+
end
|
65
|
+
end
|
48
66
|
end
|
49
67
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'perpetuity/mongodb/query_intersection'
|
2
|
+
require 'perpetuity/mongodb/query_expression'
|
3
|
+
|
4
|
+
module Perpetuity
|
5
|
+
class MongoDB
|
6
|
+
describe QueryIntersection do
|
7
|
+
let(:lhs) { QueryExpression.new :first, :equals, 'one' }
|
8
|
+
let(:rhs) { QueryExpression.new :second, :equals, 'two' }
|
9
|
+
let(:intersection) { QueryIntersection.new lhs, rhs }
|
10
|
+
|
11
|
+
it 'returns a Mongo representation of the union of 2 expressions' do
|
12
|
+
intersection.to_db.should be == { '$and' => [{first: 'one'}, {second: 'two'}] }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'perpetuity/mongodb/query_union'
|
2
|
+
require 'perpetuity/mongodb/query_expression'
|
3
|
+
|
4
|
+
module Perpetuity
|
5
|
+
class MongoDB
|
6
|
+
describe QueryUnion do
|
7
|
+
let(:lhs) { QueryExpression.new :first, :equals, 'one' }
|
8
|
+
let(:rhs) { QueryExpression.new :second, :equals, 'two' }
|
9
|
+
let(:union) { QueryUnion.new lhs, rhs }
|
10
|
+
|
11
|
+
it 'returns the proper union of two expressions' do
|
12
|
+
union.to_db.should be == { '$or' => [{first: 'one'}, {second: 'two'}] }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -13,7 +13,7 @@ module Perpetuity
|
|
13
13
|
|
14
14
|
it 'connects to its host' do
|
15
15
|
connection = double('connection')
|
16
|
-
Mongo::
|
16
|
+
Mongo::MongoClient.should_receive(:new).and_return connection
|
17
17
|
mongo.connect
|
18
18
|
mongo.should be_connected
|
19
19
|
mongo.connection.should == connection
|
@@ -99,5 +99,32 @@ module Perpetuity
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
102
|
+
|
103
|
+
describe 'indexing' do
|
104
|
+
let(:collection) { Object }
|
105
|
+
let(:key) { 'object_id' }
|
106
|
+
|
107
|
+
before { mongo.index collection, key }
|
108
|
+
after { mongo.drop_collection collection }
|
109
|
+
|
110
|
+
it 'adds indexes for the specified key on the specified collection' do
|
111
|
+
indexes = mongo.indexes(collection).select{ |index| index.attribute == 'object_id' }
|
112
|
+
indexes.should_not be_empty
|
113
|
+
indexes.first.order.should be :ascending
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'adds descending-order indexes' do
|
117
|
+
index = mongo.index collection, 'hash', order: :descending
|
118
|
+
index.order.should be :descending
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'creates indexes on the database collection' do
|
122
|
+
mongo.delete_all collection
|
123
|
+
index = mongo.index collection, 'real_index', order: :descending, unique: true
|
124
|
+
mongo.activate_index! index
|
125
|
+
|
126
|
+
mongo.active_indexes(collection).should include index
|
127
|
+
end
|
128
|
+
end
|
102
129
|
end
|
103
130
|
end
|
data/spec/perpetuity_spec.rb
CHANGED
@@ -10,28 +10,40 @@ describe Perpetuity do
|
|
10
10
|
|
11
11
|
describe 'mapper generation' do
|
12
12
|
it 'generates mappers' do
|
13
|
-
|
13
|
+
Perpetuity.generate_mapper_for Object
|
14
14
|
Perpetuity[Object].should be_a Perpetuity::Mapper
|
15
|
-
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'provides a DSL within the generated mapper' do
|
18
|
+
Perpetuity.generate_mapper_for Object do
|
19
|
+
id { object_id + 1 }
|
20
|
+
attribute :object_id
|
21
|
+
end
|
22
|
+
|
23
|
+
mapper = Perpetuity[Object]
|
24
|
+
object = Object.new
|
25
|
+
mapper.insert object
|
26
|
+
object.id.should eq object.object_id + 1
|
27
|
+
mapper.attributes.should eq [:object_id]
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
19
31
|
describe 'persistence' do
|
20
32
|
it "persists an object" do
|
21
33
|
article = Article.new 'I have a title'
|
22
|
-
expect { Perpetuity
|
23
|
-
to change { Perpetuity
|
24
|
-
Perpetuity
|
34
|
+
expect { Perpetuity[Article].insert article }.
|
35
|
+
to change { Perpetuity[Article].count }.by 1
|
36
|
+
Perpetuity[Article].find(article.id).title.should eq 'I have a title'
|
25
37
|
end
|
26
38
|
|
27
39
|
it 'returns the id of the persisted object' do
|
28
40
|
article = Article.new
|
29
|
-
Perpetuity
|
41
|
+
Perpetuity[Article].insert(article).should eq article.id
|
30
42
|
end
|
31
43
|
|
32
44
|
it "gives an id to objects" do
|
33
45
|
article = Article.new
|
34
|
-
Perpetuity
|
46
|
+
Perpetuity[Article].give_id_to article, 1
|
35
47
|
|
36
48
|
article.id.should eq 1
|
37
49
|
end
|
@@ -40,20 +52,20 @@ describe Perpetuity do
|
|
40
52
|
let(:article) { Article.new }
|
41
53
|
|
42
54
|
it 'assigns an id to the inserted object' do
|
43
|
-
Perpetuity
|
55
|
+
Perpetuity[Article].insert article
|
44
56
|
article.should respond_to :id
|
45
57
|
end
|
46
58
|
|
47
59
|
it "assigns an id using Mapper.first" do
|
48
|
-
Perpetuity
|
60
|
+
Perpetuity[Article].first.should respond_to :id
|
49
61
|
end
|
50
62
|
|
51
63
|
it 'assigns an id using Mapper.retrieve.first' do
|
52
|
-
Perpetuity
|
64
|
+
Perpetuity[Article].retrieve.first.should respond_to :id
|
53
65
|
end
|
54
66
|
|
55
67
|
it 'assigns an id using Mapper.all.first' do
|
56
|
-
Perpetuity
|
68
|
+
Perpetuity[Article].all.first.should respond_to :id
|
57
69
|
end
|
58
70
|
end
|
59
71
|
|
@@ -62,15 +74,15 @@ describe Perpetuity do
|
|
62
74
|
|
63
75
|
it 'persists arrays' do
|
64
76
|
article.comments << 1 << 2 << 3
|
65
|
-
Perpetuity
|
66
|
-
Perpetuity
|
77
|
+
Perpetuity[Article].insert article
|
78
|
+
Perpetuity[Article].find(article.id).comments.should eq [1, 2, 3]
|
67
79
|
end
|
68
80
|
|
69
81
|
it 'persists arrays with unserializable objects in them' do
|
70
82
|
comment = Comment.new('my comment')
|
71
83
|
article.comments << comment
|
72
|
-
Perpetuity
|
73
|
-
Perpetuity
|
84
|
+
Perpetuity[Article].insert article
|
85
|
+
Perpetuity[Article].find(article.id).comments.first.tap do |persisted_comment|
|
74
86
|
persisted_comment.should be_a Comment
|
75
87
|
persisted_comment.body.should eq comment.body
|
76
88
|
end
|
@@ -78,54 +90,55 @@ describe Perpetuity do
|
|
78
90
|
end
|
79
91
|
|
80
92
|
it "allows mappers to set the id field" do
|
81
|
-
|
93
|
+
noise = Time.now.to_f.to_s.sub('.', '')
|
94
|
+
book = Book.new("My Title #{noise}")
|
82
95
|
|
83
|
-
Perpetuity
|
84
|
-
book.id.should eq
|
96
|
+
Perpetuity[Book].insert book
|
97
|
+
book.id.should eq "my-title-#{noise}"
|
85
98
|
end
|
86
99
|
end
|
87
100
|
|
88
101
|
describe "deletion" do
|
89
102
|
it 'deletes an object' do
|
90
|
-
2.times { Perpetuity
|
91
|
-
expect { Perpetuity
|
103
|
+
2.times { Perpetuity[Article].insert Article.new }
|
104
|
+
expect { Perpetuity[Article].delete Perpetuity[Article].first }.to change { Perpetuity[Article].count }.by(-1)
|
92
105
|
end
|
93
106
|
|
94
107
|
it 'deletes an object with a given id' do
|
95
|
-
article_id = Perpetuity
|
108
|
+
article_id = Perpetuity[Article].insert Article.new
|
96
109
|
expect {
|
97
|
-
Perpetuity
|
98
|
-
}.to change { Perpetuity
|
110
|
+
Perpetuity[Article].delete article_id
|
111
|
+
}.to change { Perpetuity[Article].count }.by(-1)
|
99
112
|
end
|
100
113
|
|
101
114
|
describe "#delete_all" do
|
102
115
|
it "should delete all objects of a certain class" do
|
103
|
-
Perpetuity
|
104
|
-
Perpetuity
|
105
|
-
Perpetuity
|
116
|
+
Perpetuity[Article].insert Article.new
|
117
|
+
Perpetuity[Article].delete_all
|
118
|
+
Perpetuity[Article].count.should eq 0
|
106
119
|
end
|
107
120
|
end
|
108
121
|
end
|
109
122
|
|
110
123
|
describe "retrieval" do
|
111
124
|
it "gets all the objects of a class" do
|
112
|
-
expect { Perpetuity
|
113
|
-
to change { Perpetuity
|
125
|
+
expect { Perpetuity[Article].insert Article.new }.
|
126
|
+
to change { Perpetuity[Article].all.count }.by 1
|
114
127
|
end
|
115
128
|
|
116
129
|
it "has an ID when retrieved" do
|
117
|
-
Perpetuity
|
118
|
-
Perpetuity
|
130
|
+
Perpetuity[Article].insert Article.new
|
131
|
+
Perpetuity[Article].first.should respond_to :id
|
119
132
|
end
|
120
133
|
|
121
134
|
it "returns a Perpetuity::Retrieval object" do
|
122
|
-
Perpetuity
|
135
|
+
Perpetuity[Article].retrieve(id: 1).should be_an_instance_of Perpetuity::Retrieval
|
123
136
|
end
|
124
137
|
|
125
138
|
it "gets an item with a specific ID" do
|
126
139
|
article = Article.new
|
127
|
-
Perpetuity
|
128
|
-
retrieved = Perpetuity
|
140
|
+
Perpetuity[Article].insert article
|
141
|
+
retrieved = Perpetuity[Article].find(article.id)
|
129
142
|
|
130
143
|
retrieved.id.should eq article.id
|
131
144
|
retrieved.title.should eq article.title
|
@@ -134,8 +147,8 @@ describe Perpetuity do
|
|
134
147
|
|
135
148
|
it "gets an item by its attributes" do
|
136
149
|
article = Article.new
|
137
|
-
Perpetuity
|
138
|
-
retrieved = Perpetuity
|
150
|
+
Perpetuity[Article].insert article
|
151
|
+
retrieved = Perpetuity[Article].retrieve(title: article.title)
|
139
152
|
|
140
153
|
retrieved.to_a.should_not be_empty
|
141
154
|
retrieved.first.title.should eq article.title
|
@@ -145,60 +158,60 @@ describe Perpetuity do
|
|
145
158
|
let(:draft) { Article.new 'Draft', 'draft content', nil, Time.now + 30 }
|
146
159
|
let(:published) { Article.new 'Published', 'content', nil, Time.now - 30, 3 }
|
147
160
|
before do
|
148
|
-
Perpetuity
|
149
|
-
Perpetuity
|
161
|
+
Perpetuity[Article].insert draft
|
162
|
+
Perpetuity[Article].insert published
|
150
163
|
end
|
151
164
|
|
152
165
|
it 'selects objects using equality' do
|
153
|
-
selected = Perpetuity
|
166
|
+
selected = Perpetuity[Article].select { title == 'Published' }
|
154
167
|
selected.map(&:id).should include published.id
|
155
168
|
selected.map(&:id).should_not include draft.id
|
156
169
|
end
|
157
170
|
|
158
171
|
it 'selects objects using greater-than' do
|
159
|
-
selected = Perpetuity
|
172
|
+
selected = Perpetuity[Article].select { published_at < Time.now }
|
160
173
|
ids = selected.map(&:id)
|
161
174
|
ids.should include published.id
|
162
175
|
ids.should_not include draft.id
|
163
176
|
end
|
164
177
|
|
165
178
|
it 'selects objects using greater-than-or-equal' do
|
166
|
-
selected = Perpetuity
|
179
|
+
selected = Perpetuity[Article].select { views >= 3 }
|
167
180
|
ids = selected.map(&:id)
|
168
181
|
ids.should include published.id
|
169
182
|
ids.should_not include draft.id
|
170
183
|
end
|
171
184
|
|
172
185
|
it 'selects objects using less-than' do
|
173
|
-
selected = Perpetuity
|
186
|
+
selected = Perpetuity[Article].select { views < 3 }
|
174
187
|
ids = selected.map(&:id)
|
175
188
|
ids.should include draft.id
|
176
189
|
ids.should_not include published.id
|
177
190
|
end
|
178
191
|
|
179
192
|
it 'selects objects using less-than-or-equal' do
|
180
|
-
selected = Perpetuity
|
193
|
+
selected = Perpetuity[Article].select { views <= 0 }
|
181
194
|
ids = selected.map(&:id)
|
182
195
|
ids.should include draft.id
|
183
196
|
ids.should_not include published.id
|
184
197
|
end
|
185
198
|
|
186
199
|
it 'selects objects using inequality' do
|
187
|
-
selected = Perpetuity
|
200
|
+
selected = Perpetuity[Article].select { title.not_equal? 'Draft' }
|
188
201
|
ids = selected.map(&:id)
|
189
202
|
ids.should_not include draft.id
|
190
203
|
ids.should include published.id
|
191
204
|
end
|
192
205
|
|
193
206
|
it 'selects objects using regular expressions' do
|
194
|
-
selected = Perpetuity
|
207
|
+
selected = Perpetuity[Article].select { title =~ /Pub/ }
|
195
208
|
ids = selected.map(&:id)
|
196
209
|
ids.should include published.id
|
197
210
|
ids.should_not include draft.id
|
198
211
|
end
|
199
212
|
|
200
213
|
it 'selects objects using inclusion' do
|
201
|
-
selected = Perpetuity
|
214
|
+
selected = Perpetuity[Article].select { title.in %w( Published ) }
|
202
215
|
ids = selected.map(&:id)
|
203
216
|
ids.should include published.id
|
204
217
|
ids.should_not include draft.id
|
@@ -208,22 +221,24 @@ describe Perpetuity do
|
|
208
221
|
|
209
222
|
describe 'pagination' do
|
210
223
|
it 'specifies the page we want' do
|
211
|
-
Perpetuity
|
224
|
+
Perpetuity[Article].retrieve.should respond_to :page
|
212
225
|
end
|
213
226
|
|
214
227
|
it 'specify the quantity per page' do
|
215
|
-
Perpetuity
|
228
|
+
Perpetuity[Article].retrieve.should respond_to :per_page
|
216
229
|
end
|
217
230
|
|
218
231
|
it 'returns an empty set when there is no data for that page' do
|
219
|
-
|
232
|
+
mapper = Perpetuity[Article]
|
233
|
+
mapper.delete_all
|
234
|
+
data = mapper.retrieve.page(2)
|
220
235
|
data.should be_empty
|
221
236
|
end
|
222
237
|
|
223
238
|
it 'specifies per-page quantity' do
|
224
|
-
Perpetuity
|
225
|
-
5.times { |i| Perpetuity
|
226
|
-
data = Perpetuity
|
239
|
+
Perpetuity[Article].delete_all
|
240
|
+
5.times { |i| Perpetuity[Article].insert Article.new i }
|
241
|
+
data = Perpetuity[Article].retrieve.page(3).per_page(2)
|
227
242
|
data.should have(1).item
|
228
243
|
end
|
229
244
|
end
|
@@ -231,8 +246,8 @@ describe Perpetuity do
|
|
231
246
|
describe 'associations with other objects' do
|
232
247
|
let(:user) { User.new }
|
233
248
|
let(:topic) { Topic.new }
|
234
|
-
let(:user_mapper) { Perpetuity
|
235
|
-
let(:topic_mapper) { Perpetuity
|
249
|
+
let(:user_mapper) { Perpetuity[User] }
|
250
|
+
let(:topic_mapper) { Perpetuity[Topic] }
|
236
251
|
|
237
252
|
before do
|
238
253
|
user.name = 'Flump'
|
@@ -262,18 +277,32 @@ describe Perpetuity do
|
|
262
277
|
|
263
278
|
describe 'updating' do
|
264
279
|
let(:article) { Article.new }
|
280
|
+
let(:mapper) { Perpetuity[Article] }
|
281
|
+
let(:new_title) { 'I has a new title!' }
|
282
|
+
|
265
283
|
before do
|
266
|
-
|
284
|
+
mapper.insert article
|
267
285
|
end
|
268
286
|
|
269
287
|
it 'updates an object in the database' do
|
270
|
-
|
271
|
-
|
288
|
+
mapper.update article, title: new_title
|
289
|
+
mapper.find(article.id).title.should eq new_title
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'updates the object in memory' do
|
293
|
+
mapper.update article, title: new_title
|
294
|
+
article.title.should eq new_title
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'resaves the object in the database' do
|
298
|
+
article.title = new_title
|
299
|
+
mapper.save article
|
300
|
+
mapper.find(article.id).title.should eq new_title
|
272
301
|
end
|
273
302
|
end
|
274
303
|
|
275
304
|
describe 'validations' do
|
276
|
-
let(:car_mapper) { Perpetuity
|
305
|
+
let(:car_mapper) { Perpetuity[Car] }
|
277
306
|
|
278
307
|
it 'raises an exception when inserting an invalid object' do
|
279
308
|
car = Car.new
|
@@ -290,8 +319,8 @@ describe Perpetuity do
|
|
290
319
|
# The Message class stores its data members differently internally than it receives them
|
291
320
|
it 'uses accessor methods to read/write data' do
|
292
321
|
message = Message.new 'My Message!'
|
293
|
-
Perpetuity
|
294
|
-
saved_message = Perpetuity
|
322
|
+
Perpetuity[Message].insert message
|
323
|
+
saved_message = Perpetuity[Message].find(message.id)
|
295
324
|
saved_message.instance_variable_get(:@text).should eq 'My Message!'.reverse
|
296
325
|
saved_message.text.should eq 'My Message!'
|
297
326
|
end
|
@@ -353,4 +382,54 @@ describe Perpetuity do
|
|
353
382
|
end
|
354
383
|
end
|
355
384
|
end
|
385
|
+
|
386
|
+
describe 'methods on mappers' do
|
387
|
+
it 'allows methods to act as scopes' do
|
388
|
+
published = Article.new('Published', 'I love cats', nil, Time.now - 30)
|
389
|
+
draft = Article.new('Draft', 'I do not like moose', nil, nil)
|
390
|
+
not_yet_published = Article.new('Tomorrow', 'Dogs', nil, Time.now + 30)
|
391
|
+
|
392
|
+
mapper = Perpetuity[Article]
|
393
|
+
mapper.insert published
|
394
|
+
mapper.insert draft
|
395
|
+
mapper.insert not_yet_published
|
396
|
+
|
397
|
+
published_ids = mapper.published.to_a.map(&:id)
|
398
|
+
published_ids.should include published.id
|
399
|
+
published_ids.should_not include draft.id, not_yet_published.id
|
400
|
+
|
401
|
+
unpublished_ids = mapper.unpublished.to_a.map(&:id)
|
402
|
+
unpublished_ids.should_not include published.id
|
403
|
+
unpublished_ids.should include draft.id, not_yet_published.id
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe 'indexing' do
|
408
|
+
let(:mapper_class) do
|
409
|
+
Class.new(Perpetuity::Mapper) do
|
410
|
+
map Object
|
411
|
+
attribute :name
|
412
|
+
index :name
|
413
|
+
end
|
414
|
+
end
|
415
|
+
let(:mapper) { mapper_class.new }
|
416
|
+
let(:name_index) do
|
417
|
+
mapper.indexes.find do |index|
|
418
|
+
index.attribute.to_s == :name
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'adds indexes to database collections/tables' do
|
423
|
+
name_index.attribute.name.should be == :name
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'verifies that indexes are inactive' do
|
427
|
+
name_index.should be_inactive
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'creates indexes' do
|
431
|
+
mapper.reindex!
|
432
|
+
name_index.should be_active
|
433
|
+
end
|
434
|
+
end
|
356
435
|
end
|