simple_search_like 0.1.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/.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
|