search_magic 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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