chewy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +12 -0
  7. data/Guardfile +24 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +208 -0
  10. data/Rakefile +6 -0
  11. data/chewy.gemspec +32 -0
  12. data/lib/chewy.rb +55 -0
  13. data/lib/chewy/config.rb +48 -0
  14. data/lib/chewy/fields/base.rb +49 -0
  15. data/lib/chewy/fields/default.rb +10 -0
  16. data/lib/chewy/fields/root.rb +10 -0
  17. data/lib/chewy/index.rb +71 -0
  18. data/lib/chewy/index/actions.rb +43 -0
  19. data/lib/chewy/index/client.rb +13 -0
  20. data/lib/chewy/index/search.rb +26 -0
  21. data/lib/chewy/query.rb +141 -0
  22. data/lib/chewy/query/criteria.rb +81 -0
  23. data/lib/chewy/query/loading.rb +27 -0
  24. data/lib/chewy/query/pagination.rb +39 -0
  25. data/lib/chewy/rspec.rb +1 -0
  26. data/lib/chewy/rspec/update_index.rb +121 -0
  27. data/lib/chewy/type.rb +22 -0
  28. data/lib/chewy/type/adapter/active_record.rb +27 -0
  29. data/lib/chewy/type/adapter/object.rb +22 -0
  30. data/lib/chewy/type/base.rb +41 -0
  31. data/lib/chewy/type/import.rb +67 -0
  32. data/lib/chewy/type/mapping.rb +50 -0
  33. data/lib/chewy/type/observe.rb +37 -0
  34. data/lib/chewy/type/wrapper.rb +35 -0
  35. data/lib/chewy/version.rb +3 -0
  36. data/spec/chewy/config_spec.rb +50 -0
  37. data/spec/chewy/fields/base_spec.rb +70 -0
  38. data/spec/chewy/fields/default_spec.rb +6 -0
  39. data/spec/chewy/fields/root_spec.rb +6 -0
  40. data/spec/chewy/index/actions_spec.rb +53 -0
  41. data/spec/chewy/index/client_spec.rb +18 -0
  42. data/spec/chewy/index/search_spec.rb +54 -0
  43. data/spec/chewy/index_spec.rb +65 -0
  44. data/spec/chewy/query/criteria_spec.rb +73 -0
  45. data/spec/chewy/query/loading_spec.rb +37 -0
  46. data/spec/chewy/query/pagination_spec.rb +40 -0
  47. data/spec/chewy/query_spec.rb +110 -0
  48. data/spec/chewy/rspec/update_index_spec.rb +149 -0
  49. data/spec/chewy/type/import_spec.rb +68 -0
  50. data/spec/chewy/type/mapping_spec.rb +54 -0
  51. data/spec/chewy/type/observe_spec.rb +55 -0
  52. data/spec/chewy/type/wrapper_spec.rb +35 -0
  53. data/spec/chewy/type_spec.rb +43 -0
  54. data/spec/chewy_spec.rb +36 -0
  55. data/spec/spec_helper.rb +48 -0
  56. data/spec/support/class_helpers.rb +16 -0
  57. data/spec/support/fail_helpers.rb +13 -0
  58. metadata +249 -0
@@ -0,0 +1,35 @@
1
+ module Chewy
2
+ module Type
3
+ module Wrapper
4
+ extend ActiveSupport::Concern
5
+
6
+ attr_accessor :attributes
7
+
8
+ def initialize(attributes = {})
9
+ @attributes = attributes.stringify_keys
10
+ end
11
+
12
+ def ==(other)
13
+ if other.is_a?(Chewy::Type::Base)
14
+ self.class == other.class && (respond_to?(:id) ? id == other.id : attributes == other.attributes)
15
+ elsif other.respond_to?(:id)
16
+ id.to_s == other.id.to_s
17
+ else
18
+ false
19
+ end
20
+ end
21
+
22
+ def method_missing(method, *args, &block)
23
+ if @attributes.key?(method.to_s)
24
+ @attributes[method.to_s]
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def respond_to_missing?(method, _)
31
+ @attributes.key?(method.to_s) || super
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Chewy
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Config do
4
+ include ClassHelpers
5
+ subject { described_class.send(:new) }
6
+
7
+ its(:observing_enabled) { should be_true }
8
+
9
+ describe '#atomic?' do
10
+ its(:atomic?) { should be_false }
11
+ specify { subject.atomic { subject.atomic?.should be_true } }
12
+ specify { subject.atomic { }; subject.atomic?.should be_false }
13
+ end
14
+
15
+ describe '#atomic' do
16
+ before do
17
+ stub_index(:dummies) do
18
+ define_type :dummy
19
+ end
20
+ end
21
+ let(:dummy_type) { DummiesIndex::Dummy }
22
+
23
+ specify { subject.atomic { 42 }.should == 42 }
24
+ specify { expect { subject.atomic { subject.atomic_stash Class.new, 42 } }.to raise_error ArgumentError }
25
+ specify { subject.atomic { subject.atomic { subject.atomic_stash.should == [{}, {}] } } }
26
+
27
+ specify do
28
+ expect(dummy_type).to receive(:import).with([1,2,3]).once
29
+ subject.atomic do
30
+ subject.atomic_stash dummy_type, 1, 2
31
+ subject.atomic_stash dummy_type, [2, 3]
32
+ end
33
+ end
34
+
35
+ specify do
36
+ expect(dummy_type).to receive(:import).with([2,3]).once
37
+ expect(dummy_type).to receive(:import).with([1,2]).once
38
+ subject.atomic do
39
+ subject.atomic_stash dummy_type, [2, 3]
40
+ subject.atomic do
41
+ subject.atomic_stash dummy_type, 1, 2
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#atomic_stash' do
48
+ specify { subject.atomic { subject.atomic_stash.should == [{}] } }
49
+ end
50
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Fields::Base do
4
+ specify { described_class.new('name').name.should == :name }
5
+ specify { described_class.new('name', type: 'integer').options[:type].should == 'integer' }
6
+
7
+ describe '#compose' do
8
+ let(:field) { described_class.new(:name, value: ->(o){ o.value }) }
9
+
10
+ specify { field.compose(double(value: 'hello')).should == {name: 'hello'} }
11
+ specify { field.compose(double(value: ['hello', 'world'])).should == {name: ['hello', 'world']} }
12
+
13
+ specify { described_class.new(:name).compose(double(name: 'hello')).should == {name: 'hello'} }
14
+
15
+ context do
16
+ before do
17
+ field.nested(described_class.new(:subname1, value: ->(o){ o.subvalue1 }))
18
+ field.nested(described_class.new(:subname2))
19
+ end
20
+
21
+ specify { field.compose(double(value: double(subvalue1: 'hello', subname2: 'world')))
22
+ .should == {name: {'subname1' => 'hello', 'subname2' => 'world'}} }
23
+ specify { field.compose(double(value: [double(subvalue1: 'hello1', subname2: 'world1'), double(subvalue1: 'hello2', subname2: 'world2')]))
24
+ .should == {name: [{'subname1' => 'hello1', 'subname2' => 'world1'}, {'subname1' => 'hello2', 'subname2' => 'world2'}]} }
25
+ end
26
+
27
+ context do
28
+ let(:field) { described_class.new(:name, type: 'multi_field') }
29
+ before do
30
+ field.nested(described_class.new(:name))
31
+ field.nested(described_class.new(:untouched))
32
+ end
33
+
34
+ specify { field.compose(double(name: 'Alex')).should == {name: 'Alex'} }
35
+ end
36
+ end
37
+
38
+ describe '#nested' do
39
+ let(:field) { described_class.new(:name) }
40
+
41
+ specify { expect { field.nested(described_class.new(:name1)) }
42
+ .to change { field.nested[:name1] }.from(nil).to(an_instance_of(described_class)) }
43
+ end
44
+
45
+ describe '#mappings_hash' do
46
+ let(:field) { described_class.new(:name, type: 'string') }
47
+ let(:fields1) { 2.times.map { |i| described_class.new("name#{i+1}", type: "string#{i+1}") } }
48
+ let(:fields2) { 2.times.map { |i| described_class.new("name#{i+3}", type: "string#{i+3}") } }
49
+ before do
50
+ fields1.each { |m| field.nested(m) }
51
+ fields2.each { |m| fields1[0].nested(m) }
52
+ end
53
+
54
+ specify { field.mappings_hash.should == {name: {type: 'string', properties: {
55
+ name1: {type: 'string1', properties: {
56
+ name3: {type: 'string3'}, name4: {type: 'string4'}
57
+ }}, name2: {type: 'string2'}
58
+ }}} }
59
+
60
+ context do
61
+ let(:field) { described_class.new(:name, type: 'multi_field') }
62
+
63
+ specify { field.mappings_hash.should == {name: {type: 'multi_field', fields: {
64
+ name1: {type: 'string1', properties: {
65
+ name3: {type: 'string3'}, name4: {type: 'string4'}
66
+ }}, name2: {type: 'string2'}
67
+ }}} }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Fields::Default do
4
+ specify { described_class.new('name').options[:type].should == 'string' }
5
+ # TODO: add 'should_behave_like base_field'
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Fields::Root do
4
+ specify { described_class.new('name').value.should be_a(Proc) }
5
+ # TODO: add 'should_behave_like base_field'
6
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index::Actions do
4
+ include ClassHelpers
5
+ before { Chewy::Index.client.indices.delete }
6
+
7
+ before { stub_index :dummies }
8
+
9
+ describe '.index_exists?' do
10
+ specify { DummiesIndex.index_exists?.should be_false }
11
+
12
+ context do
13
+ before { DummiesIndex.index_create }
14
+ specify { DummiesIndex.index_exists?.should be_true }
15
+ end
16
+ end
17
+
18
+ describe '.index_create' do
19
+ specify { DummiesIndex.index_create.should be_true }
20
+
21
+ context do
22
+ before { DummiesIndex.index_create }
23
+ specify { DummiesIndex.index_create.should be_false }
24
+ end
25
+ end
26
+
27
+ describe '.index_create!' do
28
+ specify { expect { DummiesIndex.index_create! }.not_to raise_error }
29
+
30
+ context do
31
+ before { DummiesIndex.index_create }
32
+ specify { expect { DummiesIndex.index_create! }.to raise_error }
33
+ end
34
+ end
35
+
36
+ describe '.index_delete' do
37
+ specify { DummiesIndex.index_delete.should be_false }
38
+
39
+ context do
40
+ before { DummiesIndex.index_create }
41
+ specify { DummiesIndex.index_delete.should be_true }
42
+ end
43
+ end
44
+
45
+ describe '.index_delete!' do
46
+ specify { expect { DummiesIndex.index_delete! }.to raise_error }
47
+
48
+ context do
49
+ before { DummiesIndex.index_create }
50
+ specify { expect { DummiesIndex.index_delete! }.not_to raise_error }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index::Client do
4
+ include ClassHelpers
5
+
6
+ describe '.client' do
7
+ specify { stub_index(:dummies1).client.should == stub_index(:dummies2).client }
8
+
9
+ context do
10
+ before do
11
+ stub_index(:dummies1)
12
+ stub_index(:dummies2, Dummies1Index)
13
+ end
14
+
15
+ specify { Dummies1Index.client.should == Dummies2Index.client }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index::Search do
4
+ include ClassHelpers
5
+
6
+ before do
7
+ stub_index(:products) do
8
+ define_type :product
9
+ define_type :product2
10
+ end
11
+ end
12
+
13
+ let(:product) { ProductsIndex.product }
14
+
15
+ describe '.search' do
16
+ specify do
17
+ product.search.should be_a Chewy::Query
18
+ end
19
+ end
20
+
21
+ describe '.search_string' do
22
+ specify do
23
+ expect(ProductsIndex.client).to receive(:search).with(hash_including(q: 'hello')).twice
24
+ ProductsIndex.search_string('hello')
25
+ product.search_string('hello')
26
+ end
27
+
28
+ specify do
29
+ expect(ProductsIndex.client).to receive(:search).with(hash_including(explain: true)).twice
30
+ ProductsIndex.search_string('hello', explain: true)
31
+ product.search_string('hello', explain: true)
32
+ end
33
+
34
+ specify do
35
+ expect(ProductsIndex.client).to receive(:search).with(hash_including(index: 'products', type: ['product', 'product2']))
36
+ ProductsIndex.search_string('hello')
37
+ end
38
+
39
+ specify do
40
+ expect(ProductsIndex.client).to receive(:search).with(hash_including(index: 'products', type: 'product'))
41
+ product.search_string('hello')
42
+ end
43
+ end
44
+
45
+ describe '.search_index' do
46
+ specify { ProductsIndex.search_index.should == ProductsIndex }
47
+ specify { product.search_index.should == ProductsIndex }
48
+ end
49
+
50
+ describe '.search_type' do
51
+ specify { ProductsIndex.search_type.should == ['product', 'product2'] }
52
+ specify { product.search_type.should == 'product' }
53
+ end
54
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index do
4
+ include ClassHelpers
5
+
6
+ describe '.define_type' do
7
+ context 'blank name' do
8
+ before do
9
+ stub_index(:dummies) do
10
+ define_type :dummy
11
+ end
12
+ end
13
+
14
+ specify { DummiesIndex.types.should have_key 'dummy' }
15
+ specify { DummiesIndex.types['dummy'].should be < Chewy::Type::Base }
16
+ specify { DummiesIndex.types['dummy'].type_name.should == 'dummy' }
17
+ end
18
+ end
19
+
20
+ describe '.index_name' do
21
+ specify { expect { Class.new(Chewy::Index).index_name }.to raise_error Chewy::UndefinedIndex }
22
+ specify { Class.new(Chewy::Index) { index_name :myindex }.index_name.should == 'myindex' }
23
+ specify { stub_const('DeveloperIndex', Class.new(Chewy::Index)).index_name.should == 'developer' }
24
+ specify { stub_const('DevelopersIndex', Class.new(Chewy::Index)).index_name.should == 'developers' }
25
+ end
26
+
27
+ describe '.index_params' do
28
+ specify { stub_index(:documents).index_params.should == {} }
29
+ specify { stub_index(:documents) { settings number_of_shards: 1 }.index_params.keys.should == [:settings] }
30
+ specify { stub_index(:documents) do
31
+ define_type :document do
32
+ field :name, type: 'string'
33
+ end
34
+ end.index_params.keys.should == [:mappings] }
35
+ specify { stub_index(:documents) do
36
+ settings number_of_shards: 1
37
+ define_type :document do
38
+ field :name, type: 'string'
39
+ end
40
+ end.index_params.keys.should =~ [:mappings, :settings] }
41
+ end
42
+
43
+ describe '.settings_hash' do
44
+ specify { stub_index(:documents).settings_hash.should == {} }
45
+ specify { stub_index(:documents) { settings number_of_shards: 1 }.settings_hash.should == {settings: {number_of_shards: 1}} }
46
+ end
47
+
48
+ describe '.mappings_hash' do
49
+ specify { stub_index(:documents).mappings_hash.should == {} }
50
+ specify { stub_index(:documents) { define_type :document }.mappings_hash.should == {} }
51
+ specify { stub_index(:documents) do
52
+ define_type :document do
53
+ field :name, type: 'string'
54
+ end
55
+ end.mappings_hash.should == {mappings: {document: {properties: {name: {type: 'string'}}}}} }
56
+ specify { stub_index(:documents) do
57
+ define_type :document do
58
+ field :name, type: 'string'
59
+ end
60
+ define_type :document2 do
61
+ field :name, type: 'string'
62
+ end
63
+ end.mappings_hash[:mappings].keys.should =~ [:document, :document2] }
64
+ end
65
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Query::Criteria do
4
+ include ClassHelpers
5
+
6
+ subject { described_class.new }
7
+
8
+ its(:search) { should == {} }
9
+ its(:query) { should == {} }
10
+ its(:facets) { should == {} }
11
+ its(:filters) { should == [] }
12
+ its(:sort) { should == [] }
13
+ its(:fields) { should == [] }
14
+
15
+ its(:search?) { should be_false }
16
+ its(:query?) { should be_false }
17
+ its(:facets?) { should be_false }
18
+ its(:filters?) { should be_false }
19
+ its(:sort?) { should be_false }
20
+ its(:fields?) { should be_false }
21
+
22
+ describe '#update_search' do
23
+ specify { expect { subject.update_search(field: 'hello') }.to change { subject.search? }.to(true) }
24
+ specify { expect { subject.update_search(field: 'hello') }.to change { subject.search }.to(field: 'hello') }
25
+ end
26
+
27
+ describe '#update_query' do
28
+ specify { expect { subject.update_query(field: 'hello') }.to change { subject.query? }.to(true) }
29
+ specify { expect { subject.update_query(field: 'hello') }.to change { subject.query }.to(field: 'hello') }
30
+ end
31
+
32
+ describe '#update_facets' do
33
+ specify { expect { subject.update_facets(field: 'hello') }.to change { subject.facets? }.to(true) }
34
+ specify { expect { subject.update_facets(field: 'hello') }.to change { subject.facets }.to(field: 'hello') }
35
+ end
36
+
37
+ describe '#update_filters' do
38
+ specify { expect { subject.update_filters(field: 'hello') }.to change { subject.filters? }.to(true) }
39
+ specify { expect { subject.update_filters(field: 'hello') }.to change { subject.filters }.to([{field: 'hello'}]) }
40
+ specify { expect { subject.update_filters(field: 'hello'); subject.update_filters(field: 'world') }
41
+ .to change { subject.filters }.to([{field: 'hello'}, {field: 'world'}]) }
42
+ specify { expect { subject.update_filters([{field: 'hello'}, {field: 'world'}, nil]) }
43
+ .to change { subject.filters }.to([{field: 'hello'}, {field: 'world'}]) }
44
+ end
45
+
46
+ describe '#update_sort' do
47
+ specify { expect { subject.update_sort(:field) }.to change { subject.sort? }.to(true) }
48
+
49
+ specify { expect { subject.update_sort([:field]) }.to change { subject.sort }.to([:field]) }
50
+ specify { expect { subject.update_sort([:field1, :field2]) }.to change { subject.sort }.to([:field1, :field2]) }
51
+ specify { expect { subject.update_sort([{field: :asc}]) }.to change { subject.sort }.to([{field: :asc}]) }
52
+ specify { expect { subject.update_sort([:field1, field2: {order: :asc}]) }.to change { subject.sort }.to([:field1, {field2: {order: :asc}}]) }
53
+ specify { expect { subject.update_sort([{field1: {order: :asc}}, :field2]) }.to change { subject.sort }.to([{field1: {order: :asc}}, :field2]) }
54
+ specify { expect { subject.update_sort([field1: :asc, field2: {order: :asc}]) }.to change { subject.sort }.to([{field1: :asc}, {field2: {order: :asc}}]) }
55
+ specify { expect { subject.update_sort([{field1: {order: :asc}}, :field2, :field3]) }.to change { subject.sort }.to([{field1: {order: :asc}}, :field2, :field3]) }
56
+ specify { expect { subject.update_sort([{field1: {order: :asc}}, [:field2, :field3]]) }.to change { subject.sort }.to([{field1: {order: :asc}}, :field2, :field3]) }
57
+ specify { expect { subject.update_sort([{field1: {order: :asc}}, [:field2], :field3]) }.to change { subject.sort }.to([{field1: {order: :asc}}, :field2, :field3]) }
58
+ specify { expect { subject.update_sort([{field1: {order: :asc}, field2: :desc}, [:field3], :field4]) }.to change { subject.sort }.to([{field1: {order: :asc}}, {field2: :desc}, :field3, :field4]) }
59
+ specify { expect { subject.tap { |s| s.update_sort([field1: {order: :asc}, field2: :desc]) }.update_sort([[:field3], :field4]) }.to change { subject.sort }.to([{field1: {order: :asc}}, {field2: :desc}, :field3, :field4]) }
60
+ specify { expect { subject.tap { |s| s.update_sort([field1: {order: :asc}, field2: :desc]) }.update_sort([[:field3], :field4], purge: true) }.to change { subject.sort }.to([:field3, :field4]) }
61
+ end
62
+
63
+ describe '#update_fields' do
64
+ specify { expect { subject.update_fields(:field) }.to change { subject.fields? }.to(true) }
65
+ specify { expect { subject.update_fields(:field) }.to change { subject.fields }.to(['field']) }
66
+ specify { expect { subject.update_fields([:field, :field]) }.to change { subject.fields }.to(['field']) }
67
+ specify { expect { subject.update_fields([:field1, :field2]) }.to change { subject.fields }.to(['field1', 'field2']) }
68
+ specify { expect { subject.tap { |s| s.update_fields(:field1) }.update_fields([:field2, :field3]) }
69
+ .to change { subject.fields }.to(['field1', 'field2', 'field3']) }
70
+ specify { expect { subject.tap { |s| s.update_fields(:field1) }.update_fields([:field2, :field3], purge: true) }
71
+ .to change { subject.fields }.to(['field2', 'field3']) }
72
+ end
73
+ end