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.
- data/Rakefile +19 -0
- data/lib/api/sphinx/client.rb +1093 -0
- data/lib/api/sphinx/request.rb +50 -0
- data/lib/api/sphinx/response.rb +69 -0
- data/lib/api/sphinx.rb +6 -0
- data/lib/enygma/adapters/abstract_adapter.rb +31 -0
- data/lib/enygma/adapters/active_record.rb +26 -0
- data/lib/enygma/adapters/berkeley.rb +23 -0
- data/lib/enygma/adapters/datamapper.rb +21 -0
- data/lib/enygma/adapters/memcache.rb +25 -0
- data/lib/enygma/adapters/sequel.rb +51 -0
- data/lib/enygma/adapters/tokyo_cabinet.rb +71 -0
- data/lib/enygma/configuration.rb +204 -0
- data/lib/enygma/extensions/float.rb +13 -0
- data/lib/enygma/geodistance_proxy.rb +66 -0
- data/lib/enygma/resource.rb +43 -0
- data/lib/enygma/search.rb +250 -0
- data/lib/enygma/version.rb +13 -0
- data/lib/enygma.rb +75 -0
- data/spec/adapters/abstract_adapter_spec.rb +5 -0
- data/spec/adapters/active_record_spec.rb +7 -0
- data/spec/adapters/datamapper_spec.rb +7 -0
- data/spec/adapters/sequel_spec.rb +7 -0
- data/spec/configuration_spec.rb +217 -0
- data/spec/enygma_spec.rb +74 -0
- data/spec/extensions/float_spec.rb +1 -0
- data/spec/geodistance_proxy_spec.rb +78 -0
- data/spec/search_spec.rb +5 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/version_spec.rb +20 -0
- metadata +33 -3
@@ -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
|
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,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
|
data/spec/enygma_spec.rb
ADDED
@@ -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
|