fias 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -22
  3. data/.rubocop.yml +7 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +1 -1
  6. data/LICENSE.txt +2 -2
  7. data/README.md +259 -155
  8. data/Rakefile +6 -1
  9. data/config/names.txt +0 -0
  10. data/config/synonyms.yml +50 -0
  11. data/examples/create.rb +106 -0
  12. data/examples/generate_index.rb +63 -0
  13. data/fias.gemspec +33 -21
  14. data/lib/fias.rb +197 -10
  15. data/lib/fias/config.rb +74 -0
  16. data/lib/fias/import/copy.rb +62 -0
  17. data/lib/fias/import/dbf.rb +81 -0
  18. data/lib/fias/import/download_service.rb +37 -0
  19. data/lib/fias/import/restore_parent_id.rb +51 -0
  20. data/lib/fias/import/tables.rb +74 -0
  21. data/lib/fias/name/append.rb +30 -0
  22. data/lib/fias/name/canonical.rb +42 -0
  23. data/lib/fias/name/extract.rb +85 -0
  24. data/lib/fias/name/house_number.rb +71 -0
  25. data/lib/fias/name/split.rb +60 -0
  26. data/lib/fias/name/synonyms.rb +93 -0
  27. data/lib/fias/query.rb +43 -0
  28. data/lib/fias/query/estimate.rb +67 -0
  29. data/lib/fias/query/finder.rb +75 -0
  30. data/lib/fias/query/params.rb +101 -0
  31. data/lib/fias/railtie.rb +3 -17
  32. data/lib/fias/version.rb +1 -1
  33. data/spec/fixtures/ACTSTAT.DBF +0 -0
  34. data/spec/fixtures/NORDOC99.DBF +0 -0
  35. data/spec/fixtures/STRSTAT.DBF +0 -0
  36. data/spec/fixtures/addressing.yml +93 -0
  37. data/spec/fixtures/query.yml +79 -0
  38. data/spec/fixtures/query_sanitization.yml +75 -0
  39. data/spec/fixtures/status_append.yml +60 -0
  40. data/spec/lib/import/copy_spec.rb +44 -0
  41. data/spec/lib/import/dbf_spec.rb +28 -0
  42. data/spec/lib/import/download_service_spec.rb +15 -0
  43. data/spec/lib/import/restore_parent_id_spec.rb +34 -0
  44. data/spec/lib/import/tables_spec.rb +26 -0
  45. data/spec/lib/name/append_spec.rb +14 -0
  46. data/spec/lib/name/canonical_spec.rb +20 -0
  47. data/spec/lib/name/extract_spec.rb +67 -0
  48. data/spec/lib/name/house_number_spec.rb +45 -0
  49. data/spec/lib/name/query_spec.rb +21 -0
  50. data/spec/lib/name/split_spec.rb +15 -0
  51. data/spec/lib/name/synonyms_spec.rb +51 -0
  52. data/spec/lib/query/params_spec.rb +15 -0
  53. data/spec/lib/query_spec.rb +27 -0
  54. data/spec/spec_helper.rb +30 -0
  55. data/spec/support/db.rb +30 -0
  56. data/spec/support/query.rb +13 -0
  57. data/tasks/db.rake +52 -0
  58. data/tasks/download.rake +15 -0
  59. metadata +246 -64
  60. data/lib/fias/active_record/address_object.rb +0 -231
  61. data/lib/fias/active_record/address_object_type.rb +0 -15
  62. data/lib/fias/dbf_wrapper.rb +0 -90
  63. data/lib/fias/importer.rb +0 -30
  64. data/lib/fias/importer/base.rb +0 -59
  65. data/lib/fias/importer/pg.rb +0 -81
  66. data/lib/fias/importer/sqlite.rb +0 -38
  67. data/lib/generators/fias/migration.rb +0 -34
  68. data/lib/generators/fias/templates/create_fias_tables.rb +0 -5
  69. data/tasks/fias.rake +0 -68
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Import::Copy do
4
+ let(:name) { 'actual_statuses' }
5
+ let(:files) { Fias::Import::Dbf.new('spec/fixtures').only(name) }
6
+ let(:table_name) { "fias_#{name}".to_sym }
7
+ let(:db) { double('db') }
8
+ let(:raw_connection) { double('raw_connection') }
9
+
10
+ subject { Fias::Import::Tables.new(db, files).copy.first }
11
+
12
+ before do
13
+ stub_const('Fias::Import::Tables::UUID', name.to_sym => %w(name))
14
+ end
15
+
16
+ context '#encode' do
17
+ it do
18
+ expect(PgDataEncoder::EncodeForCopy).to receive(:new).with(
19
+ column_types: { 1 => :uuid }
20
+ ).and_call_original
21
+
22
+ subject.encode
23
+ end
24
+ end
25
+
26
+ context '#copy' do
27
+ let(:result) { double('result') }
28
+
29
+ before do
30
+ table_obj = double(table_name)
31
+ expect(db).to receive(:[]).with(table_name).and_return(table_obj)
32
+ expect(table_obj).to receive(:truncate)
33
+ expect(db).to receive(:run).with(/SET client/)
34
+ expect(db).to receive(:copy_into).with(
35
+ :fias_actual_statuses, columns: [:actstatid, :name], format: :binary
36
+ ).and_yield.and_yield
37
+ end
38
+
39
+ it do
40
+ subject.encode
41
+ subject.copy
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Import::Dbf do
4
+ subject { described_class.new('spec/fixtures') }
5
+
6
+ context '#initialize' do
7
+ it 'fails without files' do
8
+ expect { described_class.new('foo') }.to raise_error
9
+ end
10
+
11
+ it 'returns correct file list' do
12
+ expect(subject.files.keys).to eq(
13
+ [:actual_statuses, :structure_statuses, :nordoc99]
14
+ )
15
+ expect(subject.files.values).to all(be_present)
16
+ expect(subject.files.values).to all(be_kind_of(DBF::Table))
17
+ end
18
+ end
19
+
20
+ context '#only' do
21
+ it 'returns only houses' do
22
+ expect(subject.only(:nordocs).keys).to eq([:nordoc99])
23
+ expect(subject.only(:address_objects, :structure_statuses).keys).to eq(
24
+ [:structure_statuses]
25
+ )
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Import::DownloadService do
4
+ it 'parses url' do
5
+ stub_request(
6
+ :post,
7
+ 'http://fias.nalog.ru/WebServices/Public/DownloadService.asmx'
8
+ ).with(described_class::OPTIONS).to_return(
9
+ status: 200,
10
+ body: '<FiasCompleteDbfUrl>http://www.ya.ru</FiasCompleteDbfUrl>'
11
+ )
12
+
13
+ expect(described_class.url).to eq('http://www.ya.ru')
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Import::RestoreParentId do
4
+ let(:db) { Sequel.sqlite }
5
+ let(:table) { db[:address_objects] }
6
+ let(:records) { table.select_map([:id, :parent_id]).index_by(&:first) }
7
+
8
+ subject { described_class.new(table) }
9
+
10
+ before do
11
+ db.create_table! :address_objects do
12
+ primary_key :id
13
+ column :aoguid, :string
14
+ column :parentguid, :string
15
+ column :parent_id, :integer
16
+ end
17
+
18
+ table.insert(id: 1, aoguid: 'g1')
19
+ table.insert(id: 2, aoguid: 'g2')
20
+ table.insert(id: 3, aoguid: 'g3', parentguid: 'g1')
21
+ table.insert(id: 4, aoguid: 'g4', parentguid: 'g1')
22
+ table.insert(id: 5, aoguid: 'g5', parentguid: 'g2')
23
+ end
24
+
25
+ it do
26
+ subject.restore
27
+
28
+ expect(records[1].last).to be_nil
29
+ expect(records[2].last).to be_nil
30
+ expect(records[3].last).to eq(1)
31
+ expect(records[4].last).to eq(1)
32
+ expect(records[5].last).to eq(2)
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Import::Tables do
4
+ let(:files) { Fias::Import::Dbf.new('spec/fixtures').files }
5
+ let(:db) { double('db') }
6
+
7
+ subject { described_class.new(db, files, '_fias') }
8
+
9
+ it '#create' do
10
+ stub_const('Fias::Import::Tables::UUID', actual_statuses: %w(name))
11
+
12
+ expect(db).to receive(:create_table).with(:_fias_actual_statuses).and_yield
13
+ expect(db).to receive(:create_table).with(:_fias_nordoc99)
14
+ expect(db).to receive(:create_table).with(:_fias_structure_statuses)
15
+
16
+ expect(subject).to receive(:primary_key).with(:id)
17
+ expect(subject).to receive(:column).with(:name, :uuid)
18
+ expect(subject).to receive(:column).with(:actstatid, :float)
19
+
20
+ expect { subject.create }.to_not raise_error
21
+ end
22
+
23
+ it '#copy' do
24
+ expect(subject.copy.size).to eq(3)
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::Append do
4
+ context '#append' do
5
+ YAML.load_file('spec/fixtures/status_append.yml').each do |item|
6
+ name, toponym = *item.first
7
+ short, long = *item.slice(1..-1)
8
+
9
+ it "#{toponym} #{name} must generate #{short} and #{long}" do
10
+ expect(described_class.append(name, toponym)).to eq([short, long])
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::Canonical do
4
+ context '#canonical' do
5
+ {
6
+ 'ул' => %w(улица ул ул.),
7
+ 'Улица' => %w(улица ул ул.),
8
+ 'микр' => %w(микрорайон мкр мкр. мкрн микр),
9
+ 'микрорайон' => %w(микрорайон мкр мкр. мкрн микр),
10
+ 'АО' => ['автономный округ', 'АО', 'АО'],
11
+ 'Аобл' => ['автономная область', 'Аобл', 'Аобл'],
12
+ 'республика' => %w(Республика Респ Респ.),
13
+ 'Чувашия' => ['Чувашская Республика - Чувашия', 'Чувашия']
14
+ }.each do |to, normalized|
15
+ it "#{to} must become #{normalized.inspect}" do
16
+ expect(described_class.canonical(to)).to eq(normalized)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::Extract do
4
+ {
5
+ 'г Краснодар' => %w(Краснодар город г г.),
6
+ 'г. Краснодар' => %w(Краснодар город г г.),
7
+ 'Краснодар город' => %w(Краснодар город г г.),
8
+ 'Раменский район' => ['Раменский', 'район', 'р-н', 'р-н'],
9
+ 'Ямало-Ненецкий АО' => ['Ямало-Ненецкий', 'автономный округ', 'АО', 'АО'],
10
+ 'Еврейский автономный округ' => [
11
+ 'Еврейский', 'автономный округ', 'АО', 'АО'
12
+ ],
13
+ 'Корягинский район' => ['Корягинский', 'район', 'р-н', 'р-н'],
14
+ 'гопотека Южное Бутово' => ['гопотека Южное Бутово'],
15
+ 'ул. Длинная' => %w(Длинная улица ул ул.),
16
+ 'ул. им. Злых Марсиан-3' => ['им. Злых Марсиан-3', 'улица', 'ул', 'ул.'],
17
+ 'Тверь г' => ['Тверь', 'город', 'г', 'г.'],
18
+ 'Тверь г.' => ['Тверь', 'город', 'г', 'г.'],
19
+ 'Гаврилова' => ['Гаврилова'],
20
+ 'пр Космонавтов' => ['Космонавтов', 'проспект', 'пр-кт', 'пр-кт'],
21
+ 'Искровский проспект' => ['Искровский', 'проспект', 'пр-кт', 'пр-кт'],
22
+ 'коттеджный поселок Морозов' => [
23
+ 'Морозов', 'коттеджный поселок', 'кп', 'кп'
24
+ ],
25
+ 'поселок городского типа Свердловский' => [
26
+ 'Свердловский', 'поселок городского типа', 'пгт', 'пгт'
27
+ ],
28
+ 'поселок Павловская Слобода' => [
29
+ 'Павловская Слобода', 'поселок', 'п', 'п.'
30
+ ],
31
+ 'Павловская Слобода поселок' => [
32
+ 'Павловская Слобода', 'поселок', 'п', 'п.'
33
+ ],
34
+ 'Иваново рабочий поселок' => ['Иваново', 'рабочий поселок', 'рп', 'рп'],
35
+ 'ул Славянский бульвар' => ['Славянский бульвар', 'улица', 'ул', 'ул.'],
36
+ 'ул Набережная' => ['Набережная', 'улица', 'ул', 'ул.'],
37
+ 'Сокольнический Вал ул.' => ['Сокольнический Вал', 'улица', 'ул', 'ул.'],
38
+ 'Сокольнический Вал улица' => ['Сокольнический Вал', 'улица', 'ул', 'ул.'],
39
+ 'ул Сокольнический Вал' => ['Сокольнический Вал', 'улица', 'ул', 'ул.'],
40
+ 'улица Сокольнический Вал' => ['Сокольнический Вал', 'улица', 'ул', 'ул.'],
41
+ 'М.Бронная ул.' => ['М.Бронная', 'улица', 'ул', 'ул.'],
42
+ '1-я улица Машиностроения' => ['1-я Машиностроения', 'улица', 'ул', 'ул.'],
43
+ 'Аллея Ильича ул' => ['Аллея Ильича', 'улица', 'ул', 'ул.'],
44
+ 'ул.Сычева' => %w(Сычева улица ул ул.),
45
+ 'Мира улица.' => %w(Мира улица ул ул.),
46
+ 'Малаховка городского типа поселок' => [
47
+ 'Малаховка', 'поселок городского типа', 'пгт', 'пгт'
48
+ ],
49
+ 'Калининград город' => ['Калининград', 'город', 'г', 'г.'],
50
+ 'коттеджный поселок Морозов' => [
51
+ 'Морозов', 'коттеджный поселок', 'кп', 'кп'
52
+ ],
53
+ 'коттеджный Морозов' => ['Морозов', 'коттеджный поселок', 'кп', 'кп'],
54
+ 'Морозов коттеджный' => ['Морозов', 'коттеджный поселок', 'кп', 'кп'],
55
+ 'Морозов коттеджный поселок' => [
56
+ 'Морозов', 'коттеджный поселок', 'кп', 'кп'
57
+ ],
58
+ '' => nil,
59
+ nil => nil
60
+ }.each do |name, expected|
61
+ it "must extract status from #{name} correctly" do
62
+ given = described_class.extract(name)
63
+ given = given.first(4) if given.is_a?(Array)
64
+ expect(given).to eq(expected)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::HouseNumber do
4
+ {
5
+ '14' => ['14', nil],
6
+ 'мкр. 14' => ['мкр. 14', nil],
7
+ 'лин. 5' => ['лин. 5', nil],
8
+ 'линия 14' => ['линия 14', nil],
9
+ '2 мкр' => ['2 мкр', nil],
10
+ '2-я советская' => ['2-я советская', nil],
11
+ '50 лет октября' => ['50 лет октября', nil],
12
+ 'советская 2-я' => ['советская 2-я', nil],
13
+ 'Советской Армии ул,238а' => ['Советской Армии ул', '238а'],
14
+ 'Советской Армии ул,181,корп.в' => ['Советской Армии ул', '181,корп.в'],
15
+ 'пр.Энергетиков, 72/2' => ['пр.Энергетиков', '72/2'],
16
+ 'Засечное с, 34' => ['Засечное с', '34'],
17
+ 'Ново-Садовая ул,303а' => ['Ново-Садовая ул', '303а'],
18
+ 'ЖК Бутово Парк-2, корп. 11' => ['ЖК Бутово Парк-2', 'корп. 11'],
19
+ 'советская 2-я' => ['советская 2-я', nil],
20
+ 'Вербицкого 25' => %w(Вербицкого 25),
21
+ 'Новоколомяжский 11к1' => %w(Новоколомяжский 11к1),
22
+ 'пр.Энергетиков 72/2' => ['пр.Энергетиков', '72/2'],
23
+ '2-я линия В.О. 12' => ['2-я линия В.О.', '12'],
24
+ 'Шуваловский пр.88к1' => ['Шуваловский пр.', '88к1'],
25
+ 'Мкр. Северное Чертан, вл.1А' => ['Мкр. Северное Чертан', 'вл.1А'],
26
+ 'Московское ш,43 а,корп.стр.' => ['Московское ш', '43 а,корп.стр.'],
27
+ 'Заводская ул 35-А' => ['Заводская ул 35-А', nil],
28
+ '50 лет октября' => ['50 лет октября', nil],
29
+ 'ул 1905 года 28' => ['ул 1905 года', '28'],
30
+ 'Новослободская 29 корп 3' => ['Новослободская', '29 корп 3'],
31
+ 'Александрова д 29' => %w(Александрова 29),
32
+ 'Николаева дом 29' => %w(Николаева 29),
33
+ 'Невский д. 29' => %w(Невский 29),
34
+ 'ул 1905 года' => ['ул 1905 года', nil],
35
+ 'ул 1905 года 28' => ['ул 1905 года', '28'],
36
+ 'Малая Морская ом. 29' => ['Малая Морская', '29'],
37
+ '26 линия В.О. д.15 к.A' => ['26 линия В.О.', '15 к.A'],
38
+ 'Дунайский пр.' => ['Дунайский пр.', nil],
39
+ 'Пионерстроя ул. 14 к 1' => ['Пионерстроя ул.', '14 к 1']
40
+ }.each do |street, extracted|
41
+ it "Must extract house_number from #{street}" do
42
+ expect(described_class.extract(street)).to eq(extracted)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Query::Params do
4
+ YAML.load_file('spec/fixtures/query_sanitization.yml').each do |pair|
5
+ from, to = pair
6
+ from.symbolize_keys!
7
+ to.symbolize_keys!
8
+
9
+ it from.values.to_s do
10
+ expect(described_class.new(from).sanitized).to eq(to)
11
+ end
12
+ end
13
+
14
+ it 'must not resanitize already sanitized parts' do
15
+ sanitized = described_class.new(
16
+ city: 'г Краснодар', street: 'Ушинского'
17
+ ).sanitized
18
+
19
+ expect(described_class.new(sanitized).sanitized).to eq(sanitized)
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::Split do
4
+ {
5
+ '"Новый" Оккервиль' => %w(новый оккервиль),
6
+ 'Старый Дом (ограниченное снт)' => %w(старый дом ограниченное снт),
7
+ 'ю.р.г.Эрвье' => %w(ю.р.г. эрвье),
8
+ 'А.Невского' => %w(а. невского),
9
+ 'им.Максима Горького' => %w(им. максима горького),
10
+ '50-летия Октября' => ['50-летия', 'октября'],
11
+ 'героя советского союза Жукова' => ['героя советского союза', 'жукова']
12
+ }.each do |name, tokens|
13
+ it(name) { expect(described_class.split(name)).to eq(tokens) }
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Name::Synonyms do
4
+ context '#expand' do
5
+ {
6
+ '2-я Советская улица' => [
7
+ %w(
8
+ 2й 2-й 2я 2-я 2е 2-е 2ая 2-ая 2ий 2-ий 2ый 2-ый
9
+ 2ой 2-ой 2ые 2-ые 2ое 2-ое 2го 2-го 2
10
+ ),
11
+ %w(советская),
12
+ %w(улица)],
13
+ '2-ой проспект им. 50 лет Николая Лавочкина героя соцтруда улица' => [
14
+ %w(
15
+ 2й 2-й 2я 2-я 2е 2-е 2ая 2-ая 2ий 2-ий 2ый 2-ый
16
+ 2ой 2-ой 2ые 2-ые 2ое 2-ое 2го 2-го 2
17
+ ),
18
+ ['проспект'],
19
+ ['им', 'имени', 'им.', ''],
20
+ ['50-летия', '50-лет', '50 летия', '50 лет', '50-летие', '50 летие'],
21
+ ['николая', ''],
22
+ ['лавочкина'],
23
+ ['героя', ''],
24
+ ['соцтруда', 'соц.труда', ''],
25
+ ['улица']
26
+ ],
27
+ 'ул. И.А.Покрышкина' => [
28
+ ['ул.'], ['и.а.', ''], ['покрышкина']
29
+ ]
30
+ }.each do |name, synonyms|
31
+ it(name) do
32
+ expect(described_class.expand(name)).to eq(synonyms)
33
+ end
34
+ end
35
+ end
36
+
37
+ it '#forms' do
38
+ expect(described_class.forms('им. И.П.Павлова')).to eq(
39
+ [
40
+ 'и.п. им павлова',
41
+ 'им павлова',
42
+ 'и.п. имени павлова',
43
+ 'имени павлова',
44
+ 'и.п. им. павлова',
45
+ 'им. павлова',
46
+ 'и.п. павлова',
47
+ 'павлова'
48
+ ]
49
+ )
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Query::Params do
4
+ context '#initialize' do
5
+ YAML.load_file('spec/fixtures/query_sanitization.yml').each do |pair|
6
+ from, to = pair
7
+ from.symbolize_keys!
8
+ to.symbolize_keys!
9
+
10
+ it from.values.to_s do
11
+ expect(described_class.new(from).sanitized).to eq(to)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fias::Query do
4
+ context '#perform' do
5
+ context 'by default' do
6
+ subject { UndefinedQuery.new(city: 'Санкт-Петербург') }
7
+
8
+ it 'raises error' do
9
+ expect { subject.perform }.to raise_error(NotImplementedError)
10
+ end
11
+ end
12
+
13
+ context 'with provided #find method' do
14
+ YAML.load_file('spec/fixtures/query.yml').each do |query, indexes|
15
+ query = query.symbolize_keys
16
+ it "looks up #{query.inspect}" do
17
+ result = TestQuery.new(query).perform
18
+ result = result.map(&:last).sort_by { |r| r[:id] }
19
+
20
+ expected = SHORTCUTS.values_at(*indexes).sort_by { |r| r[:id] }
21
+
22
+ expect(result.first(expected.size)).to eq(expected)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ $LOAD_PATH << '.' unless $LOAD_PATH.include?('.')
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'simplecov'
6
+ require 'webmock/rspec'
7
+ require 'sequel'
8
+
9
+ SimpleCov.start do
10
+ add_filter 'spec'
11
+ end
12
+
13
+ if ENV['CODECLIMATE_REPO_TOKEN']
14
+ require 'codeclimate-test-reporter'
15
+ CodeClimate::TestReporter.start
16
+ end
17
+
18
+ require 'fias'
19
+ require 'spec/support/query'
20
+ require 'spec/support/db'
21
+
22
+ WebMock.disable_net_connect!(allow: %w(codeclimate.com))
23
+
24
+ RSpec.configure do |config|
25
+ config.order = :random
26
+ config.run_all_when_everything_filtered = true
27
+ config.filter_run :focus
28
+ end
29
+
30
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')