picky 4.6.3 → 4.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/lib/performant.c +4 -4
  2. data/lib/picky/analyzer.rb +6 -3
  3. data/lib/picky/backends/backend.rb +40 -0
  4. data/lib/picky/backends/file/json.rb +4 -0
  5. data/lib/picky/backends/file.rb +1 -25
  6. data/lib/picky/backends/memory/json.rb +4 -0
  7. data/lib/picky/backends/memory.rb +1 -29
  8. data/lib/picky/backends/redis/directly_manipulable.rb +15 -7
  9. data/lib/picky/backends/redis.rb +91 -92
  10. data/lib/picky/backends/sqlite/basic.rb +6 -0
  11. data/lib/picky/bundle.rb +12 -10
  12. data/lib/picky/categories_indexing.rb +0 -13
  13. data/lib/picky/category.rb +24 -21
  14. data/lib/picky/category_indexing.rb +8 -22
  15. data/lib/picky/constants.rb +0 -1
  16. data/lib/picky/generators/aliases.rb +2 -0
  17. data/lib/picky/generators/partial.rb +27 -0
  18. data/lib/picky/generators/similarity.rb +27 -0
  19. data/lib/picky/generators/weights.rb +32 -0
  20. data/lib/picky/helpers/identification.rb +18 -0
  21. data/lib/picky/helpers/indexing.rb +16 -0
  22. data/lib/picky/index.rb +6 -0
  23. data/lib/picky/index_indexing.rb +9 -21
  24. data/lib/picky/indexes_indexing.rb +5 -14
  25. data/lib/picky/loader.rb +204 -199
  26. data/lib/picky/query/indexes.rb +12 -1
  27. data/lib/picky/search.rb +1 -0
  28. data/lib/picky/source.rb +23 -0
  29. data/lib/picky/tokenizer.rb +35 -13
  30. data/spec/functional/facets_spec.rb +1 -1
  31. data/spec/functional/remap_qualifiers_spec.rb +43 -0
  32. data/spec/functional/tokenizer_spec.rb +1 -1
  33. data/spec/lib/api/search/boost_spec.rb +1 -1
  34. data/spec/lib/category_spec.rb +1 -4
  35. data/spec/lib/generators/partial_spec.rb +58 -0
  36. data/spec/lib/generators/similarity_spec.rb +59 -0
  37. data/spec/lib/generators/weights_spec.rb +68 -0
  38. data/spec/lib/index_indexing_spec.rb +2 -4
  39. data/spec/lib/index_spec.rb +6 -0
  40. data/spec/lib/pool_spec.rb +39 -35
  41. data/spec/lib/sinatra_spec.rb +2 -2
  42. data/spec/lib/source_spec.rb +63 -0
  43. data/spec/lib/tokenizer_spec.rb +64 -2
  44. metadata +20 -20
  45. data/lib/picky/api/category/partial.rb +0 -26
  46. data/lib/picky/api/category/similarity.rb +0 -26
  47. data/lib/picky/api/category/weight.rb +0 -28
  48. data/lib/picky/api/source.rb +0 -35
  49. data/lib/picky/api/tokenizer.rb +0 -37
  50. data/lib/picky/deployment.rb +0 -211
  51. data/spec/lib/api/category/partial_spec.rb +0 -49
  52. data/spec/lib/api/category/similarity_spec.rb +0 -50
  53. data/spec/lib/api/category/weight_spec.rb +0 -55
  54. data/spec/lib/api/source_spec.rb +0 -68
  55. data/spec/lib/api/tokenizer_spec.rb +0 -42
data/lib/performant.c CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // Copying internal ruby methods.
4
4
  //
5
- inline VALUE rb_ary_elt(ary, offset)
5
+ static inline VALUE rb_ary_elt(ary, offset)
6
6
  VALUE ary;
7
7
  long offset;
8
8
  {
@@ -12,7 +12,7 @@ inline VALUE rb_ary_elt(ary, offset)
12
12
  }
13
13
  return RARRAY_PTR(ary)[offset];
14
14
  }
15
- inline VALUE ary_make_hash(ary1, ary2)
15
+ static inline VALUE ary_make_hash(ary1, ary2)
16
16
  VALUE ary1, ary2;
17
17
  {
18
18
  VALUE hash = rb_hash_new();
@@ -28,7 +28,7 @@ inline VALUE ary_make_hash(ary1, ary2)
28
28
  }
29
29
  return hash;
30
30
  }
31
- inline VALUE rb_ary_length(VALUE ary) {
31
+ static inline VALUE rb_ary_length(VALUE ary) {
32
32
  long length = RARRAY_LEN(ary);
33
33
  return LONG2NUM(length);
34
34
  }
@@ -37,7 +37,7 @@ inline VALUE rb_ary_length(VALUE ary) {
37
37
  // * orders the arrays by ascending size, small to large.
38
38
  // * calls the & consecutively for all arrays.
39
39
  //
40
- inline VALUE memory_efficient_intersect(VALUE self, VALUE unsorted_array_of_arrays) {
40
+ static inline VALUE memory_efficient_intersect(VALUE self, VALUE unsorted_array_of_arrays) {
41
41
  // Counters.
42
42
  //
43
43
  long i, j;
@@ -135,17 +135,20 @@ class Analyzer
135
135
  return if analysis[:__keys].zero?
136
136
  ary = ["index key cardinality: #{"%10d" % analysis[:__keys]}"]
137
137
  return ary.join "\n" unless analysis[:index]
138
- ary << "index key length range (avg): #{"%10s" % analysis[:index][:key_length]} (#{analysis[:index][:key_length_average].round(2)})"
139
- ary << "index ids per key length range (avg): #{"%10s" % analysis[:index][:ids_length]} (#{analysis[:index][:ids_length_average].round(2)})"
138
+ ary << formatted('', :key_length)
139
+ ary << formatted('ids per', :ids_length)
140
140
  ary.join "\n"
141
141
  end
142
+ def formatted description, key, index = :index
143
+ "index #{description} key length range (avg): #{"%10s" % analysis[index][key]} (#{analysis[index][:"#{key}_average"].round(2)})"
144
+ end
142
145
  def weights_to_s
143
146
  return unless analysis[:weights]
144
147
  %Q{weights range (avg): #{"%10s" % analysis[:weights][:weight_range]} (#{analysis[:weights][:weight_average].round(2)})}
145
148
  end
146
149
  def similarity_to_s
147
150
  return unless analysis[:similarity]
148
- %Q{similarity key length range (avg): #{"%10s" % analysis[:similarity][:key_length]} (#{analysis[:similarity][:key_length_average].round(2)})}
151
+ formatted('similarity', :key_length, :similarity)
149
152
  end
150
153
  def configuration_to_s
151
154
  # analysis[:configuration]
@@ -5,6 +5,46 @@ module Picky
5
5
  #
6
6
  #
7
7
  class Backend
8
+
9
+ # This is the default behaviour and should be overridden
10
+ # for different backends.
11
+ #
12
+
13
+ # Returns an object that on #initial, #load returns
14
+ # an object that responds to:
15
+ # [:token] # => [id, id, id, id, id] (an array of ids)
16
+ #
17
+ def create_inverted bundle
18
+ json bundle.index_path(:inverted)
19
+ end
20
+ # Returns an object that on #initial, #load returns
21
+ # an object that responds to:
22
+ # [:token] # => 1.23 (a weight)
23
+ #
24
+ def create_weights bundle
25
+ json bundle.index_path(:weights)
26
+ end
27
+ # # Returns an object that on #initial, #load returns
28
+ # # an object that responds to:
29
+ # # [:encoded] # => [:original, :original] (an array of original symbols this similarity encoded thing maps to)
30
+ # #
31
+ # def create_similarity bundle
32
+ # Marshal.new bundle.index_path(:similarity)
33
+ # end
34
+ # Returns an object that on #initial, #load returns
35
+ # an object that responds to:
36
+ # [:key] # => value (a value for this config key)
37
+ #
38
+ def create_configuration bundle
39
+ json bundle.index_path(:configuration)
40
+ end
41
+ # Returns an object that on #initial, #load returns
42
+ # an object that responds to:
43
+ # [id] # => [:sym1, :sym2]
44
+ #
45
+ def create_realtime bundle
46
+ json bundle.index_path(:realtime)
47
+ end
8
48
 
9
49
  # Returns the total score of the combinations.
10
50
  #
@@ -3,6 +3,10 @@ module Picky
3
3
  module Backends
4
4
 
5
5
  class File
6
+
7
+ def json *args
8
+ JSON.new *args
9
+ end
6
10
 
7
11
  # File-based index files dumped in the JSON format.
8
12
  #
@@ -10,35 +10,11 @@ module Picky
10
10
  #
11
11
  class File < Backend
12
12
 
13
- # Returns an object that on #initial, #load returns an object that responds to:
14
- # [:token] # => [id, id, id, id, id] (an array of ids)
15
- #
16
- def create_inverted bundle
17
- JSON.new bundle.index_path(:inverted)
18
- end
19
- # Returns an object that on #initial, #load returns an object that responds to:
20
- # [:token] # => 1.23 (a weight)
21
- #
22
- def create_weights bundle
23
- JSON.new bundle.index_path(:weights)
24
- end
25
13
  # Returns an object that on #initial, #load returns an object that responds to:
26
14
  # [:encoded] # => [:original, :original] (an array of original symbols this similarity encoded thing maps to)
27
15
  #
28
16
  def create_similarity bundle
29
- JSON.new bundle.index_path(:similarity)
30
- end
31
- # Returns an object that on #initial, #load returns an object that responds to:
32
- # [:key] # => value (a value for this config key)
33
- #
34
- def create_configuration bundle
35
- JSON.new bundle.index_path(:configuration)
36
- end
37
- # Returns an object that on #initial, #load returns an object that responds to:
38
- # [id] # => [:sym1, :sym2]
39
- #
40
- def create_realtime bundle
41
- JSON.new bundle.index_path(:realtime)
17
+ json bundle.index_path(:similarity)
42
18
  end
43
19
 
44
20
  end
@@ -3,6 +3,10 @@ module Picky
3
3
  module Backends
4
4
 
5
5
  class Memory
6
+
7
+ def json *args
8
+ JSON.new *args
9
+ end
6
10
 
7
11
  # Memory-based index files dumped in the JSON format.
8
12
  #
@@ -3,21 +3,7 @@ module Picky
3
3
  module Backends
4
4
 
5
5
  class Memory < Backend
6
-
7
- # Returns an object that on #initial, #load returns
8
- # an object that responds to:
9
- # [:token] # => [id, id, id, id, id] (an array of ids)
10
- #
11
- def create_inverted bundle
12
- JSON.new bundle.index_path(:inverted)
13
- end
14
- # Returns an object that on #initial, #load returns
15
- # an object that responds to:
16
- # [:token] # => 1.23 (a weight)
17
- #
18
- def create_weights bundle
19
- JSON.new bundle.index_path(:weights)
20
- end
6
+
21
7
  # Returns an object that on #initial, #load returns
22
8
  # an object that responds to:
23
9
  # [:encoded] # => [:original, :original] (an array of original symbols this similarity encoded thing maps to)
@@ -25,20 +11,6 @@ module Picky
25
11
  def create_similarity bundle
26
12
  Marshal.new bundle.index_path(:similarity)
27
13
  end
28
- # Returns an object that on #initial, #load returns
29
- # an object that responds to:
30
- # [:key] # => value (a value for this config key)
31
- #
32
- def create_configuration bundle
33
- JSON.new bundle.index_path(:configuration)
34
- end
35
- # Returns an object that on #initial, #load returns
36
- # an object that responds to:
37
- # [id] # => [:sym1, :sym2]
38
- #
39
- def create_realtime bundle
40
- JSON.new bundle.index_path(:realtime)
41
- end
42
14
 
43
15
  end
44
16
 
@@ -18,25 +18,33 @@ module Picky
18
18
  #
19
19
  @@append_index = 0
20
20
  def << value
21
- super value
22
- backend.client.zadd "#{backend.namespace}:#{key}", (@@append_index+=1), value
23
- backend[key]
21
+ super
22
+ zadd value, @@append_index+=1
24
23
  end
25
24
 
26
25
  # THINK Current implementation is very brittle.
27
26
  #
28
27
  @@unshift_index = 0
29
28
  def unshift value
30
- super value
31
- backend.client.zadd "#{backend.namespace}:#{key}", (@@unshift_index-=1), value
32
- backend[key]
29
+ super
30
+ zadd value, @@unshift_index -= 1
33
31
  end
34
-
32
+
33
+ # Deletes the value.
34
+ #
35
35
  def delete value
36
36
  result = super value
37
37
  backend.client.zrem "#{backend.namespace}:#{key}", value if result
38
38
  result
39
39
  end
40
+
41
+ # Z-Adds a value with the given index.
42
+ #
43
+ def zadd value, index
44
+ backend.client.zadd "#{backend.namespace}:#{key}", index, value
45
+ backend[key]
46
+ end
47
+
40
48
  end
41
49
 
42
50
  end
@@ -169,104 +169,24 @@ module Picky
169
169
  require 'digest/sha1'
170
170
  @@ids_sent_once = nil
171
171
 
172
- # Scripting version of #ids.
172
+ # Overrides _this_ method.
173
173
  #
174
- class << self
175
- def ids combinations, amount, offset
176
- identifiers = combinations.inject([]) do |identifiers, combination|
177
- identifiers << "#{combination.identifier}"
178
- end
179
-
180
- # Assume it's using EVALSHA.
181
- #
182
- begin
183
- if identifiers.size > 1
184
- client.evalsha @@ids_sent_once,
185
- identifiers.size,
186
- *identifiers,
187
- generate_intermediate_result_id,
188
- offset,
189
- (offset + amount)
190
- else
191
- client.zrange identifiers.first,
192
- offset,
193
- (offset + amount)
194
- end
195
- rescue RuntimeError => e
196
- # Make the server have a SHA-1 for the script.
197
- #
198
- @@ids_sent_once = Digest::SHA1.hexdigest @@ids_script
199
- client.eval @@ids_script,
200
- identifiers.size,
201
- *identifiers,
202
- generate_intermediate_result_id,
203
- offset,
204
- (offset + amount)
205
- end
206
- end
207
- end
174
+ extend Scripting
208
175
  else
209
- # Non-Scripting version of #ids.
176
+ # Overrides _this_ method.
210
177
  #
211
- class << self
212
- def ids combinations, amount, offset
213
- identifiers = combinations.inject([]) do |identifiers, combination|
214
- identifiers << "#{combination.identifier}"
215
- end
216
-
217
- result_id = generate_intermediate_result_id
218
-
219
- # Little optimization.
220
- #
221
- if identifiers.size > 1
222
- # Intersect and store.
223
- #
224
- intersected = client.zinterstore result_id, identifiers
225
-
226
- # Return clean and early if there has been no intersection.
227
- #
228
- if intersected.zero?
229
- client.del result_id
230
- return []
231
- end
232
-
233
- # Get the stored result.
234
- #
235
- results = client.zrange result_id, offset, (offset + amount)
236
-
237
- # Delete the stored result as it was only for temporary purposes.
238
- #
239
- # Note: I could also not delete it, but that
240
- # would not be clean at all.
241
- #
242
- client.del result_id
243
- else
244
- results = client.zrange identifiers.first, offset, (offset + amount)
245
- end
246
-
247
- results
248
- end
249
- end
178
+ extend NonScripting
250
179
  end
251
180
  else
252
- class << self
253
- def ids combinations, _, _
254
- # Get the ids for each combination.
255
- #
256
- id_arrays = combinations.inject([]) do |total, combination|
257
- total << combination.ids
258
- end
259
-
260
- # Call the optimized C algorithm.
261
- #
262
- # Note: It orders the passed arrays by size.
263
- #
264
- Performant::Array.memory_efficient_intersect id_arrays
265
- end
266
- end
181
+ # Remove _this_ method and use the super
182
+ # class method from now on.
183
+ #
184
+ # Note: This fails if there are multiple
185
+ # Redis backends with different versions.
186
+ #
187
+ self.class.send :remove_method, __method__
267
188
  end
268
-
269
- # Call the newly installed version.
189
+ # Call the newly installed / super class version.
270
190
  #
271
191
  ids combinations, amount, offset
272
192
  end
@@ -291,6 +211,85 @@ module Picky
291
211
  def generate_intermediate_result_id
292
212
  @intermediate_result_id ||= "#{host}:#{pid}:picky:result"
293
213
  end
214
+
215
+ # Uses Lua scripting on Redis 2.6.
216
+ #
217
+ module Scripting
218
+ def ids combinations, amount, offset
219
+ identifiers = combinations.inject([]) do |identifiers, combination|
220
+ identifiers << "#{combination.identifier}"
221
+ end
222
+
223
+ # Assume it's using EVALSHA.
224
+ #
225
+ begin
226
+ if identifiers.size > 1
227
+ client.evalsha @@ids_sent_once,
228
+ identifiers.size,
229
+ *identifiers,
230
+ generate_intermediate_result_id,
231
+ offset,
232
+ (offset + amount)
233
+ else
234
+ client.zrange identifiers.first,
235
+ offset,
236
+ (offset + amount)
237
+ end
238
+ rescue RuntimeError => e
239
+ # Make the server have a SHA-1 for the script.
240
+ #
241
+ @@ids_sent_once = Digest::SHA1.hexdigest @@ids_script
242
+ client.eval @@ids_script,
243
+ identifiers.size,
244
+ *identifiers,
245
+ generate_intermediate_result_id,
246
+ offset,
247
+ (offset + amount)
248
+ end
249
+ end
250
+ end
251
+
252
+ # Does not use Lua scripting, < Redis 2.6.
253
+ #
254
+ module NonScripting
255
+ def ids combinations, amount, offset
256
+ identifiers = combinations.inject([]) do |identifiers, combination|
257
+ identifiers << "#{combination.identifier}"
258
+ end
259
+
260
+ result_id = generate_intermediate_result_id
261
+
262
+ # Little optimization.
263
+ #
264
+ if identifiers.size > 1
265
+ # Intersect and store.
266
+ #
267
+ intersected = client.zinterstore result_id, identifiers
268
+
269
+ # Return clean and early if there has been no intersection.
270
+ #
271
+ if intersected.zero?
272
+ client.del result_id
273
+ return []
274
+ end
275
+
276
+ # Get the stored result.
277
+ #
278
+ results = client.zrange result_id, offset, (offset + amount)
279
+
280
+ # Delete the stored result as it was only for temporary purposes.
281
+ #
282
+ # Note: I could also not delete it, but that
283
+ # would not be clean at all.
284
+ #
285
+ client.del result_id
286
+ else
287
+ results = client.zrange identifiers.first, offset, (offset + amount)
288
+ end
289
+
290
+ results
291
+ end
292
+ end
294
293
 
295
294
  end
296
295
 
@@ -17,6 +17,12 @@ module Picky
17
17
  @realtime = options[:realtime]
18
18
 
19
19
  lazily_initialize_client
20
+
21
+ # Note: If on OSX, too many files get opened during
22
+ # the specs -> ulimit -n 3000
23
+ #
24
+ # rescue SQLite3::CantOpenException => e
25
+ #
20
26
  end
21
27
 
22
28
  def initial
data/lib/picky/bundle.rb CHANGED
@@ -91,22 +91,24 @@ module Picky
91
91
  # the strategy itself pretends to be an index.
92
92
  #
93
93
  def initialize_backends
94
- @inverted = @backend_inverted.initial
95
- @weights = @weight_strategy.respond_to?(:saved?) && !@weight_strategy.saved? ? @weight_strategy : @backend_weights.initial
96
- @similarity = @backend_similarity.initial
97
- @configuration = @backend_configuration.initial
98
- @realtime = @backend_realtime.initial
94
+ on_all_indexes_call :initial
99
95
  end
100
96
 
101
97
  # "Empties" the index(es) by getting a new empty
102
98
  # internal backend instance.
103
99
  #
104
100
  def empty
105
- @inverted = @backend_inverted.empty
106
- @weights = @weight_strategy.respond_to?(:saved?) && !@weight_strategy.saved? ? @weight_strategy : @backend_weights.empty
107
- @similarity = @backend_similarity.empty
108
- @configuration = @backend_configuration.empty
109
- @realtime = @backend_realtime.empty
101
+ on_all_indexes_call :empty
102
+ end
103
+
104
+ # Extracted to avoid duplicate code.
105
+ #
106
+ def on_all_indexes_call method_name
107
+ @inverted = @backend_inverted.send method_name
108
+ @weights = @weight_strategy.respond_to?(:saved?) && !@weight_strategy.saved? ? @weight_strategy : @backend_weights.send(method_name)
109
+ @similarity = @backend_similarity.send method_name
110
+ @configuration = @backend_configuration.send method_name
111
+ @realtime = @backend_realtime.send method_name
110
112
  end
111
113
 
112
114
  # Delete all index files.
@@ -8,19 +8,6 @@ module Picky
8
8
  :clear,
9
9
  :prepare,
10
10
  :to => :categories
11
-
12
- # First prepares all categories,
13
- # then caches all categories.
14
- #
15
- def index scheduler = Scheduler.new
16
- timed_indexing scheduler do
17
- categories.prepare scheduler
18
- scheduler.finish
19
-
20
- categories.cache scheduler
21
- scheduler.finish
22
- end
23
- end
24
11
  end
25
12
 
26
13
  end
@@ -3,10 +3,6 @@ module Picky
3
3
  class Category
4
4
 
5
5
  include API::Tokenizer
6
- include API::Source
7
- include API::Category::Weight
8
- include API::Category::Partial
9
- include API::Category::Similarity
10
6
 
11
7
  attr_accessor :exact,
12
8
  :partial
@@ -40,11 +36,17 @@ module Picky
40
36
  @name = name
41
37
  @index = index
42
38
 
43
- # Indexing.
44
- #
45
- @source = extract_source options[:source], nil_ok: true
39
+ configure_from options
40
+ configure_indexes_from options
41
+ end
42
+
43
+ def configure_from options
46
44
  @from = options[:from]
47
- @tokenizer = extract_tokenizer options[:indexing]
45
+
46
+ # Instantly extracted to raise an error instantly.
47
+ #
48
+ @source = Source.from options[:source], true, @index.name
49
+ @tokenizer = Tokenizer.from options[:indexing], @index.name, name
48
50
 
49
51
  @key_format = options.delete :key_format
50
52
  @backend = options.delete :backend
@@ -52,10 +54,16 @@ module Picky
52
54
  @qualifiers = extract_qualifiers_from options
53
55
 
54
56
  # @symbols = options[:use_symbols] || index.use_symbols? # SYMBOLS.
57
+ end
55
58
 
56
- weights = extract_weight options[:weight]
57
- partial = extract_partial options[:partial]
58
- similarity = extract_similarity options[:similarity]
59
+ # TODO I do a lot of helper method calls here. Refactor?
60
+ #
61
+ def configure_indexes_from options
62
+ index_name = @index.name
63
+
64
+ weights = Generators::Weights.from options[:weight], index_name, name
65
+ partial = Generators::Partial.from options[:partial], index_name, name
66
+ similarity = Generators::Similarity.from options[:similarity], index_name, name
59
67
 
60
68
  no_partial = Generators::Partial::None.new
61
69
  no_similarity = Generators::Similarity::None.new
@@ -99,12 +107,6 @@ module Picky
99
107
  partial.reset_backend
100
108
  end
101
109
 
102
- # Index name.
103
- #
104
- def index_name
105
- @index.name
106
- end
107
-
108
110
  # Returns the qualifiers if set or
109
111
  # just the name if not.
110
112
  #
@@ -136,11 +138,12 @@ module Picky
136
138
  @prepared_index_file ||= Backends::Prepared::Text.new prepared_index_path
137
139
  @prepared_index_file.open &block
138
140
  end
139
-
140
- # The index directory for this category.
141
- #
141
+
142
142
  def index_directory
143
- @index_directory ||= ::File.join(PICKY_ROOT, 'index', PICKY_ENVIRONMENT, @index.name.to_s)
143
+ @index.directory
144
+ end
145
+ def index_name
146
+ @index.name
144
147
  end
145
148
 
146
149
  # Identifier for technical output.
@@ -9,20 +9,6 @@ module Picky
9
9
  attr_reader :exact,
10
10
  :partial
11
11
 
12
- # Prepares and caches this category.
13
- #
14
- # This one should be used by users.
15
- #
16
- def index scheduler = Scheduler.new
17
- timed_indexing scheduler do
18
- prepare scheduler
19
- scheduler.finish
20
-
21
- cache scheduler
22
- scheduler.finish
23
- end
24
- end
25
-
26
12
  # Indexes, creates the "prepared_..." file.
27
13
  #
28
14
  def prepare scheduler = Scheduler.new
@@ -73,14 +59,6 @@ module Picky
73
59
  prepared.retrieve { |id, token| add_tokenized_token id, token, :<< }
74
60
  end
75
61
 
76
- # Return an appropriate source.
77
- #
78
- # If we have no explicit source, we'll check the index for one.
79
- #
80
- def source
81
- (@source = extract_source(@source, nil_ok: true)) || @index.source
82
- end
83
-
84
62
  # Return the key format.
85
63
  #
86
64
  # If no key_format is defined on the category
@@ -98,6 +76,14 @@ module Picky
98
76
  def from
99
77
  @from || name
100
78
  end
79
+
80
+ # Return an appropriate source.
81
+ #
82
+ # If we have no explicit source, we'll check the index for one.
83
+ #
84
+ def source
85
+ @source || @index.source
86
+ end
101
87
 
102
88
  # The indexer is lazily generated and cached.
103
89
  #
@@ -4,7 +4,6 @@
4
4
  # Use rack's environment for the search engine.
5
5
  #
6
6
  ENV['PICKY_ENV'] ||= ENV['RUBY_ENV'] || ENV['RACK_ENV']
7
-
8
7
  PICKY_ENVIRONMENT = ENV['PICKY_ENV'] || 'development' unless defined? PICKY_ENVIRONMENT
9
8
  PICKY_ROOT = Dir.pwd unless defined? PICKY_ROOT
10
9
 
@@ -1,3 +1,5 @@
1
+ # Removes and reinstalls convenience generator accessors.
2
+ #
1
3
  module Picky
2
4
  remove_const :Partial if defined? Partial
3
5
  Partial = Generators::Partial