picky 0.3.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/lib/picky/application.rb +2 -2
  2. data/lib/picky/cacher/partial/default.rb +1 -1
  3. data/lib/picky/configuration/field.rb +8 -10
  4. data/lib/picky/configuration/indexes.rb +6 -6
  5. data/lib/picky/configuration/queries.rb +4 -3
  6. data/lib/picky/cores.rb +2 -2
  7. data/lib/picky/extensions/array.rb +2 -12
  8. data/lib/picky/generator.rb +27 -4
  9. data/lib/picky/index/bundle.rb +5 -41
  10. data/lib/picky/index/bundle_checker.rb +58 -0
  11. data/lib/picky/index/type.rb +4 -1
  12. data/lib/picky/index/wrappers/exact_first.rb +57 -0
  13. data/lib/picky/indexes.rb +12 -19
  14. data/lib/picky/loader.rb +7 -8
  15. data/lib/picky/query/allocation.rb +1 -1
  16. data/lib/picky/query/combinations.rb +9 -6
  17. data/lib/picky/query/combinator.rb +11 -5
  18. data/lib/picky/rack/harakiri.rb +1 -1
  19. data/lib/picky/results/base.rb +4 -12
  20. data/lib/picky/results/live.rb +0 -6
  21. data/lib/picky/routing.rb +17 -17
  22. data/lib/picky/sources/csv.rb +1 -2
  23. data/lib/picky/sources/db.rb +0 -1
  24. data/lib/picky/sources/delicious.rb +41 -0
  25. data/lib/picky/tokenizers/base.rb +52 -43
  26. data/lib/picky/tokenizers/default/index.rb +7 -0
  27. data/lib/picky/tokenizers/default/query.rb +7 -0
  28. data/lib/picky/tokenizers/index.rb +0 -9
  29. data/lib/picky/tokenizers/query.rb +0 -9
  30. data/lib/tasks/application.rake +1 -1
  31. data/lib/tasks/cache.rake +41 -48
  32. data/lib/tasks/framework.rake +1 -1
  33. data/lib/tasks/index.rake +22 -12
  34. data/lib/tasks/server.rake +3 -3
  35. data/lib/tasks/shortcuts.rake +9 -2
  36. data/lib/tasks/statistics.rake +8 -8
  37. data/lib/tasks/try.rake +4 -2
  38. data/project_prototype/Gemfile +1 -1
  39. data/project_prototype/app/application.rb +7 -3
  40. data/spec/lib/cacher/partial/default_spec.rb +1 -1
  41. data/spec/lib/cacher/partial/none_spec.rb +12 -0
  42. data/spec/lib/cacher/partial/subtoken_spec.rb +29 -1
  43. data/spec/lib/configuration/field_spec.rb +162 -3
  44. data/spec/lib/configuration/indexes_spec.rb +150 -0
  45. data/spec/lib/cores_spec.rb +43 -0
  46. data/spec/lib/extensions/module_spec.rb +27 -16
  47. data/spec/lib/generator_spec.rb +3 -3
  48. data/spec/lib/index/bundle_checker_spec.rb +67 -0
  49. data/spec/lib/index/bundle_spec.rb +0 -50
  50. data/spec/lib/index/type_spec.rb +47 -0
  51. data/spec/lib/index/wrappers/exact_first_spec.rb +95 -0
  52. data/spec/lib/indexers/base_spec.rb +18 -2
  53. data/spec/lib/loader_spec.rb +21 -1
  54. data/spec/lib/query/allocation_spec.rb +25 -0
  55. data/spec/lib/query/base_spec.rb +37 -0
  56. data/spec/lib/query/combination_spec.rb +10 -1
  57. data/spec/lib/query/combinations_spec.rb +82 -3
  58. data/spec/lib/query/combinator_spec.rb +45 -0
  59. data/spec/lib/query/token_spec.rb +24 -0
  60. data/spec/lib/rack/harakiri_spec.rb +28 -0
  61. data/spec/lib/results/base_spec.rb +24 -0
  62. data/spec/lib/results/live_spec.rb +15 -0
  63. data/spec/lib/routing_spec.rb +5 -0
  64. data/spec/lib/sources/db_spec.rb +31 -1
  65. data/spec/lib/sources/delicious_spec.rb +75 -0
  66. data/spec/lib/tokenizers/base_spec.rb +160 -49
  67. data/spec/lib/tokenizers/default/index_spec.rb +11 -0
  68. data/spec/lib/tokenizers/default/query_spec.rb +11 -0
  69. metadata +26 -5
  70. data/lib/picky/index/combined.rb +0 -45
  71. data/lib/picky/tokenizers/default.rb +0 -3
@@ -1,5 +1,3 @@
1
- require 'csv'
2
-
3
1
  module Sources
4
2
 
5
3
  # Describes a CSV source, a file with csv in it.
@@ -12,6 +10,7 @@ module Sources
12
10
  attr_reader :file_name, :field_names
13
11
 
14
12
  def initialize *field_names, options
13
+ require 'csv'
15
14
  @field_names = field_names
16
15
  @file_name = Hash === options && options[:file] || raise_no_file_given(field_names)
17
16
  end
@@ -50,7 +50,6 @@ module Sources
50
50
  #
51
51
  def connect_backend
52
52
  configure @options
53
- return if PICKY_ENVIRONMENT.to_s == 'test' # TODO Unclean.
54
53
  raise "Database backend not configured" unless connection_options
55
54
  database.establish_connection connection_options
56
55
  end
@@ -0,0 +1,41 @@
1
+ module Sources
2
+
3
+ class Delicious < Base
4
+
5
+ def initialize username, password
6
+ require 'www/delicious'
7
+ @username = username
8
+ @password = password
9
+ end
10
+
11
+ # Harvests the data to index.
12
+ #
13
+ def harvest _, field
14
+ get_data do |uid, data|
15
+ indexed_id = uid
16
+ text = data[field.name]
17
+ next unless text
18
+ text.force_encoding 'utf-8' # TODO Still needed?
19
+ yield indexed_id, text
20
+ end
21
+ end
22
+
23
+ #
24
+ #
25
+ def get_data
26
+ @generated_id ||= 0
27
+ @posts ||= WWW::Delicious.new(@username, @password).posts_recent(:count => 100)
28
+ @posts.each do |post|
29
+ data = {
30
+ :title => post.title,
31
+ :tags => post.tags.join(' '),
32
+ :url => post.url.to_s
33
+ }
34
+ @generated_id += 1
35
+ yield @generated_id, data
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -4,75 +4,80 @@ module Tokenizers
4
4
  #
5
5
  class Base
6
6
 
7
+ # TODO use frozen EMPTY_STRING for ''
8
+ #
9
+
7
10
  # Stopwords.
8
11
  #
9
- def self.stopwords regexp
10
- define_method :remove_stopwords do |text|
11
- text.gsub! regexp, ''
12
- end
13
- # Use this method if you don't want to remove
14
- # stopwords if it is just one word.
15
- #
16
- @@non_single_stopword_regexp = /^\b[\w:]+?\b[\.\*\~]?\s?$/
17
- define_method :remove_non_single_stopwords do |text|
18
- return text if text.match @@non_single_stopword_regexp
19
- remove_stopwords text
20
- end
12
+ def stopwords regexp
13
+ @remove_stopwords_regexp = regexp
14
+ end
15
+ def remove_stopwords text
16
+ text.gsub! @remove_stopwords_regexp, '' if @remove_stopwords_regexp
17
+ text
18
+ end
19
+ @@non_single_stopword_regexp = /^\b[\w:]+?\b[\.\*\~]?\s?$/
20
+ def remove_non_single_stopwords text
21
+ return text if text.match @@non_single_stopword_regexp
22
+ remove_stopwords text
21
23
  end
22
- def remove_stopwords text; end
23
24
 
24
25
  # Contraction.
25
26
  #
26
- def self.contracts_expressions what, to_what
27
- define_method :contract do |text|
28
- text.gsub! what, to_what
29
- end
27
+ def contracts_expressions what, to_what
28
+ @contract_what = what
29
+ @contract_to_what = to_what
30
+ end
31
+ def contract text
32
+ text.gsub! @contract_what, @contract_to_what if @contract_what
30
33
  end
31
- def contract text; end
32
34
 
33
35
  # Illegals.
34
36
  #
35
37
  # TODO Should there be a legal?
36
38
  #
37
- def self.removes_characters regexp
38
- define_method :remove_illegals do |text|
39
- text.gsub! regexp, ''
40
- end
39
+ def removes_characters regexp
40
+ @removes_characters_regexp = regexp
41
+ end
42
+ def remove_illegals text
43
+ text.gsub! @removes_characters_regexp, '' if @removes_characters_regexp
44
+ text
41
45
  end
42
- def remove_illegals text; end
43
46
 
44
47
  # Splitting.
45
48
  #
46
- def self.splits_text_on regexp
47
- define_method :split do |text|
48
- text.split regexp
49
- end
49
+ def splits_text_on regexp
50
+ @splits_text_on_regexp = regexp
51
+ end
52
+ def split text
53
+ text.split @splits_text_on_regexp
50
54
  end
51
- def split text; end
52
55
 
53
56
  # Normalizing.
54
57
  #
55
- def self.normalizes_words regexp_replaces
56
- define_method :normalize_with_patterns do |text|
57
- regexp_replaces.each do |regex, replace|
58
- # This should be sufficient
59
- #
60
- text.gsub!(regex, replace) and break
61
- end
62
- remove_after_normalizing_illegals text
63
- text
58
+ def normalizes_words regexp_replaces
59
+ @normalizes_words_regexp_replaces = regexp_replaces
60
+ end
61
+ def normalize_with_patterns text
62
+ return text unless @normalizes_words_regexp_replaces
63
+
64
+ @normalizes_words_regexp_replaces.each do |regex, replace|
65
+ # This should be sufficient
66
+ #
67
+ text.gsub!(regex, replace) and break
64
68
  end
69
+ remove_after_normalizing_illegals text
70
+ text
65
71
  end
66
- def normalize_with_patterns text; end
67
72
 
68
73
  # Illegal after normalizing.
69
74
  #
70
- def self.removes_characters_after_splitting regexp
71
- define_method :remove_after_normalizing_illegals do |text|
72
- text.gsub! regexp, ''
73
- end
75
+ def removes_characters_after_splitting regexp
76
+ @removes_characters_after_splitting_regexp = regexp
77
+ end
78
+ def remove_after_normalizing_illegals text
79
+ text.gsub! @removes_characters_after_splitting_regexp, '' if @removes_characters_after_splitting_regexp
74
80
  end
75
- def remove_after_normalizing_illegals text; end
76
81
 
77
82
  # Returns a number of tokens, generated from the given text.
78
83
  #
@@ -93,6 +98,10 @@ module Tokenizers
93
98
 
94
99
  def initialize substituter = UmlautSubstituter.new
95
100
  @substituter = substituter
101
+
102
+ # TODO Default handling.
103
+ #
104
+ splits_text_on(/\s/)
96
105
  end
97
106
 
98
107
  # Hooks.
@@ -0,0 +1,7 @@
1
+ module Tokenizers
2
+ module Default
3
+ # Default is always an instance.
4
+ #
5
+ Index = ::Tokenizers::Index.new
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Tokenizers
2
+ module Default
3
+ # Default is always an instance.
4
+ #
5
+ Query = ::Tokenizers::Query.new
6
+ end
7
+ end
@@ -5,15 +5,6 @@ module Tokenizers
5
5
  #
6
6
  class Index < Base
7
7
 
8
- # Default handling definitions. Override in config.
9
- #
10
- removes_characters(//)
11
- stopwords(//)
12
- contracts_expressions(//, '')
13
- splits_text_on(/\s/)
14
- normalizes_words([])
15
- removes_characters_after_splitting(//)
16
-
17
8
  # Default indexing preprocessing hook.
18
9
  #
19
10
  # Does:
@@ -13,15 +13,6 @@ module Tokenizers
13
13
  #
14
14
  class Query < Base
15
15
 
16
- # Default query tokenizer behaviour. Override in config.
17
- #
18
- removes_characters(//)
19
- stopwords(//)
20
- contracts_expressions(//, '')
21
- splits_text_on(/\s/)
22
- normalizes_words([])
23
- removes_characters_after_splitting(//)
24
-
25
16
  def preprocess text
26
17
  remove_illegals text # Remove illegal characters
27
18
  remove_non_single_stopwords text # remove stop words
@@ -1,4 +1,4 @@
1
- desc "Loads the application, including its configuration."
1
+ # desc "Loads the application, including its configuration."
2
2
  task :application => :framework do
3
3
  puts "Running rake task 'application'."
4
4
  Loader.load_application
data/lib/tasks/cache.rake CHANGED
@@ -1,53 +1,46 @@
1
1
  namespace :cache do
2
2
 
3
- namespace :structure do
4
-
5
- desc 'create the directory structure for the cache indexes'
6
- task :create => :application do
7
- Indexes.create_directory_structure
8
- puts "Directory structure generated."
9
- end
10
-
11
- end
12
-
13
- desc "Generates the index cache files."
14
- task :generate => :application do
15
- Indexes.generate_caches
16
- puts "Caches generated."
17
- end
3
+ # Move to index namespace.
4
+ #
5
+
6
+ # desc "Generates the index cache files."
7
+ # task :generate => :application do
8
+ # Indexes.generate_caches
9
+ # puts "Caches generated."
10
+ # end
18
11
 
19
- desc "Generates a specific index cache file like field=books:title. Note: Index tables need to be there. Will generate just the cache."
20
- task :only => :application do
21
- type_and_field = ENV['FIELD'] || ENV['field']
22
- type, field = type_and_field.split ':'
23
- Indexes.generate_cache_only type.to_sym, field.to_sym
24
- end
25
-
26
-
27
- desc 'Checks the index cache files'
28
- task :check => :application do
29
- Indexes.check_caches
30
- puts "All caches look ok."
31
- end
32
-
33
-
34
- desc "Removes the index cache files."
35
- task :clear => :application do
36
- Indexes.clear_caches
37
- puts "All index cache files removed."
38
- end
39
-
40
-
41
- desc 'Backup the index cache files'
42
- task :backup => :application do
43
- Indexes.backup_caches
44
- puts "Index cache files moved to the backup directory"
45
- end
46
-
47
- desc 'Restore the index cache files'
48
- task :restore => :application do
49
- Indexes.restore_caches
50
- puts "Index cache files restored from the backup directory"
51
- end
12
+ # desc "Generates a specific index cache file like field=books:title. Note: Index tables need to be there. Will generate just the cache."
13
+ # task :only => :application do
14
+ # type_and_field = ENV['FIELD'] || ENV['field']
15
+ # type, field = type_and_field.split ':'
16
+ # Indexes.generate_cache_only type.to_sym, field.to_sym
17
+ # end
18
+
19
+
20
+ # desc 'Checks the index cache files'
21
+ # task :check => :application do
22
+ # Indexes.check_caches
23
+ # puts "All caches look ok."
24
+ # end
25
+
26
+
27
+ # desc "Removes the index cache files."
28
+ # task :clear => :application do
29
+ # Indexes.clear_caches
30
+ # puts "All index cache files removed."
31
+ # end
32
+
33
+
34
+ # desc 'Backup the index cache files'
35
+ # task :backup => :application do
36
+ # Indexes.backup_caches
37
+ # puts "Index cache files moved to the backup directory"
38
+ # end
39
+
40
+ # desc 'Restore the index cache files'
41
+ # task :restore => :application do
42
+ # Indexes.restore_caches
43
+ # puts "Index cache files restored from the backup directory"
44
+ # end
52
45
 
53
46
  end
@@ -1,4 +1,4 @@
1
- desc "Loads the framework."
1
+ # desc "Loads the framework."
2
2
  task :framework do
3
3
  require File.expand_path '../../picky', __FILE__
4
4
  end
data/lib/tasks/index.rake CHANGED
@@ -2,24 +2,34 @@
2
2
  #
3
3
  namespace :index do
4
4
 
5
- desc "Generates the temp tables: Copies the data from the public entries."
6
- task :generate_temp_tables => :application do
7
- Indexes.take_snapshot
5
+ # Not necessary anymore.
6
+ #
7
+ # namespace :directories do
8
+ # desc 'Creates the directory structure for the indexes.'
9
+ # task :make => :application do
10
+ # Indexes.create_directory_structure
11
+ # puts "Directory structure generated."
12
+ # end
13
+ # end
14
+
15
+ desc "Takes a snapshot, indexes, and caches."
16
+ task :generate, [:order] => :application do |_, options|
17
+ randomly = (options.order == 'ordered') ? false : true
18
+ Indexes.index randomly
8
19
  end
9
20
 
10
- desc "rake index:tables:update"
11
- task :prepare => :application do
21
+ desc "Generates the index snapshots."
22
+ task :generate_snapshots => :application do
12
23
  Indexes.take_snapshot
13
- Indexes.configuration.index
14
24
  end
15
25
 
16
- desc "E.g. Generates a specific index table. Note: temp tables need to be there. Will generate just the index table."
17
- task :only, [:type, :field] => :application do |_, options|
18
- type, field = options.type, options.field
19
- Indexes.generate_index_only type.to_sym, field.to_sym
20
- end
26
+ # desc "E.g. Generates a specific index table. Note: intermediate indexes need to be there."
27
+ # task :only, [:type, :field] => :application do |_, options|
28
+ # type, field = options.type, options.field
29
+ # Indexes.generate_index_only type.to_sym, field.to_sym
30
+ # end
21
31
 
22
- desc "E.g. Generates a specific index cache file. Note: temp tables need to be there. Will generate index table and cache."
32
+ desc "Generates a specific index from index snapshots."
23
33
  task :specific, [:type, :field] => :application do |_, options|
24
34
  type, field = options.type, options.field
25
35
  Indexes.generate_index_only type.to_sym, field.to_sym
@@ -11,7 +11,7 @@ namespace :server do
11
11
  pid.blank? ? nil : pid.chomp
12
12
  end
13
13
 
14
- desc "Start the unicorns. Weheee!"
14
+ desc "Start the unicorns. (Wehee!)"
15
15
  task :start => :framework do
16
16
  chdir_to_root
17
17
  # Rake::Task[:"solr:start"].invoke # TODO Move to better place.
@@ -21,13 +21,13 @@ namespace :server do
21
21
  exec command
22
22
  end
23
23
 
24
- desc "Stop the unicorns. Blam!"
24
+ desc "Stop the unicorns. (Blam!)"
25
25
  task :stop => :framework do
26
26
  `kill -QUIT #{current_pid}` if current_pid
27
27
  # Rake::Task[:"solr:stop"].invoke # TODO Move to better place.
28
28
  end
29
29
 
30
- desc "Restart the unicorns!"
30
+ desc "Restart the unicorns."
31
31
  task :restart do
32
32
  Rake::Task[:"server:stop"].invoke
33
33
  sleep 5
@@ -1,6 +1,13 @@
1
- desc "Shortcut for indexing and caching."
1
+ desc "Shortcut for index:generate."
2
2
  task :index => :application do
3
- Indexes.index
3
+ Rake::Task[:'index:generate'].invoke
4
+ end
5
+
6
+ desc "Shortcut for try:both"
7
+ task :try, [:text, :type_and_field] => :application do |_, options|
8
+ text, type_and_field = options.text, options.type_and_field
9
+
10
+ Rake::Task[:'try:both'].invoke text, type_and_field
4
11
  end
5
12
 
6
13
  desc "shortcut for server:start"
@@ -1,13 +1,13 @@
1
1
  namespace :statistics do
2
2
 
3
- desc "start the server"
4
- task :start => :application do
5
- Statistics.start unless PICKY_ENVIRONMENT == 'test'
6
- end
3
+ # desc "start the server"
4
+ # task :start => :application do
5
+ # Statistics.start unless PICKY_ENVIRONMENT == 'test'
6
+ # end
7
7
 
8
- desc "stop the server"
9
- task :stop => :application do
10
- Statistics.stop unless PICKY_ENVIRONMENT == 'test'
11
- end
8
+ # desc "stop the server"
9
+ # task :stop => :application do
10
+ # Statistics.stop unless PICKY_ENVIRONMENT == 'test'
11
+ # end
12
12
 
13
13
  end
data/lib/tasks/try.rake CHANGED
@@ -6,7 +6,7 @@ namespace :try do
6
6
  task :index, [:text, :type_and_field] => :application do |_, options|
7
7
  text, type_and_field = options.text, options.type_and_field
8
8
 
9
- tokenizer = type_and_field ? Indexes.find(*type_and_field.split(':')).tokenizer : Tokenizers::Index.new
9
+ tokenizer = type_and_field ? Indexes.find(*type_and_field.split(':')).tokenizer : Tokenizers::Default::Index
10
10
 
11
11
  puts "\"#{text}\" is index tokenized as #{tokenizer.tokenize(text).to_a}"
12
12
  end
@@ -17,7 +17,9 @@ namespace :try do
17
17
 
18
18
  # TODO tokenize destroys the original text...
19
19
  #
20
- puts "\"#{text}\" is query tokenized as #{Tokenizers::Query.new.tokenize(text.dup).to_a.map(&:to_s)}"
20
+ # TODO Use the Query Tokenizer.
21
+ #
22
+ puts "\"#{text}\" is query tokenized as #{Tokenizers::Default::Query.tokenize(text.dup).to_a.map(&:to_s).map(&:to_sym)}"
21
23
  end
22
24
 
23
25
  desc "Try the given text with both the index and the query (type:field optional)."
@@ -2,7 +2,7 @@ source :gemcutter
2
2
 
3
3
  # Gems required by Picky.
4
4
  #
5
- gem 'picky', '~> 0.3.0'
5
+ gem 'picky', '~> 0.9.0'
6
6
  gem 'bundler', '>= 0.9.26'
7
7
  gem 'rack', '~> 1.2.1'
8
8
  gem 'rack-mount', '~> 0.6.9'
@@ -18,9 +18,13 @@ class PickySearch < Application
18
18
  Sources::CSV.new(:title, :author, :isbn, :year, :publisher, :subjects, :file => 'app/library.csv'),
19
19
  # Use a database as source:
20
20
  # Sources::DB.new('SELECT id, title, author, isbn13 as isbn FROM books', :file => 'app/db.yml'),
21
- field(:title, :similarity => Similarity::DoubleLevenshtone.new(3)), # Up to three similar title word indexed.
22
- field(:author),
23
- field(:isbn, :partial => Partial::None.new) # Partially searching on an ISBN makes not much sense.
21
+ field(:title,
22
+ :partial => Partial::Subtoken.new(:down_to => 1), # Index partial down to character 1 (default: -3),
23
+ # e.g. florian -> floria, flori, flor, flo, fl, f
24
+ # Like this, you'll find florian even when entering just an "f".
25
+ :similarity => Similarity::DoubleLevenshtone.new(3)), # Up to three similar title word indexed.
26
+ field(:author, :partial => Partial::Subtoken.new(:down_to => 1)),
27
+ field(:isbn, :partial => Partial::None.new) # Partially searching on an ISBN makes not much sense, neither does similarity.
24
28
 
25
29
  # Defines the maximum tokens (words) that pass through to the engine.
26
30
  #
@@ -6,7 +6,7 @@ describe Cacher::Partial::Default do
6
6
  Cacher::Partial::Default.should be_kind_of(Cacher::Partial::Subtoken)
7
7
  end
8
8
  it "should be a the right down to" do
9
- Cacher::Partial::Default.down_to.should == 1
9
+ Cacher::Partial::Default.down_to.should == -3
10
10
  end
11
11
  it "should be a the right starting at" do
12
12
  Cacher::Partial::Default.starting_at.should == -1
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacher::Partial::None do
4
+
5
+ it "has the right superclass" do
6
+ Cacher::Partial::None.should < Cacher::Partial::Strategy
7
+ end
8
+ it "returns an empty index" do
9
+ Cacher::Partial::None.new.generate_from(:unimportant).should == {}
10
+ end
11
+
12
+ end
@@ -122,7 +122,7 @@ describe Cacher::Partial::Subtoken do
122
122
  end
123
123
  end
124
124
  end
125
- context 'starting_at -1' do
125
+ context 'starting_at set' do
126
126
  before(:each) do
127
127
  @cacher = Cacher::Partial::Subtoken.new :down_to => 4, :starting_at => -2
128
128
  end
@@ -148,6 +148,34 @@ describe Cacher::Partial::Subtoken do
148
148
  end
149
149
  end
150
150
  end
151
+ context 'starting_at set' do
152
+ before(:each) do
153
+ @cacher = Cacher::Partial::Subtoken.new :down_to => 4, :starting_at => 0
154
+ end
155
+ describe 'starting_at' do
156
+ it 'should return the right value' do
157
+ @cacher.starting_at.should == 0
158
+ end
159
+ end
160
+ describe 'down_to' do
161
+ it 'should return the right value' do
162
+ @cacher.down_to.should == 4
163
+ end
164
+ end
165
+ describe 'generate_from' do
166
+ it 'should generate the right index' do
167
+ @cacher.generate_from( :florian => [1], :flavia => [2] ).should == {
168
+ :florian => [1],
169
+ :floria => [1],
170
+ :flori => [1],
171
+ :flor => [1],
172
+ :flavia => [2],
173
+ :flavi => [2],
174
+ :flav => [2]
175
+ }
176
+ end
177
+ end
178
+ end
151
179
  end
152
180
 
153
181
  end