wcc-contentful 1.3.2 → 1.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -13
  3. data/lib/wcc/contentful/active_record_shim.rb +2 -2
  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 +25 -8
  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 +9 -2
  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 -0
  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 +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' => { '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.rc1'
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.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: 2023-04-18 00:00:00.000000000 Z
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.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
@@ -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> ##