wcc-contentful 1.1.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3facd27f1d4401d01c3e42dc9ab11d75652cc6859a74c5c74ffb58647b13c28d
4
- data.tar.gz: 48072021ae935b5c7d9a684f488dafb3ffa53b94ea153bfeeba7f1b5f5a46806
3
+ metadata.gz: f05ea585d54f7ca83c96e018021f88776ebb148cc40b7d719d6199b3a3cd9d0c
4
+ data.tar.gz: 0da490d12beb9938ec51c0ac40e140420966667b72afebc5c9895cf197308ea8
5
5
  SHA512:
6
- metadata.gz: b884394e95a3c722c8a3a1327780700f153f9e724c920d9d57e81409d836e5111777f3493b504bfe7b6321a1d83ed95f6fe2613b9f77677393d2caab631caa4e
7
- data.tar.gz: 6891f2e8a291d4a72c725ce09a7270681ed8e2f7485d2f68d195f6fc0f55005e07408855f194d1ac130fe3c874c59c3bdc4e5aa87b512b776c37be792dd5e605
6
+ metadata.gz: 979360a35356b041dbe0f8bbd4f0e5bfcaf91909ccc567322ceef8493de25300ea09a081e755142fe23bf9cfd5ea86ca7f919d3218d5dec44a98bcab5921af7d
7
+ data.tar.gz: 9e5f29e70fdcbccffd72aeb8b896e3518d2236cd699704289e8a3fdf1392201dc49671bab0eb83948e79ee079366de90b46add4ce2027e065db2da28b2be4e85
@@ -11,7 +11,7 @@ module WCC::Contentful
11
11
  args = default_configuration.merge!(args)
12
12
 
13
13
  client = WCC::Contentful::SimpleClient::Management.new(
14
- args
14
+ **args
15
15
  )
16
16
  enable_webhook(client, args.slice(:receive_url, :webhook_username, :webhook_password))
17
17
  end
@@ -64,13 +64,13 @@ module WCC::Contentful::ActiveRecordShim
64
64
  }
65
65
  }
66
66
 
67
- find_all(filter).each_slice(batch_size, &block)
67
+ find_all(**filter).each_slice(batch_size, &block)
68
68
  end
69
69
 
70
70
  def where(**conditions)
71
71
  # TODO: return a Query object that implements more of the ActiveRecord query interface
72
72
  # https://guides.rubyonrails.org/active_record_querying.html#conditions
73
- find_all(conditions)
73
+ find_all(**conditions)
74
74
  end
75
75
  end
76
76
  end
@@ -137,6 +137,8 @@ module WCC::Contentful
137
137
  :Json
138
138
  when 'Location'
139
139
  :Coordinates
140
+ when 'RichText'
141
+ :RichText
140
142
  when 'Array'
141
143
  find_field_type(field.try(:items) || field['items'])
142
144
  when 'Link'
@@ -7,10 +7,10 @@ module WCC::Contentful::Event
7
7
 
8
8
  # Creates an Event out of a raw value received by a webhook or given from
9
9
  # the Contentful Sync API.
10
- def self.from_raw(raw, context = nil)
10
+ def self.from_raw(raw, context = nil, source: nil)
11
11
  const = Registry.instance.get(raw.dig('sys', 'type'))
12
12
 
13
- const.new(raw, context)
13
+ const.new(raw, context, source: source)
14
14
  end
15
15
 
16
16
  class Registry
@@ -134,12 +134,6 @@ class WCC::Contentful::Event::SyncComplete
134
134
  include WCC::Contentful::Event
135
135
 
136
136
  def initialize(items, context = nil, source: nil)
137
- items =
138
- items.map do |item|
139
- next item if item.is_a? WCC::Contentful::Event
140
-
141
- WCC::Contentful::Event.from_raw(item, context, source: source)
142
- end
143
137
  @items = items.freeze
144
138
  @source = source
145
139
  @sys = WCC::Contentful::Sys.new(
@@ -103,6 +103,7 @@ module WCC::Contentful
103
103
  Boolean
104
104
  Json
105
105
  Coordinates
106
+ RichText
106
107
  Link
107
108
  Asset
108
109
  ].freeze
@@ -20,7 +20,7 @@ module WCC::Contentful::Middleware::Store
20
20
  event = 'miss'
21
21
  # if it's not a contentful ID don't hit the API.
22
22
  # Store a nil object if we can't find the object on the CDN.
23
- (store.find(key, options) || nil_obj(key)) if key =~ /^\w+$/
23
+ (store.find(key, **options) || nil_obj(key)) if key =~ /^\w+$/
24
24
  end
25
25
  _instrument(event, key: key, options: options)
26
26
 
@@ -68,7 +68,7 @@ module WCC::Contentful::ModelAPI
68
68
  store = options[:preview] ? services.preview_store : services.store
69
69
  raw =
70
70
  _instrumentation.instrument 'find.model.contentful.wcc', id: id, options: options do
71
- store.find(id, options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
71
+ store.find(id, **options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
72
72
  end
73
73
 
74
74
  new_from_raw(raw, options) if raw.present?
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative './link'
4
4
  require_relative './sys'
5
+ require_relative './rich_text'
5
6
 
6
7
  module WCC::Contentful
7
8
  class ModelBuilder
@@ -95,6 +96,8 @@ module WCC::Contentful
95
96
  #
96
97
  # when :DateTime
97
98
  # raw_value = Time.parse(raw_value).localtime
99
+ when :RichText
100
+ raw_value = WCC::Contentful::RichText.tokenize(raw_value)
98
101
  when :Int
99
102
  raw_value = Integer(raw_value)
100
103
  when :Float
@@ -16,7 +16,7 @@ module WCC::Contentful::ModelSingletonMethods
16
16
  raw =
17
17
  _instrumentation.instrument 'find.model.contentful.wcc',
18
18
  content_type: content_type, id: id, options: options do
19
- store.find(id, { hint: type }.merge!(options.except(:preview)))
19
+ store.find(id, **{ hint: type }.merge!(options.except(:preview)))
20
20
  end
21
21
  new(raw, options) if raw.present?
22
22
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::RichText
4
+ module Node
5
+ extend ActiveSupport::Concern
6
+
7
+ def keys
8
+ members.map(&:to_s)
9
+ end
10
+
11
+ included do
12
+ include Enumerable
13
+
14
+ alias_method :node_type, :nodeType
15
+
16
+ # Make the nodes read-only
17
+ undef_method :[]=
18
+ members.each do |member|
19
+ undef_method("#{member}=")
20
+ end
21
+
22
+ # Override each so it has a Hash-like interface rather than Struct-like.
23
+ # The goal being to mimic a JSON-parsed hash representation of the raw
24
+ def each
25
+ members.map do |key|
26
+ tuple = [key.to_s, self.[](key)]
27
+ yield tuple if block_given?
28
+ tuple
29
+ end
30
+ end
31
+ end
32
+
33
+ class_methods do
34
+ # Default value for node_type covers most cases
35
+ def node_type
36
+ name.demodulize.underscore.dasherize
37
+ end
38
+
39
+ def tokenize(raw, context = nil)
40
+ unless raw['nodeType'] == node_type
41
+ raise ArgumentError, "Expected '#{node_type}', got '#{raw['nodeType']}'"
42
+ end
43
+
44
+ values =
45
+ members.map do |symbol|
46
+ val = raw[symbol.to_s]
47
+
48
+ case symbol
49
+ when :content
50
+ WCC::Contentful::RichText.tokenize(val, context)
51
+ # when :data
52
+ # TODO: resolve links...
53
+ else
54
+ val
55
+ end
56
+ end
57
+
58
+ new(*values)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './rich_text/node'
4
+
5
+ ##
6
+ # This module contains a number of structs representing nodes in a Contentful
7
+ # rich text field. When the Model layer parses a Rich Text field from
8
+ # Contentful, it is turned into a WCC::Contentful::RichText::Document node.
9
+ # The {WCC::Contentful::RichText::Document#content content} method of this
10
+ # node is an Array containing paragraph, blockquote, entry, and other nodes.
11
+ #
12
+ # The various structs in the RichText object model are designed to mimic the
13
+ # Hash interface, so that the indexing operator `#[]` and the `#dig` method
14
+ # can be used to traverse the data. The data can also be accessed by the
15
+ # attribute reader methods defined on the structs. Both of these are considered
16
+ # part of the public API of the model and will not change.
17
+ #
18
+ # In a future release we plan to implement automatic link resolution. When that
19
+ # happens, the `.data` attribute of embedded entries and assets will return a
20
+ # new class that is able to resolve the `.target` automatically into a full
21
+ # entry or asset. This future class will still respect the hash accessor methods
22
+ # `#[]`, `#dig`, `#keys`, and `#each`, so it is safe to use those.
23
+ module WCC::Contentful::RichText
24
+ ##
25
+ # Recursively converts a raw JSON-parsed hash into the RichText object model.
26
+ def self.tokenize(raw, context = nil)
27
+ return unless raw
28
+ return raw.map { |c| tokenize(c, context) } if raw.is_a?(Array)
29
+
30
+ klass =
31
+ case raw['nodeType']
32
+ when 'document'
33
+ Document
34
+ when 'paragraph'
35
+ Paragraph
36
+ when 'blockquote'
37
+ Blockquote
38
+ when 'text'
39
+ Text
40
+ when 'embedded-entry-inline'
41
+ EmbeddedEntryInline
42
+ when 'embedded-entry-block'
43
+ EmbeddedEntryBlock
44
+ when 'embedded-asset-block'
45
+ EmbeddedAssetBlock
46
+ when /heading\-(\d+)/
47
+ size = Regexp.last_match(1)
48
+ const_get("Heading#{size}")
49
+ else
50
+ Unknown
51
+ end
52
+
53
+ klass.tokenize(raw, context)
54
+ end
55
+
56
+ Document =
57
+ Struct.new(:nodeType, :data, :content) do
58
+ include WCC::Contentful::RichText::Node
59
+ end
60
+
61
+ Paragraph =
62
+ Struct.new(:nodeType, :data, :content) do
63
+ include WCC::Contentful::RichText::Node
64
+ end
65
+
66
+ Blockquote =
67
+ Struct.new(:nodeType, :data, :content) do
68
+ include WCC::Contentful::RichText::Node
69
+ end
70
+
71
+ Text =
72
+ Struct.new(:nodeType, :value, :marks, :data) do
73
+ include WCC::Contentful::RichText::Node
74
+ end
75
+
76
+ EmbeddedEntryInline =
77
+ Struct.new(:nodeType, :data, :content) do
78
+ include WCC::Contentful::RichText::Node
79
+ end
80
+
81
+ EmbeddedEntryBlock =
82
+ Struct.new(:nodeType, :data, :content) do
83
+ include WCC::Contentful::RichText::Node
84
+ end
85
+
86
+ EmbeddedAssetBlock =
87
+ Struct.new(:nodeType, :data, :content) do
88
+ include WCC::Contentful::RichText::Node
89
+ end
90
+
91
+ (1..5).each do |i|
92
+ struct =
93
+ Struct.new(:nodeType, :data, :content) do
94
+ include WCC::Contentful::RichText::Node
95
+ end
96
+ sz = i
97
+ struct.define_singleton_method(:node_type) { "heading-#{sz}" }
98
+ const_set("Heading#{sz}", struct)
99
+ end
100
+
101
+ Unknown =
102
+ Struct.new(:nodeType, :data, :content) do
103
+ include WCC::Contentful::RichText::Node
104
+ end
105
+ end
@@ -49,7 +49,6 @@ class WCC::Contentful::SimpleClient
49
49
 
50
50
  def next_page
51
51
  return unless next_page?
52
- return @next_page if @next_page
53
52
 
54
53
  query = (@request[:query] || {}).merge({
55
54
  skip: page_items.length + skip
@@ -58,7 +57,7 @@ class WCC::Contentful::SimpleClient
58
57
  _instrument 'page', url: @request[:url], query: query do
59
58
  @client.get(@request[:url], query)
60
59
  end
61
- @next_page = np.assert_ok!
60
+ np.assert_ok!
62
61
  end
63
62
 
64
63
  def initialize(client, request, raw_response)
@@ -77,16 +76,7 @@ class WCC::Contentful::SimpleClient
77
76
  def each_page(&block)
78
77
  raise ArgumentError, 'Not a collection response' unless page_items
79
78
 
80
- ret =
81
- Enumerator.new do |y|
82
- y << self
83
-
84
- if next_page?
85
- next_page.each_page.each do |page|
86
- y << page
87
- end
88
- end
89
- end
79
+ ret = PaginatingEnumerable.new(self)
90
80
 
91
81
  if block_given?
92
82
  ret.map(&block)
@@ -118,11 +108,6 @@ class WCC::Contentful::SimpleClient
118
108
  raw.dig('includes')&.each_with_object({}) do |(_t, entries), h|
119
109
  entries.each { |e| h[e.dig('sys', 'id')] = e }
120
110
  end || {}
121
-
122
- return @includes unless @next_page
123
-
124
- # This could be more efficient - maybe not worth worrying about
125
- @includes.merge(@next_page.includes)
126
111
  end
127
112
  end
128
113
 
@@ -144,8 +129,8 @@ class WCC::Contentful::SimpleClient
144
129
  @client.get(url)
145
130
  end
146
131
 
147
- @next_page ||= SyncResponse.new(next_page)
148
- @next_page.assert_ok!
132
+ next_page = SyncResponse.new(next_page)
133
+ next_page.assert_ok!
149
134
  end
150
135
 
151
136
  def next_sync_token
@@ -160,16 +145,7 @@ class WCC::Contentful::SimpleClient
160
145
  def each_page
161
146
  raise ArgumentError, 'Not a collection response' unless page_items
162
147
 
163
- ret =
164
- Enumerator.new do |y|
165
- y << self
166
-
167
- if next_page?
168
- next_page.each_page.each do |page|
169
- y << page
170
- end
171
- end
172
- end
148
+ ret = PaginatingEnumerable.new(self)
173
149
 
174
150
  if block_given?
175
151
  ret.map(&block)
@@ -190,6 +166,26 @@ class WCC::Contentful::SimpleClient
190
166
  end
191
167
  end
192
168
 
169
+ class PaginatingEnumerable
170
+ include Enumerable
171
+
172
+ def initialize(initial_page)
173
+ raise ArgumentError, 'Must provide initial page' unless initial_page.present?
174
+
175
+ @initial_page = initial_page
176
+ end
177
+
178
+ def each
179
+ page = @initial_page
180
+ yield page
181
+
182
+ while page.next_page?
183
+ page = page.next_page
184
+ yield page
185
+ end
186
+ end
187
+ end
188
+
193
189
  class ApiError < StandardError
194
190
  attr_reader :response
195
191
 
@@ -72,9 +72,13 @@ module WCC::Contentful::Store
72
72
  delegate :count, to: :response
73
73
 
74
74
  def to_enum
75
- return response.items unless @options[:include]
75
+ return response.each_page.flat_map(&:page_items) unless @options[:include]
76
76
 
77
- response.items.map { |e| resolve_includes(e, @options[:include]) }
77
+ response.each_page
78
+ .flat_map { |page| page.page_items.each_with_object(page).to_a }
79
+ .map do |e, page|
80
+ resolve_includes(e, page.includes, depth: @options[:include])
81
+ end
78
82
  end
79
83
 
80
84
  def initialize(store, client:, relation:, options: nil, **extra)
@@ -160,18 +164,18 @@ module WCC::Contentful::Store
160
164
  end
161
165
  end
162
166
 
163
- def resolve_includes(entry, depth)
167
+ def resolve_includes(entry, includes, depth:)
164
168
  return entry unless entry && depth && depth > 0
165
169
 
166
170
  # Dig links out of response.includes and insert them into the entry
167
171
  WCC::Contentful::LinkVisitor.new(entry, :Link, depth: depth - 1).map! do |val|
168
- resolve_link(val)
172
+ resolve_link(val, includes)
169
173
  end
170
174
  end
171
175
 
172
- def resolve_link(val)
176
+ def resolve_link(val, includes)
173
177
  return val unless val.is_a?(Hash) && val.dig('sys', 'type') == 'Link'
174
- return val unless included = response.includes[val.dig('sys', 'id')]
178
+ return val unless included = includes[val.dig('sys', 'id')]
175
179
 
176
180
  included
177
181
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '1.1.2'
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
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.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watermark Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-16 00:00:00.000000000 Z
11
+ date: 2022-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -282,14 +282,14 @@ dependencies:
282
282
  requirements:
283
283
  - - "~>"
284
284
  - !ruby/object:Gem::Version
285
- version: 1.3.6
285
+ version: '1.4'
286
286
  type: :development
287
287
  prerelease: false
288
288
  version_requirements: !ruby/object:Gem::Requirement
289
289
  requirements:
290
290
  - - "~>"
291
291
  - !ruby/object:Gem::Version
292
- version: 1.3.6
292
+ version: '1.4'
293
293
  - !ruby/object:Gem::Dependency
294
294
  name: timecop
295
295
  requirement: !ruby/object:Gem::Requirement
@@ -461,6 +461,8 @@ files:
461
461
  - lib/wcc/contentful/model_singleton_methods.rb
462
462
  - lib/wcc/contentful/rails.rb
463
463
  - lib/wcc/contentful/rake.rb
464
+ - lib/wcc/contentful/rich_text.rb
465
+ - lib/wcc/contentful/rich_text/node.rb
464
466
  - lib/wcc/contentful/rspec.rb
465
467
  - lib/wcc/contentful/services.rb
466
468
  - lib/wcc/contentful/simple_client.rb
@@ -501,7 +503,7 @@ homepage: https://github.com/watermarkchurch/wcc-contentful/wcc-contentful
501
503
  licenses:
502
504
  - MIT
503
505
  metadata:
504
- documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.1/wcc-contentful
506
+ documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.2/wcc-contentful
505
507
  post_install_message:
506
508
  rdoc_options: []
507
509
  require_paths:
@@ -517,8 +519,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
517
519
  - !ruby/object:Gem::Version
518
520
  version: '0'
519
521
  requirements: []
520
- rubyforge_project:
521
- rubygems_version: 2.7.6.2
522
+ rubygems_version: 3.3.7
522
523
  signing_key:
523
524
  specification_version: 4
524
525
  summary: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](https://rubygems.org/gems/wcc-contentful)