pose 1.2.0 → 1.2.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/README.md +1 -1
- data/lib/pose.rb +3 -2
- data/lib/pose/{base_additions.rb → activerecord_base_additions.rb} +2 -1
- data/lib/pose/internal_helpers.rb +118 -0
- data/lib/pose/model_additions.rb +5 -5
- data/lib/pose/models/pose_assignment.rb +0 -4
- data/lib/pose/models/pose_word.rb +18 -4
- data/lib/pose/railtie.rb +1 -1
- data/lib/pose/static_api.rb +87 -0
- data/lib/pose/version.rb +1 -1
- data/spec/internal_helpers_spec.rb +178 -0
- data/spec/pose_api_spec.rb +279 -0
- data/spec/pose_assignment_spec.rb +10 -13
- data/spec/pose_word_spec.rb +19 -16
- data/spec/spec_helper.rb +34 -57
- data/spec/support/matchers.rb +20 -0
- data/spec/support/models.rb +8 -0
- metadata +29 -6
- data/lib/pose/static_helpers.rb +0 -173
- data/spec/pose_spec.rb +0 -401
@@ -0,0 +1,279 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Pose do
|
6
|
+
subject { PosableOne.new }
|
7
|
+
|
8
|
+
describe 'associations' do
|
9
|
+
it 'allows to access the associated words of a posable object directly' do
|
10
|
+
subject.should have(0).pose_words
|
11
|
+
subject.pose_words << PoseWord.new(text: 'one')
|
12
|
+
subject.should have_pose_words(['one'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#update_pose_index' do
|
17
|
+
|
18
|
+
context "in the 'test' environment" do
|
19
|
+
# Set global env configuration.
|
20
|
+
before :each do
|
21
|
+
Pose::CONFIGURATION[:search_in_tests] = search_in_tests
|
22
|
+
end
|
23
|
+
|
24
|
+
# Restores global configuration to default.
|
25
|
+
after :each do
|
26
|
+
Pose::CONFIGURATION[:search_in_tests] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
context "search_in_tests flag is not enabled" do
|
30
|
+
let(:search_in_tests) { false }
|
31
|
+
|
32
|
+
it "doesn't call update_pose_words" do
|
33
|
+
subject.should_not_receive :update_pose_words
|
34
|
+
subject.update_pose_index
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "search_in_tests flag is enabled" do
|
39
|
+
let(:search_in_tests) { true }
|
40
|
+
|
41
|
+
it "calls update_pose_words" do
|
42
|
+
subject.should_receive :update_pose_words
|
43
|
+
subject.update_pose_index
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO(REMOVE).
|
49
|
+
context "in the 'production' environment' do" do
|
50
|
+
before :each do
|
51
|
+
@old_env = Rails.env
|
52
|
+
Rails.env = 'production'
|
53
|
+
Pose::CONFIGURATION[:search_in_tests] = false
|
54
|
+
end
|
55
|
+
|
56
|
+
after :each do
|
57
|
+
Rails.env = @old_env
|
58
|
+
Pose::CONFIGURATION[:search_in_tests] = true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "always calls update_pose_words, even when search_in_tests is disabled" do
|
62
|
+
subject.should_receive :update_pose_words
|
63
|
+
subject.update_pose_index
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
describe '#update_pose_words' do
|
70
|
+
|
71
|
+
it 'saves the words for search' do
|
72
|
+
subject.text = 'foo bar'
|
73
|
+
subject.update_pose_words
|
74
|
+
subject.should have_pose_words ['foo', 'bar']
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'updates the search index when the text is changed' do
|
78
|
+
subject.text = 'foo'
|
79
|
+
subject.save!
|
80
|
+
|
81
|
+
subject.text = 'other text'
|
82
|
+
subject.update_pose_words
|
83
|
+
|
84
|
+
subject.should have_pose_words ['other', 'text']
|
85
|
+
end
|
86
|
+
|
87
|
+
it "doesn't create duplicate words" do
|
88
|
+
subject.text = 'foo foo'
|
89
|
+
subject.save!
|
90
|
+
subject.should have(1).pose_words
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
describe 'search' do
|
96
|
+
|
97
|
+
it 'works' do
|
98
|
+
pos = PosableOne.create text: 'foo'
|
99
|
+
result = Pose.search 'foo', PosableOne
|
100
|
+
result.should == { PosableOne => [pos] }
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'classes parameter' do
|
104
|
+
|
105
|
+
before :each do
|
106
|
+
@pos1 = PosableOne.create text: 'foo'
|
107
|
+
@pos2 = PosableTwo.create text: 'foo'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns all requested classes' do
|
111
|
+
result = Pose.search 'foo', [PosableOne, PosableTwo]
|
112
|
+
|
113
|
+
result.keys.should have(2).items
|
114
|
+
result.keys.should include PosableOne
|
115
|
+
result.keys.should include PosableTwo
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns all matching instances of each requested class' do
|
119
|
+
result = Pose.search 'foo', [PosableOne, PosableTwo]
|
120
|
+
|
121
|
+
result[PosableOne].should == [@pos1]
|
122
|
+
result[PosableTwo].should == [@pos2]
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'returns only instances of the given classes' do
|
126
|
+
result = Pose.search 'foo', PosableOne
|
127
|
+
result.keys.should include PosableOne
|
128
|
+
result.keys.should_not include PosableTwo
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'query parameter' do
|
133
|
+
|
134
|
+
it 'returns an empty array if nothing matches' do
|
135
|
+
pos1 = PosableOne.create text: 'one'
|
136
|
+
result = Pose.search 'two', PosableOne
|
137
|
+
result[PosableOne].should be_empty
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'returns only objects that match all given query words' do
|
141
|
+
pos1 = PosableOne.create text: 'one two'
|
142
|
+
pos2 = PosableOne.create text: 'one three'
|
143
|
+
pos3 = PosableOne.create text: 'two three'
|
144
|
+
|
145
|
+
result = Pose.search 'two one', PosableOne
|
146
|
+
|
147
|
+
result[PosableOne].should == [pos1]
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'returns nothing if searching for a non-existing word' do
|
151
|
+
pos1 = PosableOne.create text: 'one two'
|
152
|
+
result = Pose.search 'one zonk', PosableOne
|
153
|
+
result[PosableOne].should be_empty
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'works if the query is given in uppercase' do
|
157
|
+
pos1 = PosableOne.create text: 'one two'
|
158
|
+
result = Pose.search 'OnE TwO', PosableOne
|
159
|
+
result[PosableOne].should == [pos1]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
describe "'limit' parameter" do
|
165
|
+
|
166
|
+
it 'works' do
|
167
|
+
FactoryGirl.create :posable_one, text: 'foo one'
|
168
|
+
FactoryGirl.create :posable_one, text: 'foo two'
|
169
|
+
FactoryGirl.create :posable_one, text: 'foo three'
|
170
|
+
|
171
|
+
result = Pose.search 'foo', PosableOne, limit: 2
|
172
|
+
|
173
|
+
result[PosableOne].should have(2).items
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
describe "'result_type' parameter" do
|
179
|
+
|
180
|
+
before :each do
|
181
|
+
@foo_one = FactoryGirl.create :posable_one, text: 'foo one'
|
182
|
+
end
|
183
|
+
|
184
|
+
describe 'default behavior' do
|
185
|
+
it 'returns full objects' do
|
186
|
+
result = Pose.search 'foo', PosableOne
|
187
|
+
result[PosableOne].first.should == @foo_one
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context ':ids given' do
|
192
|
+
it 'returns ids instead of objects' do
|
193
|
+
result = Pose.search 'foo', PosableOne, result_type: :ids
|
194
|
+
result[PosableOne].first.should == @foo_one.id
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
describe "'scopes' parameter" do
|
201
|
+
|
202
|
+
before :each do
|
203
|
+
@one = FactoryGirl.create :posable_one, text: 'foo one', private: true
|
204
|
+
@bar = FactoryGirl.create :posable_one, text: 'bar one', private: true
|
205
|
+
@two = FactoryGirl.create :posable_one, text: 'foo two', private: false
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'with result type :classes' do
|
209
|
+
|
210
|
+
it 'limits the result set by the given scope' do
|
211
|
+
result = Pose.search 'foo', PosableOne, scope: [ private: true ]
|
212
|
+
result[PosableOne].should have(1).item
|
213
|
+
result[PosableOne].should include @one
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'allows to use the hash syntax for queries' do
|
217
|
+
result = Pose.search 'foo', PosableOne, scope: [ private: true ]
|
218
|
+
result[PosableOne].should have(1).item
|
219
|
+
result[PosableOne].should include @one
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'allows to use the string syntax for queries' do
|
223
|
+
result = Pose.search 'foo', PosableOne, scope: [ ['private = ?', true] ]
|
224
|
+
result[PosableOne].should have(1).item
|
225
|
+
result[PosableOne].should include @one
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'allows to combine several scopes' do
|
229
|
+
@three = FactoryGirl.create :posable_one, text: 'foo two', private: true
|
230
|
+
result = Pose.search 'foo', PosableOne, scope: [ {private: true}, ['text = ?', 'foo two'] ]
|
231
|
+
result[PosableOne].should have(1).item
|
232
|
+
result[PosableOne].should include @three
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'with result type :ids' do
|
237
|
+
|
238
|
+
it 'limits the result set by the given scope' do
|
239
|
+
result = Pose.search 'foo', PosableOne, result_type: :ids, scope: [ private: true ]
|
240
|
+
result[PosableOne].should have(1).item
|
241
|
+
result[PosableOne].should include @one.id
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
describe 'autocomplete_words' do
|
249
|
+
|
250
|
+
it 'returns words that start with the given phrase' do
|
251
|
+
PosableOne.create text: 'great green pine tree'
|
252
|
+
|
253
|
+
result = Pose::Helpers.autocomplete_words 'gr'
|
254
|
+
|
255
|
+
result.should have(2).words
|
256
|
+
result.should include 'great'
|
257
|
+
result.should include 'green'
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'returns words that match the given phrase exactly' do
|
261
|
+
PoseWord.create text: 'cat'
|
262
|
+
result = Pose::Helpers.autocomplete_words 'cat'
|
263
|
+
result.should == ['cat']
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'stems the search query' do
|
267
|
+
PosableOne.create text: 'car'
|
268
|
+
result = Pose::Helpers.autocomplete_words 'cars'
|
269
|
+
result.should have(1).words
|
270
|
+
result[0].should == 'car'
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'returns nothing if the search query is empty' do
|
274
|
+
PosableOne.create text: 'foo bar'
|
275
|
+
result = Pose::Helpers.autocomplete_words ''
|
276
|
+
result.should be_empty
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -2,47 +2,44 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe PoseAssignment do
|
4
4
|
|
5
|
-
before :each do
|
6
|
-
PoseAssignment.delete_all
|
7
|
-
end
|
8
|
-
|
9
5
|
describe "delete_class_index" do
|
10
|
-
|
6
|
+
|
11
7
|
before :each do
|
12
8
|
FactoryGirl.create :pose_assignment, posable_id: 1, posable_type: 'PosableOne'
|
13
9
|
FactoryGirl.create :pose_assignment, posable_id: 2, posable_type: 'PosableTwo'
|
14
10
|
PoseAssignment.delete_class_index PosableOne
|
15
11
|
end
|
16
|
-
|
12
|
+
|
17
13
|
it "deletes all PoseAssignments for the given class" do
|
18
14
|
PoseAssignment.where(posable_type: 'PosableOne').should have(0).items
|
19
15
|
end
|
20
|
-
|
16
|
+
|
21
17
|
it "doesn't delete PoseAssignments for other classes" do
|
22
18
|
PoseAssignment.where(posable_type: 'PosableTwo').should have(1).items
|
23
19
|
end
|
24
20
|
end
|
25
|
-
|
21
|
+
|
22
|
+
|
26
23
|
describe "cleanup_orphaned_pose_assignments" do
|
27
|
-
|
24
|
+
|
28
25
|
it "deletes the assignment if the posable object doesn't exist" do
|
29
26
|
FactoryGirl.create :pose_assignment, posable_id: 2, posable_type: 'PosableOne'
|
30
27
|
PoseAssignment.count.should > 0
|
31
28
|
PoseAssignment.cleanup_orphaned_pose_assignments
|
32
|
-
PoseAssignment.should
|
29
|
+
PoseAssignment.count.should == 0
|
33
30
|
end
|
34
|
-
|
31
|
+
|
35
32
|
it "deletes the assignment if the pose_word doesn't exist" do
|
36
33
|
assignment = FactoryGirl.create :pose_assignment, pose_word: nil, pose_word_id: 27
|
37
34
|
PoseAssignment.cleanup_orphaned_pose_assignments
|
38
35
|
PoseAssignment.find_by_id(assignment.id).should be_nil
|
39
36
|
end
|
40
|
-
|
37
|
+
|
41
38
|
it "doesn't delete the assignment if it is still used" do
|
42
39
|
assignment = FactoryGirl.create :pose_assignment
|
43
40
|
PoseAssignment.cleanup_orphaned_pose_assignments
|
44
41
|
PoseAssignment.find_by_id(assignment.id).should_not be_nil
|
45
42
|
end
|
46
43
|
end
|
47
|
-
|
44
|
+
|
48
45
|
end
|
data/spec/pose_word_spec.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe PoseWord do
|
4
|
-
|
5
|
-
before :all do
|
6
|
-
PoseWord.delete_all
|
7
|
-
end
|
8
|
-
|
9
|
-
describe 'remove_unused_words' do
|
10
4
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
5
|
+
describe 'class methods' do
|
6
|
+
subject { PoseWord }
|
7
|
+
|
8
|
+
describe '::remove_unused_words' do
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
instantiate_objects
|
12
|
+
PoseWord.remove_unused_words
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'having unused words' do
|
16
|
+
let(:instantiate_objects) { FactoryGirl.create :pose_word }
|
17
|
+
its(:count) { should == 0 }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'having used words' do
|
21
|
+
let(:instantiate_objects) { FactoryGirl.create :posable_one }
|
22
|
+
its(:count) { should > 0 }
|
23
|
+
end
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,8 @@ require 'active_support/core_ext/string'
|
|
9
9
|
require 'pose'
|
10
10
|
require 'faker'
|
11
11
|
require 'factory_girl'
|
12
|
+
require 'database_cleaner'
|
13
|
+
|
12
14
|
FactoryGirl.find_definitions
|
13
15
|
|
14
16
|
# Configure Rails Environment
|
@@ -16,7 +18,7 @@ ENV["RAILS_ENV"] = "test"
|
|
16
18
|
Rails = Hashie::Mash.new({env: 'test'})
|
17
19
|
|
18
20
|
# We have no Railtie in these tests --> load Pose manually.
|
19
|
-
ActiveRecord::Base.send :extend, Pose::
|
21
|
+
ActiveRecord::Base.send :extend, Pose::ActiveRecordBaseAdditions
|
20
22
|
|
21
23
|
# Load support files
|
22
24
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
@@ -35,51 +37,21 @@ RSpec.configure do |config|
|
|
35
37
|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
36
38
|
# examples within a transaction, remove the following line or assign false
|
37
39
|
# instead of true.
|
38
|
-
#
|
40
|
+
# config.use_transactional_fixtures = true
|
39
41
|
|
40
42
|
config.before :suite do
|
41
43
|
setup_db
|
42
44
|
Pose::CONFIGURATION[:search_in_tests] = true
|
45
|
+
DatabaseCleaner.strategy = :deletion
|
43
46
|
end
|
44
|
-
|
45
|
-
config.after :suite do
|
46
|
-
teardown_db
|
47
|
-
end
|
48
|
-
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
# MATCHERS
|
53
|
-
#
|
54
|
-
|
55
|
-
# Verifies that a taggable object has the given tags.
|
56
|
-
RSpec::Matchers.define :have_pose_words do |expected|
|
57
|
-
match do |actual|
|
58
|
-
actual.should have(expected.size).pose_words
|
59
|
-
texts = actual.pose_words.map &:text
|
60
|
-
expected.each do |expected_word|
|
61
|
-
# Note (KG): Can't use text.should include(expected_word) here
|
62
|
-
# because Ruby thinks I want to include a Module for some reason.
|
63
|
-
texts.include?(expected_word).should be_true
|
64
|
-
end
|
65
|
-
end
|
66
|
-
failure_message_for_should do |actual|
|
67
|
-
texts = actual.pose_words.map &:text
|
68
|
-
"expected that subject would have pose words [#{expected.join ', '}], but it has [#{texts.join ', '}]"
|
48
|
+
config.before :each do
|
49
|
+
DatabaseCleaner.start
|
69
50
|
end
|
70
|
-
end
|
71
|
-
|
72
51
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
class PosableOne < ActiveRecord::Base
|
78
|
-
posify { text }
|
79
|
-
end
|
80
|
-
|
81
|
-
class PosableTwo < ActiveRecord::Base
|
82
|
-
posify { text }
|
52
|
+
config.after :each do
|
53
|
+
DatabaseCleaner.clean
|
54
|
+
end
|
83
55
|
end
|
84
56
|
|
85
57
|
|
@@ -88,32 +60,37 @@ end
|
|
88
60
|
#
|
89
61
|
|
90
62
|
def setup_db
|
91
|
-
ActiveRecord::
|
63
|
+
ActiveRecord::Base.establish_connection adapter: 'postgresql',
|
64
|
+
database: 'pose_test',
|
65
|
+
min_messages: 'INFO'
|
92
66
|
|
93
|
-
|
94
|
-
|
95
|
-
|
67
|
+
ActiveRecord::Schema.define(version: 1) do
|
68
|
+
unless table_exists? 'posable_ones'
|
69
|
+
create_table 'posable_ones' do |t|
|
70
|
+
t.string 'text'
|
71
|
+
t.boolean 'private'
|
72
|
+
end
|
96
73
|
end
|
97
74
|
|
98
|
-
|
99
|
-
|
100
|
-
|
75
|
+
unless table_exists? 'posable_twos'
|
76
|
+
create_table 'posable_twos' do |t|
|
77
|
+
t.string 'text'
|
78
|
+
t.boolean 'private'
|
79
|
+
end
|
101
80
|
end
|
102
81
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
82
|
+
unless table_exists? 'pose_assignments'
|
83
|
+
create_table "pose_assignments" do |t|
|
84
|
+
t.integer "pose_word_id", null: false
|
85
|
+
t.integer "posable_id", null: false
|
86
|
+
t.string "posable_type", limit: 20, null: false
|
87
|
+
end
|
107
88
|
end
|
108
89
|
|
109
|
-
|
110
|
-
|
90
|
+
unless table_exists? 'pose_words'
|
91
|
+
create_table "pose_words" do |t|
|
92
|
+
t.string "text", limit: 80, null: false
|
93
|
+
end
|
111
94
|
end
|
112
95
|
end
|
113
96
|
end
|
114
|
-
|
115
|
-
def teardown_db
|
116
|
-
ActiveRecord::Base.connection.tables.each do |table|
|
117
|
-
ActiveRecord::Base.connection.drop_table(table)
|
118
|
-
end
|
119
|
-
end
|