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.
@@ -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
- def initialize(types)
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
- return WCC::Contentful::Model.const_get(const) if WCC::Contentful::Model.const_defined?(const)
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
- WCC::Contentful::Model.const_set(const,
29
- Class.new(WCC::Contentful::Model) do
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.store(context[:preview])
58
- .find_by(content_type: self.class.content_type,
59
- filter: { 'sys.id' => id },
60
- options: context.except(*MODEL_LAYER_CONTEXT_KEYS).merge!({
61
- include: [depth, 10].min
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
- WCC::Contentful::Model.find(id, options: new_context)
171
+ self.class.model_namespace.find(id, options: new_context)
174
172
  end
175
173
  else
176
- WCC::Contentful::Model.new_from_raw(raw, new_context)
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 = store(options[:preview])
15
+ store = options[:preview] ? services.preview_store : services.store
16
16
  raw =
17
- WCC::Contentful::Instrumentation.instrument 'find.model.contentful.wcc',
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 = store(options[:preview])
37
+ store = options[:preview] ? services.preview_store : services.store
38
38
  query =
39
- WCC::Contentful::Instrumentation.instrument 'find_all.model.contentful.wcc',
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 = store(options[:preview])
59
+ store = options[:preview] ? services.preview_store : services.store
60
60
  result =
61
- WCC::Contentful::Instrumentation.instrument 'find_by.model.contentful.wcc',
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
@@ -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(content_type, **attrs)
16
- const = WCC::Contentful::Model.resolve_constant(content_type.to_s)
17
- instance = contentful_create(content_type, **attrs)
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(content_type, options, raw: instance.raw, **attrs)
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(content_type, options, raw: instance.raw, **attrs)
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__ ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
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
- def configuration
12
- @configuration ||= WCC::Contentful.configuration
13
- end
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
- ensure_configured do |config|
49
- WCC::Contentful::Store::Factory.new(
50
- config,
51
- :direct,
52
- :preview
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
- ensure_configured do |config|
64
- WCC::Contentful::SimpleClient::Cdn.new(
65
- **config.connection_options,
66
- access_token: config.access_token,
67
- space: config.space,
68
- default_locale: config.default_locale,
69
- connection: config.connection,
70
- environment: config.environment
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
- ensure_configured do |config|
82
- if config.preview_token.present?
83
- WCC::Contentful::SimpleClient::Preview.new(
84
- **config.connection_options,
85
- preview_token: config.preview_token,
86
- space: config.space,
87
- default_locale: config.default_locale,
88
- connection: config.connection,
89
- environment: config.environment
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
- ensure_configured do |config|
102
- if config.management_token.present?
103
- WCC::Contentful::SimpleClient::Management.new(
104
- **config.connection_options,
105
- management_token: config.management_token,
106
- space: config.space,
107
- default_locale: config.default_locale,
108
- connection: config.connection,
109
- environment: config.environment
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
- WCC::Contentful.configuration.instrumentation_adapter ||
129
+ configuration.instrumentation_adapter ||
140
130
  ActiveSupport::Notifications
141
131
  end
142
-
143
- private
144
-
145
- def ensure_configured
146
- raise StandardError, 'WCC::Contentful has not yet been configured!' if configuration.nil?
147
-
148
- yield configuration
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 = (WCC::Contentful::Services.instance_methods -
153
- Object.instance_methods)
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
- WCC::Contentful::LinkVisitor.new(entry, :Link, :Asset, depth: depth - 1).map! do |val|
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
- (WCC::Contentful::SERVICES - %i[store preview_store]).each do |s|
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, :Asset,
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
@@ -49,7 +49,6 @@ module WCC::Contentful
49
49
  end
50
50
 
51
51
  @store = store
52
- @state = read_state if should_sync?
53
52
  end
54
53
  if state
55
54
  @state = token_wrapper_factory(state)
@@ -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(content_type, **attrs)
11
- const = WCC::Contentful::Model.resolve_constant(content_type)
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(content_type, context = nil, **attrs)
11
- const = WCC::Contentful::Model.resolve_constant(content_type.to_s)
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')
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '1.0.8'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end