wcc-contentful 1.1.0 → 1.2.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.
@@ -34,6 +34,17 @@ module WCC::Contentful::Store
34
34
  @extra = extra
35
35
  end
36
36
 
37
+ FALSE_VALUES = [
38
+ false, 0,
39
+ '0', :"0",
40
+ 'f', :f,
41
+ 'F', :F,
42
+ 'false', :false, # rubocop:disable Lint/BooleanSymbol
43
+ 'FALSE', :FALSE,
44
+ 'off', :off,
45
+ 'OFF', :OFF
46
+ ].to_set.freeze
47
+
37
48
  # Returns a new chained Query that has a new condition. The new condition
38
49
  # represents the WHERE comparison being applied here. The underlying store
39
50
  # implementation translates this condition statement into an appropriate
@@ -50,7 +61,16 @@ module WCC::Contentful::Store
50
61
  # @expected The expected value to compare the field's value against.
51
62
  # @context A context object optionally containing `context[:locale]`
52
63
  def apply_operator(operator, field, expected, context = nil)
64
+ operator ||= expected.is_a?(Array) ? :in : :eq
53
65
  raise ArgumentError, "Operator #{operator} not supported" unless respond_to?(operator)
66
+ raise ArgumentError, 'value cannot be nil (try using exists: false)' if expected.nil?
67
+
68
+ case operator
69
+ when :in, :nin, :all
70
+ expected = Array(expected)
71
+ when :exists
72
+ expected = !FALSE_VALUES.include?(expected)
73
+ end
54
74
 
55
75
  field = field.to_s if field.is_a? Symbol
56
76
  path = field.is_a?(Array) ? field : field.split('.')
@@ -151,7 +171,7 @@ module WCC::Contentful::Store
151
171
  elsif op?(k)
152
172
  { path: path, op: k.to_sym, expected: v }
153
173
  else
154
- { path: path + [k], op: :eq, expected: v }
174
+ { path: path + [k], op: nil, expected: v }
155
175
  end
156
176
  end
157
177
  end
@@ -528,68 +528,6 @@ RSpec.shared_examples 'basic store' do
528
528
  expect(found.dig('sys', 'id')).to eq('idTwo')
529
529
  expect(found.dig('fields', 'system', 'en-US')).to eq('Two')
530
530
  end
531
-
532
- [
533
- [Integer, proc { rand(-4_611_686_018_427_387_903..4_611_686_018_427_387_903) }],
534
- [Float, proc { rand }]
535
- ].each do |(type, generator)|
536
- context "by #{type} equality" do
537
- it 'can apply filter object' do
538
- data =
539
- 1.upto(10).map do |i|
540
- {
541
- 'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
542
- 'fields' => { type.to_s => { 'en-US' => generator.call } }
543
- }
544
- end
545
-
546
- desired_value = generator.call
547
- desired = {
548
- 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
549
- 'fields' => { type.to_s => { 'en-US' => desired_value } }
550
- }
551
-
552
- data << desired
553
- data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
554
-
555
- # act
556
- found = subject.find_by(content_type: 'test1', filter: { type.to_s => desired_value })
557
-
558
- # assert
559
- expect(found).to_not be_nil
560
- expect(found).to eq(desired)
561
- end
562
-
563
- it 'filter object can find value in array' do
564
- data =
565
- 1.upto(10).map do |i|
566
- {
567
- 'sys' => {
568
- 'id' => "k#{i}",
569
- 'contentType' => { 'sys' => { 'id' => 'test1' } }
570
- },
571
- 'fields' => { 'name' => { 'en-US' => [generator.call, generator.call] } }
572
- }
573
- end
574
-
575
- desired_value = generator.call
576
- desired = {
577
- 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
578
- 'fields' => { type.to_s => { 'en-US' => [generator.call, desired_value].shuffle } }
579
- }
580
-
581
- data << desired
582
- data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
583
-
584
- # act
585
- found = subject.find_by(content_type: 'test1', filter: { type.to_s => { eq: desired_value } })
586
-
587
- # assert
588
- expect(found).to_not be_nil
589
- expect(found).to eq(desired)
590
- end
591
- end
592
- end
593
531
  end
594
532
 
595
533
  describe '#find_all' do
@@ -634,29 +572,6 @@ RSpec.shared_examples 'basic store' do
634
572
  %w[k1 k5 k9]
635
573
  )
636
574
  end
637
-
638
- it 'filter query eq can find value in array' do
639
- content_types = %w[test1 test2 test3 test4]
640
- data =
641
- 1.upto(10).map do |i|
642
- {
643
- 'sys' => {
644
- 'id' => "k#{i}",
645
- 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
646
- },
647
- 'fields' => { 'name' => { 'en-US' => ["test#{i}", "test_2_#{i}"] } }
648
- }
649
- end
650
- data.each { |d| subject.set(d.dig('sys', 'id'), d) }
651
-
652
- # act
653
- found = subject.find_all(content_type: 'test2')
654
- .apply('name' => { eq: 'test_2_5' })
655
-
656
- # assert
657
- expect(found.count).to eq(1)
658
- expect(found.first.dig('sys', 'id')).to eq('k5')
659
- end
660
575
  end
661
576
 
662
577
  def make_link_to(id, link_type = 'Entry')
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'supports :eq operator' do
4
+ [
5
+ [String, proc { "test#{rand(1..10_000)}" }],
6
+ [Integer, proc { rand(-4_611_686_018_427_387_903..4_611_686_018_427_387_903) }],
7
+ [Float, proc { rand }]
8
+ ].each do |(type, generator)|
9
+ context "with #{type} value" do
10
+ let(:desired_value) {
11
+ generator.call
12
+ }
13
+
14
+ let(:data) {
15
+ 1.upto(3).map do |i|
16
+ {
17
+ 'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
18
+ 'fields' => { type.to_s => { 'en-US' => generator.call } }
19
+ }
20
+ end
21
+ }
22
+
23
+ let(:desired) {
24
+ {
25
+ 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
26
+ 'fields' => { type.to_s => { 'en-US' => desired_value } }
27
+ }
28
+ }
29
+
30
+ it 'find_by can apply filter object' do
31
+ [*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
32
+
33
+ # act
34
+ found = subject.find_by(content_type: 'test1', filter: { type.to_s => desired_value })
35
+
36
+ # assert
37
+ expect(found).to_not be_nil
38
+ expect(found).to eq(desired)
39
+ end
40
+
41
+ it 'find_by can find value in array' do
42
+ data =
43
+ 1.upto(3).map do |i|
44
+ {
45
+ 'sys' => {
46
+ 'id' => "k#{i}",
47
+ 'contentType' => { 'sys' => { 'id' => 'test1' } }
48
+ },
49
+ 'fields' => { 'name' => { 'en-US' => [generator.call, generator.call] } }
50
+ }
51
+ end
52
+
53
+ desired_value = generator.call
54
+ desired = {
55
+ 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
56
+ 'fields' => { type.to_s => { 'en-US' => [generator.call, desired_value].shuffle } }
57
+ }
58
+
59
+ data << desired
60
+ data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
61
+
62
+ # act
63
+ found = subject.find_by(content_type: 'test1', filter: { type.to_s => { eq: desired_value } })
64
+
65
+ # assert
66
+ expect(found).to_not be_nil
67
+ expect(found).to eq(desired)
68
+ end
69
+
70
+ it 'find_all can apply operator' do
71
+ desired =
72
+ 4.upto(5).map do |i|
73
+ {
74
+ 'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
75
+ 'fields' => { type.to_s => { 'en-US' => desired_value } }
76
+ }
77
+ end
78
+
79
+ [*data, *desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
80
+
81
+ # act
82
+ found = subject.find_all(content_type: 'test1')
83
+ .eq(type.to_s, desired_value)
84
+
85
+ # assert
86
+ expect(found.count).to eq(2)
87
+ sorted = found.to_a.sort_by { |item| item.dig('sys', 'id') }
88
+ expect(sorted).to eq(desired)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'supports :in operator' do
4
+ it 'find_all with array on string field' do
5
+ ids = 1.upto(10).to_a
6
+ data =
7
+ ids.map do |i|
8
+ {
9
+ 'sys' => {
10
+ 'id' => "k#{i}",
11
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
12
+ },
13
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
14
+ }
15
+ end
16
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
17
+
18
+ to_find = ids.shuffle.take(2)
19
+
20
+ # act
21
+ found = subject.find_all(content_type: 'test')
22
+ .in('name', to_find.map { |i| "test#{i}" })
23
+
24
+ expect(found.count).to eq(2)
25
+ expect(found.map { |item| item.dig('sys', 'id') }.sort).to eq(
26
+ to_find.map { |i| "k#{i}" }.sort
27
+ )
28
+ end
29
+
30
+ it 'find_all with array on array field' do
31
+ ids = 1.upto(10).to_a
32
+ data =
33
+ ids.map do |i|
34
+ {
35
+ 'sys' => {
36
+ 'id' => "k#{i}",
37
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
38
+ },
39
+ 'fields' => { 'name' => { 'en-US' => ["test#{i}", "test_2_#{i}"] } }
40
+ }
41
+ end
42
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
43
+
44
+ to_find1, to_find2 = ids.shuffle
45
+
46
+ # act
47
+ found = subject.find_all(content_type: 'test')
48
+ .in('name', ["test#{to_find1}", "test_2_#{to_find2}"])
49
+
50
+ expect(found.count).to eq(2)
51
+ expect(found.map { |item| item.dig('sys', 'id') }.sort).to eq(
52
+ ["k#{to_find1}", "k#{to_find2}"].sort
53
+ )
54
+ end
55
+
56
+ it 'find_all defaults to :in when given an array' do
57
+ ids = 1.upto(10).to_a
58
+ data =
59
+ ids.map do |i|
60
+ {
61
+ 'sys' => {
62
+ 'id' => "k#{i}",
63
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
64
+ },
65
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
66
+ }
67
+ end
68
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
69
+
70
+ to_find = ids.shuffle.take(3)
71
+
72
+ # act
73
+ found = subject.find_all(content_type: 'test')
74
+ .apply('name' => to_find.map { |i| "test#{i}" })
75
+
76
+ expect(found.count).to eq(3)
77
+ expect(found.map { |item| item.dig('sys', 'id') }.sort).to eq(
78
+ to_find.map { |i| "k#{i}" }.sort
79
+ )
80
+ end
81
+
82
+ it 'find_by with array on string field' do
83
+ ids = 1.upto(10).to_a
84
+ data =
85
+ ids.map do |i|
86
+ {
87
+ 'sys' => {
88
+ 'id' => "k#{i}",
89
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
90
+ },
91
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
92
+ }
93
+ end
94
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
95
+
96
+ to_find = ids.sample
97
+
98
+ # act
99
+ found = subject.find_by(
100
+ content_type: 'test',
101
+ filter: { name: { in: ['asdf', "test#{to_find}"] } }
102
+ )
103
+
104
+ expect(found.dig('sys', 'id')).to eq("k#{to_find}")
105
+ end
106
+
107
+ it 'find_by defaults to :in when given an array' do
108
+ ids = 1.upto(10).to_a
109
+ data =
110
+ ids.map do |i|
111
+ {
112
+ 'sys' => {
113
+ 'id' => "k#{i}",
114
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
115
+ },
116
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
117
+ }
118
+ end
119
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
120
+
121
+ to_find = ids.sample
122
+
123
+ # act
124
+ found = subject.find_by(
125
+ content_type: 'test',
126
+ filter: { name: ['asdf', "test#{to_find}"] }
127
+ )
128
+
129
+ expect(found.dig('sys', 'id')).to eq("k#{to_find}")
130
+ end
131
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'supports :ne operator' do
4
+ [
5
+ [String, proc { "test#{rand(1..100_000)}" }],
6
+ [Integer, proc { rand(-4_611_686_018_427_387_903..4_611_686_018_427_387_903) }],
7
+ [Float, proc { rand }]
8
+ ].each do |(type, generator)|
9
+ context "with #{type} value" do
10
+ let(:specified_value) {
11
+ generator.call
12
+ }
13
+
14
+ let(:desired) {
15
+ # desired entry doesn't have the specified_value
16
+ {
17
+ 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
18
+ 'fields' => { type.to_s => { 'en-US' => 1.upto(rand(2..5)).map { generator.call } } }
19
+ }
20
+ }
21
+
22
+ let(:data) {
23
+ 1.upto(3).map do |i|
24
+ random_values = 1.upto(rand(2..5)).map { generator.call }
25
+
26
+ # remaining data does include the specified_value
27
+ {
28
+ 'sys' => {
29
+ 'id' => "k#{i}",
30
+ 'contentType' => { 'sys' => { 'id' => 'test1' } }
31
+ },
32
+ 'fields' => { type.to_s => { 'en-US' => [*random_values, specified_value].shuffle } }
33
+ }
34
+ end
35
+ }
36
+
37
+ it 'find_by can apply filter object' do
38
+ specified_value = generator.call
39
+ data = {
40
+ 'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
41
+ 'fields' => { type.to_s => { 'en-US' => specified_value } }
42
+ }
43
+
44
+ subject.set(data.dig('sys', 'id'), data)
45
+
46
+ # act
47
+ found = subject.find_by(content_type: 'test1', filter: { type.to_s => { ne: specified_value } })
48
+
49
+ # assert
50
+ expect(found).to be_nil
51
+ end
52
+
53
+ it 'find_by can find value in array' do
54
+ [*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
55
+
56
+ # act
57
+ found = subject.find_by(content_type: 'test1', filter: { type.to_s => { ne: specified_value } })
58
+
59
+ # assert
60
+ expect(found).to_not be_nil
61
+ expect(found).to eq(desired)
62
+ end
63
+
64
+ it 'find_all can apply operator' do
65
+ [*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
66
+
67
+ # act
68
+ found = subject.find_all(content_type: 'test1')
69
+ .ne(type.to_s, specified_value)
70
+
71
+ # assert
72
+ expect(found.count).to eq(1)
73
+ expect(found.first).to eq(desired)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'supports :nin operator' do
4
+ it 'find_all with array on string field' do
5
+ ids = 1.upto(10).to_a
6
+ data =
7
+ ids.map do |i|
8
+ {
9
+ 'sys' => {
10
+ 'id' => "k#{i}",
11
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
12
+ },
13
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
14
+ }
15
+ end
16
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
17
+
18
+ to_exclude = ids.shuffle.take(2)
19
+
20
+ # act
21
+ found = subject.find_all(content_type: 'test')
22
+ .nin('name', to_exclude.map { |i| "test#{i}" })
23
+
24
+ expect(found.count).to eq(8)
25
+ expect(found.map { |item| item.dig('sys', 'id') }.sort).to eq(
26
+ (ids - to_exclude).map { |i| "k#{i}" }.sort
27
+ )
28
+ end
29
+
30
+ it 'find_all with array on array field' do
31
+ ids = 1.upto(10).to_a
32
+ data =
33
+ ids.map do |i|
34
+ {
35
+ 'sys' => {
36
+ 'id' => "k#{i}",
37
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
38
+ },
39
+ 'fields' => { 'name' => { 'en-US' => ["test#{i}", "test_2_#{i}"] } }
40
+ }
41
+ end
42
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
43
+
44
+ to_exclude1, to_exclude2 = ids.shuffle
45
+
46
+ # act
47
+ found = subject.find_all(content_type: 'test')
48
+ .nin('name', ["test#{to_exclude1}", "test_2_#{to_exclude2}"])
49
+
50
+ expect(found.count).to eq(8)
51
+ expect(found.map { |item| item.dig('sys', 'id') }.sort).to eq(
52
+ (ids - [to_exclude1, to_exclude2]).map { |i| "k#{i}" }.sort
53
+ )
54
+ end
55
+
56
+ it 'find_by with array on string field' do
57
+ ids = 1.upto(2).to_a
58
+ data =
59
+ ids.map do |i|
60
+ {
61
+ 'sys' => {
62
+ 'id' => "k#{i}",
63
+ 'contentType' => { 'sys' => { 'id' => 'test' } }
64
+ },
65
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
66
+ }
67
+ end
68
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
69
+
70
+ to_exclude, to_expect = ids.shuffle
71
+
72
+ # act
73
+ found = subject.find_by(
74
+ content_type: 'test',
75
+ filter: { name: { nin: ['asdf', "test#{to_exclude}"] } }
76
+ )
77
+
78
+ expect(found.dig('sys', 'id')).to eq("k#{to_expect}")
79
+ end
80
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './operators/eq'
4
+ require_relative './operators/ne'
5
+ require_relative './operators/in'
6
+ require_relative './operators/nin'
7
+
8
+ (WCC::Contentful::Store::Query::Interface::OPERATORS -
9
+ %i[eq ne in nin]).each do |op|
10
+ RSpec.shared_examples "supports :#{op} operator" do
11
+ it 'TODO'
12
+ end
13
+ end
14
+
15
+ RSpec.shared_examples 'operators' do |feature_set|
16
+ supported_operators =
17
+ if feature_set.nil?
18
+ WCC::Contentful::Store::Query::Interface::OPERATORS
19
+ .each_with_object({}) { |k, h| h[k] = 'pending' }
20
+ elsif feature_set.is_a?(Array)
21
+ WCC::Contentful::Store::Query::Interface::OPERATORS
22
+ .each_with_object({}) { |k, h| h[k] = feature_set.include?(k.to_sym) }
23
+ elsif feature_s.is_a?(Hash)
24
+ feature_set
25
+ else
26
+ raise ArgumentError, 'Please provide a hash or array of operators to test'
27
+ end
28
+
29
+ supported_operators.each do |op, value|
30
+ next if value
31
+
32
+ it "does not support :#{op}" do
33
+ expect {
34
+ subject.find_all(content_type: 'test')
35
+ .apply('name' => { op => 'test' })
36
+ .to_a
37
+ }.to raise_error do |ex|
38
+ expect(ex.to_s).to match(/not supported/)
39
+ end
40
+ end
41
+ end
42
+
43
+ supported_operators.each do |op, value|
44
+ next unless value
45
+
46
+ it_behaves_like "supports :#{op} operator" do
47
+ before { pending(":#{op} operator to be implemented") } if value == 'pending'
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './rspec_examples/basic_store'
4
+ require_relative './rspec_examples/operators'
4
5
  require_relative './rspec_examples/nested_queries'
5
6
  require_relative './rspec_examples/include_param'
6
7
 
@@ -41,6 +42,7 @@ RSpec.shared_examples 'contentful store' do |feature_set|
41
42
  }.merge(feature_set&.symbolize_keys || {})
42
43
 
43
44
  include_examples 'basic store'
45
+ include_examples 'operators', feature_set[:operators]
44
46
  include_examples 'supports nested queries', feature_set[:nested_queries]
45
47
  include_examples 'supports include param', feature_set[:include_param]
46
48
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '1.1.0'
5
+ VERSION = '1.2.0'
6
6
  end
7
7
  end
@@ -3,7 +3,7 @@
3
3
  require 'wcc/contentful/version'
4
4
 
5
5
  require 'active_support'
6
- require 'active_support/core_ext/object'
6
+ require 'active_support/all'
7
7
 
8
8
  require 'wcc/contentful/active_record_shim'
9
9
  require 'wcc/contentful/configuration'
@@ -54,7 +54,7 @@ Gem::Specification.new do |spec|
54
54
  spec.add_development_dependency 'generator_spec', '~> 0.9.4'
55
55
  # spec.add_development_dependency 'rails', '~> 5.0'
56
56
  # spec.add_development_dependency 'rspec-rails', '~> 3.7'
57
- spec.add_development_dependency 'sqlite3', '~> 1.3.6'
57
+ spec.add_development_dependency 'sqlite3', '~> 1.4'
58
58
  spec.add_development_dependency 'timecop', '~> 0.9.1'
59
59
 
60
60
  # optional dependencies