contentful 1.2.2 → 2.0.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 +21 -0
- data/LICENSE.txt +1 -0
- data/README.md +8 -0
- data/contentful.gemspec +2 -1
- data/examples/custom_classes.rb +23 -26
- data/examples/raise_errors.rb +2 -2
- data/lib/contentful.rb +0 -1
- data/lib/contentful/array.rb +27 -19
- data/lib/contentful/array_like.rb +51 -0
- data/lib/contentful/asset.rb +43 -11
- data/lib/contentful/base_resource.rb +87 -0
- data/lib/contentful/client.rb +43 -34
- data/lib/contentful/coercions.rb +116 -0
- data/lib/contentful/content_type.rb +23 -8
- data/lib/contentful/content_type_cache.rb +26 -0
- data/lib/contentful/deleted_asset.rb +2 -5
- data/lib/contentful/deleted_entry.rb +2 -5
- data/lib/contentful/entry.rb +55 -33
- data/lib/contentful/error.rb +1 -1
- data/lib/contentful/field.rb +37 -9
- data/lib/contentful/fields_resource.rb +115 -0
- data/lib/contentful/file.rb +7 -8
- data/lib/contentful/link.rb +3 -6
- data/lib/contentful/locale.rb +6 -6
- data/lib/contentful/location.rb +7 -5
- data/lib/contentful/resource_builder.rb +72 -226
- data/lib/contentful/space.rb +16 -6
- data/lib/contentful/support.rb +41 -3
- data/lib/contentful/sync_page.rb +17 -10
- data/lib/contentful/version.rb +1 -1
- data/spec/array_spec.rb +4 -8
- data/spec/client_class_spec.rb +12 -23
- data/spec/client_configuration_spec.rb +13 -23
- data/spec/content_type_spec.rb +0 -5
- data/spec/entry_spec.rb +130 -125
- data/spec/error_requests_spec.rb +1 -1
- data/spec/field_spec.rb +0 -5
- data/spec/file_spec.rb +0 -5
- data/spec/fixtures/vcr_cassettes/entry.yml +54 -64
- data/spec/fixtures/vcr_cassettes/entry/include_resolution.yml +101 -0
- data/spec/fixtures/vcr_cassettes/entry/marshall.yml +227 -251
- data/spec/fixtures/vcr_cassettes/entry/raw.yml +88 -124
- data/spec/fixtures/vcr_cassettes/entry_locales.yml +56 -74
- data/spec/fixtures/vcr_cassettes/human.yml +63 -40
- data/spec/fixtures/vcr_cassettes/location.yml +99 -211
- data/spec/fixtures/vcr_cassettes/multi_locale_array_reference.yml +12 -16
- data/spec/fixtures/vcr_cassettes/not_found.yml +26 -21
- data/spec/fixtures/vcr_cassettes/nyancat.yml +53 -63
- data/spec/fixtures/vcr_cassettes/ratelimit.yml +1 -1
- data/spec/fixtures/vcr_cassettes/reloaded_entry.yml +54 -64
- data/spec/fixtures/vcr_cassettes/unauthorized.yml +1 -1
- data/spec/fixtures/vcr_cassettes/unavailable.yml +27 -15
- data/spec/link_spec.rb +3 -2
- data/spec/locale_spec.rb +0 -5
- data/spec/location_spec.rb +1 -6
- data/spec/request_spec.rb +3 -2
- data/spec/resource_building_spec.rb +10 -7
- data/spec/response_spec.rb +1 -1
- data/spec/space_spec.rb +0 -5
- data/spec/spec_helper.rb +3 -0
- data/spec/support/json_responses.rb +3 -3
- data/spec/sync_page_spec.rb +1 -6
- data/spec/sync_spec.rb +11 -7
- metadata +69 -20
- data/examples/dynamic_entries.rb +0 -124
- data/examples/resource_mapping.rb +0 -32
- data/lib/contentful/constants.rb +0 -504
- data/lib/contentful/dynamic_entry.rb +0 -57
- data/lib/contentful/resource.rb +0 -239
- data/lib/contentful/resource/array_like.rb +0 -39
- data/lib/contentful/resource/asset_fields.rb +0 -58
- data/lib/contentful/resource/custom_resource.rb +0 -29
- data/lib/contentful/resource/fields.rb +0 -73
- data/lib/contentful/resource/system_properties.rb +0 -55
- data/spec/coercions_spec.rb +0 -23
- data/spec/dynamic_entry_spec.rb +0 -75
- data/spec/resource_spec.rb +0 -79
@@ -1,57 +0,0 @@
|
|
1
|
-
require_relative 'resource'
|
2
|
-
require_relative 'resource/fields'
|
3
|
-
require_relative 'location'
|
4
|
-
|
5
|
-
module Contentful
|
6
|
-
# Wrapper for Entries with Cached Content Types
|
7
|
-
class DynamicEntry < Entry
|
8
|
-
# Coercions from Contentful Types to Ruby native types
|
9
|
-
KNOWN_TYPES = {
|
10
|
-
'String' => :string,
|
11
|
-
'Text' => :string,
|
12
|
-
'Symbol' => :string,
|
13
|
-
'Integer' => :integer,
|
14
|
-
'Float' => :float,
|
15
|
-
'Boolean' => :boolean,
|
16
|
-
'Date' => :date,
|
17
|
-
'Location' => Location
|
18
|
-
}
|
19
|
-
|
20
|
-
# @private
|
21
|
-
def self.create(content_type)
|
22
|
-
unless content_type.is_a? ContentType
|
23
|
-
content_type = ContentType.new(content_type)
|
24
|
-
end
|
25
|
-
|
26
|
-
fields_coercions = Hash[
|
27
|
-
content_type.fields.map do |field|
|
28
|
-
[field.id.to_sym, KNOWN_TYPES[field.type]]
|
29
|
-
end
|
30
|
-
]
|
31
|
-
|
32
|
-
Class.new DynamicEntry do
|
33
|
-
content_type.fields.each do |f|
|
34
|
-
define_method Support.snakify(f.id).to_sym do |wanted_locale = nil|
|
35
|
-
fields(wanted_locale)[f.id.to_sym]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
define_singleton_method :fields_coercions do
|
40
|
-
fields_coercions
|
41
|
-
end
|
42
|
-
|
43
|
-
define_singleton_method :content_type do
|
44
|
-
content_type
|
45
|
-
end
|
46
|
-
|
47
|
-
define_singleton_method :to_s do
|
48
|
-
"Contentful::DynamicEntry[#{content_type.id}]"
|
49
|
-
end
|
50
|
-
|
51
|
-
define_singleton_method :inspect do
|
52
|
-
"Contentful::DynamicEntry[#{content_type.id}]"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/contentful/resource.rb
DELETED
@@ -1,239 +0,0 @@
|
|
1
|
-
require_relative 'resource/system_properties'
|
2
|
-
require 'contentful/constants'
|
3
|
-
require 'date'
|
4
|
-
|
5
|
-
module Contentful
|
6
|
-
# Include this module to declare a class to be a contentful resource.
|
7
|
-
# This is done by the default in the existing resource classes
|
8
|
-
#
|
9
|
-
# You can define your own classes that behave like contentful resources:
|
10
|
-
# See examples/custom_classes.rb to see how.
|
11
|
-
#
|
12
|
-
# Take a look at examples/resource_mapping.rb on how to register them to be returned
|
13
|
-
# by the client by default
|
14
|
-
#
|
15
|
-
# @see _ examples/custom_classes.rb Custom Class as Resource
|
16
|
-
# @see _ examples/resource_mapping.rb Mapping a Custom Class
|
17
|
-
module Resource
|
18
|
-
# @private
|
19
|
-
# rubocop:disable Style/DoubleNegation
|
20
|
-
COERCIONS = {
|
21
|
-
string: ->(v) { v.nil? ? nil : v.to_s },
|
22
|
-
integer: ->(v) { v.to_i },
|
23
|
-
float: ->(v) { v.to_f },
|
24
|
-
boolean: ->(v) { !!v },
|
25
|
-
date: ->(v) { DateTime.parse(v) }
|
26
|
-
}
|
27
|
-
# rubocop:enable Style/DoubleNegation
|
28
|
-
|
29
|
-
attr_reader :properties, :request, :client, :default_locale, :raw
|
30
|
-
|
31
|
-
# @private
|
32
|
-
def initialize(object = nil,
|
33
|
-
request = nil,
|
34
|
-
client = nil,
|
35
|
-
default_locale = Contentful::Client::DEFAULT_CONFIGURATION[:default_locale])
|
36
|
-
self.class.update_coercions!
|
37
|
-
@default_locale = default_locale
|
38
|
-
|
39
|
-
@properties = {}
|
40
|
-
self.class.property_coercions.keys.each do |property_name|
|
41
|
-
@properties[property_name] = nil
|
42
|
-
end
|
43
|
-
|
44
|
-
@properties = @properties.merge(
|
45
|
-
extract_from_object(object, :property,
|
46
|
-
self.class.property_coercions.keys)
|
47
|
-
)
|
48
|
-
@request = request
|
49
|
-
@client = client
|
50
|
-
@raw = object
|
51
|
-
end
|
52
|
-
|
53
|
-
# @private
|
54
|
-
def inspect(info = nil)
|
55
|
-
properties_info = properties.empty? ? '' : " @properties=#{properties.inspect}"
|
56
|
-
"#<#{self.class}:#{properties_info}#{info}>"
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns true for resources that are entries
|
60
|
-
def entry?
|
61
|
-
false
|
62
|
-
end
|
63
|
-
|
64
|
-
# Returns true for resources that behave like an array
|
65
|
-
def array?
|
66
|
-
false
|
67
|
-
end
|
68
|
-
|
69
|
-
# Resources that don't include SystemProperties return nil for #sys
|
70
|
-
def sys
|
71
|
-
nil
|
72
|
-
end
|
73
|
-
|
74
|
-
# Resources that don't include Fields or AssetFields return nil for #fields
|
75
|
-
def fields
|
76
|
-
nil
|
77
|
-
end
|
78
|
-
|
79
|
-
# Issues the request that was made to fetch this response again.
|
80
|
-
# Only works for top-level resources
|
81
|
-
def reload
|
82
|
-
if request
|
83
|
-
request.get
|
84
|
-
else
|
85
|
-
false
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Register the resources properties on class level by using the #property method
|
90
|
-
# @private
|
91
|
-
module ClassMethods
|
92
|
-
# By default, fields come flattened in the current locale. This is different for sync
|
93
|
-
def property_coercions
|
94
|
-
@property_coercions ||= {}
|
95
|
-
end
|
96
|
-
|
97
|
-
# Defines which properties of a resource your class expects
|
98
|
-
# Define them in :camelCase, they will be available as #snake_cased methods
|
99
|
-
#
|
100
|
-
# You can pass in a second "type" argument:
|
101
|
-
# - If it is a class, it will be initialized for the property
|
102
|
-
# - Symbols are looked up in the COERCION constant for a lambda that
|
103
|
-
# defines a type conversion to apply
|
104
|
-
#
|
105
|
-
# Note: This second argument is not meant for contentful sub-resources,
|
106
|
-
# but for structured objects (like locales in a space)
|
107
|
-
# Sub-resources are handled by the resource builder
|
108
|
-
def property(name, property_class = nil)
|
109
|
-
property_coercions[name.to_sym] = property_class
|
110
|
-
define_method Contentful::Support.snakify(name) do
|
111
|
-
properties[name.to_sym]
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Ensure inherited classes pick up coercions
|
116
|
-
def update_coercions!
|
117
|
-
return if @coercions_updated
|
118
|
-
|
119
|
-
if superclass.respond_to? :property_coercions
|
120
|
-
@property_coercions = superclass.property_coercions.dup.merge(@property_coercions || {})
|
121
|
-
end
|
122
|
-
|
123
|
-
if superclass.respond_to? :sys_coercions
|
124
|
-
@sys_coercions = superclass.sys_coercions.dup.merge(@sys_coercions || {})
|
125
|
-
end
|
126
|
-
|
127
|
-
if superclass.respond_to? :fields_coercions
|
128
|
-
@fields_coercions = superclass.fields_coercions.dup.merge(@fields_coercions || {})
|
129
|
-
end
|
130
|
-
|
131
|
-
@coercions_updated = true
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# @private
|
136
|
-
def self.included(base)
|
137
|
-
base.extend(ClassMethods)
|
138
|
-
end
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def initialize_fields_for_localized_resource(object)
|
143
|
-
@fields = {}
|
144
|
-
|
145
|
-
object.fetch('fields', {}).each do |field_name, nested_child_object|
|
146
|
-
if Support.localized?(nested_child_object)
|
147
|
-
nested_child_object.each do |object_locale, real_child_object|
|
148
|
-
@fields[object_locale] ||= {}
|
149
|
-
@fields[object_locale].merge! extract_from_object(
|
150
|
-
{ field_name => real_child_object }, :fields
|
151
|
-
)
|
152
|
-
end
|
153
|
-
else
|
154
|
-
# if sys.locale property not present (due to select operator) use default_locale
|
155
|
-
@fields[locale || default_locale] ||= {}
|
156
|
-
@fields[locale || default_locale].merge! extract_from_object({ field_name => nested_child_object }, :fields)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def extract_from_object(object, namespace, keys = nil)
|
162
|
-
if object
|
163
|
-
keys ||= object.keys
|
164
|
-
keys.each.with_object({}) do |name, res|
|
165
|
-
value = object.is_a?(::Array) ? object : object[name.to_s]
|
166
|
-
kind = self.class.public_send(:"#{namespace}_coercions")[name.to_sym]
|
167
|
-
res[name.to_sym] = coerce_value_or_array(value, kind)
|
168
|
-
end
|
169
|
-
else
|
170
|
-
{}
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def coerce_value_or_array(value, what = nil)
|
175
|
-
if value.is_a? ::Array
|
176
|
-
value.map { |v| coerce_or_create_class(v, what) }
|
177
|
-
elsif should_coerce_hash?(value)
|
178
|
-
::Hash[value.map do |k, v|
|
179
|
-
to_coerce = pre_coerce(v)
|
180
|
-
coercion = v.is_a?(Numeric) ? v : coerce_or_create_class(to_coerce, what)
|
181
|
-
[k.to_sym, coercion]
|
182
|
-
end]
|
183
|
-
else
|
184
|
-
coerce_or_create_class(value, what)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
def pre_coerce(value)
|
189
|
-
case value
|
190
|
-
when Numeric, true, false, nil
|
191
|
-
value
|
192
|
-
when Hash
|
193
|
-
result = {}
|
194
|
-
value.each_key do |k|
|
195
|
-
result[k.to_sym] = pre_coerce(value[k])
|
196
|
-
end
|
197
|
-
result
|
198
|
-
when ::Array
|
199
|
-
value.map { |e| pre_coerce(e) }
|
200
|
-
else
|
201
|
-
value.to_s
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
def should_coerce_hash?(value)
|
206
|
-
value.is_a?(::Hash) &&
|
207
|
-
!is_a?(Asset) &&
|
208
|
-
!is_a?(Field) &&
|
209
|
-
!location?(value) &&
|
210
|
-
!link?(value) &&
|
211
|
-
!image?(value)
|
212
|
-
end
|
213
|
-
|
214
|
-
def location?(value)
|
215
|
-
value.key?('lat') || value.key?('lon')
|
216
|
-
end
|
217
|
-
|
218
|
-
def link?(value)
|
219
|
-
value.key?('sys') && value['sys']['type'] == 'Link'
|
220
|
-
end
|
221
|
-
|
222
|
-
def image?(value)
|
223
|
-
value.key?('image')
|
224
|
-
end
|
225
|
-
|
226
|
-
def coerce_or_create_class(value, what)
|
227
|
-
case what
|
228
|
-
when Symbol
|
229
|
-
COERCIONS[what] ? COERCIONS[what][value] : value
|
230
|
-
when Proc
|
231
|
-
what[value]
|
232
|
-
when Class
|
233
|
-
what.new(value, client) if value
|
234
|
-
else
|
235
|
-
value
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module Contentful
|
2
|
-
module Resource
|
3
|
-
# Useful methods for array-like resources that can be included if an
|
4
|
-
# :items property exists
|
5
|
-
module ArrayLike
|
6
|
-
include Enumerable
|
7
|
-
|
8
|
-
# Returns true for array-like resources
|
9
|
-
#
|
10
|
-
# @return [true]
|
11
|
-
def array?
|
12
|
-
true
|
13
|
-
end
|
14
|
-
|
15
|
-
# Delegates to items#each
|
16
|
-
#
|
17
|
-
# @yield [Contentful::Entry, Contentful::Asset]
|
18
|
-
def each_item(&block)
|
19
|
-
items.each(&block)
|
20
|
-
end
|
21
|
-
alias each each_item
|
22
|
-
|
23
|
-
# Delegates to items#empty?
|
24
|
-
#
|
25
|
-
# @return [Boolean]
|
26
|
-
def empty?
|
27
|
-
items.empty?
|
28
|
-
end
|
29
|
-
|
30
|
-
# Delegetes to items#size
|
31
|
-
#
|
32
|
-
# @return [Number]
|
33
|
-
def size
|
34
|
-
items.size
|
35
|
-
end
|
36
|
-
alias length size
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require_relative '../file'
|
2
|
-
|
3
|
-
module Contentful
|
4
|
-
module Resource
|
5
|
-
# Special fields for Asset. Don't include together wit Contentful::Resource::Fields
|
6
|
-
#
|
7
|
-
# It depends on system properties being available
|
8
|
-
module AssetFields
|
9
|
-
# Special field coercions for Asset.
|
10
|
-
FIELDS_COERCIONS = {
|
11
|
-
title: :string,
|
12
|
-
description: :string,
|
13
|
-
file: File
|
14
|
-
}
|
15
|
-
|
16
|
-
# Returns all fields of the asset
|
17
|
-
#
|
18
|
-
# @return [Hash] localized fields
|
19
|
-
def fields(wanted_locale = default_locale)
|
20
|
-
@fields[locale || wanted_locale] || {}
|
21
|
-
end
|
22
|
-
|
23
|
-
# @private
|
24
|
-
def initialize(object, *)
|
25
|
-
super
|
26
|
-
|
27
|
-
initialize_fields_for_localized_resource(object)
|
28
|
-
end
|
29
|
-
|
30
|
-
# @private
|
31
|
-
def inspect(info = nil)
|
32
|
-
if fields.empty?
|
33
|
-
super(info)
|
34
|
-
else
|
35
|
-
super("#{info} @fields=#{fields.inspect}")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# @private
|
40
|
-
module ClassMethods
|
41
|
-
def fields_coercions
|
42
|
-
FIELDS_COERCIONS
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# @private
|
47
|
-
def self.included(base)
|
48
|
-
base.extend(ClassMethods)
|
49
|
-
|
50
|
-
base.fields_coercions.keys.each do |name|
|
51
|
-
base.send :define_method, Contentful::Support.snakify(name) do
|
52
|
-
fields[name.to_sym]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Contentful
|
2
|
-
module Resource
|
3
|
-
# Module for simplifying Custom Resource Creation
|
4
|
-
# Allows auto-mapping of fields to properties and properties to fields
|
5
|
-
module CustomResource
|
6
|
-
# @private
|
7
|
-
def initialize(*)
|
8
|
-
super
|
9
|
-
|
10
|
-
update_mappings!
|
11
|
-
end
|
12
|
-
|
13
|
-
# @private
|
14
|
-
def update_mappings!
|
15
|
-
properties.keys.each do |name|
|
16
|
-
define_singleton_method Contentful::Support.snakify(name).to_sym do |wanted_locale = default_locale|
|
17
|
-
properties[name] ||= fields(wanted_locale)[name]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# @private
|
23
|
-
def marshal_load(raw_object)
|
24
|
-
super raw_object
|
25
|
-
update_mappings!
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require 'contentful/constants'
|
2
|
-
|
3
|
-
module Contentful
|
4
|
-
module Resource
|
5
|
-
# Include this module into your Resource class to enable it
|
6
|
-
# to deal with entry fields (but not asset fields)
|
7
|
-
#
|
8
|
-
# It depends on system properties being available
|
9
|
-
module Fields
|
10
|
-
# Returns all fields of the asset
|
11
|
-
#
|
12
|
-
# @return [Hash] fields for Resource on selected locale
|
13
|
-
def fields(wanted_locale = nil)
|
14
|
-
wanted_locale = (locale || default_locale) if wanted_locale.nil?
|
15
|
-
@fields[wanted_locale.to_s] || {}
|
16
|
-
end
|
17
|
-
|
18
|
-
# Returns all fields of the asset with locales nested by field
|
19
|
-
#
|
20
|
-
# @return [Hash] fields for Resource grouped by field name
|
21
|
-
def fields_with_locales
|
22
|
-
remapped_fields = {}
|
23
|
-
locales.each do |locale|
|
24
|
-
fields(locale).each do |name, value|
|
25
|
-
remapped_fields[name] ||= {}
|
26
|
-
remapped_fields[name][locale.to_sym] = value
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
remapped_fields
|
31
|
-
end
|
32
|
-
|
33
|
-
# @private
|
34
|
-
module ClassMethods
|
35
|
-
# No coercions, since no content type available
|
36
|
-
def fields_coercions
|
37
|
-
{}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# @private
|
42
|
-
def self.included(base)
|
43
|
-
base.extend(ClassMethods)
|
44
|
-
end
|
45
|
-
|
46
|
-
# @private
|
47
|
-
def initialize(object = nil, *)
|
48
|
-
super
|
49
|
-
extract_fields_from_object! object if object
|
50
|
-
end
|
51
|
-
|
52
|
-
# @private
|
53
|
-
def inspect(info = nil)
|
54
|
-
if fields.empty?
|
55
|
-
super(info)
|
56
|
-
else
|
57
|
-
super("#{info} @fields=#{fields.inspect}")
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Provides a list of the available locales for a Resource
|
62
|
-
def locales
|
63
|
-
@fields.keys
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def extract_fields_from_object!(object)
|
69
|
-
initialize_fields_for_localized_resource(object)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|