contentful_model 0.2.0 → 1.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 (84) hide show
  1. checksums.yaml +5 -5
  2. data/lib/contentful_model.rb +17 -13
  3. data/lib/contentful_model/asset.rb +30 -0
  4. data/lib/contentful_model/asset_dimension_query.rb +70 -0
  5. data/lib/contentful_model/asset_dimensions.rb +54 -0
  6. data/lib/contentful_model/associations/associations.rb +10 -4
  7. data/lib/contentful_model/associations/belongs_to.rb +9 -4
  8. data/lib/contentful_model/associations/belongs_to_many.rb +22 -45
  9. data/lib/contentful_model/associations/has_many.rb +15 -13
  10. data/lib/contentful_model/associations/has_many_nested.rb +39 -37
  11. data/lib/contentful_model/associations/has_one.rb +22 -14
  12. data/lib/contentful_model/base.rb +115 -74
  13. data/lib/contentful_model/client.rb +14 -3
  14. data/lib/contentful_model/errors.rb +0 -1
  15. data/lib/contentful_model/manageable.rb +49 -21
  16. data/lib/contentful_model/management.rb +1 -0
  17. data/lib/contentful_model/migrations/content_type.rb +24 -24
  18. data/lib/contentful_model/migrations/content_type_factory.rb +9 -6
  19. data/lib/contentful_model/migrations/migration.rb +9 -4
  20. data/lib/contentful_model/queries.rb +60 -9
  21. data/lib/contentful_model/query.rb +148 -5
  22. data/lib/contentful_model/validations/lambda_validation.rb +17 -0
  23. data/lib/contentful_model/validations/validates_presence_of.rb +4 -1
  24. data/lib/contentful_model/validations/validations.rb +56 -8
  25. data/lib/contentful_model/version.rb +1 -1
  26. data/spec/asset_spec.rb +141 -0
  27. data/spec/associations/belongs_to_many_spec.rb +38 -0
  28. data/spec/associations/belongs_to_spec.rb +22 -0
  29. data/spec/associations/has_many_nested_spec.rb +321 -0
  30. data/spec/associations/has_many_spec.rb +33 -0
  31. data/spec/associations/has_one_spec.rb +32 -0
  32. data/spec/base_spec.rb +199 -0
  33. data/spec/chainable_queries_spec.rb +199 -0
  34. data/spec/client_spec.rb +11 -0
  35. data/spec/contentful_model_spec.rb +25 -0
  36. data/spec/fixtures/vcr_cassettes/asset/all.yml +161 -0
  37. data/spec/fixtures/vcr_cassettes/asset/find.yml +118 -0
  38. data/spec/fixtures/vcr_cassettes/association/belongs_to_many.yml +439 -0
  39. data/spec/fixtures/vcr_cassettes/association/has_many.yml +161 -0
  40. data/spec/fixtures/vcr_cassettes/association/has_one.yml +161 -0
  41. data/spec/fixtures/vcr_cassettes/association/nested_with_root_root.yml +240 -0
  42. data/spec/fixtures/vcr_cassettes/association/nested_without_root_child_higher_include.yml +123 -0
  43. data/spec/fixtures/vcr_cassettes/association/nested_without_root_child_parent.yml +609 -0
  44. data/spec/fixtures/vcr_cassettes/association/nested_without_root_childless.yml +688 -0
  45. data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle.yml +319 -0
  46. data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle_higher_include.yml +161 -0
  47. data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle_parent.yml +489 -0
  48. data/spec/fixtures/vcr_cassettes/association/nested_without_root_parentless.yml +331 -0
  49. data/spec/fixtures/vcr_cassettes/association/nested_without_root_parentless_higher_include.yml +82 -0
  50. data/spec/fixtures/vcr_cassettes/associations/nested_without_root_middle.yml +161 -0
  51. data/spec/fixtures/vcr_cassettes/associations/nested_without_root_middle_higher_include.yml +82 -0
  52. data/spec/fixtures/vcr_cassettes/base/content_type.yml +185 -0
  53. data/spec/fixtures/vcr_cassettes/base/return_nil_for_empty.yml +202 -0
  54. data/spec/fixtures/vcr_cassettes/base/return_nil_for_empty_with_value.yml +124 -0
  55. data/spec/fixtures/vcr_cassettes/client.yml +98 -0
  56. data/spec/fixtures/vcr_cassettes/dog.yml +161 -0
  57. data/spec/fixtures/vcr_cassettes/human.yml +199 -0
  58. data/spec/fixtures/vcr_cassettes/management/client.yml +1362 -0
  59. data/spec/fixtures/vcr_cassettes/management/nyancat.yml +1449 -0
  60. data/spec/fixtures/vcr_cassettes/management/nyancat_2.yml +1449 -0
  61. data/spec/fixtures/vcr_cassettes/management/nyancat_publish.yml +481 -0
  62. data/spec/fixtures/vcr_cassettes/management/nyancat_refetch_and_fail.yml +558 -0
  63. data/spec/fixtures/vcr_cassettes/management/nyancat_refetch_and_save.yml +620 -0
  64. data/spec/fixtures/vcr_cassettes/management/nyancat_save.yml +479 -0
  65. data/spec/fixtures/vcr_cassettes/management/nyancat_save_2.yml +479 -0
  66. data/spec/fixtures/vcr_cassettes/nyancat.yml +196 -0
  67. data/spec/fixtures/vcr_cassettes/playground/nyancat.yml +177 -0
  68. data/spec/fixtures/vcr_cassettes/query/each_entry.yml +319 -0
  69. data/spec/fixtures/vcr_cassettes/query/each_page.yml +319 -0
  70. data/spec/fixtures/vcr_cassettes/query/empty.yml +180 -0
  71. data/spec/fixtures/vcr_cassettes/query/load.yml +197 -0
  72. data/spec/fixtures/vcr_cassettes/query/manual_pagination.yml +161 -0
  73. data/spec/fixtures/vcr_cassettes/query/nyancat_invalid_elements.yml +247 -0
  74. data/spec/manageable_spec.rb +186 -0
  75. data/spec/management_spec.rb +17 -0
  76. data/spec/migrations/content_type_factory_spec.rb +41 -0
  77. data/spec/migrations/content_type_spec.rb +176 -0
  78. data/spec/migrations/migration_spec.rb +75 -0
  79. data/spec/queries_spec.rb +85 -0
  80. data/spec/query_spec.rb +126 -0
  81. data/spec/spec_helper.rb +58 -0
  82. data/spec/validations/validations_spec.rb +182 -0
  83. metadata +213 -19
  84. data/lib/contentful_model/chainable_queries.rb +0 -104
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ class InvalidSaveCat < ContentfulModel::Base
4
+ self.content_type_id = 'cat'
5
+
6
+ validate :always_fail, -> (e) { false }
7
+ end
8
+
9
+ describe ContentfulModel::Manageable do
10
+ before :each do
11
+ ContentfulModel.configure do |c|
12
+ c.default_locale = 'en-US'
13
+ c.management_token = '<ACCESS_TOKEN>'
14
+ c.access_token = '4d0f55d940975f78139daae5d965b463c0816e88ad16062d2c1ee3d6cb930521'
15
+ c.space = 'facgnwwgj5fe'
16
+ c.entry_mapping = {}
17
+ end
18
+
19
+ InvalidSaveCat.client = nil
20
+ Cat.client = nil
21
+ end
22
+
23
+ let(:space_id) { 'facgnwwgj5fe' }
24
+ let(:entry_id) { 'IJLRrADsqq2AmwcugoYeK' }
25
+ subject { vcr('playground/nyancat') { Cat.find(entry_id) } }
26
+
27
+ describe 'class methods' do
28
+ it '::management' do
29
+ expect(Cat.management).to be_a(ContentfulModel::Management)
30
+ end
31
+
32
+ describe '::create' do
33
+ before do
34
+ @mock_client = Object.new
35
+ @mock_ct = Object.new
36
+ @mock_entry = Object.new
37
+ allow(Cat).to receive(:management) { @mock_client }
38
+ allow(@mock_client).to receive(:entries) { @mock_client }
39
+ allow(@mock_client).to receive(:content_types) { @mock_client }
40
+ allow(@mock_client).to receive(:find).with('cat') { @mock_ct }
41
+ @values = {'name' => 'Nyan Cat'}
42
+ end
43
+
44
+ it 'creates an entry' do
45
+ expect(@mock_client).to receive(:create).with(@mock_ct, @values) { @mock_entry }
46
+
47
+ Cat.create(@values)
48
+ end
49
+
50
+ it 'publishes after creation' do
51
+ allow(@mock_client).to receive(:create).with(@mock_ct, @values) { @mock_entry }
52
+ allow(@mock_entry).to receive(:id) { entry_id }
53
+ allow(Cat).to receive(:find).with(entry_id) { @mock_entry }
54
+ expect(@mock_entry).to receive(:publish)
55
+
56
+ Cat.create(@values, true)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe 'instance methods' do
62
+ it '#to_management' do
63
+ vcr('management/client') {
64
+ expect(subject.to_management).to be_a(Contentful::Management::Entry)
65
+ expect(subject.to_management.id).to eq(entry_id)
66
+ }
67
+ end
68
+
69
+ it 'creates setters for fields' do
70
+ expect(subject.respond_to?(:name=)).to be_truthy
71
+ end
72
+
73
+ it 'allows to override values' do
74
+ expect(subject.name).to eq('Nyan Cat')
75
+ subject.name = 'Fat Cat'
76
+ expect(subject.name).to eq('Fat Cat')
77
+ end
78
+
79
+ context 'modifying the space' do
80
+ describe '#save' do
81
+ it 'fails on invalid instance' do
82
+ vcr('management/nyancat') {
83
+ subject = InvalidSaveCat.find(entry_id)
84
+
85
+ expect(subject).not_to receive(:to_management)
86
+
87
+ expect(subject.save).to be_falsey
88
+
89
+ expect(subject.errors).to match_array [
90
+ 'always_fail: validation not met'
91
+ ]
92
+ }
93
+ end
94
+
95
+ it 'calls #save on management object' do
96
+ vcr('management/nyancat') {
97
+ subject = Cat.find(entry_id)
98
+
99
+ version = subject.to_management.sys[:version]
100
+
101
+ expect(subject.name).to eq 'Nyan Cat'
102
+
103
+ subject.name = 'Fat Cat'
104
+ vcr('management/nyancat_save') {
105
+ subject.save
106
+
107
+ expect(subject.to_management.sys[:version]).to eq(version + 1)
108
+ }
109
+ }
110
+ end
111
+
112
+ it 'sets dirty to false' do
113
+ vcr('management/nyancat') {
114
+ subject = Cat.find(entry_id)
115
+
116
+ expect(subject.dirty).to be_falsey
117
+
118
+ subject.name = 'Fat Cat'
119
+
120
+ expect(subject.dirty).to be_truthy
121
+ vcr('management/nyancat_save') {
122
+ subject.save
123
+
124
+ expect(subject.dirty).to be_falsey
125
+ }
126
+ }
127
+ end
128
+
129
+ it 'refetches management entry if conflict occurs' do
130
+ vcr('management/nyancat') {
131
+ subject = Cat.find(entry_id)
132
+
133
+ vcr('management/nyancat_refetch_and_save') {
134
+ expect(subject).to receive(:refetch_management_entry).and_call_original
135
+
136
+ subject.name = 'Foo Cat'
137
+ subject.save
138
+ }
139
+ }
140
+ end
141
+
142
+ it 'raises error if conflict happens after refetch' do
143
+ vcr('management/nyancat') {
144
+ subject = Cat.find(entry_id)
145
+
146
+ vcr('management/nyancat_refetch_and_fail') {
147
+ expect(subject).to receive(:refetch_management_entry).and_call_original
148
+
149
+ subject.name = 'Foo Cat'
150
+
151
+ expect { subject.save }.to raise_error ContentfulModel::VersionMismatchError
152
+ }
153
+ }
154
+ end
155
+ end
156
+
157
+ describe '#publish' do
158
+ it 'calls #publish on management object' do
159
+ vcr('management/nyancat_2') {
160
+ subject = Cat.find(entry_id)
161
+
162
+ version = subject.to_management.sys[:version]
163
+
164
+ expect(subject.name).to eq 'Nyan Cat'
165
+
166
+ subject.name = 'Fat Cat'
167
+ vcr('management/nyancat_save_2') {
168
+ subject.save
169
+
170
+ management_subject = subject.to_management
171
+ published_at = management_subject.sys[:publishedAt]
172
+ expect(management_subject.sys[:version]).to eq(version + 1)
173
+ vcr('management/nyancat_publish') {
174
+ subject.publish
175
+
176
+ management_subject = subject.to_management
177
+ expect(management_subject.sys[:version]).to eq(version + 2)
178
+ expect(management_subject.sys[:publishedAt]).not_to eq(published_at)
179
+ }
180
+ }
181
+ }
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe ContentfulModel::Management do
4
+ before do
5
+ ContentfulModel.configure do |c|
6
+ c.management_token = 'foobar'
7
+ end
8
+ end
9
+
10
+ it 'is a Contentful::Management::Client' do
11
+ expect(subject).to be_a(Contentful::Management::Client)
12
+ end
13
+
14
+ it 'gets initialized with the configured management token' do
15
+ expect(subject.access_token).to eq('foobar')
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe ContentfulModel::Migrations::ContentTypeFactory do
4
+ describe '::create' do
5
+ before do
6
+ @mock_ct = Object.new
7
+ allow(ContentfulModel::Migrations::ContentType).to receive(:new) { @mock_ct }
8
+ expect(@mock_ct).to receive(:save)
9
+ end
10
+
11
+ it 'saves it' do
12
+ described_class.create('foo')
13
+ end
14
+
15
+ it 'calls :field for each field sent' do
16
+ expect(@mock_ct).to receive(:field).with(:bar, :symbol)
17
+ expect(@mock_ct).to receive(:field).with(:baz, :text)
18
+
19
+ described_class.create('foo', bar: :symbol, baz: :text)
20
+ end
21
+
22
+ it 'yields it' do
23
+ expect(@mock_ct).to receive(:field).with(:bar, :symbol)
24
+
25
+ described_class.create('foo') do |ct|
26
+ ct.field(:bar, :symbol)
27
+ end
28
+ end
29
+ end
30
+
31
+ it '::find' do
32
+ ContentfulModel.configure { |config| config.space = 'space_id' }
33
+ mock_client = Object.new
34
+
35
+ allow_any_instance_of(ContentfulModel::Management).to receive(:content_types).with('space_id', 'master') { mock_client }
36
+ expect(mock_client).to receive(:find).with('ct_id')
37
+
38
+ result = described_class.find('ct_id')
39
+ expect(result).to be_a(ContentfulModel::Migrations::ContentType)
40
+ end
41
+ end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+
3
+ describe ContentfulModel::Migrations::ContentType do
4
+ describe 'instance methods' do
5
+ describe '#new?' do
6
+ it 'newly created returns true' do
7
+ expect(described_class.new.new?).to be_truthy
8
+ end
9
+
10
+ it 'returns true if management content type present but no id' do
11
+ mock_ct = Object.new
12
+ allow(mock_ct).to receive(:id) { nil }
13
+
14
+ expect(described_class.new(nil, mock_ct).new?).to be_truthy
15
+ end
16
+
17
+ it 'returns false if management content type present with id' do
18
+ mock_ct = Object.new
19
+ allow(mock_ct).to receive(:id) { 'foo' }
20
+
21
+ expect(described_class.new(nil, mock_ct).new?).to be_falsey
22
+ end
23
+ end
24
+
25
+ describe '#save' do
26
+ before do
27
+ ContentfulModel.configure do |c|
28
+ c.space = 'space_id'
29
+ c.management_token = 'token'
30
+ end
31
+ end
32
+
33
+ it 'creates a new content type on contentful' do
34
+ mock_client = Object.new
35
+ expect(mock_client).to receive(:create).with(id: 'foo', name: 'foo', fields: [])
36
+ allow_any_instance_of(ContentfulModel::Management).to receive(:content_types) { mock_client }
37
+
38
+ described_class.new('foo').save
39
+ end
40
+
41
+ it 'updates an existing content type' do
42
+ mock_ct = Object.new
43
+ allow(mock_ct).to receive(:id) { 'foo' }
44
+
45
+ expect(mock_ct).to receive(:fields=)
46
+ expect(mock_ct).to receive(:save)
47
+
48
+ described_class.new(nil, mock_ct).save
49
+ end
50
+ end
51
+
52
+ it '#remove_field' do
53
+ mock_ct = Object.new
54
+ mock_fields = Object.new
55
+
56
+ expect(mock_ct).to receive(:fields) { mock_fields }
57
+ expect(mock_fields).to receive(:destroy).with('foo')
58
+
59
+ described_class.new(nil, mock_ct).remove_field('foo')
60
+ end
61
+
62
+ describe '#id' do
63
+ it 'can set the id' do
64
+ subject.id = 'foo_ct'
65
+ expect(subject.id).to eq 'foo_ct'
66
+ end
67
+
68
+ it 'when saving, defined id will be used' do
69
+ subject.id = 'foo_ct'
70
+
71
+ expect_any_instance_of(::Contentful::Management::ClientContentTypeMethodsFactory).to receive(:create).with(
72
+ id: 'foo_ct',
73
+ name: subject.name,
74
+ fields: subject.fields
75
+ )
76
+
77
+ subject.save
78
+ end
79
+ end
80
+
81
+ describe '#field' do
82
+ it 'snake cases field id' do
83
+ field = subject.field('Foo Test', :symbol)
84
+
85
+ expect(field.id).to eq('foo_test')
86
+ expect(field.name).to eq('Foo Test')
87
+ end
88
+
89
+ it 'regular field' do
90
+ field = subject.field('foo', :symbol)
91
+
92
+ expect(field.id).to eq('foo')
93
+ expect(field.name).to eq('foo')
94
+ expect(field.type).to eq('Symbol')
95
+ expect(field.link_type).to eq(nil)
96
+ expect(field.items).to eq(nil)
97
+ end
98
+
99
+ it 'converts :string to :symbol field' do
100
+ field = subject.field('foo', :string)
101
+
102
+ expect(field.type).to eq('Symbol')
103
+ end
104
+
105
+ it 'link field' do
106
+ field = subject.field('foo', :entry_link)
107
+
108
+ expect(field.id).to eq('foo')
109
+ expect(field.name).to eq('foo')
110
+ expect(field.type).to eq('Link')
111
+ expect(field.link_type).to eq('Entry')
112
+ expect(field.items).to eq(nil)
113
+ end
114
+
115
+ it 'array field' do
116
+ field = subject.field('foo', :asset_array)
117
+
118
+ expect(field.id).to eq('foo')
119
+ expect(field.name).to eq('foo')
120
+ expect(field.type).to eq('Array')
121
+ expect(field.link_type).to eq(nil)
122
+
123
+ items = field.items
124
+
125
+ expect(items).to be_a(Contentful::Management::Field)
126
+ expect(items.type).to eq('Link')
127
+ expect(items.link_type).to eq('Asset')
128
+ end
129
+
130
+ it 'fails on unknown type' do
131
+ expect { subject.field('foo', :bar) }.to raise_error ContentfulModel::Migrations::InvalidFieldTypeError
132
+ end
133
+ end
134
+
135
+ describe '#fields' do
136
+ describe 'from a new content type' do
137
+ it 'starts empty' do
138
+ expect(subject.fields).to eq([])
139
+ end
140
+
141
+ it 'adds fields when you call :field' do
142
+ expect(subject.fields.size).to eq(0)
143
+
144
+ subject.field('foo', :text)
145
+
146
+ expect(subject.fields.size).to eq(1)
147
+ expect(subject.fields[0]).to be_a(Contentful::Management::Field)
148
+ end
149
+ end
150
+
151
+ describe 'from an existing content type' do
152
+ before do
153
+ @mock_ct = Object.new
154
+ allow(@mock_ct).to receive(:id) { 'foo' }
155
+ allow(@mock_ct).to receive(:fields) { [Contentful::Management::Field.new] }
156
+ end
157
+
158
+ it 'returns previously existing fields' do
159
+ subject = described_class.new(nil, @mock_ct)
160
+
161
+ expect(subject.fields.size).to eq(1)
162
+ end
163
+
164
+ it 'appends fields to existing fields' do
165
+ subject = described_class.new(nil, @mock_ct)
166
+
167
+ expect(subject.fields.size).to eq(1)
168
+
169
+ subject.field('foo', :text)
170
+
171
+ expect(subject.fields.size).to eq(2)
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ class MockMigration
4
+ include ContentfulModel::Migrations::Migration
5
+ end
6
+
7
+ describe ContentfulModel::Migrations::Migration do
8
+ subject { MockMigration.new }
9
+
10
+ before do
11
+ @mock_content_type = Object.new
12
+ @mock_field = Object.new
13
+ allow(ContentfulModel::Migrations::ContentTypeFactory).to receive(:find).with('ct_id') { @mock_content_type }
14
+ allow(@mock_content_type).to receive(:save) { @mock_content_type }
15
+ allow(@mock_content_type).to receive(:publish)
16
+ allow(@mock_content_type).to receive(:field).with('name', :type) { @mock_field }
17
+ allow(@mock_content_type).to receive(:remove_field).with('name')
18
+ end
19
+
20
+ describe "#create_content_type" do
21
+ it 'creates content type' do
22
+ expect(ContentfulModel::Migrations::ContentTypeFactory).to receive(:create)
23
+
24
+ subject.create_content_type('foo')
25
+ end
26
+ end
27
+
28
+ describe '#add_content_type_field' do
29
+ it 'creates field' do
30
+ expect(@mock_content_type).to receive(:field)
31
+
32
+ subject.add_content_type_field('ct_id', 'name', :type)
33
+ end
34
+
35
+ it 'publishes content type' do
36
+ expect(@mock_content_type).to receive(:publish)
37
+
38
+ subject.add_content_type_field('ct_id', 'name', :type)
39
+ end
40
+
41
+ it 'saves content type' do
42
+ expect(@mock_content_type).to receive(:save)
43
+
44
+ subject.add_content_type_field('ct_id', 'name', :type)
45
+ end
46
+
47
+ it 'yields content type' do
48
+ expect(@mock_field).to receive(:validations=)
49
+
50
+ subject.add_content_type_field('ct_id', 'name', :type) do |field|
51
+ field.validations = []
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#remove_content_type_field' do
57
+ it 'removes field' do
58
+ expect(@mock_content_type).to receive(:remove_field).with('name')
59
+
60
+ subject.remove_content_type_field('ct_id', 'name')
61
+ end
62
+
63
+ it 'publishes content type' do
64
+ expect(@mock_content_type).to receive(:publish)
65
+
66
+ subject.remove_content_type_field('ct_id', 'name')
67
+ end
68
+
69
+ it 'saves content type' do
70
+ expect(@mock_content_type).to receive(:save)
71
+
72
+ subject.remove_content_type_field('ct_id', 'name')
73
+ end
74
+ end
75
+ end