contentful 1.2.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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