easy_search 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.textile +124 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/easy_search.gemspec +25 -0
- data/lib/easy_search.rb +1 -0
- data/lib/easy_search/constants.rb +15 -0
- data/lib/easy_search/core.rb +134 -0
- data/lib/easy_search/errors.rb +25 -0
- data/lib/easy_search/setup.rb +87 -0
- data/lib/easy_search/validations.rb +39 -0
- data/lib/easy_search/version.rb +3 -0
- data/tasks/easy_search_tasks.rake +4 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2766cdc5f1074ec9e13036fb2c2fede256a7a23f
|
4
|
+
data.tar.gz: 1d00907cd274a6edf63fc6eac697bcc6b9b6d701
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ce0fa34e0dcd8377e7c9b91cfab9ff444cc33b42b6b66f7d5ced39be59933141bcecc6743acc90873aa5e145e15e793a85d24ab4f2623521ffd271ac7a0d7d5
|
7
|
+
data.tar.gz: c40f743b06954e8e84bd41cde5ea1ef25fea83a3e65e4a7dec541969d0bd8f2ecfabdf330de42909fab1885fce07529de81a151aa56af9436326eb70effcd89c
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Ryan Heath
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
h1. EasySearch
|
2
|
+
|
3
|
+
Easy search provides a convenient DSL for quickly searching your @ActiveRecord@ models.
|
4
|
+
|
5
|
+
h2. What It's NOT
|
6
|
+
|
7
|
+
Firstly, let's just get this out of the way. EasySearch is _not_ intended for search-intensive or high-performance applications. There are plenty of other (awesome) full-text plugins for that (e.g. acts_as_ferret, sphinx/ultrasphinx, etc). It's just an easy, quick-n-dirty search for your @ActiveRecord@ models. So if you're looking for a search solution to hunt through your 10 million record database, please, leave now while you still can.
|
8
|
+
|
9
|
+
h2. So, Why Build it?
|
10
|
+
|
11
|
+
Honestly? Because building a DSL interface to a simple thing is fun. It's actually a lot of fun. Also, I think it's a reasonable place to start for creating something bigger. Overall, this plugin is more-or-less the result of me toying around with the dynamism of Ruby :-)
|
12
|
+
|
13
|
+
h2. Installation
|
14
|
+
|
15
|
+
You can install this as a Rails plugin. Navigate to your project root and type:
|
16
|
+
|
17
|
+
<pre><code>git clone git://github.com/rpheath/easy_search.git vendor/plugins/easy_search</code></pre>
|
18
|
+
|
19
|
+
Use it at your own risk. You've been warned.
|
20
|
+
|
21
|
+
h2. General Use Case
|
22
|
+
|
23
|
+
Again, please don't attempt to use this if you'll be searching giant databases, as I'd imagine you would need something performance aware, which this is _not_. But. It should work fine for hundreds, thousands, maybe even tens-of-thousands of records. You know, like blogs, small forums, etc.
|
24
|
+
|
25
|
+
h3. Configuration
|
26
|
+
|
27
|
+
In config/initializers/easy_search_setup.rb (or config/environment.rb if you aren't running Rails 2.0+), you'd do something like this:
|
28
|
+
|
29
|
+
<pre><code>
|
30
|
+
EasySearch::Setup.config do
|
31
|
+
setup_tables do
|
32
|
+
users :first_name, :last_name, :email, :login
|
33
|
+
projects :title, :description
|
34
|
+
comments :name, :body
|
35
|
+
groups :name, :description
|
36
|
+
end
|
37
|
+
end
|
38
|
+
</code></pre>
|
39
|
+
|
40
|
+
The @setup_tables@ method allows you to specify which columns should be searched for each one of your tables. It's required if you want to use @EasySearch@ with one of your models. Otherwise, @EasySearch@ wouldn't have a clue what to do with your keywords.
|
41
|
+
|
42
|
+
And of course, should you try to search a model that has not been configured, @EasySearch@ will let you know :-)
|
43
|
+
|
44
|
+
h2. Examples
|
45
|
+
|
46
|
+
We'll go straight into it. First thing, you install the plugin. Then you add a generic class and give it the @EasySearch@ functionality. Like so:
|
47
|
+
|
48
|
+
<pre><code>
|
49
|
+
# app/models/search.rb
|
50
|
+
class Search
|
51
|
+
include EasySearch
|
52
|
+
end
|
53
|
+
</pre></code>
|
54
|
+
|
55
|
+
Now we have a @Search@ class that can easily handle search your @ActiveRecord@ models! Notice how this class _does not_ descend from @ActiveRecord@.
|
56
|
+
|
57
|
+
h3. Usage
|
58
|
+
|
59
|
+
In words, let's say we want to "search the users table with 'ryan heath'". To represent that in code, it's basically the same thing:
|
60
|
+
|
61
|
+
<pre><code>
|
62
|
+
$> Search.users.with('ryan heath')
|
63
|
+
$> => [<#User ...>]
|
64
|
+
|
65
|
+
$> Search.projects.with('webdesign')
|
66
|
+
$> => [<#Project ...>, <#Project ...>, ...]
|
67
|
+
</code></pre>
|
68
|
+
|
69
|
+
Note: the above will not compare 'ryan heath' with each of the configured columns, but it will compare 'ryan' and 'heath' separately against each of those columns for better results. Another thing worthy of mention about keyword splitting: it's slightly more sophisticated, in that it will strip out the dull keywords that would return innaccurate results, which also helps performance. For instance:
|
70
|
+
|
71
|
+
You can also search by an exact phrase. Let's say you wanted to support a search for '"ryan heath"', where you didn't want EasySearch to search "ryan" and "heath" separately. Well, all you have to do is tell your users to wrap their search terms in quotes (just like Google) and EasySearch will know to search that phrase, in that order, without splitting up the words.
|
72
|
+
|
73
|
+
<pre><code>Search.users.with('ryan and a term')</code></pre>
|
74
|
+
|
75
|
+
That would only search 'ryan' and 'term' against your columns. @EasySearch@ tries to be somewhat smart about what it searches. And in fact, you can see what @EasySearch@ considers "dull keywords" by doing:
|
76
|
+
|
77
|
+
<pre><code>
|
78
|
+
# default dull keywords (this is just an example)
|
79
|
+
$> EasySearch::Setup.dull_keywords
|
80
|
+
$> => ['a', 'and', 'the']
|
81
|
+
</code></pre>
|
82
|
+
|
83
|
+
The list is _far from complete_. So if you have words that you'd like to add to the "dull keywords" list (meaning words that would not be searched), you can do so quite easily. Going back to the @Setup.config@ block:
|
84
|
+
|
85
|
+
<pre><code>
|
86
|
+
EasySearch::Setup.config do
|
87
|
+
strip_keywords do
|
88
|
+
['what', 'then', 'if', 'is', 'it']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# dull_keywords is now a sum of the defaults plus your custom keywords
|
93
|
+
$> EasySearch::Setup.dull_keywords
|
94
|
+
$> => ['a', 'and', 'the', 'what', 'then', 'if', 'is', 'it']
|
95
|
+
</code></pre>
|
96
|
+
|
97
|
+
Those keywords are appended to the existing list (all duplicates are removed). And if you'd rather your custom list replace the existing defaults entirely, just pass 'true' to the @strip_keywords@ method, like so:
|
98
|
+
|
99
|
+
<pre><code>
|
100
|
+
EasySearch::Setup.config do
|
101
|
+
strip_keywords(true) do
|
102
|
+
['what', 'then', 'if', 'is', 'it']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# dull_keywords is now only your custom keywords
|
107
|
+
$> EasySearch::Setup.dull_keywords
|
108
|
+
$> => ['what', 'then', 'if', 'is', 'it']
|
109
|
+
</code></pre>
|
110
|
+
|
111
|
+
And finally, if you have additional conditions that you need to apply to all search results, just use a :conditions option on the @with@ method, like so:
|
112
|
+
|
113
|
+
<pre><code>
|
114
|
+
$> Search.users.with('ryan heath', :conditions => 'active => 1')
|
115
|
+
$> # active users matching 'ryan' and/or 'heath'
|
116
|
+
</code></pre>
|
117
|
+
|
118
|
+
Pretty easy, huh?
|
119
|
+
|
120
|
+
Again, this plugin is not meant for searching high-volume Databases. It doesn't handle indexing or any of the other things a full-text solution might handle. But it was a fun plugin to write, and it's a fun API/DSL to use, and that's why I wrote it.
|
121
|
+
|
122
|
+
h2. License
|
123
|
+
|
124
|
+
Copyright (c) 2008 Ryan Heath, released under the MIT license
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "easy_search"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/easy_search.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'easy_search/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "easy_search"
|
8
|
+
spec.version = EasySearch::VERSION
|
9
|
+
spec.authors = ["Ryan Heath"]
|
10
|
+
spec.email = ["ryan@rpheath.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A small DSL for simple searches.}
|
13
|
+
spec.description = %q{A small Ruby library that provides a DSL for simple LIKE searches.}
|
14
|
+
spec.homepage = "http://www.github.com/rpheath/easy_search"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
end
|
data/lib/easy_search.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'easy_search/core')
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module EasySearch
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
# holds any regexp constants when dealing with search terms
|
5
|
+
Regex = OpenStruct.new(
|
6
|
+
:email => /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/
|
7
|
+
)
|
8
|
+
|
9
|
+
Defaults = OpenStruct.new(
|
10
|
+
# these keywords will be removed from any search terms, as they
|
11
|
+
# provide no value and just increase the size of the query.
|
12
|
+
# (the idea is a small attempt to be as efficient as possible)
|
13
|
+
:dull_keywords => ['a', 'the', 'and', 'or', 'what', 'then', 'if', 'is', 'it']
|
14
|
+
)
|
15
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
%w(constants errors setup validations version).each do |f|
|
2
|
+
require File.join(File.dirname(__FILE__), f)
|
3
|
+
end
|
4
|
+
|
5
|
+
module EasySearch
|
6
|
+
def self.included(base)
|
7
|
+
base.send(:extend, ClassMethods)
|
8
|
+
base.send(:include, InstanceMethods)
|
9
|
+
|
10
|
+
# before continuing, validate that the models identified (if any) in the
|
11
|
+
# setup_tables block (within `Setup.config') exist and are valid ActiveRecord descendants
|
12
|
+
Validations.validate_settings!
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
# "Search.users" translates to "User.find" dynamically
|
17
|
+
def method_missing(name, *args)
|
18
|
+
# instantiate a new instance of self with
|
19
|
+
# the @klass set to the missing method
|
20
|
+
self.new(name.to_sym)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
def initialize(klass)
|
26
|
+
@klass = klass
|
27
|
+
|
28
|
+
# validate that the class derived from the missing method descends from
|
29
|
+
# ActiveRecord and has been "configured" in `Setup.config { setup_tables {...} }'
|
30
|
+
# (i.e. "Search.userz.with(...)" where "userz" is an invalid model)
|
31
|
+
Validations.validate_class!(@klass)
|
32
|
+
end
|
33
|
+
|
34
|
+
# used to collect/parse the keywords that are to be searched, and return
|
35
|
+
# the search results (hands off to the Rails finder)
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
# Search.users.with("ryan heath")
|
39
|
+
# # => <#User ... > or []
|
40
|
+
def with(keywords, options={})
|
41
|
+
search_terms = keywords.match(/"(.+)"/) ? extract($1, :exact => true) : extract(keywords)
|
42
|
+
return [] if search_terms.blank?
|
43
|
+
|
44
|
+
klass = to_model(@klass)
|
45
|
+
|
46
|
+
conditions = "(#{build_conditions_for(search_terms)})"
|
47
|
+
conditions << " AND (#{options[:conditions]})" unless options[:conditions].blank?
|
48
|
+
sanitized_sql_conditions = klass.send(:sanitize_sql_for_conditions, conditions)
|
49
|
+
|
50
|
+
options = { :select => 'DISTINCT *', :conditions => sanitized_sql_conditions, :order => options[:order], :limit => options[:limit] }
|
51
|
+
options.update :include => associations_to_include
|
52
|
+
klass.find(:all, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
# constructs the conditions for the WHERE clause in the SQL statement.
|
57
|
+
# (compares each search term against each configured column for that model)
|
58
|
+
#
|
59
|
+
# ultimately this allows for a single query rather than several small ones,
|
60
|
+
# alleviating the need to open/close DB connections and instantiate multiple
|
61
|
+
# ActiveRecord objects through the loop
|
62
|
+
#
|
63
|
+
# it should be noted that a search with too many keywords against too many columns
|
64
|
+
# in a DB with too many rows will inevitably hurt performance (use ultrasphinx!)
|
65
|
+
def build_conditions_for(terms)
|
66
|
+
klass = to_model(@klass)
|
67
|
+
|
68
|
+
[].tap do |clause|
|
69
|
+
Setup.table_settings[@klass].each do |column|
|
70
|
+
# handle search associated objects
|
71
|
+
if Hash === column
|
72
|
+
column.each do |association, columns|
|
73
|
+
reflection = klass.reflect_on_association(association.to_sym)
|
74
|
+
next unless reflection
|
75
|
+
model = reflection.class_name.constantize
|
76
|
+
columns.each do |associated_column|
|
77
|
+
clause << build_conditions_for_terms_on_model(model, associated_column, terms)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
else
|
81
|
+
clause << build_conditions_for_terms_on_model(klass, column, terms)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end.flatten.join(" OR ")
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_conditions_for_terms_on_model(klass, column, terms)
|
88
|
+
terms.inject([]) do |clause, term|
|
89
|
+
if klass.columns.map(&:name).include?(column.to_s)
|
90
|
+
clause << "`#{klass.table_name}`.`#{column}` LIKE '%#{term}%'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def associations_to_include
|
96
|
+
Setup.table_settings[@klass].collect do |e|
|
97
|
+
Hash === e ? e.keys : nil
|
98
|
+
end.compact || []
|
99
|
+
end
|
100
|
+
|
101
|
+
# using scan(/\w+/) to parse the words
|
102
|
+
#
|
103
|
+
# emails were being separated (split on the "@" symbol since it's not a word)
|
104
|
+
# so "rheath@test.com" became ["rheath", "test.com"] as search terms, when we
|
105
|
+
# really want to keep emails intact. as a work around, the emails are pulled out before
|
106
|
+
# the words are scanned, then each email is pushed back into the array as its own criteria.
|
107
|
+
#
|
108
|
+
# TODO: refactor this method to be less complex for such a simple problem.
|
109
|
+
def extract(terms, options={})
|
110
|
+
return [terms] if options.delete(:exact)
|
111
|
+
|
112
|
+
terms.gsub!("'", "")
|
113
|
+
emails = strip_emails_from(terms)
|
114
|
+
|
115
|
+
keywords = unless emails.blank?
|
116
|
+
emails.inject(terms.gsub(Regex.email, '').scan(/\w+/)) { |t, email| t << email }
|
117
|
+
else
|
118
|
+
terms.scan(/\w+/)
|
119
|
+
end
|
120
|
+
|
121
|
+
return (keywords.collect { |k| k.downcase } - Setup.dull_keywords.collect { |k| k.downcase })
|
122
|
+
end
|
123
|
+
|
124
|
+
# extracts the emails from the keywords
|
125
|
+
def strip_emails_from(text)
|
126
|
+
text.split.reject { |t| t.match(Regex.email) == nil }
|
127
|
+
end
|
128
|
+
|
129
|
+
# converts the symbol representation of a table to an actual ActiveRecord model
|
130
|
+
def to_model(klass)
|
131
|
+
klass.to_s.singularize.classify.constantize
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module EasySearch
|
2
|
+
class Error < RuntimeError
|
3
|
+
def self.message(msg=nil); msg.nil? ? @message : self.message = msg; end
|
4
|
+
def self.message=(msg); @message = msg; end
|
5
|
+
end
|
6
|
+
|
7
|
+
# raised when a model is attempted to be configured, but doesn't exist
|
8
|
+
class NoModelError < Error
|
9
|
+
message "you've specified a model that doesn't exist"; end
|
10
|
+
|
11
|
+
# raised when any model (consant) is derived from the `Setup.config' block
|
12
|
+
# and is not a subclass of ActiveRecord::Base
|
13
|
+
class InvalidActiveRecordModel < Error
|
14
|
+
message "all models specified in `Setup.config' must descend from ActiveRecord"; end
|
15
|
+
|
16
|
+
# raised when a model has not been configured, but is attempted to be searched
|
17
|
+
# (each model used with EasySearch has to be configured, meaning, the columns
|
18
|
+
# to be searched for that model must be set)
|
19
|
+
class InvalidSettings < Error
|
20
|
+
message "you must specify all Models and the fields to search for each Model in the `Setup.config' block"; end
|
21
|
+
|
22
|
+
# raised when block passed to `Setup.strip_keywords' does not evaluate as an instance of Array
|
23
|
+
class InvalidDullKeywordsType < Error
|
24
|
+
message "specified keywords must be of type Array in the `Setup.strip_keywords' block"; end
|
25
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module EasySearch
|
2
|
+
class Setup
|
3
|
+
class << self
|
4
|
+
# returns a hash with the keys as the models to be searched
|
5
|
+
# and the values as arrays of columns for the respective model
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# $> Setup.settings
|
9
|
+
# $> => {"users"=>[:first_name, :last_name, :email], "projects"=>[:title, :description]}
|
10
|
+
def table_settings
|
11
|
+
@@table_settings ||= HashWithIndifferentAccess.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# returns an array of keywords that serve as no benefit in a search
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# $> Setup.dull_keywords
|
18
|
+
# $> => ['a', 'and', 'but', 'the', ...]
|
19
|
+
def dull_keywords
|
20
|
+
(@@dull_keywords ||= Defaults.dull_keywords).flatten.uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
# accepts a block that specifies the columns
|
24
|
+
# to search for each model
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
# Setup.config do
|
28
|
+
# setup_tables { ... }
|
29
|
+
# strip_keywords { ... }
|
30
|
+
# end
|
31
|
+
def config(&block)
|
32
|
+
return nil unless block_given?
|
33
|
+
self.class_eval(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# REQUIRED
|
37
|
+
# accepts a block that specifies the columns
|
38
|
+
# to search for each model
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# setup_tables do
|
42
|
+
# users :first_name, :last_name, :email
|
43
|
+
# projects :title, :description
|
44
|
+
# end
|
45
|
+
def setup_tables(&block)
|
46
|
+
return nil unless block_given?
|
47
|
+
self.class_eval(&block)
|
48
|
+
self.table_settings
|
49
|
+
end
|
50
|
+
|
51
|
+
# OPTIONAL
|
52
|
+
# allows customization of the dull_keywords setting
|
53
|
+
# (can be overwritten or appended)
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
# DEFAULT_DULL_KEYWORDS = ['the', 'and', 'is']
|
57
|
+
#
|
58
|
+
# 1) appending keywords to the default list
|
59
|
+
# strip_keywords do
|
60
|
+
# ['it', 'why', 'is']
|
61
|
+
# end
|
62
|
+
# # => ['the', 'and', 'it', 'why', 'is']
|
63
|
+
#
|
64
|
+
# 2) overwriting existing keywords
|
65
|
+
# strip_keywords(true) do
|
66
|
+
# ['something', 'whatever']
|
67
|
+
# end
|
68
|
+
# # => ['something', 'whatever']
|
69
|
+
def strip_keywords(overwrite=false, &block)
|
70
|
+
return nil unless block_given?
|
71
|
+
raise(InvalidDullKeywordsType, InvalidDullKeywordsType.message) unless block.call.is_a?(Array)
|
72
|
+
|
73
|
+
overwrite ? @@dull_keywords = block.call : @@dull_keywords = (self.dull_keywords << block.call)
|
74
|
+
self.dull_keywords
|
75
|
+
end
|
76
|
+
|
77
|
+
# this is the magic that makes `setup_tables' work like it does.
|
78
|
+
# once the block is eval'd those missing methods (i.e. "users" and "projects")
|
79
|
+
# will be caught here and the table_settings hash will be updated with the
|
80
|
+
# key set to the table, and the value set to the columns. this is what allows the
|
81
|
+
# EasySearch plugin to work generically for any rails application.
|
82
|
+
def method_missing(table, *columns)
|
83
|
+
table_settings[table] = columns
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module EasySearch
|
2
|
+
class Validations
|
3
|
+
# called once the EasySearch module is included.
|
4
|
+
#
|
5
|
+
# it ensures that any/all models in the settings hash exist
|
6
|
+
# and decend from ActiveRecord::Base
|
7
|
+
def self.validate_settings!
|
8
|
+
unless Setup.table_settings.blank?
|
9
|
+
Setup.table_settings.keys.each do |klass|
|
10
|
+
unless klass.to_s.classify.constantize.ancestors.include?(ActiveRecord::Base)
|
11
|
+
raise( InvalidActiveRecordModel, InvalidActiveRecordModel.message )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rescue NameError
|
16
|
+
raise $!
|
17
|
+
raise( NoModelError, "you've specified a table in your `Setup.config' block that doesn't exist" )
|
18
|
+
end
|
19
|
+
|
20
|
+
# called when a new EasySearch containing class is instantiated.
|
21
|
+
#
|
22
|
+
# For example, `Search.users.with("ryan heath")' would instantiate a
|
23
|
+
# new Search, setting the "users" part to the @klass variable.
|
24
|
+
# the following would ensure that the @klass variable is indeed valid.
|
25
|
+
def self.validate_class!(klass)
|
26
|
+
raise( InvalidActiveRecordModel, InvalidActiveRecordModel.message ) unless valid_model?(klass)
|
27
|
+
raise( InvalidSettings, InvalidSettings.message ) unless model_has_settings?(klass)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def self.valid_model?(klass)
|
32
|
+
klass.to_s.singularize.classify.constantize.ancestors.include?(ActiveRecord::Base) rescue false
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.model_has_settings?(klass)
|
36
|
+
!Setup.table_settings[klass].blank? rescue false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easy_search
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Heath
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A small Ruby library that provides a DSL for simple LIKE searches.
|
56
|
+
email:
|
57
|
+
- ryan@rpheath.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.textile
|
66
|
+
- Rakefile
|
67
|
+
- bin/console
|
68
|
+
- bin/setup
|
69
|
+
- easy_search.gemspec
|
70
|
+
- lib/easy_search.rb
|
71
|
+
- lib/easy_search/constants.rb
|
72
|
+
- lib/easy_search/core.rb
|
73
|
+
- lib/easy_search/errors.rb
|
74
|
+
- lib/easy_search/setup.rb
|
75
|
+
- lib/easy_search/validations.rb
|
76
|
+
- lib/easy_search/version.rb
|
77
|
+
- tasks/easy_search_tasks.rake
|
78
|
+
homepage: http://www.github.com/rpheath/easy_search
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.2.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: A small DSL for simple searches.
|
102
|
+
test_files: []
|