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 ADDED
@@ -0,0 +1,14 @@
1
+ *.gem
2
+ *.rvmrc
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ coverage.data
7
+ coverage/*
8
+ .yardoc
9
+ tmp
10
+ vendor/bundle
11
+ *.swp
12
+ *.swo
13
+ *.DS_Store
14
+ .idea/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order rand
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
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.
@@ -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,3 @@
1
+ module Searchtastic
2
+ VERSION = '0.5.0'
3
+ end
@@ -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
@@ -0,0 +1,2 @@
1
+ require 'active_support/inflector'
2
+ require 'searchtastic'
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