afmotion 0.9.0 → 2.0.0.rc1

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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NWRhODIzZDU2NDU2NTgyODMyYmFlNTBhMWM4N2ZkODg1YzQ0NzI1Nw==
5
- data.tar.gz: !binary |-
6
- NTg1ZGUwYjNkZmViM2EyZWNlYTJiNmFjM2E0ZDc2YzUxNGJmMGQ5OQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- ZTBhZDVmZjVhYmM5ZjU0OTIyMWUwMzEzZjQ0N2JkOGIzYWE1NmNiNzkzZDZh
10
- ZjM1N2M5ZDQxYjVkYTllODU4MzJkYmU4MTM2NzAwMjM3OTQ2ODU4MTdkNzI3
11
- N2U2N2YwMjczN2MxZjk2Mzg2ZjAzODUzYmRkMjFkOTkzZjYyNDI=
12
- data.tar.gz: !binary |-
13
- ZGIwZGFhYjI0NzYxNGIxZWI3MDQ0NzlmZjZkODNmMTQ3NzUxNjdhNTA4NGM5
14
- M2U1NTgyZTdhMWQwZjhiZjczMjFiMWRhYWQ3MjA5MmMzNzJmNTZmMWY0NGM1
15
- MjllNTE5Mzc2YmUxZmY1MGI0ZjlkZGI3ZTkyNzM5ZTFlNmNjOTQ=
2
+ SHA1:
3
+ metadata.gz: 6272282a85e6fd949a516177786517a7be33bc12
4
+ data.tar.gz: c60b1826698311ed4f8d1d249fe55b67e1df332b
5
+ SHA512:
6
+ metadata.gz: a353b2ecc16ac721bc08bc893eb5da6001a3dcb0c0b0616dbfc47c43a02047739cead584e6d8393aaa1531b8b92ee2ad8b4d53bc313d1f4536ff45d9223b2476
7
+ data.tar.gz: 42d31e7089ce294031b79b3e37cd1483138f7b9f998369d5677eff2350b972b5dd10914bee192f472680de1f126dac72dba8846bcf22144814f4e751252b9733
data/AFMotion.gemspec CHANGED
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
16
  s.require_paths = ["lib"]
17
17
 
18
- s.add_dependency "motion-cocoapods", "~> 1.3.6"
18
+ s.add_dependency "motion-cocoapods", "~> 1.4.0"
19
+ s.add_dependency "motion-require", "~> 0.0.7"
19
20
  s.add_development_dependency 'rake'
20
21
  end
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- afmotion (0.9.0)
5
- motion-cocoapods (~> 1.3.6)
4
+ afmotion (2.0.0.rc1)
5
+ motion-cocoapods (~> 1.4.0)
6
+ motion-require (~> 0.0.7)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -11,33 +12,34 @@ GEM
11
12
  i18n (~> 0.6, >= 0.6.4)
12
13
  multi_json (~> 1.0)
13
14
  claide (0.3.2)
14
- cocoapods (0.23.0)
15
- activesupport (~> 3.2.13)
15
+ cocoapods (0.26.2)
16
+ activesupport (~> 3.2)
16
17
  claide (~> 0.3.2)
17
- cocoapods-core (= 0.23.0)
18
- cocoapods-downloader (~> 0.1.2)
18
+ cocoapods-core (= 0.26.2)
19
+ cocoapods-downloader (~> 0.2.0)
19
20
  colored (~> 1.2)
20
21
  escape (~> 0.0.4)
21
- json (~> 1.8.0)
22
- open4 (~> 1.3.0)
23
- xcodeproj (~> 0.9.0)
24
- cocoapods-core (0.23.0)
25
- activesupport (~> 3.2.13)
26
- json (~> 1.8.0)
27
- nap (~> 0.5.1)
28
- cocoapods-downloader (0.1.2)
22
+ json (~> 1.8)
23
+ open4 (~> 1.3)
24
+ xcodeproj (~> 0.13.0)
25
+ cocoapods-core (0.26.2)
26
+ activesupport (~> 3.0)
27
+ json (~> 1.8)
28
+ nap (~> 0.5)
29
+ cocoapods-downloader (0.2.0)
29
30
  colored (1.2)
30
31
  escape (0.0.4)
31
32
  i18n (0.6.5)
32
33
  json (1.8.0)
33
- motion-cocoapods (1.3.6)
34
- cocoapods (>= 0.17.0)
35
- multi_json (1.7.9)
34
+ motion-cocoapods (1.4.0)
35
+ cocoapods (>= 0.26.2)
36
+ motion-require (0.0.7)
37
+ multi_json (1.8.0)
36
38
  nap (0.5.1)
37
39
  open4 (1.3.0)
38
- rake (0.9.2.2)
39
- xcodeproj (0.9.0)
40
- activesupport (~> 3.2.13)
40
+ rake (10.1.0)
41
+ xcodeproj (0.13.1)
42
+ activesupport (~> 3.0)
41
43
  colored (~> 1.2)
42
44
 
43
45
  PLATFORMS
data/README.md CHANGED
@@ -18,26 +18,51 @@ end
18
18
 
19
19
  ### Web Services
20
20
 
21
- If you're interacting with a web service, you can use `AFHTTPClient` with this nice wrapper:
22
-
23
21
  ```ruby
24
- # DSL Mapping to properties of AFHTTPClient
25
22
 
26
- AFMotion::Client.build_shared("https://alpha-api.app.net/") do
27
- header "Accept", "application/json"
23
+ @client = AFMotion::... # create your client
28
24
 
29
- operation :json
30
- end
31
-
32
- AFMotion::Client.shared.get("stream/0/posts/stream/global") do |result|
25
+ @client.get("stream/0/posts/stream/global") do |result|
33
26
  if result.success?
34
- p result.object
27
+ p (result.operation || result.task) # depending on your client
35
28
  elsif result.failure?
36
29
  p result.error.localizedDescription
37
30
  end
38
31
  end
39
32
  ```
40
33
 
34
+ You can either use `AFMotion::Client` or `AFMotion::SessionClient` to group similar requests. They have identical APIs, except for their creation and that their request `result` objects contain either `result.operation` (for `::Client`) or `result.task` (for `::SessionClient`).
35
+
36
+ #### AFMotion::Client
37
+
38
+ If you're interacting with a web service, you can use [`AFHTTPRequestOperationManager`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Classes/AFHTTPRequestOperationManager.html) with this nice wrapper:
39
+
40
+ ```ruby
41
+ # DSL Mapping to properties of AFHTTPRequestOperationManager
42
+
43
+ @client = AFMotion::Client.build("https://alpha-api.app.net/") do
44
+ header "Accept", "application/json"
45
+
46
+ response_serializer :json
47
+ end
48
+ ```
49
+
50
+ #### AFMotion::SessionClient
51
+
52
+ If you're using iOS7, you can use [`AFHTTPSessionManager`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Classes/AFHTTPSessionManager.html):
53
+
54
+ ```ruby
55
+ # DSL Mapping to properties of AFHTTPSessionManager
56
+
57
+ @client = AFMotion::SessionClient.build("https://alpha-api.app.net/") do
58
+ session_configuration :default
59
+
60
+ header "Accept", "application/json"
61
+
62
+ response_serializer :json
63
+ end
64
+ ```
65
+
41
66
  ### Images
42
67
 
43
68
  Loading images from the internet is pretty common. AFNetworking's existing methods aren't bad at all, but just incase you want extra Ruby:
@@ -66,18 +91,6 @@ You can also request arbitrary images:
66
91
 
67
92
  2. `require 'afmotion'` or add to your `Gemfile`
68
93
 
69
- 3. In your `Rakefile`, add:
70
-
71
- ```ruby
72
- Motion::Project::App.setup do |app|
73
- ...
74
-
75
- app.pods do
76
- pod 'AFNetworking'
77
- end
78
- end
79
- ```
80
-
81
94
  ## Overview
82
95
 
83
96
  ### Results
@@ -104,22 +117,6 @@ AFMotion::some_function do |result|
104
117
  end
105
118
  ```
106
119
 
107
- ### Operations
108
-
109
- There are wrappers for each `AFURLConnectionOperation` subclass, each of the form:
110
-
111
- ```ruby
112
- AFMotion::Operation::[Operation Type].for_request(ns_url_request) do |result|
113
- ...
114
- end
115
- ```
116
-
117
- - `AFMotion::Operation::HTTP.for_request...`
118
- - `AFMotion::Operation::JSON.for_request...`
119
- - `AFMotion::Operation::XML.for_request...`
120
- - `AFMotion::Operation::PLIST.for_request...`
121
- - `AFMotion::Operation::Image.for_request...`
122
-
123
120
  ### One-off Requests
124
121
 
125
122
  There are wrappers which automatically run a URL request for a given URL and HTTP method, of the form:
@@ -146,23 +143,43 @@ end
146
143
 
147
144
  ### HTTP Client
148
145
 
149
- If you're constantly accesing a web service, it's a good idea to use an `AFHTTPClient`. Things lets you add a common base URL and request headers to all the requests issued through it, like so:
146
+ If you're constantly accesing a web service, it's a good idea to use an `AFHTTPRequestOperationManager`. Things lets you add a common base URL and request headers to all the requests issued through it, like so:
150
147
 
151
148
  ```ruby
152
149
  client = AFMotion::Client.build("https://alpha-api.app.net/") do
153
150
  header "Accept", "application/json"
154
151
 
155
- operation :json
152
+ response_serializer :json
156
153
  end
157
154
 
158
155
  client.get("stream/0/posts/stream/global") do |result|
156
+ # result.operation exists
157
+ ...
158
+ end
159
+ ```
160
+
161
+ If you're using iOS7, you can use [`AFHTTPSessionManager`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Classes/AFHTTPSessionManager.html):
162
+
163
+ ```ruby
164
+ # DSL Mapping to properties of AFHTTPSessionManager
165
+
166
+ client = AFMotion::SessionClient.build("https://alpha-api.app.net/") do
167
+ session_configuration :default
168
+
169
+ header "Accept", "application/json"
170
+
171
+ response_serializer :json
172
+ end
173
+
174
+ client.get("stream/0/posts/stream/global") do |result|
175
+ # result.task exists
159
176
  ...
160
177
  end
161
178
  ```
162
179
 
163
180
  If you're constantly used one web service, you can use the `AFMotion::Client.shared` variable have a common reference. It can be set like a normal variable or created with `AFMotion::Client.build_shared`.
164
181
 
165
- `AFHTTPClient` supports methods of the form `AFHTTPClient#get/post/put/patch/delete(url, request_parameters)`. The `request_parameters` is a hash containing your parameters to attach as the request body or URL parameters, depending on request type. For example:
182
+ `AFHTTPRequestOperationManager` & `AFHTTPSessionManager` support methods of the form `Client#get/post/put/patch/delete(url, request_parameters)`. The `request_parameters` is a hash containing your parameters to attach as the request body or URL parameters, depending on request type. For example:
166
183
 
167
184
  ```ruby
168
185
  client.get("users", id: 1) do |result|
@@ -176,14 +193,14 @@ end
176
193
 
177
194
  #### Multipart Requests
178
195
 
179
- `AFHTTPClient` supports multipart form requests (i.e. for image uploading). Simply prepend `multipart!` to any other request method and it'll convert your parameters into properly encoded multipart data:
196
+ `AFHTTPRequestOperationManager` & `AFHTTPSessionManager` support multipart form requests (i.e. for image uploading) - simply use `multipart_post` and it'll convert your parameters into properly encoded multipart data. For all other types of request data, use the `form_data` object passed to your callback:
180
197
 
181
198
  ```ruby
182
199
  # an instance of UIImage
183
200
  image = my_function.get_image
184
201
  data = UIImagePNGRepresentation(image)
185
202
 
186
- client.multipart!.post("avatars") do |result, form_data|
203
+ client.multipart_post("avatars") do |result, form_data|
187
204
  if form_data
188
205
  # Called before request runs
189
206
  # see: https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ
@@ -196,17 +213,20 @@ client.multipart!.post("avatars") do |result, form_data|
196
213
  end
197
214
  ```
198
215
 
216
+ This is an instance of [`AFMultipartFormData`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Protocols/AFMultipartFormData.html).
217
+
199
218
  If you want to track upload progress, you can add a third callback argument which returns the upload percentage between 0.0 and 1.0:
200
219
 
201
220
  ```ruby
202
- client.multipart!.post("avatars") do |result, form_data, progress|
221
+ client.multipart_post("avatars") do |result, form_data, progress|
203
222
  if form_data
204
223
  # Called before request runs
205
224
  # see: https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ
206
225
  form_data.appendPartWithFileData(data, name: "avatar", fileName:"avatar.png", mimeType: "image/png")
207
226
  elsif progress
208
- # 0.0 <= progress <= 1.0
227
+ # 0.0 < progress < 1.0
209
228
  my_widget.update_progress(progress)
229
+ else
210
230
  ...
211
231
  end
212
232
  ```
@@ -226,26 +246,15 @@ client.headers.delete "Accept"
226
246
  #=> "application/something_else"
227
247
  ```
228
248
 
229
- #### Client Operations
230
-
231
- If you want to grab an `AFURLConnectionOperation` from your client instance, use `create_operation` or `create_multipart_operation`:
232
-
233
- ```ruby
234
- operation = client.create_operation(:get, "http://google.com", {q: "hello"}) do |result|
235
- end
236
-
237
- multipart_operation = client.create_multipart_operation(:get, "http://google.com", {q: "hello"}) do |result, form_data, progress|
238
- end
239
-
240
- # elsewhere
241
- client.enqueueHTTPRequestOperation(operation)
242
- ```
243
-
244
249
  #### Client Building DSL
245
250
 
246
- The `AFMotion::Client` DSL allows the following properties:
251
+ The `AFMotion::Client` & `AFMotion::SessionClient` DSLs allows the following properties:
247
252
 
248
253
  - `header(header, value)`
249
254
  - `authorization(username: ___, password: ____)` for HTTP Basic auth, or `authorization(token: ____)` for Token based auth.
250
- - `operation(operation_type)`. Allows you to set a common operation class for all your client's requests. So if your API is always going to be JSON, you should set `operation(:json)`. Accepts `:json`, `:plist`, `:xml`, or `:http`
251
- - `parameter_encoding(encoding)`. Allows you to set a body format for requests parameters. For example, when you send a POST request you might want the parameters to be encoding as a JSON object instead of the traditional `key=val` format. Accepts `:json`, `:plist`, and `:form` (normal encoding).
255
+ - `request_serializer(serializer)`. Allows you to set an [`AFURLRequestSerialization`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Protocols/AFURLRequestSerialization.html) for all your client's requests, which determines how data is encoded on the way to the server. So if your API is always going to be JSON, you should set `operation(:json)`. Accepts `:json` and `:plist`, or any instance of `AFURLRequestSerialization`.
256
+ - `response_serializer(serializer)`. Allows you to set an [`AFURLResponseSerialization`](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Protocols/AFURLResponseSerialization.html), which determines how data is decoded once the server respnds. Accepts `:json`, `:xml`, `:plist`, `:image`, `:http`, or any instance of `AFURLResponseSerialization`.
257
+
258
+ For `AFMotion::SessionClient` only:
259
+
260
+ - `session_configuration(session_configuration, identifier = nil)`. Allows you to set the [`NSURLSessionConfiguration`](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionConfiguration_class/Reference/Reference.html#//apple_ref/occ/cl/NSURLSessionConfiguration). Accepts `:default`, `:ephemeral`, `:background` (with the `identifier` as a String), or an instance of `NSURLSessionConfiguration`.
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  $:.unshift("/Library/RubyMotion/lib")
3
- require 'motion/project'
3
+ require 'motion/project/template/ios'
4
4
  require "bundler/gem_tasks"
5
5
  require "bundler/setup"
6
6
  Bundler.require :default
@@ -11,8 +11,4 @@ require './lib/afmotion'
11
11
  Motion::Project::App.setup do |app|
12
12
  # Use `rake config' to see complete project settings.
13
13
  app.name = 'AFMotion'
14
-
15
- app.pods do
16
- pod "AFNetworking"
17
- end
18
14
  end
@@ -0,0 +1,90 @@
1
+ module AFMotion
2
+ module ClientShared
3
+ def headers
4
+ requestSerializer.headers
5
+ end
6
+
7
+ def all_headers
8
+ requestSerializer.HTTPRequestHeaders
9
+ end
10
+
11
+ def authorization=(authorization)
12
+ requestSerializer.authorization = authorization
13
+ end
14
+
15
+ def multipart_post(path, parameters = {}, &callback)
16
+ create_multipart_operation(path, parameters, &callback)
17
+ end
18
+
19
+ def create_multipart_operation(path, parameters = {}, &callback)
20
+ parameters = AFMotion::MultipartParametersWrapper.new(parameters)
21
+ inner_callback = Proc.new do |result, form_data, bytes_written_now, total_bytes_written, total_bytes_expect|
22
+
23
+ case callback.arity
24
+ when 1
25
+ callback.call(result)
26
+ when 2
27
+ callback.call(result, form_data)
28
+ when 3
29
+ progress = nil
30
+ if total_bytes_written && total_bytes_expect
31
+ progress = total_bytes_written.to_f / total_bytes_expect.to_f
32
+ end
33
+ callback.call(result, form_data, progress)
34
+ when 5
35
+ callback.call(result, form_data, bytes_written_now, total_bytes_written, total_bytes_expect)
36
+ end
37
+ end
38
+
39
+ multipart_callback = nil
40
+ if callback.arity == 2
41
+ multipart_callback = lambda { |formData|
42
+ inner_callback.call(nil, formData)
43
+ }
44
+ end
45
+
46
+ upload_callback = nil
47
+ if callback.arity > 2
48
+ upload_callback = lambda { |bytes_written_now, total_bytes_written, total_bytes_expect|
49
+ inner_callback.call(nil, nil, bytes_written_now, total_bytes_written, total_bytes_expect)
50
+ }
51
+ end
52
+
53
+ operation = self.POST(path,
54
+ parameters: parameters,
55
+ constructingBodyWithBlock: multipart_callback,
56
+ success: lambda {|operation, responseObject|
57
+ result = AFMotion::HTTPResult.new(operation, responseObject, nil)
58
+ inner_callback.call(result)
59
+ }, failure: lambda {|operation, error|
60
+ result = AFMotion::HTTPResult.new(operation, nil, error)
61
+ inner_callback.call(result)
62
+ })
63
+ if upload_callback
64
+ operation.setUploadProgressBlock(upload_callback)
65
+ end
66
+ operation
67
+ end
68
+
69
+ def create_operation(http_method, path, parameters = {}, &callback)
70
+ method_signature = "#{http_method.upcase}:parameters:success:failure:"
71
+ method = self.method(method_signature)
72
+ success_block = AFMotion::Operation.success_block_for_http_method(http_method, callback)
73
+ operation = method.call(path, parameters, success_block, AFMotion::Operation.failure_block(callback))
74
+ end
75
+
76
+ alias_method :create_task, :create_operation
77
+
78
+ private
79
+ # To force RubyMotion pre-compilation of these methods
80
+ def dummy
81
+ self.GET("", parameters: nil, success: nil, failure: nil)
82
+ self.HEAD("", parameters: nil, success: nil, failure: nil)
83
+ self.POST("", parameters: nil, success: nil, failure: nil)
84
+ self.POST("", parameters: nil, constructingBodyWithBlock: nil, success: nil, failure: nil)
85
+ self.PUT("", parameters: nil, success: nil, failure: nil)
86
+ self.DELETE("", parameters: nil, success: nil, failure: nil)
87
+ self.PATCH("", parameters: nil, success: nil, failure: nil)
88
+ end
89
+ end
90
+ end
data/lib/afmotion/http.rb CHANGED
@@ -1,92 +1,52 @@
1
- module AFMotion
2
- module HTTPBuilder
3
- def self.included(base)
4
- AFMotion::HTTP_METHODS.each do |method|
5
- base.send(:define_singleton_method, method, -> (request_or_url, parameters = {}, &callback) do
6
- request = request_or_url
7
- if !request.is_a?(NSURLRequest)
8
- request = NSMutableURLRequest.requestWithURL(request_or_url.to_url)
9
- request.HTTPMethod = method.upcase
10
- if [:get, :head].member? method.downcase.to_sym
11
- request.HTTPShouldUsePipelining = true
12
- end
13
- # SEE NSURLRequest_params.rb
14
- request.parameters = parameters.merge(__encoding__: self.parameter_encoding)
15
- end
16
-
17
- operation = (self.request_module.for_request(request) do |result|
18
- callback.call(result)
19
- end)
1
+ motion_require 'version'
20
2
 
21
- operation.start
22
- operation
23
- end)
3
+ module AFMotion
4
+ class HTTP
5
+ def self.operation_manager
6
+ @operation_manager ||= begin
7
+ manager = AFHTTPRequestOperationManager.manager
8
+ configure_manager(manager)
9
+ manager
24
10
  end
25
11
  end
26
- end
27
-
28
- module HTTP
29
- include AFMotion::HTTPBuilder
30
12
 
31
- module_function
32
- def request_module
33
- AFMotion::Operation::HTTP
34
- end
35
-
36
- def parameter_encoding
37
- AFFormURLParameterEncoding
13
+ def self.configure_manager(manager)
38
14
  end
39
15
  end
40
16
 
41
- module JSON
42
- include AFMotion::HTTPBuilder
43
-
44
- module_function
45
- def request_module
46
- AFMotion::Operation::JSON
47
- end
48
-
49
- def parameter_encoding
50
- AFJSONParameterEncoding
17
+ class JSON < HTTP
18
+ def self.configure_manager(manager)
19
+ manager.json!
51
20
  end
52
21
  end
53
22
 
54
- module XML
55
- include AFMotion::HTTPBuilder
56
-
57
- module_function
58
- def request_module
59
- AFMotion::Operation::XML
60
- end
61
-
62
- def parameter_encoding
63
- AFFormURLParameterEncoding
23
+ class XML < HTTP
24
+ def self.configure_manager(manager)
25
+ manager.xml!
64
26
  end
65
27
  end
66
28
 
67
- module PLIST
68
- include AFMotion::HTTPBuilder
69
-
70
- module_function
71
- def request_module
72
- AFMotion::Operation::PLIST
73
- end
74
-
75
- def parameter_encoding
76
- AFPropertyListParameterEncoding
29
+ class PLIST < HTTP
30
+ def self.configure_manager(manager)
31
+ manager.plist!
77
32
  end
78
33
  end
79
34
 
80
- module Image
81
- include AFMotion::HTTPBuilder
82
-
83
- module_function
84
- def request_module
85
- AFMotion::Operation::Image
35
+ class Image < HTTP
36
+ def self.configure_manager(operation)
37
+ operation.image!
86
38
  end
39
+ end
87
40
 
88
- def parameter_encoding
89
- AFFormURLParameterEncoding
41
+ [HTTP, JSON, XML, PLIST, Image].each do |base|
42
+ AFMotion::HTTP_METHODS.each do |method_name|
43
+ method_signature = "#{method_name.to_s.upcase}:parameters:success:failure:"
44
+ base.define_singleton_method(method_name, -> (url, parameters = {}, &callback) do
45
+ base.operation_manager.send(method_signature, url,
46
+ parameters,
47
+ AFMotion::Operation.success_block_for_http_method(method_name, callback),
48
+ AFMotion::Operation.failure_block(callback))
49
+ end)
90
50
  end
91
51
  end
92
52
  end