searchtastic 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Readme.md CHANGED
@@ -21,7 +21,7 @@ Make a model searchable by providing an array of attributes to search on:
21
21
  class User < ActiveRecord::Base
22
22
  attr_accessible :name, :bio
23
23
  has_one :club, class_name: Organization
24
- searchable_by %w(name bio club.name)
24
+ searchable_by :name, :bio, :'club.name'
25
25
  end
26
26
  ```
27
27
 
@@ -39,11 +39,25 @@ end
39
39
  ...
40
40
  ```
41
41
 
42
+ Note that this works for any ActiveRelation so this will work as well:
43
+
44
+ ```ruby
45
+ ...
46
+ def index
47
+ #@filter == "pete"
48
+ @users = @foo.users.search(@filter)
49
+ ...
50
+ end
51
+ ...
52
+ ```
53
+
42
54
  # To Do
43
55
 
44
- * Search on Dates -- there's no good way to do this right now
45
56
  * Search on combined fields, e.g. first_name + last_name
46
57
  * Search on all accessible attributes shorthand
58
+ * Search on Date Ranges
59
+ * Search against has_many :through associations
60
+ * Search against HABTM associations
47
61
 
48
62
  # Contributors
49
63
 
data/lib/searchtastic.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'searchtastic/version'
2
+ require 'chronic'
2
3
 
3
4
  # This module is automatically included in all ActiveRecords
4
5
  module Searchtastic
@@ -24,8 +25,10 @@ module Searchtastic
24
25
  # @filtered_models = User.search(filter)
25
26
  #
26
27
  def search filter
27
- if filter && self.fields.count
28
- handle_joins(self.fields).where(build_filter(filter, self.fields))
28
+ if filter && is_searchable?
29
+ filter = [filter, Chronic.parse(filter).strftime("%Y-%m-%d")] rescue filter
30
+ handle_joins(self.fields, scoped.select("DISTINCT(`#{self.table_name}`.`id`), `#{self.table_name}`.*"))
31
+ .where(build_filter(filter, self.fields))
29
32
  else
30
33
  scoped
31
34
  end
@@ -36,7 +39,7 @@ module Searchtastic
36
39
  # User.search(@filter) if User.is_searchable?
37
40
  #
38
41
  def is_searchable?
39
- self.fields.count < 0
42
+ self.fields.count > 0
40
43
  end
41
44
 
42
45
  private
@@ -59,41 +62,64 @@ module Searchtastic
59
62
  # Before adding the where clauses, we have to make sure the right tables are joined
60
63
  # into the query. We use .includes() instead of .joins() so we can get an OUTER JOIN
61
64
  #
62
- def handle_joins fields
63
- ret = scoped
64
- fields.each do |field|
65
- assoc, table = field_table(field)
66
- ret = ret.includes(assoc) unless table == self.table_name.to_sym
65
+ def handle_joins fields, select = nil
66
+ ret = select || scoped
67
+ fields.each do |qualified_field|
68
+ assoc, foreign_table, field = parse_field(qualified_field)
69
+ ret = ret.joins(join_string(assoc, foreign_table)) if assoc
67
70
  end
68
71
  ret
69
72
  end
70
73
 
74
+ def join_string(assoc, foreign_table)
75
+ reflection = self.reflect_on_association(assoc.to_sym)
76
+ case reflection.macro
77
+ when :belongs_to
78
+ "LEFT OUTER JOIN `#{foreign_table}` on `#{foreign_table}`.`id` = `#{self.table_name}`.`#{assoc.singularize}_id`"
79
+ when :has_one
80
+ if reflection.options.has_key?(:through)
81
+ "LEFT OUTER JOIN `#{reflection.options[:through].pluralize}` on `#{reflection.options[:through].pluralize}`.`id` = `#{self.table_name}`.`#{reflection.options[:through]}_id` "+
82
+ "LEFT OUTER JOIN `#{foreign_table}` on `#{foreign_table}`.`id` = `#{reflection.options[:through].pluralize}`.`#{assoc.singularize}_id`"
83
+ else
84
+ "LEFT OUTER JOIN `#{foreign_table}` on `#{foreign_table}`.`id` = `#{self.table_name}`.`#{assoc.singularize}_id`"
85
+ end
86
+ when :has_many
87
+ "LEFT OUTER JOIN `#{foreign_table}` on `#{self.table_name}`.`id` = `#{foreign_table}`.`#{self.table_name.singularize}_id`"
88
+ #when :has_and_belongs_to_many
89
+ else
90
+ raise "Searching against HABTM associations is not supported"
91
+ end
92
+ end
93
+
71
94
  # Get table name from association name
72
95
  #
73
96
  # class User < ActiveRecord::Base
74
97
  # belongs_to :company, class_name: Organization
75
- # searchable_by %w(name, email, company.name)
98
+ # searchable_by :name, :email, :'company.name'
76
99
  # end
77
100
  #
78
- # field_table('company.name') => [:companies, :organizations]
101
+ # parse_field('company.name') => ['companies', 'organizations', 'name']
79
102
  #
80
- def field_table(field)
81
- if field.include? '.'
82
- assoc = field.split('.').first
103
+ def parse_field(qualified_field)
104
+ if qualified_field.include? '.'
105
+ assoc, field = qualified_field.split('.')
83
106
  if assoc != self.table_name
84
- return assoc.to_sym, self.reflect_on_association(assoc.to_sym).table_name.to_sym
107
+ return assoc, self.reflect_on_association(assoc.to_sym).table_name, field
85
108
  end
86
109
  end
87
110
 
88
- return nil, self.table_name
111
+ return nil, self.table_name, field
89
112
 
90
113
  end
91
114
 
92
115
  # Build the filter for the .where() clause in the search method
93
116
  #
94
- def build_filter filter, fields
95
- where = [associations_to_tables(fields).map { |f| "#{f} like ?" }.join(" || ")]
96
- fields.count.times { |n| where << "%#{filter}%" }
117
+ def build_filter filters, fields
118
+ filters = [filters] unless filters.is_a? Array
119
+ where = [Array.new(filters.count, associations_to_tables(fields).map { |f| "#{f} LIKE ?" }.join(" || ")).join(" || ")]
120
+ filters.each do |filter|
121
+ fields.count.times { |n| where << "%#{filter}%" }
122
+ end
97
123
  where
98
124
  end
99
125
 
@@ -102,7 +128,7 @@ module Searchtastic
102
128
  def associations_to_tables fields
103
129
  fields.map do |field|
104
130
  _, column = field.split('.')
105
- "#{field_table(field)[1]}.#{column}"
131
+ "#{parse_field(field)[1]}.#{column}"
106
132
  end
107
133
  end
108
134
 
@@ -1,3 +1,3 @@
1
1
  module Searchtastic
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
data/searchtastic.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.require_paths = ["config", "lib"]
18
18
 
19
19
  #s.add_dependency 'activesupport', '~> 3.2'
20
- #s.add_dependency 'actionpack', '~> 3.2'
20
+ s.add_dependency 'chronic', '~> 0.9'
21
21
 
22
22
  s.add_development_dependency 'rake', '~> 0.9.2'
23
23
  s.add_development_dependency 'rspec', '~> 2.10'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-17 00:00:00.000000000 Z
12
+ date: 2013-02-20 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chronic
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.9'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: rake
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -112,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
128
  version: '0'
113
129
  requirements: []
114
130
  rubyforge_project: searchtastic
115
- rubygems_version: 1.8.24
131
+ rubygems_version: 1.8.25
116
132
  signing_key:
117
133
  specification_version: 3
118
134
  summary: Enables ActiveRecord Model Searching