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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
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.
@@ -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.
@@ -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
@@ -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