contentful_middleman 4.0.1 → 4.1.0

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
  SHA256:
3
- metadata.gz: d430a70041785e3a13f1c6261adc2d876bc20543a00c0be6defbd0e22e6a9eca
4
- data.tar.gz: f62729d654a5a66f99d16ffc823acb9acf21d9e58f0fb970f6b70fddce503296
3
+ metadata.gz: 1307e1d94d361acbb2e3dff98d646982d7b437e8d0998c18ce8b4c8d69ae1e66
4
+ data.tar.gz: f56e068dd9463f21a02a587f2c69a9e105dc3194c3db267953a2f51f2676c3ae
5
5
  SHA512:
6
- metadata.gz: 38de6c8762ed8202ad5f722d4ba282029f97f316875c13fd24566867e1e8c0de265d64d6ab992a753c5805c904c4258621fb9b2b1a522599cf3dd28a29e4fd57
7
- data.tar.gz: f3520bdc3bb57a5e8031d64a3e8889efcbdfedde90264d5ca733dbe82efa363332709bb252552c4e615f50e738ffdf0d5d901c6c7c3bc73c947b3414f8fe2c2e
6
+ metadata.gz: 4f02722dce5f2e2be856200bc0162af50f873927432e53f82b45887d1c20f80209659f85b73113480f15bdf434d86c620c0886c8d5e8cbe75a613da709507b00
7
+ data.tar.gz: bd9b842b364b540c4ef7da32dbb22038c2f01ce887c084f43bd892ddc6224b49623851528bf22a945f1cc32e974fc2ec3de504a675ac1e83cdbcb9cf412425e8
data/.travis.yml CHANGED
@@ -1,8 +1,7 @@
1
1
  rvm:
2
- - 2.2.2
3
- - 2.2.6
4
2
  - 2.3.3
5
- - 2.4.0
3
+ - 2.4.4
4
+ - 2.5.1
6
5
  - jruby
7
6
 
8
7
  script: "bundle exec rake test"
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Change Log
2
2
  ## Unreleased
3
3
 
4
+ ## 4.1.0
5
+ ### Added
6
+ * *This feature is currently in BETA state, changes may occur as patch fixes* Added support for RichText and added the `rich_text` view helper.
7
+
4
8
  ## 4.0.1
5
9
  ### Fixed
6
10
  * Allow usage of `use_camel_case` in client options.
data/README.md CHANGED
@@ -50,20 +50,21 @@ activate :contentful do |f|
50
50
  end
51
51
  ```
52
52
 
53
- Parameter | Description
54
- ---------- | ------------
55
- space | Hash with an user choosen name for the space as key and the space id as value
56
- access_token | Contentful Delivery API access token
57
- cda_query | Hash describing query configuration. See [contentful.rb](https://github.com/contentful/contentful.rb) for more info (look for filter options there). Note that by default only 100 entries will be fetched, this can be configured to up to 1000 entries using the `limit` option. Example: `f.cda_query = { limit: 1000 }`
58
- client_options | Hash describing client configuration. See [contentful.rb](https://github.com/contentful/contentful.rb#client-configuration-options) for more info. This option should commonly be used to change Rate Limit Management, Include Resolution, Logging and Proxies.
59
- content_types | Hash describing the mapping applied to entries of the imported content types
60
- default_locale | String with the value for the default locale for your space. Defaults to `'en-US'`.
61
- use_preview_api | Boolean to toggle the used API. Set it to `false` to use `cdn.contentful.com` (default value). Set it to `true` to use `preview.contentful.com`. More info in [the documentation](https://www.contentful.com/developers/documentation/content-delivery-api/#preview-api)
62
- 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'`
63
- all_entries_page_size | Integer amount of items per page for `:all_entries` requests, allowing for smaller page sizes on content heavy requests.
64
- 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`
65
- 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
66
- webhook_controller | Class for handling Webhook response, defaults to `::ContentfulMiddleman::WebhookHandler`
53
+ Parameter | Description
54
+ ---------- | ------------
55
+ space | Hash with an user choosen name for the space as key and the space id as value.
56
+ access_token | Contentful Delivery API access token.
57
+ cda_query | Hash describing query configuration. See [contentful.rb](https://github.com/contentful/contentful.rb) for more info (look for filter options there). Note that by default only 100 entries will be fetched, this can be configured to up to 1000 entries using the `limit` option. Example: `f.cda_query = { limit: 1000 }`.
58
+ client_options | Hash describing client configuration. See [contentful.rb](https://github.com/contentful/contentful.rb#client-configuration-options) for more info. This option should commonly be used to change Rate Limit Management, Include Resolution, Logging and Proxies.
59
+ content_types | Hash describing the mapping applied to entries of the imported content types.
60
+ default_locale | String with the value for the default locale for your space. Defaults to `'en-US'`.
61
+ use_preview_api | Boolean to toggle the used API. Set it to `false` to use `cdn.contentful.com` (default value). Set it to `true` to use `preview.contentful.com`. More info in [the documentation](https://www.contentful.com/developers/documentation/content-delivery-api/#preview-api)
62
+ 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'`.
63
+ all_entries_page_size | Integer amount of items per page for `:all_entries` requests, allowing for smaller page sizes on content heavy requests.
64
+ 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`.
65
+ 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.
66
+ webhook_controller | Class for handling Webhook response, defaults to `::ContentfulMiddleman::WebhookHandler`.
67
+ rich_text_mappings | Hash with `'nodeTyoe' => RendererClass` pairs determining overrides for the [`RichTextRenderer` library](https://github.com/contentful/rich-text-renderer.rb) configuration.
67
68
 
68
69
  You can activate the extension multiple times to import entries from different spaces.
69
70
 
@@ -164,6 +165,45 @@ end
164
165
  *NOTE*: This kind of Composite Mapper is static, therefore if you want to have multiple combinations of mappers
165
166
  for multiple entries, you'd need to write code a bit differently.
166
167
 
168
+ ### Rich Text *BETA*
169
+
170
+ To render rich text in your views, you can use the `rich_text` view helper.
171
+
172
+ An example using `erb`:
173
+
174
+ ```erb
175
+ <% data.my_space.my_type.each do |_, entry| %>
176
+ <%= rich_text(entry.rich_field) %>
177
+ <% end %>
178
+ ```
179
+
180
+ This will output the generated HTML generated by the [`RichTextRenderer` library](https://github.com/contentful/rich-text-renderer.rb).
181
+
182
+ #### Adding custom renderers
183
+
184
+ When using rich text, if you're planning to embed entries, then you need to create your custom renderer for them. You can read how create your own renderer classes [here](https://github.com/contentful/rich-text-renderer.rb#using-different-renderers).
185
+
186
+ To configure the mappings, you need to add them in your `activate` block like follows:
187
+
188
+ ```ruby
189
+ activate :contentful do |f|
190
+ # ... all the regular config ...
191
+ f.rich_text_mappings = { 'embedded-entry-block' => MyCustomRenderer }
192
+ end
193
+ ```
194
+
195
+ You can also add renderers for all other types of nodes if you want to have more granular control over the rendering.
196
+
197
+ #### Using the helper with multiple activated Contentful extensions
198
+
199
+ In case you have multiple activated extensions, and have different mapping configurations for them. You can specify which extension instance you want to pull the configuration from when using the helper.
200
+
201
+ The helper receives an additional optional parameter for the extension instance. By default it is `0`, indicating the first activated extension.
202
+
203
+ The instances are sequentially numbered in order of activation, starting from 0.
204
+
205
+ So, if for example you have 2 active instances with different configuration, to use the second instance configuration, you should call the helper as: `rich_text(entry.rich_field, 1)`.
206
+
167
207
  ## Configuration: examples
168
208
 
169
209
  ```ruby
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  # Additional dependencies
26
26
  s.add_dependency("contentful", '~> 2.1')
27
27
  s.add_dependency("contentful-webhook-listener", '~> 0.1')
28
+ s.add_dependency("rich_text_renderer", '~> 0.1')
28
29
 
29
30
  s.add_development_dependency 'rubygems-tasks', '~> 0.2'
30
31
  s.add_development_dependency "guard"
@@ -52,6 +52,9 @@ module ContentfulMiddleman
52
52
  option :webhook_controller, ::ContentfulMiddleman::WebhookHandler,
53
53
  "Controller for managing Webhook callbacks"
54
54
 
55
+ option :rich_text_mappings, {},
56
+ "Custom renderers for the RichTextRenderer library"
57
+
55
58
 
56
59
  helpers ContentfulMiddleman::Helpers
57
60
  include ContentfulMiddleman::Helpers
@@ -62,6 +65,7 @@ module ContentfulMiddleman
62
65
  expose_to_template localize: :localize
63
66
  expose_to_template localize_array: :localize_array
64
67
  expose_to_template localize_value: :localize_value
68
+ expose_to_template rich_text: :rich_text
65
69
 
66
70
  attr_reader :middleman_app
67
71
  def initialize(app, options_hash = {}, &block)
@@ -1,5 +1,6 @@
1
1
  require 'thor/core_ext/hash_with_indifferent_access'
2
2
  require 'contentful_middleman/tools/preview_proxy'
3
+ require 'rich_text_renderer'
3
4
 
4
5
  module ContentfulMiddleman
5
6
  module Helpers
@@ -47,5 +48,15 @@ module ContentfulMiddleman
47
48
 
48
49
  block.call(preview_client)
49
50
  end
51
+
52
+ def rich_text(document_field, instance_index = 0)
53
+ mappings = begin
54
+ app.extensions[:contentful]["instance_#{instance_index}"].options[:rich_text_mappings] || {}
55
+ rescue Exception
56
+ {}
57
+ end
58
+
59
+ RichTextRenderer::Renderer.new(mappings).render(document_field)
60
+ end
50
61
  end
51
62
  end
@@ -61,6 +61,8 @@ module ContentfulMiddleman
61
61
  map_entry(value)
62
62
  when Array
63
63
  map_array(value, locale)
64
+ when Hash
65
+ map_hash(value, locale)
64
66
  else
65
67
  value
66
68
  end
@@ -140,6 +142,17 @@ module ContentfulMiddleman
140
142
  def map_array(array, locale = nil)
141
143
  array.map {|element| map_value(element, locale)}
142
144
  end
145
+
146
+ def map_hash(hash, locale = nil)
147
+ return hash.transform_values {|element| map_value(element, locale)} if hash.respond_to?(:transform_values)
148
+
149
+ # Support for Ruby versions previous to 2.4
150
+ result = {}
151
+ hash.each do |k, v|
152
+ result[k] = map_value(v, locale)
153
+ end
154
+ result
155
+ end
143
156
  end
144
157
  end
145
158
  end
@@ -1,3 +1,3 @@
1
1
  module ContentfulMiddleman
2
- VERSION = "4.0.1"
2
+ VERSION = "4.1.0"
3
3
  end
@@ -1,7 +1,51 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class CustomEntryRenderer < RichTextRenderer::BaseNodeRenderer
4
+ def render(node)
5
+ "<div>Custom Content</div>"
6
+ end
7
+ end
8
+
9
+ class OtherCustomEntryRenderer < RichTextRenderer::BaseNodeRenderer
10
+ def render(node)
11
+ "<h1>#{node['data'].body}</h1>"
12
+ end
13
+ end
14
+
15
+ class InstanceMock
16
+ def initialize(mappings = {})
17
+ @mappings = mappings
18
+ end
19
+
20
+ def options
21
+ {
22
+ rich_text_mappings: @mappings
23
+ }
24
+ end
25
+ end
26
+
27
+ class AppMock
28
+ def initialize(instances = {})
29
+ @instances = instances
30
+ end
31
+
32
+ def extensions
33
+ {
34
+ contentful: @instances
35
+ }
36
+ end
37
+ end
38
+
3
39
  class HelpersMock
4
40
  include ContentfulMiddleman::Helpers
41
+
42
+ def initialize(instances = {})
43
+ @instances = instances
44
+ end
45
+
46
+ def app
47
+ AppMock.new(@instances)
48
+ end
5
49
  end
6
50
 
7
51
  class InstanceDouble
@@ -178,5 +222,109 @@ describe ContentfulMiddleman::Helpers do
178
222
  end
179
223
  end
180
224
  end
225
+
226
+ describe 'rich text helpers' do
227
+ describe '#rich_text' do
228
+ it 'renders a rich text field to HTML' do
229
+ expected = [
230
+ '<h1>Some heading</h1>',
231
+ '<p></p>',
232
+ '<div>{"target"=>{"sys"=>{"id"=>"49rofLvvxCOiIMIi6mk8ai", "type"=>"Link", "linkType"=>"Entry"}}}</div>',
233
+ '<h2>Some subheading</h2>',
234
+ '<p><b>Some bold</b></p>',
235
+ '<p><i>Some italics</i></p>',
236
+ '<p><u>Some underline</u></p>',
237
+ '<p></p>',
238
+ '<p></p>',
239
+ '<div>{"target"=>{"sys"=>{"id"=>"5ZF9Q4K6iWSYIU2OUs0UaQ", "type"=>"Link", "linkType"=>"Entry"}}}</div>',
240
+ '<p></p>',
241
+ '<p>Some raw content</p>',
242
+ '<p></p>',
243
+ '<p>An unpublished embed:</p>',
244
+ '<p></p>',
245
+ '<div>{"target"=>{"sys"=>{"id"=>"q2hGXkd5tICym64AcgeKK", "type"=>"Link", "linkType"=>"Entry"}}}</div>',
246
+ '<p>Some more content</p>'
247
+ ].join("\n")
248
+
249
+ expect(subject.rich_text(json('structured_text'))).to eq expected
250
+ end
251
+
252
+ it 'supports multiple configurations' do
253
+ vcr('helpers/rich_text') {
254
+ # Instances are a 0-based progressive hash with keys in the shape "instance_#{index}"
255
+ instances = {
256
+ "instance_0" => InstanceMock.new('embedded-entry-block' => CustomEntryRenderer),
257
+ "instance_1" => InstanceMock.new('embedded-entry-block' => OtherCustomEntryRenderer)
258
+ }
259
+ subject = HelpersMock.new(instances)
260
+
261
+ expected_default = [
262
+ '<h1>Some heading</h1>',
263
+ '<p></p>',
264
+ '<div>Custom Content</div>',
265
+ '<h2>Some subheading</h2>',
266
+ '<p><b>Some bold</b></p>',
267
+ '<p><i>Some italics</i></p>',
268
+ '<p><u>Some underline</u></p>',
269
+ '<p></p>',
270
+ '<p></p>',
271
+ '<div>Custom Content</div>',
272
+ '<p></p>',
273
+ '<p>Some raw content</p>',
274
+ '<p></p>',
275
+ '<p>An unpublished embed:</p>',
276
+ '<p></p>',
277
+ '<p>Some more content</p>',
278
+ '<p><code>Some code</code></p>',
279
+ '<p><a href="https://www.contentful.com">A hyperlink</a></p>',
280
+ '<ul><li><p>Ul list</p></li><li><p>A few <b>items</b></p><ol><li><p>Ordered list nested inside an Unordered list item</p></li></ol></li></ul>',
281
+ '<ol><li><p>Ol list</p></li><li><p>two</p></li><li><p>three</p></li></ol>',
282
+ '<hr />',
283
+ '<p></p>',
284
+ '<blockqoute><p>An inspirational quote</p><p></p></blockqoute>',
285
+ '<p></p>'
286
+ ].join("\n")
287
+
288
+ expected_different_config = [
289
+ '<h1>Some heading</h1>',
290
+ '<p></p>',
291
+ '<h1>Embedded 1</h1>',
292
+ '<h2>Some subheading</h2>',
293
+ '<p><b>Some bold</b></p>',
294
+ '<p><i>Some italics</i></p>',
295
+ '<p><u>Some underline</u></p>',
296
+ '<p></p>',
297
+ '<p></p>',
298
+ '<h1>Embedded 2</h1>',
299
+ '<p></p>',
300
+ '<p>Some raw content</p>',
301
+ '<p></p>',
302
+ '<p>An unpublished embed:</p>',
303
+ '<p></p>',
304
+ '<p>Some more content</p>',
305
+ '<p><code>Some code</code></p>',
306
+ '<p><a href="https://www.contentful.com">A hyperlink</a></p>',
307
+ '<ul><li><p>Ul list</p></li><li><p>A few <b>items</b></p><ol><li><p>Ordered list nested inside an Unordered list item</p></li></ol></li></ul>',
308
+ '<ol><li><p>Ol list</p></li><li><p>two</p></li><li><p>three</p></li></ol>',
309
+ '<hr />',
310
+ '<p></p>',
311
+ '<blockqoute><p>An inspirational quote</p><p></p></blockqoute>',
312
+ '<p></p>'
313
+ ].join("\n")
314
+
315
+ client = Contentful::Client.new(
316
+ space: 'jd7yc4wnatx3',
317
+ access_token: '6256b8ef7d66805ca41f2728271daf27e8fa6055873b802a813941a0fe696248',
318
+ dynamic_entries: :auto,
319
+ gzip_encoded: false
320
+ )
321
+ entry = client.entry('4BupPSmi4M02m0U48AQCSM')
322
+
323
+ expect(subject.rich_text(entry.body)).to eq expected_default
324
+ expect(subject.rich_text(entry.body, 1)).to eq expected_different_config
325
+ }
326
+ end
327
+ end
328
+ end
181
329
  end
182
330
  end
File without changes
@@ -0,0 +1,206 @@
1
+ {
2
+ "content": [
3
+ {
4
+ "data": {},
5
+ "content": [
6
+ {
7
+ "marks": [],
8
+ "value": "Some heading",
9
+ "nodeType": "text",
10
+ "nodeClass": "text"
11
+ }
12
+ ],
13
+ "nodeType": "heading-1",
14
+ "nodeClass": "block"
15
+ },
16
+ {
17
+ "data": {},
18
+ "content": [
19
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
20
+ ],
21
+ "nodeType": "paragraph",
22
+ "nodeClass": "block"
23
+ },
24
+ {
25
+ "data": {
26
+ "target": {
27
+ "sys": {
28
+ "id": "49rofLvvxCOiIMIi6mk8ai",
29
+ "type": "Link",
30
+ "linkType": "Entry"
31
+ }
32
+ }
33
+ },
34
+ "content": [
35
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
36
+ ],
37
+ "nodeType": "embedded-entry-block",
38
+ "nodeClass": "block"
39
+ },
40
+ {
41
+ "data": {},
42
+ "content": [
43
+ {
44
+ "marks": [],
45
+ "value": "Some subheading",
46
+ "nodeType": "text",
47
+ "nodeClass": "text"
48
+ }
49
+ ],
50
+ "nodeType": "heading-2",
51
+ "nodeClass": "block"
52
+ },
53
+ {
54
+ "data": {},
55
+ "content": [
56
+ {
57
+ "marks": [{"data": {}, "type": "bold", "object": "mark"}],
58
+ "value": "Some bold",
59
+ "nodeType": "text",
60
+ "nodeClass": "text"
61
+ }
62
+ ],
63
+ "nodeType": "paragraph",
64
+ "nodeClass": "block"
65
+ },
66
+ {
67
+ "data": {},
68
+ "content": [
69
+ {
70
+ "marks": [{"data": {}, "type": "italic", "object": "mark"}],
71
+ "value": "Some italics",
72
+ "nodeType": "text",
73
+ "nodeClass": "text"
74
+ }
75
+ ],
76
+ "nodeType": "paragraph",
77
+ "nodeClass": "block"
78
+ },
79
+ {
80
+ "data": {},
81
+ "content": [
82
+ {
83
+ "marks": [{"data": {}, "type": "underline", "object": "mark"}],
84
+ "value": "Some underline",
85
+ "nodeType": "text",
86
+ "nodeClass": "text"
87
+ }
88
+ ],
89
+ "nodeType": "paragraph",
90
+ "nodeClass": "block"
91
+ },
92
+ {
93
+ "data": {},
94
+ "content": [
95
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
96
+ ],
97
+ "nodeType": "paragraph",
98
+ "nodeClass": "block"
99
+ },
100
+ {
101
+ "data": {},
102
+ "content": [
103
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
104
+ ],
105
+ "nodeType": "paragraph",
106
+ "nodeClass": "block"
107
+ },
108
+ {
109
+ "data": {
110
+ "target": {
111
+ "sys": {
112
+ "id": "5ZF9Q4K6iWSYIU2OUs0UaQ",
113
+ "type": "Link",
114
+ "linkType": "Entry"
115
+ }
116
+ }
117
+ },
118
+ "content": [
119
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
120
+ ],
121
+ "nodeType": "embedded-entry-block",
122
+ "nodeClass": "block"
123
+ },
124
+ {
125
+ "data": {},
126
+ "content": [
127
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
128
+ ],
129
+ "nodeType": "paragraph",
130
+ "nodeClass": "block"
131
+ },
132
+ {
133
+ "data": {},
134
+ "content": [
135
+ {
136
+ "marks": [],
137
+ "value": "Some raw content",
138
+ "nodeType": "text",
139
+ "nodeClass": "text"
140
+ }
141
+ ],
142
+ "nodeType": "paragraph",
143
+ "nodeClass": "block"
144
+ },
145
+ {
146
+ "data": {},
147
+ "content": [
148
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
149
+ ],
150
+ "nodeType": "paragraph",
151
+ "nodeClass": "block"
152
+ },
153
+ {
154
+ "data": {},
155
+ "content": [
156
+ {
157
+ "marks": [],
158
+ "value": "An unpublished embed:",
159
+ "nodeType": "text",
160
+ "nodeClass": "text"
161
+ }
162
+ ],
163
+ "nodeType": "paragraph",
164
+ "nodeClass": "block"
165
+ },
166
+ {
167
+ "data": {},
168
+ "content": [
169
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
170
+ ],
171
+ "nodeType": "paragraph",
172
+ "nodeClass": "block"
173
+ },
174
+ {
175
+ "data": {
176
+ "target": {
177
+ "sys": {
178
+ "id": "q2hGXkd5tICym64AcgeKK",
179
+ "type": "Link",
180
+ "linkType": "Entry"
181
+ }
182
+ }
183
+ },
184
+ "content": [
185
+ {"marks": [], "value": "", "nodeType": "text", "nodeClass": "text"}
186
+ ],
187
+ "nodeType": "embedded-entry-block",
188
+ "nodeClass": "block"
189
+ },
190
+ {
191
+ "data": {},
192
+ "content": [
193
+ {
194
+ "marks": [],
195
+ "value": "Some more content",
196
+ "nodeType": "text",
197
+ "nodeClass": "text"
198
+ }
199
+ ],
200
+ "nodeType": "paragraph",
201
+ "nodeClass": "block"
202
+ }
203
+ ],
204
+ "nodeType": "document",
205
+ "nodeClass": "document"
206
+ }