Returns a filtered, by named scopes, result set using filter params.
The result is automatically filtered using params[:filter_*] type params where * would be the name of the named_scoped to apply. Multiple named_scopes can be applied simultaniously.
You can also restrict the search to specific scopes. Examples:
>> Book.paginated_filtered_search(params) >> Book.paginated_filtered_search(params, :scopes => [ :text ])
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 84
84: def filtered_search(params = {}, options = {})
85: scopes = select_scopes(params, options[:scopes])
86: scopes.inject(self) do |results, pair|
87: pair.last.blank? ? results : results.send(pair.first, pair.last)
88: end.all(options.except(:scopes))
89: end
This macro adds two class methods to facilitate search and filtering within a model: Example:
>> Book.paginated_filtered_search(params) >> Book.filtered_search(params)
filter_* like params are matched against the avaliable named_scopes (removing the filter_ part) and applied.
You can also enable and disable scopes and activate o deactivate default scopes (which are created automatically). Example:
>> filtered_search_scopes # By default all scopes are enabled >> filtered_search_scopes :defaults => false # Disable auto-generated scopes >> filtered_search_scopes :enable => [ :text, :published_start ,:publish_end ], :defaults => false
It is also possible to define which fields should be affected by a text search (filter_text param matched against a dinamically generated text named_scope):
>> filtered_search_scopes :text => [ :title, :description ]
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 36
36: def filtered_search_scopes(options = {})
37: options.reverse_merge!({:defaults => true})
38: @enabled_scopes = options[:enable] || []
39: text_scope(options[:text]) if (options[:defaults] || options[:text])
40: if options[:defaults]
41: published_at_scopes
42: @enabled_scopes << :locale
43: end
44: end
Returns a paginated and filtered result set. You can use params[:page] to specify which page to retrieve.
It also takes into account order_by and sort_order params. Check filtered_search for more details
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 51
51: def paginated_filtered_search(params = {}, options = {})
52: order_by = params[:order_by] || options[:order_by] || "#{table_name}.id"
53: sort_order = params[:sort_order] || options[:sort_order] || 'desc'
54:
55: order_by_segments = order_by.split('.')
56:
57: # To deal with relation columns. Ex: :'author.name'
58: # We need to deal with a special case with categories
59: if order_by_segments.size > 2
60: table, assoc, column = order_by_segments
61: assoc_table = (self.reflections[assoc.to_sym] ||
62: self.reflections[assoc.pluralize.to_sym]).table_name
63: options[:include] = assoc_table == 'categories' ? assoc.pluralize : assoc
64: order_by = "#{assoc_table}.#{column}"
65: end
66:
67: options[:order] = "#{order_by} #{sort_order}"
68:
69: ubiquo_paginate(:page => params[:page]) do
70: filtered_search params, options.except(:order_by, :sort_order)
71: end
72: end
(Not documented)
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 130
130: def accent_insensitive_regexp(text)
131: regexps = ["(a|á|à|â|ã|A|Á|À|Â|Ã)", "(e|é|è|ê|E|É|È|Ê)", "(i|í|ì|I|Í|Ì)", "(o|ó|ò|ô|õ|O|Ó|Ò|Ô|Õ)", "(u|ú|ù|U|Ú|Ù)", "(c|ç|C|Ç)", "(ñ|Ñ)"]
132: regexps.each { |exp| text.gsub! Regexp.new(exp), exp }
133: text
134: end
Defines the default columns to search based on existing columns. Currently it uses title, name and description
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 123
123: def default_text_fields
124: ["title", "name", "description"].inject([]) do |result, text_field|
125: result << text_field if table_exists? && column_names.include?(text_field)
126: result
127: end
128: end
Defines publish_start and publish_end named scopes if the published_at column exists
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 112
112: def published_at_scopes
113: # TODO: End and removal of parse_date, see I18n.parse_date
114: if table_exists? && column_names.include?("published_at")
115: @enabled_scopes.concat [ :publish_start, :publish_end ]
116: named_scope :publish_start , lambda { |value| { :conditions => ["#{table_name}.published_at >= ?", parse_date(value)] } }
117: named_scope :publish_end , lambda { |value| { :conditions => ["#{table_name}.published_at <= ?", parse_date(value, :time_offset => 1.day)] } }
118: end
119: end
Returns an array of valid scopes filtering out invalid ones
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 137
137: def select_scopes(params,restrict_scopes)
138: filters = params.reject {|k,v| !k.to_s.match /^filter_/ }
139: scopes = filters.inject({}) { |h, (k,v)| h[k.gsub('filter_', '').to_sym] = v; h } # transform filter_bla keys into :bla
140: valid_scopes = restrict_scopes || @enabled_scopes
141: rogue_filters = scopes.keys - valid_scopes
142: unless rogue_filters.blank?
143: raise Ubiquo::InvalidFilter, "Unexpected filter received in params: #{rogue_filters.inspect}"
144: end
145: scopes
146: end
Defines a named_scope for text search using the received fields or the default ones.
It uses a regexp based and case insensitive and accent insensitive search.
# File vendor/plugins/ubiquo_core/lib/ubiquo/filtered_search.rb, line 98
98: def text_scope(selected_fields)
99: fields = selected_fields || default_text_fields
100: regexp_op = connection.adapter_name == "PostgreSQL" ? "~*" : "REGEXP"
101: @enabled_scopes.concat [:text]
102: named_scope :text, lambda { |value|
103: match = accent_insensitive_regexp(value.downcase)
104: matches = fields.inject([]) { |r, f| r << match }
105: conditions = fields.map { |f| "lower(#{table_name}.#{f}) #{regexp_op} ?" }.join(" OR ")
106: { :conditions => [ conditions, *matches ] }
107: }
108: end
Disabled; run with $DEBUG to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.