search_qd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.sqlite3
6
+ db_config.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,3 @@
1
+ ## v0.0.1
2
+
3
+ * initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in search_qd.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Karsten Gallinowski
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,55 @@
1
+ # SearchQd
2
+
3
+ ## DESCRIPTION
4
+
5
+ SearchQd extends ActiveRecord and offers a method called search\_qd
6
+ for a simple full text search. Simple because the search functionality is
7
+ executed as SQL LIKE statements for given (text) columns.
8
+
9
+ ## Quick start
10
+
11
+
12
+ ```ruby
13
+ gem install search_qd
14
+ ```
15
+
16
+ * Rails 3
17
+
18
+ Add the following line to your Gemfile
19
+
20
+ ```ruby
21
+ gem 'search_qd'
22
+ ```
23
+
24
+ * ActiveRecord outside of Rails
25
+
26
+ ```ruby
27
+ require 'search_qd'
28
+ ActiveRecord::Base.send(:include, SearchQd)
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ Assuming your model name is Blog and the model has two text columns title and content:
34
+
35
+ ```ruby
36
+ class Blog < ActiveRecord::Base
37
+ search_qd_columns :title, :content
38
+ end
39
+
40
+ Blog.search_qd("some text to seach for") # search for 'some text to search for' in title and content column
41
+ Blog.search_qd("some text to search for", "title") # search for 'some text to search for' in title column
42
+ Blog.search_qd("some text to search for", "user_name") # search for 'some text to search for' in user_name column
43
+ ```
44
+
45
+ As shown in the example above the search\_qd method expects the search query as a string and a list of columns. If no list
46
+ of columns is given, the search\_qd method uses the column list defined by search\_qd\_columns (see class Blog definition).
47
+
48
+ ## REQUIREMENTS
49
+
50
+ * ActiveRecord
51
+ * Ruby 1.9.\*
52
+
53
+ ## License
54
+
55
+ This gem is created by Karsten Gallinowski and released under the MIT License.
@@ -0,0 +1,52 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'active_record'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default do
8
+ Rake::Task["db:setup"].invoke
9
+ Rake::Task[:spec].invoke
10
+ end
11
+
12
+ namespace :db do
13
+ desc 'Create and configure test database'
14
+ task :setup do
15
+ spec_dir = File.expand_path(File.dirname(__FILE__) + '/spec')
16
+ db_config = "#{spec_dir}/db_config.yml"
17
+
18
+ unless File.exists?(db_config)
19
+ STDOUT.puts "Database configuration file #{db_config} could not been found."
20
+ else
21
+ STDOUT.puts "Try running migrations"
22
+ Rake::Task["db:drop"].invoke
23
+ Rake::Task["db:migrate"].invoke
24
+ end
25
+ end
26
+
27
+ desc 'Run migrations for test database'
28
+ task :migrate do
29
+ spec_dir = File.expand_path(File.dirname(__FILE__) + '/spec')
30
+ db_config = "#{spec_dir}/db_config.yml"
31
+
32
+ ActiveRecord::Base.establish_connection(YAML.load_file(db_config))
33
+ ActiveRecord::Migration.instance_eval do
34
+ create_table :blogs do |t|
35
+ t.string :title
36
+ t.text :content
37
+ t.string :user_name
38
+ end
39
+ end
40
+ end
41
+
42
+ desc 'Drop tables from test database'
43
+ task :drop do
44
+ spec_dir = File.expand_path(File.dirname(__FILE__) + '/spec')
45
+ db_config = "#{spec_dir}/db_config.yml"
46
+
47
+ ActiveRecord::Base.establish_connection(YAML.load_file(db_config))
48
+ ActiveRecord::Migration.instance_eval do
49
+ drop_table :blogs if self.table_exists?(:blogs)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ require 'search_qd/version'
3
+ require 'active_record' unless defined? Rails
4
+ require 'search_qd/rails' if defined? Rails
5
+
6
+ module SearchQd
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+ # method which can be used inside class definitions
13
+ # to define the default search columns of the class
14
+ # and to extend the class with search_qd singleton method
15
+ def search_qd_columns(*column_names)
16
+ @searchable_columns = []
17
+ [column_names].flatten.each do |column_name|
18
+ @searchable_columns << column_name
19
+ end
20
+ extend SearchQd::SingletonMethods
21
+ end
22
+ end
23
+
24
+ module SingletonMethods
25
+ # search method which provides possibility
26
+ # to define search columns (fields) if default
27
+ # columns should not be used
28
+ def search_qd(query, fields=nil)
29
+ where(search_qd_conditions(query, fields))
30
+ end
31
+
32
+ private
33
+
34
+ # method to create plain SQL LIKE
35
+ # statements for each search term and search column
36
+ #
37
+ # SQL injection should not be an issue because
38
+ # query will be converted into LIKE statements word by
39
+ # word and special characters will be suppressed
40
+ def search_qd_conditions(query, fields=nil)
41
+ return nil if query.blank?
42
+ fields ||= @searchable_columns
43
+
44
+ # suppress special characters
45
+ search_qd_rm_special_chars(query)
46
+
47
+ # split the search string by spaces
48
+ words = query.split(' ')
49
+
50
+ or_statements = []
51
+ for word in words
52
+ like_statements = []
53
+ for column in fields
54
+ like_statements << "#{column} LIKE '%#{word.downcase}%'"
55
+ end
56
+ or_statements << ' ('+like_statements.join(' OR ')+') '
57
+ end
58
+ or_statements.join(' AND ')
59
+ end
60
+
61
+ # German umlauts are the only special characters
62
+ # which won't be removed from the search query
63
+ def search_qd_rm_special_chars(query)
64
+ query.gsub(/[^0-9a-zA-Z\säüöÜÄÖ]/, '')
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,7 @@
1
+ module SearchQd
2
+ class Railtie < Rails::Railtie
3
+ initializer "search_qd.include" do
4
+ ActiveRecord::Base.send(:include, SearchQd)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module SearchQd
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "search_qd/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "search_qd"
7
+ s.version = SearchQd::VERSION
8
+ s.authors = ["kgalli"]
9
+ s.email = ["mail@kgalli.de"]
10
+ s.homepage = "https://github.com/kgalli/search_qd"
11
+ s.summary = %q{Quick and dirty fulltext search for an ActiveRecord model.}
12
+ s.description = %q{SearchQd provides a search method for string/text columns of an ActiveRecord model. The search itself is handled via simple SQL LIKE statements. That is why it is called quick and dirty.}
13
+
14
+ s.rubyforge_project = "search_qd"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "sqlite3"
23
+
24
+ s.add_dependency "activerecord"
25
+ end
@@ -0,0 +1,4 @@
1
+ adapter: sqlite3
2
+ database: search_qd.sqlite3
3
+ pool: 5
4
+ timeout: 5000
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper.rb'
3
+
4
+ # blog class represents active record class
5
+ # which should be extended with functionality
6
+ # provided by SearchQd module
7
+ ActiveRecord::Base.send(:include, SearchQd)
8
+ class Blog < ActiveRecord::Base
9
+ search_qd_columns :title, :content
10
+
11
+ end
12
+
13
+ describe SearchQd do
14
+ describe ".search_qd" do
15
+ before do
16
+ Blog.all.each do |blog|
17
+ blog.delete
18
+ end
19
+ @blog1 = Blog.create title: "Find important blog using test as query", content: "This is some test text."
20
+ @blog2 = Blog.create title: "Do not find me", content: "This blog should not been found."
21
+ @blog3 = Blog.create title: "Second test", content: "Not very important."
22
+ @blog4 = Blog.create title: "Not important", content: "This is only some test content."
23
+
24
+ end
25
+
26
+ it "should find all blogs where title or content include test" do
27
+ Blog.search_qd("test").should eq([@blog1, @blog3, @blog4])
28
+ end
29
+
30
+ it "should find all blogs where title or content include blog" do
31
+ Blog.search_qd("blog").should eq([@blog1, @blog2])
32
+ end
33
+
34
+ it "should find all blogs where title includes test" do
35
+ Blog.search_qd("test", ["title"]).should eq([@blog1, @blog3])
36
+ end
37
+
38
+ it "should find all blogs where content includes important" do
39
+ Blog.search_qd("important", ["content"]).should eq([@blog3])
40
+ end
41
+
42
+ it "should find all blogs where title or content include not important" do
43
+ Blog.search_qd("not important").should eq([@blog3, @blog4])
44
+ end
45
+
46
+ end
47
+
48
+ describe ".search_qd_conditions" do
49
+ it "creates SQL LIKE statement for search_qd_columns" do
50
+ search_string = "Test"
51
+ expected_output = " (title LIKE '%test%' OR content LIKE '%test%') "
52
+ Blog.send("search_qd_conditions", search_string).should eq expected_output
53
+ end
54
+
55
+ it "creates SQL LIKE statement for dynamic fields" do
56
+ search_string = "Test"
57
+ search_fields = %w{ description title }
58
+ expected_output = " (description LIKE '%test%' OR title LIKE '%test%') "
59
+ Blog.send("search_qd_conditions", search_string, search_fields).should eq expected_output
60
+ end
61
+
62
+ it "creates multiple SQL LIKE statements combined with AND for each word in search string" do
63
+ search_string = "Test SearchQd"
64
+ expected_output_word1 = " (title LIKE '%test%' OR content LIKE '%test%') "
65
+ expected_output_word2 = " (title LIKE '%searchqd%' OR content LIKE '%searchqd%') "
66
+ expected_output = [ expected_output_word1, expected_output_word2 ].join(" AND ")
67
+ Blog.send("search_qd_conditions", search_string).should eq expected_output
68
+ end
69
+ end
70
+
71
+ describe ".search_qd_rm_special_chars" do
72
+ it "removes special characters" do
73
+ special_chars = "§$%&/(){}#.,´`!,;@"
74
+ Blog.send("search_qd_rm_special_chars", special_chars).should eq('')
75
+ end
76
+
77
+ it "does not remove word characters" do
78
+ word_character = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
79
+ Blog.send("search_qd_rm_special_chars", word_character).should eq(word_character)
80
+ end
81
+
82
+ it "does not remove numbers" do
83
+ numbers = "1234567890"
84
+ Blog.send("search_qd_rm_special_chars", numbers).should eq(numbers)
85
+ end
86
+
87
+ it "does not remove German umlauts" do
88
+ german_umlauts = "äÄüÜöÖ"
89
+ Blog.send("search_qd_rm_special_chars", german_umlauts).should eq(german_umlauts)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ require 'yaml'
2
+ require 'search_qd'
3
+
4
+ config = YAML.load_file File.expand_path(File.dirname(__FILE__)+ '/db_config.yml')
5
+ ActiveRecord::Base.establish_connection config
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: search_qd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - kgalli
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70355747987560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70355747987560
25
+ - !ruby/object:Gem::Dependency
26
+ name: sqlite3
27
+ requirement: &70355747987140 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70355747987140
36
+ - !ruby/object:Gem::Dependency
37
+ name: activerecord
38
+ requirement: &70355747986720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70355747986720
47
+ description: SearchQd provides a search method for string/text columns of an ActiveRecord
48
+ model. The search itself is handled via simple SQL LIKE statements. That is why
49
+ it is called quick and dirty.
50
+ email:
51
+ - mail@kgalli.de
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - .rspec
58
+ - CHANGELOG.md
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - lib/search_qd.rb
64
+ - lib/search_qd/rails.rb
65
+ - lib/search_qd/version.rb
66
+ - search_qd.gemspec
67
+ - spec/db_config.yml.example
68
+ - spec/search_qd_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: https://github.com/kgalli/search_qd
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project: search_qd
90
+ rubygems_version: 1.8.6
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Quick and dirty fulltext search for an ActiveRecord model.
94
+ test_files:
95
+ - spec/db_config.yml.example
96
+ - spec/search_qd_spec.rb
97
+ - spec/spec_helper.rb