wcc-contentful 0.4.0.pre.alpha → 1.0.0.pre.rc3
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/Guardfile +43 -0
- data/README.md +246 -11
- data/Rakefile +5 -0
- 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 +1 -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 +4 -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 +23 -11
- metadata +299 -116
- data/Gemfile +0 -6
- 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
@@ -1,20 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './interface'
|
4
|
+
|
3
5
|
# @api Store
|
4
6
|
module WCC::Contentful::Store
|
5
7
|
# This is the base class for stores which implement #index, and therefore
|
6
8
|
# must be kept up-to-date via the Sync API.
|
7
|
-
# @abstract At a minimum subclasses should override {#find}, {#
|
9
|
+
# @abstract At a minimum subclasses should override {#find}, {#execute}, {#set},
|
8
10
|
# and #{delete}. As an alternative to overriding set and delete, the subclass
|
9
11
|
# can override {#index}. Index is called when a webhook triggers a sync, to
|
10
12
|
# update the store.
|
13
|
+
#
|
14
|
+
# To implement a new store, you should include the rspec_examples in your rspec
|
15
|
+
# tests for the store. See spec/wcc/contentful/store/memory_store_spec.rb for
|
16
|
+
# an example.
|
11
17
|
class Base
|
12
|
-
|
13
|
-
# @abstract Subclasses should implement this at a minimum to provide data
|
14
|
-
# to the WCC::Contentful::Model API.
|
15
|
-
def find(_id)
|
16
|
-
raise NotImplementedError, "#{self.class} does not implement #find"
|
17
|
-
end
|
18
|
+
include WCC::Contentful::Store::Interface
|
18
19
|
|
19
20
|
# Sets the value of the entry with the given ID in the store.
|
20
21
|
# @abstract
|
@@ -28,6 +29,22 @@ module WCC::Contentful::Store
|
|
28
29
|
raise NotImplementedError, "#{self.class} does not implement #delete"
|
29
30
|
end
|
30
31
|
|
32
|
+
# Executes a WCC::Contentful::Store::Query object created by {#find_all} or
|
33
|
+
# {#find_by}. Implementations should override this to translate the query's
|
34
|
+
# conditions into a query against the datastore.
|
35
|
+
#
|
36
|
+
# For a very naiive implementation see WCC::Contentful::Store::MemoryStore#execute
|
37
|
+
# @abstract
|
38
|
+
def execute(_query)
|
39
|
+
raise NotImplementedError, "#{self.class} does not implement #execute"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if this store can persist entries and assets which are
|
43
|
+
# retrieved from the sync API.
|
44
|
+
def index?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
31
48
|
# Processes a data point received via the Sync API. This can be a published
|
32
49
|
# entry or asset, or a 'DeletedEntry' or 'DeletedAsset'. The default
|
33
50
|
# implementation calls into #set and #delete to perform the appropriate
|
@@ -76,17 +93,19 @@ module WCC::Contentful::Store
|
|
76
93
|
|
77
94
|
# Finds all entries of the given content type. A content type is required.
|
78
95
|
#
|
79
|
-
#
|
80
|
-
#
|
96
|
+
# Subclasses may override this to provide their own query implementation,
|
97
|
+
# or else override #execute to run the query after it has been parsed.
|
81
98
|
# @param [String] content_type The ID of the content type to search for.
|
82
99
|
# @param [Hash] options An optional set of additional parameters to the query
|
83
100
|
# defining for example include depth. Not all store implementations respect all options.
|
84
101
|
# @return [Query] A query object that exposes methods to apply filters
|
85
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
86
102
|
def find_all(content_type:, options: nil)
|
87
|
-
|
103
|
+
Query.new(
|
104
|
+
self,
|
105
|
+
content_type: content_type,
|
106
|
+
options: options
|
107
|
+
)
|
88
108
|
end
|
89
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
90
109
|
|
91
110
|
def initialize
|
92
111
|
@mutex = Concurrent::ReentrantReadWriteLock.new
|
@@ -96,114 +115,10 @@ module WCC::Contentful::Store
|
|
96
115
|
raise ArgumentError, 'Value must be a Hash' unless val.is_a?(Hash)
|
97
116
|
end
|
98
117
|
|
99
|
-
|
118
|
+
private
|
100
119
|
|
101
120
|
attr_reader :mutex
|
102
|
-
|
103
|
-
# The base class for query objects returned by find_all. Subclasses should
|
104
|
-
# override the #result method to return an array-like containing the query
|
105
|
-
# results.
|
106
|
-
class Query
|
107
|
-
delegate :first, to: :result
|
108
|
-
delegate :map, to: :result
|
109
|
-
delegate :count, to: :result
|
110
|
-
|
111
|
-
OPERATORS = %i[
|
112
|
-
eq
|
113
|
-
ne
|
114
|
-
all
|
115
|
-
in
|
116
|
-
nin
|
117
|
-
exists
|
118
|
-
lt
|
119
|
-
lte
|
120
|
-
gt
|
121
|
-
gte
|
122
|
-
query
|
123
|
-
match
|
124
|
-
].freeze
|
125
|
-
|
126
|
-
# @abstract Subclasses should provide this in order to fetch the results
|
127
|
-
# of the query.
|
128
|
-
def result
|
129
|
-
raise NotImplementedError
|
130
|
-
end
|
131
|
-
|
132
|
-
def initialize(store)
|
133
|
-
@store = store
|
134
|
-
end
|
135
|
-
|
136
|
-
# @abstract Subclasses can either override this method to properly respond
|
137
|
-
# to find_by query objects, or they can define a method for each supported
|
138
|
-
# operator. Ex. `#eq`, `#ne`, `#gt`.
|
139
|
-
def apply_operator(operator, field, expected, context = nil)
|
140
|
-
respond_to?(operator) ||
|
141
|
-
raise(ArgumentError, "Operator not implemented: #{operator}")
|
142
|
-
|
143
|
-
public_send(operator, field, expected, context)
|
144
|
-
end
|
145
|
-
|
146
|
-
# Called with a filter object by {Base#find_by} in order to apply the filter.
|
147
|
-
def apply(filter, context = nil)
|
148
|
-
filter.reduce(self) do |query, (field, value)|
|
149
|
-
if value.is_a?(Hash)
|
150
|
-
if op?(k = value.keys.first)
|
151
|
-
query.apply_operator(k.to_sym, field.to_s, value[k], context)
|
152
|
-
else
|
153
|
-
query.nested_conditions(field, value, context)
|
154
|
-
end
|
155
|
-
else
|
156
|
-
query.apply_operator(:eq, field.to_s, value)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
protected
|
162
|
-
|
163
|
-
# naive implementation recursively descends the graph to turns links into
|
164
|
-
# the actual entry data. This calls {Base#find} for each link and so it is
|
165
|
-
# very inefficient.
|
166
|
-
#
|
167
|
-
# @abstract Override this to provide a more efficient implementation for
|
168
|
-
# a given store.
|
169
|
-
def resolve_includes(entry, depth)
|
170
|
-
return entry unless entry && depth && depth > 0 && fields = entry['fields']
|
171
|
-
|
172
|
-
fields.each do |(_name, locales)|
|
173
|
-
# TODO: handle non-* locale
|
174
|
-
locales.each do |(locale, val)|
|
175
|
-
locales[locale] =
|
176
|
-
if val.is_a? Array
|
177
|
-
val.map { |e| resolve_link(e, depth) }
|
178
|
-
else
|
179
|
-
resolve_link(val, depth)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
entry
|
185
|
-
end
|
186
|
-
|
187
|
-
def resolve_link(val, depth)
|
188
|
-
return val unless val.is_a?(Hash) && val.dig('sys', 'type') == 'Link'
|
189
|
-
return val unless included = @store.find(val.dig('sys', 'id'))
|
190
|
-
|
191
|
-
resolve_includes(included, depth - 1)
|
192
|
-
end
|
193
|
-
|
194
|
-
private
|
195
|
-
|
196
|
-
def op?(key)
|
197
|
-
OPERATORS.include?(key.to_sym)
|
198
|
-
end
|
199
|
-
|
200
|
-
def sys?(field)
|
201
|
-
field.to_s =~ /sys\./
|
202
|
-
end
|
203
|
-
|
204
|
-
def id?(field)
|
205
|
-
field.to_sym == :id
|
206
|
-
end
|
207
|
-
end
|
208
121
|
end
|
209
122
|
end
|
123
|
+
|
124
|
+
require_relative './query'
|
@@ -2,13 +2,30 @@
|
|
2
2
|
|
3
3
|
module WCC::Contentful::Store
|
4
4
|
class CDNAdapter
|
5
|
-
|
5
|
+
include WCC::Contentful::Store::Interface
|
6
|
+
# Note: CDNAdapter should not instrument store events cause it's not a store.
|
7
|
+
|
8
|
+
attr_writer :client, :preview_client
|
9
|
+
|
10
|
+
def client
|
11
|
+
@preview ? @preview_client : @client
|
12
|
+
end
|
13
|
+
|
14
|
+
# The CDNAdapter cannot index data coming back from the Sync API.
|
15
|
+
def index?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def index
|
20
|
+
raise NotImplementedError, 'Cannot put data to the CDN!'
|
21
|
+
end
|
6
22
|
|
7
23
|
# Intentionally not implementing write methods
|
8
24
|
|
9
|
-
def initialize(client)
|
25
|
+
def initialize(client = nil, preview: false)
|
10
26
|
super()
|
11
27
|
@client = client
|
28
|
+
@preview = preview
|
12
29
|
end
|
13
30
|
|
14
31
|
def find(key, hint: nil, **options)
|
@@ -37,39 +54,61 @@ module WCC::Contentful::Store
|
|
37
54
|
|
38
55
|
def find_all(content_type:, options: nil)
|
39
56
|
Query.new(
|
40
|
-
|
41
|
-
client:
|
57
|
+
self,
|
58
|
+
client: client,
|
42
59
|
relation: { content_type: content_type },
|
43
60
|
options: options
|
44
61
|
)
|
45
62
|
end
|
46
63
|
|
47
|
-
class Query
|
64
|
+
class Query
|
65
|
+
include WCC::Contentful::Store::Query::Interface
|
66
|
+
include Enumerable
|
67
|
+
|
68
|
+
# by default all enumerable methods delegated to the lazy enumerable
|
69
|
+
delegate(*(Enumerable.instance_methods - Module.instance_methods), to: :to_enum)
|
70
|
+
|
71
|
+
# response.count gets the number of items
|
48
72
|
delegate :count, to: :response
|
49
73
|
|
50
|
-
def
|
74
|
+
def to_enum
|
51
75
|
return response.items unless @options[:include]
|
52
76
|
|
53
77
|
response.items.map { |e| resolve_includes(e, @options[:include]) }
|
54
78
|
end
|
55
79
|
|
56
|
-
def initialize(store
|
80
|
+
def initialize(store, client:, relation:, options: nil, **extra)
|
57
81
|
raise ArgumentError, 'Client cannot be nil' unless client.present?
|
58
82
|
raise ArgumentError, 'content_type must be provided' unless relation[:content_type].present?
|
59
83
|
|
60
|
-
|
84
|
+
@store = store
|
61
85
|
@client = client
|
62
86
|
@relation = relation
|
63
87
|
@options = options || {}
|
64
88
|
@extra = extra || {}
|
65
89
|
end
|
66
90
|
|
91
|
+
# Called with a filter object by {Base#find_by} in order to apply the filter.
|
92
|
+
def apply(filter, context = nil)
|
93
|
+
filter.reduce(self) do |query, (field, value)|
|
94
|
+
if value.is_a?(Hash)
|
95
|
+
if op?(k = value.keys.first)
|
96
|
+
query.apply_operator(k.to_sym, field.to_s, value[k], context)
|
97
|
+
else
|
98
|
+
query.nested_conditions(field, value, context)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
query.apply_operator(:eq, field.to_s, value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
67
106
|
def apply_operator(operator, field, expected, context = nil)
|
68
107
|
op = operator == :eq ? nil : operator
|
69
108
|
param = parameter(field, operator: op, context: context, locale: true)
|
70
109
|
|
71
110
|
self.class.new(
|
72
|
-
|
111
|
+
@store,
|
73
112
|
client: @client,
|
74
113
|
relation: @relation.merge(param => expected),
|
75
114
|
options: @options,
|
@@ -85,7 +124,7 @@ module WCC::Contentful::Store
|
|
85
124
|
end
|
86
125
|
end
|
87
126
|
|
88
|
-
|
127
|
+
WCC::Contentful::Store::Query::Interface::OPERATORS.each do |op|
|
89
128
|
define_method(op) do |field, expected, context = nil|
|
90
129
|
apply_operator(op, field, expected, context)
|
91
130
|
end
|
@@ -93,6 +132,18 @@ module WCC::Contentful::Store
|
|
93
132
|
|
94
133
|
private
|
95
134
|
|
135
|
+
def op?(key)
|
136
|
+
WCC::Contentful::Store::Query::Interface::OPERATORS.include?(key.to_sym)
|
137
|
+
end
|
138
|
+
|
139
|
+
def sys?(field)
|
140
|
+
field.to_s =~ /sys\./
|
141
|
+
end
|
142
|
+
|
143
|
+
def id?(field)
|
144
|
+
field.to_sym == :id
|
145
|
+
end
|
146
|
+
|
96
147
|
def response
|
97
148
|
@response ||=
|
98
149
|
if @relation[:content_type] == 'Asset'
|
@@ -104,11 +155,19 @@ module WCC::Contentful::Store
|
|
104
155
|
end
|
105
156
|
end
|
106
157
|
|
107
|
-
def
|
158
|
+
def resolve_includes(entry, depth)
|
159
|
+
return entry unless entry && depth && depth > 0
|
160
|
+
|
161
|
+
WCC::Contentful::LinkVisitor.new(entry, :Link, :Asset, depth: depth - 1).map! do |val|
|
162
|
+
resolve_link(val)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def resolve_link(val)
|
108
167
|
return val unless val.is_a?(Hash) && val.dig('sys', 'type') == 'Link'
|
109
168
|
return val unless included = response.includes[val.dig('sys', 'id')]
|
110
169
|
|
111
|
-
|
170
|
+
included
|
112
171
|
end
|
113
172
|
|
114
173
|
def parameter(field, operator: nil, context: nil, locale: false)
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative 'memory_store'
|
5
|
+
require_relative 'cdn_adapter'
|
6
|
+
require_relative '../middleware/store'
|
7
|
+
require_relative '../middleware/store/caching_middleware'
|
8
|
+
|
9
|
+
module WCC::Contentful::Store
|
10
|
+
# This factory presents a DSL for configuring the store stack. The store stack
|
11
|
+
# sits in between the Model layer and the datastore, which can be Contentful
|
12
|
+
# or something else like Postgres.
|
13
|
+
#
|
14
|
+
# A set of "presets" are available to get pre-configured stacks based on what
|
15
|
+
# we've found most useful.
|
16
|
+
class Factory
|
17
|
+
attr_reader :preset, :options, :config
|
18
|
+
|
19
|
+
# Set the base store instance.
|
20
|
+
attr_accessor :store
|
21
|
+
|
22
|
+
# An array of tuples that set up and configure a Store middleware.
|
23
|
+
def middleware
|
24
|
+
@middleware ||= self.class.default_middleware.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(config = WCC::Contentful.configuration, preset = :direct, options = nil)
|
28
|
+
@config = config
|
29
|
+
@preset = preset || :custom
|
30
|
+
@options = [*options] || []
|
31
|
+
|
32
|
+
# Infer whether they passed in a store implementation object or class
|
33
|
+
if class_implements_store_interface?(@preset) ||
|
34
|
+
object_implements_store_interface?(@preset)
|
35
|
+
@options.unshift(@preset)
|
36
|
+
@preset = :custom
|
37
|
+
end
|
38
|
+
|
39
|
+
configure_preset(@preset)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds a middleware to the chain. Use a block here to configure the middleware
|
43
|
+
# after it has been created.
|
44
|
+
def use(middleware, *middleware_params, &block)
|
45
|
+
configure_proc = block_given? ? Proc.new(&block) : nil
|
46
|
+
self.middleware << [middleware, middleware_params, configure_proc]
|
47
|
+
end
|
48
|
+
|
49
|
+
def replace(middleware, *middleware_params, &block)
|
50
|
+
idx = self.middleware.find_index { |m| m[0] == middleware }
|
51
|
+
raise ArgumentError, "Middleware #{middleware} not present" if idx.nil?
|
52
|
+
|
53
|
+
configure_proc = block_given? ? Proc.new(&block) : nil
|
54
|
+
self.middleware[idx] = [middleware, middleware_params, configure_proc]
|
55
|
+
end
|
56
|
+
|
57
|
+
def unuse(middleware)
|
58
|
+
idx = self.middleware.find_index { |m| m[0] == middleware }
|
59
|
+
return if idx.nil?
|
60
|
+
|
61
|
+
self.middleware.delete_at idx
|
62
|
+
end
|
63
|
+
|
64
|
+
def build(services = WCC::Contentful::Services.instance)
|
65
|
+
store_instance = build_store(services)
|
66
|
+
options = {
|
67
|
+
config: config,
|
68
|
+
services: services
|
69
|
+
}
|
70
|
+
middleware.reverse
|
71
|
+
.reduce(store_instance) do |memo, middleware_config|
|
72
|
+
# May have added a middleware with `middleware << MyMiddleware.new`
|
73
|
+
middleware_config = [middleware_config] unless middleware_config.is_a? Array
|
74
|
+
|
75
|
+
middleware, params, configure_proc = middleware_config
|
76
|
+
middleware_options = options.merge((params || []).extract_options!)
|
77
|
+
middleware = middleware.call(memo, *params, **middleware_options)
|
78
|
+
middleware&.instance_exec(&configure_proc) if configure_proc
|
79
|
+
middleware || memo
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate!
|
84
|
+
unless preset.nil? || PRESETS.include?(preset)
|
85
|
+
raise ArgumentError, "Please use one of #{PRESETS} instead of #{preset}"
|
86
|
+
end
|
87
|
+
|
88
|
+
middleware.each do |m|
|
89
|
+
next if m[0].respond_to?(:call)
|
90
|
+
|
91
|
+
raise ArgumentError, "The middleware '#{m[0]&.try(:name) || m[0]}' cannot be applied! " \
|
92
|
+
'It must respond to :call'
|
93
|
+
end
|
94
|
+
|
95
|
+
validate_store!(store)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the "eager sync" preset using one of the preregistered stores like :postgres
|
99
|
+
def preset_eager_sync
|
100
|
+
store = options.shift || :memory
|
101
|
+
store = SYNC_STORES[store]&.call(config, *options) if store.is_a?(Symbol)
|
102
|
+
self.store = store
|
103
|
+
end
|
104
|
+
|
105
|
+
# Configures a "lazy sync" preset which caches direct lookups but hits Contentful
|
106
|
+
# for any missing information. The cache is kept up to date by the sync engine.
|
107
|
+
def preset_lazy_sync
|
108
|
+
preset_direct
|
109
|
+
use(WCC::Contentful::Middleware::Store::CachingMiddleware,
|
110
|
+
ActiveSupport::Cache.lookup_store(*options))
|
111
|
+
end
|
112
|
+
|
113
|
+
# Configures the default "direct" preset which passes everything through to
|
114
|
+
# Contentful CDN
|
115
|
+
def preset_direct
|
116
|
+
self.store = CDNAdapter.new(preview: options.include?(:preview))
|
117
|
+
end
|
118
|
+
|
119
|
+
def preset_custom
|
120
|
+
self.store = options.shift
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def validate_store!(store)
|
126
|
+
raise ArgumentError, 'No store provided' unless store
|
127
|
+
|
128
|
+
return true if class_implements_store_interface?(store) ||
|
129
|
+
object_implements_store_interface?(store)
|
130
|
+
|
131
|
+
methods = [*store.try(:instance_methods), *store.try(:methods)]
|
132
|
+
WCC::Contentful::Store::Interface::INTERFACE_METHODS.each do |method|
|
133
|
+
next if methods.include?(method)
|
134
|
+
|
135
|
+
raise ArgumentError, "Custom store '#{store}' must respond to the #{method} method"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def configure_preset(preset)
|
140
|
+
unless respond_to?("preset_#{preset}")
|
141
|
+
raise ArgumentError, "Don't know how to build content delivery method '#{preset}'"
|
142
|
+
end
|
143
|
+
|
144
|
+
public_send("preset_#{preset}")
|
145
|
+
end
|
146
|
+
|
147
|
+
def build_store(services)
|
148
|
+
store_class = store
|
149
|
+
store =
|
150
|
+
if object_implements_store_interface?(store_class)
|
151
|
+
store_class
|
152
|
+
else
|
153
|
+
store_class.new(config, *options - [store_class])
|
154
|
+
end
|
155
|
+
|
156
|
+
# 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
|
163
|
+
|
164
|
+
store
|
165
|
+
end
|
166
|
+
|
167
|
+
def class_implements_store_interface?(klass)
|
168
|
+
(WCC::Contentful::Store::Interface::INTERFACE_METHODS -
|
169
|
+
(klass.try(:instance_methods) || [])).empty?
|
170
|
+
end
|
171
|
+
|
172
|
+
def object_implements_store_interface?(object)
|
173
|
+
(WCC::Contentful::Store::Interface::INTERFACE_METHODS -
|
174
|
+
(object.try(:methods) || [])).empty?
|
175
|
+
end
|
176
|
+
|
177
|
+
class << self
|
178
|
+
# The middleware that by default lives at the top of the middleware stack.
|
179
|
+
def default_middleware
|
180
|
+
[
|
181
|
+
[WCC::Contentful::Store::InstrumentationMiddleware]
|
182
|
+
].freeze
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|