contentful 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,8 @@ module Contentful
2
2
  module Resource
3
3
  # Adds the feature to have system properties to a Resource.
4
4
  module SystemProperties
5
+ # @private
6
+ # Coercions for System Properties to native types
5
7
  SYS_COERCIONS = {
6
8
  type: :string,
7
9
  id: :string,
@@ -15,11 +17,13 @@ module Contentful
15
17
  }
16
18
  attr_reader :sys
17
19
 
20
+ # @private
18
21
  def initialize(object = nil, *)
19
22
  super
20
23
  @sys = object ? extract_from_object(object['sys'], :sys) : {}
21
24
  end
22
25
 
26
+ # @private
23
27
  def inspect(info = nil)
24
28
  if sys.empty?
25
29
  super(info)
@@ -28,12 +32,15 @@ module Contentful
28
32
  end
29
33
  end
30
34
 
35
+ # @private
31
36
  module ClassMethods
37
+ # @private
32
38
  def sys_coercions
33
39
  SYS_COERCIONS
34
40
  end
35
41
  end
36
42
 
43
+ # @private
37
44
  def self.included(base)
38
45
  base.extend(ClassMethods)
39
46
 
@@ -14,6 +14,8 @@ module Contentful
14
14
  # Transforms a Contentful::Response into a Contentful::Resource or a Contentful::Error
15
15
  # See example/resource_mapping.rb for advanced usage
16
16
  class ResourceBuilder
17
+ # Default Resource Mapping
18
+ # @see _ README for more information on Resource Mapping
17
19
  DEFAULT_RESOURCE_MAPPING = {
18
20
  'Space' => Space,
19
21
  'ContentType' => ContentType,
@@ -24,11 +26,17 @@ module Contentful
24
26
  'DeletedEntry' => DeletedEntry,
25
27
  'DeletedAsset' => DeletedAsset
26
28
  }
29
+ # Default Entry Mapping
30
+ # @see _ README for more information on Entry Mapping
27
31
  DEFAULT_ENTRY_MAPPING = {}
28
32
 
29
33
  attr_reader :client, :response, :resource_mapping, :entry_mapping, :resource
30
34
 
31
- def initialize(client, response, resource_mapping = {}, entry_mapping = {}, default_locale = Contentful::Client::DEFAULT_CONFIGURATION[:default_locale])
35
+ def initialize(client,
36
+ response,
37
+ resource_mapping = {},
38
+ entry_mapping = {},
39
+ default_locale = Contentful::Client::DEFAULT_CONFIGURATION[:default_locale])
32
40
  @response = response
33
41
  @client = client
34
42
  @included_resources = {}
@@ -39,7 +47,7 @@ module Contentful
39
47
  end
40
48
 
41
49
  # Starts the parsing process.
42
- # Either returns an Error, or the parsed Resource
50
+ # @return [Contentful::Resource, Contentful::Error]
43
51
  def run
44
52
  if response.status == :ok
45
53
  create_all_resources!
@@ -93,9 +101,8 @@ module Contentful
93
101
 
94
102
  # Finds the proper DynamicEntry class for an entry
95
103
  def get_dynamic_entry(object)
96
- if content_id = content_type_id_for_entry(object)
97
- client.dynamic_entry_cache[content_id.to_sym]
98
- end
104
+ content_id = content_type_id_for_entry(object)
105
+ client.dynamic_entry_cache[content_id.to_sym] if content_id
99
106
  end
100
107
 
101
108
  # Returns the id of the related ContentType, if there is one
@@ -131,7 +138,7 @@ module Contentful
131
138
  when Proc
132
139
  res_class[object]
133
140
  when nil
134
- fail UnparsableResource.new(response)
141
+ fail UnparsableResource, response
135
142
  else
136
143
  res_class
137
144
  end
@@ -194,10 +201,10 @@ module Contentful
194
201
  if included_objects
195
202
  included_objects.each do |type, objects|
196
203
  @included_resources[type] = Hash[
197
- objects.map do |object|
198
- res = create_resource(object)
199
- [res.id, res]
200
- end
204
+ objects.map do |object|
205
+ res = create_resource(object)
206
+ [res.id, res]
207
+ end
201
208
  ]
202
209
  end
203
210
  end
@@ -206,15 +213,15 @@ module Contentful
206
213
  def replace_links_with_known_resources(res, seen_resource_ids = [])
207
214
  seen_resource_ids << res.id
208
215
 
209
- [:properties, :sys, :fields].map do |property_container_name|
216
+ property_containers = [:properties, :sys, :fields].map do |property_container_name|
210
217
  res.public_send(property_container_name)
211
- end.compact.each do |property_container|
218
+ end.compact
219
+
220
+ property_containers.each do |property_container|
212
221
  replace_links_in_properties(property_container, seen_resource_ids)
213
222
  end
214
223
 
215
- if res.array?
216
- replace_links_in_array res.items, seen_resource_ids
217
- end
224
+ replace_links_in_array res.items, seen_resource_ids if res.array?
218
225
  end
219
226
 
220
227
  def replace_links_in_properties(property_container, seen_resource_ids)
@@ -242,8 +249,8 @@ module Contentful
242
249
  end
243
250
 
244
251
  def maybe_replace_link(link, parent, key)
245
- if @known_resources[link.link_type] &&
246
- @known_resources[link.link_type].key?(link.id)
252
+ if @known_resources[link.link_type] &&
253
+ @known_resources[link.link_type].key?(link.id)
247
254
  parent[key] = @known_resources[link.link_type][link.id]
248
255
  end
249
256
  end
@@ -3,8 +3,12 @@ module Contentful
3
3
  module Support
4
4
  class << self
5
5
  # Transforms CamelCase into snake_case (taken from zucker)
6
+ #
7
+ # @param [String] object camelCaseName
8
+ #
9
+ # @return [String] snake_case_name
6
10
  def snakify(object)
7
- snake = String(object).gsub(/(?<!^)[A-Z]/) { "_#$&" }
11
+ snake = String(object).gsub(/(?<!^)[A-Z]/) { "_#{$&}" }
8
12
  snake.downcase
9
13
  end
10
14
  end
@@ -4,6 +4,8 @@ require_relative 'deleted_asset'
4
4
  require_relative 'sync_page'
5
5
 
6
6
  module Contentful
7
+ # Resource class for Sync.
8
+ # @see _ https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization
7
9
  class Sync
8
10
  attr_reader :next_sync_url
9
11
 
@@ -14,46 +16,50 @@ module Contentful
14
16
  end
15
17
 
16
18
  # Iterates over all pages of the current sync
17
- # Please Keep in Mind: Iterating fires a new request for each page
18
- def each_page(&block)
19
+ #
20
+ # @note Please Keep in Mind: Iterating fires a new request for each page
21
+ #
22
+ # @yield [Contentful::SyncPage]
23
+ def each_page
19
24
  page = first_page
20
- block.call(page)
25
+ yield page if block_given?
21
26
 
22
27
  until completed?
23
28
  page = page.next_page
24
- block.call(page)
29
+ yield page if block_given?
25
30
  end
26
31
  end
27
32
 
28
33
  # Returns the first sync result page
34
+ #
35
+ # @return [Contentful::SyncPage]
29
36
  def first_page
30
37
  get(@first_page_options_or_url)
31
38
  end
32
39
 
33
40
  # Returns false as long as last sync page has not been reached
41
+ #
42
+ # @return [Boolean]
34
43
  def completed?
44
+ # rubocop:disable Style/DoubleNegation
35
45
  !!next_sync_url
46
+ # rubocop:enable Style/DoubleNegation
36
47
  end
37
48
 
38
49
  # Directly iterates over all resources that have changed
50
+ #
51
+ # @yield [Contentful::Entry, Contentful::Asset]
39
52
  def each_item(&block)
40
53
  each_page do |page|
41
- page.each_item do |item|
42
- block.call item
43
- end
54
+ page.each_item(&block)
44
55
  end
45
56
  end
46
57
 
58
+ # @private
47
59
  def get(options_or_url)
48
- if options_or_url.is_a? String
49
- page = Request.new(@client, options_or_url).get
50
- else
51
- page = Request.new(@client, '/sync', options_or_url).get
52
- end
60
+ page = fetch_page(options_or_url)
53
61
 
54
- if @client.configuration[:raw_mode]
55
- return page
56
- end
62
+ return page if @client.configuration[:raw_mode]
57
63
 
58
64
  link_page_to_sync! page
59
65
  update_sync_state_from! page
@@ -63,6 +69,11 @@ module Contentful
63
69
 
64
70
  private
65
71
 
72
+ def fetch_page(options_or_url)
73
+ return Request.new(@client, options_or_url).get if options_or_url.is_a? String
74
+ Request.new(@client, '/sync', options_or_url).get
75
+ end
76
+
66
77
  def link_page_to_sync!(page)
67
78
  page.instance_variable_set :@sync, self
68
79
  end
@@ -2,6 +2,7 @@ require_relative 'resource'
2
2
  require_relative 'resource/array_like'
3
3
 
4
4
  module Contentful
5
+ # Wrapper Class for Sync results
5
6
  class SyncPage
6
7
  attr_reader :sync
7
8
 
@@ -13,14 +14,25 @@ module Contentful
13
14
  property :nextSyncUrl
14
15
  property :nextPageUrl
15
16
 
17
+ # Requests next sync page from API
18
+ #
19
+ # @return [Contentful::SyncPage, void]
16
20
  def next_page
17
21
  sync.get(next_page_url) if next_page?
18
22
  end
19
23
 
24
+ # Returns wether there is a next sync page
25
+ #
26
+ # @return [Boolean]
20
27
  def next_page?
28
+ # rubocop:disable Style/DoubleNegation
21
29
  !!next_page_url
30
+ # rubocop:enable Style/DoubleNegation
22
31
  end
23
32
 
33
+ # Returns wether it is the last sync page
34
+ #
35
+ # @return [Boolean]
24
36
  def last_page?
25
37
  !next_page_url
26
38
  end
@@ -1,3 +1,5 @@
1
+ # Contentful Namespace
1
2
  module Contentful
2
- VERSION = '0.8.0'
3
+ # Gem Version
4
+ VERSION = '0.9.0'
3
5
  end
data/spec/entry_spec.rb CHANGED
@@ -82,4 +82,120 @@ describe Contentful::Entry do
82
82
  end
83
83
  end
84
84
  end
85
+
86
+ it '#raw' do
87
+ vcr('entry/raw') {
88
+ nyancat = create_client.entry('nyancat')
89
+ expect(nyancat.raw).to eq(create_client(raw_mode: true).entry('nyancat').object)
90
+ }
91
+ end
92
+
93
+ describe 'custom resources' do
94
+ class Kategorie < Contentful::Entry
95
+ include ::Contentful::Resource::CustomResource
96
+
97
+ property :title
98
+ property :slug
99
+ property :image
100
+ property :top
101
+ property :subcategories
102
+ property :featuredArticles
103
+ property :catIntroHead
104
+ property :catIntroduction
105
+ property :seoText
106
+ property :metaKeywords
107
+ property :metaDescription
108
+ property :metaRobots
109
+ end
110
+
111
+ it 'maps fields properly' do
112
+ vcr('entry/custom_resource') {
113
+ entry = create_client(
114
+ space: 'g2b4ltw00meh',
115
+ dynamic_entries: :auto,
116
+ entry_mapping: {
117
+ 'kategorie' => Kategorie
118
+ }
119
+ ).entries.first
120
+
121
+ expect(entry).to be_a Kategorie
122
+ expect(entry.title).to eq "Some Title"
123
+ expect(entry.slug).to eq "/asda.html"
124
+ expect(entry.featured_articles.first.is_a?(Contentful::Link)).to be_truthy
125
+ expect(entry.top).to be_truthy
126
+ }
127
+ end
128
+
129
+ describe 'can be marshalled' do
130
+ class Cat < Contentful::Entry
131
+ include ::Contentful::Resource::CustomResource
132
+
133
+ property :name
134
+ property :lives
135
+ property :bestFriend, Cat
136
+ property :catPack
137
+ end
138
+
139
+ def test_dump(nyancat)
140
+ dump = Marshal.dump(nyancat)
141
+ new_cat = Marshal.load(dump)
142
+
143
+ # Attributes
144
+ expect(new_cat).to be_a Cat
145
+ expect(new_cat.name).to eq "Nyan Cat"
146
+ expect(new_cat.lives).to eq 1337
147
+
148
+ # Single linked objects
149
+ expect(new_cat.best_friend).to be_a Cat
150
+ expect(new_cat.best_friend.name).to eq "Happy Cat"
151
+
152
+ # Array of linked objects
153
+ expect(new_cat.cat_pack.count).to eq 2
154
+ expect(new_cat.cat_pack[0].name).to eq "Happy Cat"
155
+ expect(new_cat.cat_pack[1].name).to eq "Worried Cat"
156
+
157
+ # Nested Links
158
+ expect(new_cat.best_friend.best_friend).to be_a Cat
159
+ expect(new_cat.best_friend.best_friend.name).to eq "Worried Cat"
160
+
161
+ # Nested array of linked objects
162
+ expect(new_cat.best_friend.cat_pack.count).to eq 2
163
+ expect(new_cat.best_friend.cat_pack[0].name).to eq "Nyan Cat"
164
+ expect(new_cat.best_friend.cat_pack[1].name).to eq "Worried Cat"
165
+
166
+ # Array of linked objects in a nested array of linked objects
167
+ expect(new_cat.cat_pack.first.name).to eq "Happy Cat"
168
+ expect(new_cat.cat_pack.first.cat_pack[0].name).to eq "Nyan Cat"
169
+ expect(new_cat.cat_pack.first.cat_pack[1].name).to eq "Worried Cat"
170
+ end
171
+
172
+ it 'using entry_mapping' do
173
+ vcr('entry/marshall') {
174
+ nyancat = create_client(entry_mapping: {'cat' => Cat}).entries(include: 2, 'sys.id' => 'nyancat').first
175
+ test_dump(nyancat)
176
+ }
177
+ end
178
+
179
+ it 'using resource_mapping' do
180
+ vcr('entry/marshall') {
181
+ nyancat = create_client(resource_mapping: {
182
+ 'Entry' => ->(_json_object) do
183
+ return Cat if _json_object.fetch('sys', {}).fetch('contentType', {}).fetch('sys', {}).fetch('id', nil) == 'cat'
184
+ Contentful::Entry
185
+ end
186
+ }).entries(include: 2, 'sys.id' => 'nyancat').first
187
+ test_dump(nyancat)
188
+ }
189
+ end
190
+
191
+ it 'newly created custom resources have property mappings' do
192
+ entry = Cat.new
193
+
194
+ expect(entry).to respond_to :name
195
+ expect(entry).to respond_to :lives
196
+ expect(entry).to respond_to :best_friend
197
+ expect(entry).to respond_to :cat_pack
198
+ end
199
+ end
200
+ end
85
201
  end
@@ -0,0 +1,168 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://cdn.contentful.com/spaces/g2b4ltw00meh/content_types?limit=1000
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - RubyContentfulGem/0.8.0
12
+ Authorization:
13
+ - Bearer <ACCESS_TOKEN>
14
+ Content-Type:
15
+ - application/vnd.contentful.delivery.v1+json
16
+ Accept-Encoding:
17
+ - gzip
18
+ Connection:
19
+ - close
20
+ Host:
21
+ - cdn.contentful.com
22
+ response:
23
+ status:
24
+ code: 200
25
+ message: OK
26
+ headers:
27
+ Access-Control-Allow-Headers:
28
+ - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation
29
+ Access-Control-Allow-Methods:
30
+ - GET,HEAD,OPTIONS
31
+ Access-Control-Allow-Origin:
32
+ - "*"
33
+ Access-Control-Expose-Headers:
34
+ - Etag
35
+ Access-Control-Max-Age:
36
+ - '86400'
37
+ Cache-Control:
38
+ - max-age=0
39
+ Content-Encoding:
40
+ - gzip
41
+ Content-Type:
42
+ - application/vnd.contentful.delivery.v1+json
43
+ Server:
44
+ - nginx
45
+ X-Contentful-Request-Id:
46
+ - 90a-2090959606
47
+ Content-Length:
48
+ - '653'
49
+ Accept-Ranges:
50
+ - bytes
51
+ Date:
52
+ - Mon, 07 Dec 2015 17:08:37 GMT
53
+ Via:
54
+ - 1.1 varnish
55
+ Age:
56
+ - '5494'
57
+ Connection:
58
+ - close
59
+ X-Served-By:
60
+ - cache-atl6233-ATL
61
+ X-Cache:
62
+ - HIT
63
+ X-Cache-Hits:
64
+ - '1'
65
+ Vary:
66
+ - Accept-Encoding
67
+ body:
68
+ encoding: ASCII-8BIT
69
+ string: !binary |-
70
+ H4sIAAAAAAAAA9VW3W/aMBB/569AeV6q8NF24o12VJXaaVLJXlpVlUkOsHDi
71
+ yL6syyr+99lOHAJxNg3Bw/KAsH0fv/P9fHcfvX7fk4X0Jv0P9VctsMhArbyp
72
+ EKTw1N72k5ZBjoSp/YFZyQ3N1CIwC0YTivooCMoNipBoiy/GYmlXyaUkMZYf
73
+ CMKKCwqeVjdOlxRYvFNpqplzqxoq06xWM0c01jaRItvZMwc2kBB+4r4K4xFh
74
+ 9BdozSVhEioYVaxaewfb2LL+vz89urxLlq/2963zeZEs+AHi493fUBa7/NOE
75
+ rDqif6Tp5iB6tRPaJEsJJ7ucOyCYC/CnAunGnaZlKRJrkYiBdF9aST1LDpMA
76
+ SylLpjJHdY5bURpa7uKcpSgMme1nSG0/7/iMzGjKgGKeriQqnvXvgcTK8UEy
77
+ So5GBN+oAsLf1krKHfpp+XKAzkWdGlScR0h56oZ10jeUIohNqwboZFSPWeYL
78
+ BausEf8/RUKe+e2S1wgXeea+9RvOGZCDlBzP1XueQKYKxZ/RrCupf4AUU0kW
79
+ zJRTFDmc6GnNZ9/8Nu/KpySBt4/OUfC/zsKp/wDFOxeqP+2XJNN4EkDiPj9P
80
+ CzCAvoCMBM3a77W8HY2pU+SMsG5JylOqemvXRXUInBHSE19w7Eyc6/RIMFU7
81
+ ea1HmriRI8WT+ko89VoyRoo7PfK0R5e9YcwUCZmRSM9NzdbXkjKSFvrfuuHc
82
+ WGySuS69q+FizPA9CBJY7/VL14hUO7zlqqanaKaKndmqmrtKvSfgB5W63Uz6
83
+ w4ZGJNSAAPFUj5PeMBhc+oOhH1yHg8tJMJqMgovB9efnpos8i7sURsPJ+Opi
84
+ fDV+toFsTRD697W37f0G3Z/jL/sKAAA=
85
+ http_version:
86
+ recorded_at: Mon, 07 Dec 2015 17:08:37 GMT
87
+ - request:
88
+ method: get
89
+ uri: https://cdn.contentful.com/spaces/g2b4ltw00meh/entries
90
+ body:
91
+ encoding: US-ASCII
92
+ string: ''
93
+ headers:
94
+ User-Agent:
95
+ - RubyContentfulGem/0.8.0
96
+ Authorization:
97
+ - Bearer <ACCESS_TOKEN>
98
+ Content-Type:
99
+ - application/vnd.contentful.delivery.v1+json
100
+ Accept-Encoding:
101
+ - gzip
102
+ Connection:
103
+ - close
104
+ Host:
105
+ - cdn.contentful.com
106
+ response:
107
+ status:
108
+ code: 200
109
+ message: OK
110
+ headers:
111
+ Access-Control-Allow-Headers:
112
+ - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation
113
+ Access-Control-Allow-Methods:
114
+ - GET,HEAD,OPTIONS
115
+ Access-Control-Allow-Origin:
116
+ - "*"
117
+ Access-Control-Expose-Headers:
118
+ - Etag
119
+ Access-Control-Max-Age:
120
+ - '86400'
121
+ Cache-Control:
122
+ - max-age=0
123
+ Content-Encoding:
124
+ - gzip
125
+ Content-Type:
126
+ - application/vnd.contentful.delivery.v1+json
127
+ Server:
128
+ - nginx
129
+ X-Contentful-Request-Id:
130
+ - 90a-2090959610
131
+ Content-Length:
132
+ - '499'
133
+ Accept-Ranges:
134
+ - bytes
135
+ Date:
136
+ - Mon, 07 Dec 2015 17:08:38 GMT
137
+ Via:
138
+ - 1.1 varnish
139
+ Age:
140
+ - '5494'
141
+ Connection:
142
+ - close
143
+ X-Served-By:
144
+ - cache-atl6233-ATL
145
+ X-Cache:
146
+ - HIT
147
+ X-Cache-Hits:
148
+ - '17'
149
+ Vary:
150
+ - Accept-Encoding
151
+ body:
152
+ encoding: ASCII-8BIT
153
+ string: !binary |-
154
+ H4sIAAAAAAAAA91US4/aMBC+8yuQz4UGSFiVG9r20MelBVRpq9XKOAOx8nBq
155
+ T0KjFf+9fiTEobR72VN9iDKT8ffNN488j8ZjohpFVuNn/aoNbErQFllLSRui
156
+ fec3JgYF0kz7Z9ZSKS+1EVgj4zlH8ylwNkfIDeAPC+hgr1gskyopM1RdhHN6
157
+ uRiHdXY5feFFSgxnfzR7kW7bnDcW8SqAx0bOcb4PMzwFQQ6JUdWd8+Xd6nTn
158
+ QvihQNl4gISJAqHAlvD1Mr/3cG/mn1KEo5AcXk7eCV4kn9Zq98DuNj8/hio9
159
+ Ldny++6rr0VCzRUXhS7PwtPOJGiueG1aSubBLJrM5pPgbjuLVkG4CqNpFEYP
160
+ Pk5Vxn+9EK0WwTR8txxcyASjmR0yKCa7Tafo0gBy4JDF/UyaphDk6O5sRA7j
161
+ rbW8rFVWHQ3iW6piOk0wzwZto/jEdS/FUwLUzkOiy6kjmboZFlcMXWUIHuFQ
162
+ D5hAbOGXrQ7UhyFADkg/Q3MS0qZPalAq9i+bgPegmORlR1CDjvWUmJB7WoiC
163
+ 6yo5FvZnxDexF9hysAHFQbevkrqBEjnLoN9EN9v+zN7YyqsFuLFxduP7nbte
164
+ kRbgn0M42OB+A/W/5vLl0e9ttWfd+P8HclCYfyfKClq1TrV5Po7Oo9/jOBmT
165
+ kwUAAA==
166
+ http_version:
167
+ recorded_at: Mon, 07 Dec 2015 17:08:38 GMT
168
+ recorded_with: VCR 2.9.3