contentful_middleman 1.2.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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