wcc-contentful 0.3.0.pre.rc3 → 1.0.0.pre.rc1
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 +5 -5
- data/.rspec +1 -0
- data/Guardfile +101 -0
- data/README.md +161 -11
- data/app/controllers/wcc/contentful/webhook_controller.rb +25 -24
- data/app/jobs/wcc/contentful/webhook_enable_job.rb +36 -2
- data/config/routes.rb +1 -1
- data/doc/wcc-contentful.png +0 -0
- data/lib/tasks/download_schema.rake +12 -0
- data/lib/wcc/contentful.rb +70 -16
- data/lib/wcc/contentful/active_record_shim.rb +72 -0
- data/lib/wcc/contentful/configuration.rb +177 -46
- data/lib/wcc/contentful/content_type_indexer.rb +14 -0
- data/lib/wcc/contentful/downloads_schema.rb +112 -0
- data/lib/wcc/contentful/engine.rb +33 -14
- data/lib/wcc/contentful/event.rb +171 -0
- data/lib/wcc/contentful/events.rb +41 -0
- data/lib/wcc/contentful/exceptions.rb +3 -0
- data/lib/wcc/contentful/indexed_representation.rb +2 -2
- data/lib/wcc/contentful/instrumentation.rb +31 -0
- data/lib/wcc/contentful/link.rb +28 -0
- data/lib/wcc/contentful/link_visitor.rb +122 -0
- data/lib/wcc/contentful/middleware.rb +7 -0
- data/lib/wcc/contentful/middleware/store.rb +158 -0
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +114 -0
- data/lib/wcc/contentful/model.rb +37 -3
- data/lib/wcc/contentful/model_builder.rb +1 -0
- data/lib/wcc/contentful/model_methods.rb +40 -15
- data/lib/wcc/contentful/model_singleton_methods.rb +47 -30
- data/lib/wcc/contentful/rake.rb +3 -0
- data/lib/wcc/contentful/rspec.rb +46 -0
- data/lib/wcc/contentful/services.rb +61 -27
- data/lib/wcc/contentful/simple_client.rb +81 -25
- data/lib/wcc/contentful/simple_client/management.rb +43 -10
- data/lib/wcc/contentful/simple_client/response.rb +61 -22
- data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +17 -17
- data/lib/wcc/contentful/store.rb +7 -66
- data/lib/wcc/contentful/store/README.md +85 -0
- data/lib/wcc/contentful/store/base.rb +34 -119
- data/lib/wcc/contentful/store/cdn_adapter.rb +71 -12
- data/lib/wcc/contentful/store/factory.rb +186 -0
- data/lib/wcc/contentful/store/instrumentation.rb +55 -0
- data/lib/wcc/contentful/store/interface.rb +82 -0
- data/lib/wcc/contentful/store/memory_store.rb +27 -24
- data/lib/wcc/contentful/store/postgres_store.rb +268 -101
- data/lib/wcc/contentful/store/postgres_store/schema_1.sql +73 -0
- data/lib/wcc/contentful/store/postgres_store/schema_2.sql +21 -0
- data/lib/wcc/contentful/store/query.rb +246 -0
- data/lib/wcc/contentful/store/query/interface.rb +63 -0
- data/lib/wcc/contentful/store/rspec_examples.rb +48 -0
- data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +629 -0
- data/lib/wcc/contentful/store/rspec_examples/include_param.rb +283 -0
- data/lib/wcc/contentful/store/rspec_examples/nested_queries.rb +342 -0
- data/lib/wcc/contentful/sync_engine.rb +181 -0
- data/lib/wcc/contentful/test.rb +7 -0
- data/lib/wcc/contentful/test/attributes.rb +56 -0
- data/lib/wcc/contentful/test/double.rb +76 -0
- data/lib/wcc/contentful/test/factory.rb +101 -0
- data/lib/wcc/contentful/version.rb +1 -1
- data/wcc-contentful.gemspec +22 -10
- metadata +282 -103
- data/Gemfile +0 -8
- data/app/jobs/wcc/contentful/delayed_sync_job.rb +0 -63
- data/lib/wcc/contentful/client_ext.rb +0 -28
- data/lib/wcc/contentful/graphql.rb +0 -14
- data/lib/wcc/contentful/graphql/builder.rb +0 -177
- data/lib/wcc/contentful/graphql/types.rb +0 -54
- data/lib/wcc/contentful/simple_client/http_adapter.rb +0 -24
- data/lib/wcc/contentful/store/lazy_cache_store.rb +0 -161
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class WCC::Contentful::Link
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :link_type
|
6
|
+
attr_reader :raw
|
7
|
+
|
8
|
+
LINK_TYPES = {
|
9
|
+
Asset: 'Asset',
|
10
|
+
Link: 'Entry'
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def initialize(model, link_type = nil)
|
14
|
+
@id = model.try(:id) || model
|
15
|
+
@link_type = link_type
|
16
|
+
@link_type ||= model.is_a?(WCC::Contentful::Model::Asset) ? :Asset : :Link
|
17
|
+
@raw =
|
18
|
+
{
|
19
|
+
'sys' => {
|
20
|
+
'type' => 'Link',
|
21
|
+
'linkType' => LINK_TYPES[@link_type] || link_type,
|
22
|
+
'id' => @id
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :to_h, :raw
|
28
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The LinkVisitor is a utility class for walking trees of linked entries.
|
4
|
+
# It is used internally by the Store layer to compose the resulting resolved hashes.
|
5
|
+
# But you can use it too!
|
6
|
+
class WCC::Contentful::LinkVisitor
|
7
|
+
attr_reader :entry
|
8
|
+
attr_reader :type
|
9
|
+
attr_reader :fields
|
10
|
+
attr_reader :depth
|
11
|
+
|
12
|
+
# @param [Hash] entry The entry hash (resolved or unresolved) to walk
|
13
|
+
# @param [Array<String, Symbol>] The fields to select from the entry tree.
|
14
|
+
# Use `:Link` to select only links, or `'slug'` to select all slugs in the tree.
|
15
|
+
# @param [Fixnum] depth (optional) How far to walk down the tree of links. Be careful of
|
16
|
+
# recursive trees!
|
17
|
+
# @example
|
18
|
+
# entry = store.find_by(id: id, include: 3)
|
19
|
+
# WCC::Contentful::LinkVisitor.new(entry, 'slug', depth: 3)
|
20
|
+
# .map { |slug| 'https://mirror-site' + slug }
|
21
|
+
def initialize(entry, *fields, depth: 0)
|
22
|
+
unless entry.is_a?(Hash) && entry.dig('sys', 'id')
|
23
|
+
raise ArgumentError, "Please provide an entry as a hash value (got #{entry})"
|
24
|
+
end
|
25
|
+
unless ct = entry.dig('sys', 'contentType', 'sys', 'id')
|
26
|
+
raise ArgumentError, 'Entry has no content type!'
|
27
|
+
end
|
28
|
+
|
29
|
+
@type = WCC::Contentful.types[ct]
|
30
|
+
raise ArgumentError, "Unknown content type '#{ct}'" unless @type
|
31
|
+
|
32
|
+
@entry = entry
|
33
|
+
@fields = fields
|
34
|
+
@depth = depth
|
35
|
+
end
|
36
|
+
|
37
|
+
# Walks an entry and its resolved links, without transforming the entry.
|
38
|
+
# @yield [value, field, locale]
|
39
|
+
# @yieldparam [Object] value The value of the selected field.
|
40
|
+
# @yieldparam [WCC::Contentful::IndexedRepresentation::Field] field The type of the selected field
|
41
|
+
# @yieldparam [String] locale The locale of the current field value
|
42
|
+
# @returns nil
|
43
|
+
def each(&block)
|
44
|
+
_each do |val, field, locale, index|
|
45
|
+
yield(val, field, locale, index) if should_yield_field?(field)
|
46
|
+
|
47
|
+
next unless should_walk_link?(field, val)
|
48
|
+
|
49
|
+
self.class.new(val, *fields, depth: depth - 1).each(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def map!(&block)
|
56
|
+
_each do |val, field, locale, index|
|
57
|
+
if should_yield_field?(field)
|
58
|
+
val = yield(val, field, locale, index)
|
59
|
+
set_field(field, locale, index, val)
|
60
|
+
end
|
61
|
+
|
62
|
+
next unless should_walk_link?(field, val)
|
63
|
+
|
64
|
+
self.class.new(val, *fields, depth: depth - 1).map!(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
entry
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def _each(&block)
|
73
|
+
type.fields.each_value do |f|
|
74
|
+
each_field(f, &block)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def each_field(field)
|
79
|
+
each_locale(field) do |val, locale|
|
80
|
+
if field.array
|
81
|
+
val&.each_with_index do |v, index|
|
82
|
+
yield(v, field, locale, index) unless v.nil?
|
83
|
+
end
|
84
|
+
else
|
85
|
+
yield(val, field, locale) unless val.nil?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def each_locale(field)
|
91
|
+
raw_value = entry.dig('fields', field.name)
|
92
|
+
if locale = entry.dig('sys', 'locale')
|
93
|
+
if raw_value.is_a?(Hash) && raw_value[locale]
|
94
|
+
# it's a locale=* entry, but they've added sys.locale to those now
|
95
|
+
raw_value = raw_value[locale]
|
96
|
+
end
|
97
|
+
yield(raw_value, locale)
|
98
|
+
else
|
99
|
+
raw_value&.each_with_object({}) do |(l, val), h|
|
100
|
+
h[l] = yield(val, l)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def should_yield_field?(field)
|
106
|
+
fields.empty? || fields.include?(field.type) || fields.include?(field.name)
|
107
|
+
end
|
108
|
+
|
109
|
+
def should_walk_link?(field, val, dep = depth)
|
110
|
+
dep > 0 && field.type == :Link && val.dig('sys', 'type') == 'Entry'
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_field(field, locale, index, val)
|
114
|
+
current_field = (entry['fields'][field.name] ||= {})
|
115
|
+
|
116
|
+
if field.array
|
117
|
+
(current_field[locale] ||= [])[index] = val
|
118
|
+
else
|
119
|
+
current_field[locale] = val
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../middleware'
|
4
|
+
|
5
|
+
# A Store middleware wraps the Store interface to perform any desired transformations
|
6
|
+
# on the Contentful entries coming back from the store. A Store middleware must
|
7
|
+
# implement the Store interface as well as a `store=` attribute writer, which is
|
8
|
+
# used to inject the next store or middleware in the chain.
|
9
|
+
#
|
10
|
+
# The Store interface can be seen on the WCC::Contentful::Store::Base class. It
|
11
|
+
# consists of the `#find, #find_by, #find_all, #set, #delete,` and `#index` methods.
|
12
|
+
#
|
13
|
+
# Including this concern will define those methods to pass through to the next store.
|
14
|
+
# Any of those methods can be overridden on the implementing middleware.
|
15
|
+
# It will also expose two overridable methods, `#select?` and `#transform`. These
|
16
|
+
# methods are applied when reading values out of the store, and can be used to
|
17
|
+
# apply a filter or transformation to each entry in the store.
|
18
|
+
module WCC::Contentful::Middleware::Store
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
include WCC::Contentful::Store::Interface
|
21
|
+
|
22
|
+
attr_accessor :store
|
23
|
+
|
24
|
+
delegate :index, :index?, to: :store
|
25
|
+
|
26
|
+
class_methods do
|
27
|
+
def call(store, *content_delivery_params, **_)
|
28
|
+
instance = new(*content_delivery_params)
|
29
|
+
instance.store = store
|
30
|
+
instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def find(id, **options)
|
35
|
+
found = store.find(id, **options)
|
36
|
+
return transform(found) if found && (!has_select? || select?(found))
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_by(options: nil, **args)
|
40
|
+
result = store.find_by(**args.merge(options: options))
|
41
|
+
return unless result && (!has_select? || select?(result))
|
42
|
+
|
43
|
+
result = resolve_includes(result, options[:include]) if options && options[:include]
|
44
|
+
transform(result)
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_all(options: nil, **args)
|
48
|
+
DelegatingQuery.new(
|
49
|
+
store.find_all(**args.merge(options: options)),
|
50
|
+
middleware: self,
|
51
|
+
options: options
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def resolve_includes(entry, depth)
|
56
|
+
return entry unless entry && depth && depth > 0
|
57
|
+
|
58
|
+
WCC::Contentful::LinkVisitor.new(entry, :Link, depth: depth).map! do |val|
|
59
|
+
resolve_link(val)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def resolve_link(val)
|
64
|
+
return val unless resolved_link?(val)
|
65
|
+
|
66
|
+
if !has_select? || select?(val)
|
67
|
+
transform(val)
|
68
|
+
else
|
69
|
+
# Pretend it's an unresolved link -
|
70
|
+
# matches the behavior of a store when the link cannot be retrieved
|
71
|
+
WCC::Contentful::Link.new(val.dig('sys', 'id'), val.dig('sys', 'type')).to_h
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def resolved_link?(value)
|
76
|
+
value.is_a?(Hash) && value.dig('sys', 'type') == 'Entry'
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_select? # rubocop:disable Naming/PredicateName
|
80
|
+
respond_to?(:select?)
|
81
|
+
end
|
82
|
+
|
83
|
+
# The default version of `#transform` just returns the entry.
|
84
|
+
# Override this with your own implementation.
|
85
|
+
def transform(entry)
|
86
|
+
entry
|
87
|
+
end
|
88
|
+
|
89
|
+
class DelegatingQuery
|
90
|
+
include WCC::Contentful::Store::Query::Interface
|
91
|
+
include Enumerable
|
92
|
+
|
93
|
+
# by default all enumerable methods delegated to the to_enum method
|
94
|
+
delegate(*(Enumerable.instance_methods - Module.instance_methods), to: :to_enum)
|
95
|
+
|
96
|
+
def count
|
97
|
+
if middleware.has_select?
|
98
|
+
raise NameError, "Count cannot be determined because the middleware '#{middleware}'" \
|
99
|
+
" implements the #select? method. Please use '.to_a.count' to count entries that" \
|
100
|
+
' pass the #select? method.'
|
101
|
+
end
|
102
|
+
|
103
|
+
# The wrapped query may get count from the "Total" field in the response,
|
104
|
+
# or apply a "COUNT(*)" to the query.
|
105
|
+
wrapped_query.count
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :wrapped_query, :middleware, :options
|
109
|
+
|
110
|
+
def to_enum
|
111
|
+
result = wrapped_query.to_enum
|
112
|
+
result = result.select { |x| middleware.select?(x) } if middleware.has_select?
|
113
|
+
|
114
|
+
if options && options[:include]
|
115
|
+
result = result.map { |x| middleware.resolve_includes(x, options[:include]) }
|
116
|
+
end
|
117
|
+
|
118
|
+
result.map { |x| middleware.transform(x) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def apply(filter, context = nil)
|
122
|
+
self.class.new(
|
123
|
+
wrapped_query.apply(filter, context),
|
124
|
+
middleware: middleware,
|
125
|
+
options: options,
|
126
|
+
**@extra
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
def apply_operator(operator, field, expected, context = nil)
|
131
|
+
self.class.new(
|
132
|
+
wrapped_query.apply_operator(operator, field, expected, context),
|
133
|
+
middleware: middleware,
|
134
|
+
options: options,
|
135
|
+
**@extra
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
WCC::Contentful::Store::Query::Interface::OPERATORS.each do |op|
|
140
|
+
# @see #apply_operator
|
141
|
+
define_method(op) do |field, expected, context = nil|
|
142
|
+
self.class.new(
|
143
|
+
wrapped_query.public_send(op, field, expected, context),
|
144
|
+
middleware: middleware,
|
145
|
+
options: options,
|
146
|
+
**@extra
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def initialize(wrapped_query, middleware:, options: nil, **extra)
|
152
|
+
@wrapped_query = wrapped_query
|
153
|
+
@middleware = middleware
|
154
|
+
@options = options
|
155
|
+
@extra = extra
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WCC::Contentful::Middleware::Store
|
4
|
+
class CachingMiddleware
|
5
|
+
include WCC::Contentful::Middleware::Store
|
6
|
+
# include instrumentation, but not specifically store stack instrumentation
|
7
|
+
include WCC::Contentful::Instrumentation
|
8
|
+
|
9
|
+
attr_accessor :expires_in
|
10
|
+
|
11
|
+
def initialize(cache = nil)
|
12
|
+
@cache = cache || ActiveSupport::Cache::MemoryStore.new
|
13
|
+
@expires_in = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def find(key, **options)
|
17
|
+
event = 'fresh'
|
18
|
+
found =
|
19
|
+
@cache.fetch(key, expires_in: expires_in) do
|
20
|
+
event = 'miss'
|
21
|
+
# if it's not a contentful ID don't hit the API.
|
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+$/
|
24
|
+
end
|
25
|
+
_instrument(event, key: key, options: options)
|
26
|
+
|
27
|
+
case found.try(:dig, 'sys', 'type')
|
28
|
+
when 'Nil', 'DeletedEntry', 'DeletedAsset'
|
29
|
+
nil
|
30
|
+
else
|
31
|
+
found
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: https://github.com/watermarkchurch/wcc-contentful/issues/18
|
36
|
+
# figure out how to cache the results of a find_by query, ex:
|
37
|
+
# `find_by('slug' => '/about')`
|
38
|
+
def find_by(content_type:, filter: nil, options: nil)
|
39
|
+
if filter&.keys == ['sys.id']
|
40
|
+
# Direct ID lookup, like what we do in `WCC::Contentful::ModelMethods.resolve`
|
41
|
+
# We can return just this item. Stores are not required to implement :include option.
|
42
|
+
if found = @cache.read(filter['sys.id'])
|
43
|
+
return found
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
store.find_by(content_type: content_type, filter: filter, options: options)
|
48
|
+
end
|
49
|
+
|
50
|
+
delegate :find_all, to: :store
|
51
|
+
|
52
|
+
# #index is called whenever the sync API comes back with more data.
|
53
|
+
def index(json)
|
54
|
+
delegated_result = store.index(json) if store.index?
|
55
|
+
caching_result = _index(json)
|
56
|
+
# _index returns nil if we don't already have it cached - so use the store result.
|
57
|
+
# store result is nil if it doesn't index, so use the caching result if we have it.
|
58
|
+
# They ought to be the same thing if it's cached and the store also indexes.
|
59
|
+
caching_result || delegated_result
|
60
|
+
end
|
61
|
+
|
62
|
+
def index?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
LAZILY_CACHEABLE_TYPES = %w[
|
69
|
+
Entry
|
70
|
+
Asset
|
71
|
+
DeletedEntry
|
72
|
+
DeletedAsset
|
73
|
+
].freeze
|
74
|
+
|
75
|
+
def _index(json)
|
76
|
+
ensure_hash(json)
|
77
|
+
id = json.dig('sys', 'id')
|
78
|
+
type = json.dig('sys', 'type')
|
79
|
+
prev = @cache.read(id)
|
80
|
+
return if prev.nil? && LAZILY_CACHEABLE_TYPES.include?(type)
|
81
|
+
|
82
|
+
if (prev_rev = prev&.dig('sys', 'revision')) && (next_rev = json.dig('sys', 'revision'))
|
83
|
+
return prev if next_rev < prev_rev
|
84
|
+
end
|
85
|
+
|
86
|
+
# we also set DeletedEntry objects in the cache - no need to go hit the API when we know
|
87
|
+
# this is a nil object
|
88
|
+
@cache.write(id, json, expires_in: expires_in)
|
89
|
+
|
90
|
+
case type
|
91
|
+
when 'DeletedEntry', 'DeletedAsset'
|
92
|
+
_instrument 'delete', id: id
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
_instrument 'set', id: id
|
96
|
+
json
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def nil_obj(id)
|
101
|
+
{
|
102
|
+
'sys' => {
|
103
|
+
'id' => id,
|
104
|
+
'type' => 'Nil',
|
105
|
+
'revision' => 1
|
106
|
+
}
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
def ensure_hash(val)
|
111
|
+
raise ArgumentError, 'Value must be a Hash' unless val.is_a?(Hash)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/wcc/contentful/model.rb
CHANGED
@@ -54,15 +54,29 @@ class WCC::Contentful::Model
|
|
54
54
|
|
55
55
|
@@registry = {}
|
56
56
|
|
57
|
+
def self.store(preview = false)
|
58
|
+
if preview
|
59
|
+
if preview_store.nil?
|
60
|
+
raise ArgumentError,
|
61
|
+
'You must include a contentful preview token in your WCC::Contentful.configure block'
|
62
|
+
end
|
63
|
+
preview_store
|
64
|
+
else
|
65
|
+
super()
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
57
69
|
# Finds an Entry or Asset by ID in the configured contentful space
|
58
70
|
# and returns an initialized instance of the appropriate model type.
|
59
71
|
#
|
60
72
|
# Makes use of the {WCC::Contentful::Services#store configured store}
|
61
73
|
# to access the Contentful CDN.
|
62
|
-
def self.find(id,
|
63
|
-
|
74
|
+
def self.find(id, options: nil)
|
75
|
+
options ||= {}
|
76
|
+
raw = store(options[:preview])
|
77
|
+
.find(id, options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
64
78
|
|
65
|
-
new_from_raw(raw,
|
79
|
+
new_from_raw(raw, options) if raw.present?
|
66
80
|
end
|
67
81
|
|
68
82
|
# Creates a new initialized instance of the appropriate model type for the
|
@@ -77,6 +91,8 @@ class WCC::Contentful::Model
|
|
77
91
|
# Accepts a content type ID as a string and returns the Ruby constant
|
78
92
|
# stored in the registry that represents this content type.
|
79
93
|
def self.resolve_constant(content_type)
|
94
|
+
raise ArgumentError, 'content_type cannot be nil' unless content_type
|
95
|
+
|
80
96
|
const = @@registry[content_type]
|
81
97
|
return const if const
|
82
98
|
|
@@ -129,6 +145,24 @@ class WCC::Contentful::Model
|
|
129
145
|
@@registry.dup.freeze
|
130
146
|
end
|
131
147
|
|
148
|
+
def self.reload!
|
149
|
+
registry = self.registry
|
150
|
+
registry.each do |(content_type, klass)|
|
151
|
+
const_name = klass.name
|
152
|
+
begin
|
153
|
+
const = Object.const_missing(const_name)
|
154
|
+
register_for_content_type(content_type, klass: const) if const
|
155
|
+
rescue NameError => e
|
156
|
+
msg = "Error when reloading constant #{const_name} - #{e}"
|
157
|
+
if defined?(Rails) && Rails.logger
|
158
|
+
Rails.logger.error msg
|
159
|
+
else
|
160
|
+
puts msg
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
132
166
|
# Checks if a content type has already been registered to a class and returns
|
133
167
|
# that class. If nil, the generated WCC::Contentful::Model::{content_type} class
|
134
168
|
# will be resolved for this content type.
|