sander6-enygma 0.0.7 → 0.1.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.
@@ -0,0 +1,43 @@
1
+ module Enygma
2
+ module Resource
3
+
4
+ class InvalidInclusionClass < StandardError; end
5
+
6
+ class << self
7
+ def included(base)
8
+ if defined?(ActiveRecord) && base.ancestors.include?(ActiveRecord::Base)
9
+ configure_for_active_record(base)
10
+ elsif defined?(Sequel) && base.ancestors.include?(Sequel::Model)
11
+ configure_for_sequel_model(base)
12
+ elsif defined?(Datamapper) && base.included_modules.include?(Datamapper::Resource)
13
+ configure_for_datamapper_resource(base)
14
+ else
15
+ raise InvalidInclusionClass, "Enygma::Resource has to be included in a subclass of ActiveRecord::Base or Sequel::Model or a class including Datamapper::Resource! You might want to try just including Enygma."
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def configure_for_active_record(base)
22
+ base.__send__(:include, Enygma)
23
+ base.configure_enygma do
24
+ adapter :active_record
25
+ table base
26
+ end
27
+ end
28
+
29
+ def configure_for_sequel_model(base)
30
+ base.__send__(:include, Enygma)
31
+ base.configure_enygma do
32
+ adapter :sequel
33
+ table base
34
+ end
35
+ end
36
+
37
+ def configure_for_datamapper_resource(base)
38
+ raise "Datamapper support isn't implemented yet! Sorry!"
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,250 @@
1
+ module Enygma
2
+
3
+ class Search
4
+
5
+ class InvalidFilter < StandardError
6
+ def message
7
+ "You can only filter on an Array or Range of values."
8
+ end
9
+ end
10
+
11
+ class InvalidSphinxQuery < StandardError
12
+ def message
13
+ "Sphinx rejected the query; perhaps you're trying to search on a non-existent index?"
14
+ end
15
+ end
16
+
17
+ class MultipleResultSetsError < StandardError
18
+ def message
19
+ "Results were returned for multiple tables, so some attributes are ambiguous."
20
+ end
21
+ end
22
+
23
+ class AmbiguousIndexes < StandardError
24
+ def message
25
+ "You haven't specified which indexes go to which table!"
26
+ end
27
+ end
28
+
29
+ MATCH_MODES = {
30
+ :all => Sphinx::Client::SPH_MATCH_ALL,
31
+ :any => Sphinx::Client::SPH_MATCH_ANY,
32
+ :phrase => Sphinx::Client::SPH_MATCH_PHRASE,
33
+ :boolean => Sphinx::Client::SPH_MATCH_BOOLEAN,
34
+ :extended => Sphinx::Client::SPH_MATCH_EXTENDED,
35
+ :full => Sphinx::Client::SPH_MATCH_FULLSCAN,
36
+ :extended2 => Sphinx::Client::SPH_MATCH_EXTENDED2
37
+ }
38
+
39
+ GROUP_FUNCTIONS = {
40
+ :day => Sphinx::Client::SPH_GROUPBY_DAY,
41
+ :week => Sphinx::Client::SPH_GROUPBY_WEEK,
42
+ :month => Sphinx::Client::SPH_GROUPBY_MONTH,
43
+ :year => Sphinx::Client::SPH_GROUPBY_YEAR,
44
+ :attr => Sphinx::Client::SPH_GROUPBY_ATTR,
45
+ :pair => Sphinx::Client::SPH_GROUPBY_ATTRPAIR
46
+ }
47
+
48
+ SORT_MODES = {
49
+ :relevance => Sphinx::Client::SPH_SORT_RELEVANCE,
50
+ :date_desc => Sphinx::Client::SPH_SORT_ATTR_DESC,
51
+ :date_asc => Sphinx::Client::SPH_SORT_ATTR_ASC,
52
+ :time_segments => Sphinx::Client::SPH_SORT_TIME_SEGMENTS,
53
+ :extended => Sphinx::Client::SPH_SORT_EXTENDED,
54
+ :expression => Sphinx::Client::SPH_SORT_EXPR
55
+ }
56
+
57
+ def initialize(*args, &block)
58
+ overrides = args.last.is_a?(Hash) ? args.pop : {}
59
+
60
+ config = if args.first.is_a?(Enygma::Configuration)
61
+ args.first
62
+ elsif block_given?
63
+ Enygma::Configuration.new(&block)
64
+ else
65
+ raise NoConfiguration, "You must supply an Enygma::Configuration object or definition block to create an Enygma::Search!"
66
+ end
67
+
68
+ @database = {
69
+ :adapter => config.adapter,
70
+ :table => config.table
71
+ }
72
+ @sphinx = Sphinx::Client.new
73
+
74
+ @indexes = config.indexes
75
+ @term = overrides[:term] || ""
76
+ @target_attr = config.target_attr
77
+ @match_mode = MATCH_MODES[config.match_mode]
78
+ @key_prefix = config.key_prefix || ''
79
+
80
+ @latitude = config.latitude
81
+ @longitude = config.longitude
82
+
83
+ @limit = @sphinx.instance_variable_get(:@limit)
84
+ @offset = @sphinx.instance_variable_get(:@offset)
85
+ @max = @sphinx.instance_variable_get(:@maxmatches)
86
+ @cutoff = @sphinx.instance_variable_get(:@cutoff)
87
+
88
+ @return_attributes = []
89
+
90
+ @sphinx.SetServer(config.sphinx[:host], config.sphinx[:port])
91
+ @sphinx.SetMatchMode(@match_mode)
92
+ end
93
+
94
+ def run
95
+ query_database(query_sphinx)
96
+ end
97
+
98
+ def method_missing(name, *args, &block)
99
+ self.run.__send__(name, *args, &block)
100
+ end
101
+
102
+ def count
103
+ query_sphinx['total']
104
+ end
105
+
106
+ def for(*terms)
107
+ @term = terms.join(" ")
108
+ self
109
+ end
110
+
111
+ def in(table)
112
+ @database[:table] = table
113
+ self
114
+ end
115
+
116
+ def using_match_mode(match_mode)
117
+ @match_mode = MATCH_MODES[match_mode]
118
+ @sphinx.SetMatchMode(@match_mode)
119
+ self
120
+ end
121
+
122
+ def using_indexes(*indexes)
123
+ @indexes = indexes.collect { |idx| Enygma.indexify(idx) }
124
+ self
125
+ end
126
+ alias_method :using_index, :using_indexes
127
+
128
+ def filter(attribute, values, exclude = false)
129
+ attribute = attribute.to_s
130
+ case values
131
+ when Array
132
+ @sphinx.SetFilter(attribute, values, exclude)
133
+ when Range
134
+ if values.begin.is_a?(Float) || values.end.is_a?(Float)
135
+ @sphinx.SetFilterFloatRange(attribute, values.begin.to_f, values.end.to_f, exclude)
136
+ else
137
+ @sphinx.SetFilterRange(attribute, values.begin.to_i, values.end.to_i, exclude)
138
+ end
139
+ when Numeric
140
+ @sphinx.SetFilterFloatRange(attribute, 0.0, values.to_f, exclude)
141
+ else
142
+ raise InvalidFilter
143
+ end
144
+ self
145
+ end
146
+
147
+ def exclude(attribute, values)
148
+ filter(attribute, values, true)
149
+ end
150
+
151
+ def group_by(attribute, function, sort = "@group DESC")
152
+ @sphinx.SetGroupBy(attribute, GROUP_FUNCTIONS[function], sort)
153
+ self
154
+ end
155
+
156
+ def sort_by(type, sort_by = '')
157
+ case type
158
+ when :date
159
+ if arg == :asc
160
+ sort_mode = :date_asc
161
+ else
162
+ sort_mode = :date_desc
163
+ end
164
+ when :time
165
+ sort_mode = :time_segments
166
+ when :expression
167
+ sort_mode = :expression
168
+ when String
169
+ sort_mode = :extended
170
+ sort_by = type
171
+ else
172
+ sort_mode = :relevance
173
+ end
174
+ @sphinx.SetSortMode(SORT_MODES[sort_mode], sort_by)
175
+ self
176
+ end
177
+
178
+ def select(*attributes)
179
+ @sphinx.SetSelect(attributes.join(','))
180
+ self
181
+ end
182
+
183
+ def limit(value)
184
+ @limit = value
185
+ set_limits
186
+ self
187
+ end
188
+
189
+ def offset(value)
190
+ @offset = value
191
+ set_limits
192
+ self
193
+ end
194
+
195
+ def max(value)
196
+ @max = value
197
+ set_limits
198
+ self
199
+ end
200
+
201
+ def cutoff(value)
202
+ @cutoff = value
203
+ set_limits
204
+ self
205
+ end
206
+
207
+ def within(distance)
208
+ Enygma::GeoDistanceProxy.new(self, distance)
209
+ end
210
+
211
+ def anchor(point_or_lat, lng = nil)
212
+ if lng.nil?
213
+ if point_or_lat.respond_to?(:lat) && point_or_lat.respond_to?(:lng)
214
+ lat, lng = point_or_lat.lat, point_or_lat.lng
215
+ elsif point_or_lat.respond_to?(:coordinates) && point_or_lat.coordinates.respond_to?(:lat) && point_or_lat.coordinates.respond_to?(:lng)
216
+ lat, lng = point_or_lat.coordinates.lat, point_or_lat.coordinates.lng
217
+ elsif point_or_lat.respond_to?(:point) && point_or_lat.point.respond_to?(:lat) && point_or_lat.point.respond_to?(:lng)
218
+ lat, lng = point_or_lat.point.lat, point_or_lat.point.lng
219
+ else
220
+ raise ArgumentError, "#{point_or_lat.inspect} doesn't seem to be a geometry-enabled object!"
221
+ end
222
+ else
223
+ lat, lng = point_or_lat, lng
224
+ end
225
+ geo_anchor(lat, lng)
226
+ self
227
+ end
228
+
229
+ private
230
+
231
+ def geo_anchor(lat, lng)
232
+ @sphinx.SetGeoAnchor(@latitude, @longitude, lat, lng)
233
+ end
234
+
235
+ def set_limits
236
+ @sphinx.SetLimits(@offset, @limit, @max, @cutoff)
237
+ end
238
+
239
+ def query_sphinx
240
+ sphinx_response = @sphinx.Query(@term, @indexes.join(', '))
241
+ raise InvalidSphinxQuery unless sphinx_response
242
+ sphinx_response
243
+ end
244
+
245
+ def query_database(results)
246
+ ids = results['matches'].collect { |match| match['attrs'][@target_attr] }.uniq
247
+ @database[:adapter].query(:table => @database[:table], :ids => ids, :key_prefix => @key_prefix)
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,13 @@
1
+ module Enygma
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+
10
+ def self.version
11
+ Enygma::Version::STRING
12
+ end
13
+ end
data/lib/enygma.rb ADDED
@@ -0,0 +1,75 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'api/sphinx'
4
+
5
+ require 'enygma/version'
6
+
7
+ require 'enygma/extensions/float'
8
+
9
+ require 'enygma/adapters/abstract_adapter'
10
+ require 'enygma/configuration'
11
+ require 'enygma/geodistance_proxy'
12
+ require 'enygma/search'
13
+
14
+ require 'enygma/resource'
15
+
16
+ module Enygma
17
+
18
+ class << self
19
+
20
+ def included(base)
21
+ config_name = :"#{base.name.gsub(/(?!^)([A-Z])/, '_\1').upcase}_ENYGMA_CONFIGURATION"
22
+ base.const_set(config_name, Enygma::Configuration.new) unless base.const_defined?(config_name)
23
+ base.__send__(:extend, Enygma::ClassMethods)
24
+ if defined?(ActiveRecord) && base.ancestors.include?(ActiveRecord::Base)
25
+ configure_for_active_record(base)
26
+ elsif defined?(Sequel) && base.ancestors.include?(Sequel::Model)
27
+ configure_for_sequel_model(base)
28
+ elsif defined?(Datamapper) && base.included_modules.include?(Datamapper::Resource)
29
+ configure_for_datamapper_resource(base)
30
+ end
31
+ end
32
+
33
+ def indexify(name)
34
+ name.to_s =~ %r{#{Enygma::Configuration.index_suffix}$} ? name.to_s : name.to_s + Enygma::Configuration.index_suffix
35
+ end
36
+
37
+ private
38
+
39
+ def configure_for_active_record(base)
40
+ base.configure_enygma do
41
+ adapter :active_record
42
+ datastore base
43
+ end
44
+ end
45
+
46
+ def configure_for_sequel_model(base)
47
+ base.configure_enygma do
48
+ adapter :sequel
49
+ datastore base
50
+ end
51
+ end
52
+
53
+ def configure_for_datamapper_resource(base)
54
+ raise "Datamapper support isn't implemented yet! Sorry!"
55
+ end
56
+ end
57
+
58
+ module ClassMethods
59
+
60
+ def enygma_configuration
61
+ self.const_get(:"#{self.name.gsub(/(?!^)([A-Z])/, '_\1').upcase}_ENYGMA_CONFIGURATION")
62
+ end
63
+
64
+ def configure_enygma(&config)
65
+ enygma_configuration.instance_eval(&config)
66
+ end
67
+
68
+ def search(table = nil)
69
+ src = Enygma::Search.new(enygma_configuration)
70
+ src.in(table) if table
71
+ return src
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Enygma::Adapters::AbstractAdapter do
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'active_record'
3
+ require 'enygma/adapters/active_record'
4
+
5
+ describe Enygma::Adapters::ActiveRecordAdapter do
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'datamapper'
3
+ require 'enygma/adapters/datamapper'
4
+
5
+ describe Enygma::Adapters::DatamapperAdapter do
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'sequel'
3
+ require 'enygma/adapters/sequel'
4
+
5
+ describe Enygma::Adapters::SequelAdapter do
6
+
7
+ end
@@ -0,0 +1,217 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Enygma::Configuration do
4
+
5
+ describe Enygma::Configuration::InvalidAdapterName do
6
+ it "should be raised when the adapter is set to something not supported" do
7
+ lambda { Enygma::Configuration.adapter(:skull_of_orm) }.should raise_error(Enygma::Configuration::InvalidAdapterName)
8
+ end
9
+
10
+ it "should come with a message along the lines of 'Invalid adapter type!'" do
11
+ Enygma::Configuration::InvalidAdapterName.new.message.should =~ Regexp.new("Invalid adapter type!")
12
+ end
13
+ end
14
+
15
+ describe Enygma::Configuration::AdapterNotSet do
16
+ before do
17
+ @config = Enygma::Configuration.new
18
+ end
19
+
20
+ it "should be raised when trying to connect to a database when an adapter isn't set" do
21
+ lambda {
22
+ @config.adapter(:none)
23
+ @config.datastore("postgres://user@localhost/database")
24
+ }.should raise_error(Enygma::Configuration::AdapterNotSet)
25
+ end
26
+
27
+ it "should come with a message along the lines of 'You haven't chosen an adatper to use.'" do
28
+ Enygma::Configuration::AdapterNotSet.new.message.should =~ Regexp.new("You haven't chosen an adapter to use.")
29
+ end
30
+ end
31
+
32
+ describe "class variables" do
33
+
34
+ describe "@@index_suffix" do
35
+ it "should default to '_idx'" do
36
+ Enygma::Configuration.index_suffix.should == '_idx'
37
+ end
38
+
39
+ it "should be set with .index_suffix" do
40
+ Enygma::Configuration.index_suffix('_index')
41
+ Enygma::Configuration.index_suffix.should == '_index'
42
+ end
43
+ end
44
+
45
+ describe "@@target_attr" do
46
+ it "should default to 'item_id'" do
47
+ Enygma::Configuration.target_attr.should == 'item_id'
48
+ end
49
+
50
+ it "should be set with .target_attr" do
51
+ Enygma::Configuration.target_attr('record_id')
52
+ Enygma::Configuration.target_attr.should == 'record_id'
53
+ end
54
+ end
55
+
56
+ describe "@@adapter" do
57
+ it "should default to nil" do
58
+ Enygma::Configuration.adapter.should be_nil
59
+ end
60
+
61
+ it "should be set with .adapter" do
62
+ Enygma::Configuration.adapter(:sequel)
63
+ end
64
+
65
+ it "should return the adapter after being set" do
66
+ Enygma::Configuration.adapter(:sequel)
67
+ Enygma::Configuration.adapter.should be_an_instance_of(Enygma::Adapters::SequelAdapter)
68
+ end
69
+
70
+ it "should raise an error if the adapter isn't available" do
71
+ lambda { Enygma::Configuration.adapter(:skull_of_orm) }.should raise_error(Enygma::Configuration::InvalidAdapterName)
72
+ end
73
+
74
+ it "should reset the adapter to nil if passed :none" do
75
+ Enygma::Configuration.adapter(:sequel)
76
+ Enygma::Configuration.adapter.should_not be_nil
77
+ Enygma::Configuration.adapter(:none)
78
+ Enygma::Configuration.adapter.should be_nil
79
+ end
80
+ end
81
+
82
+ describe "@@sphinx" do
83
+ it "should default to { :port => 3312, :host => 'localhost' }" do
84
+ Enygma::Configuration.sphinx.should == { :port => 3312, :host => 'localhost' }
85
+ end
86
+
87
+ it "should return the sphinx configuration hash with .sphinx" do
88
+ Enygma::Configuration.sphinx.should be_an_instance_of(Hash)
89
+ end
90
+
91
+ it "should be able to be set on a per-key basis just like any other hash" do
92
+ Enygma::Configuration.sphinx[:port] = 303
93
+ Enygma::Configuration.sphinx[:host] = 'g.host'
94
+ Enygma::Configuration.sphinx.should == { :port => 303, :host => 'g.host' }
95
+ end
96
+
97
+ it "should respond to #port and #host to allow pretty configuration" do
98
+ Enygma::Configuration.sphinx.should respond_to(:port)
99
+ Enygma::Configuration.sphinx.should respond_to(:host)
100
+ end
101
+
102
+ it "should be able to be configured using the accessor methods instead of []" do
103
+ Enygma::Configuration.sphinx.port(303)
104
+ Enygma::Configuration.sphinx.host('g.host')
105
+ Enygma::Configuration.sphinx.should == { :port => 303, :host => 'g.host' }
106
+ end
107
+ end
108
+ end
109
+
110
+ describe ".global" do
111
+ it "should instance_eval self against the block" do
112
+ Enygma::Configuration.expects(:instance_eval)
113
+ Enygma::Configuration.global { }
114
+ end
115
+
116
+ it "should set the proper class variable using the accessors" do
117
+ Enygma::Configuration.global do
118
+ index_suffix '_index'
119
+ target_attr 'record_id'
120
+ adapter :sequel
121
+ sphinx.port 303
122
+ sphinx.host 'g.host'
123
+ end
124
+ Enygma::Configuration.index_suffix.should == '_index'
125
+ Enygma::Configuration.target_attr.should == 'record_id'
126
+ Enygma::Configuration.adapter.should be_an_instance_of(Enygma::Adapters::SequelAdapter)
127
+ Enygma::Configuration.sphinx.should == { :port => 303, :host => 'g.host' }
128
+ end
129
+ end
130
+
131
+ describe "default instance variables" do
132
+ before(:each) do
133
+ Enygma::Configuration.global do
134
+ index_suffix '_index'
135
+ target_attr 'record_id'
136
+ adapter :sequel
137
+ sphinx.port 303
138
+ sphinx.host 'g.host'
139
+ end
140
+ @config = Enygma::Configuration.new
141
+ end
142
+
143
+ it "should set @adapter to @@adapter" do
144
+ @config.adapter.should == Enygma::Configuration.adapter
145
+ end
146
+
147
+ it "should set @table to nil" do
148
+ @config.table.should be_nil
149
+ end
150
+
151
+ it "should set @indexes to []" do
152
+ @config.indexes.should be_an_instance_of(Array)
153
+ @config.indexes.should be_empty
154
+ end
155
+
156
+ it "should set @target_attr to @@target_attr" do
157
+ @config.target_attr.should == Enygma::Configuration.target_attr
158
+ end
159
+
160
+ it "should set @weights to {}" do
161
+ @config.weights.should == {}
162
+ end
163
+
164
+ it "should set @latitude to 'lat'" do
165
+ @config.latitude.should == 'lat'
166
+ end
167
+
168
+ it "should set @longitude to 'lng'" do
169
+ @config.longitude.should == 'lng'
170
+ end
171
+ end
172
+
173
+ describe "sphinx" do
174
+ before(:each) do
175
+ @config = Enygma::Configuration.new
176
+ end
177
+
178
+ it "should return the class default sphinx configuration" do
179
+ @config.sphinx.should == Enygma::Configuration.sphinx
180
+ end
181
+ end
182
+
183
+ describe "table" do
184
+ before(:each) do
185
+ @config = Enygma::Configuration.new
186
+ end
187
+
188
+ it "should set the name as the default table" do
189
+ @config.table :things
190
+ @config.table.should == :things
191
+ end
192
+ end
193
+
194
+ describe "index" do
195
+ before(:each) do
196
+ Enygma::Configuration.index_suffix '_idx'
197
+ @config = Enygma::Configuration.new
198
+ end
199
+
200
+ it "should add the index name, massaged through Enygma.indexify, to the named table's indexes" do
201
+ @config.index :things
202
+ @config.indexes.should include(Enygma.indexify(:things))
203
+ end
204
+ end
205
+
206
+ describe "weight" do
207
+ before(:each) do
208
+ @config = Enygma::Configuration.new
209
+ end
210
+
211
+ it "should add the given weight to the given attribute to the hash of weights" do
212
+ @config.weight :name, 20
213
+ @config.weights.should have_key('name')
214
+ @config.weights['name'].should == 20
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Enygma do
4
+
5
+ describe "including the module" do
6
+ before(:each) do
7
+ class Thing; end
8
+ end
9
+
10
+ it "should set <class_name>_ENYGMA_CONFIGURATION on the base class" do
11
+ Thing.__send__(:include, Enygma)
12
+ Thing.const_defined?(:THING_ENYGMA_CONFIGURATION).should be_true
13
+ end
14
+
15
+ it "should extend the base class with Enygma::ClassMethods" do
16
+ Thing.expects(:extend).with(Enygma::ClassMethods)
17
+ Thing.__send__(:include, Enygma)
18
+ end
19
+ end
20
+
21
+ describe "enygma_configuration" do
22
+ before(:each) do
23
+ class Thing; include Enygma; end
24
+ end
25
+
26
+ it "should return the class' <class_name>_ENYGMA_CONFIGURATION" do
27
+ Thing.enygma_configuration.should == Thing.const_get(:THING_ENYGMA_CONFIGURATION)
28
+ end
29
+
30
+ it "should be an instance of Enygma::Configuration" do
31
+ Thing.enygma_configuration.should be_an_instance_of(Enygma::Configuration)
32
+ end
33
+ end
34
+
35
+ describe "configure_enygma" do
36
+ before(:each) do
37
+ class Thing; include Enygma; end
38
+ end
39
+
40
+ it "should instance_eval the block against the class' @enygma_configuration" do
41
+ Thing.enygma_configuration.expects(:instance_eval)
42
+ Thing.configure_enygma { }
43
+ end
44
+ end
45
+
46
+ describe "search" do
47
+ before(:each) do
48
+ class Thing; include Enygma; end
49
+ end
50
+
51
+ it "should return an instance of Enygma::Search" do
52
+ Thing.search.should be_an_instance_of(Enygma::Search)
53
+ end
54
+ end
55
+
56
+ describe ".indexify" do
57
+ before(:each) do
58
+ Enygma::Configuration.index_suffix '_index'
59
+ end
60
+
61
+ it "should append the default index suffix to the argument" do
62
+ Enygma.indexify('butter').should == 'butter_index'
63
+ end
64
+
65
+ it "should convert symbols into strings" do
66
+ Enygma.indexify(:butter).should == 'butter_index'
67
+ end
68
+
69
+ it "should leave arguments that already end in the default suffix alone" do
70
+ Enygma.indexify(:butter_index).should == 'butter_index'
71
+ end
72
+ end
73
+
74
+ end