contentful 0.3.4 → 0.3.5
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 +7 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +43 -24
- data/README.md +22 -0
- data/RELEASE.md +8 -0
- data/Rakefile +4 -6
- data/contentful.gemspec +7 -9
- data/examples/custom_classes.rb +1 -2
- data/examples/dynamic_entries.rb +2 -4
- data/examples/example_queries.rb +2 -3
- data/examples/raise_errors.rb +3 -5
- data/examples/raw_mode.rb +2 -3
- data/examples/resource_mapping.rb +3 -4
- data/lib/contentful/array.rb +0 -1
- data/lib/contentful/asset.rb +1 -1
- data/lib/contentful/client.rb +47 -38
- data/lib/contentful/deleted_entry.rb +1 -1
- data/lib/contentful/request.rb +1 -2
- data/lib/contentful/resource.rb +1 -1
- data/lib/contentful/resource/array_like.rb +2 -3
- data/lib/contentful/resource/asset_fields.rb +1 -1
- data/lib/contentful/resource/fields.rb +2 -3
- data/lib/contentful/resource/system_properties.rb +1 -1
- data/lib/contentful/resource_builder.rb +14 -23
- data/lib/contentful/response.rb +40 -35
- data/lib/contentful/sync.rb +1 -3
- data/lib/contentful/sync_page.rb +2 -3
- data/lib/contentful/version.rb +1 -1
- data/spec/array_spec.rb +20 -20
- data/spec/asset_spec.rb +14 -14
- data/spec/auto_includes_spec.rb +4 -4
- data/spec/client_class_spec.rb +22 -24
- data/spec/client_configuration_spec.rb +48 -49
- data/spec/coercions_spec.rb +4 -4
- data/spec/content_type_spec.rb +12 -12
- data/spec/deleted_asset_spec.rb +10 -11
- data/spec/deleted_entry_spec.rb +9 -10
- data/spec/dynamic_entry_spec.rb +11 -11
- data/spec/entry_spec.rb +14 -14
- data/spec/error_class_spec.rb +15 -18
- data/spec/error_requests_spec.rb +6 -7
- data/spec/field_spec.rb +9 -9
- data/spec/file_spec.rb +7 -7
- data/spec/link_spec.rb +14 -14
- data/spec/locale_spec.rb +6 -6
- data/spec/location_spec.rb +10 -10
- data/spec/request_spec.rb +13 -13
- data/spec/resource_building_spec.rb +7 -8
- data/spec/resource_spec.rb +14 -14
- data/spec/response_spec.rb +25 -24
- data/spec/space_spec.rb +10 -10
- data/spec/spec_helper.rb +1 -2
- data/spec/support/json_responses.rb +2 -2
- data/spec/support/vcr.rb +3 -3
- data/spec/sync_page_spec.rb +26 -27
- data/spec/sync_spec.rb +22 -24
- metadata +41 -66
data/spec/auto_includes_spec.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Auto-include resources' do
|
4
|
-
let(:entries){ vcr('entries'){ create_client.entries } } # entries come with asset includes
|
4
|
+
let(:entries) { vcr('entries') { create_client.entries } } # entries come with asset includes
|
5
5
|
|
6
6
|
it 'replaces Contentful::Links which are actually included with the resource' do
|
7
7
|
asset = entries.items[1].fields[:image]
|
8
8
|
|
9
|
-
expect(
|
10
|
-
expect(
|
9
|
+
expect(asset).not_to be_a Contentful::Link
|
10
|
+
expect(asset).to be_a Contentful::Asset
|
11
11
|
end
|
12
|
-
end
|
12
|
+
end
|
data/spec/client_class_spec.rb
CHANGED
@@ -1,57 +1,55 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
3
|
describe Contentful::Client do
|
5
4
|
describe '#get' do
|
6
|
-
let(:client){ create_client }
|
7
|
-
let(:request)
|
5
|
+
let(:client) { create_client }
|
6
|
+
let(:request)do
|
8
7
|
Contentful::Request.new(nil, '/content_types', nil, 'cat')
|
9
|
-
|
10
|
-
|
8
|
+
end
|
11
9
|
|
12
10
|
it 'uses #base_url' do
|
13
|
-
stub(client).base_url{
|
14
|
-
vcr('content_type'){ client.get(request) }
|
15
|
-
expect(
|
11
|
+
stub(client).base_url { 'https://cdn.contentful.com/spaces/cfexampleapi' }
|
12
|
+
vcr('content_type') { client.get(request) }
|
13
|
+
expect(client).to have_received.base_url
|
16
14
|
end
|
17
15
|
|
18
16
|
it 'uses #request_headers' do
|
19
|
-
stub(client).request_headers{
|
17
|
+
stub(client).request_headers do{
|
20
18
|
'User-Agent' => 'RubyContentfulGem/0.1.0',
|
21
19
|
'Authorization' => 'Bearer b4c0n73n7fu1',
|
22
20
|
'Content-Type' => 'application/vnd.contentful.delivery.v1+json',
|
23
|
-
}
|
24
|
-
vcr('content_type'){ client.get(request) }
|
25
|
-
expect(
|
21
|
+
}end
|
22
|
+
vcr('content_type') { client.get(request) }
|
23
|
+
expect(client).to have_received.request_headers
|
26
24
|
end
|
27
25
|
|
28
26
|
it 'uses Request#url' do
|
29
|
-
stub(request).url{
|
30
|
-
vcr('content_type'){ client.get(request) }
|
31
|
-
expect(
|
27
|
+
stub(request).url { '/content_types/cat' }
|
28
|
+
vcr('content_type') { client.get(request) }
|
29
|
+
expect(request).to have_received.url
|
32
30
|
end
|
33
31
|
|
34
32
|
it 'uses Request#query' do
|
35
33
|
stub(request).query
|
36
|
-
vcr('content_type'){ client.get(request) }
|
37
|
-
expect(
|
34
|
+
vcr('content_type') { client.get(request) }
|
35
|
+
expect(request).to have_received.query
|
38
36
|
end
|
39
37
|
|
40
38
|
it 'calls #get_http' do
|
41
|
-
stub(client.class).get_http{ raw_fixture('content_type') }
|
39
|
+
stub(client.class).get_http { raw_fixture('content_type') }
|
42
40
|
client.get(request)
|
43
|
-
expect(
|
41
|
+
expect(client.class).to have_received.get_http(client.base_url + request.url, request.query, client.request_headers)
|
44
42
|
end
|
45
43
|
|
46
44
|
describe 'build_resources parameter' do
|
47
45
|
it 'returns Contentful::Resource object if second parameter is true [default]' do
|
48
|
-
res = vcr('content_type'){ client.get(request) }
|
49
|
-
expect(
|
46
|
+
res = vcr('content_type') { client.get(request) }
|
47
|
+
expect(res).to be_a Contentful::Resource
|
50
48
|
end
|
51
49
|
|
52
50
|
it 'returns a Contentful::Response object if second parameter is not true' do
|
53
|
-
res = vcr('content_type'){ client.get(request, false) }
|
54
|
-
expect(
|
51
|
+
res = vcr('content_type') { client.get(request, false) }
|
52
|
+
expect(res).to be_a Contentful::Response
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
@@ -59,7 +57,7 @@ describe Contentful::Client do
|
|
59
57
|
|
60
58
|
describe '#sync' do
|
61
59
|
it 'creates a new Sync object' do
|
62
|
-
expect(
|
60
|
+
expect(create_client.sync).to be_a Contentful::Sync
|
63
61
|
end
|
64
62
|
end
|
65
63
|
end
|
@@ -1,20 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
3
|
describe 'Client Configuration Options' do
|
5
4
|
describe ':space' do
|
6
5
|
it 'is required' do
|
7
|
-
expect
|
6
|
+
expect do
|
8
7
|
Contentful::Client.new(access_token: 'b4c0n73n7fu1')
|
9
|
-
|
8
|
+
end.to raise_error(ArgumentError)
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
13
12
|
describe ':access_token' do
|
14
13
|
it 'is required' do
|
15
|
-
expect
|
14
|
+
expect do
|
16
15
|
Contentful::Client.new(space: 'cfexampleapi')
|
17
|
-
|
16
|
+
end.to raise_error(ArgumentError)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
@@ -34,42 +33,42 @@ describe 'Client Configuration Options' do
|
|
34
33
|
|
35
34
|
describe ':raise_errors' do
|
36
35
|
it 'will raise response errors if set to true [default]' do
|
37
|
-
expect_vcr('not found')
|
38
|
-
create_client.entry
|
39
|
-
|
36
|
+
expect_vcr('not found')do
|
37
|
+
create_client.entry 'not found'
|
38
|
+
end.to raise_error Contentful::NotFound
|
40
39
|
end
|
41
40
|
|
42
41
|
it 'will not raise response errors if set to false' do
|
43
42
|
res = nil
|
44
43
|
|
45
|
-
expect_vcr('not found')
|
46
|
-
res = create_client(raise_errors: false).entry
|
47
|
-
|
48
|
-
expect(
|
44
|
+
expect_vcr('not found')do
|
45
|
+
res = create_client(raise_errors: false).entry 'not found'
|
46
|
+
end.not_to raise_error
|
47
|
+
expect(res).to be_instance_of Contentful::NotFound
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
51
|
describe ':dynamic_entries' do
|
53
52
|
it 'will create static if dynamic_entry_cache is empty' do
|
54
|
-
entry = vcr('nyancat'){ create_client(dynamic_entries: :manual).entry('nyancat') }
|
55
|
-
expect(
|
53
|
+
entry = vcr('nyancat') { create_client(dynamic_entries: :manual).entry('nyancat') }
|
54
|
+
expect(entry).not_to be_a Contentful::DynamicEntry
|
56
55
|
end
|
57
56
|
|
58
57
|
it 'will create dynamic entries if dynamic_entry_cache is not empty' do
|
59
58
|
client = create_client(dynamic_entries: :manual)
|
60
|
-
vcr('entry_cache'){ client.update_dynamic_entry_cache! }
|
61
|
-
entry = vcr('nyancat'){ client.entry('nyancat') }
|
59
|
+
vcr('entry_cache') { client.update_dynamic_entry_cache! }
|
60
|
+
entry = vcr('nyancat') { client.entry('nyancat') }
|
62
61
|
|
63
|
-
expect(
|
62
|
+
expect(entry).to be_a Contentful::DynamicEntry
|
64
63
|
end
|
65
64
|
|
66
65
|
context ':auto' do
|
67
66
|
it 'will call update dynamic_entry_cache on start-up' do
|
68
|
-
client = vcr('entry_cache')
|
69
|
-
|
70
|
-
|
71
|
-
expect(
|
72
|
-
expect(
|
67
|
+
client = vcr('entry_cache')do
|
68
|
+
create_client(dynamic_entries: :auto)
|
69
|
+
end
|
70
|
+
expect(client.dynamic_entry_cache).not_to be_empty
|
71
|
+
expect(client.dynamic_entry_cache.values.first.ancestors).to \
|
73
72
|
include Contentful::DynamicEntry
|
74
73
|
end
|
75
74
|
end
|
@@ -77,40 +76,40 @@ describe 'Client Configuration Options' do
|
|
77
76
|
context ':manual' do
|
78
77
|
it 'will not call #update_dynamic_entry_cache! on start-up' do
|
79
78
|
client = create_client(dynamic_entries: :manual)
|
80
|
-
expect(
|
79
|
+
expect(client.dynamic_entry_cache).to be_empty
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
83
|
describe '#update_dynamic_entry_cache!' do
|
85
|
-
let(:client){ create_client(dynamic_entries: :manual) }
|
84
|
+
let(:client) { create_client(dynamic_entries: :manual) }
|
86
85
|
|
87
86
|
it 'will fetch all content_types' do
|
88
|
-
stub(client).content_types{{}}
|
87
|
+
stub(client).content_types { {} }
|
89
88
|
client.update_dynamic_entry_cache!
|
90
|
-
expect(
|
89
|
+
expect(client).to have_received.content_types(limit: 1000)
|
91
90
|
end
|
92
91
|
|
93
92
|
it 'will save dynamic entries in @dynamic_entry_cache' do
|
94
|
-
vcr('entry_cache')
|
93
|
+
vcr('entry_cache')do
|
95
94
|
client.update_dynamic_entry_cache!
|
96
|
-
|
97
|
-
expect(
|
98
|
-
expect(
|
95
|
+
end
|
96
|
+
expect(client.dynamic_entry_cache).not_to be_empty
|
97
|
+
expect(client.dynamic_entry_cache.values.first.ancestors).to \
|
99
98
|
include Contentful::DynamicEntry
|
100
99
|
end
|
101
100
|
end
|
102
101
|
|
103
102
|
describe '#register_dynamic_entry' do
|
104
|
-
let(:client){ create_client(dynamic_entries: :manual) }
|
103
|
+
let(:client) { create_client(dynamic_entries: :manual) }
|
105
104
|
|
106
105
|
it 'can be used to register a dynamic entry manually' do
|
107
|
-
cat = vcr('content_type'){ client.content_type 'cat' }
|
106
|
+
cat = vcr('content_type') { client.content_type 'cat' }
|
108
107
|
CatEntry = Contentful::DynamicEntry.create(cat)
|
109
108
|
client.register_dynamic_entry 'cat', CatEntry
|
110
109
|
|
111
|
-
entry = vcr('nyancat'){ client.entry('nyancat') }
|
112
|
-
expect(
|
113
|
-
expect(
|
110
|
+
entry = vcr('nyancat') { client.entry('nyancat') }
|
111
|
+
expect(entry).to be_a Contentful::DynamicEntry
|
112
|
+
expect(entry).to be_a CatEntry
|
114
113
|
end
|
115
114
|
end
|
116
115
|
end
|
@@ -119,13 +118,13 @@ describe 'Client Configuration Options' do
|
|
119
118
|
it 'is "cdn.contentful.com" [default]' do
|
120
119
|
expect(
|
121
120
|
create_client.configuration[:api_url]
|
122
|
-
).to eq
|
121
|
+
).to eq 'cdn.contentful.com'
|
123
122
|
end
|
124
123
|
|
125
124
|
it 'will be used as base url' do
|
126
125
|
expect(
|
127
|
-
create_client(api_url:
|
128
|
-
).to start_with
|
126
|
+
create_client(api_url: 'cdn2.contentful.com').base_url
|
127
|
+
).to start_with 'https://cdn2.contentful.com'
|
129
128
|
end
|
130
129
|
end
|
131
130
|
|
@@ -139,7 +138,7 @@ describe 'Client Configuration Options' do
|
|
139
138
|
it 'will used for the http content type request header' do
|
140
139
|
expect(
|
141
140
|
create_client(api_version: 2).request_headers['Content-Type']
|
142
|
-
).to eq
|
141
|
+
).to eq 'application/vnd.contentful.delivery.v2+json'
|
143
142
|
end
|
144
143
|
end
|
145
144
|
|
@@ -148,7 +147,7 @@ describe 'Client Configuration Options' do
|
|
148
147
|
it 'will add the :access_token as authorization bearer token request header' do
|
149
148
|
expect(
|
150
149
|
create_client.request_headers['Authorization']
|
151
|
-
).to eq
|
150
|
+
).to eq 'Bearer b4c0n73n7fu1'
|
152
151
|
end
|
153
152
|
|
154
153
|
it 'will not add :access_token to query' do
|
@@ -163,7 +162,7 @@ describe 'Client Configuration Options' do
|
|
163
162
|
expect(
|
164
163
|
create_client(authentication_mechanism: :query_string).
|
165
164
|
request_query({})['access_token']
|
166
|
-
).to eq
|
165
|
+
).to eq 'b4c0n73n7fu1'
|
167
166
|
end
|
168
167
|
|
169
168
|
it 'will not add the :access_token as authorization bearer token request header' do
|
@@ -179,7 +178,7 @@ describe 'Client Configuration Options' do
|
|
179
178
|
it 'lets you register your own resource classes for certain response types' do
|
180
179
|
class MyBetterAsset < Contentful::Asset
|
181
180
|
def https_image_url
|
182
|
-
image_url.sub %r
|
181
|
+
image_url.sub %r{\A//}, 'https://'
|
183
182
|
end
|
184
183
|
end
|
185
184
|
|
@@ -189,9 +188,9 @@ describe 'Client Configuration Options' do
|
|
189
188
|
}
|
190
189
|
)
|
191
190
|
|
192
|
-
nyancat = vcr('asset'){ client.asset 'nyancat' }
|
193
|
-
expect(
|
194
|
-
expect(
|
191
|
+
nyancat = vcr('asset') { client.asset 'nyancat' }
|
192
|
+
expect(nyancat).to be_a MyBetterAsset
|
193
|
+
expect(nyancat.https_image_url).to start_with 'https'
|
195
194
|
end
|
196
195
|
end
|
197
196
|
|
@@ -207,10 +206,10 @@ describe 'Client Configuration Options' do
|
|
207
206
|
}
|
208
207
|
)
|
209
208
|
|
210
|
-
nyancat = vcr('entry'){ client.entry 'nyancat' }
|
211
|
-
finn = vcr('human'){ client.entry 'finn' }
|
212
|
-
expect(
|
213
|
-
expect(
|
209
|
+
nyancat = vcr('entry') { client.entry 'nyancat' }
|
210
|
+
finn = vcr('human') { client.entry 'finn' }
|
211
|
+
expect(nyancat).to be_a Cat
|
212
|
+
expect(finn).to be_a Contentful::Entry
|
214
213
|
end
|
215
214
|
end
|
216
215
|
end
|
data/spec/coercions_spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Coercion Examples' do
|
4
|
-
let(:entry){ vcr(
|
4
|
+
let(:entry) { vcr('entry') { create_client.entry 'nyancat' } }
|
5
5
|
|
6
6
|
it 'converts contentful to ruby DateTime objects' do
|
7
|
-
expect(
|
8
|
-
expect(
|
7
|
+
expect(entry.created_at).to be_a DateTime
|
8
|
+
expect(entry.created_at.day).to eq 27
|
9
9
|
end
|
10
|
-
end
|
10
|
+
end
|
data/spec/content_type_spec.rb
CHANGED
@@ -1,44 +1,44 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Contentful::ContentType do
|
4
|
-
let(:content_type){ vcr('content_type'){ create_client.content_type 'cat' } }
|
4
|
+
let(:content_type) { vcr('content_type') { create_client.content_type 'cat' } }
|
5
5
|
|
6
6
|
describe 'SystemProperties' do
|
7
7
|
it 'has a #sys getter returning a hash with symbol keys' do
|
8
|
-
expect(
|
9
|
-
expect(
|
8
|
+
expect(content_type.sys).to be_a Hash
|
9
|
+
expect(content_type.sys.keys.sample).to be_a Symbol
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'has #id' do
|
13
|
-
expect(
|
13
|
+
expect(content_type.id).to eq 'cat'
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'has #type' do
|
17
|
-
expect(
|
17
|
+
expect(content_type.type).to eq 'ContentType'
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
describe 'Properties' do
|
22
22
|
it 'has a #properties getter returning a hash with symbol keys' do
|
23
|
-
expect(
|
24
|
-
expect(
|
23
|
+
expect(content_type.properties).to be_a Hash
|
24
|
+
expect(content_type.properties.keys.sample).to be_a Symbol
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'has #name' do
|
28
|
-
expect(
|
28
|
+
expect(content_type.name).to eq 'Cat'
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'has #description' do
|
32
|
-
expect(
|
32
|
+
expect(content_type.description).to eq 'Meow.'
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'has #fields' do
|
36
|
-
expect(
|
37
|
-
expect(
|
36
|
+
expect(content_type.fields).to be_a Array
|
37
|
+
expect(content_type.fields.first).to be_a Contentful::Field
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'could have #display_field' do
|
41
|
-
expect(
|
41
|
+
expect(content_type).to respond_to :display_field
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/spec/deleted_asset_spec.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
3
|
describe 'DeletedAsset' do
|
5
|
-
let(:deleted_asset)
|
6
|
-
vcr('sync_deleted_asset')
|
4
|
+
let(:deleted_asset)do
|
5
|
+
vcr('sync_deleted_asset')do
|
7
6
|
create_client.sync(initial: true, type: 'DeletedAsset').first_page.items[0]
|
8
|
-
|
9
|
-
|
7
|
+
end
|
8
|
+
end
|
10
9
|
|
11
10
|
describe 'SystemProperties' do
|
12
11
|
it 'has a #sys getter returning a hash with symbol keys' do
|
13
|
-
expect(
|
14
|
-
expect(
|
12
|
+
expect(deleted_asset.sys).to be_a Hash
|
13
|
+
expect(deleted_asset.sys.keys.sample).to be_a Symbol
|
15
14
|
end
|
16
15
|
|
17
16
|
it 'has #id' do
|
18
|
-
expect(
|
17
|
+
expect(deleted_asset.id).to eq '5c6VY0gWg0gwaIeYkUUiqG'
|
19
18
|
end
|
20
19
|
|
21
20
|
it 'has #type' do
|
22
|
-
expect(
|
21
|
+
expect(deleted_asset.type).to eq 'DeletedAsset'
|
23
22
|
end
|
24
23
|
|
25
24
|
it 'has #deleted_at' do
|
26
|
-
expect(
|
25
|
+
expect(deleted_asset.created_at).to be_a DateTime
|
27
26
|
end
|
28
27
|
end
|
29
|
-
end
|
28
|
+
end
|
data/spec/deleted_entry_spec.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
3
|
describe 'DeletedEntry' do
|
5
|
-
let(:deleted_entry)
|
6
|
-
vcr('sync_deleted_entry')
|
4
|
+
let(:deleted_entry)do
|
5
|
+
vcr('sync_deleted_entry')do
|
7
6
|
create_client.sync(initial: true, type: 'DeletedEntry').first_page.items[0]
|
8
|
-
|
9
|
-
|
7
|
+
end
|
8
|
+
end
|
10
9
|
|
11
10
|
describe 'SystemProperties' do
|
12
11
|
it 'has a #sys getter returning a hash with symbol keys' do
|
13
|
-
expect(
|
14
|
-
expect(
|
12
|
+
expect(deleted_entry.sys).to be_a Hash
|
13
|
+
expect(deleted_entry.sys.keys.sample).to be_a Symbol
|
15
14
|
end
|
16
15
|
|
17
16
|
it 'has #id' do
|
18
|
-
expect(
|
17
|
+
expect(deleted_entry.id).to eq 'CVebBDcQsSsu6yKKIayy'
|
19
18
|
end
|
20
19
|
|
21
20
|
it 'has #type' do
|
22
|
-
expect(
|
21
|
+
expect(deleted_entry.type).to eq 'DeletedEntry'
|
23
22
|
end
|
24
23
|
|
25
24
|
it 'has #deleted_at' do
|
26
|
-
expect(
|
25
|
+
expect(deleted_entry.created_at).to be_a DateTime
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|