search_magic 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- search_magic (0.0.8)
4
+ search_magic (0.0.9)
5
5
  mongoid (>= 2.0.0)
6
6
 
7
7
  GEM
data/README.textile CHANGED
@@ -86,7 +86,27 @@ end
86
86
 
87
87
  p. Now all entries within *:searchable_values* for *:tags* will retain meaningful punctuation. The previous example is interesting for another reason: embedded arrays are handled specially. Specifically, the selector for an embedded array will be singularized. In the case of the previous example, this would result in a selector of "tag".
88
88
 
89
- Finally, it should be noted that nesting of searchable documents is possible. (Currently, cyclic searches, where, for documents A and B, A searching on B which also searches on A should be avoided. Doing so is not prohibited, but will likely cause the environment to crash.) If a given document searches on an association with another document which, in and of itself, searches on a third document, the first automatically has access to the third document's searchable fields.
89
+ Two documents may search on each other's fields; doing so will cause each document to only search upon those fields stemming from itself once. Given the following example, _Foo_ would be able to search on @[:name, :bar_value]@, while _Bar_ would be able to search on @[:value, :foo_name]@.
90
+
91
+ bc.. class Foo
92
+ include Mongoid::Document
93
+ include SearchMagic::FullTextSearch
94
+ field :name
95
+ references_many :bars
96
+ search_on :name
97
+ search_on :bars
98
+ end
99
+
100
+ class Bar
101
+ include Mongoid::Document
102
+ include SearchMagic::FullTextSearch
103
+ field :value
104
+ referenced_in :foo
105
+ search_on :value
106
+ search_on :foo
107
+ end
108
+
109
+ Finally, it should be noted that nesting of searchable documents is possible. If a given document searches on an association with another document which, in and of itself, searches on a third document, the first automatically has access to the third document's searchable fields.
90
110
 
91
111
  bc.. class Part
92
112
  include Mongoid::Document
@@ -119,7 +139,7 @@ class PartCategory
119
139
  search_on :name
120
140
  end
121
141
 
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_
142
+ 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_.
123
143
 
124
144
  h3. :search_for
125
145
 
@@ -0,0 +1,20 @@
1
+ module SearchMagic
2
+ class Breadcrumb
3
+ attr_accessor :field_name, :options
4
+
5
+ def initialize(field_name, options)
6
+ self.field_name = field_name
7
+ self.options = options
8
+ self.options[:except] = Array.wrap(self.options[:except]).compact
9
+ self.options[:only] = Array.wrap(self.options[:only]).compact
10
+ end
11
+
12
+ def term
13
+ @term ||= options[:skip_prefix].presence ? nil : (options[:as] || field_name.to_s.pluralize.singularize)
14
+ end
15
+
16
+ def clone
17
+ Breadcrumb.new(field_name, options)
18
+ end
19
+ end
20
+ end
@@ -51,25 +51,24 @@ module SearchMagic
51
51
  private
52
52
 
53
53
  def create_searchables
54
- fields = searchable_fields.map do |field_name, options|
55
- if association = reflect_on_association(field_name)
56
- options[:as] ||= nil
57
- only = [options[:only]].flatten.compact
58
- except = [:_D_E_A_D_B_3_3_F_, options[:except]].flatten.compact
59
- associated = association.class_name.constantize.searchables
60
- wanted = associated.keys.grep(/^(?!.*?(#{except.join("|")})).*/).grep(/^#{only.join("|")}/)
61
- associated.select {|key, value| wanted.include?(key)}.map do |name, metadata|
62
- Metadata.new(:type => self, :through => lambda do |obj|
63
- value = obj.send(field_name)
64
- value.is_a?(Array) ? value.map {|item| metadata.through.call(item)} : metadata.through.call(value)
65
- end, :prefix => field_name.to_s.singularize.to_sym, :field_name => name, :relation_metadata => association, :options => metadata.options.merge(options))
54
+ stack, visited, fields = [StackFrame.new(self)], {}, []
55
+ until stack.empty?
56
+ current = stack.shift
57
+ unless visited.has_key?(current.type)
58
+ visited[current.type] = true
59
+ current.type.searchable_fields.each do |field_name, options|
60
+ next unless current.wants_field?(field_name)
61
+ path = current.path.clone + [Breadcrumb.new(field_name, options)]
62
+ if association = current.type.reflect_on_association(field_name)
63
+ stack << StackFrame.new(association.class_name.constantize, path)
64
+ else
65
+ fields << Metadata.new(:origin_type => current.type, :through => path, :options => options)
66
+ end
66
67
  end
67
- else
68
- Metadata.new(:type => self, :through => lambda {|obj| obj.send(field_name) }, :field_name => field_name.to_s.pluralize.singularize.to_sym, :options => options)
69
68
  end
70
- end.flatten
69
+ end
71
70
 
72
- Hash[*fields.map {|metadata| [metadata.name, metadata]}.flatten]
71
+ fields.index_by(&:name)
73
72
  end
74
73
  end
75
74
 
@@ -1,7 +1,7 @@
1
1
  module SearchMagic
2
2
  class Metadata
3
- attr_accessor :type, :through, :prefix, :field_name, :relation_metadata, :options
4
-
3
+ attr_accessor :origin_type, :through, :options
4
+
5
5
  def initialize(attributes = {})
6
6
  attributes.each do |key, value|
7
7
  send(:"#{key}=", value)
@@ -9,23 +9,28 @@ module SearchMagic
9
9
  end
10
10
 
11
11
  def name
12
- @name ||= [options[:skip_prefix].presence ? nil : (prefix.present? ? (options[:as] || prefix) : nil),
13
- prefix.present? ? field_name : (options[:as] || field_name)].compact.join("_").to_sym
12
+ @name ||= through.map(&:term).compact.join("_").to_sym
14
13
  end
15
14
 
16
15
  def value_for(obj, keep_punctuation)
17
- v = self.through.call(obj)
16
+ v = get_value(obj)
18
17
  v = v.is_a?(Array) ? v.join(" ") : v.to_s
19
18
  v = v.gsub(/[[:punct:]]/, ' ') unless keep_punctuation
20
19
  v
21
20
  end
22
21
 
23
22
  def arrangeable_value_for(obj)
24
- self.through.call(obj)
23
+ get_value(obj)
25
24
  end
26
25
 
27
26
  def searchable_value_for(obj)
28
27
  value_for(obj, options[:keep_punctuation]).downcase.split.map {|word| [name, word].join(":")}
29
28
  end
29
+
30
+ private
31
+
32
+ def get_value(obj)
33
+ self.through.map(&:field_name).inject(obj) {|memo, method| memo.is_a?(Array) ? memo.map{|o| o.send(method)} : memo.send(method)}
34
+ end
30
35
  end
31
36
  end
@@ -0,0 +1,28 @@
1
+ module SearchMagic
2
+ class StackFrame
3
+ attr_accessor :type, :path
4
+
5
+ def initialize(type, path = [])
6
+ self.type = type
7
+ self.path = path
8
+ end
9
+
10
+ def wants_field?(field_name)
11
+ !field_excluded?(field_name) && field_included?(field_name)
12
+ end
13
+
14
+ private
15
+
16
+ def field_included?(field_name)
17
+ options[:only].blank? || options[:only].include?(field_name)
18
+ end
19
+
20
+ def field_excluded?(field_name)
21
+ options[:except].present? && options[:except].include?(field_name)
22
+ end
23
+
24
+ def options
25
+ @options ||= (path.last.try(:options) || {})
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module SearchMagic
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.9"
3
3
  end
data/lib/search_magic.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  module SearchMagic
2
+ require 'search_magic/breadcrumb'
3
+ require 'search_magic/stack_frame'
2
4
  require 'search_magic/metadata'
3
5
  require 'search_magic/full_text_search'
4
6
  end
@@ -0,0 +1,8 @@
1
+ class Graph1ModelA
2
+ include Mongoid::Document
3
+ include SearchMagic::FullTextSearch
4
+ field :foo
5
+ references_and_referenced_in_many :graph_1_model_bs
6
+ search_on :foo
7
+ search_on :graph_1_model_bs
8
+ end
@@ -0,0 +1,8 @@
1
+ class Graph1ModelB
2
+ include Mongoid::Document
3
+ include SearchMagic::FullTextSearch
4
+ field :bar
5
+ references_and_referenced_in_many :graph_1_model_as
6
+ search_on :bar
7
+ search_on :graph_1_model_as
8
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe SearchMagic::FullTextSearch do
4
+ context "when given two models which search on each other" do
5
+ describe "calculating searchables from the first model" do
6
+ subject { Graph1ModelA }
7
+ it("should not raise an error") { expect { subject.searchables }.to_not raise_error }
8
+ its("searchables.keys") { should include(:foo, :graph_1_model_b_bar) }
9
+ its("searchables.keys") { should_not include(:graph_1_model_b_graph_1_model_a, :graph_1_model_b_graph_1_model_a_foo) }
10
+ end
11
+
12
+ describe "calculating searchables from the second model" do
13
+ subject { Graph1ModelB }
14
+ it("should not raise an error") { expect { subject.searchables }.to_not raise_error }
15
+ its("searchables.keys") { should include(:bar, :graph_1_model_a_foo) }
16
+ its("searchables.keys") { should_not include(:graph_1_model_a_graph_1_model_b, :graph_1_model_a_graph_1_model_b_bar) }
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: search_magic
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.8
5
+ version: 0.0.9
6
6
  platform: ruby
7
7
  authors:
8
8
  - Joshua Bowers
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-03 00:00:00 -07:00
13
+ date: 2011-04-05 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -73,8 +73,10 @@ files:
73
73
  - README.textile
74
74
  - Rakefile
75
75
  - lib/search_magic.rb
76
+ - lib/search_magic/breadcrumb.rb
76
77
  - lib/search_magic/full_text_search.rb
77
78
  - lib/search_magic/metadata.rb
79
+ - lib/search_magic/stack_frame.rb
78
80
  - lib/search_magic/version.rb
79
81
  - search_magic.gemspec
80
82
  - spec/models/absolutely_not_searchable.rb
@@ -82,6 +84,8 @@ files:
82
84
  - spec/models/asset.rb
83
85
  - spec/models/developer.rb
84
86
  - spec/models/game.rb
87
+ - spec/models/graph_1_model_a.rb
88
+ - spec/models/graph_1_model_b.rb
85
89
  - spec/models/is_searchable.rb
86
90
  - spec/models/no_searchables.rb
87
91
  - spec/models/part.rb
@@ -94,6 +98,7 @@ files:
94
98
  - spec/unit/search_magic/arrangements_spec.rb
95
99
  - spec/unit/search_magic/associations_spec.rb
96
100
  - spec/unit/search_magic/fields_spec.rb
101
+ - spec/unit/search_magic/graph_searches_spec.rb
97
102
  - spec/unit/search_magic/model_updates_spec.rb
98
103
  has_rdoc: true
99
104
  homepage: http://github.com/joshuabowers/search_magic
@@ -129,6 +134,8 @@ test_files:
129
134
  - spec/models/asset.rb
130
135
  - spec/models/developer.rb
131
136
  - spec/models/game.rb
137
+ - spec/models/graph_1_model_a.rb
138
+ - spec/models/graph_1_model_b.rb
132
139
  - spec/models/is_searchable.rb
133
140
  - spec/models/no_searchables.rb
134
141
  - spec/models/part.rb
@@ -141,4 +148,5 @@ test_files:
141
148
  - spec/unit/search_magic/arrangements_spec.rb
142
149
  - spec/unit/search_magic/associations_spec.rb
143
150
  - spec/unit/search_magic/fields_spec.rb
151
+ - spec/unit/search_magic/graph_searches_spec.rb
144
152
  - spec/unit/search_magic/model_updates_spec.rb