picky 0.12.3 → 1.0.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/lib/deployment.rb +2 -2
- data/lib/picky/application.rb +172 -12
- data/lib/picky/cacher/generator.rb +1 -1
- data/lib/picky/calculations/location.rb +9 -1
- data/lib/picky/character_substituters/west_european.rb +1 -1
- data/lib/picky/configuration/index.rb +1 -1
- data/lib/picky/cores.rb +1 -1
- data/lib/picky/extensions/array.rb +1 -1
- data/lib/picky/extensions/hash.rb +1 -1
- data/lib/picky/extensions/module.rb +1 -1
- data/lib/picky/extensions/object.rb +1 -1
- data/lib/picky/extensions/symbol.rb +1 -1
- data/lib/picky/generator.rb +2 -2
- data/lib/picky/helpers/cache.rb +7 -5
- data/lib/picky/helpers/gc.rb +2 -0
- data/lib/picky/helpers/measuring.rb +2 -0
- data/lib/picky/index/bundle.rb +1 -1
- data/lib/picky/index_api.rb +33 -15
- data/lib/picky/indexed/bundle.rb +1 -1
- data/lib/picky/indexed/index.rb +1 -1
- data/lib/picky/indexed/wrappers/bundle/location.rb +1 -1
- data/lib/picky/indexers/no_source_specified_error.rb +1 -1
- data/lib/picky/indexes_api.rb +1 -1
- data/lib/picky/indexing/bundle.rb +1 -1
- data/lib/picky/indexing/index.rb +1 -1
- data/lib/picky/loader.rb +1 -1
- data/lib/picky/loggers/search.rb +1 -1
- data/lib/picky/performant.rb +3 -0
- data/lib/picky/query/allocation.rb +1 -1
- data/lib/picky/query/allocations.rb +1 -1
- data/lib/picky/query/base.rb +48 -16
- data/lib/picky/query/combination.rb +1 -1
- data/lib/picky/query/combinations.rb +1 -1
- data/lib/picky/query/full.rb +7 -2
- data/lib/picky/query/live.rb +9 -7
- data/lib/picky/query/qualifiers.rb +6 -2
- data/lib/picky/query/solr.rb +1 -1
- data/lib/picky/query/token.rb +2 -1
- data/lib/picky/query/tokens.rb +4 -1
- data/lib/picky/query/weigher.rb +1 -1
- data/lib/picky/query/weights.rb +1 -1
- data/lib/picky/rack/harakiri.rb +14 -5
- data/lib/picky/results/base.rb +1 -1
- data/lib/picky/routing.rb +1 -1
- data/lib/picky/solr/schema_generator.rb +2 -1
- data/lib/picky/sources/base.rb +39 -25
- data/lib/picky/sources/couch.rb +22 -8
- data/lib/picky/sources/csv.rb +29 -6
- data/lib/picky/sources/db.rb +46 -30
- data/lib/picky/sources/delicious.rb +12 -2
- data/lib/picky/sources/wrappers/base.rb +3 -1
- data/lib/picky/tokenizers/base.rb +1 -1
- data/project_prototype/Gemfile +1 -1
- data/project_prototype/app/README +0 -1
- data/spec/lib/calculations/location_spec.rb +28 -16
- data/spec/lib/index_api_spec.rb +64 -0
- data/spec/lib/indexed/index_spec.rb +2 -2
- data/spec/lib/indexed/wrappers/exact_first_spec.rb +2 -2
- data/spec/lib/indexing/index_spec.rb +2 -2
- data/spec/lib/rack/harakiri_spec.rb +22 -10
- metadata +7 -4
data/lib/picky/sources/db.rb
CHANGED
@@ -1,11 +1,32 @@
|
|
1
1
|
module Sources
|
2
2
|
|
3
|
-
# Describes a database source.
|
3
|
+
# Describes a database source. Needs a SELECT statement
|
4
4
|
# (with id in it), and a file option or the options from an AR config file.
|
5
5
|
#
|
6
|
+
# The select statement can be as complicated as you want,
|
7
|
+
# as long as it has an id in it and as long as it can be
|
8
|
+
# used in a CREATE TABLE AS statement.
|
9
|
+
# (working on that last one)
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
# Sources::DB.new('SELECT id, title, author, year FROM books') # Uses the config from app/db.yml by default.
|
13
|
+
# Sources::DB.new('SELECT id, title, author, year FROM books', file: 'app/some_db.yml')
|
14
|
+
# Sources::DB.new('SELECT b.id, b.title, b.author, b.publishing_year as year FROM books b INNER JOIN ON ...', file: 'app/some_db.yml')
|
15
|
+
# Sources::DB.new('SELECT id, title, author, year FROM books', adapter: 'mysql', host:'localhost', ...)
|
16
|
+
#
|
6
17
|
class DB < Base
|
7
18
|
|
8
|
-
|
19
|
+
# The select statement that was passed in.
|
20
|
+
#
|
21
|
+
attr_reader :select_statement
|
22
|
+
|
23
|
+
# The database adapter.
|
24
|
+
#
|
25
|
+
attr_reader :database
|
26
|
+
|
27
|
+
# The database connection options that were either passed in or loaded from the given file.
|
28
|
+
#
|
29
|
+
attr_reader :connection_options
|
9
30
|
|
10
31
|
def initialize select_statement, options = { file: 'app/db.yml' }
|
11
32
|
@select_statement = select_statement
|
@@ -13,17 +34,10 @@ module Sources
|
|
13
34
|
@options = options
|
14
35
|
end
|
15
36
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
# * file => 'some/filename.yml' # With an active record configuration.
|
21
|
-
# Or
|
22
|
-
# * The configuration as a hash.
|
23
|
-
#
|
24
|
-
# TODO Do not use ActiveRecord directly.
|
25
|
-
#
|
26
|
-
def create_database_adapter
|
37
|
+
# Creates a database adapter for use with this source.
|
38
|
+
def create_database_adapter # :nodoc:
|
39
|
+
# TODO Do not use ActiveRecord directly.
|
40
|
+
#
|
27
41
|
adapter_class = Class.new ActiveRecord::Base
|
28
42
|
adapter_class.abstract_class = true
|
29
43
|
adapter_class
|
@@ -37,7 +51,7 @@ module Sources
|
|
37
51
|
# Or
|
38
52
|
# * The configuration as a hash.
|
39
53
|
#
|
40
|
-
def configure options
|
54
|
+
def configure options # :nodoc:
|
41
55
|
@connection_options = if filename = options[:file]
|
42
56
|
File.open(File.join(PICKY_ROOT, filename)) { |file| YAML::load(file) }
|
43
57
|
else
|
@@ -48,13 +62,17 @@ module Sources
|
|
48
62
|
|
49
63
|
# Connect the backend.
|
50
64
|
#
|
65
|
+
# Will raise unless connection options have been given.
|
66
|
+
#
|
51
67
|
def connect_backend
|
52
68
|
configure @options
|
53
69
|
raise "Database backend not configured" unless connection_options
|
54
70
|
database.establish_connection connection_options
|
55
71
|
end
|
56
72
|
|
57
|
-
# Take the
|
73
|
+
# Take a snapshot of the data.
|
74
|
+
#
|
75
|
+
# Uses CREATE TABLE AS with the given SELECT statement to create a snapshot of the data.
|
58
76
|
#
|
59
77
|
def take_snapshot type
|
60
78
|
connect_backend
|
@@ -75,23 +93,19 @@ module Sources
|
|
75
93
|
|
76
94
|
# Counts all the entries that are used for the index.
|
77
95
|
#
|
78
|
-
def count type
|
96
|
+
def count type # :nodoc:
|
79
97
|
connect_backend
|
80
98
|
|
81
99
|
database.connection.select_value("SELECT COUNT(id) FROM #{snapshot_table_name(type)}").to_i
|
82
100
|
end
|
83
101
|
|
84
|
-
# Ok here?
|
85
102
|
#
|
86
|
-
|
103
|
+
#
|
104
|
+
def snapshot_table_name type # :nodoc:
|
87
105
|
"#{type.name}_type_index"
|
88
106
|
end
|
89
107
|
|
90
|
-
# Harvests the data to index
|
91
|
-
#
|
92
|
-
# Subclasses should override harvest_statement to define how their data is found.
|
93
|
-
# Example:
|
94
|
-
# "SELECT indexed_id, value FROM bla_table st WHERE kind = 'bla'"
|
108
|
+
# Harvests the data to index in chunks.
|
95
109
|
#
|
96
110
|
def harvest type, category
|
97
111
|
connect_backend
|
@@ -105,9 +119,9 @@ module Sources
|
|
105
119
|
end
|
106
120
|
end
|
107
121
|
|
108
|
-
# Gets
|
122
|
+
# Gets the data from the backend.
|
109
123
|
#
|
110
|
-
def get_data type, category, offset
|
124
|
+
def get_data type, category, offset # :nodoc:
|
111
125
|
database.connection.execute harvest_statement_with_offset(type, category, offset)
|
112
126
|
end
|
113
127
|
|
@@ -115,7 +129,7 @@ module Sources
|
|
115
129
|
#
|
116
130
|
# TODO Use the adapter for this.
|
117
131
|
#
|
118
|
-
def harvest_statement_with_offset type, category, offset
|
132
|
+
def harvest_statement_with_offset type, category, offset # :nodoc:
|
119
133
|
statement = harvest_statement type, category
|
120
134
|
|
121
135
|
statement += statement.include?('WHERE') ? ' AND' : ' WHERE'
|
@@ -123,15 +137,17 @@ module Sources
|
|
123
137
|
"#{statement} st.id > #{offset} LIMIT #{chunksize}"
|
124
138
|
end
|
125
139
|
|
126
|
-
#
|
140
|
+
# The harvest statement used to pull data from the snapshot table.
|
127
141
|
#
|
128
|
-
def harvest_statement type, category
|
142
|
+
def harvest_statement type, category # :nodoc:
|
129
143
|
"SELECT indexed_id, #{category.from} FROM #{snapshot_table_name(type)} st"
|
130
144
|
end
|
131
145
|
|
132
|
-
#
|
146
|
+
# The amount of records that are loaded each chunk.
|
133
147
|
#
|
134
|
-
def chunksize
|
148
|
+
def chunksize # :nodoc:
|
149
|
+
# TODO Make parametrizable.
|
150
|
+
#
|
135
151
|
25_000
|
136
152
|
end
|
137
153
|
|
@@ -1,5 +1,15 @@
|
|
1
1
|
module Sources
|
2
2
|
|
3
|
+
# Describes a Delicious (http://deli.cio.us) source.
|
4
|
+
#
|
5
|
+
# This source has a fixed set of categories:
|
6
|
+
# * title
|
7
|
+
# * tags
|
8
|
+
# * url
|
9
|
+
#
|
10
|
+
# Examples:
|
11
|
+
# Sources::CSV.new('usrnam', 'paswrd')
|
12
|
+
#
|
3
13
|
class Delicious < Base
|
4
14
|
|
5
15
|
def initialize username, password
|
@@ -7,7 +17,7 @@ module Sources
|
|
7
17
|
@username = username
|
8
18
|
@password = password
|
9
19
|
end
|
10
|
-
def check_gem
|
20
|
+
def check_gem # :nodoc:
|
11
21
|
require 'www/delicious'
|
12
22
|
rescue LoadError
|
13
23
|
puts "Delicious gem missing!\nTo use the delicious source, you need to:\n 1. Add the following line to Gemfile:\n gem 'www-delicious'\n 2. Then, run:\n bundle update\n"
|
@@ -28,7 +38,7 @@ module Sources
|
|
28
38
|
|
29
39
|
#
|
30
40
|
#
|
31
|
-
def get_data
|
41
|
+
def get_data # :nodoc:
|
32
42
|
@generated_id ||= 0
|
33
43
|
@posts ||= WWW::Delicious.new(@username, @password).posts_recent(count: 100)
|
34
44
|
@posts.each do |post|
|
data/project_prototype/Gemfile
CHANGED
@@ -2,32 +2,44 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Calculations::Location do
|
4
4
|
|
5
|
-
context '
|
5
|
+
context 'with precision 1' do
|
6
6
|
before(:each) do
|
7
|
-
@calculation = Calculations::Location.new
|
8
|
-
@calculation.minimum =
|
7
|
+
@calculation = Calculations::Location.new 1.5, 1
|
8
|
+
@calculation.minimum = 42.7
|
9
9
|
end
|
10
|
-
describe '
|
11
|
-
it '
|
12
|
-
@calculation.recalculate(
|
10
|
+
describe 'recalculate' do
|
11
|
+
it 'sets the minimum close value to the minimum minus user grid' do
|
12
|
+
@calculation.recalculate(41.2).should == 1
|
13
13
|
end
|
14
|
-
it '
|
15
|
-
@calculation.recalculate(
|
14
|
+
it 'sets the minimum value to 1 plus precision' do
|
15
|
+
@calculation.recalculate(42.7).should == 2
|
16
|
+
end
|
17
|
+
it 'sets the minimum value plus 2/3 of the grid size to 2 plus 1 grid length' do
|
18
|
+
@calculation.recalculate(43.7).should == 3
|
19
|
+
end
|
20
|
+
it 'sets the minimum value plus 20/3 of the grid size to 2 plus 10 grid length' do
|
21
|
+
@calculation.recalculate(52.7).should == 12
|
16
22
|
end
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
20
|
-
context '
|
26
|
+
context 'with precision 3' do
|
21
27
|
before(:each) do
|
22
|
-
@calculation = Calculations::Location.new
|
23
|
-
@calculation.minimum =
|
28
|
+
@calculation = Calculations::Location.new 1.5, 3
|
29
|
+
@calculation.minimum = 42.7
|
24
30
|
end
|
25
|
-
describe '
|
26
|
-
it '
|
27
|
-
@calculation.recalculate(
|
31
|
+
describe 'recalculate' do
|
32
|
+
it 'sets the minimum close value to the minimum minus user grid' do
|
33
|
+
@calculation.recalculate(41.2).should == 1
|
34
|
+
end
|
35
|
+
it 'sets the minimum value to 1 plus precision' do
|
36
|
+
@calculation.recalculate(42.7).should == 4
|
37
|
+
end
|
38
|
+
it 'sets the minimum value plus 2/3 of the grid size plus 1 plus precision plus 1 grid length' do
|
39
|
+
@calculation.recalculate(43.7).should == 6
|
28
40
|
end
|
29
|
-
it '
|
30
|
-
@calculation.recalculate(
|
41
|
+
it 'sets the minimum value plus 20/3 of the grid size to 2 plus 10 grid length' do
|
42
|
+
@calculation.recalculate(52.7).should == 27
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe IndexAPI do
|
6
|
+
|
7
|
+
context 'initializer' do
|
8
|
+
it 'works' do
|
9
|
+
lambda { IndexAPI.new :some_index_name, :some_source }.should_not raise_error
|
10
|
+
end
|
11
|
+
it 'registers with the indexes' do
|
12
|
+
@api = IndexAPI.allocate
|
13
|
+
|
14
|
+
Indexes.should_receive(:register).once.with @api
|
15
|
+
|
16
|
+
@api.send :initialize, :some_index_name, :some_source
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'unit' do
|
21
|
+
before(:each) do
|
22
|
+
@api = IndexAPI.new :some_index_name, :some_source
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'define_category' do
|
26
|
+
context 'with block' do
|
27
|
+
it 'returns itself' do
|
28
|
+
@api.define_category(:some_name){ |indexing, indexed| }.should == @api
|
29
|
+
end
|
30
|
+
it 'takes a string' do
|
31
|
+
lambda { @api.define_category('some_name'){ |indexing, indexed| } }.should_not raise_error
|
32
|
+
end
|
33
|
+
it 'yields both the indexing category and the indexed category' do
|
34
|
+
@api.define_category(:some_name) do |indexing, indexed|
|
35
|
+
indexing.should be_kind_of(Indexing::Category)
|
36
|
+
indexed.should be_kind_of(Indexed::Category)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
it 'yields the indexing category which has the given name' do
|
40
|
+
@api.define_category(:some_name) do |indexing, indexed|
|
41
|
+
indexing.name.should == :some_name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
it 'yields the indexed category which has the given name' do
|
45
|
+
@api.define_category(:some_name) do |indexing, indexed|
|
46
|
+
indexed.name.should == :some_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
context 'without block' do
|
51
|
+
it 'works' do
|
52
|
+
lambda { @api.define_category(:some_name) }.should_not raise_error
|
53
|
+
end
|
54
|
+
it 'takes a string' do
|
55
|
+
lambda { @api.define_category('some_name').should == @api }.should_not raise_error
|
56
|
+
end
|
57
|
+
it 'returns itself' do
|
58
|
+
@api.define_category(:some_name).should == @api
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -7,8 +7,8 @@ describe Indexed::Index do
|
|
7
7
|
@categories = stub :categories
|
8
8
|
|
9
9
|
@index = Indexed::Index.new :some_name
|
10
|
-
@index.
|
11
|
-
@index.
|
10
|
+
@index.define_category :some_category_name1
|
11
|
+
@index.define_category :some_category_name2
|
12
12
|
|
13
13
|
@index.stub! :categories => @categories
|
14
14
|
end
|
@@ -16,7 +16,7 @@ describe Indexed::Wrappers::ExactFirst do
|
|
16
16
|
#
|
17
17
|
# it "wraps each category" do
|
18
18
|
# type = Index::Type.new :type_name
|
19
|
-
# type.
|
19
|
+
# type.define_category :some_category
|
20
20
|
#
|
21
21
|
# Index::Wrappers::ExactFirst.wrap type
|
22
22
|
#
|
@@ -24,7 +24,7 @@ describe Indexed::Wrappers::ExactFirst do
|
|
24
24
|
# end
|
25
25
|
it "returns the type" do
|
26
26
|
type = Indexed::Index.new :type_name
|
27
|
-
type.
|
27
|
+
type.define_category :some_category
|
28
28
|
|
29
29
|
Indexed::Wrappers::ExactFirst.wrap(type).should == type
|
30
30
|
end
|
@@ -9,8 +9,8 @@ describe Indexing::Index do
|
|
9
9
|
@categories = stub :categories
|
10
10
|
|
11
11
|
@index = Indexing::Index.new :some_name, @source
|
12
|
-
@index.
|
13
|
-
@index.
|
12
|
+
@index.define_category :some_category_name1
|
13
|
+
@index.define_category :some_category_name2
|
14
14
|
|
15
15
|
@index.stub! :categories => @categories
|
16
16
|
end
|
@@ -7,21 +7,33 @@ describe Rack::Harakiri do
|
|
7
7
|
end
|
8
8
|
context "defaults" do
|
9
9
|
before(:each) do
|
10
|
-
@
|
10
|
+
@ronin = Rack::Harakiri.new @app
|
11
11
|
end
|
12
|
-
it "should quit after
|
13
|
-
@
|
12
|
+
it "should quit after a default amount of requests" do
|
13
|
+
@ronin.instance_variable_get(:@quit_after_requests).should == 50
|
14
|
+
end
|
15
|
+
describe 'harakiri?' do
|
16
|
+
it "should be true after 50 harakiri calls" do
|
17
|
+
50.times { @ronin.harakiri }
|
18
|
+
|
19
|
+
@ronin.harakiri?.should == true
|
20
|
+
end
|
21
|
+
it "should not be true after just 49 harakiri calls" do
|
22
|
+
49.times { @ronin.harakiri }
|
23
|
+
|
24
|
+
@ronin.harakiri?.should == false
|
25
|
+
end
|
14
26
|
end
|
15
27
|
describe "harakiri" do
|
16
28
|
it "should kill the process after 50 harakiri calls" do
|
17
29
|
Process.should_receive(:kill).once
|
18
30
|
|
19
|
-
50.times { @
|
31
|
+
50.times { @ronin.harakiri }
|
20
32
|
end
|
21
33
|
it "should not kill the process after 49 harakiri calls" do
|
22
34
|
Process.should_receive(:kill).never
|
23
35
|
|
24
|
-
49.times { @
|
36
|
+
49.times { @ronin.harakiri }
|
25
37
|
end
|
26
38
|
end
|
27
39
|
describe "call" do
|
@@ -30,27 +42,27 @@ describe Rack::Harakiri do
|
|
30
42
|
@app.stub! :harakiri
|
31
43
|
end
|
32
44
|
it "calls harakiri" do
|
33
|
-
@
|
45
|
+
@ronin.should_receive(:harakiri).once.with
|
34
46
|
|
35
|
-
@
|
47
|
+
@ronin.call :env
|
36
48
|
end
|
37
49
|
it "calls the app" do
|
38
50
|
@app.should_receive(:call).once.with :env
|
39
51
|
|
40
|
-
@
|
52
|
+
@ronin.call :env
|
41
53
|
end
|
42
54
|
end
|
43
55
|
end
|
44
56
|
context "with harakiri set" do
|
45
57
|
before(:each) do
|
46
58
|
Rack::Harakiri.after = 100
|
47
|
-
@
|
59
|
+
@ronin = Rack::Harakiri.new @app
|
48
60
|
end
|
49
61
|
after(:each) do
|
50
62
|
Rack::Harakiri.after = nil
|
51
63
|
end
|
52
64
|
it "should quit after an amount of requests" do
|
53
|
-
@
|
65
|
+
@ronin.instance_variable_get(:@quit_after_requests).should == 100
|
54
66
|
end
|
55
67
|
end
|
56
68
|
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: picky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
+
- 1
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.12.3
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Florian Hanke
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-12-09 00:00:00 +01:00
|
18
18
|
default_executable: picky
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/picky/indexing/indexes.rb
|
104
104
|
- lib/picky/loader.rb
|
105
105
|
- lib/picky/loggers/search.rb
|
106
|
+
- lib/picky/performant.rb
|
106
107
|
- lib/picky/query/allocation.rb
|
107
108
|
- lib/picky/query/allocations.rb
|
108
109
|
- lib/picky/query/base.rb
|
@@ -189,6 +190,7 @@ files:
|
|
189
190
|
- spec/lib/index/file/marshal_spec.rb
|
190
191
|
- spec/lib/index/file/text_spec.rb
|
191
192
|
- spec/lib/index/files_spec.rb
|
193
|
+
- spec/lib/index_api_spec.rb
|
192
194
|
- spec/lib/indexed/bundle_spec.rb
|
193
195
|
- spec/lib/indexed/categories_spec.rb
|
194
196
|
- spec/lib/indexed/category_spec.rb
|
@@ -294,6 +296,7 @@ test_files:
|
|
294
296
|
- spec/lib/index/file/marshal_spec.rb
|
295
297
|
- spec/lib/index/file/text_spec.rb
|
296
298
|
- spec/lib/index/files_spec.rb
|
299
|
+
- spec/lib/index_api_spec.rb
|
297
300
|
- spec/lib/indexed/bundle_spec.rb
|
298
301
|
- spec/lib/indexed/categories_spec.rb
|
299
302
|
- spec/lib/indexed/category_spec.rb
|