oedipus-dm 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 +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +451 -0
- data/Rakefile +17 -0
- data/lib/oedipus/data_mapper/collection.rb +52 -0
- data/lib/oedipus/data_mapper/conversions.rb +64 -0
- data/lib/oedipus/data_mapper/index.rb +268 -0
- data/lib/oedipus/data_mapper/pagination.rb +48 -0
- data/lib/oedipus/data_mapper/version.rb +14 -0
- data/lib/oedipus/data_mapper.rb +44 -0
- data/lib/oedipus-dm.rb +10 -0
- data/oedipus-dm.gemspec +31 -0
- data/spec/data/.gitkeep +0 -0
- data/spec/integration/index_spec.rb +398 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/models/post.rb +11 -0
- data/spec/support/models/user.rb +8 -0
- metadata +134 -0
@@ -0,0 +1,398 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Oedipus::DataMapper::Index do
|
4
|
+
include Oedipus::RSpec::TestHarness
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
set_data_dir File.expand_path("../../data", __FILE__)
|
8
|
+
set_searchd ENV["SEARCHD"]
|
9
|
+
start_searchd
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) { stop_searchd }
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
Post.destroy!
|
16
|
+
User.destroy!
|
17
|
+
empty_indexes
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:conn) do
|
21
|
+
Oedipus::Connection.new(searchd_host)
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:index) do
|
25
|
+
Oedipus::DataMapper::Index.new(Post, name: :posts_rt, connection: conn) do |idx|
|
26
|
+
idx.map :id
|
27
|
+
idx.map :title
|
28
|
+
idx.map :body
|
29
|
+
idx.map :user_id
|
30
|
+
idx.map :views, with: :view_count
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#insert" do
|
35
|
+
let(:user) do
|
36
|
+
User.create(username: "bob")
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:post) do
|
40
|
+
Post.create(
|
41
|
+
title: "There was one was a badger",
|
42
|
+
body: "And a nice one he was.",
|
43
|
+
user: user,
|
44
|
+
view_count: 98
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
before(:each) { index.insert(post) }
|
49
|
+
|
50
|
+
it "inserts the object into the index" do
|
51
|
+
conn[:posts_rt].fetch(post.id)[:user_id].should == user.id
|
52
|
+
end
|
53
|
+
|
54
|
+
it "uses the defined mappings" do
|
55
|
+
conn[:posts_rt].fetch(post.id)[:views].should == 98
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
pending "the sphinxql grammar does not currently support this, though I'm patching it" do
|
60
|
+
describe "#update" do
|
61
|
+
let(:user) do
|
62
|
+
User.create(username: "bob")
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:post) do
|
66
|
+
Post.create(
|
67
|
+
title: "There was one was a badger",
|
68
|
+
body: "And a nice one he was.",
|
69
|
+
user: user,
|
70
|
+
view_count: 98
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
before(:each) do
|
75
|
+
conn[:posts_rt].insert(post.id, title: "Not this", body: "Or this", views: 0, user_id: 100)
|
76
|
+
index.update(post)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "updates the object in the index" do
|
80
|
+
conn[:posts_rt].fetch(post.id)[:user_id].should == user.id
|
81
|
+
end
|
82
|
+
|
83
|
+
it "uses the defined mappings" do
|
84
|
+
conn[:posts_rt].fetch(post.id)[:views].should == 98
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#replace" do
|
90
|
+
let(:user) do
|
91
|
+
User.create(username: "bob")
|
92
|
+
end
|
93
|
+
|
94
|
+
let(:post) do
|
95
|
+
Post.create(
|
96
|
+
title: "There was one was a badger",
|
97
|
+
body: "And a nice one he was.",
|
98
|
+
user: user,
|
99
|
+
view_count: 98
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
before(:each) do
|
104
|
+
conn[:posts_rt].insert(post.id, title: "Not this", body: "Nor this", user_id: 100, views: 0)
|
105
|
+
index.replace(post)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "updates the object in the index" do
|
109
|
+
conn[:posts_rt].fetch(post.id)[:user_id].should == user.id
|
110
|
+
end
|
111
|
+
|
112
|
+
it "uses the defined mappings" do
|
113
|
+
conn[:posts_rt].fetch(post.id)[:views].should == 98
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#delete" do
|
118
|
+
let(:user) do
|
119
|
+
User.create(username: "bob")
|
120
|
+
end
|
121
|
+
|
122
|
+
let(:post) do
|
123
|
+
Post.create(
|
124
|
+
title: "There was one was a badger",
|
125
|
+
body: "And a nice one he was.",
|
126
|
+
user: user,
|
127
|
+
view_count: 98
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
before(:each) do
|
132
|
+
conn[:posts_rt].insert(post.id, title: "Not this", body: "Nor this", user_id: 100, views: 0)
|
133
|
+
index.delete(post)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "removes the object in the index" do
|
137
|
+
conn[:posts_rt].fetch(post.id).should be_nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#search" do
|
142
|
+
before(:each) do
|
143
|
+
@user_a = User.create(username: "bob")
|
144
|
+
@user_b = User.create(username: "abi")
|
145
|
+
[
|
146
|
+
@a = Post.create(
|
147
|
+
title: "Badgers on the run",
|
148
|
+
body: "Big badger little badger",
|
149
|
+
view_count: 7,
|
150
|
+
user: @user_a
|
151
|
+
),
|
152
|
+
@b = Post.create(
|
153
|
+
title: "Do it for the badgers!",
|
154
|
+
body: "The badgers need you",
|
155
|
+
view_count: 11,
|
156
|
+
user: @user_a
|
157
|
+
),
|
158
|
+
@c = Post.create(
|
159
|
+
title: "Talk to the hand, not to the badgers",
|
160
|
+
body: "Cos this badger ain't listening",
|
161
|
+
view_count: 6,
|
162
|
+
user: @user_b
|
163
|
+
),
|
164
|
+
@d = Post.create(
|
165
|
+
title: "Rabbits doing rabbity things",
|
166
|
+
body: "Being all cute, with their floppy little ears",
|
167
|
+
view_count: 9,
|
168
|
+
user: @user_a
|
169
|
+
)
|
170
|
+
].each do |p|
|
171
|
+
conn[:posts_rt].insert(p.id, title: p.title, body: p.body, views: p.view_count, user_id: p.user.id)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "returns a datamapper collection" do
|
176
|
+
index.search("badgers").should be_a_kind_of(DataMapper::Collection)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "returns models of the correct type" do
|
180
|
+
index.search("badgers").map(&:model).should == [Post, Post, Post]
|
181
|
+
end
|
182
|
+
|
183
|
+
it "loads the records matching the search" do
|
184
|
+
index.search("badgers", order: :id).collect { |p| { id: p.id, title: p.title } }.should == [
|
185
|
+
{ id: @a.id, title: @a.title },
|
186
|
+
{ id: @b.id, title: @b.title },
|
187
|
+
{ id: @c.id, title: @c.title }
|
188
|
+
]
|
189
|
+
end
|
190
|
+
|
191
|
+
it "handles the attribute mappings" do
|
192
|
+
index.search("badgers", order: :id).collect { |p| { view_count: p.view_count } }.should == [
|
193
|
+
{ view_count: @a.view_count },
|
194
|
+
{ view_count: @b.view_count },
|
195
|
+
{ view_count: @c.view_count }
|
196
|
+
]
|
197
|
+
end
|
198
|
+
|
199
|
+
it "provides the count for the total number of records matched" do
|
200
|
+
index.search("badgers", limit: 2).total_found.should == 3
|
201
|
+
end
|
202
|
+
|
203
|
+
it "provides the count for the number of records returned" do
|
204
|
+
index.search("badgers", limit: 2).count.should == 2
|
205
|
+
end
|
206
|
+
|
207
|
+
it "loads the models directly from the index" do
|
208
|
+
index.search("badgers").each do |p|
|
209
|
+
Post.user_id.loaded?(p).should be_true
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
it "allows lazy-loading of the non-indexed attributes" do
|
214
|
+
index.search("badgers").each do |p|
|
215
|
+
Post.created_at.loaded?(p).should be_false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "symbol operators" do
|
220
|
+
describe "Symbol.not" do
|
221
|
+
it "works like Oedipus.not" do
|
222
|
+
index.search("badgers", :user_id.not => @user_a.id, order: :id).map(&:id).should == [@c.id]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "Symbol.gt" do
|
227
|
+
it "works like Oedipus.gt" do
|
228
|
+
index.search("badgers", :views.gt => 7, order: :id).map(&:id).should == [@b.id]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "Symbol.gte" do
|
233
|
+
it "works like Oedipus.gte" do
|
234
|
+
index.search("badgers", :views.gte => 7, order: :id).map(&:id).should == [@a.id, @b.id]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "Symbol.lt" do
|
239
|
+
it "works like Oedipus.lt" do
|
240
|
+
index.search("badgers", :views.lt => 7, order: :id).map(&:id).should == [@c.id]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "Symbol.lte" do
|
245
|
+
it "works like Oedipus.lte" do
|
246
|
+
index.search("badgers", :views.lte => 7, order: :id).map(&:id).should == [@a.id, @c.id]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "Symbol.asc" do
|
251
|
+
it "is converted to Hash notation" do
|
252
|
+
index.search("badgers", order: :id.asc).map(&:id).should == [@a.id, @b.id, @c.id]
|
253
|
+
end
|
254
|
+
|
255
|
+
context "inside an array" do
|
256
|
+
it "is converted to Hash notation" do
|
257
|
+
index.search("badgers", order: [:id.asc]).map(&:id).should == [@a.id, @b.id, @c.id]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe "Symbol.desc" do
|
263
|
+
it "is converted to Hash notation" do
|
264
|
+
index.search("badgers", order: :id.desc).map(&:id).should == [@c.id, @b.id, @a.id]
|
265
|
+
end
|
266
|
+
|
267
|
+
context "inside an array" do
|
268
|
+
it "is converted to Hash notation" do
|
269
|
+
index.search("badgers", order: [:id.desc]).map(&:id).should == [@c.id, @b.id, @a.id]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe "pagination" do
|
276
|
+
before(:each) do
|
277
|
+
[
|
278
|
+
@e = Post.create(
|
279
|
+
title: "Badgers again",
|
280
|
+
body: "Blah blah badger",
|
281
|
+
view_count: 20,
|
282
|
+
user: @user_a
|
283
|
+
),
|
284
|
+
@f = Post.create(
|
285
|
+
title: "Don't take my badger!",
|
286
|
+
body: "Badgers are afraid of the dark",
|
287
|
+
view_count: 21,
|
288
|
+
user: @user_a
|
289
|
+
),
|
290
|
+
@g = Post.create(
|
291
|
+
title: "You seen one badger, you seen em all",
|
292
|
+
body: "Badgers, they're all the same",
|
293
|
+
view_count: 4,
|
294
|
+
user: @user_b
|
295
|
+
)
|
296
|
+
].each do |p|
|
297
|
+
conn[:posts_rt].insert(p.id, title: p.title, body: p.body, views: p.view_count, user_id: p.user.id)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context "with :per_page specified" do
|
302
|
+
context "with :page => 1" do
|
303
|
+
it "returns the first page of the results" do
|
304
|
+
index.search("badgers", order: :id, pager: {page: 1, per_page: 2}).map(&:id).should == [@a.id, @b.id]
|
305
|
+
end
|
306
|
+
|
307
|
+
it "provides a #pager with #current_page = 1" do
|
308
|
+
index.search("badgers", order: :id, pager: {page: 1, per_page: 2}).pager.current_page.should == 1
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context "with :page => 2" do
|
313
|
+
it "returns the second page of the results" do
|
314
|
+
index.search("badgers", order: :id, pager: {page: 2, per_page: 2}).map(&:id).should == [@c.id, @e.id]
|
315
|
+
end
|
316
|
+
|
317
|
+
it "provides a #pager with #current_page = 2" do
|
318
|
+
index.search("badgers", order: :id, pager: {page: 2, per_page: 2}).pager.current_page.should == 2
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe "with :facets" do
|
325
|
+
it "returns the main results in the collection" do
|
326
|
+
index.search(
|
327
|
+
"badgers",
|
328
|
+
order: :id,
|
329
|
+
facets: {
|
330
|
+
popular: {:views.gte => 7}
|
331
|
+
}
|
332
|
+
).map(&:id).should == [@a.id, @b.id, @c.id]
|
333
|
+
end
|
334
|
+
|
335
|
+
it "returns the facets inside the collection" do
|
336
|
+
index.search(
|
337
|
+
"badgers",
|
338
|
+
order: :id,
|
339
|
+
facets: {
|
340
|
+
popular: {:views.gte => 7}
|
341
|
+
}
|
342
|
+
).facets[:popular].map(&:id).should == [@a.id, @b.id]
|
343
|
+
end
|
344
|
+
|
345
|
+
it "provides data on the matches inside the facets" do
|
346
|
+
index.search(
|
347
|
+
"badgers",
|
348
|
+
order: :id,
|
349
|
+
facets: {
|
350
|
+
popular: {:views.gte => 7}
|
351
|
+
}
|
352
|
+
).facets[:popular].total_found.should == 2
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe "#multi_search" do
|
358
|
+
before(:each) do
|
359
|
+
@user_a = User.create(username: "bob")
|
360
|
+
@user_b = User.create(username: "abi")
|
361
|
+
[
|
362
|
+
@a = Post.create(
|
363
|
+
title: "Badgers on the run",
|
364
|
+
body: "Big badger little badger",
|
365
|
+
view_count: 7,
|
366
|
+
user: @user_a
|
367
|
+
),
|
368
|
+
@b = Post.create(
|
369
|
+
title: "Do it for the badgers!",
|
370
|
+
body: "The badgers need you",
|
371
|
+
view_count: 11,
|
372
|
+
user: @user_a
|
373
|
+
),
|
374
|
+
@c = Post.create(
|
375
|
+
title: "Talk to the hand, not to the badgers",
|
376
|
+
body: "Cos this badger ain't listening",
|
377
|
+
view_count: 6,
|
378
|
+
user: @user_b
|
379
|
+
),
|
380
|
+
@d = Post.create(
|
381
|
+
title: "Rabbits doing rabbity things",
|
382
|
+
body: "Being all cute, with their floppy little ears",
|
383
|
+
view_count: 9,
|
384
|
+
user: @user_a
|
385
|
+
)
|
386
|
+
].each do |p|
|
387
|
+
conn[:posts_rt].insert(p.id, title: p.title, body: p.body, views: p.view_count, user_id: p.user.id)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
it "returns a Hash mapping the search names with their collections" do
|
392
|
+
index.multi_search(
|
393
|
+
popular_badgers: ["badgers", :views.gte => 7],
|
394
|
+
rabbits: "rabbits"
|
395
|
+
).should be_a_kind_of(Hash)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "oedipus-dm"
|
3
|
+
require "oedipus/rspec/test_harness"
|
4
|
+
require "dm-pager"
|
5
|
+
|
6
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each { |f| require f }
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.before(:suite) do
|
10
|
+
DataMapper::Model.raise_on_save_failure = true
|
11
|
+
DataMapper.setup(:default, adapter: :in_memory)
|
12
|
+
DataMapper.finalize
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oedipus-dm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- d11wtq
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: oedipus
|
16
|
+
requirement: &14446500 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.0.5
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *14446500
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dm-core
|
27
|
+
requirement: &14445360 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.2'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *14445360
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &14444580 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *14444580
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &14443860 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *14443860
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: dm-pager
|
60
|
+
requirement: &14431500 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *14431500
|
69
|
+
description: ! '== DataMapper Integration for Oedipus
|
70
|
+
|
71
|
+
|
72
|
+
This gem adds the possibility to find DataMapper models by searching in
|
73
|
+
|
74
|
+
a Sphinx index, and to update/delete/replace them.
|
75
|
+
|
76
|
+
|
77
|
+
Faceted searches are cleanly supported.
|
78
|
+
|
79
|
+
'
|
80
|
+
email:
|
81
|
+
- chris@w3style.co.uk
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- lib/oedipus-dm.rb
|
93
|
+
- lib/oedipus/data_mapper.rb
|
94
|
+
- lib/oedipus/data_mapper/collection.rb
|
95
|
+
- lib/oedipus/data_mapper/conversions.rb
|
96
|
+
- lib/oedipus/data_mapper/index.rb
|
97
|
+
- lib/oedipus/data_mapper/pagination.rb
|
98
|
+
- lib/oedipus/data_mapper/version.rb
|
99
|
+
- oedipus-dm.gemspec
|
100
|
+
- spec/data/.gitkeep
|
101
|
+
- spec/integration/index_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
- spec/support/models/post.rb
|
104
|
+
- spec/support/models/user.rb
|
105
|
+
homepage: https://github.com/d11wtq/oedipus-dm
|
106
|
+
licenses: []
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.11
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: DataMapper Integration for the Oedipus Sphinx 2 Client
|
129
|
+
test_files:
|
130
|
+
- spec/data/.gitkeep
|
131
|
+
- spec/integration/index_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/support/models/post.rb
|
134
|
+
- spec/support/models/user.rb
|