searchtastic 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/Rakefile +17 -0
- data/Readme.md +67 -0
- data/lib/searchtastic.rb +112 -0
- data/lib/searchtastic/version.rb +3 -0
- data/searchtastic.gemspec +27 -0
- data/spec/spec_helper.rb +2 -0
- metadata +120 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
namespace :spec do
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:normal) do |t|
|
8
|
+
t.pattern ='spec/**/*_spec.rb'
|
9
|
+
t.rcov = false
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "RSpec tests"
|
15
|
+
task "spec" => "spec:normal"
|
16
|
+
|
17
|
+
task "default" => "spec"
|
data/Readme.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Searchtastic
|
2
|
+
|
3
|
+
Searchtastic makes it possible to filter any collection of ActiveRecord models automatically based on any field,
|
4
|
+
including associated fields not even on the model itself. It's kind of awesome.
|
5
|
+
|
6
|
+
# Installation
|
7
|
+
|
8
|
+
In Rails 3, add this to your Gemfile and run the `bundle` command.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'searchtastic'
|
12
|
+
```
|
13
|
+
|
14
|
+
# Basic Usage
|
15
|
+
|
16
|
+
### 1. Set up Your Models
|
17
|
+
|
18
|
+
Make a model searchable by providing an array of attributes to search on:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
class User < ActiveRecord::Base
|
22
|
+
attr_accessible :name, :bio
|
23
|
+
has_one :club, class_name: Organization
|
24
|
+
searchable_by %w(name bio club.name)
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
### 2. Perform the Search
|
29
|
+
|
30
|
+
Searching works anywhere, but normally you'll search from within a controller action:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
...
|
34
|
+
def index
|
35
|
+
#@filter == "test"
|
36
|
+
@users = User.search(@filter)
|
37
|
+
...
|
38
|
+
end
|
39
|
+
...
|
40
|
+
```
|
41
|
+
|
42
|
+
# To Do
|
43
|
+
|
44
|
+
* Search on Dates -- there's no good way to do this right now
|
45
|
+
* Search on combined fields, e.g. first_name + last_name
|
46
|
+
* Search on all accessible attributes shorthand
|
47
|
+
|
48
|
+
# Contributors
|
49
|
+
|
50
|
+
`searchtastic` is solely Pete Michaud's (me@petermichaud.com) fault, so blame him for everything.
|
51
|
+
|
52
|
+
# License
|
53
|
+
|
54
|
+
(The MIT License)
|
55
|
+
|
56
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
57
|
+
documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the
|
58
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
59
|
+
persons to whom the Software is furnished to do so, subject to the following conditions:
|
60
|
+
|
61
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
62
|
+
Software.
|
63
|
+
|
64
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
65
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
66
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
67
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/searchtastic.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'searchtastic/version'
|
2
|
+
|
3
|
+
# This module is automatically included in all ActiveRecords
|
4
|
+
module Searchtastic
|
5
|
+
module ActiveRecord
|
6
|
+
|
7
|
+
# Array of fields upon which it's possible to search
|
8
|
+
def fields
|
9
|
+
@fields || []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the searchable fields for a given model
|
13
|
+
# class User < ActiveRecord::Base
|
14
|
+
# search_fields %w(name email bio)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
def searchable_by *fields
|
18
|
+
self.fields = process_fields(fields)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Called on an ActiveRelation containing the model
|
22
|
+
#
|
23
|
+
# filter = "Pete"
|
24
|
+
# @filtered_models = User.search(filter)
|
25
|
+
#
|
26
|
+
def search filter
|
27
|
+
if filter && self.fields.count
|
28
|
+
handle_joins(self.fields).where(build_filter(filter, self.fields))
|
29
|
+
else
|
30
|
+
scoped
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Callable on an instance or the call itself to detect whether .search() will have meaningful results
|
35
|
+
#
|
36
|
+
# User.search(@filter) if User.is_searchable?
|
37
|
+
#
|
38
|
+
def is_searchable?
|
39
|
+
self.fields.count < 0
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Set fields -- private, see searchable_by
|
45
|
+
def fields=(value)
|
46
|
+
@fields = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Fields can be specified with or without the model table name, but internally all
|
50
|
+
# fields need to be qualified by table name for disambiguation
|
51
|
+
#
|
52
|
+
# fields = %w(name email)
|
53
|
+
# process_fields(fields) => %w(users.name users.email)
|
54
|
+
#
|
55
|
+
def process_fields(fields)
|
56
|
+
fields.map { |field| field.to_s.include?('.') ? field.to_s : "#{self.table_name}.#{field}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Before adding the where clauses, we have to make sure the right tables are joined
|
60
|
+
# into the query. We use .includes() instead of .joins() so we can get an OUTER JOIN
|
61
|
+
#
|
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
|
67
|
+
end
|
68
|
+
ret
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get table name from association name
|
72
|
+
#
|
73
|
+
# class User < ActiveRecord::Base
|
74
|
+
# belongs_to :company, class_name: Organization
|
75
|
+
# searchable_by %w(name, email, company.name)
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# field_table('company.name') => [:companies, :organizations]
|
79
|
+
#
|
80
|
+
def field_table(field)
|
81
|
+
if field.include? '.'
|
82
|
+
assoc = field.split('.').first
|
83
|
+
if assoc != self.table_name
|
84
|
+
return assoc.to_sym, self.reflect_on_association(assoc.to_sym).table_name.to_sym
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
return nil, self.table_name
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Build the filter for the .where() clause in the search method
|
93
|
+
#
|
94
|
+
def build_filter filter, fields
|
95
|
+
where = [associations_to_tables(fields).map { |f| "#{f} like ?" }.join(" || ")]
|
96
|
+
fields.count.times { |n| where << "%#{filter}%" }
|
97
|
+
where
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set the field to the right table and column name for the database
|
101
|
+
#
|
102
|
+
def associations_to_tables fields
|
103
|
+
fields.map do |field|
|
104
|
+
_, column = field.split('.')
|
105
|
+
"#{field_table(field)[1]}.#{column}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end #ActiveRecord
|
110
|
+
end #Searchtastic
|
111
|
+
|
112
|
+
ActiveRecord::Base.extend Searchtastic::ActiveRecord
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "searchtastic/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "searchtastic"
|
7
|
+
s.version = Searchtastic::VERSION
|
8
|
+
s.authors = ["Pete Michaud"]
|
9
|
+
s.email = ["me@petermichaud.com"]
|
10
|
+
s.homepage = "http://github.com/PeteMichaud/searchtastic"
|
11
|
+
s.summary = "Enables ActiveRecord Model Searching"
|
12
|
+
s.description = "Enables ActiveRecord Model Searching"
|
13
|
+
s.rubyforge_project = "searchtastic"
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["config", "lib"]
|
18
|
+
|
19
|
+
#s.add_dependency 'activesupport', '~> 3.2'
|
20
|
+
#s.add_dependency 'actionpack', '~> 3.2'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rake', '~> 0.9.2'
|
23
|
+
s.add_development_dependency 'rspec', '~> 2.10'
|
24
|
+
s.add_development_dependency 'yard'
|
25
|
+
s.add_development_dependency 'minitest-rails', '~> 0.2'
|
26
|
+
s.add_development_dependency 'minitest', '~> 3.0' if RUBY_PLATFORM == "java"
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: searchtastic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pete Michaud
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.9.2
|
22
|
+
type: :development
|
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.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.10'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.10'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest-rails
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.2'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0.2'
|
78
|
+
description: Enables ActiveRecord Model Searching
|
79
|
+
email:
|
80
|
+
- me@petermichaud.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .rspec
|
87
|
+
- Gemfile
|
88
|
+
- Rakefile
|
89
|
+
- Readme.md
|
90
|
+
- lib/searchtastic.rb
|
91
|
+
- lib/searchtastic/version.rb
|
92
|
+
- searchtastic.gemspec
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
homepage: http://github.com/PeteMichaud/searchtastic
|
95
|
+
licenses: []
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- config
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project: searchtastic
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Enables ActiveRecord Model Searching
|
119
|
+
test_files:
|
120
|
+
- spec/spec_helper.rb
|