contentful 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +12 -10
- data/contentful.gemspec +1 -1
- data/lib/contentful/client.rb +8 -2
- data/lib/contentful/request.rb +17 -1
- data/lib/contentful/resource_builder.rb +28 -11
- data/lib/contentful/support.rb +0 -8
- data/lib/contentful/version.rb +1 -1
- data/spec/client_configuration_spec.rb +19 -0
- data/spec/fixtures/vcr_cassettes/human.yml +95 -0
- data/spec/request_spec.rb +8 -0
- metadata +4 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be9193f04c4996121d4fc9ca04b4dc164fa2ef81
|
4
|
+
data.tar.gz: e7fbe7fde7897b9e148920026a149cfd50466d35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a6ac299c0adbf1504a99f164170959020845d450961418caa9ae9dccb1d8097d0cb120be135288d5a195c404daded8a05ad68480a149921646dbc25aa7b260d
|
7
|
+
data.tar.gz: 634debe7bd43a3b4c8a9803441b1f20659121a9766a76146713a7d136033698e1a1e8d1a70e77408bb33d175b3805e8c50d12606c64fdf1f291236fd7f6a5582
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
### 0.2.0
|
2
|
+
|
3
|
+
* Introduce new :entry_mapping configuration to enable custom Entry classes based on ContentTypes
|
4
|
+
* Update HTTP gem dependency to 0.6
|
5
|
+
* Convert arrays in query values to strings, separated by comma
|
6
|
+
|
7
|
+
|
1
8
|
### 0.1.3
|
2
9
|
|
3
10
|
* Better link inclusion processing, prevent "stack level to deep" errors
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Add to your Gemfile and bundle:
|
|
20
20
|
)
|
21
21
|
|
22
22
|
You can query for entries, assets, etc. very similar as described in the
|
23
|
-
[Delivery API Documentation](https://www.contentful.com/developers/documentation/content-delivery-api/). Please note, that all methods of the Ruby client library are snake_cased, instead of JavaScript's camelCase
|
23
|
+
[Delivery API Documentation](https://www.contentful.com/developers/documentation/content-delivery-api/). Please note, that **all methods of the Ruby client library are snake_cased, instead of JavaScript's camelCase**:
|
24
24
|
|
25
25
|
client.content_types
|
26
26
|
client.entry 'nyancat'
|
@@ -37,7 +37,7 @@ The results are returned as Contentful::Resource objects. Multiple results will
|
|
37
37
|
content_type.description # "Meow."
|
38
38
|
|
39
39
|
|
40
|
-
Alternatively, the data can be accessed as Ruby `Hash` with symbolized keys:
|
40
|
+
Alternatively, the data can be accessed as Ruby `Hash` with symbolized keys (and in camelCase):
|
41
41
|
|
42
42
|
content_type.properties # { name: '...', description: '...' }
|
43
43
|
|
@@ -111,6 +111,7 @@ Resources, that have been requested directly (i.e. no child resources), can be f
|
|
111
111
|
entries = client.entries
|
112
112
|
entries.reload # Fetches the array of entries again
|
113
113
|
|
114
|
+
|
114
115
|
### Field Type "Object"
|
115
116
|
|
116
117
|
While for known field types, the field data is accessible using methods or the `#fields` hash with symbol keys, it behaves differently for nested data of the type "Object". The client will treat them as arbitrary hashes and will not parse the data inside, which also means, this data is indexed by Ruby strings, not symbols.
|
@@ -173,21 +174,22 @@ You can register your custom class on client initialization:
|
|
173
174
|
|
174
175
|
More information on `:resource_mapping` can be found in examples/resource_mapping.rb and more on custom classes in examples/custom_classes.rb
|
175
176
|
|
177
|
+
You can also register custom entry classes to be used based on the entry's content_type using the :entry_mapping configuration:
|
176
178
|
|
177
|
-
|
178
|
-
|
179
|
-
|
179
|
+
class Cat < Contentful::Entry
|
180
|
+
# define methods based on :fields, etc
|
181
|
+
end
|
180
182
|
|
181
183
|
client = Contentful::Client.new(
|
182
184
|
space: 'cfexampleapi',
|
183
185
|
access_token: 'b4c0n73n7fu1',
|
184
|
-
|
186
|
+
entry_mapping: {
|
187
|
+
'cat' => Cat
|
188
|
+
}
|
185
189
|
)
|
186
|
-
content_type = client.content_type 'cat'
|
187
|
-
MyCat = Contentful::DynamicEntry.create(content_type)
|
188
|
-
client.register_dynamic_entry 'cat', MyCat
|
189
190
|
|
190
|
-
|
191
|
+
client.entry('nyancat') # is instance of Cat
|
192
|
+
|
191
193
|
|
192
194
|
## License
|
193
195
|
|
data/contentful.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^spec/})
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
|
20
|
-
gem.add_dependency 'http', '~> 0
|
20
|
+
gem.add_dependency 'http', '~> 0.6'
|
21
21
|
gem.add_dependency 'multi_json', '~> 1'
|
22
22
|
|
23
23
|
gem.add_development_dependency 'bundler', '~> 1.5'
|
data/lib/contentful/client.rb
CHANGED
@@ -17,6 +17,7 @@ module Contentful
|
|
17
17
|
authentication_mechanism: :header,
|
18
18
|
resource_builder: ResourceBuilder,
|
19
19
|
resource_mapping: {},
|
20
|
+
entry_mapping: {},
|
20
21
|
raw_mode: false
|
21
22
|
}
|
22
23
|
|
@@ -41,7 +42,7 @@ module Contentful
|
|
41
42
|
|
42
43
|
# Returns the default configuration
|
43
44
|
def default_configuration
|
44
|
-
DEFAULT_CONFIGURATION
|
45
|
+
DEFAULT_CONFIGURATION.dup
|
45
46
|
end
|
46
47
|
|
47
48
|
# Gets the client's space
|
@@ -130,7 +131,12 @@ module Contentful
|
|
130
131
|
|
131
132
|
return response if !build_resource || configuration[:raw_mode]
|
132
133
|
|
133
|
-
result = configuration[:resource_builder].new(
|
134
|
+
result = configuration[:resource_builder].new(
|
135
|
+
self,
|
136
|
+
response, configuration[:resource_mapping],
|
137
|
+
configuration[:entry_mapping]
|
138
|
+
).run
|
139
|
+
|
134
140
|
raise result if result.is_a?(Error) && configuration[:raise_errors]
|
135
141
|
result
|
136
142
|
end
|
data/lib/contentful/request.rb
CHANGED
@@ -8,7 +8,9 @@ module Contentful
|
|
8
8
|
def initialize(client, endpoint, query = {}, id = nil)
|
9
9
|
@client = client
|
10
10
|
@endpoint = endpoint
|
11
|
-
@query =
|
11
|
+
@query = if query && !query.empty?
|
12
|
+
normalize_query(query)
|
13
|
+
end
|
12
14
|
|
13
15
|
if id
|
14
16
|
@type = :single
|
@@ -33,5 +35,19 @@ module Contentful
|
|
33
35
|
def copy
|
34
36
|
Marshal.load(Marshal.dump(self))
|
35
37
|
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def normalize_query(query)
|
43
|
+
Hash[
|
44
|
+
query.map do |key, value|
|
45
|
+
[
|
46
|
+
key.to_sym,
|
47
|
+
value.is_a?(::Array) ? value.join(',') : value
|
48
|
+
]
|
49
|
+
end
|
50
|
+
]
|
51
|
+
end
|
36
52
|
end
|
37
53
|
end
|
@@ -15,21 +15,23 @@ module Contentful
|
|
15
15
|
DEFAULT_RESOURCE_MAPPING = {
|
16
16
|
'Space' => Space,
|
17
17
|
'ContentType' => ContentType,
|
18
|
-
'Entry' => :
|
18
|
+
'Entry' => :find_entry_class,
|
19
19
|
'Asset' => Asset,
|
20
20
|
'Array' => Array,
|
21
21
|
'Link' => Link,
|
22
22
|
}
|
23
|
+
DEFAULT_ENTRY_MAPPING = {}
|
23
24
|
|
24
|
-
attr_reader :client, :response, :resource_mapping, :resource
|
25
|
+
attr_reader :client, :response, :resource_mapping, :entry_mapping, :resource
|
25
26
|
|
26
27
|
|
27
|
-
def initialize(client, response, resource_mapping = {})
|
28
|
+
def initialize(client, response, resource_mapping = {}, entry_mapping = {})
|
28
29
|
@response = response
|
29
30
|
@client = client
|
30
31
|
@included_resources = {}
|
31
32
|
@known_resources = Hash.new{ |h,k| h[k] = {} }
|
32
33
|
@resource_mapping = default_resource_mapping.merge(resource_mapping)
|
34
|
+
@entry_mapping = default_entry_mapping.merge(entry_mapping)
|
33
35
|
end
|
34
36
|
|
35
37
|
# Starts the parsing process.
|
@@ -37,7 +39,7 @@ module Contentful
|
|
37
39
|
def run
|
38
40
|
case response.status
|
39
41
|
when :contentful_error
|
40
|
-
Error[response.raw.
|
42
|
+
Error[response.raw.status].new(response)
|
41
43
|
when :unparsable_json
|
42
44
|
UnparsableJson.new(response)
|
43
45
|
when :not_contentful
|
@@ -80,21 +82,30 @@ module Contentful
|
|
80
82
|
res
|
81
83
|
end
|
82
84
|
|
83
|
-
#
|
85
|
+
# Checks in a custom class for an entry was defined in entry_mapping
|
86
|
+
def find_entry_class(object)
|
87
|
+
entry_mapping[content_type_id_for_entry(object)] || try_dynamic_entry(object)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Automatically converts Entry to DynamicEntry if in cache
|
84
91
|
def try_dynamic_entry(object)
|
85
92
|
get_dynamic_entry(object) || Entry
|
86
93
|
end
|
87
94
|
|
88
95
|
# Finds the proper DynamicEntry class for an entry
|
89
96
|
def get_dynamic_entry(object)
|
90
|
-
if
|
91
|
-
|
92
|
-
object["sys"]["contentType"]["sys"] &&
|
93
|
-
object["sys"]["contentType"]["sys"]["id"]
|
94
|
-
client.dynamic_entry_cache[id.to_sym]
|
97
|
+
if content_id = content_type_id_for_entry(object)
|
98
|
+
client.dynamic_entry_cache[content_id.to_sym]
|
95
99
|
end
|
96
100
|
end
|
97
101
|
|
102
|
+
def content_type_id_for_entry(object)
|
103
|
+
object["sys"] &&
|
104
|
+
object["sys"]["contentType"] &&
|
105
|
+
object["sys"]["contentType"]["sys"] &&
|
106
|
+
object["sys"]["contentType"]["sys"]["id"]
|
107
|
+
end
|
108
|
+
|
98
109
|
# Uses the resource mapping to find the proper Resource class to initialize
|
99
110
|
# for this Response object type
|
100
111
|
#
|
@@ -104,6 +115,7 @@ module Contentful
|
|
104
115
|
# - Symbol: Will be called as method of the ResourceBuilder itself
|
105
116
|
def detect_resource_class(object)
|
106
117
|
type = object["sys"] && object["sys"]["type"]
|
118
|
+
|
107
119
|
case res_class = resource_mapping[type]
|
108
120
|
when Symbol
|
109
121
|
public_send(res_class, object)
|
@@ -118,7 +130,12 @@ module Contentful
|
|
118
130
|
|
119
131
|
# The default mapping for #detect_resource_class
|
120
132
|
def default_resource_mapping
|
121
|
-
DEFAULT_RESOURCE_MAPPING
|
133
|
+
DEFAULT_RESOURCE_MAPPING.dup
|
134
|
+
end
|
135
|
+
|
136
|
+
# The default entry mapping
|
137
|
+
def default_entry_mapping
|
138
|
+
DEFAULT_ENTRY_MAPPING.dup
|
122
139
|
end
|
123
140
|
|
124
141
|
|
data/lib/contentful/support.rb
CHANGED
@@ -7,14 +7,6 @@ module Contentful
|
|
7
7
|
snake = String(object).gsub(/(?<!^)[A-Z]/) { "_#$&" }
|
8
8
|
snake.downcase
|
9
9
|
end
|
10
|
-
|
11
|
-
# Transforms each hash key into a symbol (like in AS)
|
12
|
-
def symbolize_keys(hash)
|
13
|
-
result = {}
|
14
|
-
# XXX remove inline rescue
|
15
|
-
hash.each_key { |key| result[(key.to_sym rescue key)] = hash[key] }
|
16
|
-
result
|
17
|
-
end
|
18
10
|
end
|
19
11
|
end
|
20
12
|
end
|
data/lib/contentful/version.rb
CHANGED
@@ -194,4 +194,23 @@ describe 'Client Configuration Options' do
|
|
194
194
|
expect( nyancat.https_image_url ).to start_with 'https'
|
195
195
|
end
|
196
196
|
end
|
197
|
+
|
198
|
+
describe ':entry_mapping' do
|
199
|
+
it 'lets you register your own entry classes for certain entry types' do
|
200
|
+
class Cat < Contentful::Entry
|
201
|
+
# define methods based on :fields, etc
|
202
|
+
end
|
203
|
+
|
204
|
+
client = create_client(
|
205
|
+
entry_mapping: {
|
206
|
+
'cat' => Cat
|
207
|
+
}
|
208
|
+
)
|
209
|
+
|
210
|
+
nyancat = vcr('entry'){ client.entry 'nyancat' }
|
211
|
+
finn = vcr('human'){ client.entry 'finn' }
|
212
|
+
expect( nyancat ).to be_a Cat
|
213
|
+
expect( finn ).to be_a Contentful::Entry
|
214
|
+
end
|
215
|
+
end
|
197
216
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://cdn.contentful.com/spaces/cfexampleapi/entries/finn
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- RubyContentfulGem/0.1.3
|
12
|
+
Authorization:
|
13
|
+
- Bearer b4c0n73n7fu1
|
14
|
+
Content-Type:
|
15
|
+
- application/vnd.contentful.delivery.v1+json
|
16
|
+
Host:
|
17
|
+
- cdn.contentful.com
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Date:
|
24
|
+
- Fri, 18 Apr 2014 17:56:56 GMT
|
25
|
+
Server:
|
26
|
+
- nginx/1.1.19
|
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
|
29
|
+
Access-Control-Allow-Methods:
|
30
|
+
- GET,HEAD,OPTIONS
|
31
|
+
Access-Control-Allow-Origin:
|
32
|
+
- "*"
|
33
|
+
Access-Control-Max-Age:
|
34
|
+
- '86400'
|
35
|
+
Cache-Control:
|
36
|
+
- max-age=0
|
37
|
+
Content-Type:
|
38
|
+
- application/vnd.contentful.delivery.v1+json
|
39
|
+
Etag:
|
40
|
+
- "\"fcdc41e711c72e3cbc389211fde21287\""
|
41
|
+
X-Contentful-Request-Id:
|
42
|
+
- f1c-737727925
|
43
|
+
Content-Length:
|
44
|
+
- '591'
|
45
|
+
Accept-Ranges:
|
46
|
+
- bytes
|
47
|
+
Via:
|
48
|
+
- 1.1 varnish
|
49
|
+
Age:
|
50
|
+
- '0'
|
51
|
+
X-Served-By:
|
52
|
+
- cache-fra1229-FRA
|
53
|
+
X-Cache:
|
54
|
+
- MISS
|
55
|
+
X-Cache-Hits:
|
56
|
+
- '0'
|
57
|
+
Vary:
|
58
|
+
- Accept-Encoding
|
59
|
+
body:
|
60
|
+
encoding: UTF-8
|
61
|
+
string: |
|
62
|
+
{
|
63
|
+
"fields": {
|
64
|
+
"name": "Finn",
|
65
|
+
"description": "Fearless adventurer! Defender of pancakes.",
|
66
|
+
"likes": [
|
67
|
+
"adventure"
|
68
|
+
]
|
69
|
+
},
|
70
|
+
"sys": {
|
71
|
+
"space": {
|
72
|
+
"sys": {
|
73
|
+
"type": "Link",
|
74
|
+
"linkType": "Space",
|
75
|
+
"id": "cfexampleapi"
|
76
|
+
}
|
77
|
+
},
|
78
|
+
"type": "Entry",
|
79
|
+
"contentType": {
|
80
|
+
"sys": {
|
81
|
+
"type": "Link",
|
82
|
+
"linkType": "ContentType",
|
83
|
+
"id": "human"
|
84
|
+
}
|
85
|
+
},
|
86
|
+
"id": "finn",
|
87
|
+
"revision": 6,
|
88
|
+
"createdAt": "2013-06-27T22:46:21.450Z",
|
89
|
+
"updatedAt": "2013-09-09T16:15:01.297Z",
|
90
|
+
"locale": "en-US"
|
91
|
+
}
|
92
|
+
}
|
93
|
+
http_version:
|
94
|
+
recorded_at: Fri, 18 Apr 2014 17:56:56 GMT
|
95
|
+
recorded_with: VCR 2.9.0
|
data/spec/request_spec.rb
CHANGED
@@ -11,6 +11,14 @@ describe Contentful::Request do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
describe '#query' do
|
15
|
+
it 'converts arrays given in query to comma strings' do
|
16
|
+
client = create_client
|
17
|
+
request = Contentful::Request.new(client, '/entries', {'fields.likes[in]' => ['jake', 'finn']})
|
18
|
+
expect( request.query[:'fields.likes[in]'] ).to eq 'jake,finn'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
context '[single resource]' do
|
15
23
|
let(:request){
|
16
24
|
Contentful::Request.new(create_client, '/content_types', nil, 'nyancat')
|
metadata
CHANGED
@@ -1,23 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contentful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Contentful GmbH (Jan Lelis)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
- - "<"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '0.6'
|
23
20
|
type: :runtime
|
@@ -25,9 +22,6 @@ dependencies:
|
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - "~>"
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
- - "<"
|
31
25
|
- !ruby/object:Gem::Version
|
32
26
|
version: '0.6'
|
33
27
|
- !ruby/object:Gem::Dependency
|
@@ -225,6 +219,7 @@ files:
|
|
225
219
|
- spec/fixtures/vcr_cassettes/entry.yml
|
226
220
|
- spec/fixtures/vcr_cassettes/entry_cache.yml
|
227
221
|
- spec/fixtures/vcr_cassettes/field.yml
|
222
|
+
- spec/fixtures/vcr_cassettes/human.yml
|
228
223
|
- spec/fixtures/vcr_cassettes/locale.yml
|
229
224
|
- spec/fixtures/vcr_cassettes/location.yml
|
230
225
|
- spec/fixtures/vcr_cassettes/not_found.yml
|
@@ -299,6 +294,7 @@ test_files:
|
|
299
294
|
- spec/fixtures/vcr_cassettes/entry.yml
|
300
295
|
- spec/fixtures/vcr_cassettes/entry_cache.yml
|
301
296
|
- spec/fixtures/vcr_cassettes/field.yml
|
297
|
+
- spec/fixtures/vcr_cassettes/human.yml
|
302
298
|
- spec/fixtures/vcr_cassettes/locale.yml
|
303
299
|
- spec/fixtures/vcr_cassettes/location.yml
|
304
300
|
- spec/fixtures/vcr_cassettes/not_found.yml
|