wcc-jsonapi 0.1.0 → 0.2.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/README.md +14 -0
- data/Rakefile +9 -2
- data/lib/wcc/jsonapi/concerns/api_action_caching.rb +128 -0
- data/lib/wcc/jsonapi/concerns/pagination_helper.rb +49 -0
- data/lib/wcc/jsonapi/concerns/serializer_with_caching.rb +171 -0
- data/lib/wcc/jsonapi/rspec/api_cache_control_examples.rb +97 -0
- data/lib/wcc/jsonapi/rspec/snapshot_helper.rb +158 -0
- data/lib/wcc/jsonapi/rspec.rb +11 -0
- data/lib/wcc/{json_api → jsonapi}/version.rb +1 -1
- data/lib/wcc/jsonapi.rb +9 -0
- data/lib/wcc-jsonapi.rb +3 -0
- metadata +27 -206
- data/.gitignore +0 -14
- data/Gemfile +0 -6
- data/LICENSE.txt +0 -0
- data/lib/generators/wcc/json_api/client_generator.rb +0 -9
- data/lib/wcc/json_api/active_record_shim.rb +0 -54
- data/lib/wcc/json_api/base_client/api_error.rb +0 -26
- data/lib/wcc/json_api/base_client/base_response.rb +0 -127
- data/lib/wcc/json_api/base_client/http_adapter.rb +0 -19
- data/lib/wcc/json_api/base_client/typhoeus_adapter.rb +0 -63
- data/lib/wcc/json_api/base_client.rb +0 -142
- data/wcc-jsonapi.gemspec +0 -38
metadata
CHANGED
@@ -1,244 +1,66 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wcc-jsonapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Watermark Dev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: actionpack-action_caching
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '1.0'
|
19
|
+
version: '1.2'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '1.0'
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: bundler
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '1.6'
|
40
|
-
type: :development
|
41
|
-
prerelease: false
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - "~>"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '1.6'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: dotenv
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - "~>"
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 0.10.0
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - "~>"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: 0.10.0
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: faraday
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '0.9'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '0.9'
|
26
|
+
version: '1.2'
|
75
27
|
- !ruby/object:Gem::Dependency
|
76
|
-
name:
|
28
|
+
name: rails
|
77
29
|
requirement: !ruby/object:Gem::Requirement
|
78
30
|
requirements:
|
79
|
-
- - "
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '2.15'
|
82
|
-
type: :development
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '2.15'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: guard-rspec
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '4.7'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '4.7'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: guard-rubocop
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '1.3'
|
110
|
-
type: :development
|
111
|
-
prerelease: false
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
113
|
-
requirements:
|
114
|
-
- - "~>"
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
version: '1.3'
|
117
|
-
- !ruby/object:Gem::Dependency
|
118
|
-
name: httplog
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - "~>"
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '1.0'
|
124
|
-
type: :development
|
125
|
-
prerelease: false
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
127
|
-
requirements:
|
128
|
-
- - "~>"
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version: '1.0'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: rake
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - "~>"
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '12.3'
|
138
|
-
type: :development
|
139
|
-
prerelease: false
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - "~>"
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '12.3'
|
145
|
-
- !ruby/object:Gem::Dependency
|
146
|
-
name: rspec
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
148
|
-
requirements:
|
149
|
-
- - "~>"
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '3.3'
|
152
|
-
type: :development
|
153
|
-
prerelease: false
|
154
|
-
version_requirements: !ruby/object:Gem::Requirement
|
155
|
-
requirements:
|
156
|
-
- - "~>"
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '3.3'
|
159
|
-
- !ruby/object:Gem::Dependency
|
160
|
-
name: rspec_junit_formatter
|
161
|
-
requirement: !ruby/object:Gem::Requirement
|
162
|
-
requirements:
|
163
|
-
- - "~>"
|
164
|
-
- !ruby/object:Gem::Version
|
165
|
-
version: 0.3.0
|
166
|
-
type: :development
|
167
|
-
prerelease: false
|
168
|
-
version_requirements: !ruby/object:Gem::Requirement
|
169
|
-
requirements:
|
170
|
-
- - "~>"
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
version: 0.3.0
|
173
|
-
- !ruby/object:Gem::Dependency
|
174
|
-
name: rubocop
|
175
|
-
requirement: !ruby/object:Gem::Requirement
|
176
|
-
requirements:
|
177
|
-
- - '='
|
178
|
-
- !ruby/object:Gem::Version
|
179
|
-
version: 0.69.0
|
180
|
-
type: :development
|
181
|
-
prerelease: false
|
182
|
-
version_requirements: !ruby/object:Gem::Requirement
|
183
|
-
requirements:
|
184
|
-
- - '='
|
185
|
-
- !ruby/object:Gem::Version
|
186
|
-
version: 0.69.0
|
187
|
-
- !ruby/object:Gem::Dependency
|
188
|
-
name: typhoeus
|
189
|
-
requirement: !ruby/object:Gem::Requirement
|
190
|
-
requirements:
|
191
|
-
- - "~>"
|
192
|
-
- !ruby/object:Gem::Version
|
193
|
-
version: '1.3'
|
194
|
-
type: :development
|
195
|
-
prerelease: false
|
196
|
-
version_requirements: !ruby/object:Gem::Requirement
|
197
|
-
requirements:
|
198
|
-
- - "~>"
|
199
|
-
- !ruby/object:Gem::Version
|
200
|
-
version: '1.3'
|
201
|
-
- !ruby/object:Gem::Dependency
|
202
|
-
name: webmock
|
203
|
-
requirement: !ruby/object:Gem::Requirement
|
204
|
-
requirements:
|
205
|
-
- - "~>"
|
31
|
+
- - ">="
|
206
32
|
- !ruby/object:Gem::Version
|
207
|
-
version: '
|
208
|
-
type: :
|
33
|
+
version: '4'
|
34
|
+
type: :runtime
|
209
35
|
prerelease: false
|
210
36
|
version_requirements: !ruby/object:Gem::Requirement
|
211
37
|
requirements:
|
212
|
-
- - "
|
38
|
+
- - ">="
|
213
39
|
- !ruby/object:Gem::Version
|
214
|
-
version: '
|
215
|
-
description:
|
216
|
-
convention
|
40
|
+
version: '4'
|
41
|
+
description: Helpers for creating APIs which conform to the JsonAPI standard
|
217
42
|
email:
|
218
43
|
- dev@watermark.org
|
219
44
|
executables: []
|
220
45
|
extensions: []
|
221
46
|
extra_rdoc_files: []
|
222
47
|
files:
|
223
|
-
- ".gitignore"
|
224
|
-
- Gemfile
|
225
|
-
- LICENSE.txt
|
226
48
|
- README.md
|
227
49
|
- Rakefile
|
228
|
-
- lib/
|
229
|
-
- lib/wcc/
|
230
|
-
- lib/wcc/
|
231
|
-
- lib/wcc/
|
232
|
-
- lib/wcc/
|
233
|
-
- lib/wcc/
|
234
|
-
- lib/wcc/
|
235
|
-
- lib/wcc/
|
236
|
-
- wcc
|
237
|
-
homepage: https://github.com/watermarkchurch/
|
50
|
+
- lib/wcc-jsonapi.rb
|
51
|
+
- lib/wcc/jsonapi.rb
|
52
|
+
- lib/wcc/jsonapi/concerns/api_action_caching.rb
|
53
|
+
- lib/wcc/jsonapi/concerns/pagination_helper.rb
|
54
|
+
- lib/wcc/jsonapi/concerns/serializer_with_caching.rb
|
55
|
+
- lib/wcc/jsonapi/rspec.rb
|
56
|
+
- lib/wcc/jsonapi/rspec/api_cache_control_examples.rb
|
57
|
+
- lib/wcc/jsonapi/rspec/snapshot_helper.rb
|
58
|
+
- lib/wcc/jsonapi/version.rb
|
59
|
+
homepage: https://www.github.com/watermarkchurch/paper-signs
|
238
60
|
licenses:
|
239
61
|
- MIT
|
240
62
|
metadata: {}
|
241
|
-
post_install_message:
|
63
|
+
post_install_message:
|
242
64
|
rdoc_options: []
|
243
65
|
require_paths:
|
244
66
|
- lib
|
@@ -253,9 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
253
75
|
- !ruby/object:Gem::Version
|
254
76
|
version: '0'
|
255
77
|
requirements: []
|
256
|
-
|
257
|
-
|
258
|
-
signing_key:
|
78
|
+
rubygems_version: 3.1.6
|
79
|
+
signing_key:
|
259
80
|
specification_version: 4
|
260
|
-
summary:
|
81
|
+
summary: Helpers for creating APIs which conform to the JsonAPI standard
|
261
82
|
test_files: []
|
data/.gitignore
DELETED
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
File without changes
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module WCC::API::ActiveRecordShim
|
2
|
-
def self.included(base)
|
3
|
-
base.public_send :include, InstanceMethods
|
4
|
-
base.extend ClassMethods
|
5
|
-
end
|
6
|
-
|
7
|
-
module InstanceMethods
|
8
|
-
def attributes
|
9
|
-
raw.keys.each_with_object({}) do |key, h|
|
10
|
-
next unless respond_to?(key)
|
11
|
-
|
12
|
-
val = public_send(key)
|
13
|
-
h[key] =
|
14
|
-
if val.is_a? Array
|
15
|
-
val.map { |v| v.respond_to?(:to_h) ? v.to_h : v }
|
16
|
-
else
|
17
|
-
val.respond_to?(:to_h) ? val.to_h : val
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module ClassMethods
|
24
|
-
def model_name
|
25
|
-
name
|
26
|
-
end
|
27
|
-
|
28
|
-
def table_name
|
29
|
-
endpoint
|
30
|
-
end
|
31
|
-
|
32
|
-
def unscoped
|
33
|
-
yield
|
34
|
-
end
|
35
|
-
|
36
|
-
def find_in_batches(options, &block)
|
37
|
-
options = options ? options.dup : {}
|
38
|
-
batch_size = options.delete(:batch_size) || 1000
|
39
|
-
|
40
|
-
filter = {
|
41
|
-
limit: batch_size,
|
42
|
-
offset: options.delete(:start) || 0
|
43
|
-
}
|
44
|
-
|
45
|
-
find_all(filter).each_slice(batch_size, &block)
|
46
|
-
end
|
47
|
-
|
48
|
-
def where(**conditions)
|
49
|
-
# TODO: return a Query object that implements more of the ActiveRecord query interface
|
50
|
-
# https://guides.rubyonrails.org/active_record_querying.html#conditions
|
51
|
-
find_all(conditions)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WCC::JsonAPI
|
4
|
-
class BaseClient
|
5
|
-
class ApiError < StandardError
|
6
|
-
attr_reader :response
|
7
|
-
|
8
|
-
def self.[](code)
|
9
|
-
case code
|
10
|
-
when 404
|
11
|
-
NotFoundError
|
12
|
-
else
|
13
|
-
ApiError
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize(response)
|
18
|
-
@response = response
|
19
|
-
super(response.error_message)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class NotFoundError < ApiError
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
require_relative 'api_error'
|
5
|
-
|
6
|
-
module WCC::JsonAPI
|
7
|
-
class BaseClient
|
8
|
-
class BaseResponse
|
9
|
-
extend ::Forwardable
|
10
|
-
|
11
|
-
attr_reader :raw_response
|
12
|
-
attr_reader :raw_body
|
13
|
-
attr_reader :client
|
14
|
-
attr_reader :request
|
15
|
-
|
16
|
-
def_delegators :raw_response, :status, :headers
|
17
|
-
alias_method :code, :status
|
18
|
-
|
19
|
-
def body
|
20
|
-
@body ||= ::JSON.parse(raw_body)
|
21
|
-
end
|
22
|
-
alias_method :to_json, :body
|
23
|
-
|
24
|
-
def initialize(client, request, raw_response)
|
25
|
-
@client = client
|
26
|
-
@request = request
|
27
|
-
@raw_response = raw_response
|
28
|
-
@raw_body = raw_response.body.to_s
|
29
|
-
end
|
30
|
-
|
31
|
-
def skip
|
32
|
-
body.dig('meta', 'skip')
|
33
|
-
end
|
34
|
-
|
35
|
-
def count
|
36
|
-
body.dig('meta', 'total')
|
37
|
-
end
|
38
|
-
|
39
|
-
def data
|
40
|
-
body['data']
|
41
|
-
end
|
42
|
-
|
43
|
-
def links
|
44
|
-
@links ||= OpenStruct.new(body['links'])
|
45
|
-
end
|
46
|
-
|
47
|
-
def collection_response?
|
48
|
-
data.is_a?(Array) ? true : false
|
49
|
-
end
|
50
|
-
|
51
|
-
def error_message
|
52
|
-
parsed_message =
|
53
|
-
begin
|
54
|
-
body.dig('error', 'message') || body.dig('message')
|
55
|
-
rescue ::JSON::ParserError
|
56
|
-
nil
|
57
|
-
end
|
58
|
-
parsed_message || "#{code}: #{raw_response.body}"
|
59
|
-
end
|
60
|
-
|
61
|
-
def next_page?
|
62
|
-
return false unless collection_response?
|
63
|
-
return false if count.nil?
|
64
|
-
|
65
|
-
page_items.length + skip < count
|
66
|
-
end
|
67
|
-
|
68
|
-
def next_page
|
69
|
-
return unless next_page?
|
70
|
-
|
71
|
-
@next_page ||= @client.get(
|
72
|
-
@request[:url],
|
73
|
-
(@request[:query] || {}).merge(next_page_query)
|
74
|
-
)
|
75
|
-
@next_page.assert_ok!
|
76
|
-
end
|
77
|
-
|
78
|
-
def assert_ok!
|
79
|
-
return self if code >= 200 && code < 300
|
80
|
-
|
81
|
-
raise ApiError[code], self
|
82
|
-
end
|
83
|
-
|
84
|
-
# This method has a bit of complexity that is better kept in one location
|
85
|
-
def each_page(&block)
|
86
|
-
raise ArgumentError, 'Not a collection response' unless collection_response?
|
87
|
-
|
88
|
-
ret =
|
89
|
-
Enumerator.new do |y|
|
90
|
-
y << self
|
91
|
-
|
92
|
-
if next_page?
|
93
|
-
next_page.each_page.each do |page|
|
94
|
-
y << page
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
if block_given?
|
100
|
-
ret.map(&block)
|
101
|
-
else
|
102
|
-
ret.lazy
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def items
|
107
|
-
return unless collection_response?
|
108
|
-
|
109
|
-
each_page.flat_map(&:page_items)
|
110
|
-
end
|
111
|
-
|
112
|
-
def first
|
113
|
-
raise ArgumentError, 'Not a collection response' unless collection_response?
|
114
|
-
|
115
|
-
page_items.first
|
116
|
-
end
|
117
|
-
|
118
|
-
def next_page_query
|
119
|
-
return unless collection_response?
|
120
|
-
|
121
|
-
{
|
122
|
-
skip: page_items.length + skip
|
123
|
-
}
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
gem 'http'
|
4
|
-
require 'http'
|
5
|
-
|
6
|
-
module WCC::JsonAPI
|
7
|
-
class BaseClient
|
8
|
-
class HttpAdapter
|
9
|
-
def call(url, query, headers = {}, proxy = {})
|
10
|
-
if proxy[:host]
|
11
|
-
HTTP[headers].via(proxy[:host], proxy[:port], proxy[:username], proxy[:password])
|
12
|
-
.get(url, params: query)
|
13
|
-
else
|
14
|
-
HTTP[headers].get(url, params: query)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
gem 'typhoeus'
|
5
|
-
require 'typhoeus'
|
6
|
-
|
7
|
-
class WCC::JsonAPI::BaseClient::TyphoeusAdapter
|
8
|
-
def get(url, params = {}, headers = {})
|
9
|
-
req = OpenStruct.new(params: params, headers: headers)
|
10
|
-
yield req if block_given?
|
11
|
-
Response.new(
|
12
|
-
Typhoeus.get(
|
13
|
-
url,
|
14
|
-
params: req.params,
|
15
|
-
headers: req.headers
|
16
|
-
)
|
17
|
-
)
|
18
|
-
end
|
19
|
-
|
20
|
-
def post(url, body, headers = {})
|
21
|
-
Response.new(
|
22
|
-
Typhoeus.post(
|
23
|
-
url,
|
24
|
-
body: body.is_a?(String) ? body : body.to_json,
|
25
|
-
headers: headers
|
26
|
-
)
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
def put(url, body, headers = {})
|
31
|
-
Response.new(
|
32
|
-
Typhoeus.put(
|
33
|
-
url,
|
34
|
-
body: body.is_a?(String) ? body : body.to_json,
|
35
|
-
headers: headers
|
36
|
-
)
|
37
|
-
)
|
38
|
-
end
|
39
|
-
|
40
|
-
def delete(url, query = {}, headers = {})
|
41
|
-
Response.new(
|
42
|
-
Typhoeus.delete(
|
43
|
-
url,
|
44
|
-
headers: headers
|
45
|
-
)
|
46
|
-
)
|
47
|
-
end
|
48
|
-
|
49
|
-
class Response < SimpleDelegator
|
50
|
-
def raw
|
51
|
-
__getobj__
|
52
|
-
end
|
53
|
-
|
54
|
-
def to_s
|
55
|
-
body&.to_s
|
56
|
-
end
|
57
|
-
|
58
|
-
def status
|
59
|
-
code
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|