pose 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,20 @@
|
|
1
|
+
# Verifies that a posable object has the given pose words in its search index.
|
2
|
+
RSpec::Matchers.define :have_pose_words do |expected|
|
3
|
+
|
4
|
+
match do |actual|
|
5
|
+
actual.should have(expected.size).pose_words
|
6
|
+
texts = actual.pose_words.map &:text
|
7
|
+
expected.each do |expected_word|
|
8
|
+
# Note (KG): Can't use text.should include(expected_word) here
|
9
|
+
# because Ruby thinks I want to include a Module for some reason.
|
10
|
+
texts.include?(expected_word).should be_true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
failure_message_for_should do |actual|
|
15
|
+
texts = actual.pose_words.map &:text
|
16
|
+
"expected that subject would have pose words [#{expected.join ', '}], but it has [#{texts.join ', '}]"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -75,6 +75,22 @@ dependencies:
|
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: database_cleaner
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
78
94
|
- !ruby/object:Gem::Dependency
|
79
95
|
name: factory_girl
|
80
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,12 +199,13 @@ files:
|
|
183
199
|
- doc/top-level-namespace.html
|
184
200
|
- lib/generators/pose_generator.rb
|
185
201
|
- lib/generators/templates/migration.rb
|
186
|
-
- lib/pose/
|
202
|
+
- lib/pose/activerecord_base_additions.rb
|
203
|
+
- lib/pose/internal_helpers.rb
|
187
204
|
- lib/pose/model_additions.rb
|
188
205
|
- lib/pose/models/pose_assignment.rb
|
189
206
|
- lib/pose/models/pose_word.rb
|
190
207
|
- lib/pose/railtie.rb
|
191
|
-
- lib/pose/
|
208
|
+
- lib/pose/static_api.rb
|
192
209
|
- lib/pose/version.rb
|
193
210
|
- lib/pose.rb
|
194
211
|
- lib/tasks/pose_tasks.rake
|
@@ -196,10 +213,13 @@ files:
|
|
196
213
|
- Rakefile
|
197
214
|
- README.md
|
198
215
|
- spec/factories.rb
|
216
|
+
- spec/internal_helpers_spec.rb
|
217
|
+
- spec/pose_api_spec.rb
|
199
218
|
- spec/pose_assignment_spec.rb
|
200
|
-
- spec/pose_spec.rb
|
201
219
|
- spec/pose_word_spec.rb
|
202
220
|
- spec/spec_helper.rb
|
221
|
+
- spec/support/matchers.rb
|
222
|
+
- spec/support/models.rb
|
203
223
|
homepage: http://github.com/kevgo/pose
|
204
224
|
licenses: []
|
205
225
|
post_install_message:
|
@@ -226,7 +246,10 @@ specification_version: 3
|
|
226
246
|
summary: A polymorphic, storage-system independent search engine for Ruby on Rails.
|
227
247
|
test_files:
|
228
248
|
- spec/factories.rb
|
249
|
+
- spec/internal_helpers_spec.rb
|
250
|
+
- spec/pose_api_spec.rb
|
229
251
|
- spec/pose_assignment_spec.rb
|
230
|
-
- spec/pose_spec.rb
|
231
252
|
- spec/pose_word_spec.rb
|
232
253
|
- spec/spec_helper.rb
|
254
|
+
- spec/support/matchers.rb
|
255
|
+
- spec/support/models.rb
|
data/lib/pose/static_helpers.rb
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
# Static helper methods of the Pose gem.
|
2
|
-
module Pose
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
# By default, doesn't run in tests.
|
6
|
-
# Set this to true to test the search functionality.
|
7
|
-
CONFIGURATION = { search_in_tests: false }
|
8
|
-
|
9
|
-
class <<self
|
10
|
-
|
11
|
-
# Returns whether Pose is configured to perform search.
|
12
|
-
# This setting exists to disable search in tests.
|
13
|
-
#
|
14
|
-
# @return [false, true]
|
15
|
-
def perform_search?
|
16
|
-
!(Rails.env == 'test' and !CONFIGURATION[:search_in_tests])
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns all words that begin with the given query string.
|
20
|
-
# This can be used for autocompletion functionality.
|
21
|
-
#
|
22
|
-
# @param [String]
|
23
|
-
# @return [Array<String>]
|
24
|
-
def autocomplete_words query
|
25
|
-
return [] if query.blank?
|
26
|
-
PoseWord.where('text LIKE ?', "#{Pose.root_word(query)[0]}%").map(&:text)
|
27
|
-
end
|
28
|
-
|
29
|
-
# Returns all strings that are in new_words, but not in existing_words.
|
30
|
-
# Helper method.
|
31
|
-
#
|
32
|
-
# @param [Array<String>] existing_words The words that are already associated with the object.
|
33
|
-
# @param [Array<String>] new_words The words thet the object should have from now on.
|
34
|
-
#
|
35
|
-
# @return [Array<String>] The words that need to be added to the existing_words array.
|
36
|
-
def get_words_to_add existing_words, new_words
|
37
|
-
new_words - existing_words.map(&:text)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Helper method.
|
41
|
-
# Returns the id of all word objects that are in existing_words, but not in new_words.
|
42
|
-
#
|
43
|
-
# @param [Array<String>] existing_words The words that are already associated with the object.
|
44
|
-
# @param [Array<String>] new_words The words thet the object should have from now on.
|
45
|
-
#
|
46
|
-
# @return [Array<String>] The words that need to be removed from the existing_words array.
|
47
|
-
def get_words_to_remove existing_words, new_words
|
48
|
-
existing_words.map do |existing_word|
|
49
|
-
existing_word unless new_words.include?(existing_word.text)
|
50
|
-
end.compact
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns whether the given string is a URL.
|
54
|
-
#
|
55
|
-
# @param [String] word The string to check.
|
56
|
-
#
|
57
|
-
# @return [Boolean]
|
58
|
-
def is_url? word
|
59
|
-
URI::parse(word).scheme == 'http'
|
60
|
-
rescue URI::InvalidURIError
|
61
|
-
false
|
62
|
-
end
|
63
|
-
|
64
|
-
# Simplifies the given word to a generic search form.
|
65
|
-
#
|
66
|
-
# @param [String] raw_word The word to make searchable.
|
67
|
-
#
|
68
|
-
# @return [String] The stemmed version of the word.
|
69
|
-
def root_word raw_word
|
70
|
-
result = []
|
71
|
-
raw_word_copy = raw_word[0..-1]
|
72
|
-
raw_word_copy.gsub! '%20', ' '
|
73
|
-
raw_word_copy.gsub! /[()*<>'",;\?\-\=&%#]/, ' '
|
74
|
-
raw_word_copy.gsub! /\s+/, ' '
|
75
|
-
raw_word_copy.split(' ').each do |word|
|
76
|
-
if Pose.is_url?(word)
|
77
|
-
result.concat word.split(/[\.\/\:]/).delete_if(&:blank?)
|
78
|
-
else
|
79
|
-
word.gsub! /[\-\/\._:]/, ' '
|
80
|
-
word.gsub! /\s+/, ' '
|
81
|
-
word.split(' ').each do |w|
|
82
|
-
stemmed_word = w.parameterize.singularize
|
83
|
-
result.concat stemmed_word.split ' '
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
result.uniq
|
88
|
-
end
|
89
|
-
|
90
|
-
# Returns all objects matching the given query.
|
91
|
-
#
|
92
|
-
# @param [String] query
|
93
|
-
# @param (Class|[Array<Class>]) classes
|
94
|
-
# @param [Hash?] options Additional options.
|
95
|
-
#
|
96
|
-
# @return [Hash<Class, ActiveRecord::Relation>]
|
97
|
-
def search query, classes, options = {}
|
98
|
-
|
99
|
-
# Turn 'classes' into an array.
|
100
|
-
classes = [classes].flatten
|
101
|
-
classes_names = classes.map &:name
|
102
|
-
classes_names = classes_names[0] if classes_names.size == 1
|
103
|
-
|
104
|
-
# Get the ids of the results.
|
105
|
-
result_classes_and_ids = {}
|
106
|
-
query_words = query.split(' ').map{|query_word| Pose.root_word query_word}.flatten
|
107
|
-
query_words.each do |query_word|
|
108
|
-
current_word_classes_and_ids = {}
|
109
|
-
classes.each { |clazz| current_word_classes_and_ids[clazz.name] = [] }
|
110
|
-
query = PoseAssignment.joins(:pose_word) \
|
111
|
-
.select('pose_assignments.posable_id, pose_assignments.posable_type') \
|
112
|
-
.where('pose_words.text LIKE ?', "#{query_word}%") \
|
113
|
-
.where('posable_type IN (?)', classes_names)
|
114
|
-
PoseAssignment.connection.select_all(query.to_sql).each do |pose_assignment|
|
115
|
-
current_word_classes_and_ids[pose_assignment['posable_type']] << pose_assignment['posable_id'].to_i
|
116
|
-
end
|
117
|
-
# This is the old ActiveRecord way. Removed for performance reasons.
|
118
|
-
# query.each do |pose_assignment|
|
119
|
-
# current_word_classes_and_ids[pose_assignment.posable_type] << pose_assignment.posable_id
|
120
|
-
# end
|
121
|
-
|
122
|
-
current_word_classes_and_ids.each do |class_name, ids|
|
123
|
-
if result_classes_and_ids.has_key? class_name
|
124
|
-
result_classes_and_ids[class_name] = result_classes_and_ids[class_name] & ids
|
125
|
-
else
|
126
|
-
result_classes_and_ids[class_name] = ids
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# Load the results by id.
|
132
|
-
{}.tap do |result|
|
133
|
-
result_classes_and_ids.each do |class_name, ids|
|
134
|
-
result_class = Kernel.const_get class_name
|
135
|
-
|
136
|
-
if ids.size == 0
|
137
|
-
# Handle no results.
|
138
|
-
result[result_class] = []
|
139
|
-
|
140
|
-
else
|
141
|
-
# Here we have results.
|
142
|
-
|
143
|
-
# Limit.
|
144
|
-
ids = ids.slice(0, options[:limit]) if options[:limit]
|
145
|
-
|
146
|
-
if options[:result_type] == :ids
|
147
|
-
# Ids requested for result.
|
148
|
-
|
149
|
-
if options.has_key? :scope
|
150
|
-
# We have a scope.
|
151
|
-
options[:scope].each do |scope|
|
152
|
-
result[result_class] = result_class.select('id').where(scope).map(&:id)
|
153
|
-
end
|
154
|
-
else
|
155
|
-
result[result_class] = ids
|
156
|
-
end
|
157
|
-
|
158
|
-
else
|
159
|
-
# Classes requested for result.
|
160
|
-
|
161
|
-
result[result_class] = result_class.where(id: ids)
|
162
|
-
if options.has_key? :scope
|
163
|
-
options[:scope].each do |scope|
|
164
|
-
result[result_class] = result[result_class].where(scope)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
data/spec/pose_spec.rb
DELETED
@@ -1,401 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
ActiveRecord::Base.establish_connection adapter: 'postgresql', database: 'pose_test', min_messages: 'WARNING'
|
6
|
-
|
7
|
-
describe Pose do
|
8
|
-
subject { PosableOne.new }
|
9
|
-
|
10
|
-
before :each do
|
11
|
-
PosableOne.delete_all
|
12
|
-
PosableTwo.delete_all
|
13
|
-
PoseAssignment.delete_all
|
14
|
-
PoseWord.delete_all
|
15
|
-
end
|
16
|
-
|
17
|
-
describe 'associations' do
|
18
|
-
it 'allows to access the associated words of a posable object directly' do
|
19
|
-
subject.should have(0).pose_words
|
20
|
-
subject.pose_words << PoseWord.new(text: 'one')
|
21
|
-
subject.should have_pose_words(['one'])
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe 'update_pose_index' do
|
26
|
-
|
27
|
-
context "in the 'test' environment" do
|
28
|
-
after :each do
|
29
|
-
Pose::CONFIGURATION[:search_in_tests] = true
|
30
|
-
end
|
31
|
-
|
32
|
-
it "doesn't calls update_pose_words in tests if the test flag is not enabled" do
|
33
|
-
Pose::CONFIGURATION[:search_in_tests] = false
|
34
|
-
subject.should_not_receive :update_pose_words
|
35
|
-
subject.update_pose_index
|
36
|
-
end
|
37
|
-
|
38
|
-
it "calls update_pose_words in tests if the test flag is enabled" do
|
39
|
-
Pose::CONFIGURATION[:search_in_tests] = true
|
40
|
-
subject.should_receive :update_pose_words
|
41
|
-
subject.update_pose_index
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context "in the 'production' environment' do" do
|
46
|
-
before :each do
|
47
|
-
@old_env = Rails.env
|
48
|
-
Rails.env = 'production'
|
49
|
-
end
|
50
|
-
|
51
|
-
after :each do
|
52
|
-
Rails.env = @old_env
|
53
|
-
end
|
54
|
-
|
55
|
-
it "calls update_pose_words" do
|
56
|
-
subject.should_receive :update_pose_words
|
57
|
-
subject.update_pose_index
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe 'update_pose_words' do
|
63
|
-
|
64
|
-
it 'saves the words for search' do
|
65
|
-
subject.text = 'foo bar'
|
66
|
-
subject.update_pose_words
|
67
|
-
subject.should have(2).pose_words
|
68
|
-
subject.should have_pose_words ['foo', 'bar']
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'updates the search index when the text is changed' do
|
72
|
-
subject.text = 'foo'
|
73
|
-
subject.save!
|
74
|
-
|
75
|
-
subject.text = 'other text'
|
76
|
-
subject.update_pose_words
|
77
|
-
|
78
|
-
subject.should have_pose_words ['other', 'text']
|
79
|
-
end
|
80
|
-
|
81
|
-
it "doesn't create duplicate words" do
|
82
|
-
subject.text = 'foo foo'
|
83
|
-
subject.save!
|
84
|
-
subject.should have(1).pose_words
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe 'get_words_to_remove' do
|
89
|
-
|
90
|
-
it "returns an array of word objects that need to be removed" do
|
91
|
-
word1 = PoseWord.new text: 'one'
|
92
|
-
word2 = PoseWord.new text: 'two'
|
93
|
-
existing_words = [word1, word2]
|
94
|
-
new_words = ['one', 'three']
|
95
|
-
|
96
|
-
result = Pose.get_words_to_remove existing_words, new_words
|
97
|
-
|
98
|
-
result.should eql([word2])
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'returns an empty array if there are no words to be removed' do
|
102
|
-
word1 = PoseWord.new text: 'one'
|
103
|
-
word2 = PoseWord.new text: 'two'
|
104
|
-
existing_words = [word1, word2]
|
105
|
-
new_words = ['one', 'two']
|
106
|
-
|
107
|
-
result = Pose.get_words_to_remove existing_words, new_words
|
108
|
-
|
109
|
-
result.should eql([])
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
describe 'get_words_to_add' do
|
114
|
-
|
115
|
-
it 'returns an array with strings that need to be added' do
|
116
|
-
word1 = PoseWord.new text: 'one'
|
117
|
-
word2 = PoseWord.new text: 'two'
|
118
|
-
existing_words = [word1, word2]
|
119
|
-
new_words = ['one', 'three']
|
120
|
-
|
121
|
-
result = Pose.get_words_to_add existing_words, new_words
|
122
|
-
|
123
|
-
result.should eql(['three'])
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'returns an empty array if there is nothing to be added' do
|
127
|
-
word1 = PoseWord.new text: 'one'
|
128
|
-
word2 = PoseWord.new text: 'two'
|
129
|
-
existing_words = [word1, word2]
|
130
|
-
new_words = ['one', 'two']
|
131
|
-
|
132
|
-
result = Pose.get_words_to_add existing_words, new_words
|
133
|
-
|
134
|
-
result.should eql([])
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
describe 'root_word' do
|
139
|
-
|
140
|
-
it 'converts words into singular' do
|
141
|
-
Pose.root_word('bars').should eql(['bar'])
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'removes special characters' do
|
145
|
-
Pose.root_word('(bar').should eql(['bar'])
|
146
|
-
Pose.root_word('bar)').should eql(['bar'])
|
147
|
-
Pose.root_word('(bar)').should eql(['bar'])
|
148
|
-
Pose.root_word('>foo').should eql(['foo'])
|
149
|
-
Pose.root_word('<foo').should eql(['foo'])
|
150
|
-
Pose.root_word('"foo"').should eql(['foo'])
|
151
|
-
Pose.root_word('"foo').should eql(['foo'])
|
152
|
-
Pose.root_word("'foo'").should eql(['foo'])
|
153
|
-
Pose.root_word("'foo's").should eql(['foo'])
|
154
|
-
Pose.root_word("foo?").should eql(['foo'])
|
155
|
-
Pose.root_word("foo!").should eql(['foo'])
|
156
|
-
Pose.root_word("foo/bar").should eql(['foo', 'bar'])
|
157
|
-
Pose.root_word("foo-bar").should eql(['foo', 'bar'])
|
158
|
-
Pose.root_word("foo--bar").should eql(['foo', 'bar'])
|
159
|
-
Pose.root_word("foo.bar").should eql(['foo', 'bar'])
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'removes umlauts' do
|
163
|
-
Pose.root_word('fünf').should eql(['funf'])
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'splits up numbers' do
|
167
|
-
Pose.root_word('11.2.2011').should eql(['11', '2', '2011'])
|
168
|
-
Pose.root_word('11-2-2011').should eql(['11', '2', '2011'])
|
169
|
-
Pose.root_word('30:4-5').should eql(['30', '4', '5'])
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'converts into lowercase' do
|
173
|
-
Pose.root_word('London').should eql(['london'])
|
174
|
-
end
|
175
|
-
|
176
|
-
it "stores single-letter words" do
|
177
|
-
Pose.root_word('a b').should eql(['a', 'b'])
|
178
|
-
end
|
179
|
-
|
180
|
-
it "does't encode external URLs" do
|
181
|
-
Pose.root_word('http://web.com').should eql(['http', 'web', 'com'])
|
182
|
-
end
|
183
|
-
|
184
|
-
it "doesn't store empty words" do
|
185
|
-
Pose.root_word(' one two ').should eql(['one', 'two'])
|
186
|
-
end
|
187
|
-
|
188
|
-
it "removes duplicates" do
|
189
|
-
Pose.root_word('one_one').should eql(['one'])
|
190
|
-
Pose.root_word('one one').should eql(['one'])
|
191
|
-
end
|
192
|
-
|
193
|
-
it "splits up complex URLs" do
|
194
|
-
Pose.root_word('books?id=p7uyWPcVGZsC&dq=closure%20definitive%20guide&pg=PP1#v=onepage&q&f=false').should eql([
|
195
|
-
"book", "id", "p7uywpcvgzsc", "dq", "closure", "definitive", "guide", "pg", "pp1", "v", "onepage", "q", "f", "false"])
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe 'search' do
|
200
|
-
|
201
|
-
it 'works' do
|
202
|
-
pos1 = PosableOne.create text: 'one'
|
203
|
-
|
204
|
-
result = Pose.search 'one', PosableOne
|
205
|
-
|
206
|
-
result.should have(1).items
|
207
|
-
result[PosableOne].should have(1).items
|
208
|
-
result[PosableOne][0].should == pos1
|
209
|
-
end
|
210
|
-
|
211
|
-
describe 'classes parameter' do
|
212
|
-
it 'returns all different classes by default' do
|
213
|
-
pos1 = PosableOne.create text: 'foo'
|
214
|
-
pos2 = PosableTwo.create text: 'foo'
|
215
|
-
|
216
|
-
result = Pose.search 'foo', [PosableOne, PosableTwo]
|
217
|
-
|
218
|
-
result.should have(2).items
|
219
|
-
result[PosableOne].should == [pos1]
|
220
|
-
result[PosableTwo].should == [pos2]
|
221
|
-
end
|
222
|
-
|
223
|
-
it 'allows to provide different classes to return' do
|
224
|
-
pos1 = PosableOne.create text: 'foo'
|
225
|
-
pos2 = PosableTwo.create text: 'foo'
|
226
|
-
|
227
|
-
result = Pose.search 'foo', [PosableOne, PosableTwo]
|
228
|
-
|
229
|
-
result.should have(2).items
|
230
|
-
result[PosableOne].should == [pos1]
|
231
|
-
result[PosableTwo].should == [pos2]
|
232
|
-
end
|
233
|
-
|
234
|
-
it 'returns only instances of the given classes' do
|
235
|
-
pos1 = PosableOne.create text: 'one'
|
236
|
-
pos2 = PosableTwo.create text: 'one'
|
237
|
-
|
238
|
-
result = Pose.search 'one', PosableOne
|
239
|
-
|
240
|
-
result.should have(1).items
|
241
|
-
result[PosableOne].should == [pos1]
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
describe 'query parameter' do
|
246
|
-
|
247
|
-
it 'returns an empty array if nothing matches' do
|
248
|
-
pos1 = PosableOne.create text: 'one'
|
249
|
-
|
250
|
-
result = Pose.search 'two', PosableOne
|
251
|
-
|
252
|
-
result.should == { PosableOne => [] }
|
253
|
-
end
|
254
|
-
|
255
|
-
it 'returns only objects that match all given query words' do
|
256
|
-
pos1 = PosableOne.create text: 'one two'
|
257
|
-
pos2 = PosableOne.create text: 'one three'
|
258
|
-
pos3 = PosableOne.create text: 'two three'
|
259
|
-
|
260
|
-
result = Pose.search 'two one', PosableOne
|
261
|
-
|
262
|
-
result.should have(1).items
|
263
|
-
result[PosableOne].should == [pos1]
|
264
|
-
end
|
265
|
-
|
266
|
-
it 'returns nothing if searching for a non-existing word' do
|
267
|
-
pos1 = PosableOne.create text: 'one two'
|
268
|
-
|
269
|
-
result = Pose.search 'one zonk', PosableOne
|
270
|
-
|
271
|
-
result.should have(1).items
|
272
|
-
result[PosableOne].should == []
|
273
|
-
end
|
274
|
-
|
275
|
-
it 'works if the query is given in uppercase' do
|
276
|
-
pos1 = PosableOne.create text: 'one two'
|
277
|
-
|
278
|
-
result = Pose.search 'OnE TwO', PosableOne
|
279
|
-
|
280
|
-
result.should have(1).items
|
281
|
-
result[PosableOne].should == [pos1]
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
describe "'limit' parameter" do
|
286
|
-
|
287
|
-
it 'works' do
|
288
|
-
FactoryGirl.create :posable_one, text: 'foo one'
|
289
|
-
FactoryGirl.create :posable_one, text: 'foo two'
|
290
|
-
FactoryGirl.create :posable_one, text: 'foo three'
|
291
|
-
FactoryGirl.create :posable_one, text: 'foo four'
|
292
|
-
|
293
|
-
result = Pose.search 'foo', PosableOne, limit: 3
|
294
|
-
|
295
|
-
result[PosableOne].should have(3).items
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
describe "'result_type' parameter" do
|
300
|
-
|
301
|
-
before :each do
|
302
|
-
@foo_one = FactoryGirl.create :posable_one, text: 'foo one'
|
303
|
-
end
|
304
|
-
|
305
|
-
describe 'default behavior' do
|
306
|
-
it 'returns full objects' do
|
307
|
-
result = Pose.search 'foo', PosableOne
|
308
|
-
result[PosableOne][0].should == @foo_one
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
context ':ids given' do
|
313
|
-
it 'returns ids instead of objects' do
|
314
|
-
result = Pose.search 'foo', PosableOne, result_type: :ids
|
315
|
-
result[PosableOne][0].should == @foo_one.id
|
316
|
-
end
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
describe "'scopes' parameter" do
|
321
|
-
|
322
|
-
before :each do
|
323
|
-
@one = FactoryGirl.create :posable_one, text: 'foo one', private: true
|
324
|
-
@two = FactoryGirl.create :posable_one, text: 'foo two', private: false
|
325
|
-
end
|
326
|
-
|
327
|
-
context 'with result type :classes' do
|
328
|
-
|
329
|
-
it 'limits the result set by the given scope' do
|
330
|
-
result = Pose.search 'foo', PosableOne, scope: [ private: true ]
|
331
|
-
result[PosableOne].should have(1).item
|
332
|
-
result[PosableOne].should include @one
|
333
|
-
end
|
334
|
-
|
335
|
-
it 'allows to use the hash syntax for queries' do
|
336
|
-
result = Pose.search 'foo', PosableOne, scope: [ private: true ]
|
337
|
-
result[PosableOne].should have(1).item
|
338
|
-
result[PosableOne].should include @one
|
339
|
-
end
|
340
|
-
|
341
|
-
it 'allows to use the string syntax for queries' do
|
342
|
-
result = Pose.search 'foo', PosableOne, scope: [ ['private = ?', true] ]
|
343
|
-
result[PosableOne].should have(1).item
|
344
|
-
result[PosableOne].should include @one
|
345
|
-
end
|
346
|
-
|
347
|
-
it 'allows to combine several scopes' do
|
348
|
-
@three = FactoryGirl.create :posable_one, text: 'foo two', private: true
|
349
|
-
result = Pose.search 'foo', PosableOne, scope: [ {private: true}, ['text = ?', 'foo two'] ]
|
350
|
-
result[PosableOne].should have(1).item
|
351
|
-
result[PosableOne].should include @three
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
context 'with result type :ids' do
|
356
|
-
|
357
|
-
it 'limits the result set by the given scope' do
|
358
|
-
result = Pose.search 'foo', PosableOne, result_type: :ids, scope: [ private: true ]
|
359
|
-
result[PosableOne].should have(1).item
|
360
|
-
result[PosableOne].should include @one.id
|
361
|
-
end
|
362
|
-
end
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
describe 'autocomplete_words' do
|
367
|
-
|
368
|
-
it 'returns words that start with the given phrase' do
|
369
|
-
PosableOne.create text: 'great green pine tree'
|
370
|
-
|
371
|
-
result = Pose.autocomplete_words 'gr'
|
372
|
-
|
373
|
-
result.should have(2).words
|
374
|
-
result.should include('great')
|
375
|
-
result.should include('green')
|
376
|
-
end
|
377
|
-
|
378
|
-
it 'returns words that match the given phrase exactly' do
|
379
|
-
PoseWord.create text: 'cat'
|
380
|
-
|
381
|
-
result = Pose.autocomplete_words 'cat'
|
382
|
-
|
383
|
-
result.should == ['cat']
|
384
|
-
end
|
385
|
-
|
386
|
-
it 'stems the search query' do
|
387
|
-
PosableOne.create text: 'car'
|
388
|
-
|
389
|
-
result = Pose.autocomplete_words 'cars'
|
390
|
-
|
391
|
-
result.should have(1).words
|
392
|
-
result[0].should == 'car'
|
393
|
-
end
|
394
|
-
|
395
|
-
it 'returns nothing if the search query is empty' do
|
396
|
-
PosableOne.create text: 'foo bar'
|
397
|
-
result = Pose.autocomplete_words ''
|
398
|
-
result.should have(0).words
|
399
|
-
end
|
400
|
-
end
|
401
|
-
end
|