contentful 0.8.0 → 0.9.0

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.
@@ -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