wcc-contentful 1.0.8 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +174 -9
- data/lib/wcc/contentful/instrumentation.rb +20 -2
- data/lib/wcc/contentful/link_visitor.rb +26 -30
- data/lib/wcc/contentful/middleware/store.rb +2 -1
- data/lib/wcc/contentful/model.rb +5 -122
- data/lib/wcc/contentful/model_api.rb +182 -0
- data/lib/wcc/contentful/model_builder.rb +10 -4
- data/lib/wcc/contentful/model_methods.rb +8 -10
- data/lib/wcc/contentful/model_singleton_methods.rb +6 -6
- data/lib/wcc/contentful/rspec.rb +7 -5
- data/lib/wcc/contentful/services.rb +59 -61
- data/lib/wcc/contentful/store/cdn_adapter.rb +2 -1
- data/lib/wcc/contentful/store/factory.rb +2 -6
- data/lib/wcc/contentful/store/query.rb +1 -1
- data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +0 -18
- data/lib/wcc/contentful/sync_engine.rb +0 -1
- data/lib/wcc/contentful/test/attributes.rb +0 -4
- data/lib/wcc/contentful/test/double.rb +4 -2
- data/lib/wcc/contentful/test/factory.rb +4 -2
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +14 -8
- metadata +108 -33
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/module/introspection'
|
4
|
+
|
5
|
+
module WCC::Contentful::ModelAPI
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include WCC::Contentful::Instrumentation
|
10
|
+
|
11
|
+
# override class-level _instrumentation from WCC::Contentful::Instrumentation
|
12
|
+
def self._instrumentation
|
13
|
+
services.instrumentation
|
14
|
+
end
|
15
|
+
|
16
|
+
# We use a class var here because this is a global registry for all subclasses
|
17
|
+
# of this namespace
|
18
|
+
@@registry = {} # rubocop:disable Style/ClassVars
|
19
|
+
end
|
20
|
+
|
21
|
+
class_methods do
|
22
|
+
attr_reader :configuration
|
23
|
+
|
24
|
+
def configure(configuration = nil, schema: nil, services: nil)
|
25
|
+
configuration ||= @configuration || WCC::Contentful::Configuration.new
|
26
|
+
yield(configuration) if block_given?
|
27
|
+
|
28
|
+
@schema = schema if schema
|
29
|
+
@services = services if services
|
30
|
+
@configuration = configuration.freeze
|
31
|
+
|
32
|
+
WCC::Contentful::ModelBuilder.new(self.schema, namespace: self).build_models
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def services
|
37
|
+
@services ||=
|
38
|
+
# try looking up the class heierarchy
|
39
|
+
(superclass.services if superclass.respond_to?(:services)) ||
|
40
|
+
# create it if we have a configuration
|
41
|
+
WCC::Contentful::Services.new(configuration)
|
42
|
+
end
|
43
|
+
|
44
|
+
def store(preview = nil)
|
45
|
+
ActiveSupport::Deprecation.warn('Use services.store instead')
|
46
|
+
|
47
|
+
preview ? services.preview_store : services.store
|
48
|
+
end
|
49
|
+
|
50
|
+
def schema
|
51
|
+
return @schema if @schema
|
52
|
+
|
53
|
+
file = configuration.schema_file
|
54
|
+
schema_json = JSON.parse(File.read(file))['contentTypes']
|
55
|
+
unless schema_json
|
56
|
+
raise ArgumentError, 'Please give either a JSON array of content types or file to load from'
|
57
|
+
end
|
58
|
+
|
59
|
+
@schema = WCC::Contentful::ContentTypeIndexer.from_json_schema(schema_json).types
|
60
|
+
end
|
61
|
+
|
62
|
+
# Finds an Entry or Asset by ID in the configured contentful space
|
63
|
+
# and returns an initialized instance of the appropriate model type.
|
64
|
+
#
|
65
|
+
# Makes use of the {WCC::Contentful::Services#store configured store}
|
66
|
+
# to access the Contentful CDN.
|
67
|
+
def find(id, options: nil)
|
68
|
+
options ||= {}
|
69
|
+
store = options[:preview] ? services.preview_store : services.store
|
70
|
+
raw =
|
71
|
+
_instrumentation.instrument 'find.model.contentful.wcc', id: id, options: options do
|
72
|
+
store.find(id, options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
73
|
+
end
|
74
|
+
|
75
|
+
new_from_raw(raw, options) if raw.present?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Creates a new initialized instance of the appropriate model type for the
|
79
|
+
# given raw value. The raw value must be the same format as returned from one
|
80
|
+
# of the stores for a given object.
|
81
|
+
def new_from_raw(raw, context = nil)
|
82
|
+
content_type = WCC::Contentful::Helpers.content_type_from_raw(raw)
|
83
|
+
const = resolve_constant(content_type)
|
84
|
+
const.new(raw, context)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Accepts a content type ID as a string and returns the Ruby constant
|
88
|
+
# stored in the registry that represents this content type.
|
89
|
+
def resolve_constant(content_type)
|
90
|
+
raise ArgumentError, 'content_type cannot be nil' unless content_type
|
91
|
+
|
92
|
+
const = @@registry[content_type]
|
93
|
+
return const if const
|
94
|
+
|
95
|
+
const_name = WCC::Contentful::Helpers.constant_from_content_type(content_type).to_s
|
96
|
+
# #parent renamed to #module_parent in Rails 6
|
97
|
+
parent = try(:module_parent) || self.parent
|
98
|
+
|
99
|
+
loop do
|
100
|
+
begin
|
101
|
+
# The app may have defined a model and we haven't loaded it yet
|
102
|
+
const = parent.const_missing(const_name)
|
103
|
+
return const if const && const < self
|
104
|
+
rescue NameError => e
|
105
|
+
raise e unless e.message =~ /uninitialized constant (.+::)*#{const_name}$/
|
106
|
+
end
|
107
|
+
|
108
|
+
# const_missing only searches recursively up the module tree in a Rails
|
109
|
+
# context. If we're in a non-Rails app, we have to do that recursion ourselves.
|
110
|
+
# Keep looking upwards until we get to Object.
|
111
|
+
break if parent == Object
|
112
|
+
|
113
|
+
parent = parent.try(:module_parent) || parent.parent
|
114
|
+
end
|
115
|
+
|
116
|
+
# Autoloading couldn't find their model - we'll register our own.
|
117
|
+
const = const_get(
|
118
|
+
WCC::Contentful::Helpers.constant_from_content_type(content_type)
|
119
|
+
)
|
120
|
+
register_for_content_type(content_type, klass: const)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Registers a class constant to be instantiated when resolving an instance
|
124
|
+
# of the given content type. This automatically happens for the first subclass
|
125
|
+
# of a generated model type, example:
|
126
|
+
#
|
127
|
+
# class MyMenu < WCC::Contentful::Model::Menu
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# In the above case, instances of MyMenu will be instantiated whenever a 'menu'
|
131
|
+
# content type is resolved.
|
132
|
+
# The mapping can be made explicit with the optional parameters. Example:
|
133
|
+
#
|
134
|
+
# class MyFoo < WCC::Contentful::Model::Foo
|
135
|
+
# register_for_content_type 'bar' # MyFoo is assumed
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# # in initializers/wcc_contentful.rb
|
139
|
+
# WCC::Contentful::Model.register_for_content_type('bar', klass: MyFoo)
|
140
|
+
def register_for_content_type(content_type = nil, klass: nil)
|
141
|
+
klass ||= self
|
142
|
+
raise ArgumentError, "#{klass} must be a class constant!" unless klass.respond_to?(:new)
|
143
|
+
|
144
|
+
content_type ||= WCC::Contentful::Helpers.content_type_from_constant(klass)
|
145
|
+
|
146
|
+
@@registry[content_type] = klass
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the current registry of content type names to constants.
|
150
|
+
def registry
|
151
|
+
return {} unless @@registry
|
152
|
+
|
153
|
+
@@registry.dup.freeze
|
154
|
+
end
|
155
|
+
|
156
|
+
def reload!
|
157
|
+
registry = self.registry
|
158
|
+
registry.each do |(content_type, klass)|
|
159
|
+
const_name = klass.name
|
160
|
+
begin
|
161
|
+
# the const_name is fully qualified so search from root
|
162
|
+
const = Object.const_missing(const_name)
|
163
|
+
register_for_content_type(content_type, klass: const) if const
|
164
|
+
rescue NameError => e
|
165
|
+
msg = "Error when reloading constant #{const_name} - #{e}"
|
166
|
+
if defined?(Rails) && Rails.logger
|
167
|
+
Rails.logger.error msg
|
168
|
+
else
|
169
|
+
puts msg
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Checks if a content type has already been registered to a class and returns
|
176
|
+
# that class. If nil, the generated WCC::Contentful::Model::{content_type} class
|
177
|
+
# will be resolved for this content type.
|
178
|
+
def registered?(content_type)
|
179
|
+
@@registry[content_type]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -7,8 +7,11 @@ module WCC::Contentful
|
|
7
7
|
class ModelBuilder
|
8
8
|
include Helpers
|
9
9
|
|
10
|
-
|
10
|
+
attr_reader :namespace
|
11
|
+
|
12
|
+
def initialize(types, namespace: WCC::Contentful::Model)
|
11
13
|
@types = types
|
14
|
+
@namespace = namespace
|
12
15
|
end
|
13
16
|
|
14
17
|
def build_models
|
@@ -21,12 +24,13 @@ module WCC::Contentful
|
|
21
24
|
|
22
25
|
def build_model(typedef)
|
23
26
|
const = typedef.name
|
24
|
-
|
27
|
+
ns = namespace
|
28
|
+
return ns.const_get(const) if ns.const_defined?(const)
|
25
29
|
|
26
30
|
# TODO: https://github.com/dkubb/ice_nine ?
|
27
31
|
typedef = typedef.deep_dup.freeze
|
28
|
-
|
29
|
-
Class.new(
|
32
|
+
ns.const_set(const,
|
33
|
+
Class.new(namespace) do
|
30
34
|
extend ModelSingletonMethods
|
31
35
|
include ModelMethods
|
32
36
|
include Helpers
|
@@ -50,6 +54,8 @@ module WCC::Contentful
|
|
50
54
|
typedef
|
51
55
|
end
|
52
56
|
|
57
|
+
define_singleton_method(:model_namespace) { ns }
|
58
|
+
|
53
59
|
define_method(:initialize) do |raw, context = nil|
|
54
60
|
ct = content_type_from_raw(raw)
|
55
61
|
if ct != typedef.content_type
|
@@ -5,8 +5,6 @@
|
|
5
5
|
#
|
6
6
|
# @api Model
|
7
7
|
module WCC::Contentful::ModelMethods
|
8
|
-
include WCC::Contentful::Instrumentation
|
9
|
-
|
10
8
|
# The set of options keys that are specific to the Model layer and shouldn't
|
11
9
|
# be passed down to the Store layer.
|
12
10
|
MODEL_LAYER_CONTEXT_KEYS = %i[
|
@@ -40,6 +38,7 @@ module WCC::Contentful::ModelMethods
|
|
40
38
|
|
41
39
|
typedef = self.class.content_type_definition
|
42
40
|
links = fields.select { |f| %i[Asset Link].include?(typedef.fields[f].type) }
|
41
|
+
store = context[:preview] ? self.class.services.preview_store : self.class.services.store
|
43
42
|
|
44
43
|
raw_link_ids =
|
45
44
|
links.map { |field_name| raw.dig('fields', field_name, sys.locale) }
|
@@ -54,12 +53,11 @@ module WCC::Contentful::ModelMethods
|
|
54
53
|
raw =
|
55
54
|
_instrument 'resolve', id: id, depth: depth, backlinks: backlinked_ids do
|
56
55
|
# use include param to do resolution
|
57
|
-
self.class.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
}))
|
56
|
+
store.find_by(content_type: self.class.content_type,
|
57
|
+
filter: { 'sys.id' => id },
|
58
|
+
options: context.except(*MODEL_LAYER_CONTEXT_KEYS).merge!({
|
59
|
+
include: [depth, 10].min
|
60
|
+
}))
|
63
61
|
end
|
64
62
|
unless raw
|
65
63
|
raise WCC::Contentful::ResolveError, "Cannot find #{self.class.content_type} with ID #{id}"
|
@@ -170,10 +168,10 @@ module WCC::Contentful::ModelMethods
|
|
170
168
|
if raw.dig('sys', 'type') == 'Link'
|
171
169
|
_instrument 'resolve',
|
172
170
|
id: self.id, depth: depth, backlinks: context[:backlinks]&.map(&:id) do
|
173
|
-
|
171
|
+
self.class.model_namespace.find(id, options: new_context)
|
174
172
|
end
|
175
173
|
else
|
176
|
-
|
174
|
+
self.class.model_namespace.new_from_raw(raw, new_context)
|
177
175
|
end
|
178
176
|
|
179
177
|
m.resolve(depth: depth - 1, context: new_context, **options) if m && depth > 1
|
@@ -12,9 +12,9 @@ module WCC::Contentful::ModelSingletonMethods
|
|
12
12
|
# WCC::Contentful::Model::Page.find(id)
|
13
13
|
def find(id, options: nil)
|
14
14
|
options ||= {}
|
15
|
-
store =
|
15
|
+
store = options[:preview] ? services.preview_store : services.store
|
16
16
|
raw =
|
17
|
-
|
17
|
+
_instrumentation.instrument 'find.model.contentful.wcc',
|
18
18
|
content_type: content_type, id: id, options: options do
|
19
19
|
store.find(id, { hint: type }.merge!(options.except(:preview)))
|
20
20
|
end
|
@@ -34,9 +34,9 @@ module WCC::Contentful::ModelSingletonMethods
|
|
34
34
|
|
35
35
|
filter.transform_keys! { |k| k.to_s.camelize(:lower) } if filter.present?
|
36
36
|
|
37
|
-
store =
|
37
|
+
store = options[:preview] ? services.preview_store : services.store
|
38
38
|
query =
|
39
|
-
|
39
|
+
_instrumentation.instrument 'find_all.model.contentful.wcc',
|
40
40
|
content_type: content_type, filter: filter, options: options do
|
41
41
|
store.find_all(content_type: content_type, options: options.except(:preview))
|
42
42
|
end
|
@@ -56,9 +56,9 @@ module WCC::Contentful::ModelSingletonMethods
|
|
56
56
|
|
57
57
|
filter.transform_keys! { |k| k.to_s.camelize(:lower) } if filter.present?
|
58
58
|
|
59
|
-
store =
|
59
|
+
store = options[:preview] ? services.preview_store : services.store
|
60
60
|
result =
|
61
|
-
|
61
|
+
_instrumentation.instrument 'find_by.model.contentful.wcc',
|
62
62
|
content_type: content_type, filter: filter, options: options do
|
63
63
|
store.find_by(content_type: content_type, filter: filter, options: options.except(:preview))
|
64
64
|
end
|
data/lib/wcc/contentful/rspec.rb
CHANGED
@@ -12,16 +12,18 @@ module WCC::Contentful::RSpec
|
|
12
12
|
# Builds out a fake Contentful entry for the given content type, and then
|
13
13
|
# stubs the Model API to return that content type for `.find` and `.find_by`
|
14
14
|
# query methods.
|
15
|
-
def contentful_stub(
|
16
|
-
const
|
17
|
-
|
15
|
+
def contentful_stub(const, **attrs)
|
16
|
+
unless const.respond_to?(:content_type_definition)
|
17
|
+
const = WCC::Contentful::Model.resolve_constant(const.to_s)
|
18
|
+
end
|
19
|
+
instance = contentful_create(const, **attrs)
|
18
20
|
|
19
21
|
# mimic what's going on inside model_singleton_methods.rb
|
20
22
|
# find, find_by, etc always return a new instance from the same raw
|
21
23
|
allow(WCC::Contentful::Model).to receive(:find)
|
22
24
|
.with(instance.id, any_args) do |_id, keyword_params|
|
23
25
|
options = keyword_params && keyword_params[:options]
|
24
|
-
contentful_create(
|
26
|
+
contentful_create(const, options, raw: instance.raw, **attrs)
|
25
27
|
end
|
26
28
|
allow(const).to receive(:find) { |id, options| WCC::Contentful::Model.find(id, **(options || {})) }
|
27
29
|
|
@@ -31,7 +33,7 @@ module WCC::Contentful::RSpec
|
|
31
33
|
filter = filter&.dup
|
32
34
|
options = filter&.delete(:options) || {}
|
33
35
|
|
34
|
-
contentful_create(
|
36
|
+
contentful_create(const, options, raw: instance.raw, **attrs)
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -4,15 +4,16 @@ module WCC::Contentful
|
|
4
4
|
class Services
|
5
5
|
class << self
|
6
6
|
def instance
|
7
|
-
@singleton__instance__ ||=
|
7
|
+
@singleton__instance__ ||= # rubocop:disable Naming/MemoizedInstanceVariableName
|
8
|
+
(new(WCC::Contentful.configuration) if WCC::Contentful.configuration)
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
attr_reader :configuration
|
13
|
+
|
14
|
+
def initialize(configuration)
|
15
|
+
raise ArgumentError, 'Not yet configured!' unless configuration
|
14
16
|
|
15
|
-
def initialize(configuration = nil)
|
16
17
|
@configuration = configuration
|
17
18
|
end
|
18
19
|
|
@@ -33,10 +34,7 @@ module WCC::Contentful
|
|
33
34
|
#
|
34
35
|
# @api Store
|
35
36
|
def store
|
36
|
-
@store ||=
|
37
|
-
ensure_configured do |config|
|
38
|
-
config.store.build(self)
|
39
|
-
end
|
37
|
+
@store ||= configuration.store.build(self)
|
40
38
|
end
|
41
39
|
|
42
40
|
# An instance of {WCC::Contentful::Store::CDNAdapter} which connects to the
|
@@ -45,13 +43,11 @@ module WCC::Contentful
|
|
45
43
|
# @api Store
|
46
44
|
def preview_store
|
47
45
|
@preview_store ||=
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
).build(self)
|
54
|
-
end
|
46
|
+
WCC::Contentful::Store::Factory.new(
|
47
|
+
configuration,
|
48
|
+
:direct,
|
49
|
+
:preview
|
50
|
+
).build(self)
|
55
51
|
end
|
56
52
|
|
57
53
|
# Gets a {WCC::Contentful::SimpleClient::Cdn CDN Client} which provides
|
@@ -60,16 +56,15 @@ module WCC::Contentful
|
|
60
56
|
# @api Client
|
61
57
|
def client
|
62
58
|
@client ||=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
59
|
+
WCC::Contentful::SimpleClient::Cdn.new(
|
60
|
+
**configuration.connection_options,
|
61
|
+
access_token: configuration.access_token,
|
62
|
+
space: configuration.space,
|
63
|
+
default_locale: configuration.default_locale,
|
64
|
+
connection: configuration.connection,
|
65
|
+
environment: configuration.environment,
|
66
|
+
instrumentation: instrumentation
|
67
|
+
)
|
73
68
|
end
|
74
69
|
|
75
70
|
# Gets a {WCC::Contentful::SimpleClient::Cdn CDN Client} which provides
|
@@ -78,17 +73,16 @@ module WCC::Contentful
|
|
78
73
|
# @api Client
|
79
74
|
def preview_client
|
80
75
|
@preview_client ||=
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
76
|
+
if configuration.preview_token.present?
|
77
|
+
WCC::Contentful::SimpleClient::Preview.new(
|
78
|
+
**configuration.connection_options,
|
79
|
+
preview_token: configuration.preview_token,
|
80
|
+
space: configuration.space,
|
81
|
+
default_locale: configuration.default_locale,
|
82
|
+
connection: configuration.connection,
|
83
|
+
environment: configuration.environment,
|
84
|
+
instrumentation: instrumentation
|
85
|
+
)
|
92
86
|
end
|
93
87
|
end
|
94
88
|
|
@@ -98,17 +92,16 @@ module WCC::Contentful
|
|
98
92
|
# @api Client
|
99
93
|
def management_client
|
100
94
|
@management_client ||=
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
95
|
+
if configuration.management_token.present?
|
96
|
+
WCC::Contentful::SimpleClient::Management.new(
|
97
|
+
**configuration.connection_options,
|
98
|
+
management_token: configuration.management_token,
|
99
|
+
space: configuration.space,
|
100
|
+
default_locale: configuration.default_locale,
|
101
|
+
connection: configuration.connection,
|
102
|
+
environment: configuration.environment,
|
103
|
+
instrumentation: instrumentation
|
104
|
+
)
|
112
105
|
end
|
113
106
|
end
|
114
107
|
|
@@ -132,25 +125,30 @@ module WCC::Contentful
|
|
132
125
|
|
133
126
|
# Gets the configured instrumentation adapter, defaulting to ActiveSupport::Notifications
|
134
127
|
def instrumentation
|
135
|
-
return @instrumentation if @instrumentation
|
136
|
-
return ActiveSupport::Notifications if WCC::Contentful.configuration.nil?
|
137
|
-
|
138
128
|
@instrumentation ||=
|
139
|
-
|
129
|
+
configuration.instrumentation_adapter ||
|
140
130
|
ActiveSupport::Notifications
|
141
131
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
132
|
+
# Allow it to be injected into a store
|
133
|
+
alias_method :_instrumentation, :instrumentation
|
134
|
+
|
135
|
+
##
|
136
|
+
# This method enables simple dependency injection -
|
137
|
+
# If the target has a setter matching the name of one of the services,
|
138
|
+
# set that setter with the value of the service.
|
139
|
+
def inject_into(target, except: [])
|
140
|
+
(WCC::Contentful::SERVICES - except).each do |s|
|
141
|
+
next unless target.respond_to?("#{s}=")
|
142
|
+
|
143
|
+
target.public_send("#{s}=",
|
144
|
+
public_send(s))
|
145
|
+
end
|
149
146
|
end
|
150
147
|
end
|
151
148
|
|
152
|
-
SERVICES =
|
153
|
-
|
149
|
+
SERVICES =
|
150
|
+
WCC::Contentful::Services.instance_methods(false)
|
151
|
+
.select { |m| WCC::Contentful::Services.instance_method(m).arity == 0 }
|
154
152
|
|
155
153
|
# Include this module to define accessors for every method defined on the
|
156
154
|
# {Services} singleton.
|
@@ -158,7 +158,8 @@ module WCC::Contentful::Store
|
|
158
158
|
def resolve_includes(entry, depth)
|
159
159
|
return entry unless entry && depth && depth > 0
|
160
160
|
|
161
|
-
|
161
|
+
# Dig links out of response.includes and insert them into the entry
|
162
|
+
WCC::Contentful::LinkVisitor.new(entry, :Link, depth: depth - 1).map! do |val|
|
162
163
|
resolve_link(val)
|
163
164
|
end
|
164
165
|
end
|
@@ -75,6 +75,7 @@ module WCC::Contentful::Store
|
|
75
75
|
middleware, params, configure_proc = middleware_config
|
76
76
|
middleware_options = options.merge((params || []).extract_options!)
|
77
77
|
middleware = middleware.call(memo, *params, **middleware_options)
|
78
|
+
services.inject_into(middleware, except: %i[store preview_store])
|
78
79
|
middleware&.instance_exec(&configure_proc) if configure_proc
|
79
80
|
middleware || memo
|
80
81
|
end
|
@@ -154,12 +155,7 @@ module WCC::Contentful::Store
|
|
154
155
|
end
|
155
156
|
|
156
157
|
# Inject services into the custom store class
|
157
|
-
(
|
158
|
-
next unless store.respond_to?("#{s}=")
|
159
|
-
|
160
|
-
store.public_send("#{s}=",
|
161
|
-
services.public_send(s))
|
162
|
-
end
|
158
|
+
services.inject_into(store, except: %i[store preview_store])
|
163
159
|
|
164
160
|
store
|
165
161
|
end
|
@@ -108,7 +108,7 @@ module WCC::Contentful::Store
|
|
108
108
|
includes = row.try(:includes) || row.try(:[], 1)
|
109
109
|
return entry unless entry && depth && depth > 0
|
110
110
|
|
111
|
-
WCC::Contentful::LinkVisitor.new(entry, :Link,
|
111
|
+
WCC::Contentful::LinkVisitor.new(entry, :Link,
|
112
112
|
# Walk all the links except for the leaf nodes
|
113
113
|
depth: depth - 1).map! do |val|
|
114
114
|
resolve_link(val, includes)
|
@@ -130,24 +130,6 @@ RSpec.shared_examples 'basic store' do
|
|
130
130
|
JSON
|
131
131
|
end
|
132
132
|
|
133
|
-
before do
|
134
|
-
allow(WCC::Contentful).to receive(:types)
|
135
|
-
.and_return({
|
136
|
-
'root' => double(fields: {
|
137
|
-
'name' => double(name: 'name', type: :String, array: false),
|
138
|
-
'link' => double(name: 'link', type: :Link, array: false),
|
139
|
-
'links' => double(name: 'links', type: :Link, array: true)
|
140
|
-
}),
|
141
|
-
'shallow' => double(fields: {
|
142
|
-
'name' => double(name: 'name', type: :String, array: false)
|
143
|
-
}),
|
144
|
-
'deep' => double(fields: {
|
145
|
-
'name' => double(name: 'name', type: :String, array: false),
|
146
|
-
'subLink' => double(name: 'subLink', type: :Link, array: false)
|
147
|
-
})
|
148
|
-
})
|
149
|
-
end
|
150
|
-
|
151
133
|
describe '#set/#find' do
|
152
134
|
describe 'ensures that the stored value is of type Hash' do
|
153
135
|
it 'should not raise an error if value is a Hash' do
|
@@ -31,10 +31,6 @@ module WCC::Contentful::Test::Attributes
|
|
31
31
|
##
|
32
32
|
# Get a hash of default values for all attributes unique to the given Contentful model.
|
33
33
|
def defaults(const)
|
34
|
-
unless const < WCC::Contentful::Model
|
35
|
-
raise ArgumentError, "#{const} is not a subclass of WCC::Contentful::Model"
|
36
|
-
end
|
37
|
-
|
38
34
|
const.content_type_definition.fields.each_with_object({}) do |(name, f), h|
|
39
35
|
h[name.to_sym] = h[name.underscore.to_sym] = default_value(f)
|
40
36
|
end
|
@@ -7,8 +7,10 @@ module WCC::Contentful::Test::Double
|
|
7
7
|
# Builds a rspec double of the Contentful model for the given content_type.
|
8
8
|
# All attributes that are known to be required fields on the content type
|
9
9
|
# will return a default value based on the field type.
|
10
|
-
def contentful_double(
|
11
|
-
const
|
10
|
+
def contentful_double(const, **attrs)
|
11
|
+
unless const.respond_to?(:content_type_definition)
|
12
|
+
const = WCC::Contentful::Model.resolve_constant(const.to_s)
|
13
|
+
end
|
12
14
|
attrs.symbolize_keys!
|
13
15
|
|
14
16
|
bad_attrs = attrs.reject { |a| const.instance_methods.include?(a) }
|
@@ -7,8 +7,10 @@ module WCC::Contentful::Test::Factory
|
|
7
7
|
# Builds a in-memory instance of the Contentful model for the given content_type.
|
8
8
|
# All attributes that are known to be required fields on the content type
|
9
9
|
# will return a default value based on the field type.
|
10
|
-
def contentful_create(
|
11
|
-
const
|
10
|
+
def contentful_create(const, context = nil, **attrs)
|
11
|
+
unless const.respond_to?(:content_type_definition)
|
12
|
+
const = WCC::Contentful::Model.resolve_constant(const.to_s)
|
13
|
+
end
|
12
14
|
attrs = attrs.transform_keys { |a| a.to_s.camelize(:lower) }
|
13
15
|
|
14
16
|
id = attrs.delete('id')
|