algoliasearch-jekyll 0.9.1 → 1.0.0.beta.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -4
  3. data/CONTRIBUTING.md +8 -1
  4. data/Gemfile +4 -5
  5. data/README.md +318 -11
  6. data/Rakefile +7 -12
  7. data/algoliasearch-jekyll.gemspec +66 -62
  8. data/gemfiles/jekyll_v2.gemfile +3 -3
  9. data/gemfiles/jekyll_v3.gemfile +4 -4
  10. data/gemfiles/jekyll_v3_1_3.gemfile +24 -0
  11. data/gemfiles/jekyll_v3_1_6.gemfile +24 -0
  12. data/lib/algoliasearch-jekyll.rb +1 -3
  13. data/lib/credential_checker.rb +2 -1
  14. data/lib/error_handler.rb +6 -0
  15. data/lib/push.rb +81 -19
  16. data/lib/record_extractor.rb +120 -140
  17. data/lib/utils.rb +13 -0
  18. data/lib/version.rb +1 -1
  19. data/scripts/release +13 -12
  20. data/scripts/test_v3 +1 -1
  21. data/scripts/watch +4 -0
  22. data/spec/error_handler_spec.rb +17 -0
  23. data/spec/fixtures/jekyll_version_2/404.html +8 -0
  24. data/spec/fixtures/jekyll_version_2/404.md +9 -0
  25. data/spec/fixtures/jekyll_version_2/_my-collection/collection-item.md +3 -0
  26. data/spec/fixtures/jekyll_version_2/_posts/2015-07-02-test-post.md +1 -1
  27. data/spec/fixtures/jekyll_version_2/about.md +3 -0
  28. data/spec/fixtures/jekyll_version_2/front_matter.md +15 -0
  29. data/spec/fixtures/jekyll_version_2/index.html +3 -1
  30. data/spec/fixtures/jekyll_version_2/only-divs.md +15 -0
  31. data/spec/fixtures/jekyll_version_2/only-paragraphs.md +15 -0
  32. data/spec/fixtures/jekyll_version_3/404.html +8 -0
  33. data/spec/fixtures/jekyll_version_3/404.md +9 -0
  34. data/spec/fixtures/jekyll_version_3/_config.yml +1 -1
  35. data/spec/fixtures/jekyll_version_3/_my-collection/collection-item.md +3 -0
  36. data/spec/fixtures/jekyll_version_3/_posts/2015-07-02-test-post.md +1 -1
  37. data/spec/fixtures/jekyll_version_3/about.md +3 -0
  38. data/spec/fixtures/jekyll_version_3/front_matter.md +15 -0
  39. data/spec/fixtures/jekyll_version_3/index.html +4 -1
  40. data/spec/fixtures/jekyll_version_3/only-divs.md +15 -0
  41. data/spec/fixtures/jekyll_version_3/only-paragraphs.md +15 -0
  42. data/spec/push_spec.rb +211 -8
  43. data/spec/record_extractor_spec.rb +296 -358
  44. data/spec/spec_helper.rb +32 -11
  45. data/txt/record_too_big +19 -0
  46. metadata +40 -51
  47. data/scripts/watch +0 -1
@@ -0,0 +1,13 @@
1
+ # Generic util helpers
2
+ class AlgoliaSearchUtils
3
+ # Check the current Jekyll version
4
+ def self.restrict_jekyll_version(more_than: nil, less_than: nil)
5
+ jekyll_version = Gem::Version.new(Jekyll::VERSION)
6
+ minimum_version = Gem::Version.new(more_than)
7
+ maximum_version = Gem::Version.new(less_than)
8
+
9
+ return false if !more_than.nil? && jekyll_version < minimum_version
10
+ return false if !less_than.nil? && jekyll_version > maximum_version
11
+ true
12
+ end
13
+ end
@@ -1,6 +1,6 @@
1
1
  # Expose gem version
2
2
  class AlgoliaSearchJekyllVersion
3
3
  def self.to_s
4
- '0.9.1'
4
+ '1.0.0.beta-1'
5
5
  end
6
6
  end
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- git checkout master || exit 1
4
- git pull || exit 1
5
- bundle install && appraisal install
6
-
7
- git rebase develop || exit 1
8
- bundle install && appraisal install
9
- rake release || exit 1
10
-
11
- git checkout develop || exit 1
12
- bundle install && appraisal install
13
- git rebase master || exit 1
14
- bundle install && appraisal install
3
+ # Stop if any command fails
4
+ set -e
5
+
6
+ git checkout master
7
+ git pull
8
+
9
+ git rebase develop
10
+ bundle install
11
+ appraisal install
12
+ rake release
13
+
14
+ git checkout develop
15
+ git rebase master
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  cd "$(dirname "$BASH_SOURCE")"/..
3
3
 
4
- echo "Testing under Jekyll 3.0"
4
+ echo "Testing under Jekyll 3"
5
5
  COVERAGE=1 appraisal jekyll-v3 bundle exec rspec
6
6
 
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ cd "$(dirname "$BASH_SOURCE")"/..
3
+
4
+ guard --guardfile ./Guardfile_jekyllv2
@@ -121,6 +121,23 @@ describe(AlgoliaSearchErrorHandler) do
121
121
  expect(actual).to eq('check_key_acl_to_tmp_index')
122
122
  end
123
123
 
124
+ it 'should warn about big records' do
125
+ # Given
126
+ parsed = {
127
+ 'http_error' => 400,
128
+ 'json' => {
129
+ 'message' => 'Record is too big size=220062 bytes'
130
+ }
131
+ }
132
+ allow(@error_handler).to receive(:parse_algolia_error).and_return(parsed)
133
+
134
+ # When
135
+ actual = @error_handler.readable_algolia_error('error')
136
+
137
+ # Then
138
+ expect(actual).to eq('record_too_big')
139
+ end
140
+
124
141
  it 'should return false if no nice message found' do
125
142
  # Given
126
143
  parsed = false
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: 404 Not Found
3
+ ---
4
+
5
+ This is a 404.html error page. [GitHub pages](https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/)
6
+ suggested that it should be a `.md` file, but the Hyde theme uses a `.html`, so we handle it as well.
7
+
8
+ It should not be indexed.
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: 404 Not Found
3
+ ---
4
+
5
+ This is a classical error page, as suggested by
6
+ [GitHub pages](https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/).
7
+
8
+ It should not be indexed.
9
+
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: Collection Item
3
3
  custom: Foo
4
+ tags:
5
+ - tag
6
+ - another tag
4
7
  ---
5
8
 
6
9
  The grandest of omelettes. Those that feast on dragon eggs often find that there
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "Test post"
3
- tags:
3
+ tags:
4
4
  - tag
5
5
  - another tag
6
6
  custom: Foo
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: About page
3
3
  custom: Foo
4
+ tags:
5
+ - tag
6
+ - another tag
4
7
  ---
5
8
 
6
9
  # Heading 1
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Front-matter test
3
+ author: John Doe
4
+ custom: foo
5
+ slug: front_matter_test
6
+ date: foo
7
+ tags: foo
8
+ url: http://www.foo.com/
9
+ type: foo
10
+ ---
11
+
12
+ # Title
13
+
14
+ Paragraph content
15
+
@@ -3,7 +3,9 @@ layout: default
3
3
  title: Home
4
4
  ---
5
5
 
6
- This default index page is used to display the paginated posts
6
+ This default index page is usually used to display the list of posts, or briefly explain the product.
7
+
8
+ I feel that it should not be indexed.
7
9
 
8
10
  {% for post in paginator.posts %}
9
11
  <a href="{{ site.baseurl }}{{ post.url }}">
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Only divs
3
+ ---
4
+
5
+ <div>This is the first paragraph</div>
6
+
7
+ <div>This is the second paragraph</div>
8
+
9
+ <div>This is the third paragraph</div>
10
+
11
+ <div>This is the fourth paragraph</div>
12
+
13
+ <div>This is the fifth paragraph</div>
14
+
15
+ <div>This is the last paragraph</div>
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Only paragraphs
3
+ ---
4
+
5
+ This is the first paragraph
6
+
7
+ This is the second paragraph
8
+
9
+ This is the third paragraph
10
+
11
+ This is the fourth paragraph
12
+
13
+ This is the fifth paragraph
14
+
15
+ This is the last paragraph
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: 404 Not Found
3
+ ---
4
+
5
+ This is a 404.html error page. [GitHub pages](https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/)
6
+ suggested that it should be a `.md` file, but the Hyde theme uses a `.html`, so we handle it as well.
7
+
8
+ It should not be indexed.
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: 404 Not Found
3
+ ---
4
+
5
+ This is a classical error page, as suggested by
6
+ [GitHub pages](https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/).
7
+
8
+ It should not be indexed.
9
+
@@ -12,6 +12,6 @@ algolia:
12
12
  - excluded.html
13
13
 
14
14
  # Jekyll 3.0 extracted the secondary features into their own plugins
15
- plugins:
15
+ gems:
16
16
  - jekyll-paginate
17
17
 
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: Collection Item
3
3
  custom: Foo
4
+ tags:
5
+ - tag
6
+ - another tag
4
7
  ---
5
8
 
6
9
  The grandest of omelettes. Those that feast on dragon eggs often find that there
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "Test post"
3
- tags:
3
+ tags:
4
4
  - tag
5
5
  - another tag
6
6
  custom: Foo
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: About page
3
3
  custom: Foo
4
+ tags:
5
+ - tag
6
+ - another tag
4
7
  ---
5
8
 
6
9
  # Heading 1
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Front-matter test
3
+ author: John Doe
4
+ custom: foo
5
+ slug: front_matter_test
6
+ date: foo
7
+ tags: foo
8
+ url: http://www.foo.com/
9
+ type: foo
10
+ ---
11
+
12
+ # Title
13
+
14
+ Paragraph content
15
+
@@ -3,7 +3,9 @@ layout: default
3
3
  title: Home
4
4
  ---
5
5
 
6
- This default index page is used to display the paginated posts
6
+ This default index page is usually used to display the list of posts, or briefly explain the product.
7
+
8
+ I feel that it should not be indexed.
7
9
 
8
10
  {% for post in paginator.posts %}
9
11
  <a href="{{ site.baseurl }}{{ post.url }}">
@@ -11,3 +13,4 @@ This default index page is used to display the paginated posts
11
13
  </a>
12
14
  {{ post.content }}
13
15
  {% endfor %}
16
+
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Only divs
3
+ ---
4
+
5
+ <div>This is the first paragraph</div>
6
+
7
+ <div>This is the second paragraph</div>
8
+
9
+ <div>This is the third paragraph</div>
10
+
11
+ <div>This is the fourth paragraph</div>
12
+
13
+ <div>This is the fifth paragraph</div>
14
+
15
+ <div>This is the last paragraph</div>
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Only paragraphs
3
+ ---
4
+
5
+ This is the first paragraph
6
+
7
+ This is the second paragraph
8
+
9
+ This is the third paragraph
10
+
11
+ This is the fourth paragraph
12
+
13
+ This is the fifth paragraph
14
+
15
+ This is the last paragraph
@@ -11,6 +11,9 @@ describe(AlgoliaSearchJekyllPush) do
11
11
  let(:document_file) { site.file_by_name('collection-item.md') }
12
12
  let(:html_document_file) { site.file_by_name('collection-item.html') }
13
13
  let(:pagination_page) { site.file_by_name('page2/index.html') }
14
+ let(:err_404) { site.file_by_name('404.md') }
15
+ let(:err_404_html) { site.file_by_name('404.html') }
16
+ let(:homepage) { site.file_by_name('index.html') }
14
17
  let(:items) do
15
18
  [{
16
19
  name: 'foo',
@@ -21,6 +24,12 @@ describe(AlgoliaSearchJekyllPush) do
21
24
  }]
22
25
  end
23
26
 
27
+ before(:each) do
28
+ allow(Jekyll.logger).to receive(:info)
29
+ allow(Jekyll.logger).to receive(:warn)
30
+ allow(Jekyll.logger).to receive(:error)
31
+ end
32
+
24
33
  describe 'init_options' do
25
34
  it 'sets options and config' do
26
35
  # Given
@@ -37,6 +46,35 @@ describe(AlgoliaSearchJekyllPush) do
37
46
  end
38
47
  end
39
48
 
49
+ describe 'lazy_update?' do
50
+ it 'should return false by default' do
51
+ # Given
52
+ push.init_options(nil, {}, {})
53
+
54
+ # When
55
+ actual = push.lazy_update?
56
+
57
+ # Then
58
+ expect(actual).to eq false
59
+ end
60
+
61
+ it 'should return true if such an option is set in the config' do
62
+ # Given
63
+ config = {
64
+ 'algolia' => {
65
+ 'lazy_update' => true
66
+ }
67
+ }
68
+ push.init_options(nil, {}, config)
69
+
70
+ # When
71
+ actual = push.lazy_update?
72
+
73
+ # Then
74
+ expect(actual).to eq true
75
+ end
76
+ end
77
+
40
78
  describe 'indexable?' do
41
79
  it 'exclude StaticFiles' do
42
80
  expect(push.indexable?(static_file)).to eq false
@@ -65,6 +103,18 @@ describe(AlgoliaSearchJekyllPush) do
65
103
  it 'does not index pagination pages' do
66
104
  expect(push.indexable?(pagination_page)).to eq false
67
105
  end
106
+
107
+ it 'does not index 404 pages (in markdown)' do
108
+ expect(push.indexable?(err_404)).to eq false
109
+ end
110
+
111
+ it 'does not index 404 pages (in html)' do
112
+ expect(push.indexable?(err_404_html)).to eq false
113
+ end
114
+
115
+ it 'does not index homepage' do
116
+ expect(push.indexable?(homepage)).to eq false
117
+ end
68
118
  end
69
119
 
70
120
  describe 'excluded_files?' do
@@ -147,7 +197,6 @@ describe(AlgoliaSearchJekyllPush) do
147
197
  @error_handler_double = double('Error Handler double').as_null_object
148
198
  push.init_options(nil, {}, {})
149
199
  allow(@index_double).to receive(:set_settings).and_raise
150
- allow(Jekyll.logger).to receive(:error)
151
200
  end
152
201
 
153
202
  it 'stops if API throw an error' do
@@ -269,6 +318,53 @@ describe(AlgoliaSearchJekyllPush) do
269
318
  end
270
319
 
271
320
  describe 'push' do
321
+ before(:each) do
322
+ allow_any_instance_of(AlgoliaSearchCredentialChecker)
323
+ .to receive(:assert_valid)
324
+ end
325
+
326
+ it 'should do a lazy update if such is configured' do
327
+ # Given
328
+ allow(push).to receive(:lazy_update?).and_return(true)
329
+ allow(push).to receive(:lazy_update)
330
+ push.init_options(nil, {}, {})
331
+ items = ['foo']
332
+
333
+ # When
334
+ push.push(items)
335
+
336
+ # Then
337
+ expect(push).to have_received(:lazy_update).with(items)
338
+ end
339
+
340
+ it 'should do a greedy update if such is configured' do
341
+ # Given
342
+ allow(push).to receive(:greedy_update?).and_return(true)
343
+ allow(push).to receive(:greedy_update)
344
+ push.init_options(nil, {}, {})
345
+ items = ['foo']
346
+
347
+ # When
348
+ push.push(items)
349
+
350
+ # Then
351
+ expect(push).to have_received(:greedy_update).with(items)
352
+ end
353
+ end
354
+
355
+ describe 'batch_add_items' do
356
+ it 'should display an error if `add_objects!` failed' do
357
+ # Given
358
+ index = double('Algolia Index').as_null_object
359
+ allow(index).to receive(:add_objects!).and_raise
360
+
361
+ # When / Then
362
+ expect(-> { push.batch_add_items(items, index) })
363
+ .to raise_error SystemExit
364
+ end
365
+ end
366
+
367
+ describe 'greedy_update' do
272
368
  let(:index_double) { double('Algolia Index').as_null_object }
273
369
  let(:config) do
274
370
  {
@@ -280,21 +376,19 @@ describe(AlgoliaSearchJekyllPush) do
280
376
 
281
377
  before(:each) do
282
378
  push.init_options(nil, {}, config)
283
- # Mock all calls to not send anything
284
379
  allow_any_instance_of(AlgoliaSearchCredentialChecker)
285
380
  .to receive(:assert_valid)
286
381
  allow(Algolia).to receive(:set_extra_header)
287
382
  allow(Algolia).to receive(:init)
288
383
  allow(Algolia).to receive(:move_index)
289
384
  allow(Algolia::Index).to receive(:new).and_return(index_double)
290
- allow(Jekyll.logger).to receive(:info)
291
385
  end
292
386
 
293
387
  it 'should create a temporary index' do
294
388
  # Given
295
389
 
296
390
  # When
297
- push.push(items)
391
+ push.greedy_update(items)
298
392
 
299
393
  # Then
300
394
  expect(Algolia::Index).to have_received(:new).with('INDEXNAME_tmp')
@@ -330,12 +424,121 @@ describe(AlgoliaSearchJekyllPush) do
330
424
  # Then
331
425
  expect(Jekyll.logger).to have_received(:info).with(/of 2 items/i)
332
426
  end
427
+ end
428
+
429
+ describe 'lazy_update' do
430
+ let(:items) do
431
+ [
432
+ { objectID: 'foo' },
433
+ { objectID: 'baz' }
434
+ ]
435
+ end
436
+ let(:remote) { %w(foo bar) }
437
+ let(:local) { %w(foo baz) }
438
+ let(:index) { double.as_null_object }
333
439
 
334
- it 'should display an error if `add_objects!` failed' do
335
- allow(index_double).to receive(:add_objects!).and_raise
440
+ describe 'remote_ids' do
441
+ it 'should call browse on the index with the attributesToRetrieve ' do
442
+ # Given
443
+ index = double.as_null_object
444
+
445
+ # Then
446
+ push.remote_ids(index)
447
+
448
+ # Then
449
+ expect(index).to have_received(:browse)
450
+ end
451
+
452
+ it 'should return an array of all objectID returned by browse' do
453
+ # Given
454
+ index = double.as_null_object
455
+ hit1 = { 'objectID' => 'foo' }
456
+ hit2 = { 'objectID' => 'bar' }
457
+ allow(index).to receive(:browse).and_yield(hit1).and_yield(hit2)
458
+
459
+ # Then
460
+ actual = push.remote_ids(index)
461
+
462
+ # Then
463
+ expect(actual).to eq %w(foo bar)
464
+ end
465
+ end
466
+
467
+ describe 'delete_remote_not_in_local' do
468
+ it 'calls delete_objects! with the array of items to delete' do
469
+ # Given
470
+
471
+ # When
472
+ push.delete_remote_not_in_local(index, local, remote)
473
+
474
+ # Then
475
+ expect(index).to have_received(:delete_objects!).with(['bar'])
476
+ end
477
+
478
+ it 'displays the number of items deleted' do
479
+ # Given
480
+
481
+ # When
482
+ push.delete_remote_not_in_local(index, local, remote)
483
+
484
+ # Then
485
+ expect(Jekyll.logger).to have_received(:info).with('Deleting 1 items')
486
+ end
336
487
 
337
- expect(Jekyll.logger).to receive(:error)
338
- expect(-> { push.push(items) }).to raise_error SystemExit
488
+ it 'should do not do an API call if there is nothing to delete' do
489
+ # Given
490
+ input = %w(foo bar)
491
+
492
+ # When
493
+ push.delete_remote_not_in_local(index, input, input)
494
+
495
+ # Then
496
+ expect(index).not_to have_received(:delete_objects!)
497
+ end
498
+ end
499
+
500
+ describe 'add_local_not_in_remote' do
501
+ it 'should push all local items not yet in remote' do
502
+ # Given
503
+ allow(push).to receive(:batch_add_items)
504
+
505
+ # When
506
+ push.add_local_not_in_remote(index, items, local, remote)
507
+
508
+ # Then
509
+ expected = [{ objectID: 'baz' }]
510
+ expect(push).to have_received(:batch_add_items).with(expected, index)
511
+ end
512
+
513
+ it 'should warn about pushing 0 records' do
514
+ # Given
515
+ input = %w(foo bar)
516
+
517
+ # When
518
+ push.add_local_not_in_remote(index, items, input, input)
519
+
520
+ # Then
521
+ expect(Jekyll.logger)
522
+ .to have_received(:info).with('Adding 0 items')
523
+ end
524
+ end
525
+
526
+ it 'should delete items from remote and push new ones' do
527
+ # Given
528
+ allow(push).to receive(:create_index).and_return(index)
529
+ allow(push).to receive(:remote_ids).and_return(remote)
530
+ allow(push).to receive(:delete_remote_not_in_local)
531
+ allow(push).to receive(:add_local_not_in_remote)
532
+ push.init_options(nil, {}, {})
533
+
534
+ # When
535
+ push.lazy_update(items)
536
+
537
+ # Then
538
+ expect(push).to have_received(:delete_remote_not_in_local)
539
+ .with(index, local, remote)
540
+ expect(push).to have_received(:add_local_not_in_remote)
541
+ .with(index, items, local, remote)
339
542
  end
340
543
  end
341
544
  end