awesome_search 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,146 @@
1
+ module Awesome
2
+ class Search
3
+
4
+ # Mixins
5
+ include Definitions::Bits
6
+ include Definitions::Types
7
+ include Definitions::Filters
8
+ include Definitions::Stopwords
9
+
10
+ @@search_types ||= {:search_types_to_type_modifiers => {},
11
+ :search_type_inceptions => {},
12
+ :search_type_regexes => {}}
13
+ @@search_filters ||= {:search_filters_to_filter_modifiers => {},
14
+ :filter_modifiers_to_search_filters => {}}
15
+
16
+ #Some defaults if stopwords are set to be used (by default they are turned off)
17
+ @@search_stopwords ||= {:standard => %w(an and are as at be but by for if in into is it no not of on or s such t that the their then there these they this to was will with),
18
+ :custom => []}
19
+
20
+ def self.configure_search_types(&block)
21
+ yield @@search_types
22
+ end
23
+
24
+ def self.configure_search_filters(&block)
25
+ yield @@search_filters
26
+ end
27
+
28
+ def self.configure_search_stopwords(&block)
29
+ yield @@search_stopwords
30
+ end
31
+
32
+ #TODO: Put these into a config block
33
+ cattr_accessor :verbose
34
+ cattr_accessor :check_inception
35
+ cattr_accessor :protect_types
36
+ cattr_accessor :protect_filters
37
+
38
+ attr_accessor(:search_text,
39
+ :search_query,
40
+ :search_type,
41
+ :search_filters,
42
+ :search_locale,
43
+ :stopwords,
44
+ :found,
45
+ :tally,
46
+ :success,
47
+ :endpoint,
48
+ :invalid_inception,
49
+ :redirect_url,
50
+ :page,
51
+ :per_page,
52
+ :replacement_for,
53
+ :multiple_types_as_one,
54
+ :tokenize_quoted,
55
+ :tokenize_unquoted,
56
+ :tokenize_without_quoted,
57
+ :quoted_exact_phrases,
58
+ :unquoted_exact_phrases,
59
+ :query_without_exact_phrases,
60
+ :gowords,
61
+ :search_tokens,
62
+ :highlight_tokens)
63
+
64
+ #CLASS METHODS
65
+ #Main focus of class methods is determining which sort of AwesomeSearch subclass we need to instantiate for the search
66
+ def initialize(*args)
67
+ @multiple_types_as_one = args.first[:multiple_types_as_one]
68
+ @stopwords = args.first[:stopwords] ?
69
+ args.first[:stopwords].is_a?(Array) ?
70
+ args.first[:stopwords] :
71
+ args.first[:stopwords].is_a?(Symbol) ?
72
+ self.class.stopwords(args.first[:stopwords]) :
73
+ self.class.stopwords(:both) :
74
+ args.first[:stopwords] != false ?
75
+ self.class.stopwords(:standard) :
76
+ []
77
+ @page = args.first[:page]
78
+ @per_page = args.first[:per_page]
79
+ @search_text = args.first[:search_text] # a string
80
+ @search_filters = args.first[:search_filters] # a ruby object (string, array, hash) to be used by subclasss search classes as a filter
81
+ #Chicken-egg problem: search_tokens and highlight_tokens both need to be set within clean_search_text (and they are!) because they must work with the text cleaned by the clean_search_text methods
82
+ @search_tokens = args.first[:search_tokens] # an array of the query terms as tokens after being cleaned, unless passed in as param (not sure why this would ever be desired, but why not allow it jic?) When not set in args, will be set by clean_search_text methods
83
+ @highlight_tokens = args.first[:highlight_tokens] # an array of the query terms as unquoted tokens after being cleaned, unless passed in as param (not sure why this would ever be desired, but why not allow it jic?) When not set in args, will be set by clean_search_text methods
84
+ @search_query = self.clean_search_text # a string to be set based on the search text by removing the search modifiers from the search text
85
+ @search_type = args.first[:search_type] # a symring (symring methods are in the Bits mixin)
86
+ @search_locale = args.first[:search_locale] # a symring (symring methods are in the Bits mixin)
87
+ @found = nil
88
+ @tally = nil
89
+ @success = true
90
+ @redirect_url = nil
91
+ @replacement_for = nil # stores the name of, or explanatory text about, the primary search if this search is a secondary (replacement) search for a primary search that returned no results
92
+ if self.class.check_inception && !self.valid_search_type_inception?
93
+ puts "search type inception is invalid (no type regex matches query string)" if Awesome::Search.verbose
94
+ @invalid_inception = true
95
+ end
96
+ end
97
+
98
+ def to_s
99
+ "search_query: #{self.search_query} \
100
+ \n\rsearch_tokens: #{self.search_tokens.inspect} \
101
+ \n\rhighlight_tokens: #{self.highlight_tokens.inspect} \
102
+ \n\rtally: #{self.tally} \
103
+ \n\rsuccess? #{self.success ? 'Yes' : 'No'}"
104
+ end
105
+
106
+ def clean_search_text
107
+ txt = Awesome::Triage.clean_search_text(self.search_text)
108
+ txt = Awesome::Search.clean_search_text(txt, self.multiple_types_as_one)
109
+ txt = self.process_stopwords(txt)
110
+ txt
111
+ end
112
+
113
+ def self.clean_search_text(text, multiple_types_as_one = false)
114
+ txt = text.gsub(self.search_type_modifiers_regex(true, multiple_types_as_one), "")
115
+ txt.gsub(self.search_filter_modifiers_regex(true), "")
116
+ end
117
+
118
+ # Main method used by the app to search
119
+ # Instantiates an Awesome::Triage which will handle setting up the search
120
+ def self.results_for(anytext, types, locales, filters = nil, multiple_types_as_one = false, page = nil, per_page = nil)
121
+ # 1. if the search is blank return nil
122
+ puts "anytext is blank" if Awesome::Search.verbose && anytext.blank?
123
+ return nil if anytext.blank?
124
+ Triage.new(:text => anytext, :types => types, :locales => locales, :filters => filters, :multiple_types_as_one => multiple_types_as_one, :page => page, :per_page => per_page)
125
+ end
126
+
127
+ #INSTANCE METHODS
128
+
129
+ # get_results is called on the instances of the subclasses.
130
+ # The subclasses override the methods called within it to return their customized stuff
131
+ # The subclasses call super to ensure that the data is valid before sending out the search.
132
+ def get_results
133
+ # 1. if the search is blank do NOT run the search (handled in subclasses)
134
+ !self.search_text.blank? && !self.search_query.blank? && !self.search_type.blank? && !self.search_locale.blank?
135
+ end
136
+ end
137
+ end
138
+
139
+ #handles a param of any string, and returns all known matches for that string
140
+ #def self.all_matches(search_text = "")
141
+ # self.match_types(search_text).map do |type|
142
+ # {type => self.matches_for_type(type, search_text, true)}
143
+ # end.compact | self.match_types(search_text).map do |type|
144
+ # {type => self.matches_for_type(type, search_text, false)}
145
+ # end.compact
146
+ #end
@@ -0,0 +1,16 @@
1
+ module Awesome
2
+ class SuperSearch < Awesome::Search
3
+ # Instance method Mixins
4
+ def get_results
5
+ return nil unless super
6
+ # initialize the search result values since
7
+ # if we've gotten this far we have a real search being executed
8
+ # tally and results are set to nil in the main initializer
9
+ # so that we can differentiate between searches that aborted
10
+ # and searches that completed but had no results.
11
+ self.found = []
12
+ self.tally = 0
13
+ return true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,122 @@
1
+ module Awesome
2
+ class Triage < Array
3
+
4
+ # Mixins
5
+ include Definitions::Bits
6
+ include Definitions::Locales
7
+
8
+ #The following is set in a config block
9
+ @@search_locales ||= {:search_locales_to_classes => {},
10
+ :search_locales_to_locale_modifiers => {},
11
+ :locale_modifiers_to_search_locales => {},
12
+ :search_locales_to_stopwords => {}}
13
+
14
+ def self.configure_search_locales(&block)
15
+ yield @@search_locales
16
+ end
17
+
18
+ #TODO: Put these into a config block
19
+ cattr_accessor :verbose
20
+
21
+ attr_accessor( :text,
22
+ :types,
23
+ :locales,
24
+ :filters,
25
+ :multiple_types_as_one,
26
+ :redirect_url,
27
+ :page,
28
+ :per_page )
29
+
30
+ def initialize(*args)
31
+ super()
32
+ @multiple_types_as_one = !args.first[:multiple_types_as_one].nil? ? args.first[:multiple_types_as_one] : false# Boolean: should the array of types be sent through to a single search, or iterated over to separate searches like locales?
33
+ @page = args.first[:page]
34
+ @per_page = args.first[:per_page]
35
+ @text = args.first[:text] # a string
36
+ @types = args.first[:types].respond_to?(:each) ? args.first[:types] : [args.first[:types]] # a symring, or array thereof (symring methods are in the Bits mixin)
37
+ @locales = args.first[:locales].respond_to?(:each) ? args.first[:locales] : [args.first[:locales]] # a symring, or array thereof (symring methods are in the Bits mixin)
38
+ @filters = args.first[:filters] # a ruby object (string, array, hash) to be used by subclasss search classes as a filter
39
+ @redirect_url = nil
40
+ # 1. Handle all locale modifiers, by creating a different search for each
41
+ self.locales.each do |locale|
42
+ puts "initializing locale: #{locale}" if self.class.verbose_locales
43
+ locale = self.class.make_symring(locale)
44
+ klass = self.class.get_class_for_locale(locale)
45
+ # 2. if there is no matching class for the locale return nil
46
+ puts "klass is nil" if Awesome::Triage.verbose && klass.nil?
47
+ next if klass.nil?
48
+ # 3. If there is a locale modifier, then make sure it matches the locale being searched or return nil
49
+ locale_mods = self.class.valid_locale_modifiers(self.text, locale)
50
+ puts "locale_mods is empty" if Awesome::Triage.verbose && locale_mods.empty?
51
+ next if locale_mods.empty?
52
+ self.add_search(klass, locale, self.class.stopwords_for_locale(locale))
53
+ #if we hit a search that tells us to redirect, do not continue
54
+ break if self.redirect_url
55
+ end
56
+ self.compact!
57
+ self
58
+ end
59
+
60
+ def add_search(klass, locale, stopwords)
61
+ # 4. Handle all type modifiers, by creating a different search for each
62
+ if self.multiple_types_as_one
63
+ new_search = self.new_search_for_types_and_locale(klass, self.types, locale, stopwords)
64
+ self.redirect_url = new_search.redirect_url
65
+ self << new_search
66
+ else
67
+ self.types.each do |typ|
68
+ new_search = self.new_search_for_type_and_locale(klass, typ, locale, stopwords)
69
+ self.redirect_url = new_search.redirect_url
70
+ self << new_search
71
+ end
72
+ end
73
+ end
74
+
75
+ # single type
76
+ def new_search_for_type_and_locale(klass, type, locale, stopwords)
77
+ new_search = nil
78
+ type = self.class.make_symring(type)
79
+ # 6. If there is a type modifier, then make sure it matches the type being searched or return nil
80
+ valid_type_mods = Awesome::Search.protect_types ? klass.valid_type_modifiers(self.text, type, self.multiple_types_as_one) : [type]
81
+ puts "valid_type_mods is empty" if Awesome::Triage.verbose && valid_type_mods.empty?
82
+ unless valid_type_mods.empty?
83
+ new_search = klass.new({:stopwords => stopwords, :multiple_types_as_one => self.multiple_types_as_one, :search_text => self.text, :search_type => type, :search_locale => locale, :search_filters => self.filter_mods(klass), :page => self.page, :per_page => self.per_page})
84
+ new_search.get_results
85
+ end
86
+ return new_search
87
+ end
88
+
89
+ # multiple types
90
+ def new_search_for_types_and_locale(klass, types, locale, stopwords)
91
+ new_search = nil
92
+ # 6. If there is a type modifier, then make sure it matches the type being searched or return nil
93
+ valid_type_mods = klass.protect_types ? klass.valid_type_modifiers(self.text, types, self.multiple_types_as_one) : types
94
+ puts "valid_type_mods is empty" if Awesome::Triage.verbose && valid_type_mods.empty?
95
+ unless valid_type_mods.empty?
96
+ new_search = klass.new({:stopwords => stopwords, :multiple_types_as_one => self.multiple_types_as_one, :search_text => self.text, :search_type => valid_type_mods, :search_locale => locale, :search_filters => self.filter_mods(klass), :page => self.page, :per_page => self.per_page})
97
+ new_search.get_results
98
+ end
99
+ return new_search
100
+ end
101
+
102
+ def filter_mods(klass)
103
+ valid_filter_mods = nil
104
+ if self.filters
105
+ # 5. If there is a filter modifier, then make sure it matches the locale being searched or return nil
106
+ valid_filter_mods = Awesome::Search.protect_filters ? klass.valid_filter_modifiers(self.text, self.filters) : self.filters
107
+ puts "valid_filter_mods is empty" if Awesome::Triage.verbose && valid_filter_mods.empty?
108
+ end
109
+ valid_filter_mods
110
+ end
111
+
112
+ def clean_search_text
113
+ txt = Awesome::Triage.clean_search_text(self.search_text)
114
+ Awesome::Search.clean_search_text(txt, self.multiple_types_as_one)
115
+ end
116
+
117
+ def self.clean_search_text(text)
118
+ text.gsub(self.search_locale_modifiers_regex(true), "")
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,7 @@
1
+ require "awesome/definitions/bits" unless defined?(Awesome::Definitions::Bits)
2
+ require "awesome/definitions/locales" unless defined?(Awesome::Definitions::Locales)
3
+ require "awesome/definitions/filters" unless defined?(Awesome::Definitions::Filters)
4
+ require "awesome/definitions/types" unless defined?(Awesome::Definitions::Types)
5
+ require "awesome/triage" unless defined?(Awesome::Triage)
6
+ require "awesome/search" unless defined?(Awesome::Search)
7
+ require "awesome/super_search" unless defined?(Awesome::SuperSearch)
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "active_support" unless defined?(ActiveSupport)
2
+
3
+ require "awesome_search" unless defined?(Awesome::Search)
data/test/helper.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require File.join(File.dirname(__FILE__), "..", "init")
8
+
9
+ require "test/search_classes/amazon"
10
+ require "test/search_classes/ebay"
11
+ require "test/search_classes/google"
12
+ require "test/search_classes/local"
13
+
14
+ class Test::Unit::TestCase
15
+ Awesome::Search.protect_types = true
16
+ Awesome::Search.protect_filters = true
17
+ Awesome::Search.verbose = false
18
+ Awesome::Search.verbose_types = false
19
+ Awesome::Search.verbose_filters = false
20
+ Awesome::Triage.verbose = false
21
+ Awesome::Triage.verbose_locales = false
22
+
23
+ Awesome::Triage.configure_search_locales do |config|
24
+ config[:search_locales_to_classes] =
25
+ { ":local" => "Local",
26
+ ":amazon" => "Amazon",
27
+ ":google" => "Google",
28
+ ":ebay" => "Ebay" }
29
+ config[:search_locales_to_locale_modifiers] =
30
+ {
31
+ ":local" =>
32
+ [ ":local" ],
33
+ ":amazon" =>
34
+ [ ":amazon",
35
+ ":amzn",
36
+ ":amz",
37
+ ":am"],
38
+ ":google" =>
39
+ [ ":google",
40
+ ":goog",
41
+ ":goo",
42
+ ":go"],
43
+ ":ebay" =>
44
+ [ ":ebay",
45
+ ":eby",
46
+ ":eb"]
47
+ }
48
+ config[:locale_modifiers_to_search_locales] =
49
+ {
50
+ ":local" => ":local",
51
+ ":amazon" => ":amazon",
52
+ ":amz" => ":amazon",
53
+ ":amzn" => ":amazon",
54
+ ":am" => ":amazon",
55
+ ":google" => ":google",
56
+ ":goog" => ":google",
57
+ ":goo" => ":google",
58
+ ":go" => ":google",
59
+ ":ebay" => ":ebay",
60
+ ":eby" => ":ebay",
61
+ ":eb" => ":ebay"
62
+ }
63
+ end
64
+
65
+ Awesome::Search.configure_search_types do |config|
66
+ config[:search_types_to_type_modifiers] =
67
+ {
68
+ ":isbn" => ":isbn",
69
+ ":sku" => ":sku",
70
+ ":upc" => ":upc",
71
+ ":asin" => ":asin",
72
+ ":id" => ":dbid",
73
+ ":text" => ":text"
74
+ }
75
+ # When using observer, how long should we wait before sending out search queries?
76
+ config[:search_type_inceptions] =
77
+ {
78
+ ":isbn" => 10,
79
+ ":sku" => 8,
80
+ ":upc" => 10,
81
+ ":asin" => 10,
82
+ ":id" => 1,
83
+ ":text" => 4
84
+ }
85
+ config[:search_type_regexes] =
86
+ {
87
+ ":isbn" => /\d{10}$|^\d{13}/, #match 10 or 13 digits for isbn
88
+ ":sku" => /[0-9a-zA-Z\-]+/, #match any alphanumeric
89
+ ":upc" => /\d{10}$|^\d{12}/,
90
+ ":asin" => /\w{10}/,
91
+ ":id" => /\d+/,
92
+ ":text" => /\w{4}/
93
+ }
94
+ end
95
+
96
+ Awesome::Search.configure_search_filters do |config|
97
+ config[:search_filters_to_filter_modifiers] =
98
+ {
99
+ ":all" =>
100
+ [ ":all",
101
+ ":every" ],
102
+ ":spotted" =>
103
+ [ "spotted" ],
104
+ ":old" =>
105
+ [ ":old",
106
+ ":ancient",
107
+ ":wrinkly",
108
+ ":grey"],
109
+ ":new" =>
110
+ [ ":new",
111
+ ":shiny",
112
+ ":fresh"]
113
+ }
114
+ config[:filter_modifiers_to_search_filters] =
115
+ {
116
+ ":all" => ":all",
117
+ ":every" => ":all",
118
+ ":spotted" => ":spotted",
119
+ ":old" => ":old",
120
+ ":ancient" => ":old",
121
+ ":wrinkly" => ":old",
122
+ ":grey" => ":old",
123
+ ":new" => ":new",
124
+ ":shiny" => ":new",
125
+ ":fresh" => ":new"
126
+ }
127
+ end
128
+ end
@@ -0,0 +1,6 @@
1
+ class Amazon < Awesome::SuperSearch
2
+ def get_results
3
+ return nil unless super
4
+ nil
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Ebay < Awesome::SuperSearch
2
+ def get_results
3
+ return nil unless super
4
+ nil
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Google < Awesome::SuperSearch
2
+ def get_results
3
+ return nil unless super
4
+ nil
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Local < Awesome::SuperSearch
2
+ def get_results
3
+ return nil unless super
4
+ nil
5
+ end
6
+ end
@@ -0,0 +1,66 @@
1
+ require 'test/helper'
2
+
3
+ class TestAwesomeSearch < Test::Unit::TestCase
4
+
5
+ context "An Awesome::Search instance" do
6
+ setup do
7
+ @awesome = Awesome::Search.new({:search_text => "this is a test", :search_type => :isbn, :search_locale => :amazon})
8
+ end
9
+
10
+ should "return search_query" do
11
+ assert_equal 'this is a test', @awesome.search_query
12
+ end
13
+ should "return search_type" do
14
+ assert_equal :isbn, @awesome.search_type
15
+ end
16
+ should "return search_locale" do
17
+ assert_equal :amazon, @awesome.search_locale
18
+ end
19
+ end
20
+
21
+ context "An Awesome::Search instance with query modifiers" do
22
+ setup do
23
+ @awesome = Awesome::Search.new({:search_text => ":ebay :sku this is a test"})
24
+ end
25
+
26
+ should "return search_query without query modifiers" do
27
+ assert_equal 'this is a test', @awesome.search_query
28
+ end
29
+ should "search_type should be nil" do
30
+ assert_equal nil, @awesome.search_type
31
+ end
32
+ should "return search_locale" do
33
+ assert_equal nil, @awesome.search_locale
34
+ end
35
+ end
36
+
37
+ context "#results_for with invalid params" do
38
+ setup do
39
+ @invalid_search = Awesome::Search.results_for(":local :text this is a test", ":upc", ":ebay")
40
+ end
41
+
42
+ should "invalid search should return nil" do
43
+ assert @invalid_search.empty?
44
+ end
45
+ end
46
+
47
+ context "#results_for with valid params" do
48
+ setup do
49
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn this is a test", ":text", ":local")
50
+ end
51
+
52
+ should "should return an array" do
53
+ assert @searches.is_a?(Array)
54
+ end
55
+
56
+ should "for a single type, get a single result set" do
57
+ assert @searches.length == 1
58
+ end
59
+ should "for a single type result set has results" do
60
+ assert !@searches.first.found.nil?
61
+ end
62
+ should "valid search should return integer tally" do
63
+ assert @searches.first.tally.is_a?(Integer)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/helper'
2
+
3
+ class TestMultiple < Test::Unit::TestCase
4
+
5
+ context "#results_for with multiple types AND multiple locales" do
6
+ setup do
7
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn this is a test 1234567890", [":text",":isbn"], [":local",":amazon"])
8
+ end
9
+ should "should return an array" do
10
+ assert @searches.is_a?(Array)
11
+ end
12
+
13
+ should "returns multiple result sets" do
14
+ assert @searches.length > 1
15
+ end
16
+ should "all have results" do
17
+ assert @searches.select {|x| x.found.is_a?(Array)}.length == 4
18
+ end
19
+ should "all have tally" do
20
+ assert @searches.select {|x| x.tally.is_a?(Integer)}.length == 4
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'test/helper'
2
+
3
+ class TestMultipleFilters < Test::Unit::TestCase
4
+
5
+ context "#results_for with multiple filters" do
6
+ setup do
7
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn :old this is a test 1234567890", ":text", ":local", [":old",":spotted",":shiny",":new"])
8
+ end
9
+ should "should return an array" do
10
+ assert @searches.is_a?(Array)
11
+ end
12
+
13
+ should "returns 1 result set per type:locale combination" do
14
+ assert @searches.length == 1
15
+ end
16
+ should "all have results" do
17
+ assert @searches.select {|x| x.found.is_a?(Array)}.length == 1
18
+ end
19
+ should "all have tally" do
20
+ assert @searches.select {|x| x.tally.is_a?(Integer)}.length == 1
21
+ end
22
+ should "all have search_filters" do
23
+ assert @searches.select {|x| !x.search_filters.nil?}.length == 1
24
+ end
25
+ should "have search_filters == [':old',':spotted',':new']" do
26
+ #:shiny is filtered out, as it is not a filter mod for the backend, only an alias for the front end
27
+ assert @searches.select {|x| x.search_filters == [":old",":spotted",":new"]}.length == 1
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/helper'
2
+
3
+ class TestMultipleLocales < Test::Unit::TestCase
4
+
5
+ context "#results_for with multiple locales" do
6
+ setup do
7
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn this is a test", ":text", [":local",":amazon"])
8
+ end
9
+ should "should return an array" do
10
+ assert @searches.is_a?(Array)
11
+ end
12
+
13
+ should "returns 1 result set per type:locale combination" do
14
+ assert @searches.length == 2
15
+ end
16
+ should "all have results" do
17
+ assert @searches.select {|x| x.found.is_a?(Array)}.length == 2
18
+ end
19
+ should "all have tally" do
20
+ assert @searches.select {|x| x.tally.is_a?(Integer)}.length == 2
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,42 @@
1
+ require 'test/helper'
2
+
3
+ class TestMultipleTypes < Test::Unit::TestCase
4
+
5
+ context "#results_for with multiple types as multiple types" do
6
+ setup do
7
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn this is a test 1234567890", [":text", ":isbn"], ":local")
8
+ end
9
+ should "should return an array" do
10
+ assert @searches.is_a?(Array)
11
+ end
12
+
13
+ should "returns 1 result set per type:locale combination" do
14
+ assert @searches.length == 2
15
+ end
16
+ should "all have results" do
17
+ assert @searches.select {|x| x.found.is_a?(Array)}.length == 2
18
+ end
19
+ should "all have tally" do
20
+ assert @searches.select {|x| x.tally.is_a?(Integer)}.length == 2
21
+ end
22
+ end
23
+
24
+ context "#results_for with multiple types as single type" do
25
+ setup do
26
+ @searches = Awesome::Search.results_for(":local :amazon :text :isbn this is a test 1234567890", [":text", ":isbn"], ":local", nil, true)
27
+ end
28
+ should "should return an array" do
29
+ assert @searches.is_a?(Array)
30
+ end
31
+
32
+ should "returns 1 result set per type:locale combination" do
33
+ assert @searches.length == 1
34
+ end
35
+ should "all have results" do
36
+ assert @searches.select {|x| x.found.is_a?(Array)}.length == 1
37
+ end
38
+ should "all have tally" do
39
+ assert @searches.select {|x| x.tally.is_a?(Integer)}.length == 1
40
+ end
41
+ end
42
+ end