gbdev-scoped_search 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Willem van Bergen
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.rdoc ADDED
@@ -0,0 +1,54 @@
1
+ = scoped_search
2
+
3
+ This simple plugin will make it easy to search your ActiveRecord models. Searching is performed using a query string, which should be passed to the named_scope <tt>search_for</tt> that uses SQL %LIKE% conditions for searching. You can specify what fields should be used for searching.
4
+
5
+ == Installation
6
+
7
+ You can enable <tt>scoped_search</tt> as a Ruby gem. You must enable the gem in your <tt>environment.rb</tt> configuration:
8
+
9
+ Rails::Initializer.run do |config|
10
+ ...
11
+ config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search', :source => 'http://gems.github.com/'
12
+ end
13
+
14
+ Make sure the gem is installed by running <tt>rake gems:install</tt> in you project root. You can also install the gem by running <tt>sudo gem install wvanbergen-scoped_search -s http://gems.github.com</tt>
15
+
16
+ You can use scoped_search as a Rails plugin as well, but this is deprecated. Simply download or
17
+ clone scoped_search into your +vendor/plugins+ directory of your project.
18
+
19
+ == Usage
20
+
21
+ First, you have to specify in what columns should be searched:
22
+
23
+ class Project < ActiveRecord::Base
24
+ searchable_on :name, :description
25
+ end
26
+
27
+ Now, the +search_for+ scope is available for queries. You should pass a query string to the scope. This can be empty or nil, in which case all no search conditions are set (and all records will be returned).
28
+
29
+ Project.search_for(params[:q]).each { |project| ... }
30
+
31
+ The search query language is simple. It supports these constructs:
32
+
33
+ * words: some search keywords
34
+ * phrases: "a single search phrase"
35
+ * negation: "look for this" -"but do not look for this phrase and this" -word
36
+
37
+ This functionality is build on named_scope. The searchable_on statement creates
38
+ a named_scope "search_for". Because of this, you can actually chain the call with
39
+ other scopes. For example, this can be very useful if you only want to search in
40
+ projects that are accessible by a given user.
41
+
42
+ class Project < ActiveRecord::Base
43
+ searchable_on :name, :description
44
+ named_scope :accessible_by, lambda { |user| ... }
45
+ end
46
+
47
+ # using chained named_scopes and will_paginate
48
+ Project.accessible_by(current_user).search_for(params[:q]).paginate(:page => params[:page], :include => :tasks)
49
+
50
+ == Disclaimer
51
+
52
+ This Rails plugin is written by Willem van Bergen for the Floorplanner.com website. It is
53
+ released under the <b>MIT license</b>. Please contact me (willem AT vanbergen DOT org if
54
+ you have any suggestions or remarks.
data/Rakefile ADDED
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+
3
+ load 'test/tasks.rake'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ namespace :gem do
9
+
10
+ desc "Sets the version and date of the scoped_search gem. Requires the VERSION environment variable."
11
+ task :version => [:manifest] do
12
+
13
+ require 'date'
14
+
15
+ new_version = ENV['VERSION']
16
+ raise "VERSION is required" unless /\d+(\.\d+)*/ =~ new_version
17
+
18
+ spec_file = Dir['*.gemspec'].first
19
+
20
+ spec = File.read(spec_file)
21
+ spec.gsub!(/^(\s*s\.version\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{new_version}'#{$5}" }
22
+ spec.gsub!(/^(\s*s\.date\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{Date.today.strftime('%Y-%m-%d')}'#{$5}" }
23
+ File.open(spec_file, 'w') { |f| f << spec }
24
+ end
25
+
26
+ task :tag => [:version] do
27
+
28
+ new_version = ENV['VERSION']
29
+ raise "VERSION is required" unless /\d+(\.\d+)*/ =~ new_version
30
+
31
+ sh "git add scoped_search.gemspec .manifest"
32
+ sh "git commit -m \"Set gem version to #{new_version}\""
33
+ sh "git push origin"
34
+ sh "git tag -a \"scoped_search-#{new_version}\" -m \"Tagged version #{new_version}\""
35
+ sh "git push --tags"
36
+ end
37
+
38
+ desc "Builds a ruby gem for scoped_search"
39
+ task :build => [:manifest] do
40
+ system %[gem build scoped_search.gemspec]
41
+ end
42
+
43
+ desc %{Update ".manifest" with the latest list of project filenames. Respect\
44
+ .gitignore by excluding everything that git ignores. Update `files` and\
45
+ `test_files` arrays in "*.gemspec" file if it's present.}
46
+ task :manifest do
47
+ list = Dir['**/*'].sort
48
+ spec_file = Dir['*.gemspec'].first
49
+ list -= [spec_file] if spec_file
50
+
51
+ File.read('.gitignore').each_line do |glob|
52
+ glob = glob.chomp.sub(/^\//, '')
53
+ list -= Dir[glob]
54
+ list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
55
+ puts "excluding #{glob}"
56
+ end
57
+
58
+ if spec_file
59
+ spec = File.read spec_file
60
+ spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
61
+ assignment = $1
62
+ bunch = $2 ? list.grep(/^test.*_test\.rb$/) : list
63
+ '%s%%w(%s)' % [assignment, bunch.join(' ')]
64
+ end
65
+
66
+ File.open(spec_file, 'w') {|f| f << spec }
67
+ end
68
+ File.open('.manifest', 'w') {|f| f << list.join("\n") }
69
+ end
70
+ end
data/TODO ADDED
@@ -0,0 +1,20 @@
1
+ TODO items for named_scope
2
+ ==========================
3
+ Contact willem AT vanbergen DOT org if you want to help out
4
+
5
+
6
+ New features:
7
+ - Search fields of associations as well
8
+ - Allow other search operators than %LIKE%
9
+
10
+ Refactoring:
11
+ - Create a separate class to build the actual SQL queries.
12
+ - For searchable_on(*fields) make it so that instead of fields it accepts options (a hash) where
13
+ :only and :except can be values. That way it is possible for all fields to be loaded except
14
+ the ones specified with :except.
15
+ - Add checks for field types because the latest version of PostgreSQL (version 8.3.3) is more
16
+ strict about searching for strings in columns that are not string types.
17
+
18
+ Documentation & testing:
19
+ - Put something useful in the wiki
20
+ - Add rdoc en comments to code
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'scoped_search'
@@ -0,0 +1,67 @@
1
+ module ScopedSearch
2
+
3
+ module ClassMethods
4
+
5
+ def self.extended(base)
6
+ require 'scoped_search/query_language_parser'
7
+ end
8
+
9
+ # Creates a named scope in the class it was called upon
10
+ def searchable_on(*fields)
11
+ self.cattr_accessor :scoped_search_fields
12
+ self.scoped_search_fields = fields
13
+ self.named_scope :search_for, lambda { |keywords| self.build_scoped_search_conditions(keywords) }
14
+ end
15
+
16
+ # Build a hash that is used for the named_scope search_for.
17
+ # This function will split the search_string into keywords, and search for all the keywords
18
+ # in the fields that were provided to searchable_on
19
+ def build_scoped_search_conditions(search_string)
20
+ if search_string.nil? || search_string.strip.blank?
21
+ return { :conditions => nil }
22
+ else
23
+ conditions = []
24
+ query_params = {}
25
+
26
+ QueryLanguageParser.parse(search_string).each_with_index do |search_condition, index|
27
+ keyword_name = "keyword_#{index}".to_sym
28
+ query_params[keyword_name] = "%#{search_condition.first}%"
29
+
30
+ # a keyword may be found in any of the provided fields, so join the conitions with OR
31
+ if search_condition.length == 2 && search_condition.last == :not
32
+ keyword_conditions = self.scoped_search_fields.map do |field|
33
+ field_name = connection.quote_table_name(table_name) + "." + connection.quote_column_name(field)
34
+ "(#{field_name} NOT LIKE :#{keyword_name.to_s} OR #{field_name} IS NULL)"
35
+ end
36
+ conditions << "(#{keyword_conditions.join(' AND ')})"
37
+ elsif search_condition.length == 2 && search_condition.last == :or
38
+ word1, word2 = query_params[keyword_name].split(' OR ')
39
+
40
+ query_params.delete(keyword_name)
41
+ keyword_name_a = "#{keyword_name.to_s}a".to_sym
42
+ keyword_name_b = "#{keyword_name.to_s}b".to_sym
43
+ query_params[keyword_name_a] = word1
44
+ query_params[keyword_name_b] = word2
45
+
46
+ keyword_conditions = self.scoped_search_fields.map do |field|
47
+ field_name = connection.quote_table_name(table_name) + "." + connection.quote_column_name(field)
48
+ "(#{field_name} LIKE :#{keyword_name_a.to_s} OR #{field_name} LIKE :#{keyword_name_b.to_s})"
49
+ end
50
+ conditions << "(#{keyword_conditions.join(' OR ')})"
51
+ else
52
+ keyword_conditions = self.scoped_search_fields.map do |field|
53
+ field_name = connection.quote_table_name(table_name) + "." + connection.quote_column_name(field)
54
+ "#{field_name} LIKE :#{keyword_name.to_s}"
55
+ end
56
+ conditions << "(#{keyword_conditions.join(' OR ')})"
57
+ end
58
+ end
59
+
60
+ # all keywords must be matched, so join the conditions with AND
61
+ return { :conditions => [conditions.join(' AND '), query_params] }
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods)
@@ -0,0 +1,71 @@
1
+ module ScopedSearch
2
+
3
+ class QueryLanguageParser
4
+
5
+ def parse_query(query = nil)
6
+ return build_conditions_tree(tokenize(query))
7
+ end
8
+
9
+ def self.parse(query)
10
+ self.new.parse_query(query)
11
+ end
12
+
13
+ protected
14
+
15
+ def build_conditions_tree(tokens)
16
+ conditions_tree = []
17
+
18
+ negate = false
19
+ tokens.each do |item|
20
+ case item
21
+ when :not
22
+ negate = true
23
+ else
24
+ if /^.+[ ]OR[ ].+$/ =~ item
25
+ conditions_tree << [item, :or]
26
+ else
27
+ conditions_tree << (negate ? [item, :not] : [item, :like])
28
+ negate = false
29
+ end
30
+ end
31
+ end
32
+ return conditions_tree
33
+ end
34
+
35
+ # **Patterns**
36
+ # Each pattern is sperated by a "|". With regular expressions the order of the expression does matter.
37
+ #
38
+ # ([\w]+[ ]OR[ ][\w]+)
39
+ # ([\w]+[ ]OR[ ]["][\w ]+["])
40
+ # (["][\w ]+["][ ]OR[ ][\w]+)
41
+ # (["][\w ]+["][ ]OR[ ]["][\w ]+["])
42
+ # Any two combinations of letters, numbers and underscores that are seperated by " OR " (a single space must
43
+ # be on each side of the "OR").
44
+ # THESE COULD BE COMBINED BUT BECAUSE OF THE WAY PARSING WORKS THIS IS NOT DONE ON PURPOSE!!
45
+ #
46
+ # ([-]?[\w]+)
47
+ # Any combination of letters, numbers and underscores that may or may not have a dash in front.
48
+ #
49
+ # ([-]?["][\w ]+["])
50
+ # Any combination of letters, numbers, underscores and spaces within double quotes that may or may not have a dash in front.
51
+ def tokenize(query)
52
+ pattern = ['([\w]+[ ]OR[ ][\w]+)',
53
+ '([\w]+[ ]OR[ ]["][\w ]+["])',
54
+ '(["][\w ]+["][ ]OR[ ][\w]+)',
55
+ '(["][\w ]+["][ ]OR[ ]["][\w ]+["])',
56
+ '([-]?[\w]+)',
57
+ '([-]?["][\w ]+["])']
58
+ pattern = Regexp.new(pattern.join('|'))
59
+
60
+ tokens = []
61
+ matches = query.scan(pattern).flatten.compact
62
+ matches.each { |match|
63
+ tokens << :not unless match.index('-').nil?
64
+ # Remove any escaped quotes and any dashes - the dash usually the first character.
65
+ # Remove any additional spaces - more that one.
66
+ tokens << match.gsub(/[-"]/,'').gsub(/[ ]{2,}/, ' ')
67
+ }
68
+ return tokens
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,140 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class QueryLanguageTest < Test::Unit::TestCase
4
+
5
+ # change this function if you switch to another query language parser
6
+ def parse_query(query)
7
+ ScopedSearch::QueryLanguageParser.parse(query)
8
+ end
9
+
10
+ def test_empty_search_query
11
+ parsed = parse_query('')
12
+ assert_equal 0, parsed.length
13
+
14
+ parsed = parse_query("\t \n")
15
+ assert_equal 0, parsed.length
16
+ end
17
+
18
+ def test_single_keyword
19
+ parsed = parse_query('hallo')
20
+ assert_equal 1, parsed.length
21
+ assert_equal 'hallo', parsed.first.first
22
+
23
+ parsed = parse_query(' hallo ')
24
+ assert_equal 1, parsed.length
25
+ assert_equal 'hallo', parsed.first.first
26
+ end
27
+
28
+ def test_multiple_keywords
29
+ parsed = parse_query(' hallo willem')
30
+ assert_equal 2, parsed.length
31
+ assert_equal 'willem', parsed.last.first
32
+
33
+ parsed = parse_query(" hallo willem van\tbergen ")
34
+ assert_equal 4, parsed.length
35
+ assert_equal 'hallo', parsed[0].first
36
+ assert_equal 'willem', parsed[1].first
37
+ assert_equal 'van', parsed[2].first
38
+ assert_equal 'bergen', parsed[3].first
39
+ end
40
+
41
+ def test_quoted_keywords
42
+ parsed = parse_query(' "hallo"')
43
+ assert_equal 1, parsed.length
44
+ assert_equal 'hallo', parsed.first.first
45
+
46
+ parsed = parse_query(' "hallo willem"')
47
+ assert_equal 1, parsed.length
48
+ assert_equal 'hallo willem', parsed.first.first
49
+
50
+ parsed = parse_query(' "hallo willem')
51
+ assert_equal 2, parsed.length
52
+ assert_equal 'hallo', parsed[0].first
53
+ assert_equal 'willem', parsed[1].first
54
+
55
+ parsed = parse_query(' "hallo wi"llem"')
56
+ assert_equal 2, parsed.length
57
+ assert_equal 'hallo wi', parsed[0].first
58
+ assert_equal 'llem', parsed[1].first
59
+ end
60
+
61
+ def test_quote_escaping
62
+ parsed = parse_query(' "hallo wi\\"llem"')
63
+ assert_equal 3, parsed.length
64
+ assert_equal 'hallo', parsed[0].first
65
+ assert_equal 'wi', parsed[1].first
66
+ assert_equal 'llem', parsed[2].first
67
+
68
+ parsed = parse_query('"\\"hallo willem\\""')
69
+ assert_equal 2, parsed.length
70
+ assert_equal 'hallo', parsed[0].first
71
+ assert_equal 'willem', parsed[1].first
72
+ end
73
+
74
+ def test_negation
75
+ parsed = parse_query('-willem')
76
+ assert_equal 1, parsed.length
77
+ assert_equal 'willem', parsed[0].first
78
+ assert_equal :not, parsed[0].last
79
+
80
+ parsed = parse_query('123 -"456 789"')
81
+ assert_equal 2, parsed.length
82
+ assert_equal '123', parsed[0].first
83
+ assert_equal :like, parsed[0].last
84
+
85
+ assert_equal '456 789', parsed[1].first
86
+ assert_equal :not, parsed[1].last
87
+ end
88
+
89
+ def test_or
90
+ parsed = parse_query('Wes OR Hays')
91
+ assert_equal 1, parsed.length
92
+ assert_equal 'Wes OR Hays', parsed[0][0]
93
+ assert_equal :or, parsed[0][1]
94
+
95
+ parsed = parse_query('"Man made" OR Dogs')
96
+ assert_equal 1, parsed.length
97
+ assert_equal 'Man made OR Dogs', parsed[0][0]
98
+ assert_equal :or, parsed[0][1]
99
+
100
+ parsed = parse_query('Cows OR "Frog Toys"')
101
+ assert_equal 1, parsed.length
102
+ assert_equal 'Cows OR Frog Toys', parsed[0][0]
103
+ assert_equal :or, parsed[0][1]
104
+
105
+ parsed = parse_query('"Happy cow" OR "Sad Frog"')
106
+ assert_equal 1, parsed.length
107
+ assert_equal 'Happy cow OR Sad Frog', parsed[0][0]
108
+ assert_equal :or, parsed[0][1]
109
+ end
110
+
111
+ def test_long_string
112
+ str = 'Wes -Hays "Hello World" -"Goodnight Moon" Bob OR Wes "Happy cow" OR "Sad Frog" "Man made" OR Dogs Cows OR "Frog Toys"'
113
+ parsed = parse_query(str)
114
+ assert_equal 8, parsed.length
115
+
116
+ assert_equal 'Wes', parsed[0].first
117
+ assert_equal :like, parsed[0].last
118
+
119
+ assert_equal 'Hays', parsed[1].first
120
+ assert_equal :not, parsed[1].last
121
+
122
+ assert_equal 'Hello World', parsed[2].first
123
+ assert_equal :like, parsed[2].last
124
+
125
+ assert_equal 'Goodnight Moon', parsed[3].first
126
+ assert_equal :not, parsed[3].last
127
+
128
+ assert_equal 'Bob OR Wes', parsed[4].first
129
+ assert_equal :or, parsed[4].last
130
+
131
+ assert_equal 'Happy cow OR Sad Frog', parsed[5].first
132
+ assert_equal :or, parsed[5].last
133
+
134
+ assert_equal 'Man made OR Dogs', parsed[6].first
135
+ assert_equal :or, parsed[6].last
136
+
137
+ assert_equal 'Cows OR Frog Toys', parsed[7].first
138
+ assert_equal :or, parsed[7].last
139
+ end
140
+ end
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ScopedSearchTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ setup_db
7
+ SearchTestModel.create_corpus!
8
+ end
9
+
10
+ def teardown
11
+ teardown_db
12
+ end
13
+
14
+ def test_enabling
15
+ assert !SearchTestModel.respond_to?(:search_for)
16
+ SearchTestModel.searchable_on :string_field, :text_field
17
+ assert SearchTestModel.respond_to?(:search_for)
18
+
19
+ assert_equal ActiveRecord::NamedScope::Scope, SearchTestModel.search_for('test').class
20
+
21
+ end
22
+
23
+ def test_search
24
+ SearchTestModel.searchable_on :string_field, :text_field
25
+
26
+ assert_equal 15, SearchTestModel.search_for('').count
27
+ assert_equal 0, SearchTestModel.search_for('456').count
28
+ assert_equal 2, SearchTestModel.search_for('hays').count
29
+ assert_equal 1, SearchTestModel.search_for('hay ob').count
30
+ assert_equal 13, SearchTestModel.search_for('o').count
31
+ assert_equal 2, SearchTestModel.search_for('-o').count
32
+ assert_equal 13, SearchTestModel.search_for('-Jim').count
33
+ assert_equal 1, SearchTestModel.search_for('Jim -Bush').count
34
+ assert_equal 1, SearchTestModel.search_for('"Hello World" -"Goodnight Moon"').count
35
+ assert_equal 2, SearchTestModel.search_for('Wes OR Bob').count
36
+ assert_equal 3, SearchTestModel.search_for('"Happy cow" OR "Sad Frog"').count
37
+ assert_equal 3, SearchTestModel.search_for('"Man made" OR Dogs').count
38
+ assert_equal 2, SearchTestModel.search_for('Cows OR "Frog Toys"').count
39
+ end
40
+
41
+ end
42
+
43
+
data/test/tasks.rake ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ desc 'Test the scoped_search plugin.'
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.verbose = true
7
+ t.libs << 'test'
8
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'active_record'
4
+
5
+ require "#{File.dirname(__FILE__)}/../lib/scoped_search"
6
+
7
+ def setup_db
8
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table :search_test_models do |t|
11
+ t.string :string_field
12
+ t.text :text_field
13
+ t.string :ignored_field
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
18
+
19
+ def teardown_db
20
+ ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
21
+ end
22
+
23
+ class SearchTestModel < ActiveRecord::Base
24
+ def self.create_corpus!
25
+ create!(:string_field => "Programmer 123", :text_field => nil, :ignored_field => "123456")
26
+ create!(:string_field => "Jim", :text_field => "Henson", :ignored_field => "123456a")
27
+ create!(:string_field => "Jim", :text_field => "Bush", :ignored_field => "123456b")
28
+ create!(:string_field => "Wes", :text_field => "Hays", :ignored_field => "123456c")
29
+ create!(:string_field => "Bob", :text_field => "Hays", :ignored_field => "123456d")
30
+ create!(:string_field => "Dogs", :text_field => "Pit Bull", :ignored_field => "123456e")
31
+ create!(:string_field => "Dogs", :text_field => "Eskimo", :ignored_field => "123456f")
32
+ create!(:string_field => "Cows", :text_field => "Farms", :ignored_field => "123456g")
33
+ create!(:string_field => "Hello World", :text_field => "Hello Moon", :ignored_field => "123456h")
34
+ create!(:string_field => "Hello World", :text_field => "Goodnight Moon", :ignored_field => "123456i")
35
+ create!(:string_field => "Happy Cow", :text_field => "Sad Cow", :ignored_field => "123456j")
36
+ create!(:string_field => "Happy Frog", :text_field => "Sad Frog", :ignored_field => "123456k")
37
+ create!(:string_field => "Excited Frog", :text_field => "Sad Frog", :ignored_field => "123456l")
38
+ create!(:string_field => "Man made", :text_field => "Woman made", :ignored_field => "123456m")
39
+ create!(:string_field => "Cat Toys", :text_field => "Frog Toys", :ignored_field => "123456n")
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gbdev-scoped_search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Willem van Bergen
8
+ - Wes Hays
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2008-09-13 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Scoped search makes it easy to search your ActiveRecord-based models. It will create a named scope according to a provided query string. The named_scope can be used like any other named_scope, so it can be cchained or combined with will_paginate.
18
+ email:
19
+ - willem@vanbergen.org
20
+ - weshays@gbdev.com
21
+ executables: []
22
+
23
+ extensions: []
24
+
25
+ extra_rdoc_files: []
26
+
27
+ files:
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - TODO
32
+ - init.rb
33
+ - lib
34
+ - lib/scoped_search
35
+ - lib/scoped_search.rb
36
+ - lib/scoped_search/query_language_parser.rb
37
+ - test
38
+ - test/query_language_test.rb
39
+ - test/search_for_test.rb
40
+ - test/tasks.rake
41
+ - test/test_helper.rb
42
+ has_rdoc: false
43
+ homepage: http://github.com/wvanbergen/scoped_search/wikis
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: A Rails plugin to search your models using a named_scope
68
+ test_files:
69
+ - test/query_language_test.rb
70
+ - test/search_for_test.rb