trailer_vote-api 0.8.4 → 0.9.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 +5 -5
- data/.gitignore +13 -13
- data/.rubocop.yml +29 -29
- data/CHANGELOG.md +56 -56
- data/Gemfile +6 -6
- data/Gemfile.lock +10 -11
- data/README.md +188 -188
- data/Rakefile +12 -12
- data/bin/console +15 -15
- data/bin/setup +8 -8
- data/lib/trailer_vote/api.rb +43 -43
- data/lib/trailer_vote/api/autoload.rb +24 -22
- data/lib/trailer_vote/api/composable/common.rb +91 -91
- data/lib/trailer_vote/api/composable/get.rb +66 -66
- data/lib/trailer_vote/api/configuration.rb +71 -71
- data/lib/trailer_vote/api/errors.rb +115 -115
- data/lib/trailer_vote/api/fallback_content_types.rb +50 -50
- data/lib/trailer_vote/api/issue.rb +31 -31
- data/lib/trailer_vote/api/issue/create.rb +72 -72
- data/lib/trailer_vote/api/issue/find.rb +42 -42
- data/lib/trailer_vote/api/links.rb +54 -54
- data/lib/trailer_vote/api/place.rb +24 -24
- data/lib/trailer_vote/api/place/children.rb +33 -0
- data/lib/trailer_vote/api/place/children/urls.rb +67 -0
- data/lib/trailer_vote/api/place/create.rb +83 -83
- data/lib/trailer_vote/api/place/find.rb +60 -59
- data/lib/trailer_vote/api/product.rb +33 -33
- data/lib/trailer_vote/api/product/create.rb +63 -63
- data/lib/trailer_vote/api/product/find.rb +55 -55
- data/lib/trailer_vote/api/product/image.rb +38 -38
- data/lib/trailer_vote/api/product/image/create.rb +83 -83
- data/lib/trailer_vote/api/product/image/find.rb +54 -54
- data/lib/trailer_vote/api/product/image/urls.rb +66 -66
- data/lib/trailer_vote/api/product/lookup.rb +97 -97
- data/lib/trailer_vote/api/product/place.rb +40 -40
- data/lib/trailer_vote/api/product/place/link.rb +75 -75
- data/lib/trailer_vote/api/product/update.rb +80 -80
- data/lib/trailer_vote/api/product/video.rb +38 -38
- data/lib/trailer_vote/api/product/video/create.rb +80 -80
- data/lib/trailer_vote/api/product/video/find.rb +56 -56
- data/lib/trailer_vote/api/product/video/urls.rb +66 -66
- data/lib/trailer_vote/api/type_registry.rb +99 -99
- data/lib/trailer_vote/api/version.rb +7 -7
- data/trailer_vote-api.gemspec +45 -45
- metadata +7 -6
@@ -1,75 +1,75 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'trailer_vote/api/composable/common'
|
4
|
-
|
5
|
-
require 'trailer_vote/api/product/place'
|
6
|
-
require 'trailer_vote/api/place/find'
|
7
|
-
|
8
|
-
module TrailerVote
|
9
|
-
module Api
|
10
|
-
class Product
|
11
|
-
class Place
|
12
|
-
|
13
|
-
# @return [TrailerVote::Api::Product::Place::Link] api to link a place to the current +product+
|
14
|
-
def link
|
15
|
-
Link.new(configuration: configuration, product: product)
|
16
|
-
end
|
17
|
-
|
18
|
-
class Link
|
19
|
-
include Composable::Common
|
20
|
-
|
21
|
-
CONTENT = MediaTypes::ProductPlaceLink.to_constructable.version(1).view('create')
|
22
|
-
SUCCESS = MediaTypes::ProductPlaceLink.to_constructable.version(1)
|
23
|
-
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
24
|
-
|
25
|
-
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
26
|
-
|
27
|
-
def initialize(configuration:, product:)
|
28
|
-
self.configuration = configuration
|
29
|
-
self.product = product
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [TrailerVote::Api::Product::Place] the api to deal with the product places
|
33
|
-
def back
|
34
|
-
product.place
|
35
|
-
end
|
36
|
-
|
37
|
-
def call(data:, url: resolve_url)
|
38
|
-
body = encode(data)
|
39
|
-
guard_network_errors do
|
40
|
-
branch(
|
41
|
-
resolve_client.headers(
|
42
|
-
Headers::ACCEPT => ACCEPT,
|
43
|
-
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
|
44
|
-
).post(url, body: body),
|
45
|
-
data: data
|
46
|
-
)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
attr_accessor :product
|
53
|
-
|
54
|
-
def resolve_url
|
55
|
-
product.links.places
|
56
|
-
end
|
57
|
-
|
58
|
-
def encode(data)
|
59
|
-
TrailerVote::Api.encode(CONTENT, data.is_a?(::Hash) ? data : { place: data })
|
60
|
-
end
|
61
|
-
|
62
|
-
def forward(result, data:)
|
63
|
-
return unless [307, 308].include?(result.status)
|
64
|
-
forward_klazz.new(configuration: configuration, product: product)
|
65
|
-
.call(data: data, url: redirected_url(result))
|
66
|
-
end
|
67
|
-
|
68
|
-
def redirect_klazz
|
69
|
-
TrailerVote::Api::Place::Find
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/composable/common'
|
4
|
+
|
5
|
+
require 'trailer_vote/api/product/place'
|
6
|
+
require 'trailer_vote/api/place/find'
|
7
|
+
|
8
|
+
module TrailerVote
|
9
|
+
module Api
|
10
|
+
class Product
|
11
|
+
class Place
|
12
|
+
|
13
|
+
# @return [TrailerVote::Api::Product::Place::Link] api to link a place to the current +product+
|
14
|
+
def link
|
15
|
+
Link.new(configuration: configuration, product: product)
|
16
|
+
end
|
17
|
+
|
18
|
+
class Link
|
19
|
+
include Composable::Common
|
20
|
+
|
21
|
+
CONTENT = MediaTypes::ProductPlaceLink.to_constructable.version(1).view('create')
|
22
|
+
SUCCESS = MediaTypes::ProductPlaceLink.to_constructable.version(1)
|
23
|
+
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
24
|
+
|
25
|
+
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
26
|
+
|
27
|
+
def initialize(configuration:, product:)
|
28
|
+
self.configuration = configuration
|
29
|
+
self.product = product
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [TrailerVote::Api::Product::Place] the api to deal with the product places
|
33
|
+
def back
|
34
|
+
product.place
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(data:, url: resolve_url)
|
38
|
+
body = encode(data)
|
39
|
+
guard_network_errors do
|
40
|
+
branch(
|
41
|
+
resolve_client.headers(
|
42
|
+
Headers::ACCEPT => ACCEPT,
|
43
|
+
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
|
44
|
+
).post(url, body: body),
|
45
|
+
data: data
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_accessor :product
|
53
|
+
|
54
|
+
def resolve_url
|
55
|
+
product.links.places
|
56
|
+
end
|
57
|
+
|
58
|
+
def encode(data)
|
59
|
+
TrailerVote::Api.encode(CONTENT, data.is_a?(::Hash) ? data : { place: data })
|
60
|
+
end
|
61
|
+
|
62
|
+
def forward(result, data:)
|
63
|
+
return unless [307, 308].include?(result.status)
|
64
|
+
forward_klazz.new(configuration: configuration, product: product)
|
65
|
+
.call(data: data, url: redirected_url(result))
|
66
|
+
end
|
67
|
+
|
68
|
+
def redirect_klazz
|
69
|
+
TrailerVote::Api::Place::Find
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,80 +1,80 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'trailer_vote/api/composable/common'
|
4
|
-
|
5
|
-
require 'trailer_vote/api/product'
|
6
|
-
require 'trailer_vote/api/product/find'
|
7
|
-
|
8
|
-
module TrailerVote
|
9
|
-
module Api
|
10
|
-
class Product
|
11
|
-
class Find
|
12
|
-
# @return [TrailerVote::Api::Product::Update] api to deal with a updating a found product
|
13
|
-
def update
|
14
|
-
Update.new(configuration: configuration, product: self)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class Update
|
19
|
-
include Composable::Common
|
20
|
-
|
21
|
-
CONTENT = MediaTypes::Product.to_constructable.version(2)
|
22
|
-
SUCCESS = MediaTypes::Product.to_constructable.version(2)
|
23
|
-
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
24
|
-
|
25
|
-
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
26
|
-
|
27
|
-
def initialize(configuration:, product:)
|
28
|
-
self.configuration = configuration
|
29
|
-
self.product = product
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [TrailerVote::Api::Product::Find] api to deal with a found product
|
33
|
-
def back
|
34
|
-
product
|
35
|
-
end
|
36
|
-
|
37
|
-
def call(data:, url: resolve_url, etag: resolve_etag)
|
38
|
-
body = encode(data)
|
39
|
-
guard_network_errors do
|
40
|
-
branch(
|
41
|
-
resolve_client.headers(
|
42
|
-
Headers::ACCEPT => ACCEPT,
|
43
|
-
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}",
|
44
|
-
Headers::IF_MATCH => etag
|
45
|
-
).put(url, body: body),
|
46
|
-
data: data
|
47
|
-
)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
attr_accessor :product
|
54
|
-
|
55
|
-
def resolve_url
|
56
|
-
product.links.self
|
57
|
-
end
|
58
|
-
|
59
|
-
def resolve_etag
|
60
|
-
product.etag
|
61
|
-
end
|
62
|
-
|
63
|
-
def encode(data)
|
64
|
-
TrailerVote::Api.encode(CONTENT, product: data)
|
65
|
-
end
|
66
|
-
|
67
|
-
def redirect_klazz
|
68
|
-
Find
|
69
|
-
end
|
70
|
-
|
71
|
-
def forward(result, data:)
|
72
|
-
return unless [307, 308].include?(result.status)
|
73
|
-
url = result['Location'] || result['Content-Location']
|
74
|
-
forward_klazz.new(configuration: configuration, product: product)
|
75
|
-
.call(data: data, url: url, etag: resolve_etag)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/composable/common'
|
4
|
+
|
5
|
+
require 'trailer_vote/api/product'
|
6
|
+
require 'trailer_vote/api/product/find'
|
7
|
+
|
8
|
+
module TrailerVote
|
9
|
+
module Api
|
10
|
+
class Product
|
11
|
+
class Find
|
12
|
+
# @return [TrailerVote::Api::Product::Update] api to deal with a updating a found product
|
13
|
+
def update
|
14
|
+
Update.new(configuration: configuration, product: self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Update
|
19
|
+
include Composable::Common
|
20
|
+
|
21
|
+
CONTENT = MediaTypes::Product.to_constructable.version(2)
|
22
|
+
SUCCESS = MediaTypes::Product.to_constructable.version(2)
|
23
|
+
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
24
|
+
|
25
|
+
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
26
|
+
|
27
|
+
def initialize(configuration:, product:)
|
28
|
+
self.configuration = configuration
|
29
|
+
self.product = product
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [TrailerVote::Api::Product::Find] api to deal with a found product
|
33
|
+
def back
|
34
|
+
product
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(data:, url: resolve_url, etag: resolve_etag)
|
38
|
+
body = encode(data)
|
39
|
+
guard_network_errors do
|
40
|
+
branch(
|
41
|
+
resolve_client.headers(
|
42
|
+
Headers::ACCEPT => ACCEPT,
|
43
|
+
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}",
|
44
|
+
Headers::IF_MATCH => etag
|
45
|
+
).put(url, body: body),
|
46
|
+
data: data
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_accessor :product
|
54
|
+
|
55
|
+
def resolve_url
|
56
|
+
product.links.self
|
57
|
+
end
|
58
|
+
|
59
|
+
def resolve_etag
|
60
|
+
product.etag
|
61
|
+
end
|
62
|
+
|
63
|
+
def encode(data)
|
64
|
+
TrailerVote::Api.encode(CONTENT, product: data)
|
65
|
+
end
|
66
|
+
|
67
|
+
def redirect_klazz
|
68
|
+
Find
|
69
|
+
end
|
70
|
+
|
71
|
+
def forward(result, data:)
|
72
|
+
return unless [307, 308].include?(result.status)
|
73
|
+
url = result['Location'] || result['Content-Location']
|
74
|
+
forward_klazz.new(configuration: configuration, product: product)
|
75
|
+
.call(data: data, url: url, etag: resolve_etag)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,38 +1,38 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'trailer_vote/api/product'
|
4
|
-
require 'trailer_vote/api/product/find'
|
5
|
-
|
6
|
-
module TrailerVote
|
7
|
-
module Api
|
8
|
-
class Product
|
9
|
-
class Find
|
10
|
-
# @return [TrailerVote::Api::Product::Video] api to deal with a product's videos
|
11
|
-
def video
|
12
|
-
Video.new(configuration: configuration, product: self)
|
13
|
-
end
|
14
|
-
|
15
|
-
alias product_video video
|
16
|
-
alias videos video
|
17
|
-
end
|
18
|
-
|
19
|
-
class Video
|
20
|
-
def initialize(configuration:, product: nil)
|
21
|
-
self.configuration = configuration
|
22
|
-
self.product = product
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [TrailerVote::Api::Product::Find] api to deal with a found product
|
26
|
-
def back
|
27
|
-
product
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
attr_accessor :configuration, :product
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
TrailerVote::MediaTypes::ProductVideo.register
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/product'
|
4
|
+
require 'trailer_vote/api/product/find'
|
5
|
+
|
6
|
+
module TrailerVote
|
7
|
+
module Api
|
8
|
+
class Product
|
9
|
+
class Find
|
10
|
+
# @return [TrailerVote::Api::Product::Video] api to deal with a product's videos
|
11
|
+
def video
|
12
|
+
Video.new(configuration: configuration, product: self)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias product_video video
|
16
|
+
alias videos video
|
17
|
+
end
|
18
|
+
|
19
|
+
class Video
|
20
|
+
def initialize(configuration:, product: nil)
|
21
|
+
self.configuration = configuration
|
22
|
+
self.product = product
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [TrailerVote::Api::Product::Find] api to deal with a found product
|
26
|
+
def back
|
27
|
+
product
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_accessor :configuration, :product
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
TrailerVote::MediaTypes::ProductVideo.register
|
@@ -1,80 +1,80 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'trailer_vote/api/composable/common'
|
4
|
-
|
5
|
-
require 'trailer_vote/api/product/video'
|
6
|
-
require 'trailer_vote/api/product/video/find'
|
7
|
-
|
8
|
-
module TrailerVote
|
9
|
-
module Api
|
10
|
-
class Product
|
11
|
-
class Video
|
12
|
-
def create
|
13
|
-
Create.new(configuration: configuration, product: product)
|
14
|
-
end
|
15
|
-
|
16
|
-
class Create
|
17
|
-
include Composable::Common
|
18
|
-
|
19
|
-
CONTENT = MediaTypes::ProductVideo.to_constructable.version(1).view('create')
|
20
|
-
SUCCESS = MediaTypes::ProductVideo.to_constructable.version(1)
|
21
|
-
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
22
|
-
|
23
|
-
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
24
|
-
|
25
|
-
def initialize(configuration:, product:)
|
26
|
-
self.configuration = configuration
|
27
|
-
self.product = product
|
28
|
-
end
|
29
|
-
|
30
|
-
def back
|
31
|
-
product.video
|
32
|
-
end
|
33
|
-
|
34
|
-
# Create the image
|
35
|
-
#
|
36
|
-
# @see https://www.rubydoc.info/gems/trailer_vote-media_types/TrailerVote/MediaTypes/ProductImage TrailerVote::MediaTypes::ProductImage
|
37
|
-
#
|
38
|
-
# @param [String] url (#resolve_url result) the url to post to
|
39
|
-
# @param [Hash] data the image data
|
40
|
-
#
|
41
|
-
# @return [TrailerVote::Api::Product::Video::Find]
|
42
|
-
def call(data:, url: resolve_url)
|
43
|
-
body = encode(data)
|
44
|
-
guard_network_errors do
|
45
|
-
branch(
|
46
|
-
resolve_client.headers(
|
47
|
-
Headers::ACCEPT => ACCEPT,
|
48
|
-
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
|
49
|
-
).post(url, body: body),
|
50
|
-
data: data
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
attr_accessor :product
|
58
|
-
|
59
|
-
def resolve_url
|
60
|
-
product.links.videos
|
61
|
-
end
|
62
|
-
|
63
|
-
def encode(data)
|
64
|
-
TrailerVote::Api.encode(CONTENT, product_video: data)
|
65
|
-
end
|
66
|
-
|
67
|
-
def forward(result, data:)
|
68
|
-
return unless [307, 308].include?(result.status)
|
69
|
-
forward_klazz.new(configuration: configuration, product: product)
|
70
|
-
.call(data: data, url: redirected_url(result))
|
71
|
-
end
|
72
|
-
|
73
|
-
def redirect_klazz
|
74
|
-
Find
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'trailer_vote/api/composable/common'
|
4
|
+
|
5
|
+
require 'trailer_vote/api/product/video'
|
6
|
+
require 'trailer_vote/api/product/video/find'
|
7
|
+
|
8
|
+
module TrailerVote
|
9
|
+
module Api
|
10
|
+
class Product
|
11
|
+
class Video
|
12
|
+
def create
|
13
|
+
Create.new(configuration: configuration, product: product)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Create
|
17
|
+
include Composable::Common
|
18
|
+
|
19
|
+
CONTENT = MediaTypes::ProductVideo.to_constructable.version(1).view('create')
|
20
|
+
SUCCESS = MediaTypes::ProductVideo.to_constructable.version(1)
|
21
|
+
FAILURE = MediaTypes::Errors.to_constructable.version(1)
|
22
|
+
|
23
|
+
ACCEPT = [SUCCESS.to_s, FAILURE.to_s(0.1)].join(', ').freeze
|
24
|
+
|
25
|
+
def initialize(configuration:, product:)
|
26
|
+
self.configuration = configuration
|
27
|
+
self.product = product
|
28
|
+
end
|
29
|
+
|
30
|
+
def back
|
31
|
+
product.video
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create the image
|
35
|
+
#
|
36
|
+
# @see https://www.rubydoc.info/gems/trailer_vote-media_types/TrailerVote/MediaTypes/ProductImage TrailerVote::MediaTypes::ProductImage
|
37
|
+
#
|
38
|
+
# @param [String] url (#resolve_url result) the url to post to
|
39
|
+
# @param [Hash] data the image data
|
40
|
+
#
|
41
|
+
# @return [TrailerVote::Api::Product::Video::Find]
|
42
|
+
def call(data:, url: resolve_url)
|
43
|
+
body = encode(data)
|
44
|
+
guard_network_errors do
|
45
|
+
branch(
|
46
|
+
resolve_client.headers(
|
47
|
+
Headers::ACCEPT => ACCEPT,
|
48
|
+
Headers::CONTENT_TYPE => "#{CONTENT}; charset=#{body.encoding.name}"
|
49
|
+
).post(url, body: body),
|
50
|
+
data: data
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_accessor :product
|
58
|
+
|
59
|
+
def resolve_url
|
60
|
+
product.links.videos
|
61
|
+
end
|
62
|
+
|
63
|
+
def encode(data)
|
64
|
+
TrailerVote::Api.encode(CONTENT, product_video: data)
|
65
|
+
end
|
66
|
+
|
67
|
+
def forward(result, data:)
|
68
|
+
return unless [307, 308].include?(result.status)
|
69
|
+
forward_klazz.new(configuration: configuration, product: product)
|
70
|
+
.call(data: data, url: redirected_url(result))
|
71
|
+
end
|
72
|
+
|
73
|
+
def redirect_klazz
|
74
|
+
Find
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|