wcc-contentful 1.3.2 → 1.4.0.rc2

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -14
  3. data/app/jobs/wcc/contentful/webhook_enable_job.rb +0 -1
  4. data/lib/wcc/contentful/configuration.rb +12 -5
  5. data/lib/wcc/contentful/downloads_schema.rb +14 -2
  6. data/lib/wcc/contentful/entry_locale_transformer.rb +107 -0
  7. data/lib/wcc/contentful/exceptions.rb +5 -0
  8. data/lib/wcc/contentful/link_visitor.rb +12 -1
  9. data/lib/wcc/contentful/middleware/store/caching_middleware.rb +34 -10
  10. data/lib/wcc/contentful/middleware/store/locale_middleware.rb +30 -0
  11. data/lib/wcc/contentful/middleware/store.rb +20 -16
  12. data/lib/wcc/contentful/model_builder.rb +10 -3
  13. data/lib/wcc/contentful/model_methods.rb +4 -6
  14. data/lib/wcc/contentful/simple_client/cdn.rb +5 -2
  15. data/lib/wcc/contentful/simple_client/management.rb +16 -0
  16. data/lib/wcc/contentful/simple_client.rb +1 -2
  17. data/lib/wcc/contentful/store/base.rb +6 -1
  18. data/lib/wcc/contentful/store/cdn_adapter.rb +13 -4
  19. data/lib/wcc/contentful/store/factory.rb +8 -1
  20. data/lib/wcc/contentful/store/memory_store.rb +27 -8
  21. data/lib/wcc/contentful/store/postgres_store.rb +4 -3
  22. data/lib/wcc/contentful/store/query/condition.rb +89 -0
  23. data/lib/wcc/contentful/store/query.rb +9 -35
  24. data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +84 -12
  25. data/lib/wcc/contentful/store/rspec_examples/locale_queries.rb +220 -0
  26. data/lib/wcc/contentful/store/rspec_examples/operators/eq.rb +1 -1
  27. data/lib/wcc/contentful/store/rspec_examples.rb +13 -1
  28. data/lib/wcc/contentful/sync_engine.rb +1 -1
  29. data/lib/wcc/contentful/test/double.rb +1 -1
  30. data/lib/wcc/contentful/test/factory.rb +2 -4
  31. data/lib/wcc/contentful/version.rb +1 -1
  32. data/lib/wcc/contentful.rb +17 -6
  33. metadata +80 -48
@@ -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' => { 'name' => { 'en-US' => [generator.call, generator.call] } }
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 = :async
141
+ self.queue_adapter ||= :async
142
142
  queue_as :default
143
143
 
144
144
  def configuration
@@ -60,7 +60,7 @@ module WCC::Contentful::Test::Double
60
60
  revision: rand(100),
61
61
  locale: 'en-US'
62
62
  },
63
- fields: attrs.transform_values { |v| { 'en-US' => v } }
63
+ fields: attrs
64
64
  }
65
65
 
66
66
  double(attrs)
@@ -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][raw.dig('sys', 'locale')] = raw_value
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).transform_values do |v|
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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '1.3.2'
5
+ VERSION = '1.4.0.rc2'
6
6
  end
7
7
  end
@@ -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
- @locales ||= { 'en-US' => {} }.freeze
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
- content_types =
96
+ schema =
97
97
  begin
98
- JSON.parse(File.read(configuration.schema_file))['contentTypes'] if File.exist?(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
- if !content_types && %i[if_possible never].include?(configuration.update_schema_file)
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
- # Drop an initial sync
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.3.2
4
+ version: 1.4.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watermark Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-18 00:00:00.000000000 Z
11
+ date: 2023-04-27 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.3/wcc-contentful
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: '0'
513
+ version: 1.3.1
510
514
  requirements: []
511
515
  rubygems_version: 3.3.7
512
516
  signing_key:
@@ -526,7 +530,7 @@ summary: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](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 3 years ago. Since that time, Contentful''s ruby stack has improved, but
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: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](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 # Find objects by id WCC::Contentful::Model::Page.find(''1E2ucWSdacxxf233sfa3'')
624
- # => #<WCC::Contentful::Model::Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17
625
- UTC...> # Find objects by field WCC::Contentful::Model::Page.find_by(slug: ''/some-slug'')
626
- # => #<WCC::Contentful::Model::Page:0x0000000005c71a78 @created_at=2018-04-16 18:41:17
627
- UTC...> # Use operators to filter by a field # must use full notation for sys attributes
628
- (except ID) WCC::Contentful::Model::Page.find_all(''sys.created_at'' => { lte: Date.today
629
- }) # => [#<WCC::Contentful::Model::Page:0x0000000005c71a78 @created_at=2018-04-16
630
- 18:41:17 UTC...>, ... ] # Nest queries to mimick joins WCC::Contentful::Model::Page.find_by(subpages:
631
- { slug: ''/some-slug'' }) # => #<WCC::Contentful::Model::Page:0x0000000005c71a78
632
- @created_at=2018-04-16 18:41:17 UTC...> # Pass the preview flag to use the preview
633
- client (must have set preview_token config param) preview_redirect = WCC::Contentful::Model::Redirect.find_by({
634
- slug: ''draft-redirect'' }, preview: true) # => #<WCC::Contentful::Model::Redirect:0x0000000005d879ad
635
- @created_at=2018-04-16 18:41:17 UTC...> preview_redirect_object.href # => ''http://www.somesite.com/slug-for-redirect''
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: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](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"=> ...}, ...] ``` See the {WCC::Contentful::Store}
647
- documentation for more details. ### Direct CDN API (SimpleClient) The SimpleClient
648
- is the bottom layer, and is used to get raw data directly from the Contentful CDN. It
649
- handles response parsing and paging, but does not resolve links or transform the
650
- result into a Model class. The following examples show how to use the SimpleClient
651
- to retrieve data directly from the Contentful CDN: ```ruby client = WCC::Contentful::Services.instance.client
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
@@ -683,8 +698,8 @@ summary: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](https://
683
698
  credentials, or to connect without setting up all the rest of WCC::Contentful, is
684
699
  easy: ```ruby WCC::Contentful::SimpleClient::Cdn.new( # required access_token:
685
700
  ''xxxx'', space: ''1234'', # optional environment: ''staging'', # omit to use master
686
- default_locale: ''*'', rate_limit_wait_timeout: 10, instrumentation: ActiveSupport::Notifications,
687
- connection: Faraday.new { |builder| ... }, ) ``` You can also create a {WCC::Contentful::SimpleClient::Preview}
701
+ rate_limit_wait_timeout: 10, instrumentation: ActiveSupport::Notifications, connection:
702
+ Faraday.new { |builder| ... }, ) ``` You can also create a {WCC::Contentful::SimpleClient::Preview}
688
703
  to talk to the Preview API, or a {WCC::Contentful::SimpleClient::Management} to
689
704
  talk to the Management API. ### Store Layer The Store Layer represents the data
690
705
  store where Contentful entries are kept for querying. By default, `WCC::Contentful.init!`
@@ -704,27 +719,44 @@ summary: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](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 optional middleware. The middleware allows custom transformation of received
708
- entries via the `#select` and `#transform` methods. To create your own middleware
709
- simply include {WCC::Contentful::Middleware::Store} and implement those methods,
710
- then call `use` when configuring the store: ```ruby config.store :direct do use
711
- MyMiddleware, param1: ''xxx'' end ``` The most useful middleware is the {WCC::Contentful::Middleware::Store::CachingMiddleware},
712
- which enables `:lazy_sync` mode (see {WCC::Contentful::Configuration#store}) ###
713
- Model Layer This is the global top layer where your Rails app looks up content
714
- similarly to ActiveModel. The models are namespaced under the root class {WCC::Contentful::Model}.
715
- Each model''s implementation of `.find`, `.find_by`, and `.find_all` simply call
716
- into the configured Store. The main benefit of the Model layer is lazy link resolution. When
717
- a model''s property is accessed, if that property is a link that has not been resolved
718
- yet (for example using the `include: n` parameter on `.find_by`), the model will
719
- automatically call `#find` on the store to resolve that linked entry. Note that
720
- this can easily result in lots of CDN calls to Contentful! To optimize this you
721
- should use the `include` parameter and/or use a different store. ## Test Helpers To
722
- use the test helpers, include the following in your rails_helper.rb: ```ruby require
723
- ''wcc/contentful/rspec'' ``` This adds the following helpers to all your specs: ```ruby
724
- ## # Builds a in-memory instance of the Contentful model for the given content_type.
725
- # All attributes that are known to be required fields on the content type # will
726
- return a default value based on the field type. instance = contentful_create(''my-content-type'',
727
- my_field: ''some-value'') # => #<WCC::Contentful::Model::MyContentType:0x0000000005c71a78
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> ##