prismic.io 1.0.0.rc6 → 1.0.0.rc7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/Gemfile.lock +1 -1
- data/lib/prismic.rb +123 -19
- data/lib/prismic/api.rb +2 -1
- data/lib/prismic/cache.rb +30 -22
- data/lib/prismic/fragments/color.rb +12 -0
- data/lib/prismic/fragments/date.rb +6 -0
- data/lib/prismic/fragments/embed.rb +6 -0
- data/lib/prismic/fragments/fragment.rb +22 -19
- data/lib/prismic/fragments/group.rb +101 -76
- data/lib/prismic/version.rb +1 -1
- data/spec/cache_spec.rb +13 -0
- data/spec/lesbonneschoses_spec.rb +130 -130
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc69199255c2d6ef198403766b9bc0d54ab2b36e
|
4
|
+
data.tar.gz: 1613eaab5156a26ab56fdd2f2470dc3009256bd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9abb44957fddcea4d59a4526ab212d139921b9518eab7aa0038aaeaf3a982dbd4c55bab6f398927c372ceca0d9c6faad5822e8d5ffc18bae01f95947946fae9a
|
7
|
+
data.tar.gz: 28eef0cc87ca7504d0ec0fee7fc9ba05f02ca8bf50724627317918fb45b782047119d7c672f3885f0c4319cdc1c44de54bfb393d22f255305f33532e2001bc76
|
data/.yardopts
ADDED
data/Gemfile.lock
CHANGED
data/lib/prismic.rb
CHANGED
@@ -49,6 +49,8 @@ module Prismic
|
|
49
49
|
# @param [String] url The URL of the prismic.io repository
|
50
50
|
# @param [String] access_token The access token
|
51
51
|
#
|
52
|
+
# @raise PrismicWSConnectionError
|
53
|
+
#
|
52
54
|
# @return [API] The API instance related to this repository
|
53
55
|
def self.api(url, opts=nil)
|
54
56
|
opts ||= {}
|
@@ -56,23 +58,54 @@ module Prismic
|
|
56
58
|
API.start(url, opts)
|
57
59
|
end
|
58
60
|
|
61
|
+
# Build the URL where the user can be redirected to authenticated himself
|
62
|
+
# using OAuth2.
|
63
|
+
# @api
|
64
|
+
#
|
65
|
+
# @note: The endpoint depends on the repository, so an API call is made to
|
66
|
+
# fetch it.
|
67
|
+
#
|
68
|
+
# @param url[String] The URL of the prismic.io repository
|
69
|
+
# @param oauth_opts [Hash] The OAuth2 options
|
70
|
+
# @param api_opts [Hash] The API options (the same than accepted by the {api}
|
71
|
+
# method)
|
72
|
+
#
|
73
|
+
# @option oauth_opts :client_id [String] The Application's client ID
|
74
|
+
# @option oauth_opts :redirect_uri [String] The Application's secret
|
75
|
+
# @option oauth_opts :scope [String] The desired scope
|
76
|
+
#
|
77
|
+
# @raise PrismicWSConnectionError
|
78
|
+
#
|
79
|
+
# @return [String] The built URL
|
59
80
|
def self.oauth_initiate_url(url, oauth_opts, api_opts=nil)
|
60
81
|
api_opts ||= {}
|
61
82
|
api_opts = {access_token: api_opts} if api_opts.is_a?(String)
|
62
83
|
API.oauth_initiate_url(url, oauth_opts, api_opts)
|
63
84
|
end
|
64
85
|
|
86
|
+
# Check a token and return an access_token
|
87
|
+
#
|
88
|
+
# This method allows to check the token received when the user has been
|
89
|
+
# redirected from the OAuth2 server. It returns an access_token that can
|
90
|
+
# be used to authenticate the user on the API.
|
91
|
+
#
|
92
|
+
# @param url [String] The URL of the prismic.io repository
|
93
|
+
# @param oauth_opts [Hash] The OAuth2 options
|
94
|
+
# @param api_opts [Hash] The API options (the same than accepted by the
|
95
|
+
# {api} method)
|
96
|
+
#
|
97
|
+
# @option oauth_opts :client_id [String] The Application's client ID
|
98
|
+
# @option oauth_opts :redirect_uri [String] The Application's secret
|
99
|
+
#
|
100
|
+
# @raise PrismicWSConnectionError
|
101
|
+
#
|
102
|
+
# @return [String] the access_token
|
65
103
|
def self.oauth_check_token(url, oauth_opts, api_opts=nil)
|
66
104
|
api_opts ||= {}
|
67
105
|
api_opts = {access_token: api_opts} if api_opts.is_a?(String)
|
68
106
|
API.oauth_check_token(url, oauth_opts, api_opts)
|
69
107
|
end
|
70
108
|
|
71
|
-
class ApiData
|
72
|
-
attr_accessor :refs, :bookmarks, :types, :tags, :forms
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
109
|
# A SearchForm represent a Form returned by the prismic.io API.
|
77
110
|
#
|
78
111
|
# These forms depend on the prismic.io repository, and can be filled and sent
|
@@ -82,6 +115,7 @@ module Prismic
|
|
82
115
|
#
|
83
116
|
# The SearchForm instance contains helper methods for each predefined form's fields.
|
84
117
|
# Note that these methods are not created if they risk to add confusion:
|
118
|
+
#
|
85
119
|
# - only letters, underscore and digits are authorized in the name
|
86
120
|
# - name starting with a digit or an underscore are forbidden
|
87
121
|
# - generated method can't override existing methods
|
@@ -121,7 +155,6 @@ module Prismic
|
|
121
155
|
# @return [SearchForm] self
|
122
156
|
|
123
157
|
# @!method page_size(page_size)
|
124
|
-
# @generated
|
125
158
|
# Specify a page size for this form.
|
126
159
|
# @param page_size [String,Fixum] The page size
|
127
160
|
# @return [SearchForm] self
|
@@ -141,26 +174,44 @@ module Prismic
|
|
141
174
|
end
|
142
175
|
private :create_field_helper_method
|
143
176
|
|
177
|
+
# Returns the form's name
|
178
|
+
#
|
179
|
+
# @return [String]
|
144
180
|
def form_name
|
145
181
|
form.name
|
146
182
|
end
|
147
183
|
|
184
|
+
# Returns the form's HTTP method
|
185
|
+
#
|
186
|
+
# @return [String]
|
148
187
|
def form_method
|
149
188
|
form.form_method
|
150
189
|
end
|
151
190
|
|
191
|
+
# Returns the form's relationship
|
192
|
+
#
|
193
|
+
# @return [String]
|
152
194
|
def form_rel
|
153
195
|
form.rel
|
154
196
|
end
|
155
197
|
|
198
|
+
# Returns the form's encoding type
|
199
|
+
#
|
200
|
+
# @return [String]
|
156
201
|
def form_enctype
|
157
202
|
form.enctype
|
158
203
|
end
|
159
204
|
|
205
|
+
# Returns the form's action (URL)
|
206
|
+
#
|
207
|
+
# @return [String]
|
160
208
|
def form_action
|
161
209
|
form.action
|
162
210
|
end
|
163
211
|
|
212
|
+
# Returns the form's fields
|
213
|
+
#
|
214
|
+
# @return [String]
|
164
215
|
def form_fields
|
165
216
|
form.fields
|
166
217
|
end
|
@@ -168,15 +219,20 @@ module Prismic
|
|
168
219
|
# Submit the form
|
169
220
|
# @api
|
170
221
|
#
|
171
|
-
# @note The reference MUST be defined, either by
|
172
|
-
#
|
173
|
-
#
|
222
|
+
# @note The reference MUST be defined, either by:
|
223
|
+
#
|
224
|
+
# - setting it at {API#create_search_form creation}
|
225
|
+
# - using the {#ref} method
|
226
|
+
# - providing the ref parameter.
|
174
227
|
#
|
175
|
-
# @param
|
228
|
+
# @param ref [Ref, String] The {Ref reference} to use (if not already
|
229
|
+
# defined)
|
176
230
|
#
|
177
|
-
# @return [Documents] The results (array of Document object + pagination
|
231
|
+
# @return [Documents] The results (array of Document object + pagination
|
232
|
+
# specifics)
|
178
233
|
def submit(ref = nil)
|
179
234
|
self.ref(ref) if ref
|
235
|
+
data['ref'] = @ref
|
180
236
|
raise NoRefSetException unless @ref
|
181
237
|
|
182
238
|
# cache_key is a mix of HTTP URL and HTTP method
|
@@ -184,7 +240,6 @@ module Prismic
|
|
184
240
|
|
185
241
|
api.caching(cache_key) {
|
186
242
|
if form_method == "GET" && form_enctype == "application/x-www-form-urlencoded"
|
187
|
-
data['ref'] = @ref
|
188
243
|
data['access_token'] = api.access_token if api.access_token
|
189
244
|
data.delete_if { |k, v| v.nil? }
|
190
245
|
|
@@ -270,12 +325,22 @@ module Prismic
|
|
270
325
|
def [](i)
|
271
326
|
@results[i]
|
272
327
|
end
|
328
|
+
alias :get :[]
|
273
329
|
|
330
|
+
# Iterates over received documents
|
331
|
+
#
|
332
|
+
# @yieldparam document [Document]
|
333
|
+
#
|
334
|
+
# This method _does not_ paginates by itself. So only the received document
|
335
|
+
# will be returned.
|
274
336
|
def each(&blk)
|
275
337
|
@results.each(&blk)
|
276
338
|
end
|
277
339
|
include Enumerable # adds map, select, etc
|
278
340
|
|
341
|
+
# Return the number of returned documents
|
342
|
+
#
|
343
|
+
# @return [Fixum]
|
279
344
|
def length
|
280
345
|
@results.length
|
281
346
|
end
|
@@ -294,20 +359,31 @@ module Prismic
|
|
294
359
|
@fragments = (fragments.is_a? Hash) ? parse_fragments(fragments) : fragments
|
295
360
|
end
|
296
361
|
|
362
|
+
# Returns the document's slug
|
363
|
+
#
|
364
|
+
# @return [String]
|
297
365
|
def slug
|
298
366
|
slugs.empty? ? '-' : slugs.first
|
299
367
|
end
|
300
368
|
|
369
|
+
# Generate an HTML representation of the entire document
|
370
|
+
#
|
371
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
372
|
+
# application's specific URL
|
373
|
+
#
|
374
|
+
# @return [String] the HTML representation
|
301
375
|
def as_html(link_resolver)
|
302
376
|
fragments.map { |field, fragment|
|
303
377
|
%(<section data-field="#{field}">#{fragment.as_html(link_resolver)}</section>)
|
304
378
|
}.join("\n")
|
305
379
|
end
|
306
380
|
|
307
|
-
# Finds the first highest title in a document
|
381
|
+
# Finds the first highest title in a document (if any)
|
308
382
|
#
|
309
|
-
#
|
383
|
+
# @return [String]
|
310
384
|
def first_title
|
385
|
+
# It is impossible to reuse the StructuredText.first_title method, since
|
386
|
+
# we need to test the highest title across the whole document
|
311
387
|
title = false
|
312
388
|
max_level = 6 # any title with a higher level kicks the current one out
|
313
389
|
@fragments.each do |_, fragment|
|
@@ -325,12 +401,16 @@ module Prismic
|
|
325
401
|
title
|
326
402
|
end
|
327
403
|
|
404
|
+
# Get a document's field
|
328
405
|
def [](field)
|
329
406
|
array = field.split('.')
|
330
|
-
|
407
|
+
if array.length != 2
|
408
|
+
raise ArgumentError, "Argument should contain one dot. Example: product.price"
|
409
|
+
end
|
331
410
|
return nil if array[0] != self.type
|
332
411
|
fragments[array[1]]
|
333
412
|
end
|
413
|
+
alias :get :[]
|
334
414
|
|
335
415
|
private
|
336
416
|
|
@@ -346,7 +426,26 @@ module Prismic
|
|
346
426
|
# (except /api) and allow to assert that the URL you use will always
|
347
427
|
# returns the same results.
|
348
428
|
class Ref
|
349
|
-
|
429
|
+
|
430
|
+
# Returns the value of attribute ref.
|
431
|
+
#
|
432
|
+
# @return [String]
|
433
|
+
attr_accessor :ref
|
434
|
+
|
435
|
+
# Returns the value of attribute label.
|
436
|
+
#
|
437
|
+
# @return [String]
|
438
|
+
attr_accessor :label
|
439
|
+
|
440
|
+
# Returns the value of attribute is_master.
|
441
|
+
#
|
442
|
+
# @return [Boolean]
|
443
|
+
attr_accessor :is_master
|
444
|
+
|
445
|
+
# Returns the value of attribute scheduled_at.
|
446
|
+
#
|
447
|
+
# @return [Time]
|
448
|
+
attr_accessor :scheduled_at
|
350
449
|
|
351
450
|
def initialize(ref, label, is_master = false, scheduled_at = nil)
|
352
451
|
@ref = ref
|
@@ -358,8 +457,13 @@ module Prismic
|
|
358
457
|
alias :master? :is_master
|
359
458
|
end
|
360
459
|
|
460
|
+
# The LinkResolver will help to build URL specific to an application, based
|
461
|
+
# on a generic prismic.io's {Fragments::DocumentLink Document link}.
|
361
462
|
class LinkResolver
|
362
463
|
attr_reader :ref
|
464
|
+
|
465
|
+
# @yieldparam doc_link [Fragments::DocumentLink] A DocumentLink instance
|
466
|
+
# @yieldreturn [String] The application specific URL of the given document
|
363
467
|
def initialize(ref, &blk)
|
364
468
|
@ref = ref
|
365
469
|
@blk = blk
|
@@ -431,11 +535,11 @@ module Prismic
|
|
431
535
|
# The {LinkResolver} will help to build URL specific to an application, based
|
432
536
|
# on a generic prismic.io's {Fragments::DocumentLink Document link}.
|
433
537
|
#
|
434
|
-
# @param
|
435
|
-
# @yieldparam
|
538
|
+
# @param ref [Ref] The ref to use
|
539
|
+
# @yieldparam doc_link [Fragments::DocumentLink] A DocumentLink instance
|
436
540
|
# @yieldreturn [String] The application specific URL of the given document
|
437
541
|
#
|
438
|
-
# @return [LinkResolver]
|
542
|
+
# @return [LinkResolver] the {LinkResolver} instance
|
439
543
|
def self.link_resolver(ref, &blk)
|
440
544
|
LinkResolver.new(ref, &blk)
|
441
545
|
end
|
data/lib/prismic/api.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Prismic
|
4
|
+
# The API is the main class
|
4
5
|
class API
|
5
|
-
@@cache = nil
|
6
6
|
@@warned_create_search_form = false
|
7
7
|
@@warned_oauth_initiate_url = false
|
8
8
|
@@warned_oauth_check_token = false
|
@@ -21,6 +21,7 @@ module Prismic
|
|
21
21
|
# If the cache is disabled, the block is always called
|
22
22
|
#
|
23
23
|
# @param key [String] the cache's key to test
|
24
|
+
# @yieldparam key [String] the key
|
24
25
|
#
|
25
26
|
# @return the return of the given block
|
26
27
|
def caching(key)
|
data/lib/prismic/cache.rb
CHANGED
@@ -1,24 +1,30 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Prismic
|
3
3
|
# This is a simple cache class provided with the prismic.io Ruby kit.
|
4
|
+
#
|
4
5
|
# It is pretty dumb but effective:
|
5
|
-
# * everything is stored in memory,
|
6
|
-
# * invalidation: not needed for prismic.io documents (they are eternally immutable), but we don't want the cache to expand indefinitely; therefore all cache but the new master ref is cleared when a new master ref gets published.
|
7
6
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
7
|
+
# * everything is stored in memory,
|
8
|
+
# * invalidation: not needed for prismic.io documents (they are eternally
|
9
|
+
# immutable), but we don't want the cache to expand indefinitely; therefore
|
10
|
+
# all cache but the new master ref is cleared when a new master ref gets
|
11
|
+
# published.
|
12
|
+
#
|
13
|
+
# If you need a smarter caching (for instance, caching in files), you can
|
14
|
+
# extend this class and replace its methods, and when creating your API object
|
15
|
+
# like this for instance: Prismic.api(url, options), pass the name of the
|
16
|
+
# class you created as a :cache option. Therefore, to use this simple cache,
|
17
|
+
# you can create your API object like this: `Prismic.api(url, cache:
|
18
|
+
# Prismic::DefaultCache)`
|
12
19
|
class Cache
|
13
20
|
|
14
21
|
# Based on http://stackoverflow.com/questions/1933866/efficient-ruby-lru-cache
|
15
22
|
# The Hash are sorted, so the age is represented by the key order
|
16
23
|
|
17
|
-
# Returns the cache object holding the responses to "results" queries (lists
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# (so that we don't have to parse anything again, it's stored already parsed).
|
24
|
+
# Returns the cache object holding the responses to "results" queries (lists
|
25
|
+
# of documents). The object that is stored as a cache_object is what is
|
26
|
+
# returned by {Prismic::JsonParser.results_parser} (so that we don't have to
|
27
|
+
# parse anything again, it's stored already parsed).
|
22
28
|
#
|
23
29
|
# @return [Hash<String,Object>]
|
24
30
|
attr_reader :intern
|
@@ -36,10 +42,10 @@ module Prismic
|
|
36
42
|
|
37
43
|
# Add a cache entry.
|
38
44
|
#
|
39
|
-
# @param key [String]
|
40
|
-
# @param value
|
45
|
+
# @param key [String] The key
|
46
|
+
# @param value [Object] The value to store
|
41
47
|
#
|
42
|
-
# @return
|
48
|
+
# @return [Object] The stored value
|
43
49
|
def store(key, value)
|
44
50
|
@intern.delete(key)
|
45
51
|
@intern[key] = value
|
@@ -55,7 +61,7 @@ module Prismic
|
|
55
61
|
# Prune the cache old oldest keys if the new max_size is older than the keys
|
56
62
|
# number.
|
57
63
|
#
|
58
|
-
# @param max_size [Fixnum]
|
64
|
+
# @param max_size [Fixnum] The new maximun number of keys to store
|
59
65
|
def max_size=(max_size)
|
60
66
|
raise ArgumentError.new(:max_size) if max_size < 1
|
61
67
|
@max_size = max_size
|
@@ -71,9 +77,9 @@ module Prismic
|
|
71
77
|
# A block can be provided: it will be used to compute (and store) the value
|
72
78
|
# if the key is missing.
|
73
79
|
#
|
74
|
-
# @param key [String]
|
80
|
+
# @param key [String] The key to fetch
|
75
81
|
#
|
76
|
-
# @return [Object]
|
82
|
+
# @return [Object] The cache object as was stored
|
77
83
|
def get(key)
|
78
84
|
found = true
|
79
85
|
value = @intern.delete(key){ found = false }
|
@@ -87,22 +93,22 @@ module Prismic
|
|
87
93
|
|
88
94
|
# Checks if a cache entry exists
|
89
95
|
#
|
90
|
-
# @param key [String]
|
96
|
+
# @param key [String] The key to test
|
91
97
|
#
|
92
98
|
# @return [Boolean]
|
93
99
|
def include?(key)
|
94
100
|
@intern.include?(key)
|
95
101
|
end
|
96
102
|
|
97
|
-
# Invalidates all
|
103
|
+
# Invalidates all entries
|
98
104
|
def invalidate_all!
|
99
105
|
@intern.clear
|
100
106
|
end
|
101
107
|
alias :clear! :invalidate_all!
|
102
108
|
|
103
|
-
# Expose
|
104
|
-
#
|
105
|
-
#
|
109
|
+
# Expose the Hash keys
|
110
|
+
#
|
111
|
+
# This is only for debugging purposes.
|
106
112
|
#
|
107
113
|
# @return [Array<String>]
|
108
114
|
def keys
|
@@ -119,5 +125,7 @@ module Prismic
|
|
119
125
|
|
120
126
|
end
|
121
127
|
|
128
|
+
# This default instance is used by the API to avoid creating a new instance
|
129
|
+
# per request (which would make the cache useless).
|
122
130
|
DefaultCache = Cache.new
|
123
131
|
end
|
@@ -8,6 +8,12 @@ module Prismic
|
|
8
8
|
@value = value
|
9
9
|
end
|
10
10
|
|
11
|
+
# Returns the RGB values in a Hash
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# color.asRGB # => {'red' => 123, 'green' => 123, 'blue' => 123}
|
15
|
+
#
|
16
|
+
# @return [Hash]
|
11
17
|
def asRGB
|
12
18
|
Fragments::Color.asRGB(@value)
|
13
19
|
end
|
@@ -20,6 +26,12 @@ module Prismic
|
|
20
26
|
}
|
21
27
|
end
|
22
28
|
|
29
|
+
# Generate an HTML representation of the fragment
|
30
|
+
#
|
31
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
32
|
+
# application's specific URL
|
33
|
+
#
|
34
|
+
# @return [String] the HTML representation
|
23
35
|
def as_html(link_resolver=nil)
|
24
36
|
%(<span class="color">##@value</span>)
|
25
37
|
end
|
@@ -8,6 +8,12 @@ module Prismic
|
|
8
8
|
@value = value
|
9
9
|
end
|
10
10
|
|
11
|
+
# Generate an HTML representation of the fragment
|
12
|
+
#
|
13
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
14
|
+
# application's specific URL
|
15
|
+
#
|
16
|
+
# @return [String] the HTML representation
|
11
17
|
def as_html(link_resolver=nil)
|
12
18
|
%(<time>#{value.iso8601(3)}</time>)
|
13
19
|
end
|
@@ -12,6 +12,12 @@ module Prismic
|
|
12
12
|
@o_embed_json = o_embed_json
|
13
13
|
end
|
14
14
|
|
15
|
+
# Generate an HTML representation of the fragment
|
16
|
+
#
|
17
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
18
|
+
# application's specific URL
|
19
|
+
#
|
20
|
+
# @return [String] the HTML representation
|
15
21
|
def as_html(link_resolver=nil)
|
16
22
|
<<-HTML
|
17
23
|
<div data-oembed="#@url"
|
@@ -1,22 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Prismic
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
3
|
+
module Fragments
|
4
|
+
# Generic fragment, this class is to be extended by all fragment classes.
|
5
|
+
class Fragment
|
6
|
+
|
7
|
+
# Generic as_html method for fragments, meant to be overriden by
|
8
|
+
# specific fragment classes.
|
9
|
+
#
|
10
|
+
# @return [String] an empty string
|
11
|
+
def as_html(link_resolver = nil)
|
12
|
+
raise NotImplementedError, "Method #{__method__} is not implemented for #{inspect}", caller
|
13
|
+
end
|
14
|
+
|
15
|
+
# Generic as_text method for fragments, meant to be overriden by
|
16
|
+
# specific fragment classes.
|
17
|
+
#
|
18
|
+
# @return [String] an empty string
|
19
|
+
def as_text()
|
20
|
+
raise NotImplementedError, "Method #{__method__} is not implemented for #{inspect}", caller
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
22
25
|
end
|
@@ -1,78 +1,103 @@
|
|
1
1
|
module Prismic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
2
|
+
module Fragments
|
3
|
+
|
4
|
+
# A fragment of type Group, which contains an array of FragmentList (which
|
5
|
+
# itself is a Hash of fragments).
|
6
|
+
#
|
7
|
+
# For instance, imagining this group is defined with two possible fragments:
|
8
|
+
# an image fragment "image", and a text fragment "caption"; then accessing
|
9
|
+
# the first image will look like this: `group[0]['image']`.
|
10
|
+
class Group < Fragment
|
11
|
+
|
12
|
+
# The array of the fragment lists
|
13
|
+
attr_accessor :fragment_list_array
|
14
|
+
|
15
|
+
def initialize(fragment_list_array)
|
16
|
+
@fragment_list_array = fragment_list_array
|
17
|
+
end
|
18
|
+
|
19
|
+
# Accessing the i-th item (fragment list) of the group: `group[i]`
|
20
|
+
def [](i)
|
21
|
+
@fragment_list_array[i]
|
22
|
+
end
|
23
|
+
alias :get :[]
|
24
|
+
|
25
|
+
# @yieldparam fragment [Fragment]
|
26
|
+
def each(&blk)
|
27
|
+
@fragment_list_array.each(&blk)
|
28
|
+
end
|
29
|
+
include Enumerable # adds map, select, etc
|
30
|
+
|
31
|
+
def length
|
32
|
+
@fragment_list_array.length
|
33
|
+
end
|
34
|
+
alias :size :length
|
35
|
+
|
36
|
+
# Generate an HTML representation of the group
|
37
|
+
#
|
38
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
39
|
+
# application's specific URL
|
40
|
+
#
|
41
|
+
# @return [String] the HTML representation
|
42
|
+
def as_html(link_resolver = nil)
|
43
|
+
@fragment_list_array.map { |fl| fl.as_html(link_resolver) }.join("\n")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate an text representation of the group
|
47
|
+
#
|
48
|
+
# @return [String] the text representation
|
49
|
+
def as_text
|
50
|
+
@fragment_list_array.map { |fl| fl.as_text }.join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
class FragmentMapping
|
55
|
+
|
56
|
+
# a hash containing all the fragments in the fragment list
|
57
|
+
attr_accessor :fragments
|
58
|
+
|
59
|
+
def initialize(fragments)
|
60
|
+
@fragments = fragments
|
61
|
+
end
|
62
|
+
|
63
|
+
# Accessing the right fragment of the fragment list: `fl['caption']`
|
64
|
+
def [](name)
|
65
|
+
@fragments[name]
|
66
|
+
end
|
67
|
+
alias :get :[]
|
68
|
+
|
69
|
+
# @yieldparam name [String]
|
70
|
+
# @yieldparam fragment [Fragment]
|
71
|
+
def each(&blk)
|
72
|
+
@fragments.each(&blk)
|
73
|
+
end
|
74
|
+
include Enumerable # adds map, select, etc
|
75
|
+
|
76
|
+
# @return [Fixum]
|
77
|
+
def length
|
78
|
+
@fragments.length
|
79
|
+
end
|
80
|
+
alias :size :length
|
81
|
+
|
82
|
+
# Generate an HTML representation of the fragments
|
83
|
+
#
|
84
|
+
# @param link_resolver [LinkResolver] The LinkResolver used to build
|
85
|
+
# application's specific URL
|
86
|
+
#
|
87
|
+
# @return [String] the HTML representation
|
88
|
+
def as_html(link_resolver = nil)
|
89
|
+
@fragments.map { |name, fragment|
|
90
|
+
%(<section data-field="#{name}">#{fragment.as_html(link_resolver)}</section>)
|
91
|
+
}.join("\n")
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generate a text representation of the fragment
|
95
|
+
#
|
96
|
+
# @return [String] the text representation
|
97
|
+
def as_text
|
98
|
+
@fragments.values.map { |fragment| fragment.as_text }.join("\n")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
78
103
|
end
|
data/lib/prismic/version.rb
CHANGED
data/spec/cache_spec.rb
CHANGED
@@ -69,5 +69,18 @@ describe "Cache's" do
|
|
69
69
|
@cache.include?('fake_key2').should be_false
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
describe 'caching on a real repository' do
|
74
|
+
before do
|
75
|
+
@api = Prismic.api("https://lesbonneschoses.prismic.io/api", access_token: 'MC5VbDdXQmtuTTB6Z0hNWHF3.c--_vVbvv73vv73vv73vv71EA--_vS_vv73vv70T77-9Ke-_ve-_vWfvv70ebO-_ve-_ve-_vQN377-9ce-_vRfvv70')
|
76
|
+
@cache = @api.cache
|
77
|
+
@master_ref = @api.master_ref
|
78
|
+
@other_ref = @api.refs['announcement of new sf shop']
|
79
|
+
end
|
80
|
+
it 'works on different refs' do
|
81
|
+
@api.form('everything').submit(@master_ref).total_results_size.should == 40
|
82
|
+
@api.form('everything').submit(@other_ref).total_results_size.should == 43
|
83
|
+
end
|
84
|
+
end
|
72
85
|
end
|
73
86
|
end
|
@@ -2,148 +2,148 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe 'LesBonnesChoses' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
before do
|
6
|
+
@api = Prismic.api("https://lesbonneschoses.prismic.io/api", nil)
|
7
|
+
@master_ref = @api.master_ref
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
describe '/api' do
|
11
|
+
it "API works" do
|
12
|
+
@api.should_not be_nil
|
13
|
+
end
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
describe 'query' do
|
17
|
+
it "queries everything and returns 20 documents" do
|
18
|
+
@api.form("everything").submit(@master_ref).size.should == 20
|
19
|
+
@api.form("everything").submit(@master_ref).results.size.should == 20
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
it "queries macarons (using a predicate) and returns 7 documents" do
|
23
|
+
@api.form("everything")
|
24
|
+
.query(%([[:d = any(document.tags, ["Macaron"])]]))
|
25
|
+
.submit(@master_ref).results.size.should == 7
|
26
|
+
@api.form("everything")
|
27
|
+
.query(%([[:d = any(document.tags, ["Macaron"])]]))
|
28
|
+
.submit(@master_ref).size.should == 7
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
it "queries macarons (using a form) and returns 7 documents" do
|
32
|
+
@api.form("macarons").submit(@master_ref).results.size.should == 7
|
33
|
+
@api.form("macarons").submit(@master_ref).size.should == 7
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
it "queries macarons or cupcakes (using a form + a predicate) and returns 11 documents" do
|
37
|
+
@api.form("products")
|
38
|
+
.query(%([[:d = any(document.tags, ["Cupcake", "Macaron"])]]))
|
39
|
+
.submit(@master_ref).results.size.should == 11
|
40
|
+
@api.form("products")
|
41
|
+
.query(%([[:d = any(document.tags, ["Cupcake", "Macaron"])]]))
|
42
|
+
.submit(@master_ref).size.should == 11
|
43
|
+
end
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
46
|
+
describe 'pagination' do
|
47
|
+
it "works in basic cases" do
|
48
|
+
documents = @api.form("everything").submit(@master_ref)
|
49
|
+
documents.page.should == 1
|
50
|
+
documents.results_per_page.should == 20
|
51
|
+
documents.results_size.should == 20
|
52
|
+
documents.total_results_size.should == 40
|
53
|
+
documents.total_pages.should == 2
|
54
|
+
documents.next_page.should == "https://lesbonneschoses.prismic.io/api/documents/search?ref=UkL0hcuvzYUANCrm&page=2&pageSize=20"
|
55
|
+
documents.prev_page.should == nil
|
56
|
+
end
|
57
|
+
it "works on page 2" do
|
58
|
+
documents = @api.form("everything").page("2").submit(@master_ref)
|
59
|
+
documents.page.should == 2
|
60
|
+
documents.results_per_page.should == 20
|
61
|
+
documents.results_size.should == 20
|
62
|
+
documents.total_results_size.should == 40
|
63
|
+
documents.total_pages.should == 2
|
64
|
+
documents.next_page.should == nil
|
65
|
+
documents.prev_page.should == "https://lesbonneschoses.prismic.io/api/documents/search?ref=UkL0hcuvzYUANCrm&page=1&pageSize=20"
|
66
|
+
end
|
67
|
+
it "works on page 2 with a pagination step of 10" do
|
68
|
+
documents = @api.form("everything").page("2").page_size("10").submit(@master_ref)
|
69
|
+
documents.page.should == 2
|
70
|
+
documents.results_per_page.should == 10
|
71
|
+
documents.results_size.should == 10
|
72
|
+
documents.total_results_size.should == 40
|
73
|
+
documents.total_pages.should == 4
|
74
|
+
documents.next_page.should == "https://lesbonneschoses.prismic.io/api/documents/search?ref=UkL0hcuvzYUANCrm&page=3&pageSize=10"
|
75
|
+
documents.prev_page.should == "https://lesbonneschoses.prismic.io/api/documents/search?ref=UkL0hcuvzYUANCrm&page=1&pageSize=10"
|
76
|
+
end
|
77
|
+
end
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
79
|
+
describe 'API::Document' do
|
80
|
+
before do
|
81
|
+
@document = @api.form('everything').query(%([[:d = at(document.id, "UkL0gMuvzYUANCpf")]])).submit(@master_ref)[0]
|
82
|
+
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
it 'Operator [] works on document' do
|
85
|
+
@document['job-offer.name'].as_html(nil).should == '<h1>Pastry Dresser</h1>'
|
86
|
+
end
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
it 'Operator [] returns nil if wrong type' do
|
89
|
+
@document['product.name'].should == nil
|
90
|
+
end
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
it 'Operator [] raises error if field is nonsense' do
|
93
|
+
expect {
|
94
|
+
@document['blablabla']
|
95
|
+
}.to raise_error(ArgumentError, "Argument should contain one dot. Example: product.price")
|
96
|
+
end
|
97
|
+
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
99
|
+
describe 'API::Documents' do
|
100
|
+
before do
|
101
|
+
@documents = @api.form('everything').submit(@master_ref)
|
102
|
+
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
104
|
+
it 'has a working [] operator' do
|
105
|
+
@documents[0].slug.should == @documents.results[0].slug
|
106
|
+
end
|
107
|
+
it 'has a proper size' do
|
108
|
+
@documents.length.should == 20
|
109
|
+
@documents.size.should == 20
|
110
|
+
end
|
111
|
+
it 'has a proper each method' do
|
112
|
+
array = []
|
113
|
+
@documents.each {|document| array << document.slug }
|
114
|
+
array.join(' ').should == 'pastry-dresser exotic-kiwi-pie apricot-pie sweet-strawberry-pie woodland-cherry-pie cool-coconut-macaron salted-caramel-macaron the-end-of-a-chapter-the-beginning-of-a-new-one get-the-right-approach-to-ganache one-day-in-the-life-of-a-les-bonnes-choses-pastry les-bonnes-chosess-internship-a-testimony our-world-famous-pastry-art-brainstorm-event tips-to-dress-a-pastry triple-chocolate-cupcake dont-be-a-stranger about-us wedding-gift-box art-director store-intern content-director'
|
115
|
+
end
|
116
|
+
it 'is a proper Enumerable' do
|
117
|
+
@documents.map {|document| document.slug }.join(' ').should == 'pastry-dresser exotic-kiwi-pie apricot-pie sweet-strawberry-pie woodland-cherry-pie cool-coconut-macaron salted-caramel-macaron the-end-of-a-chapter-the-beginning-of-a-new-one get-the-right-approach-to-ganache one-day-in-the-life-of-a-les-bonnes-choses-pastry les-bonnes-chosess-internship-a-testimony our-world-famous-pastry-art-brainstorm-event tips-to-dress-a-pastry triple-chocolate-cupcake dont-be-a-stranger about-us wedding-gift-box art-director store-intern content-director'
|
118
|
+
end
|
119
|
+
end
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
121
|
+
describe 'Fragments' do
|
122
|
+
before do
|
123
|
+
@link_resolver = Prismic.link_resolver("master"){|doc_link| "http://localhost/#{doc_link.id}" }
|
124
|
+
end
|
125
|
+
describe 'API::Fragments::StructuredText' do
|
126
|
+
it "returns a correct as_html on a StructuredText with list, span, embed and image" do
|
127
|
+
@api.form("everything")
|
128
|
+
.query(%([[:d = at(document.id, "UkL0gMuvzYUANCpr")]]))
|
129
|
+
.submit(@master_ref)[0]['blog-post.body'].as_html(@link_resolver).gsub("'", "'").should == "<h1>Get the right approach to ganache</h1>\n\n<p>A lot of people touch base with us to know about one of our key ingredients, and the essential role it plays in our creations: ganache.</p>\n\n<p>Indeed, ganache is the macaron's softener, or else, macarons would be but tough biscuits; it is the cupcake's wrapper, or else, cupcakes would be but plain old cake. We even sometimes use ganache within our cupcakes, to soften the cake itself, or as a support to our pies' content.</p>\n\n<h2>How to approach ganache</h2>\n\n<img src=\"https://prismic-io.s3.amazonaws.com/lesbonneschoses/ee7b984b98db4516aba2eabd54ab498293913c6c.jpg\" alt=\"\" width=\"640\" height=\"425\" />\n\n<p>Apart from the taste balance, which is always a challenge when it comes to pastry, the tough part about ganache is about thickness. It is even harder to predict through all the phases the ganache gets to meet (how long will it get melted? how long will it remain in the fridge?). Things get a hell of a lot easier to get once you consider that there are two main ways to get the perfect ganache:</p>\n\n<ul><li><strong>working from the top down</strong>: start with a thick, almost hard material, and soften it by manipulating it, or by mixing it with a more liquid ingredient (like milk)</li><li><strong>working from the bottom up</strong>: start from a liquid-ish state, and harden it by miwing it with thicker ingredients, or by leaving it in the fridge longer.</li></ul>\n\n<p>We do hope this advice will empower you in your ganache-making skills. Let us know how you did with it!</p>\n\n<h2>Ganache at <em>Les Bonnes Choses</em></h2>\n\n<p>We have a saying at Les Bonnes Choses: \"Once you can make ganache, you can make anything.\"</p>\n\n<p>As you may know, we like to give our workshop artists the ability to master their art to the top; that is why our Preparation Experts always start off as being Ganache Specialists for Les Bonnes Choses. That way, they're given an opportunity to focus on one exercise before moving on. Once they master their ganache, and are able to provide the most optimal delight to our customers, we consider they'll thrive as they work on other kinds of preparations.</p>\n\n<h2>About the chocolate in our ganache</h2>\n\n<p>Now, we've also had a lot of questions about how our chocolate gets made. It's true, as you might know, that we make it ourselves, from Columbian cocoa and French cow milk, with a process that much resembles the one in the following Discovery Channel documentary.</p>\n\n <div data-oembed=\"http://www.youtube.com/\"\n data-oembed-type=\"video\"\n data-oembed-provider=\"youtube\"><iframe width=\"459\" height=\"344\" src=\"http://www.youtube.com/embed/Ye78F3-CuXY?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe></div>\n"
|
130
|
+
end
|
131
|
+
it "returns a correct as_html on a StructuredText with links" do
|
132
|
+
@api.form("everything")
|
133
|
+
.query(%([[:d = at(document.id, "#{@api.bookmark('about')}")]]))
|
134
|
+
.submit(@master_ref)[0]['article.content'].as_html(@link_resolver).gsub("'", "'").should == "<h2>A tale of pastry and passion</h2>\n\n<p>As a child, Jean-Michel Pastranova learned the art of fine cuisine from his grand-father, Jacques Pastranova, who was the creator of the \"taste-design\" art current, and still today an unmissable reference of forward-thinking in cuisine. At first an assistant in his grand-father's kitchen, Jean-Michel soon found himself fascinated by sweet flavors and the tougher art of pastry, drawing his own path in the ever-changing cuisine world.</p>\n\n<p>In 1992, the first Les Bonnes Choses store opened on rue Saint-Lazare, in Paris (<a href=\"http://localhost/UkL0gMuvzYUANCpa\">we're still there!</a>), much to everyone's surprise; indeed, back then, it was very surprising for a highly promising young man with a preordained career as a restaurant chef, to open a pastry shop instead. But soon enough, contemporary chefs understood that Jean-Michel had the drive to redefine a new nobility to pastry, the same way many other kinds of cuisine were being qualified as \"fine\".</p>\n\n<p>In 1996, meeting an overwhelming demand, Jean-Michel Pastranova opened <a href=\"http://localhost/UkL0gMuvzYUANCpX\">a second shop on Paris's Champs-Élysées</a>, and <a href=\"http://localhost/UkL0gMuvzYUANCpY\">a third one in London</a>, the same week! Eventually, Les Bonnes Choses gained an international reputation as "a perfection so familiar and new at the same time, that it will feel like a taste travel" (New York Gazette), "the finest balance between surprise and comfort, enveloped in sweetness" (The Tokyo Tribune), "a renewal of the pastry genre (...), the kind that changed the way pastry is approached globally" (The San Francisco Gourmet News). Therefore, it was only a matter of time before Les Bonnes Choses opened shops in <a href=\"http://localhost/UkL0gMuvzYUANCpW\">New York</a> (2000) and <a href=\"http://localhost/UkL0gMuvzYUANCpZ\">Tokyo</a> (2004).</p>\n\n<p>In 2013, Jean-Michel Pastranova stepped down as the CEO and Director of Workshops, remaining a senior advisor to the board and to the workshop artists; he passed the light on to Selena, his daugther, who initially learned the art of pastry from him. Passion for great food runs in the Pastranova family...</p>\n\n<img src=\"https://prismic-io.s3.amazonaws.com/lesbonneschoses/df6c1d87258a5bfadf3479b163fd85c829a5c0b8.jpg\" alt=\"\" width=\"800\" height=\"533\" />\n\n<h2>Our main value: our customers' delight</h2>\n\n<p>Our every action is driven by the firm belief that there is art in pastry, and that this art is one of the dearest pleasures one can experience.</p>\n\n<p>At Les Bonnes Choses, people preparing your macarons are not simply "pastry chefs": they are "<a href=\"http://localhost/UkL0gMuvzYUANCpe\">ganache specialists</a>", "<a href=\"http://localhost/UkL0gMuvzYUANCpc\">fruit experts</a>", or "<a href=\"http://localhost/UkL0gMuvzYUANCpb\">oven instrumentalists</a>\". They are the best people out there to perform the tasks they perform to create your pastry, giving it the greatest value. And they just love to make their specialized pastry skill better and better until perfection.</p>\n\n<p>Of course, there is a workshop in each <em>Les Bonnes Choses</em> store, and every pastry you buy was made today, by the best pastry specialists in your country.</p>\n\n<p>However, the very difficult art of creating new concepts, juggling with tastes and creating brand new, powerful experiences, is performed every few months, during our "<a href=\"http://localhost/UkL0gMuvzYUANCpo\">Pastry Art Brainstorms</a>". During the event, the best pastry artists in the world (some working for <em>Les Bonnes Choses</em>, some not) gather in Paris, and showcase the experiments they've been working on; then, the other present artists comment on the piece, and iterate on it together, in order to make it the best possible masterchief!</p>\n\n<p>The session is presided by Jean-Michel Pastranova, who then selects the most delightful experiences, to add it to <em>Les Bonnes Choses</em>'s catalogue.</p>"
|
135
|
+
end
|
136
|
+
it "returns a correct as_text on a StructuredText" do
|
137
|
+
@api.form("everything")
|
138
|
+
.query(%([[:d = at(document.id, "UkL0gMuvzYUANCps")]]))
|
139
|
+
.submit(@master_ref)[0]['blog-post.body'].as_text.should == "The end of a chapter the beginning of a new one Jean-Michel Pastranova, the founder of Les Bonnes Choses, and creator of the whole concept of modern fine pastry, has decided to step down as the CEO and the Director of Workshops of Les Bonnes Choses, to focus on other projects, among which his now best-selling pastry cook books, but also to take on a primary role in a culinary television show to be announced later this year. \"I believe I've taken the Les Bonnes Choses concept as far as it can go. Les Bonnes Choses is already an entity that is driven by its people, thanks to a strong internal culture, so I don't feel like they need me as much as they used to. I'm sure they are greater ways to come, to innovate in pastry, and I'm sure Les Bonnes Choses's coming innovation will be even more mind-blowing than if I had stayed longer.\" He will remain as a senior advisor to the board, and to the workshop artists, as his daughter Selena, who has been working with him for several years, will fulfill the CEO role from now on. \"My father was able not only to create a revolutionary concept, but also a company culture that puts everyone in charge of driving the company's innovation and quality. That gives us years, maybe decades of revolutionary ideas to come, and there's still a long, wonderful path to walk in the fine pastry world.\""
|
140
|
+
end
|
141
141
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
142
|
+
it "returns a correct as_text on a StructuredText with a separator" do
|
143
|
+
@api.form("everything")
|
144
|
+
.query(%([[:d = at(document.id, "UkL0gMuvzYUANCps")]]))
|
145
|
+
.submit(@master_ref)[0]['blog-post.body'].as_text(' #### ').should == "The end of a chapter the beginning of a new one #### Jean-Michel Pastranova, the founder of Les Bonnes Choses, and creator of the whole concept of modern fine pastry, has decided to step down as the CEO and the Director of Workshops of Les Bonnes Choses, to focus on other projects, among which his now best-selling pastry cook books, but also to take on a primary role in a culinary television show to be announced later this year. #### \"I believe I've taken the Les Bonnes Choses concept as far as it can go. Les Bonnes Choses is already an entity that is driven by its people, thanks to a strong internal culture, so I don't feel like they need me as much as they used to. I'm sure they are greater ways to come, to innovate in pastry, and I'm sure Les Bonnes Choses's coming innovation will be even more mind-blowing than if I had stayed longer.\" #### He will remain as a senior advisor to the board, and to the workshop artists, as his daughter Selena, who has been working with him for several years, will fulfill the CEO role from now on. #### \"My father was able not only to create a revolutionary concept, but also a company culture that puts everyone in charge of driving the company's innovation and quality. That gives us years, maybe decades of revolutionary ideas to come, and there's still a long, wonderful path to walk in the fine pastry world.\""
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
149
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prismic.io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Étienne Vallette d'Osia"
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-05-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -76,6 +76,7 @@ extra_rdoc_files: []
|
|
76
76
|
files:
|
77
77
|
- ".gitignore"
|
78
78
|
- ".travis.yml"
|
79
|
+
- ".yardopts"
|
79
80
|
- Gemfile
|
80
81
|
- Gemfile.lock
|
81
82
|
- README.md
|