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 CHANGED
@@ -1,17 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- search_magic (0.0.5)
5
- mongoid (>= 2.0.0.rc.7)
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.4)
11
- activesupport (= 3.0.4)
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.4)
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.7)
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.24)
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.7)
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_. For environments where bundler is being used, it can be installed by adding the following to your Gemfile and running @bundle@.
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 _*:search*_ method:
36
+ p. At this point, *Address* can be searched by calling its _*:search_for*_ method:
33
37
 
34
- bc. Address.search("state:ca")
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. :search
124
+ h3. :search_for
120
125
 
121
- Searching a model with SearchMagic is simple: each model gains a class method called _:search_ 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.
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.search("table") # full text search on "table"
128
- Part.search("category_name:table") # restricts the search for "table" to "category_name"
129
- Part.search("bike serial:b1234") # full text search on "bike", with an extra requirement that the serial be "b1234"
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.search("category_name:table").arrange(:serial, :asc)
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 search(pattern)
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
- module InstanceMethods
66
- private
67
-
68
- def update_searchable_values
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 self.included(receiver)
81
- receiver.extend ClassMethods
82
- receiver.send :include, InstanceMethods
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
 
@@ -1,3 +1,3 @@
1
1
  module SearchMagic
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
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.7")
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.search("address_city:nowhereland") }
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.search("arbitrary address_post_code:54321") }
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.search("mobile_number:555-") }
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.search("mobile_number:333-") }
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.search("mobile_number:-0987") }
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.search("category_name:table").map(&:value) }
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.search("11001").map(&:value) }
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.search("category_name:table").map(&:serial) }
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.search("broken chair").map(&:serial) }
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.search("part_number:T11001").map(&:serial) }
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(:search).with(1).argument }
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.search("foo") }
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.search(nil) }
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.search("") }
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.search("foo") }
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.search("foo").map(&:title) }
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.search("title:foo").map(&:title) }
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.search("description:bazzening") }
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.search("tag:foo.bar").map(&:title) }
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.search("tag:movies cheese").map(&:title) }
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
- - 6
9
- version: 0.0.6
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-20 00:00:00 -07:00
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
- - 7
34
- version: 2.0.0.rc.7
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