prismic.io 1.0.0.preview.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 +7 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +37 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/lib/prismic.rb +167 -0
- data/lib/prismic/api.rb +94 -0
- data/lib/prismic/form.rb +19 -0
- data/lib/prismic/fragments.rb +11 -0
- data/lib/prismic/fragments/block.rb +4 -0
- data/lib/prismic/fragments/color.rb +31 -0
- data/lib/prismic/fragments/date.rb +15 -0
- data/lib/prismic/fragments/embed.rb +23 -0
- data/lib/prismic/fragments/image.rb +47 -0
- data/lib/prismic/fragments/link.rb +47 -0
- data/lib/prismic/fragments/multiple.rb +29 -0
- data/lib/prismic/fragments/number.rb +19 -0
- data/lib/prismic/fragments/select.rb +15 -0
- data/lib/prismic/fragments/structured_text.rb +200 -0
- data/lib/prismic/fragments/text.rb +15 -0
- data/lib/prismic/json_parsers.rb +174 -0
- data/lib/prismic/version.rb +5 -0
- data/prismic.gemspec +26 -0
- data/spec/fragments_spec.rb +372 -0
- data/spec/json_parsers_spec.rb +287 -0
- data/spec/prismic_spec.rb +237 -0
- data/spec/responses_mocks/api.json +192 -0
- data/spec/responses_mocks/document.json +170 -0
- data/spec/responses_mocks/fragments.json +176 -0
- data/spec/responses_mocks/structured_text_heading.json +12 -0
- data/spec/responses_mocks/structured_text_paragraph.json +30 -0
- data/spec/spec_helper.rb +10 -0
- metadata +157 -0
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Api' do
|
4
|
+
before do
|
5
|
+
json_representation = '{"foo": "bar"}'
|
6
|
+
@api = Prismic::API.new({
|
7
|
+
json: json_representation,
|
8
|
+
bookmarks: {},
|
9
|
+
tags: {},
|
10
|
+
types: {},
|
11
|
+
refs: {
|
12
|
+
'key1' => Prismic::Ref.new('ref1', 'label1'),
|
13
|
+
'key2' => Prismic::Ref.new('ref2', 'label2'),
|
14
|
+
'key3' => Prismic::Ref.new('ref3', 'label3', true),
|
15
|
+
'key4' => Prismic::Ref.new('ref4', 'label4'),
|
16
|
+
},
|
17
|
+
forms: {
|
18
|
+
'form1' => Prismic::SearchForm.new(Prismic::Form.new('form1', {}, nil, nil, nil, nil)),
|
19
|
+
'form2' => Prismic::SearchForm.new(Prismic::Form.new('form2', {}, nil, nil, nil, nil)),
|
20
|
+
'form3' => Prismic::SearchForm.new(Prismic::Form.new('form3', {}, nil, nil, nil, nil)),
|
21
|
+
'form4' => Prismic::SearchForm.new(Prismic::Form.new('form4', {}, nil, nil, nil, nil)),
|
22
|
+
},
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'ref' do
|
27
|
+
it "returns the right Ref" do
|
28
|
+
@api.ref('key2').label.should == 'label2'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'refs' do
|
33
|
+
it "returns the correct number of elements" do
|
34
|
+
@api.refs.size.should == 4
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'ref_id_by_label' do
|
39
|
+
it "returns the id of the ref" do
|
40
|
+
@api.ref_id_by_label('key4').ref == 'ref4'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'form' do
|
45
|
+
it "return the right Form" do
|
46
|
+
@api.form('form2').form.name.should == 'form2'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'forms' do
|
51
|
+
it "returns the correct number of elements" do
|
52
|
+
@api.forms.size.should == 4
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'master' do
|
57
|
+
it "returns a master Ref" do
|
58
|
+
@api.master.master?.should be_true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'parse_api_response' do
|
63
|
+
before do
|
64
|
+
@data = File.read("#{File.dirname(__FILE__)}/responses_mocks/api.json")
|
65
|
+
@json = JSON.parse(@data)
|
66
|
+
@parsed = Prismic::API.parse_api_response(@json)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not allow to be created without master Ref" do
|
70
|
+
expect {
|
71
|
+
Prismic::API.parse_api_response({
|
72
|
+
"refs" => [],
|
73
|
+
})
|
74
|
+
}.to raise_error(Prismic::API::BadPrismicResponseError, "No master Ref found")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "does not allow to be created without any Ref" do
|
78
|
+
expect {
|
79
|
+
Prismic::API.parse_api_response({})
|
80
|
+
}.to raise_error(Prismic::API::BadPrismicResponseError, "No refs given")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "creates 2 refs" do
|
84
|
+
@parsed.refs.size.should == 2
|
85
|
+
end
|
86
|
+
|
87
|
+
it "creates the right ref's ref" do
|
88
|
+
@parsed.refs['bar'].ref.should == 'foo'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "creates the right ref's label" do
|
92
|
+
@parsed.refs['bar'].label.should == 'bar'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "creates the right ref's 'master' value" do
|
96
|
+
@parsed.refs['bar'].master?.should == false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "creates 3 bookmarks" do
|
100
|
+
@parsed.bookmarks.size.should == 3
|
101
|
+
end
|
102
|
+
|
103
|
+
it "creates the right bookmarks" do
|
104
|
+
@parsed.bookmarks['about'].should == 'Ue0EDd_mqb8Dhk3j'
|
105
|
+
end
|
106
|
+
|
107
|
+
it "creates 6 types" do
|
108
|
+
@parsed.types.size.should == 6
|
109
|
+
end
|
110
|
+
|
111
|
+
it "creates the right types" do
|
112
|
+
@parsed.types['blog-post'].should == 'Blog post'
|
113
|
+
end
|
114
|
+
|
115
|
+
it "creates 4 tags" do
|
116
|
+
@parsed.tags.size.should == 4
|
117
|
+
end
|
118
|
+
|
119
|
+
it "creates the right tags" do
|
120
|
+
@parsed.tags.should include 'Cupcake'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "creates 10 forms" do
|
124
|
+
@parsed.forms.size.should == 10
|
125
|
+
end
|
126
|
+
|
127
|
+
it "creates the right form's name" do
|
128
|
+
@parsed.forms['pies'].name.should == 'Little Pies'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "creates the right form's method" do
|
132
|
+
@parsed.forms['pies'].form_method.should == 'GET'
|
133
|
+
end
|
134
|
+
|
135
|
+
it "creates the right form's rel" do
|
136
|
+
@parsed.forms['pies'].rel.should == 'collection'
|
137
|
+
end
|
138
|
+
|
139
|
+
it "creates the right form's enctype" do
|
140
|
+
@parsed.forms['pies'].enctype.should == 'application/x-www-form-urlencoded'
|
141
|
+
end
|
142
|
+
|
143
|
+
it "creates the right form's action" do
|
144
|
+
@parsed.forms['pies'].action.should == 'http://lesbonneschoses.wroom.io/api/documents/search'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "creates forms with the right fields" do
|
148
|
+
@parsed.forms['pies'].fields.size.should == 2
|
149
|
+
end
|
150
|
+
|
151
|
+
it "creates forms with the right type info" do
|
152
|
+
@parsed.forms['pies'].fields['ref'].field_type.should == 'String'
|
153
|
+
end
|
154
|
+
|
155
|
+
it "creates forms with the right default info" do
|
156
|
+
@parsed.forms['pies'].fields['q'].default.should ==
|
157
|
+
'[[at(document.tags, ["Pie"])][any(document.type, ["product"])]]'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'as_json' do
|
162
|
+
before do
|
163
|
+
@json = @api.as_json
|
164
|
+
end
|
165
|
+
|
166
|
+
it "returns the json representation of the api" do
|
167
|
+
JSON.parse(@json)['foo'].should == 'bar'
|
168
|
+
end
|
169
|
+
|
170
|
+
it "returns the json representation of the api containing one single element" do
|
171
|
+
JSON.parse(@json).size.should == 1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'Document' do
|
177
|
+
before do
|
178
|
+
fragments = {
|
179
|
+
'field1' => Prismic::Fragments::DocumentLink.new(nil, nil, nil, nil, nil),
|
180
|
+
'field2' => Prismic::Fragments::WebLink.new('weburl')
|
181
|
+
}
|
182
|
+
@document = Prismic::Document.new(nil, nil, nil, nil, ['my-slug'], fragments)
|
183
|
+
@link_resolver = Prismic::LinkResolver.new('master'){|doc_link|
|
184
|
+
"http://host/#{doc_link.id}"
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'slug' do
|
189
|
+
it "returns the first slug if found" do
|
190
|
+
@document.slug.should == 'my-slug'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "returns '-' if no slug found" do
|
194
|
+
@document.slugs = []
|
195
|
+
@document.slug.should == '-'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe 'as_html' do
|
200
|
+
it "returns a <section> HTML element" do
|
201
|
+
Nokogiri::XML(@document.as_html(@link_resolver)).child.name.should == 'section'
|
202
|
+
end
|
203
|
+
|
204
|
+
it "returns a HTML element with a 'data-field' attribute" do
|
205
|
+
Nokogiri::XML(@document.as_html(@link_resolver)).child.has_attribute?('data-field').should be_true
|
206
|
+
end
|
207
|
+
|
208
|
+
it "returns a HTML element with a 'data-field' attribute containing the name of the field" do
|
209
|
+
Nokogiri::XML(@document.as_html(@link_resolver)).child.attribute('data-field').value.should == 'field1'
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe 'SearchForm' do
|
215
|
+
before do
|
216
|
+
@field = Prismic::Field.new('String', '[foo]')
|
217
|
+
end
|
218
|
+
|
219
|
+
describe 'query' do
|
220
|
+
it "adds the query to the form's data" do
|
221
|
+
@form = Prismic::SearchForm.new(Prismic::Form.new('form1', {}, nil, nil, nil, nil), {})
|
222
|
+
@form.query('[bar]')
|
223
|
+
@form.data.should == { 'q' => '[bar]' }
|
224
|
+
end
|
225
|
+
|
226
|
+
it "adds existant non-nil queries (in fields) to the form's data" do
|
227
|
+
@form = Prismic::SearchForm.new(Prismic::Form.new('form1', {'q' => @field}, nil, nil, nil, nil), {})
|
228
|
+
@form.query('[bar]')
|
229
|
+
@form.data.should == { 'q' => '[foobar]' }
|
230
|
+
end
|
231
|
+
|
232
|
+
it "returns the form itself" do
|
233
|
+
@form = Prismic::SearchForm.new(Prismic::Form.new('form1', {'q' => @field}, nil, nil, nil, nil), {})
|
234
|
+
@form.query('[foo]').should == @form
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
{
|
2
|
+
"refs":[
|
3
|
+
{
|
4
|
+
"ref":"UfuJ2d_mqXoNBZDt",
|
5
|
+
"label":"Master",
|
6
|
+
"isMasterRef":true
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"ref":"foo",
|
10
|
+
"label":"bar",
|
11
|
+
"isMasterRef":false
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"bookmarks":{
|
15
|
+
"about":"Ue0EDd_mqb8Dhk3j",
|
16
|
+
"jobs":"Ue0EHN_mqbwDhk3l",
|
17
|
+
"stores":"Ue0EVt_mqd8Dhk3n"
|
18
|
+
},
|
19
|
+
"types":{
|
20
|
+
"blog-post":"Blog post",
|
21
|
+
"store":"Store",
|
22
|
+
"article":"Site-level article",
|
23
|
+
"selection":"Products selection",
|
24
|
+
"job-offer":"Job offer",
|
25
|
+
"product":"Product"
|
26
|
+
},
|
27
|
+
"tags":[
|
28
|
+
"Cupcake",
|
29
|
+
"Featured",
|
30
|
+
"Pie",
|
31
|
+
"Macaron"
|
32
|
+
],
|
33
|
+
"forms":{
|
34
|
+
"featured":{
|
35
|
+
"name":"Featured on homepage",
|
36
|
+
"method":"GET",
|
37
|
+
"rel":"collection",
|
38
|
+
"enctype":"application/x-www-form-urlencoded",
|
39
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
40
|
+
"fields":{
|
41
|
+
"ref":{
|
42
|
+
"type":"String"
|
43
|
+
},
|
44
|
+
"q":{
|
45
|
+
"default":"[[at(document.tags, [\"Featured\"])][any(document.type, [\"product\",\"selection\",\"blog-post\"])]]",
|
46
|
+
"type":"String"
|
47
|
+
}
|
48
|
+
}
|
49
|
+
},
|
50
|
+
"products":{
|
51
|
+
"name":"All Products",
|
52
|
+
"method":"GET",
|
53
|
+
"rel":"collection",
|
54
|
+
"enctype":"application/x-www-form-urlencoded",
|
55
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
56
|
+
"fields":{
|
57
|
+
"ref":{
|
58
|
+
"type":"String"
|
59
|
+
},
|
60
|
+
"q":{
|
61
|
+
"default":"[[any(document.type, [\"product\"])]]",
|
62
|
+
"type":"String"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
},
|
66
|
+
"macarons":{
|
67
|
+
"name":"Macarons",
|
68
|
+
"method":"GET",
|
69
|
+
"rel":"collection",
|
70
|
+
"enctype":"application/x-www-form-urlencoded",
|
71
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
72
|
+
"fields":{
|
73
|
+
"ref":{
|
74
|
+
"type":"String"
|
75
|
+
},
|
76
|
+
"q":{
|
77
|
+
"default":"[[at(document.tags, [\"Macaron\"])][any(document.type, [\"product\"])]]",
|
78
|
+
"type":"String"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
},
|
82
|
+
"cupcakes":{
|
83
|
+
"name":"Cupcakes",
|
84
|
+
"method":"GET",
|
85
|
+
"rel":"collection",
|
86
|
+
"enctype":"application/x-www-form-urlencoded",
|
87
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
88
|
+
"fields":{
|
89
|
+
"ref":{
|
90
|
+
"type":"String"
|
91
|
+
},
|
92
|
+
"q":{
|
93
|
+
"default":"[[at(document.tags, [\"Cupcake\"])][any(document.type, [\"product\"])]]",
|
94
|
+
"type":"String"
|
95
|
+
}
|
96
|
+
}
|
97
|
+
},
|
98
|
+
"pies":{
|
99
|
+
"name":"Little Pies",
|
100
|
+
"method":"GET",
|
101
|
+
"rel":"collection",
|
102
|
+
"enctype":"application/x-www-form-urlencoded",
|
103
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
104
|
+
"fields":{
|
105
|
+
"ref":{
|
106
|
+
"type":"String"
|
107
|
+
},
|
108
|
+
"q":{
|
109
|
+
"default":"[[at(document.tags, [\"Pie\"])][any(document.type, [\"product\"])]]",
|
110
|
+
"type":"String"
|
111
|
+
}
|
112
|
+
}
|
113
|
+
},
|
114
|
+
"selections":{
|
115
|
+
"name":"Product selections",
|
116
|
+
"method":"GET",
|
117
|
+
"rel":"collection",
|
118
|
+
"enctype":"application/x-www-form-urlencoded",
|
119
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
120
|
+
"fields":{
|
121
|
+
"ref":{
|
122
|
+
"type":"String"
|
123
|
+
},
|
124
|
+
"q":{
|
125
|
+
"default":"[[any(document.type, [\"selection\"])]]",
|
126
|
+
"type":"String"
|
127
|
+
}
|
128
|
+
}
|
129
|
+
},
|
130
|
+
"blog":{
|
131
|
+
"name":"The Blog",
|
132
|
+
"method":"GET",
|
133
|
+
"rel":"collection",
|
134
|
+
"enctype":"application/x-www-form-urlencoded",
|
135
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
136
|
+
"fields":{
|
137
|
+
"ref":{
|
138
|
+
"type":"String"
|
139
|
+
},
|
140
|
+
"q":{
|
141
|
+
"default":"[[any(document.type, [\"blog-post\"])]]",
|
142
|
+
"type":"String"
|
143
|
+
}
|
144
|
+
}
|
145
|
+
},
|
146
|
+
"stores":{
|
147
|
+
"name":"Stores",
|
148
|
+
"method":"GET",
|
149
|
+
"rel":"collection",
|
150
|
+
"enctype":"application/x-www-form-urlencoded",
|
151
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
152
|
+
"fields":{
|
153
|
+
"ref":{
|
154
|
+
"type":"String"
|
155
|
+
},
|
156
|
+
"q":{
|
157
|
+
"default":"[[any(document.type, [\"store\"])]]",
|
158
|
+
"type":"String"
|
159
|
+
}
|
160
|
+
}
|
161
|
+
},
|
162
|
+
"jobs":{
|
163
|
+
"name":"Jobs",
|
164
|
+
"method":"GET",
|
165
|
+
"rel":"collection",
|
166
|
+
"enctype":"application/x-www-form-urlencoded",
|
167
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
168
|
+
"fields":{
|
169
|
+
"ref":{
|
170
|
+
"type":"String"
|
171
|
+
},
|
172
|
+
"q":{
|
173
|
+
"default":"[[any(document.type, [\"job-offer\"])]]",
|
174
|
+
"type":"String"
|
175
|
+
}
|
176
|
+
}
|
177
|
+
},
|
178
|
+
"everything":{
|
179
|
+
"method":"GET",
|
180
|
+
"enctype":"application/x-www-form-urlencoded",
|
181
|
+
"action":"http://lesbonneschoses.wroom.io/api/documents/search",
|
182
|
+
"fields":{
|
183
|
+
"ref":{
|
184
|
+
"type":"String"
|
185
|
+
},
|
186
|
+
"q":{
|
187
|
+
"type":"String"
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
@@ -0,0 +1,170 @@
|
|
1
|
+
{
|
2
|
+
"id": "UdUkXt_mqZBObPeS",
|
3
|
+
"type": "product",
|
4
|
+
"href": "doc-url",
|
5
|
+
"tags": [
|
6
|
+
"Macaron"
|
7
|
+
],
|
8
|
+
"slugs": [
|
9
|
+
"vanilla-macaron"
|
10
|
+
],
|
11
|
+
"data": {
|
12
|
+
"product": {
|
13
|
+
"name": {
|
14
|
+
"type": "StructuredText",
|
15
|
+
"value": [
|
16
|
+
{
|
17
|
+
"type": "heading1",
|
18
|
+
"text": "Vanilla Macaron",
|
19
|
+
"spans": []
|
20
|
+
}
|
21
|
+
]
|
22
|
+
},
|
23
|
+
"short_lede": {
|
24
|
+
"type": "StructuredText",
|
25
|
+
"value": [
|
26
|
+
{
|
27
|
+
"type": "heading2",
|
28
|
+
"text": "Crispiness and softness, rolled into one",
|
29
|
+
"spans": []
|
30
|
+
}
|
31
|
+
]
|
32
|
+
},
|
33
|
+
"description": {
|
34
|
+
"type": "StructuredText",
|
35
|
+
"value": [
|
36
|
+
{
|
37
|
+
"type": "paragraph",
|
38
|
+
"text": "Experience the ultimate vanilla experience. Our vanilla Macarons are made with our very own (in-house) pure extract of Madagascar vanilla, and subtly dusted with our own vanilla sugar (which we make from real vanilla beans).",
|
39
|
+
"spans": [
|
40
|
+
{
|
41
|
+
"start": 103,
|
42
|
+
"end": 137,
|
43
|
+
"type": "strong"
|
44
|
+
},
|
45
|
+
{
|
46
|
+
"start": 162,
|
47
|
+
"end": 183,
|
48
|
+
"type": "strong"
|
49
|
+
}
|
50
|
+
]
|
51
|
+
}
|
52
|
+
]
|
53
|
+
},
|
54
|
+
"image": {
|
55
|
+
"type": "Image",
|
56
|
+
"value": {
|
57
|
+
"main": {
|
58
|
+
"url": "https://wroomio.s3.amazonaws.com/lesbonneschoses/0417110ebf2dc34a3e8b7b28ee4e06ac82473b70.png",
|
59
|
+
"dimensions": {
|
60
|
+
"width": 500,
|
61
|
+
"height": 500
|
62
|
+
}
|
63
|
+
},
|
64
|
+
"views": {
|
65
|
+
"icon": {
|
66
|
+
"url": "https://wroomio.s3.amazonaws.com/lesbonneschoses/babdc3421037f9af77720d8f5dcf1b84c912c6ba.png",
|
67
|
+
"dimensions": {
|
68
|
+
"width": 250,
|
69
|
+
"height": 250
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"allergens": {
|
76
|
+
"type": "Text",
|
77
|
+
"value": "Contains almonds, eggs, milk"
|
78
|
+
},
|
79
|
+
"price": {
|
80
|
+
"type": "Number",
|
81
|
+
"value": 3.55
|
82
|
+
},
|
83
|
+
"flavour": [
|
84
|
+
{
|
85
|
+
"type": "Select",
|
86
|
+
"value": "Vanilla"
|
87
|
+
}
|
88
|
+
],
|
89
|
+
"color": {
|
90
|
+
"type": "Color",
|
91
|
+
"value": "#ffeacd"
|
92
|
+
},
|
93
|
+
"related": [
|
94
|
+
{
|
95
|
+
"type": "Link.document",
|
96
|
+
"value": {
|
97
|
+
"document": {
|
98
|
+
"id": "UdUjvt_mqVNObPeO",
|
99
|
+
"type": "product",
|
100
|
+
"tags": [
|
101
|
+
"Macaron"
|
102
|
+
],
|
103
|
+
"slug": "dark-chocolate-macaron"
|
104
|
+
},
|
105
|
+
"isBroken": false
|
106
|
+
}
|
107
|
+
},
|
108
|
+
{
|
109
|
+
"type": "Link.document",
|
110
|
+
"value": {
|
111
|
+
"document": {
|
112
|
+
"id": "UdUjsN_mqT1ObPeM",
|
113
|
+
"type": "product",
|
114
|
+
"tags": [
|
115
|
+
"Macaron"
|
116
|
+
],
|
117
|
+
"slug": "salted-caramel-macaron"
|
118
|
+
},
|
119
|
+
"isBroken": false
|
120
|
+
}
|
121
|
+
}
|
122
|
+
],
|
123
|
+
"testimonial_author": [
|
124
|
+
{
|
125
|
+
"type": "StructuredText",
|
126
|
+
"value": [
|
127
|
+
{
|
128
|
+
"type": "heading3",
|
129
|
+
"text": "Chef Guillaume Bort",
|
130
|
+
"spans": []
|
131
|
+
}
|
132
|
+
]
|
133
|
+
}
|
134
|
+
],
|
135
|
+
"testimonial_quote": [
|
136
|
+
{
|
137
|
+
"type": "StructuredText",
|
138
|
+
"value": [
|
139
|
+
{
|
140
|
+
"type": "paragraph",
|
141
|
+
"text": "The taste of pure vanilla is very hard to tame, and therefore, most cooks resort to substitutes. It takes a high-skill chef to know how to get the best of tastes, and Les Bonnes Choses's vanilla macaron does just that. The result is more than a success, it simply is a gastronomic piece of art.",
|
142
|
+
"spans": [
|
143
|
+
{
|
144
|
+
"start": 97,
|
145
|
+
"end": 167,
|
146
|
+
"type": "strong"
|
147
|
+
},
|
148
|
+
{
|
149
|
+
"start": 167,
|
150
|
+
"end": 184,
|
151
|
+
"type": "strong"
|
152
|
+
},
|
153
|
+
{
|
154
|
+
"start": 167,
|
155
|
+
"end": 184,
|
156
|
+
"type": "em"
|
157
|
+
},
|
158
|
+
{
|
159
|
+
"start": 184,
|
160
|
+
"end": 217,
|
161
|
+
"type": "strong"
|
162
|
+
}
|
163
|
+
]
|
164
|
+
}
|
165
|
+
]
|
166
|
+
}
|
167
|
+
]
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|