contentful 1.2.2 → 2.0.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.
- 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
|