search_qd 0.0.1

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,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