search_magic 0.0.6 → 0.0.7
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/Gemfile.lock +8 -8
- data/README.textile +22 -9
- data/lib/search_magic/full_text_search.rb +37 -28
- data/lib/search_magic/metadata.rb +2 -2
- data/lib/search_magic/version.rb +1 -1
- data/search_magic.gemspec +1 -1
- data/spec/unit/search_magic/associations_spec.rb +10 -10
- data/spec/unit/search_magic/fields_spec.rb +35 -10
- data/spec/unit/search_magic/model_updates_spec.rb +80 -0
- metadata +7 -5
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
search_magic (0.0.
|
5
|
-
mongoid (>= 2.0.0.rc.
|
4
|
+
search_magic (0.0.7)
|
5
|
+
mongoid (>= 2.0.0.rc.8)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (3.0.
|
11
|
-
activesupport (= 3.0.
|
10
|
+
activemodel (3.0.5)
|
11
|
+
activesupport (= 3.0.5)
|
12
12
|
builder (~> 2.1.2)
|
13
13
|
i18n (~> 0.4)
|
14
|
-
activesupport (3.0.
|
14
|
+
activesupport (3.0.5)
|
15
15
|
bson (1.2.4)
|
16
16
|
bson_ext (1.2.4)
|
17
17
|
builder (2.1.2)
|
@@ -20,7 +20,7 @@ GEM
|
|
20
20
|
i18n (0.5.0)
|
21
21
|
mongo (1.2.4)
|
22
22
|
bson (>= 1.2.4)
|
23
|
-
mongoid (2.0.0.rc.
|
23
|
+
mongoid (2.0.0.rc.8)
|
24
24
|
activemodel (~> 3.0)
|
25
25
|
mongo (~> 1.2)
|
26
26
|
tzinfo (~> 0.3.22)
|
@@ -33,7 +33,7 @@ GEM
|
|
33
33
|
rspec-expectations (2.5.0)
|
34
34
|
diff-lcs (~> 1.1.2)
|
35
35
|
rspec-mocks (2.5.0)
|
36
|
-
tzinfo (0.3.
|
36
|
+
tzinfo (0.3.25)
|
37
37
|
will_paginate (3.0.pre2)
|
38
38
|
|
39
39
|
PLATFORMS
|
@@ -42,6 +42,6 @@ PLATFORMS
|
|
42
42
|
DEPENDENCIES
|
43
43
|
bson_ext
|
44
44
|
database_cleaner
|
45
|
-
mongoid (>= 2.0.0.rc.
|
45
|
+
mongoid (>= 2.0.0.rc.8)
|
46
46
|
rspec
|
47
47
|
search_magic!
|
data/README.textile
CHANGED
@@ -4,7 +4,11 @@ SearchMagic provides full-text search capabilities to "mongoid":http://github.co
|
|
4
4
|
|
5
5
|
h2. Installation
|
6
6
|
|
7
|
-
SearchMagic is built on top of the latest pre-release version of mongoid; in all likelihood, it will only work with versions greater than or equal to _2.0.0.rc.
|
7
|
+
SearchMagic is built on top of the latest pre-release version of mongoid; in all likelihood, it will only work with versions greater than or equal to _2.0.0.rc.8_. The project can be installed as a gem on a target system:
|
8
|
+
|
9
|
+
bc. gem install search_magic
|
10
|
+
|
11
|
+
For environments where bundler is being used, it can be installed by adding the following to your Gemfile and running @bundle@.
|
8
12
|
|
9
13
|
bc. gem 'search_magic'
|
10
14
|
|
@@ -29,9 +33,9 @@ bc.. class Address
|
|
29
33
|
search_on :post_code
|
30
34
|
end
|
31
35
|
|
32
|
-
p. At this point, *Address* can be searched by calling its _*:
|
36
|
+
p. At this point, *Address* can be searched by calling its _*:search_for*_ method:
|
33
37
|
|
34
|
-
bc. Address.
|
38
|
+
bc. Address.search_for("state:ca")
|
35
39
|
|
36
40
|
It is also possible to sort models on fields which have been marked as searchable through the _*:arrange*_ method:
|
37
41
|
|
@@ -86,6 +90,7 @@ Finally, it should be noted that nesting of searchable documents is possible. (C
|
|
86
90
|
|
87
91
|
bc.. class Part
|
88
92
|
include Mongoid::Document
|
93
|
+
include Mongoid::Timestamps
|
89
94
|
include SearchMagic::FullTextSearch
|
90
95
|
field :serial
|
91
96
|
references_in :part_number
|
@@ -116,17 +121,25 @@ end
|
|
116
121
|
|
117
122
|
p. *PartNumber* will be able to search on both _:number_ and _:category_name_. *Part*, on the other hand, will absorb all of the searchable fields of PartNumber, including its associations. So, it can be searched on _:serial_, _:number_, and _:category_name_
|
118
123
|
|
119
|
-
h3. :
|
124
|
+
h3. :search_for
|
120
125
|
|
121
|
-
Searching a model with SearchMagic is simple: each model gains a class method called _
|
126
|
+
Searching a model with SearchMagic is simple: each model gains a class method called _*:search_for*_ which accepts one parameter, the search pattern. This method is a "mongoid scope":http://mongoid.org/docs/querying/; it will always return a criteria object after executing. As such, it plays nicely with other scopes on your models.
|
122
127
|
|
123
128
|
SearchMagic expects the incoming _pattern_ to be a string containing whitespace delimited phrases. Each phrase can either be a single word, or a _selector:value_ pair. Multiple phrases will narrow the search field: each additional phrase places an additional requirement on a matching document. Single word phrases are matched across all entries in a model's _:searchable_values_ array. The pairs, on the other hand, restrict the search for _value_ against only those entries which match _selector_. In either case, _word_ or _value_ may contain fragments of whole entries stored within _:searchable_values_.
|
124
129
|
|
125
130
|
Using the models defined in the previous section, the following searches are all perfectly valid:
|
126
131
|
|
127
|
-
bc. Part.
|
128
|
-
Part.
|
129
|
-
Part.
|
132
|
+
bc. Part.search_for("table") # full text search on "table"
|
133
|
+
Part.search_for("category_name:table") # restricts the search for "table" to "category_name"
|
134
|
+
Part.search_for("bike serial:b1234") # full text search on "bike", with an extra requirement that the serial be "b1234"
|
135
|
+
|
136
|
+
As _*:search_for*_ is a scope, it can be called on any previous scope within the call chain:
|
137
|
+
|
138
|
+
bc. Part.where(:created_at.gt => 1.day.ago.time).search_for("table")
|
139
|
+
|
140
|
+
_*:search_for*_ can be called multiple times within the same scope chain. Doing so will append each successive pattern to the previous searches. Effectively, this is the same as performing a single _*:search_for*_ with whitespace delimited terms in the pattern. To make such expressions slightly more readable, _*:search_for*_ is aliased as _*:and_for*_:
|
141
|
+
|
142
|
+
bc. Part.search_for("bike").and_for("serial:b1234")
|
130
143
|
|
131
144
|
h3. :arrange
|
132
145
|
|
@@ -138,7 +151,7 @@ Part.arrange(:category_name, :desc) # arrange the parts in descending order by :
|
|
138
151
|
|
139
152
|
As mentioned, _*:arrange*_ is a scope, so it can be chained with other scopes on a given model:
|
140
153
|
|
141
|
-
bc. Part.
|
154
|
+
bc. Part.search_for("category_name:table").arrange(:serial, :asc)
|
142
155
|
|
143
156
|
h2. Problems? Comments?
|
144
157
|
|
@@ -1,15 +1,18 @@
|
|
1
1
|
module SearchMagic
|
2
2
|
module FullTextSearch
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :searchable_fields, :instance_writer => false
|
7
|
+
self.searchable_fields = {}
|
8
|
+
field :searchable_values, :type => Array, :default => []
|
9
|
+
field :arrangeable_values, :type => Hash, :default => {}
|
10
|
+
before_save :update_searchable_values
|
11
|
+
before_save :update_arrangeable_values
|
12
|
+
after_save :update_associated_documents
|
13
|
+
end
|
14
|
+
|
3
15
|
module ClassMethods
|
4
|
-
def self.extended(receiver)
|
5
|
-
receiver.send :class_attribute, :searchable_fields, :instance_writer => false
|
6
|
-
receiver.send :searchable_fields=, {}
|
7
|
-
receiver.send :field, :searchable_values, :type => Array, :default => []
|
8
|
-
receiver.send :field, :arrangeable_values, :type => Hash, :default => {}
|
9
|
-
receiver.send :before_save, :update_searchable_values
|
10
|
-
receiver.send :before_save, :update_arrangeable_values
|
11
|
-
end
|
12
|
-
|
13
16
|
def search_on(field_name, options = {})
|
14
17
|
searchable_fields[field_name] = options
|
15
18
|
end
|
@@ -18,7 +21,13 @@ module SearchMagic
|
|
18
21
|
@searchables ||= create_searchables
|
19
22
|
end
|
20
23
|
|
21
|
-
def
|
24
|
+
def inverse_searchables
|
25
|
+
@inverse_searchables ||= relations.values.
|
26
|
+
select {|metadata| metadata.class_name.constantize.searchable_fields.keys.include?(metadata.inverse_setter.chomp("=").to_sym)}.
|
27
|
+
map(&:name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def search_for(pattern)
|
22
31
|
rval = /("[^"]+"|\S+)/
|
23
32
|
rsearch = /(?:(#{searchables.keys.join('|')}):#{rval})|#{rval}/i
|
24
33
|
unless pattern.blank?
|
@@ -32,6 +41,7 @@ module SearchMagic
|
|
32
41
|
criteria
|
33
42
|
end
|
34
43
|
end
|
44
|
+
alias :and_for :search_for
|
35
45
|
|
36
46
|
def arrange(arrangeable, direction = :asc)
|
37
47
|
arrangeable.blank? || !searchables.keys.include?(arrangeable.to_sym) ? criteria : order_by([["arrangeable_values.#{arrangeable}", direction]])
|
@@ -51,7 +61,7 @@ module SearchMagic
|
|
51
61
|
Metadata.new(:type => self, :through => lambda do |obj|
|
52
62
|
value = obj.send(field_name)
|
53
63
|
value.is_a?(Array) ? value.map {|item| metadata.through.call(item)} : metadata.through.call(value)
|
54
|
-
end, :prefix => field_name.to_s.singularize.to_sym, :field_name => name, :options => metadata.options.merge(options))
|
64
|
+
end, :prefix => field_name.to_s.singularize.to_sym, :field_name => name, :relation_metadata => association, :options => metadata.options.merge(options))
|
55
65
|
end
|
56
66
|
else
|
57
67
|
Metadata.new(:type => self, :through => lambda {|obj| obj.send(field_name) }, :field_name => field_name.to_s.pluralize.singularize.to_sym, :options => options)
|
@@ -62,24 +72,23 @@ module SearchMagic
|
|
62
72
|
end
|
63
73
|
end
|
64
74
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
self.searchable_values = self.class.searchables.values.map {|metadata| metadata.searchable_value_for(self)}.flatten
|
70
|
-
end
|
71
|
-
|
72
|
-
def update_arrangeable_values
|
73
|
-
self.arrangeable_values = {}
|
74
|
-
self.class.searchables.values.each do |metadata|
|
75
|
-
self.arrangeable_values[metadata.name] = metadata.arrangeable_value_for(self)
|
76
|
-
end
|
77
|
-
end
|
75
|
+
private
|
76
|
+
|
77
|
+
def update_searchable_values
|
78
|
+
self.searchable_values = self.class.searchables.values.map {|metadata| metadata.searchable_value_for(self)}.flatten
|
78
79
|
end
|
79
|
-
|
80
|
-
def
|
81
|
-
|
82
|
-
|
80
|
+
|
81
|
+
def update_arrangeable_values
|
82
|
+
self.arrangeable_values = Hash[*self.class.searchables.map {|key, metadata|
|
83
|
+
[key, metadata.arrangeable_value_for(self)]
|
84
|
+
}.flatten(1)]
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_associated_documents
|
88
|
+
self.class.inverse_searchables.each do |relation_name|
|
89
|
+
relation = send(relation_name)
|
90
|
+
(relation.is_a?(Array) ? relation : [relation]).each(&:save!)
|
91
|
+
end
|
83
92
|
end
|
84
93
|
end
|
85
94
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module SearchMagic
|
2
2
|
class Metadata
|
3
|
-
attr_accessor :type, :through, :prefix, :field_name, :options
|
3
|
+
attr_accessor :type, :through, :prefix, :field_name, :relation_metadata, :options
|
4
4
|
|
5
5
|
def initialize(attributes = {})
|
6
6
|
attributes.each do |key, value|
|
@@ -9,7 +9,7 @@ module SearchMagic
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def name
|
12
|
-
@name ||= [options[:skip_prefix].presence ? nil : (prefix.present? ? options[:as] || prefix : nil),
|
12
|
+
@name ||= [options[:skip_prefix].presence ? nil : (prefix.present? ? (options[:as] || prefix) : nil),
|
13
13
|
prefix.present? ? field_name : (options[:as] || field_name)].compact.join("_").to_sym
|
14
14
|
end
|
15
15
|
|
data/lib/search_magic/version.rb
CHANGED
data/search_magic.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{SearchMagic provides scoped full text search and sort capabilities to Mongoid documents}
|
13
13
|
s.description = %q{Adds scopes to a Mongoid document providing search and sort capabilities on arbitrary fields and associations.}
|
14
14
|
|
15
|
-
s.add_dependency("mongoid", ">= 2.0.0.rc.
|
15
|
+
s.add_dependency("mongoid", ">= 2.0.0.rc.8")
|
16
16
|
s.add_development_dependency("rspec")
|
17
17
|
s.add_development_dependency("database_cleaner")
|
18
18
|
s.add_development_dependency("bson_ext")
|
@@ -42,31 +42,31 @@ describe SearchMagic::FullTextSearch do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
context "when searching for 'address_city:nowhereland'" do
|
45
|
-
subject { Person.
|
45
|
+
subject { Person.search_for("address_city:nowhereland") }
|
46
46
|
its("selector.keys") { should include(:searchable_values) }
|
47
47
|
its(:count) { should == 1 }
|
48
48
|
its("first.name") { should == "Joshua" }
|
49
49
|
end
|
50
50
|
|
51
51
|
context "when searching for 'arbitrary address_post_code:54321'" do
|
52
|
-
subject { Person.
|
52
|
+
subject { Person.search_for("arbitrary address_post_code:54321") }
|
53
53
|
its(:count) { should == 1 }
|
54
54
|
its("first.name") { should == "Samuel" }
|
55
55
|
end
|
56
56
|
|
57
57
|
context "when searching for 'mobile_number:555-'" do
|
58
|
-
subject { Person.
|
58
|
+
subject { Person.search_for("mobile_number:555-") }
|
59
59
|
its(:count) { should == 2 }
|
60
60
|
end
|
61
61
|
|
62
62
|
context "when searching for 'mobile_number:333-'" do
|
63
|
-
subject { Person.
|
63
|
+
subject { Person.search_for("mobile_number:333-") }
|
64
64
|
its(:count) { should == 1 }
|
65
65
|
its("first.name") { should == "Joshua" }
|
66
66
|
end
|
67
67
|
|
68
68
|
context "when searching for 'mobile_number:-0987'" do
|
69
|
-
subject { Person.
|
69
|
+
subject { Person.search_for("mobile_number:-0987") }
|
70
70
|
its(:count) { should == 1 }
|
71
71
|
its("first.name") { should == "Samuel" }
|
72
72
|
end
|
@@ -105,13 +105,13 @@ describe SearchMagic::FullTextSearch do
|
|
105
105
|
end
|
106
106
|
|
107
107
|
context "when searching for 'category_name:table'" do
|
108
|
-
subject { PartNumber.
|
108
|
+
subject { PartNumber.search_for("category_name:table").map(&:value) }
|
109
109
|
its(:count) { should == 2 }
|
110
110
|
it { should include("T11001", "T11002") }
|
111
111
|
end
|
112
112
|
|
113
113
|
context "when searching for '11001'" do
|
114
|
-
subject { PartNumber.
|
114
|
+
subject { PartNumber.search_for("11001").map(&:value) }
|
115
115
|
its(:count) { should == 2 }
|
116
116
|
it { should include("T11001", "C11001") }
|
117
117
|
end
|
@@ -127,19 +127,19 @@ describe SearchMagic::FullTextSearch do
|
|
127
127
|
end
|
128
128
|
|
129
129
|
context "when searching for 'category_name:table'" do
|
130
|
-
subject { Part.
|
130
|
+
subject { Part.search_for("category_name:table").map(&:serial) }
|
131
131
|
its(:count) { should == 4 }
|
132
132
|
it { should include("T0411001", "T0511010", "T0411037", "T0511178") }
|
133
133
|
end
|
134
134
|
|
135
135
|
context "when searching for 'broken chair'" do
|
136
|
-
subject { Part.
|
136
|
+
subject { Part.search_for("broken chair").map(&:serial) }
|
137
137
|
its(:count) { should == 2 }
|
138
138
|
it { should include("C0511010", "C0511010") }
|
139
139
|
end
|
140
140
|
|
141
141
|
context "when searching for 'part_number:T11001'" do
|
142
|
-
subject { Part.
|
142
|
+
subject { Part.search_for("part_number:T11001").map(&:serial) }
|
143
143
|
its(:count) { should == 2 }
|
144
144
|
it { should include("T0411001", "T0511010") }
|
145
145
|
end
|
@@ -17,7 +17,8 @@ describe SearchMagic::FullTextSearch do
|
|
17
17
|
its(:default) { should == [] }
|
18
18
|
end
|
19
19
|
|
20
|
-
it { should respond_to(:
|
20
|
+
it { should respond_to(:search_for).with(1).argument }
|
21
|
+
it { should respond_to(:and_for).with(1).argument }
|
21
22
|
end
|
22
23
|
|
23
24
|
context "when :search_on called with [:title, :description, :tags]" do
|
@@ -46,7 +47,7 @@ describe SearchMagic::FullTextSearch do
|
|
46
47
|
end
|
47
48
|
|
48
49
|
context "when :search is performed on a model without :searchables" do
|
49
|
-
subject { NoSearchables.
|
50
|
+
subject { NoSearchables.search_for("foo") }
|
50
51
|
it { should be_a(Mongoid::Criteria) }
|
51
52
|
its(:count) { should == 0 }
|
52
53
|
end
|
@@ -59,50 +60,74 @@ describe SearchMagic::FullTextSearch do
|
|
59
60
|
end
|
60
61
|
|
61
62
|
context "when searching for nil" do
|
62
|
-
subject { Asset.
|
63
|
+
subject { Asset.search_for(nil) }
|
63
64
|
it { should be_a(Mongoid::Criteria) }
|
64
65
|
its("selector.keys") { should_not include(:searchable_values) }
|
65
66
|
end
|
66
67
|
|
67
68
|
context "when searching on an empty string" do
|
68
|
-
subject { Asset.
|
69
|
+
subject { Asset.search_for("") }
|
69
70
|
it { should be_a(Mongoid::Criteria) }
|
70
71
|
its("selector.keys") { should_not include(:searchable_values) }
|
71
72
|
end
|
72
73
|
|
73
74
|
context "when searching for anything" do
|
74
|
-
subject { Asset.
|
75
|
+
subject { Asset.search_for("foo") }
|
75
76
|
it { should be_a(Mongoid::Criteria) }
|
76
77
|
its("selector.keys") { should include(:searchable_values) }
|
77
78
|
end
|
78
79
|
|
79
80
|
context "when searching for 'foo'" do
|
80
|
-
subject { Asset.
|
81
|
+
subject { Asset.search_for("foo").map(&:title) }
|
81
82
|
its(:count) { should == 2 }
|
82
83
|
it { should include("Foo Bar: The Bazzening", "Undercover Foo") }
|
83
84
|
end
|
84
85
|
|
85
86
|
context "when searching for 'title:foo'" do
|
86
|
-
subject { Asset.
|
87
|
+
subject { Asset.search_for("title:foo").map(&:title) }
|
87
88
|
its(:count) { should == 2 }
|
88
89
|
it { should include("Foo Bar: The Bazzening", "Undercover Foo") }
|
89
90
|
end
|
90
91
|
|
91
92
|
context "when searching for 'description:bazzening'" do
|
92
|
-
subject { Asset.
|
93
|
+
subject { Asset.search_for("description:bazzening") }
|
93
94
|
its(:count) { should == 0 }
|
94
95
|
end
|
95
96
|
|
96
97
|
context "when searching for 'tag:foo.bar'" do
|
97
|
-
subject { Asset.
|
98
|
+
subject { Asset.search_for("tag:foo.bar").map(&:title) }
|
98
99
|
its(:count) { should == 1 }
|
99
100
|
its(:first) { should == "Foo Bar: The Bazzening" }
|
100
101
|
end
|
101
102
|
|
102
103
|
context "when searching for 'tag:movies cheese" do
|
103
|
-
subject { Asset.
|
104
|
+
subject { Asset.search_for("tag:movies cheese").map(&:title) }
|
104
105
|
its(:count) { should == 1 }
|
105
106
|
its(:first) { should == "Cheese of the Damned" }
|
106
107
|
end
|
108
|
+
|
109
|
+
context "when chaining a search for 'foo' off of :all" do
|
110
|
+
subject { Asset.all.search_for("foo").map(&:title) }
|
111
|
+
its(:count) { should == 2 }
|
112
|
+
it { should include("Foo Bar: The Bazzening", "Undercover Foo") }
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when chaining a search for 'foo' off of a search for 'bar'" do
|
116
|
+
subject { Asset.search_for("bar").search_for("foo").map(&:title) }
|
117
|
+
its(:count) { should == 1 }
|
118
|
+
it { should == ["Foo Bar: The Bazzening"] }
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when chaining a search for 'foo' off of a search for 'bar' using :and_for" do
|
122
|
+
subject { Asset.search_for("bar").and_for("foo").map(&:title) }
|
123
|
+
its(:count) { should == 1 }
|
124
|
+
it { should == ["Foo Bar: The Bazzening"] }
|
125
|
+
end
|
126
|
+
|
127
|
+
context "when chaining a search for 'foo' off of :arrange" do
|
128
|
+
subject { Asset.arrange(:title, :desc).search_for("foo").map(&:title) }
|
129
|
+
its(:count) { should == 2 }
|
130
|
+
it { should include("Undercover Foo", "Foo Bar: The Bazzening") }
|
131
|
+
end
|
107
132
|
end
|
108
133
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
describe SearchMagic::FullTextSearch do
|
2
|
+
context "when included in a document without searchables" do
|
3
|
+
subject { NoSearchables }
|
4
|
+
it { should respond_to(:inverse_searchables) }
|
5
|
+
its(:inverse_searchables) { should be_a(Array)}
|
6
|
+
its(:inverse_searchables) { should be_blank }
|
7
|
+
end
|
8
|
+
|
9
|
+
context "when included in a document which is not searched on from another document" do
|
10
|
+
subject { Person }
|
11
|
+
it { should respond_to(:inverse_searchables) }
|
12
|
+
its(:inverse_searchables) { should be_a(Array)}
|
13
|
+
its(:inverse_searchables) { should be_blank }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when included in a document with embedded searchables" do
|
17
|
+
subject { Address }
|
18
|
+
it { should respond_to(:inverse_searchables) }
|
19
|
+
its(:inverse_searchables) { should be_a(Array) }
|
20
|
+
its(:inverse_searchables) { should_not be_blank }
|
21
|
+
its(:inverse_searchables) { should include(:person) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when included in a document with referenced searchables" do
|
25
|
+
subject { PartNumber }
|
26
|
+
it { should respond_to(:inverse_searchables) }
|
27
|
+
its(:inverse_searchables) { should be_a(Array) }
|
28
|
+
its(:inverse_searchables) { should_not be_blank }
|
29
|
+
its(:inverse_searchables) { should include(:parts) }
|
30
|
+
its(:inverse_searchables) { should_not include(:part_categories) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when included in a document which is searched on by another document, but which does not search on another" do
|
34
|
+
subject { PartCategory }
|
35
|
+
its(:inverse_searchables) { should include(:part_numbers) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when an embedded model is updated" do
|
39
|
+
before(:each) do
|
40
|
+
Person.create(:name => "Joshua", :address => {:street => "123 Example St.", :city => "Nowhereland", :state => "CA", :post_code => 12345}, :phones => [{:country_code => 1, :number => "555-1234"}, {:country_code => 2, :number => "333-7890"}])
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "the top-level document should have its :searchable_values and :arrangeable_values updated" do
|
44
|
+
subject { Person.first }
|
45
|
+
before(:each) { subject.address.update_attributes!(:city => "Nowhereville") && subject.reload }
|
46
|
+
its(:searchable_values) { should include("address_city:nowhereville") }
|
47
|
+
its(:searchable_values) { should_not include("address_city:nowhereland") }
|
48
|
+
its(:arrangeable_values) { should include("address_city" => "Nowhereville") }
|
49
|
+
its(:arrangeable_values) { should_not include("address_city" => "Nowhereland") }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when a referenced model is updated the current model's :searchable_values should change" do
|
54
|
+
before(:each) do
|
55
|
+
PartCategory.create(:name => "Table").tap do |category|
|
56
|
+
category.part_numbers.create(:value => "T11001").tap do |number|
|
57
|
+
number.parts.create(:status => "available", :serial => "T0411001")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "when a model is updated" do
|
63
|
+
subject { PartNumber.first }
|
64
|
+
before(:each) { subject.part_category.update_attributes!(:name => "Desk" ) && subject.reload }
|
65
|
+
its(:searchable_values) { should include("category_name:desk") }
|
66
|
+
its(:searchable_values) { should_not include("category_name:table") }
|
67
|
+
its(:arrangeable_values) { should include("category_name" => "Desk") }
|
68
|
+
its(:arrangeable_values) { should_not include("category_name" => "Table") }
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "when a deeply nested model is updated" do
|
72
|
+
subject { Part.first }
|
73
|
+
before(:each) { subject.part_number.part_category.update_attributes!(:name => "Desk" ) && subject.reload }
|
74
|
+
its(:searchable_values) { should include("category_name:desk") }
|
75
|
+
its(:searchable_values) { should_not include("category_name:table") }
|
76
|
+
its(:arrangeable_values) { should include("category_name" => "Desk") }
|
77
|
+
its(:arrangeable_values) { should_not include("category_name" => "Table") }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Joshua Bowers
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-03-
|
17
|
+
date: 2011-03-28 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -30,8 +30,8 @@ dependencies:
|
|
30
30
|
- 0
|
31
31
|
- 0
|
32
32
|
- rc
|
33
|
-
-
|
34
|
-
version: 2.0.0.rc.
|
33
|
+
- 8
|
34
|
+
version: 2.0.0.rc.8
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- spec/unit/search_magic/arrangements_spec.rb
|
109
109
|
- spec/unit/search_magic/associations_spec.rb
|
110
110
|
- spec/unit/search_magic/fields_spec.rb
|
111
|
+
- spec/unit/search_magic/model_updates_spec.rb
|
111
112
|
has_rdoc: true
|
112
113
|
homepage: http://github.com/joshuabowers/search_magic
|
113
114
|
licenses: []
|
@@ -156,3 +157,4 @@ test_files:
|
|
156
157
|
- spec/unit/search_magic/arrangements_spec.rb
|
157
158
|
- spec/unit/search_magic/associations_spec.rb
|
158
159
|
- spec/unit/search_magic/fields_spec.rb
|
160
|
+
- spec/unit/search_magic/model_updates_spec.rb
|