|
5 | 5 | module MetaSearch
|
6 | 6 | module Searches
|
7 | 7 |
|
8 |
| - module Mongoid |
9 |
| - extend ActiveSupport::Concern |
| 8 | + require 'delegate' |
| 9 | + class MongoidSearchBuilder < SimpleDelegator |
| 10 | + def initialize relation, params, options |
| 11 | + super(relation) |
| 12 | + @relation = relation |
| 13 | + @params, @options = params, options |
| 14 | + end |
10 | 15 |
|
11 |
| - def method_missing? name, *args, &block |
12 |
| - if name =~ metasearch_regexp |
13 |
| - field_name, condition = $1, $2 |
14 |
| - raise [field_name, condition].inspect |
15 |
| - else |
16 |
| - super |
| 16 | + def build |
| 17 | + params.each_pair do |field_query, value| |
| 18 | + field, query = field_query.scan(metasearch_regexp).first |
| 19 | + case query.to_sym |
| 20 | + when :contains |
| 21 | + @relation = relation.where(field => /#{value}/) |
| 22 | + else |
| 23 | + raise [field_query, value].inspect |
| 24 | + end |
17 | 25 | end
|
| 26 | + self |
| 27 | + end |
| 28 | + |
| 29 | + attr_reader :relation, :params, :options |
| 30 | + |
| 31 | + def base |
| 32 | + self |
18 | 33 | end
|
19 | 34 |
|
| 35 | + def klass |
| 36 | + relation |
| 37 | + end |
| 38 | + |
| 39 | + def method_missing name, *attrs, &block |
| 40 | + relation.send(name, *attrs, &block) |
| 41 | + end |
| 42 | + |
| 43 | + |
20 | 44 | def respond_to? name, include_private = false
|
21 |
| - name =~ metasearch_regexp or super |
| 45 | + name.to_s =~ metasearch_regexp or super |
22 | 46 | end
|
23 | 47 |
|
24 |
| - module ClassMethods |
25 |
| - def metasearch(params = nil, options = nil) |
26 |
| - options ||= {} |
27 |
| - params ||= {} |
28 |
| - raise [options, params] unless [options, params].all?(&:empty?) |
29 |
| - scoped |
| 48 | + def method_missing name, *args, &block |
| 49 | + if name =~ metasearch_regexp |
| 50 | + params[name] |
| 51 | + else |
| 52 | + super |
30 | 53 | end
|
31 | 54 | end
|
32 | 55 |
|
33 | 56 | def metasearch_regexp
|
34 |
| - @metasearch_regexp ||= begin |
35 |
| - field_names = fields.map(&:last).map(&:name) |
| 57 | + field_names = klass.content_columns.map(&:name) |
36 | 58 |
|
37 |
| - # general_conditions = %w[eq] |
38 |
| - # string_conditions = %w[starts_with ends_with contains] |
39 |
| - # id_conditions = [] |
40 |
| - # checkbox_conditions = %w[in] |
41 |
| - # range_conditions = %w[gte lte] |
42 |
| - # number_conditions = %w[numeric] |
43 |
| - # boolean_conditions = %w[is_true is_false is_present is_blank is_null is_not_null] |
| 59 | + conditions = MetaSearch::DEFAULT_WHERES.map {|condition| condition[0...-1]} # pop tail options |
| 60 | + |
| 61 | + /\A(#{field_names.join('|')})_(#{conditions.join('|')})/ |
| 62 | + end |
| 63 | + |
| 64 | + end |
44 | 65 |
|
45 |
| - conditions = MetaSearch::DEFAULT_WHERES.map {|condition| condition[0...-1]} # pop tail options |
| 66 | + module Mongoid |
| 67 | + extend ActiveSupport::Concern |
46 | 68 |
|
47 |
| - /\A(#{field_names.join('|')})_(#{conditions.join('|')})/ |
| 69 | + module ClassMethods |
| 70 | + def metasearch(params = nil, options = nil) |
| 71 | + options ||= {} |
| 72 | + params ||= {} |
| 73 | + MongoidSearchBuilder.new(self, params, options).build |
| 74 | + # @metasearch_query |
| 75 | + # raise [params, options].inspect unless [options, params].all?(&:empty?) |
| 76 | + # scoped |
48 | 77 | end
|
| 78 | + alias_method :search, :metasearch unless respond_to?(:search) |
49 | 79 | end
|
50 |
| - end |
51 | 80 |
|
52 |
| - # module Mongoid |
53 |
| - # |
54 |
| - # def self.included(base) |
55 |
| - # base.extend ClassMethods |
56 |
| - # |
57 |
| - # base.class_eval do |
58 |
| - # class_attribute :_metasearch_include_attributes, :_metasearch_exclude_attributes |
59 |
| - # class_attribute :_metasearch_include_associations, :_metasearch_exclude_associations |
60 |
| - # class_attribute :_metasearch_methods |
61 |
| - # self._metasearch_include_attributes = |
62 |
| - # self._metasearch_exclude_attributes = |
63 |
| - # self._metasearch_exclude_associations = |
64 |
| - # self._metasearch_include_associations = {} |
65 |
| - # self._metasearch_methods = {} |
66 |
| - # end |
67 |
| - # end |
68 |
| - # |
69 |
| - # module ClassMethods |
70 |
| - # # Prepares the search to run against your model. Returns an instance of |
71 |
| - # # MetaSearch::Builder, which behaves pretty much like an ActiveRecord::Relation, |
72 |
| - # # in that it doesn't actually query the database until you do something that |
73 |
| - # # requires it to do so. |
74 |
| - # # |
75 |
| - # # Options: |
76 |
| - # # |
77 |
| - # # * +params+ - a hash of valid searches with keys that are valid according to |
78 |
| - # # the docs in MetaSearch::Where. |
79 |
| - # # * +opts+ - A hash of additional information that will be passed through to |
80 |
| - # # the search's Builder object. +search_key+, if present, will override the |
81 |
| - # # default param name, 'search', in any sort_links generated by this Builder. |
82 |
| - # # All other keys are passed untouched to the builder, and available from the |
83 |
| - # # Builder's +options+ reader for use in :if blocks supplied to attr_searchable |
84 |
| - # # and friends. |
85 |
| - # def metasearch(params = nil, opts = nil) |
86 |
| - # builder = Searches.for(self).new(self, opts || {}) |
87 |
| - # builder.build(params || {}) |
88 |
| - # end |
89 |
| - # |
90 |
| - # alias_method :search, :metasearch unless respond_to?(:search) |
91 |
| - # |
92 |
| - # def _metasearch_method_authorized?(name, metasearch_object) |
93 |
| - # name = name.to_s |
94 |
| - # meth = self._metasearch_methods[name] |
95 |
| - # meth && (meth[:if] ? meth[:if].call(metasearch_object) : true) |
96 |
| - # end |
97 |
| - # |
98 |
| - # def _metasearch_attribute_authorized?(name, metasearch_object) |
99 |
| - # name = name.to_s |
100 |
| - # if self._metasearch_include_attributes.empty? |
101 |
| - # !_metasearch_excludes_attribute?(name, metasearch_object) |
102 |
| - # else |
103 |
| - # _metasearch_includes_attribute?(name, metasearch_object) |
104 |
| - # end |
105 |
| - # end |
106 |
| - # |
107 |
| - # def _metasearch_association_authorized?(name, metasearch_object) |
108 |
| - # name = name.to_s |
109 |
| - # if self._metasearch_include_associations.empty? |
110 |
| - # !_metasearch_excludes_association?(name, metasearch_object) |
111 |
| - # else |
112 |
| - # _metasearch_includes_association?(name, metasearch_object) |
113 |
| - # end |
114 |
| - # end |
115 |
| - # |
116 |
| - # private |
117 |
| - # |
118 |
| - # # Excludes model attributes from searchability. This means that searches can't be created against |
119 |
| - # # these columns, whether the search is based on this model, or the model's attributes are being |
120 |
| - # # searched by association from another model. If a Comment <tt>belongs_to :article</tt> but declares |
121 |
| - # # <tt>attr_unsearchable :user_id</tt> then <tt>Comment.search</tt> won't accept parameters |
122 |
| - # # like <tt>:user_id_equals</tt>, nor will an Article.search accept the parameter |
123 |
| - # # <tt>:comments_user_id_equals</tt>. |
124 |
| - # def attr_unsearchable(*args) |
125 |
| - # if table_exists? |
126 |
| - # opts = args.extract_options! |
127 |
| - # args.flatten.each do |attr| |
128 |
| - # attr = attr.to_s |
129 |
| - # raise(ArgumentError, "No persisted attribute (column) named #{attr} in #{self}") unless self.columns_hash.has_key?(attr) |
130 |
| - # self._metasearch_exclude_attributes = self._metasearch_exclude_attributes.merge( |
131 |
| - # attr => { |
132 |
| - # :if => opts[:if] |
133 |
| - # } |
134 |
| - # ) |
135 |
| - # end |
136 |
| - # end |
137 |
| - # end |
138 |
| - # |
139 |
| - # # Like <tt>attr_unsearchable</tt>, but operates as a whitelist rather than blacklist. If both |
140 |
| - # # <tt>attr_searchable</tt> and <tt>attr_unsearchable</tt> are present, the latter |
141 |
| - # # is ignored. |
142 |
| - # def attr_searchable(*args) |
143 |
| - # if table_exists? |
144 |
| - # opts = args.extract_options! |
145 |
| - # args.flatten.each do |attr| |
146 |
| - # attr = attr.to_s |
147 |
| - # raise(ArgumentError, "No persisted attribute (column) named #{attr} in #{self}") unless self.columns_hash.has_key?(attr) |
148 |
| - # self._metasearch_include_attributes = self._metasearch_include_attributes.merge( |
149 |
| - # attr => { |
150 |
| - # :if => opts[:if] |
151 |
| - # } |
152 |
| - # ) |
153 |
| - # end |
154 |
| - # end |
155 |
| - # end |
156 |
| - # |
157 |
| - # # Excludes model associations from searchability. This mean that searches can't be created against |
158 |
| - # # these associations. An article that <tt>has_many :comments</tt> but excludes comments from |
159 |
| - # # searching by declaring <tt>assoc_unsearchable :comments</tt> won't make any of the |
160 |
| - # # <tt>comments_*</tt> methods available. |
161 |
| - # def assoc_unsearchable(*args) |
162 |
| - # opts = args.extract_options! |
163 |
| - # args.flatten.each do |assoc| |
164 |
| - # assoc = assoc.to_s |
165 |
| - # raise(ArgumentError, "No such association #{assoc} in #{self}") unless self.reflect_on_all_associations.map {|a| a.name.to_s}.include?(assoc) |
166 |
| - # self._metasearch_exclude_associations = self._metasearch_exclude_associations.merge( |
167 |
| - # assoc => { |
168 |
| - # :if => opts[:if] |
169 |
| - # } |
170 |
| - # ) |
171 |
| - # end |
172 |
| - # end |
173 |
| - # |
174 |
| - # # As with <tt>attr_searchable</tt> this is the whitelist version of |
175 |
| - # # <tt>assoc_unsearchable</tt> |
176 |
| - # def assoc_searchable(*args) |
177 |
| - # opts = args.extract_options! |
178 |
| - # args.flatten.each do |assoc| |
179 |
| - # assoc = assoc.to_s |
180 |
| - # raise(ArgumentError, "No such association #{assoc} in #{self}") unless self.reflect_on_all_associations.map {|a| a.name.to_s}.include?(assoc) |
181 |
| - # self._metasearch_include_associations = self._metasearch_include_associations.merge( |
182 |
| - # assoc => { |
183 |
| - # :if => opts[:if] |
184 |
| - # } |
185 |
| - # ) |
186 |
| - # end |
187 |
| - # end |
188 |
| - # |
189 |
| - # def search_methods(*args) |
190 |
| - # opts = args.extract_options! |
191 |
| - # authorizer = opts.delete(:if) |
192 |
| - # args.flatten.map(&:to_s).each do |arg| |
193 |
| - # self._metasearch_methods = self._metasearch_methods.merge( |
194 |
| - # arg => { |
195 |
| - # :method => MetaSearch::Method.new(arg, opts), |
196 |
| - # :if => authorizer |
197 |
| - # } |
198 |
| - # ) |
199 |
| - # end |
200 |
| - # end |
201 |
| - # |
202 |
| - # alias_method :search_method, :search_methods |
203 |
| - # |
204 |
| - # def _metasearch_includes_attribute?(name, metasearch_object) |
205 |
| - # attr = self._metasearch_include_attributes[name] |
206 |
| - # attr && (attr[:if] ? attr[:if].call(metasearch_object) : true) |
207 |
| - # end |
208 |
| - # |
209 |
| - # def _metasearch_excludes_attribute?(name, metasearch_object) |
210 |
| - # attr = self._metasearch_exclude_attributes[name] |
211 |
| - # attr && (attr[:if] ? attr[:if].call(metasearch_object) : true) |
212 |
| - # end |
213 |
| - # |
214 |
| - # def _metasearch_includes_association?(name, metasearch_object) |
215 |
| - # assoc = self._metasearch_include_associations[name] |
216 |
| - # assoc && (assoc[:if] ? assoc[:if].call(metasearch_object) : true) |
217 |
| - # end |
218 |
| - # |
219 |
| - # def _metasearch_excludes_association?(name, metasearch_object) |
220 |
| - # assoc = self._metasearch_exclude_associations[name] |
221 |
| - # assoc && (assoc[:if] ? assoc[:if].call(metasearch_object) : true) |
222 |
| - # end |
223 |
| - # |
224 |
| - # end |
225 |
| - # end |
226 |
| - # |
227 |
| - # def self.for(klass) |
228 |
| - # DISPATCH[klass.name] |
229 |
| - # end |
| 81 | + end |
230 | 82 | end
|
231 | 83 | end
|
0 commit comments