contentful_middleman 1.2.0 → 1.3.1
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 +4 -4
- data/.travis.yml +6 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +0 -2
- data/README.md +61 -5
- data/contentful_middleman.gemspec +1 -1
- data/lib/contentful_middleman/core.rb +17 -5
- data/lib/contentful_middleman/helpers.rb +13 -0
- data/lib/contentful_middleman/tools/preview_proxy.rb +131 -0
- data/lib/contentful_middleman/version.rb +1 -1
- data/lib/contentful_middleman/webhook_handler.rb +1 -1
- data/spec/contentful_middleman/core_spec.rb +10 -11
- data/spec/contentful_middleman/helpers_spec.rb +17 -0
- data/spec/contentful_middleman/tools/preview_proxy_spec.rb +368 -0
- data/spec/fixtures/vcr_fixtures/helpers/preview.yml +211 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper.yml +93 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/asset.yml +104 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/asset_2.yml +104 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/assets.yml +91 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/assets_2.yml +91 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/entries.yml +121 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/entries_2.yml +96 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/entry.yml +106 -0
- data/spec/fixtures/vcr_fixtures/tools/preview_helper/entry_2.yml +119 -0
- data/spec/spec_helper.rb +7 -1
- metadata +28 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db0206f855bb8bfb8c037a5aabc0c0d1b668db0a
|
4
|
+
data.tar.gz: bfc915bd5b1d17c43f8c339fda06af0fa594417a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5661015434c5c51b32aeab1c1c735eaee5cc41f053c2e618e15bde6ab874743af5877ab594fcdad6659b2602cdf0c56bab443368398efae8a3c9c63cfc68f005
|
7
|
+
data.tar.gz: 0a23c10c9ee693e9c3a2f1f8dcf28232c3025ebf0528c055d7e0a9841bcd6448b0ce169a43b8937af36ff654e442f0cee9fdd7885f7688b9ef931c2ece593715
|
data/.travis.yml
CHANGED
@@ -6,7 +6,13 @@ rvm:
|
|
6
6
|
|
7
7
|
script: "bundle exec rake test"
|
8
8
|
|
9
|
+
before_install: gem install bundler -v 1.10.6
|
10
|
+
|
9
11
|
env: TEST=true JRUBY_OPTS='--2.0'
|
10
12
|
notifications:
|
11
13
|
slack:
|
12
14
|
secure: MMWxqKMT4m2UhZ+Ix4wgCs1nLvu9hUFCbyV/qJWmIFif7V4GUKXi6h9krMqGqBAd3YV3pP/InPlw3QoKBUGIAIr5GzDpSnU5ACv7E61v548qViEvPBqsfhRHha2M164rUHn32tpejJnIizVUyND/hIzNviIrab+G8uOaZXUtB5I=
|
15
|
+
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: jruby
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# Change Log
|
2
2
|
## Unreleased
|
3
3
|
|
4
|
+
## 1.3.1
|
5
|
+
### Fixed
|
6
|
+
* Middleman not loading extension due to `@app` reassignment
|
7
|
+
|
8
|
+
## 1.3.0 [YANKED]
|
9
|
+
### Added
|
10
|
+
* Added PreviewProxy for caching Preview API results
|
11
|
+
* Added `#with_preview` view helper for Preview API calls in real-time with a small cache
|
12
|
+
* Added `:webhook_controller` to extension options to be able to customize the Webhook Handler
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
* Moved Webhook Integration to `before_server` hook to avoid multiple Webhook server instances running
|
16
|
+
|
4
17
|
## 1.2.0
|
5
18
|
### Added
|
6
19
|
* Include `:all_entries` option to `activate` block for getting over 1000 entries[#45](https://github.com/contentful-labs/contentful_middleman/issues/45)
|
data/Gemfile
CHANGED
@@ -7,11 +7,9 @@ gem "middleman-core", '~> 3.4'
|
|
7
7
|
# Specify your gem's dependencies in contentful_middleman.gemspec
|
8
8
|
gemspec
|
9
9
|
|
10
|
-
gem "rake"
|
11
10
|
gem "yard"
|
12
11
|
|
13
12
|
# Test tools
|
14
|
-
gem "rspec"
|
15
13
|
gem "simplecov"
|
16
14
|
|
17
15
|
gem "cane", :platforms => [:mri_19, :mri_20], :require => false
|
data/README.md
CHANGED
@@ -6,6 +6,11 @@ Contentful Middleman is a [Middleman](http://middlemanapp.com/) extension to use
|
|
6
6
|
|
7
7
|
Experience the power of Middleman while staying sane as a developer by letting end-users edit content in a web-based interface.
|
8
8
|
|
9
|
+
> The main release works for Middleman v3 - The most used version
|
10
|
+
>
|
11
|
+
> If you are using Middleman v4, consider trying out our [experimental branch](https://github.com/contentful/contentful_middleman/tree/dl/upgrade-to-v4).
|
12
|
+
> Feel free to communicate with us if you are experiencing any bugs.
|
13
|
+
|
9
14
|
|
10
15
|
## Installation
|
11
16
|
Add the following line to the Gemfile of your Middleman project:
|
@@ -54,6 +59,7 @@ use_preview_api | Boolean to toggle the used API. Set it to `false` to use `
|
|
54
59
|
all_entries | Boolean to toggle multiple requests to the API for getting over 1000 entries. This uses a naive approach and can get rate limited. When using this, have in mind adding an `order` in your `:cda_query` . Default order is `order: 'sys.createdAt'`
|
55
60
|
rebuild_on_webhook | Boolean to toggle Webhook server. Server will run in port 5678, and will be expecting to receive Contentful Webhook calls on `/receive`
|
56
61
|
webhook_timeout | Integer (in seconds) for wait time after Webhook received for rebuilding. Only used if `:rebuild_on_webhook` is true. Defaults to 300 seconds
|
62
|
+
webhook_controller | Class for handling Webhook response, defaults to `::ContentfulMiddleman::WebhookHandler`
|
57
63
|
|
58
64
|
You can activate the extension multiple times to import entries from different spaces.
|
59
65
|
|
@@ -183,7 +189,7 @@ data into your templates.
|
|
183
189
|
Consider that we have data stored under `data/partners/partner`. Then in our templates we could use that data like
|
184
190
|
this:
|
185
191
|
|
186
|
-
```
|
192
|
+
```erb
|
187
193
|
<h1>Partners</h1>
|
188
194
|
<ol>
|
189
195
|
<% data.partners.partner.each do |id, partner| %>
|
@@ -213,7 +219,7 @@ Then you have the following methods of accessing locales:
|
|
213
219
|
|
214
220
|
You can access your localized fields by fetching the locale directly from the data
|
215
221
|
|
216
|
-
```
|
222
|
+
```erb
|
217
223
|
<h1>Partners</h1>
|
218
224
|
<ol>
|
219
225
|
<% data.partners.partner.each do |id, partner| %>
|
@@ -226,7 +232,7 @@ You can access your localized fields by fetching the locale directly from the da
|
|
226
232
|
|
227
233
|
You can also map an specific locale for all entry fields using `localize_entry`
|
228
234
|
|
229
|
-
```
|
235
|
+
```erb
|
230
236
|
<h1>Partners</h1>
|
231
237
|
<ol>
|
232
238
|
<% data.partners.partner.each do |id, partner| %>
|
@@ -240,7 +246,7 @@ You can also map an specific locale for all entry fields using `localize_entry`
|
|
240
246
|
|
241
247
|
The `localize` helper will map an specific locale to a field of your entry
|
242
248
|
|
243
|
-
```
|
249
|
+
```erb
|
244
250
|
<h1>Partners</h1>
|
245
251
|
<ol>
|
246
252
|
<% data.partners.partner.each do |id, partner| %>
|
@@ -257,7 +263,7 @@ Or, you can use `localize_value` or `localize_array` if you want more granularit
|
|
257
263
|
> This method is discouraged, as `localize` achieves the same goal and is a field-type
|
258
264
|
agnostic wrapper of these methods.
|
259
265
|
|
260
|
-
```
|
266
|
+
```erb
|
261
267
|
<h1>Partners</h1>
|
262
268
|
<ol>
|
263
269
|
<% data.partners.partner.each do |id, partner| %>
|
@@ -272,3 +278,53 @@ If your fields are not localized, the value of the field will be returned.
|
|
272
278
|
In case of the field being localized but no value being set for a given entry, it will use
|
273
279
|
a fallback locale, by default is `en-US` but can be specified as an additional
|
274
280
|
parameter in all the mentioned calls.
|
281
|
+
|
282
|
+
### Preview API Helper
|
283
|
+
|
284
|
+
You can use the `#with_preview` helper to try your Preview API content without having to
|
285
|
+
generate the entire `data` structures.
|
286
|
+
|
287
|
+
This generates a new Preview Contentful Client and has a cache that will store your objects
|
288
|
+
in memory until they are considered to need refresh.
|
289
|
+
|
290
|
+
It can be used like a Contentful Client:
|
291
|
+
|
292
|
+
```erb
|
293
|
+
<% with_preview(space: 'cfexampleapi', access_token: 'b4c0n73n7fu1') do |preview| %>
|
294
|
+
<% entry = preview.entry('nyancat') %>
|
295
|
+
|
296
|
+
<p>Name: <%= entry.name %></p>
|
297
|
+
<% end %>
|
298
|
+
```
|
299
|
+
|
300
|
+
If you want to clear the cache to force a refresh:
|
301
|
+
|
302
|
+
```erb
|
303
|
+
<% with_preview(space: 'cfexampleapi', access_token: 'b4c0n73n7fu1') do |preview| %>
|
304
|
+
<% preview.clear_cache %>
|
305
|
+
<% end %>
|
306
|
+
```
|
307
|
+
|
308
|
+
#### Caching Rules
|
309
|
+
|
310
|
+
* Every preview client will be cached by Space/Access Token combination
|
311
|
+
* Only `entry`, `entries`, `asset` and `assets` will be cached
|
312
|
+
* Every call will be cached by it's query parameters and ID (if ID is applicable)
|
313
|
+
* Each call will be considered, by default, stale after 3 tries or 2 hours
|
314
|
+
* Cache can be cleared by calling `#clear_cache`, this applies per preview client
|
315
|
+
|
316
|
+
#### Caching Configuration
|
317
|
+
|
318
|
+
You can configure `:tries` and `:expires_in` in the `#with_preview` call like this:
|
319
|
+
|
320
|
+
```erb
|
321
|
+
<% with_preview(
|
322
|
+
space: 'cfexampleapi',
|
323
|
+
access_token: 'b4c0n73n7fu1',
|
324
|
+
tries: 20, # Set Tries to 20 before stale
|
325
|
+
expires_in: ContentfulMiddleman::Tools::PreviewProxy.minutes(5) # Set Expiration to 5 minutes
|
326
|
+
) do |preview| %>
|
327
|
+
<!-- do your stuff -->
|
328
|
+
<% end %>
|
329
|
+
```
|
330
|
+
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# The version of middleman-core your extension depends on
|
22
|
-
s.add_dependency("middleman-core", ["~> 3.
|
22
|
+
s.add_dependency("middleman-core", ["~> 3.4"])
|
23
23
|
|
24
24
|
# Additional dependencies
|
25
25
|
s.add_dependency("contentful", '~> 0.8')
|
@@ -40,9 +40,23 @@ module ContentfulMiddleman
|
|
40
40
|
option :webhook_timeout, 300,
|
41
41
|
"Wait time before rebuild after receiving a Webhook call"
|
42
42
|
|
43
|
+
option :webhook_controller, ::ContentfulMiddleman::WebhookHandler,
|
44
|
+
"Controller for managing Webhook callbacks"
|
45
|
+
|
43
46
|
|
44
47
|
helpers ContentfulMiddleman::Helpers
|
45
48
|
|
49
|
+
attr_reader :middleman_app
|
50
|
+
def initialize(app, options_hash = {}, &block)
|
51
|
+
super
|
52
|
+
@middleman_app = app
|
53
|
+
|
54
|
+
this = self # Hack due to context change
|
55
|
+
app.before_server do
|
56
|
+
this.webhook_options
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
46
60
|
#
|
47
61
|
# Middleman hooks
|
48
62
|
#
|
@@ -50,8 +64,10 @@ module ContentfulMiddleman
|
|
50
64
|
massage_options
|
51
65
|
|
52
66
|
ContentfulMiddleman.instances << (ContentfulMiddleman::Instance.new self)
|
67
|
+
end
|
53
68
|
|
54
|
-
|
69
|
+
def webhook_options
|
70
|
+
::ContentfulMiddleman::WebhookHandler.start(options) if options.rebuild_on_webhook
|
55
71
|
end
|
56
72
|
|
57
73
|
private
|
@@ -84,9 +100,5 @@ module ContentfulMiddleman
|
|
84
100
|
|
85
101
|
options.content_types = new_content_types_options
|
86
102
|
end
|
87
|
-
|
88
|
-
def webhook_options
|
89
|
-
::ContentfulMiddleman::WebhookHandler.start(options) if options.rebuild_on_webhook
|
90
|
-
end
|
91
103
|
end
|
92
104
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'contentful_middleman/tools/preview_proxy'
|
2
|
+
|
1
3
|
module ContentfulMiddleman
|
2
4
|
module Helpers
|
3
5
|
def contentful_instances
|
@@ -32,5 +34,16 @@ module ContentfulMiddleman
|
|
32
34
|
end
|
33
35
|
value
|
34
36
|
end
|
37
|
+
|
38
|
+
def with_preview(space: '', access_token: '', tries: 3, expires_in: ContentfulMiddleman::Tools::PreviewProxy.hours(2), &block)
|
39
|
+
preview_client = ContentfulMiddleman::Tools::PreviewProxy.instance(
|
40
|
+
space: space,
|
41
|
+
access_token: access_token,
|
42
|
+
tries: tries,
|
43
|
+
expires_in: expires_in
|
44
|
+
)
|
45
|
+
|
46
|
+
block.call(preview_client)
|
47
|
+
end
|
35
48
|
end
|
36
49
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'contentful'
|
2
|
+
|
3
|
+
module ContentfulMiddleman
|
4
|
+
module Tools
|
5
|
+
class PreviewProxy < ::Contentful::Client
|
6
|
+
@@instances = []
|
7
|
+
def self.instance(space: '', access_token: '', tries: 3, expires_in: hours(2))
|
8
|
+
possible_instance = @@instances.detect { |i| i[:space] == space && i[:access_token] == access_token }
|
9
|
+
if possible_instance.nil?
|
10
|
+
preview_client = PreviewProxy.new(space: space, access_token: access_token, tries: tries, expires_in: expires_in)
|
11
|
+
@@instances << {space: space, access_token: access_token, instance: preview_client}
|
12
|
+
else
|
13
|
+
preview_client = possible_instance[:instance]
|
14
|
+
end
|
15
|
+
|
16
|
+
preview_client
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.days(amount)
|
20
|
+
amount
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.hours(amount)
|
24
|
+
amount / 24.0
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.minutes(amount)
|
28
|
+
hours(amount) / 60.0
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.seconds(amount)
|
32
|
+
minutes(amount) / 60.0
|
33
|
+
end
|
34
|
+
|
35
|
+
CACHE_MAPPINGS = {
|
36
|
+
entries: {
|
37
|
+
cache: :cached_entry_collection
|
38
|
+
},
|
39
|
+
assets: {
|
40
|
+
cache: :cached_asset_collection
|
41
|
+
},
|
42
|
+
entry: {
|
43
|
+
cache: :cached_entries
|
44
|
+
},
|
45
|
+
asset: {
|
46
|
+
cache: :cached_assets
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
def initialize(space: '', access_token: '', tries: 3, expires_in: self.class.hours(2))
|
51
|
+
super(
|
52
|
+
space: space,
|
53
|
+
access_token: access_token,
|
54
|
+
dynamic_entries: :auto,
|
55
|
+
preview: true
|
56
|
+
)
|
57
|
+
|
58
|
+
@cache_tries = tries
|
59
|
+
@expires_in = expires_in
|
60
|
+
|
61
|
+
clear_cache
|
62
|
+
end
|
63
|
+
|
64
|
+
def entries(query = {})
|
65
|
+
cache(:entries, ->(query, id) { super(query) }, query)
|
66
|
+
end
|
67
|
+
|
68
|
+
def entry(id, query = {})
|
69
|
+
cache(:entry, ->(query, id) { super(id, query) }, query, id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def assets(query = {})
|
73
|
+
cache(:assets, ->(query, id) { super(query) }, query)
|
74
|
+
end
|
75
|
+
|
76
|
+
def asset(id, query = {})
|
77
|
+
cache(:asset, ->(query, id) { super(id, query) }, query, id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def clear_cache
|
81
|
+
@cached_entry_collection = {}
|
82
|
+
@cached_asset_collection = {}
|
83
|
+
|
84
|
+
@cached_entries = {}
|
85
|
+
@cached_assets = {}
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def cache(name, super_call, query = {}, id = '')
|
91
|
+
mapping = CACHE_MAPPINGS[name]
|
92
|
+
|
93
|
+
if should_fetch_from_api?(name, query: query, id: id)
|
94
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)] ||= {}
|
95
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:tries] = 0
|
96
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:expires] = DateTime.now + @expires_in
|
97
|
+
|
98
|
+
new_resources = super_call.call(query, id)
|
99
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:data] = new_resources
|
100
|
+
return new_resources
|
101
|
+
end
|
102
|
+
|
103
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:tries] += 1
|
104
|
+
instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:data]
|
105
|
+
end
|
106
|
+
|
107
|
+
def cache_key(name, query = {}, id = '')
|
108
|
+
mapping = CACHE_MAPPINGS[name]
|
109
|
+
|
110
|
+
Marshal.dump(collection?(name) ? query : query.merge(cache_id: id))
|
111
|
+
end
|
112
|
+
|
113
|
+
def collection?(name)
|
114
|
+
name.to_s.end_with?('s')
|
115
|
+
end
|
116
|
+
|
117
|
+
def should_fetch_from_api?(name, query: {}, id: '')
|
118
|
+
mapping = CACHE_MAPPINGS[name]
|
119
|
+
|
120
|
+
cache = instance_variable_get("@#{mapping[:cache]}")
|
121
|
+
key = cache_key(name, query, id)
|
122
|
+
|
123
|
+
return true unless cache.key?(key)
|
124
|
+
return true if cache[key][:tries] >= @cache_tries
|
125
|
+
return true if cache[key][:expires] <= DateTime.now
|
126
|
+
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -14,7 +14,7 @@ module ContentfulMiddleman
|
|
14
14
|
config[:endpoints] = [{
|
15
15
|
endpoint: '/receive',
|
16
16
|
timeout: options.webhook_timeout,
|
17
|
-
controller:
|
17
|
+
controller: options.webhook_controller
|
18
18
|
}]
|
19
19
|
logger = Logger.new(STDOUT)
|
20
20
|
logger.level = Logger::INFO
|
@@ -68,21 +68,20 @@ describe ContentfulMiddleman::Core do
|
|
68
68
|
|
69
69
|
expect(ContentfulMiddleman.instances.size).to eq(1)
|
70
70
|
end
|
71
|
+
end
|
72
|
+
describe 'webhook handler' do
|
73
|
+
it 'does not get called if rebuild_on_webhook is false' do
|
74
|
+
expect(ContentfulMiddleman::WebhookHandler).not_to receive(:start)
|
71
75
|
|
72
|
-
|
73
|
-
|
74
|
-
expect(ContentfulMiddleman::WebhookHandler).not_to receive(:start)
|
75
|
-
|
76
|
-
subject.after_configuration
|
77
|
-
end
|
76
|
+
subject.middleman_app.run_hook(:before_server)
|
77
|
+
end
|
78
78
|
|
79
|
-
|
80
|
-
|
79
|
+
it 'gets called if rebuild_on_webhook is true' do
|
80
|
+
options.rebuild_on_webhook = true
|
81
81
|
|
82
|
-
|
82
|
+
expect(ContentfulMiddleman::WebhookHandler).to receive(:start)
|
83
83
|
|
84
|
-
|
85
|
-
end
|
84
|
+
subject.middleman_app.run_hook(:before_server)
|
86
85
|
end
|
87
86
|
end
|
88
87
|
end
|
@@ -95,5 +95,22 @@ describe ContentfulMiddleman::Helpers do
|
|
95
95
|
})
|
96
96
|
end
|
97
97
|
end
|
98
|
+
|
99
|
+
describe 'preview helpers' do
|
100
|
+
describe '#with_preview' do
|
101
|
+
it 'creates a preview client' do
|
102
|
+
vcr('helpers/preview') {
|
103
|
+
subject.with_preview(space: 'cfexampleapi', access_token: 'b4c0n73n7fu1') do |preview|
|
104
|
+
expect(preview).to be_a ::Contentful::Client
|
105
|
+
expect(preview).to be_a ::ContentfulMiddleman::Tools::PreviewProxy
|
106
|
+
|
107
|
+
preview_entries = preview.entries
|
108
|
+
expect(preview_entries.size).to eq 11
|
109
|
+
expect(preview_entries).to be_a ::Contentful::Array
|
110
|
+
end
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
98
115
|
end
|
99
116
|
end
|