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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/LICENSE.txt +1 -0
  4. data/README.md +8 -0
  5. data/contentful.gemspec +2 -1
  6. data/examples/custom_classes.rb +23 -26
  7. data/examples/raise_errors.rb +2 -2
  8. data/lib/contentful.rb +0 -1
  9. data/lib/contentful/array.rb +27 -19
  10. data/lib/contentful/array_like.rb +51 -0
  11. data/lib/contentful/asset.rb +43 -11
  12. data/lib/contentful/base_resource.rb +87 -0
  13. data/lib/contentful/client.rb +43 -34
  14. data/lib/contentful/coercions.rb +116 -0
  15. data/lib/contentful/content_type.rb +23 -8
  16. data/lib/contentful/content_type_cache.rb +26 -0
  17. data/lib/contentful/deleted_asset.rb +2 -5
  18. data/lib/contentful/deleted_entry.rb +2 -5
  19. data/lib/contentful/entry.rb +55 -33
  20. data/lib/contentful/error.rb +1 -1
  21. data/lib/contentful/field.rb +37 -9
  22. data/lib/contentful/fields_resource.rb +115 -0
  23. data/lib/contentful/file.rb +7 -8
  24. data/lib/contentful/link.rb +3 -6
  25. data/lib/contentful/locale.rb +6 -6
  26. data/lib/contentful/location.rb +7 -5
  27. data/lib/contentful/resource_builder.rb +72 -226
  28. data/lib/contentful/space.rb +16 -6
  29. data/lib/contentful/support.rb +41 -3
  30. data/lib/contentful/sync_page.rb +17 -10
  31. data/lib/contentful/version.rb +1 -1
  32. data/spec/array_spec.rb +4 -8
  33. data/spec/client_class_spec.rb +12 -23
  34. data/spec/client_configuration_spec.rb +13 -23
  35. data/spec/content_type_spec.rb +0 -5
  36. data/spec/entry_spec.rb +130 -125
  37. data/spec/error_requests_spec.rb +1 -1
  38. data/spec/field_spec.rb +0 -5
  39. data/spec/file_spec.rb +0 -5
  40. data/spec/fixtures/vcr_cassettes/entry.yml +54 -64
  41. data/spec/fixtures/vcr_cassettes/entry/include_resolution.yml +101 -0
  42. data/spec/fixtures/vcr_cassettes/entry/marshall.yml +227 -251
  43. data/spec/fixtures/vcr_cassettes/entry/raw.yml +88 -124
  44. data/spec/fixtures/vcr_cassettes/entry_locales.yml +56 -74
  45. data/spec/fixtures/vcr_cassettes/human.yml +63 -40
  46. data/spec/fixtures/vcr_cassettes/location.yml +99 -211
  47. data/spec/fixtures/vcr_cassettes/multi_locale_array_reference.yml +12 -16
  48. data/spec/fixtures/vcr_cassettes/not_found.yml +26 -21
  49. data/spec/fixtures/vcr_cassettes/nyancat.yml +53 -63
  50. data/spec/fixtures/vcr_cassettes/ratelimit.yml +1 -1
  51. data/spec/fixtures/vcr_cassettes/reloaded_entry.yml +54 -64
  52. data/spec/fixtures/vcr_cassettes/unauthorized.yml +1 -1
  53. data/spec/fixtures/vcr_cassettes/unavailable.yml +27 -15
  54. data/spec/link_spec.rb +3 -2
  55. data/spec/locale_spec.rb +0 -5
  56. data/spec/location_spec.rb +1 -6
  57. data/spec/request_spec.rb +3 -2
  58. data/spec/resource_building_spec.rb +10 -7
  59. data/spec/response_spec.rb +1 -1
  60. data/spec/space_spec.rb +0 -5
  61. data/spec/spec_helper.rb +3 -0
  62. data/spec/support/json_responses.rb +3 -3
  63. data/spec/sync_page_spec.rb +1 -6
  64. data/spec/sync_spec.rb +11 -7
  65. metadata +69 -20
  66. data/examples/dynamic_entries.rb +0 -124
  67. data/examples/resource_mapping.rb +0 -32
  68. data/lib/contentful/constants.rb +0 -504
  69. data/lib/contentful/dynamic_entry.rb +0 -57
  70. data/lib/contentful/resource.rb +0 -239
  71. data/lib/contentful/resource/array_like.rb +0 -39
  72. data/lib/contentful/resource/asset_fields.rb +0 -58
  73. data/lib/contentful/resource/custom_resource.rb +0 -29
  74. data/lib/contentful/resource/fields.rb +0 -73
  75. data/lib/contentful/resource/system_properties.rb +0 -55
  76. data/spec/coercions_spec.rb +0 -23
  77. data/spec/dynamic_entry_spec.rb +0 -75
  78. data/spec/resource_spec.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0282a011ae90a4b76aad56ea87739191d7cfad3
4
- data.tar.gz: 1a07148812c1bc8bb976f03e1816a5070790d880
3
+ metadata.gz: 3336395c12c33716d03ef5ccd4ca5662d0c9f0e4
4
+ data.tar.gz: ff41b0e879b3ab72bc0962abeac4dae2fb49496a
5
5
  SHA512:
6
- metadata.gz: 98d2ad17326e6bdd452b49bf20494a54b19667b14e31962abdbc07864de7bf0e77ce5cf31ddb91cbf6bf0f396382ec30d2fdb3c6bd4ff8bd5123b1a0fd0abe83
7
- data.tar.gz: 8f906415d0e28f43e6a996eb5caf68f2885ff605ad56a4fa9688ab612378a006ddac539dd190a6d956cd1572f6c4bbbe1d6a337cc456b45c6ff35bdaf177b4bc
6
+ metadata.gz: 39692cf6bc73f11ea37ede62a11fc70f9ca5ccc7346cfe415d88dc53c320a30443669b7585df78ef13b657697a7f91fe126f01e26cdbab280ab03ebd5bcbdc5b
7
+ data.tar.gz: a6d462ef93caf18c9179b36403911f255685bcacd3ff7a06c9f91eca40c5c8e1346474068f20766baa726513d9a63a0c5fee101f0037b6444230062bda5bf65a
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.0.0
6
+
7
+ **ATTENTION**: Breaking Changes introduces in order to simplify code and improve the ability to add new features.
8
+
9
+ ## Changed
10
+
11
+ * The removal of the Client and Request objects from the Resource objects, means that for example: Link#resolve and Array#next_page now require the client as a parameter.
12
+ * Client#entry now uses /entries?sys.id=ENTRY_ID instead of /entries/ENTRY_ID to properly resolve includes.
13
+ * Refactor locale handling code
14
+ * Refactor ResourceBuilder
15
+ * Update all specs to RSpec 3
16
+ * Removed DynamicEntry and Resource
17
+ * Moved ContentTypeCache outside of the client into it's own class
18
+ * Added new base BaseResource and FieldsResource classes to handle common resource attributes and fields related attributes respectively
19
+ * Coercions are now part of ContentType, each Field knows which coercion should be applied depending on Field#type
20
+ * Resource #inspect now provides a clearer and better output, without all the noise that was previously there
21
+ * CustomResource was removed, now subclasses of Entry should be used instead.
22
+ * `max_include_resolution_depth` option added to the client, defaults to 20.
23
+ * Updated LICENSE
24
+ * Updated examples
25
+
5
26
  ## 1.2.2
6
27
  ### Fixed
7
28
  * Fixed Symbol/Text field serialization when value is `null` [#117](https://github.com/contentful/contentful.rb/issues/117)
@@ -1,6 +1,7 @@
1
1
  The MIT License (MIT)
2
2
 
3
3
  Copyright (c) 2014 Contentful GmbH - Jan Lelis
4
+ Copyright (c) 2016 Contentful GmbH - David Litvak
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -5,6 +5,8 @@ Ruby client for the [Contentful](https://www.contentful.com) Content Delivery AP
5
5
 
6
6
  [Contentful](https://www.contentful.com) is a content management platform for web applications, mobile apps and connected devices. It allows you to create, edit & manage content in the cloud and publish it anywhere via powerful API. Contentful offers tools for managing editorial teams and enabling cooperation between organizations.
7
7
 
8
+ **IMPORTANT**: We're collecting feedback before releasing version 2.0.0 of the SDK, if you're interested in helping, please drop by this issue and help us improving: https://github.com/contentful/contentful.rb/issues/120
9
+
8
10
  ## Setup
9
11
 
10
12
  Add to your Gemfile and bundle:
@@ -248,6 +250,12 @@ Maximum time to wait for next available request (in seconds). Default value is 6
248
250
  can have up to 60 minutes of blocked requests. It is set to a default of 60 seconds in order to avoid blocking processes for too long, as rate limit retry behaviour
249
251
  is blocking per execution thread.
250
252
 
253
+ ### :max_include_resolution_depth
254
+
255
+ Maximum amount of levels to resolve includes for SDK entities (this is independent of API-level includes - it represents the maximum depth the include resolution
256
+ tree is allowed to resolved before falling back to `Link` objects). This include resolution strategy is in place in order to avoid having infinite circular recursion
257
+ on resources with circular dependencies. Defaults to 20. _Note_: If you're using something like `Rails::cache` it's advisable to considerably lower this value
258
+ (around 5 has proven to be a good compromise - but keep it higher or equal than your maximum API-level include parameter if you need the entire tree resolution).
251
259
 
252
260
  ### Proxy example
253
261
 
@@ -35,9 +35,10 @@ Gem::Specification.new do |gem|
35
35
  gem.add_development_dependency 'guard-rubocop'
36
36
  gem.add_development_dependency 'guard-yard'
37
37
  gem.add_development_dependency 'rubocop', '~> 0.41.0'
38
- gem.add_development_dependency 'rspec', '~> 2'
38
+ gem.add_development_dependency 'rspec', '~> 3'
39
39
  gem.add_development_dependency 'rr'
40
40
  gem.add_development_dependency 'vcr'
41
+ gem.add_development_dependency 'simplecov'
41
42
  gem.add_development_dependency 'webmock', '~> 1', '>= 1.17.3'
42
43
  gem.add_development_dependency 'tins', '~> 1.6.0'
43
44
  end
@@ -1,42 +1,39 @@
1
- # Contentful resource classes are just plain Ruby classes that include the
2
- # Contentful::Resource module.
3
- #
4
- # You can then define properties of the class. This will create a getter method
5
- # with this name. You can optionally pass a type identifier (Symbol or Class).
6
- #
7
- # Classes will be instantiated for the properties,
8
- # Symbols will be looked up in Contentful::Resource::COERCIONS
9
-
10
1
  require 'contentful'
11
2
 
12
- class MyResource
13
- include Contentful::Resource
14
-
15
- property :some
16
- property :age, :integer
17
- property :country, Contentful::Locale
3
+ # You can define your own custom classes that inherit from Contentful::Entry.
4
+ # This allows you to define custom behaviour, for example, in this case, we want
5
+ # the :country field to act as a Contentful::Locale
6
+ class MyResource < Contentful::Entry
7
+ def country(locale = nil)
8
+ @country ||= Contentful::Locale.new(fields(locale)[:country])
9
+ end
18
10
  end
19
11
 
20
- res = MyResource.new(
12
+ res = MyResource.new('fields' => {
21
13
  'some' => 'value',
22
14
  'age' => '25',
23
15
  'country' => { 'code' => 'de', 'name' => 'Deutschland' },
24
16
  'unknown_property' => 'ignored'
25
- )
17
+ })
26
18
 
27
19
  p res.some # => "value"
28
20
  p res.age # => 25
29
21
  p res.country # #<Contentful::Locale: ...
30
22
  p res.unknown_property # NoMethodError
31
23
 
32
- # Another possibility to create customized resources is to just inherit from an
33
- # existing one:
24
+ # To then have it mapped automatically from the client,
25
+ # upon client instantiation, you set the :entry_mapping for your ContentType.
34
26
 
35
- class MyBetterArray < Contentful::Array
36
- # e.g. define more methods that you need
37
- def last
38
- items.last
39
- end
40
- end
27
+ client = Contentful::Client.new(
28
+ space: 'your_space_id',
29
+ access_token: 'your_access_token',
30
+ entry_mapping: {
31
+ 'myResource' => MyResource
32
+ }
33
+ )
41
34
 
42
- # Read further in examples/resource_mapping.rb
35
+ # We request the entries, entries of the 'myResource` content type,
36
+ # will return MyResource class objects, while others will remain Contentful::Entry.
37
+ client.entries.each { |e| puts e }
38
+ # => <Contentful::Entry[other_content_type] id='foobar'>
39
+ # => <MyResource[myResource] id='baz'>
@@ -6,7 +6,7 @@ client = Contentful::Client.new(
6
6
  )
7
7
 
8
8
  begin
9
- p client.entry 'not found'
9
+ p client.asset 'not found'
10
10
  rescue => error
11
11
  p error
12
12
  end
@@ -17,4 +17,4 @@ client2 = Contentful::Client.new(
17
17
  raise_errors: false,
18
18
  )
19
19
 
20
- p client2.entry 'not found'
20
+ p client2.asset 'not found'
@@ -1,4 +1,3 @@
1
1
  require 'contentful/version'
2
2
  require 'contentful/support'
3
3
  require 'contentful/client'
4
- require 'contentful/resource/custom_resource'
@@ -1,35 +1,43 @@
1
- require_relative 'resource'
2
- require_relative 'resource/array_like'
1
+ require_relative 'base_resource'
2
+ require_relative 'array_like'
3
3
 
4
4
  module Contentful
5
5
  # Resource Class for Arrays (e.g. search results)
6
6
  # @see _ https://www.contentful.com/developers/documentation/content-delivery-api/#arrays
7
7
  # @note It also provides an #each method and includes Ruby's Enumerable module (gives you methods like #min, #first, etc)
8
- class Array
8
+ class Array < BaseResource
9
9
  # @private
10
10
  DEFAULT_LIMIT = 100
11
11
 
12
- include Contentful::Resource
13
- include Contentful::Resource::SystemProperties
14
- include Contentful::Resource::ArrayLike
12
+ include Contentful::ArrayLike
15
13
 
16
- property :total, :integer
17
- property :limit, :integer
18
- property :skip, :integer
19
- property :items
14
+ attr_reader :total, :limit, :skip, :items, :endpoint
15
+
16
+ def initialize(item = nil,
17
+ default_locale = Contentful::Client::DEFAULT_CONFIGURATION[:default_locale],
18
+ endpoint = '', *)
19
+ super(item, { default_locale: default_locale })
20
+
21
+ @endpoint = endpoint
22
+ @total = item.fetch('total', nil)
23
+ @limit = item.fetch('limit', nil)
24
+ @skip = item.fetch('skip', nil)
25
+ @items = item.fetch('items', [])
26
+ end
27
+
28
+ # @private
29
+ def inspect
30
+ "<#{repr_name} total=#{total} skip=#{skip} limit=#{limit}>"
31
+ end
20
32
 
21
33
  # Simplifies pagination
22
34
  #
23
35
  # @return [Contentful::Array, false]
24
- def next_page
25
- if request
26
- new_skip = (skip || 0) + (limit || DEFAULT_LIMIT)
27
- new_request = request.copy
28
- new_request.query[:skip] = new_skip
29
- new_request.get
30
- else
31
- false
32
- end
36
+ def next_page(client = nil)
37
+ return false if client.nil?
38
+
39
+ new_skip = (skip || 0) + (limit || DEFAULT_LIMIT)
40
+ client.send(endpoint.delete('/'), limit: limit, skip: new_skip)
33
41
  end
34
42
  end
35
43
  end
@@ -0,0 +1,51 @@
1
+ module Contentful
2
+ # Useful methods for array-like resources that can be included if an
3
+ # :items property exists
4
+ module ArrayLike
5
+ include Enumerable
6
+
7
+ # Returns true for array-like resources
8
+ #
9
+ # @return [true]
10
+ def array?
11
+ true
12
+ end
13
+
14
+ # Delegates to items#each
15
+ #
16
+ # @yield [Contentful::Entry, Contentful::Asset]
17
+ def each_item(&block)
18
+ items.each(&block)
19
+ end
20
+ alias each each_item
21
+
22
+ # Delegates to items#empty?
23
+ #
24
+ # @return [Boolean]
25
+ def empty?
26
+ items.empty?
27
+ end
28
+
29
+ # Delegetes to items#size
30
+ #
31
+ # @return [Number]
32
+ def size
33
+ items.size
34
+ end
35
+ alias length size
36
+
37
+ # Delegates to items#[]
38
+ #
39
+ # @return [Contentful::Entry, Contentful::Asset]
40
+ def [](index)
41
+ items[index]
42
+ end
43
+
44
+ # Delegates to items#last
45
+ #
46
+ # @return [Contentful::Entry, Contentful::Asset]
47
+ def last
48
+ items.last
49
+ end
50
+ end
51
+ end
@@ -1,14 +1,10 @@
1
- require_relative 'resource'
2
- require_relative 'resource/asset_fields'
1
+ require_relative 'fields_resource'
2
+ require_relative 'file'
3
3
 
4
4
  module Contentful
5
5
  # Resource class for Asset.
6
6
  # https://www.contentful.com/developers/documentation/content-delivery-api/#assets
7
- class Asset
8
- include Contentful::Resource
9
- include Contentful::Resource::SystemProperties
10
- include Contentful::Resource::AssetFields
11
-
7
+ class Asset < FieldsResource
12
8
  # @private
13
9
  def marshal_dump
14
10
  raw
@@ -16,10 +12,20 @@ module Contentful
16
12
 
17
13
  # @private
18
14
  def marshal_load(raw_object)
19
- @properties = extract_from_object(raw_object, :property, self.class.property_coercions.keys)
20
- @sys = raw_object.key?('sys') ? extract_from_object(raw_object['sys'], :sys) : {}
21
- initialize_fields_for_localized_resource(raw_object)
22
- @raw = raw_object
15
+ super(raw_object)
16
+ create_files!
17
+ define_asset_methods!
18
+ end
19
+
20
+ # @private
21
+ def inspect
22
+ "<#{repr_name} id='#{sys[:id]}' url='#{url}'>"
23
+ end
24
+
25
+ def initialize(*)
26
+ super
27
+ create_files!
28
+ define_asset_methods!
23
29
  end
24
30
 
25
31
  # Generates a URL for the Contentful Image API
@@ -54,5 +60,31 @@ module Contentful
54
60
  end
55
61
 
56
62
  alias url image_url
63
+
64
+ private
65
+
66
+ def create_files!
67
+ file_json = fields[:file]
68
+ return if file_json.nil?
69
+
70
+ is_localized = file_json.keys.none? { |f| %w(fileName contentType details url).include? f }
71
+ if is_localized
72
+ locales.each do |locale|
73
+ fields(locale)[:file] = ::Contentful::File.new(file_json[locale.to_s] || {})
74
+ end
75
+ else
76
+ fields[:file] = ::Contentful::File.new(file_json)
77
+ end
78
+ end
79
+
80
+ def define_asset_methods!
81
+ define_singleton_method :description do
82
+ fields.fetch(:description, nil)
83
+ end
84
+
85
+ define_singleton_method :file do |wanted_locale = nil|
86
+ fields(wanted_locale)[:file]
87
+ end
88
+ end
57
89
  end
58
90
  end
@@ -0,0 +1,87 @@
1
+ require_relative 'support'
2
+
3
+ module Contentful
4
+ # Base definition of a Contentful Resource containing Sys properties
5
+ class BaseResource
6
+ attr_reader :raw, :default_locale, :sys
7
+
8
+ def initialize(item, configuration = {}, _localized = false, _includes = [], depth = 0)
9
+ @raw = item
10
+ @default_locale = configuration[:default_locale]
11
+ @depth = depth
12
+ @sys = hydrate_sys
13
+ @configuration = configuration
14
+
15
+ define_sys_methods!
16
+ end
17
+
18
+ # @private
19
+ def inspect
20
+ "<#{repr_name} id='#{sys[:id]}'>"
21
+ end
22
+
23
+ # Definition of equality
24
+ def ==(other)
25
+ self.class == other.class && sys[:id] == other.sys[:id]
26
+ end
27
+
28
+ # @private
29
+ def marshal_dump
30
+ raw
31
+ end
32
+
33
+ # @private
34
+ def marshal_load(raw_object)
35
+ @raw = raw_object
36
+ @sys = hydrate_sys
37
+ @depth = 0
38
+ define_sys_methods!
39
+ end
40
+
41
+ # Issues the request that was made to fetch this response again.
42
+ # Only works for Entry, Asset, ContentType and Space
43
+ def reload(client = nil)
44
+ return client.send(Support.snakify(self.class.name.split('::').last), id) unless client.nil?
45
+
46
+ false
47
+ end
48
+
49
+ private
50
+
51
+ def define_sys_methods!
52
+ @sys.each do |k, v|
53
+ define_singleton_method k do
54
+ v
55
+ end
56
+ end
57
+ end
58
+
59
+ def hydrate_sys
60
+ result = {}
61
+ raw.fetch('sys', {}).each do |k, v|
62
+ if %w(space contentType).include?(k)
63
+ v = build_link(v)
64
+ elsif %w(createdAt updatedAt deletedAt).include?(k)
65
+ v = DateTime.parse(v)
66
+ end
67
+ result[Support.snakify(k).to_sym] = v
68
+ end
69
+ result
70
+ end
71
+
72
+ protected
73
+
74
+ def repr_name
75
+ self.class
76
+ end
77
+
78
+ def internal_resource_locale
79
+ sys.fetch(:locale, nil) || default_locale
80
+ end
81
+
82
+ def build_link(item)
83
+ require_relative 'link'
84
+ ::Contentful::Link.new(item)
85
+ end
86
+ end
87
+ end