picky 2.6.0 → 2.7.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.
Files changed (90) hide show
  1. data/lib/picky/analyzer.rb +4 -4
  2. data/lib/picky/application.rb +6 -7
  3. data/lib/picky/backend/{backend.rb → base.rb} +31 -14
  4. data/lib/picky/backend/file/basic.rb +12 -4
  5. data/lib/picky/backend/file/json.rb +5 -5
  6. data/lib/picky/backend/file/text.rb +1 -1
  7. data/lib/picky/backend/files.rb +3 -9
  8. data/lib/picky/backend/redis/basic.rb +8 -0
  9. data/lib/picky/backend/redis/list_hash.rb +5 -5
  10. data/lib/picky/backend/redis/string_hash.rb +5 -5
  11. data/lib/picky/backend/redis.rb +5 -5
  12. data/lib/picky/bundle.rb +62 -0
  13. data/lib/picky/categories.rb +10 -9
  14. data/lib/picky/categories_indexed.rb +12 -7
  15. data/lib/picky/categories_indexing.rb +7 -9
  16. data/lib/picky/category.rb +38 -26
  17. data/lib/picky/category_indexed.rb +4 -20
  18. data/lib/picky/category_indexing.rb +71 -68
  19. data/lib/picky/generators/base.rb +6 -6
  20. data/lib/picky/generators/partial/substring.rb +28 -26
  21. data/lib/picky/generators/partial_generator.rb +3 -3
  22. data/lib/picky/generators/similarity/phonetic.rb +5 -5
  23. data/lib/picky/generators/similarity_generator.rb +2 -2
  24. data/lib/picky/generators/weights/logarithmic.rb +3 -3
  25. data/lib/picky/generators/weights_generator.rb +2 -2
  26. data/lib/picky/index/base.rb +13 -10
  27. data/lib/picky/index/base_indexed.rb +2 -0
  28. data/lib/picky/index/base_indexing.rb +65 -57
  29. data/lib/picky/indexed/bundle/base.rb +21 -86
  30. data/lib/picky/indexed/bundle/memory.rb +5 -12
  31. data/lib/picky/indexed/bundle/redis.rb +42 -0
  32. data/lib/picky/indexed/wrappers/bundle/wrapper.rb +3 -3
  33. data/lib/picky/indexers/base.rb +20 -3
  34. data/lib/picky/indexers/parallel.rb +32 -14
  35. data/lib/picky/indexers/serial.rb +29 -26
  36. data/lib/picky/indexes.rb +5 -3
  37. data/lib/picky/indexes_indexed.rb +3 -15
  38. data/lib/picky/indexes_indexing.rb +18 -21
  39. data/lib/picky/indexing/bundle/base.rb +64 -45
  40. data/lib/picky/indexing/bundle/memory.rb +0 -4
  41. data/lib/picky/loader.rb +7 -6
  42. data/lib/picky/query/allocation.rb +3 -3
  43. data/lib/picky/query/token.rb +5 -1
  44. data/lib/picky/search.rb +5 -0
  45. data/lib/picky/sources/base.rb +21 -2
  46. data/lib/picky/sources/db.rb +0 -7
  47. data/lib/picky/statistics.rb +9 -12
  48. data/lib/picky/tokenizers/location.rb +1 -1
  49. data/lib/tasks/checks.rake +8 -6
  50. data/lib/tasks/index.rake +14 -20
  51. data/lib/tasks/server.rake +18 -2
  52. data/lib/tasks/statistics.rake +27 -14
  53. data/lib/tasks/todo.rake +2 -2
  54. data/lib/tasks/try.rake +12 -27
  55. data/spec/lib/application_spec.rb +1 -1
  56. data/spec/lib/backend/file/basic_spec.rb +6 -6
  57. data/spec/lib/backend/file/json_spec.rb +11 -6
  58. data/spec/lib/backend/file/marshal_spec.rb +11 -6
  59. data/spec/lib/backend/files_spec.rb +21 -7
  60. data/spec/lib/backend/redis/basic_spec.rb +6 -0
  61. data/spec/lib/backend/redis/list_hash_spec.rb +9 -3
  62. data/spec/lib/backend/redis/string_hash_spec.rb +7 -1
  63. data/spec/lib/backend/redis_spec.rb +22 -12
  64. data/spec/lib/categories_indexed_spec.rb +2 -2
  65. data/spec/lib/category_indexing_spec.rb +12 -33
  66. data/spec/lib/category_spec.rb +22 -0
  67. data/spec/lib/index/base_indexing_spec.rb +30 -0
  68. data/spec/lib/indexed/bundle/memory_spec.rb +13 -20
  69. data/spec/lib/indexers/base_spec.rb +39 -4
  70. data/spec/lib/indexers/parallel_spec.rb +2 -10
  71. data/spec/lib/indexers/serial_spec.rb +11 -26
  72. data/spec/lib/indexes_class_spec.rb +4 -4
  73. data/spec/lib/indexes_indexed_spec.rb +2 -2
  74. data/spec/lib/indexes_indexing_spec.rb +6 -10
  75. data/spec/lib/indexes_spec.rb +3 -3
  76. data/spec/lib/indexing/bundle/{super_base_spec.rb → base_spec.rb} +2 -2
  77. data/spec/lib/indexing/bundle/memory_partial_generation_speed_spec.rb +3 -3
  78. data/spec/lib/indexing/bundle/memory_spec.rb +16 -14
  79. data/spec/lib/indexing/bundle/redis_spec.rb +18 -16
  80. data/spec/lib/query/allocation_spec.rb +1 -1
  81. data/spec/lib/query/token_spec.rb +5 -7
  82. data/spec/lib/sources/base_spec.rb +53 -0
  83. data/spec/lib/sources/db_spec.rb +0 -7
  84. metadata +11 -12
  85. data/lib/picky/indexers/solr.rb +0 -56
  86. data/lib/picky/indexing/bundle/super_base.rb +0 -61
  87. data/lib/picky/solr/schema_generator.rb +0 -74
  88. data/lib/tasks/search.rake +0 -9
  89. data/lib/tasks/shortcuts.rake +0 -32
  90. data/lib/tasks/solr.rake +0 -36
@@ -17,11 +17,11 @@ class Analyzer # :nodoc:all
17
17
  #
18
18
  #
19
19
  def analyze bundle
20
- bundle.load_index
21
- analysis[:__keys] = bundle.size
22
- cardinality :index, bundle.index
20
+ bundle.load_inverted
21
+ analysis[:__keys] = bundle.inverted.size
22
+ cardinality :index, bundle.inverted
23
23
  index_analysis
24
- bundle.clear_index
24
+ bundle.clear_inverted
25
25
 
26
26
  bundle.load_weights
27
27
  weights bundle.weights
@@ -68,7 +68,7 @@
68
68
  # In full glory:
69
69
  # class MyGreatSearch < Application
70
70
  #
71
- # books = index :books do
71
+ # books = Index::Memory.new :books do
72
72
  # source Sources::CSV.new(:title, :author, :isbn, file:'app/library.csv')
73
73
  # category :title
74
74
  # end
@@ -227,12 +227,6 @@ class Application
227
227
  "WARNING: No routes defined for application configuration in #{self.class}." if rack_adapter.empty?
228
228
  end
229
229
 
230
- def to_s # :nodoc:
231
- <<-APPLICATION
232
- \033[1m#{name}\033[m
233
- #{to_stats.indented_to_s}
234
- APPLICATION
235
- end
236
230
  def to_stats
237
231
  <<-APP
238
232
  \033[1mIndexing (default)\033[m:
@@ -248,10 +242,15 @@ APPLICATION
248
242
  #{to_routes.indented_to_s}
249
243
  APP
250
244
  end
245
+
251
246
  def to_routes
252
247
  rack_adapter.to_s
253
248
  end
254
249
 
250
+ def to_s # :nodoc:
251
+ self.name
252
+ end
253
+
255
254
  end
256
255
 
257
256
  end
@@ -1,13 +1,20 @@
1
1
  module Backend
2
2
 
3
- class Backend
3
+ class Base
4
4
 
5
- attr_reader :bundle_name
6
- attr_reader :prepared, :index, :weights, :similarity, :configuration
5
+ attr_reader :bundle_name,
6
+ :prepared,
7
+ :inverted,
8
+ :weights,
9
+ :similarity,
10
+ :configuration,
11
+ :category,
12
+ :identifier
7
13
 
8
14
  delegate :index_name, :category_name, :to => :@category
9
15
 
10
16
  def initialize bundle_name, category
17
+ @identifier = "#{category.identifier}:#{bundle_name}"
11
18
  @bundle_name = bundle_name
12
19
  @category = category
13
20
  @prepared = File::Text.new category.prepared_index_path
@@ -24,23 +31,27 @@ module Backend
24
31
 
25
32
  # Dumping.
26
33
  #
27
- def dump_index index_hash
28
- index.dump index_hash
34
+ def dump_inverted inverted_hash
35
+ timed_exclaim %Q{"#{identifier}": => #{inverted}.}
36
+ inverted.dump inverted_hash
29
37
  end
30
38
  def dump_weights weights_hash
39
+ timed_exclaim %Q{"#{identifier}": => #{weights}.}
31
40
  weights.dump weights_hash
32
41
  end
33
42
  def dump_similarity similarity_hash
43
+ timed_exclaim %Q{"#{identifier}": => #{similarity}.}
34
44
  similarity.dump similarity_hash
35
45
  end
36
46
  def dump_configuration configuration_hash
47
+ timed_exclaim %Q{"#{identifier}": => #{configuration}.}
37
48
  configuration.dump configuration_hash
38
49
  end
39
50
 
40
51
  # Loading.
41
52
  #
42
- def load_index
43
- index.load
53
+ def load_inverted
54
+ inverted.load
44
55
  end
45
56
  def load_similarity
46
57
  similarity.load
@@ -54,8 +65,8 @@ module Backend
54
65
 
55
66
  # Cache ok?
56
67
  #
57
- def index_cache_ok?
58
- index.cache_ok?
68
+ def inverted_cache_ok?
69
+ inverted.cache_ok?
59
70
  end
60
71
  def similarity_cache_ok?
61
72
  similarity.cache_ok?
@@ -66,8 +77,8 @@ module Backend
66
77
 
67
78
  # Cache small?
68
79
  #
69
- def index_cache_small?
70
- index.cache_small?
80
+ def inverted_cache_small?
81
+ inverted.cache_small?
71
82
  end
72
83
  def similarity_cache_small?
73
84
  similarity.cache_small?
@@ -79,7 +90,7 @@ module Backend
79
90
  # Copies the indexes to the "backup" directory.
80
91
  #
81
92
  def backup
82
- index.backup
93
+ inverted.backup
83
94
  weights.backup
84
95
  similarity.backup
85
96
  configuration.backup
@@ -88,7 +99,7 @@ module Backend
88
99
  # Restores the indexes from the "backup" directory.
89
100
  #
90
101
  def restore
91
- index.restore
102
+ inverted.restore
92
103
  weights.restore
93
104
  similarity.restore
94
105
  configuration.restore
@@ -97,12 +108,18 @@ module Backend
97
108
  # Delete all index files.
98
109
  #
99
110
  def delete
100
- index.delete
111
+ inverted.delete
101
112
  weights.delete
102
113
  similarity.delete
103
114
  configuration.delete
104
115
  end
105
116
 
117
+ #
118
+ #
119
+ def to_s
120
+ "#{self.class}(#{prepared}, #{bundle_name}, #{category.identifier})"
121
+ end
122
+
106
123
  end
107
124
 
108
125
  end
@@ -13,6 +13,8 @@ module Backend
13
13
  #
14
14
  class Basic
15
15
 
16
+ # This file's location.
17
+ #
16
18
  attr_reader :cache_path
17
19
 
18
20
  # An index cache takes a path, without file extension,
@@ -22,10 +24,6 @@ module Backend
22
24
  @cache_path = "#{cache_path}.#{extension}"
23
25
  end
24
26
 
25
- def to_s
26
- cache_path
27
- end
28
-
29
27
  # The default extension for index files is "index".
30
28
  #
31
29
  def extension
@@ -40,12 +38,15 @@ module Backend
40
38
  prepare_backup backup_directory
41
39
  FileUtils.cp cache_path, target, verbose: true
42
40
  end
41
+
43
42
  # The backup directory of this file.
44
43
  # Equal to the file's dirname plus /backup
45
44
  #
45
+
46
46
  def backup_directory
47
47
  ::File.join ::File.dirname(cache_path), 'backup'
48
48
  end
49
+
49
50
  # Prepares the backup directory for the file.
50
51
  #
51
52
  def prepare_backup target
@@ -58,6 +59,7 @@ module Backend
58
59
  def restore
59
60
  FileUtils.cp backup_file_path_of(cache_path), cache_path, verbose: true
60
61
  end
62
+
61
63
  # The backup filename.
62
64
  #
63
65
  def backup_file_path_of path
@@ -94,6 +96,12 @@ module Backend
94
96
  `ls -l #{path} | awk '{print $5}'`.to_i
95
97
  end
96
98
 
99
+ #
100
+ #
101
+ def to_s
102
+ "#{self.class}(#{cache_path})"
103
+ end
104
+
97
105
  end
98
106
 
99
107
  end
@@ -1,11 +1,11 @@
1
1
  module Backend
2
-
2
+
3
3
  module File
4
-
4
+
5
5
  # Index files dumped in the JSON format.
6
6
  #
7
7
  class JSON < Basic
8
-
8
+
9
9
  # Uses the extension "json".
10
10
  #
11
11
  def extension
@@ -26,9 +26,9 @@ module Backend
26
26
  def retrieve
27
27
  raise "Can't retrieve from JSON file. Use text file."
28
28
  end
29
-
29
+
30
30
  end
31
-
31
+
32
32
  end
33
33
 
34
34
  end
@@ -44,7 +44,7 @@ module Backend
44
44
 
45
45
  #
46
46
  #
47
- def open_for_indexing &block
47
+ def open &block
48
48
  ::File.open cache_path, 'w:binary', &block
49
49
  end
50
50
 
@@ -1,6 +1,6 @@
1
1
  module Backend
2
2
 
3
- class Files < Backend
3
+ class Files < Base
4
4
 
5
5
  def initialize bundle_name, category
6
6
  super bundle_name, category
@@ -9,20 +9,14 @@ module Backend
9
9
  # Yajl json lib cannot load symbolized
10
10
  # values, just keys.
11
11
  #
12
- @index = File::JSON.new category.index_path(bundle_name, :index)
12
+ @inverted = File::JSON.new category.index_path(bundle_name, :inverted)
13
13
  @weights = File::JSON.new category.index_path(bundle_name, :weights)
14
14
  @similarity = File::Marshal.new category.index_path(bundle_name, :similarity)
15
15
  @configuration = File::JSON.new category.index_path(bundle_name, :configuration)
16
16
  end
17
17
 
18
18
  def to_s
19
- <<-FILES
20
- Files:
21
- #{"Index: #{@index}".indented_to_s}
22
- #{"Weights: #{@weights}".indented_to_s}
23
- #{"Similarity: #{@similarity}".indented_to_s}
24
- #{"Config: #{@configuration}".indented_to_s}
25
- FILES
19
+ "#{self.class}(#{[@prepared, @inverted, @weights, @similarity, @configuration].join(', ')})"
26
20
  end
27
21
 
28
22
  end
@@ -61,6 +61,7 @@ module Backend
61
61
  def cache_small?
62
62
  size < 1
63
63
  end
64
+
64
65
  # Is the cache ok?
65
66
  #
66
67
  # A small cache is still ok.
@@ -68,6 +69,7 @@ module Backend
68
69
  def cache_ok?
69
70
  size > 0
70
71
  end
72
+
71
73
  # Extracts the size of the file in Bytes.
72
74
  #
73
75
  # Note: This is a very forgiving implementation.
@@ -78,6 +80,12 @@ module Backend
78
80
  backend.dbsize
79
81
  end
80
82
 
83
+ #
84
+ #
85
+ def to_s
86
+ "#{self.class}(#{namespace}:*)"
87
+ end
88
+
81
89
  end
82
90
 
83
91
  end
@@ -17,7 +17,7 @@ module Backend
17
17
  end
18
18
  end
19
19
  end
20
-
20
+
21
21
  # Clear the index for this list.
22
22
  #
23
23
  # Note: Perhaps we can use a server only command.
@@ -32,14 +32,14 @@ module Backend
32
32
 
33
33
  # Get a collection.
34
34
  #
35
- def collection sym
36
- backend.lrange "#{namespace}:#{sym}", 0, -1
35
+ def collection key
36
+ backend.zrange "#{namespace}:#{key}", 0, -1
37
37
  end
38
38
 
39
39
  # Get a single value.
40
40
  #
41
- def member sym
42
- raise "Can't retrieve a single value from a Redis ListHash. Use Index::Redis::StringHash."
41
+ def member key
42
+ raise "Can't retrieve single value :#{key} from a Redis ListHash. Use Index::Redis::StringHash."
43
43
  end
44
44
 
45
45
  end
@@ -14,7 +14,7 @@ module Backend
14
14
  backend.hset namespace, key, value
15
15
  end
16
16
  end
17
-
17
+
18
18
  # Clears the hash.
19
19
  #
20
20
  def clear
@@ -23,14 +23,14 @@ module Backend
23
23
 
24
24
  # Get a collection.
25
25
  #
26
- def collection sym
27
- raise "Can't retrieve a collection from a StringHash. Use Index::Redis::ListHash."
26
+ def collection key
27
+ raise "Can't retrieve collection for :#{key} from a StringHash. Use Index::Redis::ListHash."
28
28
  end
29
29
 
30
30
  # Get a single value.
31
31
  #
32
- def member sym
33
- backend.hget namespace, sym
32
+ def member key
33
+ backend.hget namespace, key
34
34
  end
35
35
 
36
36
  end
@@ -1,24 +1,24 @@
1
1
  module Backend
2
2
 
3
- # TODO Needs a reconnect to be run after forking.
4
3
  #
5
- class Redis < Backend
4
+ #
5
+ class Redis < Base
6
6
 
7
7
  def initialize bundle_name, category
8
8
  super bundle_name, category
9
9
 
10
10
  # Refine a few Redis "types".
11
11
  #
12
- @index = Redis::ListHash.new "#{category.identifier}:#{bundle_name}:index"
12
+ @inverted = Redis::ListHash.new "#{category.identifier}:#{bundle_name}:inverted"
13
13
  @weights = Redis::StringHash.new "#{category.identifier}:#{bundle_name}:weights"
14
- @similarity = Redis::ListHash.new "#{category.identifier}:#{bundle_name}:similarity"
14
+ @similarity = Redis::ListHash.new "#{category.identifier}:#{bundle_name}:similarity"
15
15
  @configuration = Redis::StringHash.new "#{category.identifier}:#{bundle_name}:configuration"
16
16
  end
17
17
 
18
18
  # Delegate to the right collection.
19
19
  #
20
20
  def ids sym
21
- index.collection sym
21
+ inverted.collection sym
22
22
  end
23
23
 
24
24
  # Delegate to the right member value.
@@ -0,0 +1,62 @@
1
+ # A Bundle is a number of indexes
2
+ # per [index, category] combination.
3
+ #
4
+ # At most, there are three indexes:
5
+ # * *core* index (always used)
6
+ # * *weights* index (always used)
7
+ # * *similarity* index (used with similarity)
8
+ #
9
+ # In Picky, indexing is separated from the index
10
+ # handling itself through a parallel structure.
11
+ #
12
+ # Both use methods provided by this base class, but
13
+ # have very different goals:
14
+ #
15
+ # * *Indexing*::*Bundle*::*Base* is just concerned with creating index
16
+ # files / redis entries and providing helper functions to e.g. check
17
+ # the indexes.
18
+ #
19
+ # * *Index*::*Bundle*::*Base* is concerned with loading these index files into
20
+ # memory / redis and looking up search data as fast as possible.
21
+ #
22
+ class Bundle
23
+
24
+ attr_reader :identifier,
25
+ :files
26
+ attr_accessor :inverted,
27
+ :weights,
28
+ :similarity,
29
+ :configuration,
30
+ :similarity_strategy
31
+
32
+ delegate :clear, :to => :inverted
33
+ delegate :[], :[]=, :to => :configuration
34
+
35
+ def initialize name, category, similarity_strategy
36
+ @identifier = "#{category.identifier}:#{name}"
37
+ @files = Backend::Files.new name, category
38
+
39
+ @inverted = {}
40
+ @weights = {}
41
+ @similarity = {}
42
+ @configuration = {} # A hash with config options.
43
+
44
+ @similarity_strategy = similarity_strategy
45
+ end
46
+
47
+ # Get a list of similar texts.
48
+ #
49
+ # Note: Does not return itself.
50
+ #
51
+ def similar text
52
+ code = similarity_strategy.encoded text
53
+ similar_codes = code && @similarity[code]
54
+ similar_codes.delete text if similar_codes
55
+ similar_codes || []
56
+ end
57
+
58
+ def to_s
59
+ "#{self.class}(#{identifier}, #{files})"
60
+ end
61
+
62
+ end
@@ -8,6 +8,7 @@ class Categories
8
8
  :to => :categories
9
9
 
10
10
  each_delegate :reindex,
11
+ :each_category,
11
12
  :to => :categories
12
13
 
13
14
  # A list of indexed categories.
@@ -26,18 +27,25 @@ class Categories
26
27
  # Nifty! :)
27
28
  #
28
29
  def initialize options = {}
29
- clear
30
+ clear_categories
30
31
 
31
32
  @ignore_unassigned_tokens = options[:ignore_unassigned_tokens] || false
32
33
  end
33
34
 
34
35
  # Clears both the array of categories and the hash of categories.
35
36
  #
36
- def clear
37
+ def clear_categories
37
38
  @categories = []
38
39
  @category_hash = {}
39
40
  end
40
41
 
42
+ # Add the given category to the list of categories.
43
+ #
44
+ def << category
45
+ categories << category
46
+ category_hash[category.name] = category
47
+ end
48
+
41
49
  # Find a given category in the categories.
42
50
  #
43
51
  def [] category_name
@@ -48,13 +56,6 @@ class Categories
48
56
  raise %Q{Index category "#{category_name}" not found. Possible categories: "#{categories.map(&:name).join('", "')}".}
49
57
  end
50
58
 
51
- # Add the given category to the list of categories.
52
- #
53
- def << category
54
- categories << category
55
- category_hash[category.name] = category
56
- end
57
-
58
59
  def to_s
59
60
  categories.join(', ')
60
61
  end
@@ -1,5 +1,5 @@
1
1
  class Categories
2
-
2
+
3
3
  attr_reader :ignore_unassigned_tokens
4
4
 
5
5
  each_delegate :load_from_cache,
@@ -15,17 +15,17 @@ class Categories
15
15
  def possible_combinations_for token
16
16
  token.similar? ? similar_possible_for(token) : possible_for(token)
17
17
  end
18
+
18
19
  # Gets all similar tokens and puts together the possible combinations
19
20
  # for each found similar token.
20
21
  #
21
22
  def similar_possible_for token
22
- # Get as many tokens as necessary
23
- #
24
23
  tokens = similar_tokens_for token
25
- # possible combinations
26
- #
27
24
  inject_possible_for tokens
28
25
  end
26
+
27
+ # Returns all possible similar tokens for the given token.
28
+ #
29
29
  def similar_tokens_for token
30
30
  text = token.text
31
31
  categories.inject([]) do |result, category|
@@ -41,6 +41,9 @@ class Categories
41
41
  result
42
42
  end
43
43
  end
44
+
45
+ #
46
+ #
44
47
  def inject_possible_for tokens
45
48
  tokens.inject([]) do |result, token|
46
49
  possible = possible_categories token
@@ -65,8 +68,9 @@ class Categories
65
68
  # This is an optimization to mark tokens that are ignored.
66
69
  #
67
70
  return if ignore_unassigned_tokens && possible.empty?
68
- possible # wrap in combinations
71
+ possible
69
72
  end
73
+
70
74
  # This returns the possible categories for this token.
71
75
  # If the user has already preselected a category for this token,
72
76
  # like "artist:moby", if not just return all for the given token,
@@ -77,6 +81,7 @@ class Categories
77
81
  def possible_categories token
78
82
  user_defined_categories(token) || categories
79
83
  end
84
+
80
85
  # This returns the array of categories if the user has defined
81
86
  # an existing category.
82
87
  #
@@ -89,5 +94,5 @@ class Categories
89
94
  category_hash[name]
90
95
  end.compact
91
96
  end
92
-
97
+
93
98
  end
@@ -1,12 +1,10 @@
1
1
  class Categories
2
-
3
- each_delegate :backup_caches,
4
- :cache,
5
- :check_caches,
6
- :clear_caches,
7
- :create_directory_structure,
8
- :generate_caches,
9
- :restore_caches,
2
+
3
+ each_delegate :cache,
4
+ :check,
5
+ :clear,
6
+ :backup,
7
+ :restore,
10
8
  :to => :categories
11
-
9
+
12
10
  end