gbdev-scoped_search 0.2.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/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