resizing 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67c72ad237f9dbd4b471d9b249ed6fbc21cdb4c99a9ff8c86e3c4aec72233c21
4
- data.tar.gz: 67377bf88e061f477443d37c3d5f9362e1fc8546dd09336585e5b26820b13efe
3
+ metadata.gz: b2bdfaa73ba0bd1216d010dad515e8b23fe160b5b43d86501df1a53c76817798
4
+ data.tar.gz: 7f2360c0077fa45befefe90e2279190729314536022698793547747895619634
5
5
  SHA512:
6
- metadata.gz: 982272977e217042ba1cb332a918f8877667b87731a2587cecf285664ebf669f96e2265e254f43afb42a4d8101bfda07010e5790b2cce1440363924efa05d1ab
7
- data.tar.gz: fb4da505d292795e84ef79577029d0317ac199db177e0e91d51be75a2c92f89fe4bf2a3a9c0102da20c1552d1450f46ea0471065b6d10d3e8f777ead16367767
6
+ metadata.gz: 3aac8436eaf6bd4471d5dc3d5474dc4d3b8c75cf30c52bb7ed496ca57cdd3e6994847bafb9b95258f5baf2863114bd178ba81a5e3863123ea147cad8c7f3ad20
7
+ data.tar.gz: 82ba5efdd89fb3b3d5927a3470dc65541faefe1d0b5adf8498b642e9ecc893775c6a0f67cb0fde0be0f36be42f06217bfab2646442d568d3b548957d8d36e4b2
@@ -0,0 +1,149 @@
1
+ # Changelog
2
+
3
+ ## [v0.5.3](https://github.com/jksy/resizing-gem/tree/v0.5.3) (2020-10-15)
4
+
5
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.5.2...v0.5.3)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Feature/fix raise error [\#40](https://github.com/jksy/resizing-gem/pull/40) ([jksy](https://github.com/jksy))
10
+
11
+ ## [v0.5.2](https://github.com/jksy/resizing-gem/tree/v0.5.2) (2020-10-11)
12
+
13
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.5.1...v0.5.2)
14
+
15
+ ## [v0.5.1](https://github.com/jksy/resizing-gem/tree/v0.5.1) (2020-10-11)
16
+
17
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.5.0...v0.5.1)
18
+
19
+ ## [v0.5.0](https://github.com/jksy/resizing-gem/tree/v0.5.0) (2020-10-11)
20
+
21
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.4.2...v0.5.0)
22
+
23
+ ## [v0.4.2](https://github.com/jksy/resizing-gem/tree/v0.4.2) (2020-10-01)
24
+
25
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.4.1...v0.4.2)
26
+
27
+ **Merged pull requests:**
28
+
29
+ - fix raise error if tempfile is assinged [\#38](https://github.com/jksy/resizing-gem/pull/38) ([jksy](https://github.com/jksy))
30
+
31
+ ## [v0.4.1](https://github.com/jksy/resizing-gem/tree/v0.4.1) (2020-10-01)
32
+
33
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.2.1...v0.4.1)
34
+
35
+ **Closed issues:**
36
+
37
+ - pass original filename into Resizing [\#36](https://github.com/jksy/resizing-gem/issues/36)
38
+ - columnがnilの時はdefault\_urlを返す [\#33](https://github.com/jksy/resizing-gem/issues/33)
39
+
40
+ **Merged pull requests:**
41
+
42
+ - save original filename [\#37](https://github.com/jksy/resizing-gem/pull/37) ([jksy](https://github.com/jksy))
43
+ - columnがnilの時はdefault\_urlを返す \#33 [\#34](https://github.com/jksy/resizing-gem/pull/34) ([jksy](https://github.com/jksy))
44
+
45
+ ## [v0.2.1](https://github.com/jksy/resizing-gem/tree/v0.2.1) (2020-09-29)
46
+
47
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.4.0...v0.2.1)
48
+
49
+ ## [v0.4.0](https://github.com/jksy/resizing-gem/tree/v0.4.0) (2020-09-28)
50
+
51
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.3.2...v0.4.0)
52
+
53
+ **Merged pull requests:**
54
+
55
+ - change version string when put request with mockclient [\#32](https://github.com/jksy/resizing-gem/pull/32) ([jksy](https://github.com/jksy))
56
+ - add delete method [\#31](https://github.com/jksy/resizing-gem/pull/31) ([jksy](https://github.com/jksy))
57
+ - Feature/add identifier to public [\#30](https://github.com/jksy/resizing-gem/pull/30) ([jksy](https://github.com/jksy))
58
+ - add identifier [\#29](https://github.com/jksy/resizing-gem/pull/29) ([jksy](https://github.com/jksy))
59
+ - Feature/refactor public [\#28](https://github.com/jksy/resizing-gem/pull/28) ([jksy](https://github.com/jksy))
60
+ - divide to PublicId class [\#27](https://github.com/jksy/resizing-gem/pull/27) ([jksy](https://github.com/jksy))
61
+ - fix some bug & errors [\#26](https://github.com/jksy/resizing-gem/pull/26) ([jksy](https://github.com/jksy))
62
+ - fix mockclient [\#25](https://github.com/jksy/resizing-gem/pull/25) ([jksy](https://github.com/jksy))
63
+ - fix raise no method presented, no cotent type is presented [\#24](https://github.com/jksy/resizing-gem/pull/24) ([jksy](https://github.com/jksy))
64
+
65
+ ## [v0.3.2](https://github.com/jksy/resizing-gem/tree/v0.3.2) (2020-09-11)
66
+
67
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.3.1...v0.3.2)
68
+
69
+ **Merged pull requests:**
70
+
71
+ - fix raise error when call remove! with nil column [\#23](https://github.com/jksy/resizing-gem/pull/23) ([jksy](https://github.com/jksy))
72
+
73
+ ## [v0.3.1](https://github.com/jksy/resizing-gem/tree/v0.3.1) (2020-09-11)
74
+
75
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.3.0...v0.3.1)
76
+
77
+ ## [v0.3.0](https://github.com/jksy/resizing-gem/tree/v0.3.0) (2020-08-21)
78
+
79
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.2.0...v0.3.0)
80
+
81
+ **Merged pull requests:**
82
+
83
+ - Feature/fix mock client [\#22](https://github.com/jksy/resizing-gem/pull/22) ([jksy](https://github.com/jksy))
84
+
85
+ ## [v0.2.0](https://github.com/jksy/resizing-gem/tree/v0.2.0) (2020-08-12)
86
+
87
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.1.4...v0.2.0)
88
+
89
+ **Merged pull requests:**
90
+
91
+ - version 0.2.0 [\#21](https://github.com/jksy/resizing-gem/pull/21) ([jksy](https://github.com/jksy))
92
+ - support f\_auto, q\_auto parameter [\#20](https://github.com/jksy/resizing-gem/pull/20) ([jksy](https://github.com/jksy))
93
+
94
+ ## [v0.1.4](https://github.com/jksy/resizing-gem/tree/v0.1.4) (2020-08-08)
95
+
96
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.1.3...v0.1.4)
97
+
98
+ **Merged pull requests:**
99
+
100
+ - Feature/update gems and change default hostname [\#19](https://github.com/jksy/resizing-gem/pull/19) ([jksy](https://github.com/jksy))
101
+
102
+ ## [v0.1.3](https://github.com/jksy/resizing-gem/tree/v0.1.3) (2020-06-12)
103
+
104
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.1.2...v0.1.3)
105
+
106
+ **Merged pull requests:**
107
+
108
+ - fix error when record is not found [\#18](https://github.com/jksy/resizing-gem/pull/18) ([jksy](https://github.com/jksy))
109
+
110
+ ## [v0.1.2](https://github.com/jksy/resizing-gem/tree/v0.1.2) (2020-06-12)
111
+
112
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.1.1...v0.1.2)
113
+
114
+ **Merged pull requests:**
115
+
116
+ - ignore response if delete method is received 404 status code [\#17](https://github.com/jksy/resizing-gem/pull/17) ([jksy](https://github.com/jksy))
117
+
118
+ ## [v0.1.1](https://github.com/jksy/resizing-gem/tree/v0.1.1) (2020-06-12)
119
+
120
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/v0.1.0...v0.1.1)
121
+
122
+ **Merged pull requests:**
123
+
124
+ - Feature/fix raise error [\#16](https://github.com/jksy/resizing-gem/pull/16) ([jksy](https://github.com/jksy))
125
+ - change mysql port on docker-compose [\#9](https://github.com/jksy/resizing-gem/pull/9) ([jksy](https://github.com/jksy))
126
+
127
+ ## [v0.1.0](https://github.com/jksy/resizing-gem/tree/v0.1.0) (2020-06-07)
128
+
129
+ [Full Changelog](https://github.com/jksy/resizing-gem/compare/e817d9b883425ddae3e42439ccd1a9d2e270c363...v0.1.0)
130
+
131
+ **Merged pull requests:**
132
+
133
+ - add mock client for test environments [\#14](https://github.com/jksy/resizing-gem/pull/14) ([jksy](https://github.com/jksy))
134
+ - Feature/add test test for remove xxx [\#13](https://github.com/jksy/resizing-gem/pull/13) ([jksy](https://github.com/jksy))
135
+ - add rubocop & fix some tests [\#12](https://github.com/jksy/resizing-gem/pull/12) ([jksy](https://github.com/jksy))
136
+ - fix the column cant be update by null value [\#11](https://github.com/jksy/resizing-gem/pull/11) ([jksy](https://github.com/jksy))
137
+ - fix empty url when model is reload [\#10](https://github.com/jksy/resizing-gem/pull/10) ([jksy](https://github.com/jksy))
138
+ - fix return invalid url when model is reloaded [\#8](https://github.com/jksy/resizing-gem/pull/8) ([jksy](https://github.com/jksy))
139
+ - add circleci [\#7](https://github.com/jksy/resizing-gem/pull/7) ([jksy](https://github.com/jksy))
140
+ - divide some classes and add tests [\#6](https://github.com/jksy/resizing-gem/pull/6) ([jksy](https://github.com/jksy))
141
+ - fix connect host [\#5](https://github.com/jksy/resizing-gem/pull/5) ([jksy](https://github.com/jksy))
142
+ - Feature/carrier wave [\#4](https://github.com/jksy/resizing-gem/pull/4) ([jksy](https://github.com/jksy))
143
+ - faraday version is down. and some fix bugs [\#3](https://github.com/jksy/resizing-gem/pull/3) ([jksy](https://github.com/jksy))
144
+ - Feature/add default host [\#2](https://github.com/jksy/resizing-gem/pull/2) ([jksy](https://github.com/jksy))
145
+ - first implementation [\#1](https://github.com/jksy/resizing-gem/pull/1) ([jksy](https://github.com/jksy))
146
+
147
+
148
+
149
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/bash
2
+
3
+ github_changelog_generator -u jksy -p resizing-gem
@@ -13,7 +13,9 @@ module Resizing
13
13
 
14
14
  class Error < StandardError; end
15
15
  class ConfigurationError < Error; end
16
- class APIError < Error; end
16
+ class APIError < Error;
17
+ attr_accessor :decoded_body
18
+ end
17
19
 
18
20
  def self.configure
19
21
  raise ConfigurationError, 'Resizing.configure is not initialized' unless defined? @configure
@@ -32,7 +32,7 @@ module Resizing
32
32
  end
33
33
 
34
34
  def url(*args)
35
- return nil unless read_column.present?
35
+ return default_url unless read_column.present?
36
36
 
37
37
  transforms = args.map do |version|
38
38
  version = version.intern
@@ -40,17 +40,22 @@ module Resizing
40
40
  versions[version].transform_string
41
41
  end.compact
42
42
 
43
- "#{default_url}/#{transforms.join('/')}"
43
+ "#{build_url}/#{transforms.join('/')}"
44
44
  end
45
45
 
46
46
  def read_column
47
47
  model.read_attribute(serialization_column)
48
48
  end
49
49
 
50
- def default_url
50
+ def build_url
51
51
  "#{Resizing.configure.host}#{model.read_attribute(serialization_column)}"
52
52
  end
53
53
 
54
+ # need override this. if you want to return some url when target_column is nil
55
+ def default_url
56
+ nil
57
+ end
58
+
54
59
  def transform_string
55
60
  transforms = processors.map do |processor|
56
61
  transform_string_from processor
@@ -8,6 +8,7 @@ module Resizing
8
8
 
9
9
  def initialize(uploader, identifier = nil)
10
10
  @uploader = uploader
11
+ @file = nil
11
12
  @content_type = nil
12
13
  @public_id = Resizing::PublicId.new identifier
13
14
  end
@@ -76,6 +77,11 @@ module Resizing
76
77
  !!file
77
78
  end
78
79
 
80
+ def current_path
81
+ @current_path = model.send :read_attribute, serialization_column
82
+ end
83
+ alias path current_path
84
+
79
85
  def store(new_file)
80
86
  if new_file.is_a?(self.class)
81
87
  # new_file.copy_to(path)
@@ -88,15 +94,23 @@ module Resizing
88
94
  # guess content-type from extension
89
95
  @content_type ||= MIME::Types.type_for(new_file.path).first.content_type
90
96
  end
91
- @public_id = PublicId.new(model.send :read_attribute, serialization_column)
92
97
 
93
- image_id = if @public_id.empty?
94
- Resizing.configure.generate_image_id
95
- else
96
- @public_id.image_id
97
- end
98
+ original_filename = new_file.try(:original_filename) || new_file.try(:filename) || new_file.try(:path)
99
+ if original_filename.present?
100
+ original_filename = ::File.basename(original_filename)
101
+ end
98
102
 
99
- @response = Resizing.put(image_id, new_file.read, { content_type: @content_type })
103
+ content = if new_file.respond_to?(:to_io)
104
+ new_file.to_io.tap(&:rewind)
105
+ elsif new_file.respond_to?(:read) && new_file.respond_to?(:rewind)
106
+ new_file.read.tap do
107
+ new_file.rewind
108
+ end
109
+ else
110
+ new_file
111
+ end
112
+
113
+ @response = Resizing.post(content, { content_type: @content_type, filename: original_filename })
100
114
  @public_id = Resizing::PublicId.new(@response['public_id'])
101
115
 
102
116
  # force update column
@@ -176,12 +190,6 @@ module Resizing
176
190
  parameters.count == 2 && parameters[1].include?(:options)
177
191
  end
178
192
 
179
- # def store! sanitized_file
180
- # puts sanitized_file.inspect
181
- # client = Resizing::Client.new
182
- # @store_response = client.post(sanitized_file.to_file, {content_type: sanitized_file.content_type})
183
- # end
184
-
185
193
  def retrieve!(identifier)
186
194
  raise NotImplementedError, "retrieve! #{identifier}"
187
195
  end
@@ -50,9 +50,11 @@ module Resizing
50
50
 
51
51
  url = build_post_url
52
52
 
53
+ filename = gather_filename file_or_binary, options
54
+
53
55
  body = to_io(file_or_binary)
54
56
  params = {
55
- image: Faraday::UploadIO.new(body, options[:content_type])
57
+ image: Faraday::UploadIO.new(body, options[:content_type], filename)
56
58
  }
57
59
 
58
60
  response = http_client.post(url, params) do |request|
@@ -61,6 +63,8 @@ module Resizing
61
63
 
62
64
  result = handle_create_response(response)
63
65
  result
66
+ rescue Faraday::TimeoutError => e
67
+ handle_timeout_error e
64
68
  end
65
69
 
66
70
  def put(image_id, file_or_binary, options)
@@ -68,9 +72,11 @@ module Resizing
68
72
 
69
73
  url = build_put_url(image_id)
70
74
 
75
+ filename = gather_filename file_or_binary, options
76
+
71
77
  body = to_io(file_or_binary)
72
78
  params = {
73
- image: Faraday::UploadIO.new(body, options[:content_type])
79
+ image: Faraday::UploadIO.new(body, options[:content_type], filename)
74
80
  }
75
81
 
76
82
  response = http_client.put(url, params) do |request|
@@ -79,6 +85,8 @@ module Resizing
79
85
 
80
86
  result = handle_create_response(response)
81
87
  result
88
+ rescue Faraday::TimeoutError => e
89
+ handle_timeout_error e
82
90
  end
83
91
 
84
92
  def delete(image_id)
@@ -90,6 +98,8 @@ module Resizing
90
98
 
91
99
  result = handle_delete_response(response)
92
100
  result
101
+ rescue Faraday::TimeoutError => e
102
+ handle_timeout_error e
93
103
  end
94
104
 
95
105
  def metadata(image_id, options = {})
@@ -101,6 +111,8 @@ module Resizing
101
111
 
102
112
  result = handle_metadata_response(response)
103
113
  result
114
+ rescue Faraday::TimeoutError => e
115
+ handle_timeout_error e
104
116
  end
105
117
 
106
118
  private
@@ -113,6 +125,11 @@ module Resizing
113
125
  "#{config.host}/projects/#{config.project_id}/upload/images/"
114
126
  end
115
127
 
128
+ def gather_filename file_or_binary, options
129
+ filename = options[:filename]
130
+ filename ||= file_or_binary.respond_to?(:path) ? File.basename(file_or_binary.path) : nil
131
+ end
132
+
116
133
  def build_put_url(image_id)
117
134
  "#{config.host}/projects/#{config.project_id}/upload/images/#{image_id}"
118
135
  end
@@ -136,13 +153,13 @@ module Resizing
136
153
  end
137
154
 
138
155
  def to_io(data)
156
+ return data.to_io if data.respond_to? :to_io
157
+
139
158
  case data
140
- when IO
141
- data
142
159
  when String
143
160
  StringIO.new(data)
144
161
  else
145
- raise ArgumentError, 'file_or_binary is required IO class or String'
162
+ raise ArgumentError, "file_or_binary is required IO class or String:#{data.class}"
146
163
  end
147
164
  end
148
165
 
@@ -157,7 +174,10 @@ module Resizing
157
174
  when HTTP_STATUS_OK, HTTP_STATUS_CREATED
158
175
  JSON.parse(response.body)
159
176
  else
160
- raise APIError, "invalid http status code #{response.status}"
177
+ result = JSON.parse(response.body) rescue {}
178
+ err = APIError.new("invalid http status code #{response.status}")
179
+ err.decoded_body = result
180
+ raise err
161
181
  end
162
182
  end
163
183
 
@@ -168,7 +188,10 @@ module Resizing
168
188
  when HTTP_STATUS_OK, HTTP_STATUS_NOT_FOUND
169
189
  JSON.parse(response.body)
170
190
  else
171
- raise APIError, "invalid http status code #{response.status}"
191
+ result = JSON.parse(response.body) rescue {}
192
+ err = APIError.new("invalid http status code #{response.status}")
193
+ err.decoded_body = result
194
+ raise err
172
195
  end
173
196
  end
174
197
 
@@ -179,8 +202,16 @@ module Resizing
179
202
  when HTTP_STATUS_OK, HTTP_STATUS_NOT_FOUND
180
203
  JSON.parse(response.body)
181
204
  else
182
- raise APIError, "invalid http status code #{response.status}"
205
+ result = JSON.parse(response.body) rescue {}
206
+ err = APIError.new("invalid http status code #{response.status}")
207
+ err.decoded_body = result
208
+ raise err
183
209
  end
184
210
  end
211
+
212
+ def handle_timeout_error error
213
+ # error: Faraday::TimeoutError
214
+ raise APIError.new("TimeoutError: #{error.inspect}")
215
+ end
185
216
  end
186
217
  end
@@ -42,7 +42,7 @@ module Resizing
42
42
  end
43
43
 
44
44
  def library_root
45
- File.join(File.dirname(__FILE__), '..', '..')
45
+ @library_root ||= File.expand_path('../../../', __FILE__)
46
46
  end
47
47
  end
48
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Resizing
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -10,8 +10,8 @@ module Resizing
10
10
 
11
11
  @configuration_template = {
12
12
  host: 'http://192.168.56.101:5000',
13
- project_id: '098a2a0d-c387-4135-a071-1254d6d7e70a',
14
- secret_token: '4g1cshg2lq8j93ufhvqrpjswxmtjz12yhfvq6w79jpwi7cr7nnknoqgwzkwerbs6',
13
+ project_id: 'e06e710d-f026-4dcf-b2c0-eab0de8bb83f',
14
+ secret_token: 'ewbym2r1pk49x1d2lxdbiiavnqp25j2kh00hsg3koy0ppm620x5mhlmgl3rq5ci8',
15
15
  open_timeout: 10,
16
16
  response_timeout: 20
17
17
  }
@@ -70,13 +70,31 @@ module Resizing
70
70
  assert_equal('jpg', model.resizing_picture.format)
71
71
  end
72
72
 
73
+ def test_url_is_return_default_url_as_nil
74
+ model = TestModel.new
75
+ model.save!
76
+ assert_nil model.resizing_picture_url
77
+ end
78
+
79
+ def test_url_is_return_default_url
80
+ model = TestModelWithDefaultURL.new
81
+ model.save!
82
+ assert_equal('http://example.com/test.jpg', model.resizing_picture_url)
83
+ end
84
+
85
+ def test_is_successful
86
+ model = prepare_model_with_tempfile TestModel
87
+ model.save!
88
+ # assert_equal('http://192.168.56.101:5000/projects/098a2a0d-c387-4135-a071-1254d6d7e70a/upload/images/28c49144-c00d-4cb5-8619-98ce95977b9c/v1Id850__tqNsnoGWWUibtIBZ5NgjV45M/', model.resizing_picture_url)
89
+ end
90
+
73
91
  def expect_url
74
- 'http://192.168.56.101:5000/projects/098a2a0d-c387-4135-a071-1254d6d7e70a/'+
75
- 'upload/images/28c49144-c00d-4cb5-8619-98ce95977b9c/v1Id850__tqNsnoGWWUibtIBZ5NgjV45M'
92
+ 'http://192.168.56.101:5000/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/'+
93
+ 'upload/images/14ea7aac-a194-4330-931f-6b562aec413d/v_8c5lEhDB5RT3PZp1Fn5PYGm9YVx_x0e'
76
94
  end
77
95
 
78
96
  def prepare_model model
79
- VCR.use_cassette 'carrier_wave_test/save' do
97
+ VCR.use_cassette 'carrier_wave_test/save', record: :once do
80
98
  SecureRandom.stub :uuid, '28c49144-c00d-4cb5-8619-98ce95977b9c' do
81
99
  model = model.new
82
100
  file = File.open('test/data/images/sample1.jpg', 'r')
@@ -91,5 +109,16 @@ module Resizing
91
109
  end
92
110
  end
93
111
  end
112
+
113
+ def prepare_model_with_tempfile model
114
+ VCR.use_cassette 'carrier_wave_test/save', record: :once do
115
+ SecureRandom.stub :uuid, '28c49144-c00d-4cb5-8619-98ce95977b9c' do
116
+ model = model.new
117
+ file = File.open('test/data/images/sample1.jpg', 'r')
118
+ model.resizing_picture = file
119
+ return model
120
+ end
121
+ end
122
+ end
94
123
  end
95
124
  end