wcc-contentful 1.3.1 → 1.4.0.rc1
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.
- checksums.yaml +4 -4
- data/README.md +80 -13
- data/lib/wcc/contentful/configuration.rb +12 -5
- data/lib/wcc/contentful/downloads_schema.rb +14 -2
- data/lib/wcc/contentful/entry_locale_transformer.rb +107 -0
- data/lib/wcc/contentful/exceptions.rb +5 -0
- data/lib/wcc/contentful/link_visitor.rb +12 -1
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +25 -8
- data/lib/wcc/contentful/middleware/store/locale_middleware.rb +30 -0
- data/lib/wcc/contentful/middleware/store.rb +20 -16
- data/lib/wcc/contentful/model_builder.rb +9 -2
- data/lib/wcc/contentful/model_methods.rb +4 -6
- data/lib/wcc/contentful/simple_client/cdn.rb +5 -2
- data/lib/wcc/contentful/simple_client/management.rb +16 -0
- data/lib/wcc/contentful/simple_client.rb +1 -0
- data/lib/wcc/contentful/store/base.rb +6 -1
- data/lib/wcc/contentful/store/cdn_adapter.rb +13 -4
- data/lib/wcc/contentful/store/factory.rb +8 -1
- data/lib/wcc/contentful/store/memory_store.rb +27 -8
- data/lib/wcc/contentful/store/postgres_store.rb +4 -3
- data/lib/wcc/contentful/store/query/condition.rb +89 -0
- data/lib/wcc/contentful/store/query.rb +9 -35
- data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +84 -12
- data/lib/wcc/contentful/store/rspec_examples/locale_queries.rb +220 -0
- data/lib/wcc/contentful/store/rspec_examples/operators/eq.rb +1 -1
- data/lib/wcc/contentful/store/rspec_examples.rb +13 -1
- data/lib/wcc/contentful/sync_engine.rb +1 -1
- data/lib/wcc/contentful/test/double.rb +1 -1
- data/lib/wcc/contentful/test/factory.rb +2 -4
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +17 -6
- metadata +78 -46
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_examples 'supports locales in queries' do |feature_set|
|
4
|
+
describe 'supports query options: { locale: ... }' do
|
5
|
+
before { skip('querying alternate locales not supported') } if feature_set == false
|
6
|
+
|
7
|
+
generator = proc { "test#{rand(1..10_000)}" }
|
8
|
+
|
9
|
+
let(:desired_value) {
|
10
|
+
generator.call
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:data) {
|
14
|
+
1.upto(3).map do |i|
|
15
|
+
{
|
16
|
+
'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
17
|
+
'fields' => {
|
18
|
+
'slug' => {
|
19
|
+
'en-US' => generator.call,
|
20
|
+
'es-ES' => generator.call
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:desired) {
|
28
|
+
{
|
29
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
30
|
+
'fields' => {
|
31
|
+
'slug' => {
|
32
|
+
'en-US' => generator.call,
|
33
|
+
'es-ES' => desired_value
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
context 'when localized value exists' do
|
40
|
+
it 'find_by can apply filter object' do
|
41
|
+
[*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
42
|
+
|
43
|
+
# act
|
44
|
+
found = subject.find_by(content_type: 'test1',
|
45
|
+
filter: { 'slug' => desired_value },
|
46
|
+
options: { locale: 'es-ES' })
|
47
|
+
|
48
|
+
# assert
|
49
|
+
expect(found).to_not be_nil
|
50
|
+
expect(found).to eq(desired)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'find_by can find value in array' do
|
54
|
+
data =
|
55
|
+
1.upto(3).map do |i|
|
56
|
+
{
|
57
|
+
'sys' => {
|
58
|
+
'id' => "k#{i}",
|
59
|
+
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
60
|
+
},
|
61
|
+
'fields' => {
|
62
|
+
'slug' => {
|
63
|
+
'en-US' => [generator.call, generator.call],
|
64
|
+
'es-ES' => [generator.call, generator.call]
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
desired_value = generator.call
|
71
|
+
desired = {
|
72
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
73
|
+
'fields' => {
|
74
|
+
'slug' => {
|
75
|
+
'en-US' => [generator.call, generator.call],
|
76
|
+
'es-ES' => [generator.call, desired_value]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
data << desired
|
82
|
+
data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
83
|
+
|
84
|
+
# act
|
85
|
+
found = subject.find_by(content_type: 'test1',
|
86
|
+
filter: { 'slug' => { eq: desired_value } },
|
87
|
+
options: { locale: 'es-ES' })
|
88
|
+
|
89
|
+
# assert
|
90
|
+
expect(found).to_not be_nil
|
91
|
+
expect(found).to eq(desired)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'find_all can apply operator' do
|
95
|
+
desired =
|
96
|
+
4.upto(5).map do |i|
|
97
|
+
{
|
98
|
+
'sys' => { 'id' => "d#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
99
|
+
'fields' => {
|
100
|
+
'slug' => {
|
101
|
+
'en-US' => generator.call,
|
102
|
+
'es-ES' => desired_value
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
[*data, *desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
109
|
+
|
110
|
+
# act
|
111
|
+
found = subject.find_all(content_type: 'test1', options: { locale: 'es-ES' })
|
112
|
+
.eq('slug', desired_value)
|
113
|
+
|
114
|
+
# assert
|
115
|
+
expect(found.count).to eq(2)
|
116
|
+
sorted = found.to_a.sort_by { |item| item.dig('sys', 'id') }
|
117
|
+
expect(sorted).to eq(desired)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'using fallback locales' do
|
122
|
+
before { pending('querying alternate locales not yet implemented') } if feature_set&.to_s == 'pending'
|
123
|
+
|
124
|
+
before do
|
125
|
+
allow(configuration).to receive(:locale_fallbacks)
|
126
|
+
.and_return({
|
127
|
+
'es-MX' => 'es-ES',
|
128
|
+
'es-ES' => 'en-US'
|
129
|
+
})
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'find_by can apply filter object' do
|
133
|
+
[*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
134
|
+
|
135
|
+
# act
|
136
|
+
found = subject.find_by(content_type: 'test1',
|
137
|
+
filter: { 'slug' => desired_value },
|
138
|
+
options: { locale: 'es-MX' })
|
139
|
+
|
140
|
+
# assert
|
141
|
+
expect(found).to_not be_nil
|
142
|
+
expect(found).to eq(desired)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'find_by can find value in array' do
|
146
|
+
data =
|
147
|
+
1.upto(3).map do |i|
|
148
|
+
{
|
149
|
+
'sys' => {
|
150
|
+
'id' => "k#{i}",
|
151
|
+
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
152
|
+
},
|
153
|
+
'fields' => {
|
154
|
+
'slug' => {
|
155
|
+
'en-US' => [generator.call, generator.call],
|
156
|
+
'es-ES' => [generator.call, generator.call]
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
desired_value = generator.call
|
163
|
+
desired = {
|
164
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
165
|
+
'fields' => {
|
166
|
+
'slug' => {
|
167
|
+
'en-US' => [generator.call, generator.call],
|
168
|
+
'es-ES' => [generator.call, desired_value]
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
data << desired
|
174
|
+
data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
175
|
+
|
176
|
+
# act
|
177
|
+
found = subject.find_by(content_type: 'test1',
|
178
|
+
filter: { 'slug' => { eq: desired_value } },
|
179
|
+
options: { locale: 'es-MX' })
|
180
|
+
|
181
|
+
# assert
|
182
|
+
expect(found).to_not be_nil
|
183
|
+
expect(found).to eq(desired)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'find_all can apply operator' do
|
187
|
+
desired = [
|
188
|
+
{
|
189
|
+
'sys' => { 'id' => 'd1', 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
190
|
+
'fields' => {
|
191
|
+
'slug' => {
|
192
|
+
'en-US' => generator.call,
|
193
|
+
'es-ES' => desired_value
|
194
|
+
}
|
195
|
+
}
|
196
|
+
},
|
197
|
+
{
|
198
|
+
'sys' => { 'id' => 'd2', 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
199
|
+
'fields' => {
|
200
|
+
'slug' => {
|
201
|
+
'en-US' => desired_value
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
]
|
206
|
+
|
207
|
+
[*data, *desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
208
|
+
|
209
|
+
# act
|
210
|
+
found = subject.find_all(content_type: 'test1', options: { locale: 'es-MX' })
|
211
|
+
.eq('slug', desired_value)
|
212
|
+
|
213
|
+
# assert
|
214
|
+
expect(found.count).to eq(2)
|
215
|
+
sorted = found.to_a.sort_by { |item| item.dig('sys', 'id') }
|
216
|
+
expect(sorted).to eq(desired)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -46,7 +46,7 @@ RSpec.shared_examples 'supports :eq operator' do
|
|
46
46
|
'id' => "k#{i}",
|
47
47
|
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
48
48
|
},
|
49
|
-
'fields' => {
|
49
|
+
'fields' => { type.to_s => { 'en-US' => [generator.call, generator.call] } }
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
@@ -4,6 +4,7 @@ require_relative './rspec_examples/basic_store'
|
|
4
4
|
require_relative './rspec_examples/operators'
|
5
5
|
require_relative './rspec_examples/nested_queries'
|
6
6
|
require_relative './rspec_examples/include_param'
|
7
|
+
require_relative './rspec_examples/locale_queries'
|
7
8
|
|
8
9
|
# rubocop:disable Style/BlockDelimiters
|
9
10
|
|
@@ -24,6 +25,11 @@ require_relative './rspec_examples/include_param'
|
|
24
25
|
# all linked entries of an object in a single query.
|
25
26
|
# If your store does not respect the include parameter, then the Model layer
|
26
27
|
# will be calling #find a lot in order to resolve linked entries.
|
28
|
+
# [:locale_queries] - This feature defines how the store respects the `locale: x`
|
29
|
+
# key in the Options hash. If this option is set, then the store needs to
|
30
|
+
# compare the query to the appropriate localized value.
|
31
|
+
# If the store does not support this, then either the application should not
|
32
|
+
# use multiple locales OR should always query using the default locale.
|
27
33
|
#
|
28
34
|
# @example
|
29
35
|
# require 'wcc/contentful/store/rspec_examples'
|
@@ -38,13 +44,19 @@ require_relative './rspec_examples/include_param'
|
|
38
44
|
RSpec.shared_examples 'contentful store' do |feature_set|
|
39
45
|
feature_set = {
|
40
46
|
nested_queries: 'pending',
|
41
|
-
include_param: 'pending'
|
47
|
+
include_param: 'pending',
|
48
|
+
locale_queries: 'pending'
|
42
49
|
}.merge(feature_set&.symbolize_keys || {})
|
43
50
|
|
51
|
+
let(:configuration) {
|
52
|
+
WCC::Contentful::Configuration.new
|
53
|
+
}
|
54
|
+
|
44
55
|
include_examples 'basic store'
|
45
56
|
include_examples 'operators', feature_set[:operators]
|
46
57
|
include_examples 'supports nested queries', feature_set[:nested_queries]
|
47
58
|
include_examples 'supports include param', feature_set[:include_param]
|
59
|
+
include_examples 'supports locales in queries', feature_set[:locale_queries]
|
48
60
|
end
|
49
61
|
|
50
62
|
# rubocop:enable Style/BlockDelimiters
|
@@ -138,7 +138,7 @@ module WCC::Contentful
|
|
138
138
|
# This job uses the Contentful Sync API to update the configured store with
|
139
139
|
# the latest data from Contentful.
|
140
140
|
class Job < ActiveJob::Base
|
141
|
-
self.queue_adapter
|
141
|
+
self.queue_adapter ||= :async
|
142
142
|
queue_as :default
|
143
143
|
|
144
144
|
def configuration
|
@@ -24,7 +24,7 @@ module WCC::Contentful::Test::Factory
|
|
24
24
|
|
25
25
|
raw_value = v
|
26
26
|
raw_value = to_raw(v, field.type) if %i[Asset Link].include?(field.type)
|
27
|
-
raw['fields'][field.name]
|
27
|
+
raw['fields'][field.name] = raw_value
|
28
28
|
end
|
29
29
|
|
30
30
|
instance = const.new(raw, context)
|
@@ -84,9 +84,7 @@ module WCC::Contentful::Test::Factory
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def contentful_fields(model)
|
87
|
-
WCC::Contentful::Test::Attributes.defaults(model)
|
88
|
-
{ 'en-US' => v }
|
89
|
-
end
|
87
|
+
WCC::Contentful::Test::Attributes.defaults(model)
|
90
88
|
end
|
91
89
|
|
92
90
|
def to_raw(val, field_type)
|
data/lib/wcc/contentful.rb
CHANGED
@@ -10,6 +10,7 @@ require 'wcc/contentful/configuration'
|
|
10
10
|
require 'wcc/contentful/downloads_schema'
|
11
11
|
require 'wcc/contentful/exceptions'
|
12
12
|
require 'wcc/contentful/helpers'
|
13
|
+
require 'wcc/contentful/entry_locale_transformer'
|
13
14
|
require 'wcc/contentful/link_visitor'
|
14
15
|
require 'wcc/contentful/services'
|
15
16
|
require 'wcc/contentful/simple_client'
|
@@ -40,9 +41,8 @@ module WCC::Contentful
|
|
40
41
|
end
|
41
42
|
|
42
43
|
# Gets all queryable locales.
|
43
|
-
# Reserved for future use.
|
44
44
|
def locales
|
45
|
-
|
45
|
+
configuration&.locale_fallbacks
|
46
46
|
end
|
47
47
|
|
48
48
|
def logger
|
@@ -93,15 +93,18 @@ module WCC::Contentful
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
|
96
|
+
schema =
|
97
97
|
begin
|
98
|
-
JSON.parse(File.read(configuration.schema_file))
|
98
|
+
JSON.parse(File.read(configuration.schema_file)) if File.exist?(configuration.schema_file)
|
99
99
|
rescue JSON::ParserError
|
100
100
|
Services.instance.warn("Schema file invalid, ignoring it: #{configuration.schema_file}")
|
101
101
|
nil
|
102
102
|
end
|
103
103
|
|
104
|
-
|
104
|
+
content_types = schema['contentTypes'] if schema
|
105
|
+
locales = schema['locales'] if schema
|
106
|
+
|
107
|
+
if !schema && %i[if_possible never].include?(configuration.update_schema_file)
|
105
108
|
# Final fallback - try to grab content types from CDN. We can't update the file
|
106
109
|
# because the CDN doesn't have all the field validation info, but we can at least
|
107
110
|
# build the WCC::Contentful::Model instances.
|
@@ -127,7 +130,15 @@ module WCC::Contentful
|
|
127
130
|
services: WCC::Contentful::Services.instance
|
128
131
|
)
|
129
132
|
|
130
|
-
#
|
133
|
+
# Update the locale fallbacks from the schema file, unless they have already
|
134
|
+
# been configured.
|
135
|
+
locales&.each do |locale_hash|
|
136
|
+
next if @configuration.locale_fallbacks[locale_hash['code']]
|
137
|
+
|
138
|
+
@configuration.locale_fallbacks[locale_hash['code']] = locale_hash['fallbackCode']
|
139
|
+
end
|
140
|
+
|
141
|
+
# Enqueue an initial sync
|
131
142
|
WCC::Contentful::SyncEngine::Job.perform_later if defined?(WCC::Contentful::SyncEngine::Job)
|
132
143
|
|
133
144
|
@configuration = @configuration.freeze
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wcc-contentful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Watermark Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -429,6 +429,7 @@ files:
|
|
429
429
|
- lib/wcc/contentful/content_type_indexer.rb
|
430
430
|
- lib/wcc/contentful/downloads_schema.rb
|
431
431
|
- lib/wcc/contentful/engine.rb
|
432
|
+
- lib/wcc/contentful/entry_locale_transformer.rb
|
432
433
|
- lib/wcc/contentful/event.rb
|
433
434
|
- lib/wcc/contentful/events.rb
|
434
435
|
- lib/wcc/contentful/exceptions.rb
|
@@ -440,6 +441,7 @@ files:
|
|
440
441
|
- lib/wcc/contentful/middleware.rb
|
441
442
|
- lib/wcc/contentful/middleware/store.rb
|
442
443
|
- lib/wcc/contentful/middleware/store/caching_middleware.rb
|
444
|
+
- lib/wcc/contentful/middleware/store/locale_middleware.rb
|
443
445
|
- lib/wcc/contentful/model.rb
|
444
446
|
- lib/wcc/contentful/model_api.rb
|
445
447
|
- lib/wcc/contentful/model_builder.rb
|
@@ -469,10 +471,12 @@ files:
|
|
469
471
|
- lib/wcc/contentful/store/postgres_store/schema_1.sql
|
470
472
|
- lib/wcc/contentful/store/postgres_store/schema_2.sql
|
471
473
|
- lib/wcc/contentful/store/query.rb
|
474
|
+
- lib/wcc/contentful/store/query/condition.rb
|
472
475
|
- lib/wcc/contentful/store/query/interface.rb
|
473
476
|
- lib/wcc/contentful/store/rspec_examples.rb
|
474
477
|
- lib/wcc/contentful/store/rspec_examples/basic_store.rb
|
475
478
|
- lib/wcc/contentful/store/rspec_examples/include_param.rb
|
479
|
+
- lib/wcc/contentful/store/rspec_examples/locale_queries.rb
|
476
480
|
- lib/wcc/contentful/store/rspec_examples/nested_queries.rb
|
477
481
|
- lib/wcc/contentful/store/rspec_examples/operators.rb
|
478
482
|
- lib/wcc/contentful/store/rspec_examples/operators/eq.rb
|
@@ -491,7 +495,7 @@ homepage: https://github.com/watermarkchurch/wcc-contentful/wcc-contentful
|
|
491
495
|
licenses:
|
492
496
|
- MIT
|
493
497
|
metadata:
|
494
|
-
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.
|
498
|
+
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.4/wcc-contentful
|
495
499
|
rubygems_mfa_required: 'true'
|
496
500
|
post_install_message:
|
497
501
|
rdoc_options: []
|
@@ -504,9 +508,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
504
508
|
version: '2.7'
|
505
509
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
506
510
|
requirements:
|
507
|
-
- - "
|
511
|
+
- - ">"
|
508
512
|
- !ruby/object:Gem::Version
|
509
|
-
version:
|
513
|
+
version: 1.3.1
|
510
514
|
requirements: []
|
511
515
|
rubygems_version: 3.3.7
|
512
516
|
signing_key:
|
@@ -526,7 +530,7 @@ summary: '[](https://
|
|
526
530
|
Example](#advanced-configuration-example) 8. [Connecting to Multiple Spaces](#connecting-to-multiple-spaces-or-environments)
|
527
531
|
9. [Development](#development) 10. [Contributing](#contributing) 11. [License](#license) ##
|
528
532
|
Why did you rewrite the Contentful ruby stack? We started working with Contentful
|
529
|
-
almost
|
533
|
+
almost 5 years ago. Since that time, Contentful''s ruby stack has improved, but
|
530
534
|
there are still a number of pain points that we feel we have addressed better with
|
531
535
|
our gem. These are: * [Low-level caching](#low-level-caching) * [Better integration
|
532
536
|
with Rails & Rails models](#better-rails-integration) * [Automatic pagination and
|
@@ -620,19 +624,22 @@ summary: '[](https://
|
|
620
624
|
data as a set of dynamically generated Ruby objects. These objects are based on
|
621
625
|
the content types in your Contentful space. All these objects are generated by
|
622
626
|
`WCC::Contentful.init!` The following examples show how to use this API to find
|
623
|
-
entries of the `page` content type: ```ruby #
|
624
|
-
#
|
625
|
-
UTC...> # Find objects
|
626
|
-
# => #<
|
627
|
-
UTC...> # Use operators to filter by a field # must use full notation
|
628
|
-
(except ID)
|
629
|
-
}) # => [#<
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
627
|
+
entries of the `page` content type: ```ruby # app/models/page.rb class Page < WCC::Contentful::Model::Page #
|
628
|
+
You can add additional methods here end # Find objects by id Page.find(''1E2ucWSdacxxf233sfa3'')
|
629
|
+
# => #<Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17 UTC...> # Find objects
|
630
|
+
by field Page.find_by(slug: ''/some-slug'') # => #<Page:0x0000000005c71a78 @created_at=2018-04-16
|
631
|
+
18:41:17 UTC...> # Use operators to filter by a field # must use full notation
|
632
|
+
for sys attributes (except ID) Page.find_all(''sys.created_at'' => { lte: Date.today
|
633
|
+
}) # => [#<Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17 UTC...>, ...
|
634
|
+
] # Nest queries to mimick joins Page.find_by(subpages: { slug: ''/some-slug''
|
635
|
+
}) # => #<Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17 UTC...> # Fetch
|
636
|
+
an entry in a different locale spanish_homepage = Page.find_by(slug: ''/'', options:
|
637
|
+
{ locale: ''es-US'' }) # => #<Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17
|
638
|
+
UTC...> spanish_homepage.title # => Esta es la página principal # Pass the preview
|
639
|
+
flag to use the preview client (must have set preview_token config param) preview_redirect
|
640
|
+
= WCC::Contentful::Model::Redirect.find_by({ slug: ''draft-redirect'' }, preview:
|
641
|
+
true) # => #<WCC::Contentful::Model::Redirect:0x0000000005d879ad @created_at=2018-04-16
|
642
|
+
18:41:17 UTC...> preview_redirect_object.href # => ''http://www.somesite.com/slug-for-redirect''
|
636
643
|
``` See the {WCC::Contentful::Model} documentation for more details. ### Store
|
637
644
|
API The Store layer is used by the Model API to access Contentful data in a raw
|
638
645
|
form. The Store layer returns entries as hashes parsed from JSON, conforming to
|
@@ -643,12 +650,20 @@ summary: '[](https://
|
|
643
650
|
filter: { slug: ''/some-slug'' }) # => {"sys"=> # ... # "fields"=> # ...} query
|
644
651
|
= store.find_all(content_type: ''page'').eq(''group'', ''some-group'') # => #<WCC::Contentful::Store::CDNAdapter::Query:0x00007fa3d40b84f0
|
645
652
|
query.first # => {"sys"=> # ... # "fields"=> # ...} query.result # => #<Enumerator::Lazy:
|
646
|
-
...> query.result.force # => [{"sys"=> ...}, {"sys"=> ...}, ...] ```
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
to
|
653
|
+
...> query.result.force # => [{"sys"=> ...}, {"sys"=> ...}, ...] ``` The store
|
654
|
+
layer, while superficially similar to the Contentful API, tries to present a different
|
655
|
+
"View" over the data which is more compatible with the Model layer. It resolves
|
656
|
+
includes by actually replacing the in-memory `Link` objects with their linked `Entry`
|
657
|
+
representations. This lets you traverse the links naturally using `#dig` or `#[]`: ```ruby
|
658
|
+
# Include to a depth of 3 to make sure it''s included homepage = store.find_by(slug:
|
659
|
+
''/'', include: 3) # Traverse through the top nav menu => menu button 0 => about
|
660
|
+
page about_page = homepage.dig(''fields'', ''nav_menu'', ''fields'', ''buttons'',
|
661
|
+
0, ''fields'', ''page'') ``` See the {WCC::Contentful::Store} documentation for
|
662
|
+
more details. ### Direct CDN API (SimpleClient) The SimpleClient is the bottom
|
663
|
+
layer, and is used to get raw data directly from the Contentful CDN. It handles
|
664
|
+
response parsing and paging, but does not resolve links or transform the result
|
665
|
+
into a Model class. The following examples show how to use the SimpleClient to
|
666
|
+
retrieve data directly from the Contentful CDN: ```ruby client = WCC::Contentful::Services.instance.client
|
652
667
|
# => #<WCC::Contentful::SimpleClient::Cdn:0x00007fa3cde89310 response = client.entry(''5FsqsbMECsM62e04U8sY4Y'')
|
653
668
|
# => #<WCC::Contentful::SimpleClient::Response:0x00007fa3d103a4e0 response.body
|
654
669
|
# => "{\n \"sys\": {\n ... response.raw # => {"sys"=> # ... # "fields"=> # ...} client.asset(''5FsqsbMECsM62e04U8sY4Y'').raw
|
@@ -704,27 +719,44 @@ summary: '[](https://
|
|
704
719
|
example, the {WCC::Contentful::Store::MemoryStore} uses this to update the hash
|
705
720
|
with the newest version of an entry, or delete an entry out of the hash. #### Store
|
706
721
|
Middleware The store layer is made up of a base store (which implements {WCC::Contentful::Store::Interface}),
|
707
|
-
and
|
708
|
-
|
709
|
-
simply include {WCC::Contentful::Middleware::Store}
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
722
|
+
and some required middleware. The list of default middleware applied to each store
|
723
|
+
is found in {WCC::Contentful::Store::Factory.default_middleware} To create your
|
724
|
+
own middleware simply include {WCC::Contentful::Middleware::Store}. Then you can
|
725
|
+
optionally implement the `#transform` and `#select?` methods: ```ruby class MyMiddleware
|
726
|
+
include WCC::Contentful::Middleware::Store # Called for each entry that is requested
|
727
|
+
out of the backing store. You can modify the entry and return it to the # next
|
728
|
+
layer. def transform(entry, options) # Do something with the entry... # Make sure
|
729
|
+
you return it at the end! entry end def select?(entry, options) # Choose whether
|
730
|
+
this entry should exist or not. If you return false here, then the entry will act
|
731
|
+
as though it # were archived in Contentful. entry.dig(''fields'', ''hide_until'')
|
732
|
+
> Time.zone.now end end ``` You can also override any of the standard Store methods. To
|
733
|
+
apply the middleware, call `use` when configuring the store: ```ruby config.store
|
734
|
+
:direct do use MyMiddleware, param1: ''xxx'' end ``` The most useful middleware
|
735
|
+
is the {WCC::Contentful::Middleware::Store::CachingMiddleware}, which enables `:lazy_sync`
|
736
|
+
mode (see {WCC::Contentful::Configuration#store}) ### Model Layer This is the
|
737
|
+
global top layer where your Rails app looks up content similarly to ActiveModel. The
|
738
|
+
models are namespaced under the root class {WCC::Contentful::Model}. Each model''s
|
739
|
+
implementation of `.find`, `.find_by`, and `.find_all` simply call into the configured
|
740
|
+
Store. Models can be initialized directly with the `.new` method, by passing in
|
741
|
+
a hash: ```ruby entry = { ''sys'' => ..., ''fields'' => ... } Page.new(entry) ``` **The
|
742
|
+
initializer must receive a localized entry**. An entry found using a `locale=*`
|
743
|
+
query must be transformed to a localized entry using the {WCC::Contentful::EntryLocaleTransformer}
|
744
|
+
before passing it to your model: ```ruby entry = client.entry(''1234'', locale:
|
745
|
+
''*'').raw localized_entry = WCC::Contentful::EntryLocaleTransformer.transform_to_locale(entry,
|
746
|
+
''en-US'') Page.new(localized_entry) ``` The Store layer ensures that localized
|
747
|
+
entries are returned using the {WCC::Contentful::Middleware::Store::LocaleMiddleware}. The
|
748
|
+
main benefit of the Model layer is lazy link resolution. When a model''s property
|
749
|
+
is accessed, if that property is a link that has not been resolved yet (for example
|
750
|
+
using the `include: n` parameter on `.find_by`), the model will automatically call
|
751
|
+
`#find` on the store to resolve that linked entry. Note that this can easily result
|
752
|
+
in lots of CDN calls to Contentful! To optimize this you should use the `include`
|
753
|
+
parameter and/or use a different store. ## Test Helpers To use the test helpers,
|
754
|
+
include the following in your rails_helper.rb: ```ruby require ''wcc/contentful/rspec''
|
755
|
+
``` This adds the following helpers to all your specs: ```ruby ## # Builds a in-memory
|
756
|
+
instance of the Contentful model for the given content_type. # All attributes that
|
757
|
+
are known to be required fields on the content type # will return a default value
|
758
|
+
based on the field type. instance = contentful_create(''my-content-type'', my_field:
|
759
|
+
''some-value'') # => #<WCC::Contentful::Model::MyContentType:0x0000000005c71a78
|
728
760
|
@created_at=2018-04-16 18:41:17 UTC...> instance.my_field # => "some-value" instance.other_required_field
|
729
761
|
# => "default-value" instance.other_optional_field # => nil instance.not_a_field
|
730
762
|
# NoMethodError: undefined method `not_a_field'' for #<MyContentType:0x00007fbac81ee490> ##
|