simple_search_like 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.md +22 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/simple_search.rb +167 -0
- data/spec/simple_search_spec.rb +12 -0
- data/spec/spec_helper.rb +9 -0
- metadata +95 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Nate Wiger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Simple Search - Easy ActiveRecord searching with hashes
|
2
|
+
=======================================================
|
3
|
+
|
4
|
+
There is a newer project, scoped_search, which I would recommend instead. This exists for backwards compatibility.
|
5
|
+
See: [Scoped Search](http://techblog.floorplanner.com/2008/07/26/easy-search-with-activerecord/)
|
6
|
+
|
7
|
+
This gem is incompatible with the "simple_search" gem.
|
8
|
+
|
9
|
+
Example
|
10
|
+
-------
|
11
|
+
|
12
|
+
require 'simple_search'
|
13
|
+
|
14
|
+
class User < ActiveRecord::Base
|
15
|
+
end
|
16
|
+
|
17
|
+
User.simple_search(:name => 'bob')
|
18
|
+
User.simple_search_like('bob', [:name, :description])
|
19
|
+
|
20
|
+
Author
|
21
|
+
------
|
22
|
+
Copyright (c) 2010 [Nate Wiger](http://nateware.com). See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "simple_search_like"
|
8
|
+
gem.summary = %Q{Simple hash-based search for ActiveRecord}
|
9
|
+
gem.description = %Q{Simple hash-based search for ActiveRecord. Also see Scoped Search.}
|
10
|
+
gem.email = "nate@wiger.org"
|
11
|
+
gem.homepage = "http://github.com/nateware/simple_search_like"
|
12
|
+
gem.authors = ["Nate Wiger"]
|
13
|
+
gem.add_dependency 'will_paginate'
|
14
|
+
gem.add_development_dependency "bacon", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
26
|
+
spec.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |spec|
|
32
|
+
spec.libs << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :spec
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "simple_search_like #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module SimpleSearch
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# Player.simple_search(:filters => {:name => "nwiger"}, :order => 'id')
|
8
|
+
# Player.simple_search(params, :order => 'id')
|
9
|
+
def simple_search(options={}, extras=nil)
|
10
|
+
options.merge!(extras) if extras
|
11
|
+
paginate_args = simple_search_conditions(options)
|
12
|
+
|
13
|
+
# retrieve total_count to pass total_entries to paginate
|
14
|
+
use_table_name = options[:from] ? options[:from] : table_name
|
15
|
+
if paginate_args[:conditions].nil?
|
16
|
+
total_count = count(:select => "#{use_table_name}.id",
|
17
|
+
:from => use_table_name)
|
18
|
+
else
|
19
|
+
total_count = count(:select => "#{use_table_name}.id",
|
20
|
+
:from => use_table_name,
|
21
|
+
:conditions => paginate_args[:conditions],
|
22
|
+
:joins => paginate_args[:joins],
|
23
|
+
:include => paginate_args[:include])
|
24
|
+
end
|
25
|
+
|
26
|
+
unless paginate_args[:total_entries]
|
27
|
+
paginate_args[:per_page] = options[:per_page]
|
28
|
+
paginate_args[:total_entries] = total_count
|
29
|
+
end
|
30
|
+
|
31
|
+
# bounds check total_entries
|
32
|
+
if paginate_args[:total_entries] > total_count
|
33
|
+
paginate_args[:total_entries] = total_count
|
34
|
+
end
|
35
|
+
|
36
|
+
if options[:use_index]
|
37
|
+
paginate_args[:from] = "#{use_table_name} USE INDEX (#{options[:use_index]})"
|
38
|
+
end
|
39
|
+
|
40
|
+
paginate(paginate_args)
|
41
|
+
end
|
42
|
+
|
43
|
+
# clean params to prevent SQL injection
|
44
|
+
# added default sort_column (id) since if this parameter is not present in the options hash
|
45
|
+
# it will generate invalid SQL. If the ORDER BY does not specify a column in the SQL the db will generate:
|
46
|
+
# ORA-00936: missing expression:
|
47
|
+
def simple_search_conditions(options={}, extras=nil)
|
48
|
+
options.merge!(extras) if extras
|
49
|
+
paginate_args = {}
|
50
|
+
|
51
|
+
# figure out what table name to use to prefix column names with
|
52
|
+
# WOW, ActiveRecord does not properly figure out the table name to use properly in two different cases.
|
53
|
+
# 1. Count from a db view counts from the original table name set in 'table_name' when using :from option
|
54
|
+
# 2. Find does not prefix the column names correctly when selecting from a db view using the :from option.
|
55
|
+
# Example: SELECT * FROM v_my_table where id = 10
|
56
|
+
# will actually result in
|
57
|
+
# SELECT * FROM v_my_table where my_table.id = 10
|
58
|
+
# So appears ActiveRecord does not properly prefix the table_name for columns in where conditions when using the :from option properly in all cases
|
59
|
+
use_table_name = options[:from] ? options[:from] : table_name
|
60
|
+
|
61
|
+
if !options[:sort_column].nil?
|
62
|
+
paginate_args[:order] = options[:sort_column].to_s.gsub(/\W+/,'')
|
63
|
+
else
|
64
|
+
paginate_args[:order] = options[:order].nil? ? "#{use_table_name}.id" : options[:order] # allow :order to be passed explicitly
|
65
|
+
end
|
66
|
+
|
67
|
+
if !options[:sort_order].nil?
|
68
|
+
paginate_args[:order] += ' ' + options[:sort_order].to_s.gsub(/\W+/,'')
|
69
|
+
end
|
70
|
+
|
71
|
+
# extreme sanity checking
|
72
|
+
paginate_args[:page] = (options[:page] || 1).to_i
|
73
|
+
if paginate_args[:page].nil? or paginate_args[:page] == '' or paginate_args[:page] < 1
|
74
|
+
raise "Invalid page '#{paginate_args[:page]}' for #{self.name}.search"
|
75
|
+
end
|
76
|
+
|
77
|
+
# default per_page to something sane if unset
|
78
|
+
options[:per_page] = (options[:per_page] || 10).to_i
|
79
|
+
|
80
|
+
# delete filter options that are blank or nil
|
81
|
+
if !options[:filters].nil?
|
82
|
+
options[:filters] = options[:filters].delete_if {|k,v| v.nil? || v == ''}
|
83
|
+
|
84
|
+
if options[:conditions].is_a? Array
|
85
|
+
conditions = options[:conditions].shift
|
86
|
+
bind_vals = options[:conditions]
|
87
|
+
else
|
88
|
+
conditions = ''
|
89
|
+
bind_vals = []
|
90
|
+
end
|
91
|
+
|
92
|
+
options[:filters].each do |filter, value|
|
93
|
+
conditions += ' AND ' unless bind_vals.empty?
|
94
|
+
|
95
|
+
# append the proper table name to the column name in where conditions
|
96
|
+
# for handling db views when using the :from option
|
97
|
+
if value.is_a? Array # if value passed in an Array using IN instead of = (equal)
|
98
|
+
conditions += "#{use_table_name}.#{filter} IN (?)"
|
99
|
+
# Need a way to do greater than or less than with dates, so going to follow the convention
|
100
|
+
# assume if the name follows the _at ('created_at') it is a date and you want to do a diff conditional check
|
101
|
+
elsif /_at$/ =~ filter
|
102
|
+
conditions += "#{use_table_name}.#{filter} > ?"
|
103
|
+
else
|
104
|
+
conditions += "#{use_table_name}.#{filter} = ?"
|
105
|
+
end
|
106
|
+
|
107
|
+
bind_vals << value
|
108
|
+
end
|
109
|
+
|
110
|
+
paginate_args[:conditions] = [conditions, *bind_vals]
|
111
|
+
elsif !options[:conditions].nil?
|
112
|
+
paginate_args[:conditions] = options[:conditions]
|
113
|
+
end
|
114
|
+
|
115
|
+
if !options[:joins].nil?
|
116
|
+
paginate_args[:joins] = options[:joins]
|
117
|
+
end
|
118
|
+
|
119
|
+
if !options[:include].nil?
|
120
|
+
paginate_args[:include] = options[:include]
|
121
|
+
end
|
122
|
+
|
123
|
+
if !options[:from].nil?
|
124
|
+
paginate_args[:from] = options[:from]
|
125
|
+
end
|
126
|
+
|
127
|
+
if !options[:select].nil?
|
128
|
+
paginate_args[:select] = options[:select]
|
129
|
+
end
|
130
|
+
|
131
|
+
# :limit is two places due to a will_paginate workaround - see also simple_search itself
|
132
|
+
if !options[:limit].nil?
|
133
|
+
if options[:limit] < options[:per_page]
|
134
|
+
paginate_args[:per_page] = options[:limit]
|
135
|
+
paginate_args[:total_entries] = options[:limit]
|
136
|
+
else
|
137
|
+
paginate_args[:per_page] = options[:per_page]
|
138
|
+
paginate_args[:total_entries] = options[:limit]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
paginate_args
|
143
|
+
end
|
144
|
+
|
145
|
+
def simple_search_like(options={}, search_columns_in=%w(name))
|
146
|
+
# Accept single column name as a string, or an array of strings with multiple column names
|
147
|
+
search_columns = search_columns_in.is_a?(String) ? [ search_columns_in ] : search_columns_in
|
148
|
+
# Make sure the programmer got his act together
|
149
|
+
if look = options[:search] and look != ''
|
150
|
+
look = '' if look =~ /^\s*search\s*/i
|
151
|
+
text = '%'+ look.downcase.gsub(/[\'\;]/,'') +'%'
|
152
|
+
|
153
|
+
stmt, bind = [], []
|
154
|
+
search_columns.each do |el|
|
155
|
+
stmt << "lower(#{el}) like ?"
|
156
|
+
bind << text
|
157
|
+
end
|
158
|
+
simple_search(options.merge(:conditions => ['(' + stmt.join(' OR ') + ')', *bind]))
|
159
|
+
else
|
160
|
+
# passthru
|
161
|
+
simple_search(options)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
ActiveRecord::Base.extend SimpleSearch::ClassMethods if defined?(ActiveRecord::Base)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path 'spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "SimpleSearch" do
|
7
|
+
it "should add search methods to ActiveRecord::Base" do
|
8
|
+
User.respond_to?(:simple_search).should.be.true
|
9
|
+
User.respond_to?(:simple_search_like).should.be.true
|
10
|
+
User.respond_to?(:simple_search_conditions).should.be.true
|
11
|
+
end
|
12
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bacon'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path File.dirname(__FILE__))
|
5
|
+
$LOAD_PATH.unshift(File.expand_path File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
require 'active_record'
|
7
|
+
require 'simple_search'
|
8
|
+
|
9
|
+
Bacon.summary_on_exit
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_search_like
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nate Wiger
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-12 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: will_paginate
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: bacon
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
description: Simple hash-based search for ActiveRecord. Also see Scoped Search.
|
45
|
+
email: nate@wiger.org
|
46
|
+
executables: []
|
47
|
+
|
48
|
+
extensions: []
|
49
|
+
|
50
|
+
extra_rdoc_files:
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
files:
|
54
|
+
- .document
|
55
|
+
- .gitignore
|
56
|
+
- LICENSE
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- VERSION
|
60
|
+
- lib/simple_search.rb
|
61
|
+
- spec/simple_search_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/nateware/simple_search_like
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options:
|
69
|
+
- --charset=UTF-8
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 1.3.6
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Simple hash-based search for ActiveRecord
|
93
|
+
test_files:
|
94
|
+
- spec/simple_search_spec.rb
|
95
|
+
- spec/spec_helper.rb
|