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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 25dc16eb8eaa65244fc568404d2e3cf413c8dd34
4
- data.tar.gz: e0ab36f581ddb8e6848e4aa3e9b0e4dd0611c70a
3
+ metadata.gz: db0206f855bb8bfb8c037a5aabc0c0d1b668db0a
4
+ data.tar.gz: bfc915bd5b1d17c43f8c339fda06af0fa594417a
5
5
  SHA512:
6
- metadata.gz: 8f46e858aa74ed09a603e7e16b27d18d8c62dae561c7952e75fc30f106a8768b0e1363b5dff5905301cbb98b9d37149fab6cb1382188c67112cd1af4f08ce5fe
7
- data.tar.gz: 4a1948b0b9a1e0a40ca1afbf76413b4ad86275ee61590fa63ee1c71fc4bc04cc3360c5ee690a1bed9e7103506307fed28608c05eb97fbe237ea10ca40edbbf40
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
- ```html
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
- ```html
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
- ```html
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
- ```html
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
- ```html
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.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
- webhook_options
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
@@ -1,3 +1,3 @@
1
1
  module ContentfulMiddleman
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.1"
3
3
  end
@@ -14,7 +14,7 @@ module ContentfulMiddleman
14
14
  config[:endpoints] = [{
15
15
  endpoint: '/receive',
16
16
  timeout: options.webhook_timeout,
17
- controller: ::ContentfulMiddleman::WebhookHandler
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
- 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)
75
-
76
- subject.after_configuration
77
- end
76
+ subject.middleman_app.run_hook(:before_server)
77
+ end
78
78
 
79
- it 'gets called if rebuild_on_webhook is true' do
80
- options.rebuild_on_webhook = true
79
+ it 'gets called if rebuild_on_webhook is true' do
80
+ options.rebuild_on_webhook = true
81
81
 
82
- expect(ContentfulMiddleman::WebhookHandler).to receive(:start)
82
+ expect(ContentfulMiddleman::WebhookHandler).to receive(:start)
83
83
 
84
- subject.after_configuration
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