playwright-ruby-client 1.17.0 → 1.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/docs/api/api_request.md +7 -0
  3. data/documentation/docs/api/api_request_context.md +137 -0
  4. data/documentation/docs/api/api_response.md +90 -0
  5. data/documentation/docs/api/browser_context.md +4 -0
  6. data/documentation/docs/include/api_coverage.md +30 -13
  7. data/lib/playwright/api_response_impl.rb +73 -0
  8. data/lib/playwright/channel_owners/api_request_context.rb +232 -0
  9. data/lib/playwright/channel_owners/browser_context.rb +3 -1
  10. data/lib/playwright/channel_owners/page.rb +4 -0
  11. data/lib/playwright/version.rb +2 -2
  12. data/lib/playwright_api/android.rb +6 -6
  13. data/lib/playwright_api/android_device.rb +8 -8
  14. data/lib/playwright_api/api_request.rb +18 -0
  15. data/lib/playwright_api/api_request_context.rb +14 -14
  16. data/lib/playwright_api/api_response.rb +68 -0
  17. data/lib/playwright_api/browser.rb +6 -6
  18. data/lib/playwright_api/browser_context.rb +12 -12
  19. data/lib/playwright_api/browser_type.rb +6 -6
  20. data/lib/playwright_api/cdp_session.rb +6 -6
  21. data/lib/playwright_api/console_message.rb +6 -6
  22. data/lib/playwright_api/dialog.rb +6 -6
  23. data/lib/playwright_api/element_handle.rb +6 -6
  24. data/lib/playwright_api/frame.rb +6 -6
  25. data/lib/playwright_api/js_handle.rb +6 -6
  26. data/lib/playwright_api/page.rb +15 -10
  27. data/lib/playwright_api/playwright.rb +6 -6
  28. data/lib/playwright_api/request.rb +6 -6
  29. data/lib/playwright_api/response.rb +6 -6
  30. data/lib/playwright_api/route.rb +6 -6
  31. data/lib/playwright_api/selectors.rb +6 -6
  32. data/lib/playwright_api/web_socket.rb +6 -6
  33. data/lib/playwright_api/worker.rb +8 -8
  34. data/playwright.gemspec +1 -1
  35. metadata +11 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35d4a4e4ac3aa460997a5a9cd44b5272142ba89b480908f0e429fb97d7968337
4
- data.tar.gz: 2a577b8683a2eba60ed97b88b183bd351477cbde0874b8d06d287be0ec4664e8
3
+ metadata.gz: 9407fc7093bb4cdb7539eec09b879e4bfd665626801594c764798ae41d3c7823
4
+ data.tar.gz: e6047ebe532d15fa5600986ae3fc9f42a23ea670923e1ec8fac6f6ecbb7f01c4
5
5
  SHA512:
6
- metadata.gz: 283b87141e9ab9090488684973741983506da16d9ea991945589a1177cd99a5cf2c3b65b35770cc95a6ed3645514c35c9925e75aaffb949d58cbe6a47380c334
7
- data.tar.gz: 7a6071b83e537784b58e6b350c1654a7dfa90bc99576f8ae1780c2104b30242ddd2e9651f6eb9f880a3a777e9345a5706373fbd66b64cc32739ce10cb389b5fb
6
+ metadata.gz: 70b935777666f43236ec9df9b8462ebebf7fb883cde2d57e167d3342a18280a86e286e99e206f47e6ee2c739f23f6afe43daa2def9ea0a322b5e09f3b36bfc81
7
+ data.tar.gz: 3392ad01696c2c22ba411d7e4821e0bf0a2f641526481201927ff6510d7bb077c9fb6b20fcafabd55e5067ff7b1153b950d6b3c532c0bd14d77b9a0b3ac7c4c0
@@ -0,0 +1,7 @@
1
+ ---
2
+ sidebar_position: 10
3
+ ---
4
+
5
+ # APIRequest
6
+
7
+ Not Implemented
@@ -8,3 +8,140 @@ This API is used for the Web API testing. You can use it to trigger API endpoint
8
8
  environment or the service to your e2e test. When used on [Page](./page) or a [BrowserContext](./browser_context), this API will automatically use
9
9
  the cookies from the corresponding [BrowserContext](./browser_context). This means that if you log in using this API, your e2e test will be
10
10
  logged in and vice versa.
11
+
12
+ ## delete
13
+
14
+ ```
15
+ def delete(
16
+ url,
17
+ data: nil,
18
+ failOnStatusCode: nil,
19
+ form: nil,
20
+ headers: nil,
21
+ ignoreHTTPSErrors: nil,
22
+ multipart: nil,
23
+ params: nil,
24
+ timeout: nil)
25
+ ```
26
+
27
+ Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its
28
+ response. The method will populate request cookies from the context and update context cookies from the response. The
29
+ method will automatically follow redirects.
30
+
31
+ ## dispose
32
+
33
+ ```
34
+ def dispose
35
+ ```
36
+
37
+ All responses returned by [APIRequestContext#get](./api_request_context#get) and similar methods are stored in the memory, so that you
38
+ can later call [APIResponse#body](./api_response#body). This method discards all stored responses, and makes
39
+ [APIResponse#body](./api_response#body) throw "Response disposed" error.
40
+
41
+ ## fetch
42
+
43
+ ```
44
+ def fetch(
45
+ urlOrRequest,
46
+ data: nil,
47
+ failOnStatusCode: nil,
48
+ form: nil,
49
+ headers: nil,
50
+ ignoreHTTPSErrors: nil,
51
+ method: nil,
52
+ multipart: nil,
53
+ params: nil,
54
+ timeout: nil)
55
+ ```
56
+
57
+ Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
58
+ context cookies from the response. The method will automatically follow redirects.
59
+
60
+ ## get
61
+
62
+ ```
63
+ def get(
64
+ url,
65
+ failOnStatusCode: nil,
66
+ headers: nil,
67
+ ignoreHTTPSErrors: nil,
68
+ params: nil,
69
+ timeout: nil)
70
+ ```
71
+
72
+ Sends HTTP(S) [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) request and returns its response. The
73
+ method will populate request cookies from the context and update context cookies from the response. The method will
74
+ automatically follow redirects.
75
+
76
+ ## head
77
+
78
+ ```
79
+ def head(
80
+ url,
81
+ failOnStatusCode: nil,
82
+ headers: nil,
83
+ ignoreHTTPSErrors: nil,
84
+ params: nil,
85
+ timeout: nil)
86
+ ```
87
+
88
+ Sends HTTP(S) [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) request and returns its response.
89
+ The method will populate request cookies from the context and update context cookies from the response. The method will
90
+ automatically follow redirects.
91
+
92
+ ## patch
93
+
94
+ ```
95
+ def patch(
96
+ url,
97
+ data: nil,
98
+ failOnStatusCode: nil,
99
+ form: nil,
100
+ headers: nil,
101
+ ignoreHTTPSErrors: nil,
102
+ multipart: nil,
103
+ params: nil,
104
+ timeout: nil)
105
+ ```
106
+
107
+ Sends HTTP(S) [PATCH](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) request and returns its response.
108
+ The method will populate request cookies from the context and update context cookies from the response. The method will
109
+ automatically follow redirects.
110
+
111
+ ## post
112
+
113
+ ```
114
+ def post(
115
+ url,
116
+ data: nil,
117
+ failOnStatusCode: nil,
118
+ form: nil,
119
+ headers: nil,
120
+ ignoreHTTPSErrors: nil,
121
+ multipart: nil,
122
+ params: nil,
123
+ timeout: nil)
124
+ ```
125
+
126
+ Sends HTTP(S) [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request and returns its response.
127
+ The method will populate request cookies from the context and update context cookies from the response. The method will
128
+ automatically follow redirects.
129
+
130
+ ## put
131
+
132
+ ```
133
+ def put(
134
+ url,
135
+ data: nil,
136
+ failOnStatusCode: nil,
137
+ form: nil,
138
+ headers: nil,
139
+ ignoreHTTPSErrors: nil,
140
+ multipart: nil,
141
+ params: nil,
142
+ timeout: nil)
143
+ ```
144
+
145
+ Sends HTTP(S) [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request and returns its response. The
146
+ method will populate request cookies from the context and update context cookies from the response. The method will
147
+ automatically follow redirects.
@@ -0,0 +1,90 @@
1
+ ---
2
+ sidebar_position: 10
3
+ ---
4
+
5
+ # APIResponse
6
+
7
+ [APIResponse](./api_response) class represents responses returned by [APIRequestContext#get](./api_request_context#get) and similar methods.
8
+
9
+ ## body
10
+
11
+ ```
12
+ def body
13
+ ```
14
+
15
+ Returns the buffer with response body.
16
+
17
+ ## dispose
18
+
19
+ ```
20
+ def dispose
21
+ ```
22
+
23
+ Disposes the body of this response. If not called then the body will stay in memory until the context closes.
24
+
25
+ ## headers
26
+
27
+ ```
28
+ def headers
29
+ ```
30
+
31
+ An object with all the response HTTP headers associated with this response.
32
+
33
+ ## headers_array
34
+
35
+ ```
36
+ def headers_array
37
+ ```
38
+
39
+ An array with all the request HTTP headers associated with this response. Header names are not lower-cased. Headers with
40
+ multiple entries, such as `Set-Cookie`, appear in the array multiple times.
41
+
42
+ ## json
43
+
44
+ ```
45
+ def json
46
+ ```
47
+
48
+ Returns the JSON representation of response body.
49
+
50
+ This method will throw if the response body is not parsable via `JSON.parse`.
51
+
52
+ ## ok
53
+
54
+ ```
55
+ def ok
56
+ ```
57
+
58
+ Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
59
+
60
+ ## status
61
+
62
+ ```
63
+ def status
64
+ ```
65
+
66
+ Contains the status code of the response (e.g., 200 for a success).
67
+
68
+ ## status_text
69
+
70
+ ```
71
+ def status_text
72
+ ```
73
+
74
+ Contains the status text of the response (e.g. usually an "OK" for a success).
75
+
76
+ ## text
77
+
78
+ ```
79
+ def text
80
+ ```
81
+
82
+ Returns the text representation of response body.
83
+
84
+ ## url
85
+
86
+ ```
87
+ def url
88
+ ```
89
+
90
+ Contains the URL of the response.
@@ -436,4 +436,8 @@ def expect_page(predicate: nil, timeout: nil)
436
436
  Performs action and waits for a new [Page](./page) to be created in the context. If predicate is provided, it passes [Page](./page) value into the `predicate` and waits for `predicate.call(page)` to return a truthy value. Will throw an error if
437
437
  the context closes before new [Page](./page) is created.
438
438
 
439
+ ## request
440
+
441
+ API testing helper associated with this context. Requests made with this API will use context cookies.
442
+
439
443
  ## tracing
@@ -1,17 +1,5 @@
1
1
  # API coverages
2
2
 
3
- ## APIRequestContext
4
-
5
- * ~~delete~~
6
- * ~~dispose~~
7
- * ~~fetch~~
8
- * ~~get~~
9
- * ~~head~~
10
- * ~~patch~~
11
- * ~~post~~
12
- * ~~put~~
13
- * ~~storage_state~~
14
-
15
3
  ## Request
16
4
 
17
5
  * all_headers
@@ -361,7 +349,7 @@
361
349
  * expect_event
362
350
  * expect_page
363
351
  * ~~wait_for_event~~
364
- * ~~request~~
352
+ * request
365
353
  * tracing
366
354
 
367
355
  ## CDPSession
@@ -461,6 +449,35 @@
461
449
  * locator
462
450
  * nth
463
451
 
452
+ ## APIResponse
453
+
454
+ * body
455
+ * dispose
456
+ * headers
457
+ * headers_array
458
+ * json
459
+ * ok
460
+ * status
461
+ * status_text
462
+ * text
463
+ * url
464
+
465
+ ## APIRequestContext
466
+
467
+ * delete
468
+ * dispose
469
+ * fetch
470
+ * get
471
+ * head
472
+ * patch
473
+ * post
474
+ * put
475
+ * ~~storage_state~~
476
+
477
+ ## ~~APIRequest~~
478
+
479
+ * ~~new_context~~
480
+
464
481
  ## Android
465
482
 
466
483
  * devices
@@ -0,0 +1,73 @@
1
+ module Playwright
2
+ define_api_implementation :APIResponseImpl do
3
+ include Utils::Errors::SafeCloseError
4
+
5
+ # @params context [APIRequestContext]
6
+ # @params initializer [Hash]
7
+ def initialize(context, initializer)
8
+ @request = context
9
+ @initializer = initializer
10
+ @headers = RawHeaders.new(initializer['headers'])
11
+ end
12
+
13
+ def to_s
14
+ "#<APIResponse url=#{url} status=#{status} status_text=#{status_text}>"
15
+ end
16
+
17
+ def url
18
+ @initializer['url']
19
+ end
20
+
21
+ def ok
22
+ (200...300).include?(status)
23
+ end
24
+ alias_method :ok?, :ok
25
+
26
+ def status
27
+ @initializer['status']
28
+ end
29
+
30
+ def status_text
31
+ @initializer['statusText']
32
+ end
33
+
34
+ def headers
35
+ @headers.headers
36
+ end
37
+
38
+ def headers_array
39
+ @headers.headers_array
40
+ end
41
+
42
+ class AlreadyDisposedError < StandardError
43
+ def initialize
44
+ super('Response has been disposed')
45
+ end
46
+ end
47
+
48
+ def body
49
+ binary = @request.channel.send_message_to_server("fetchResponseBody", fetchUid: fetch_uid)
50
+ raise AlreadyDisposedError.new unless binary
51
+ Base64.strict_decode64(binary)
52
+ rescue => err
53
+ if safe_close_error?(err)
54
+ raise AlreadyDisposedError.new
55
+ else
56
+ raise
57
+ end
58
+ end
59
+ alias_method :text, :body
60
+
61
+ def json
62
+ JSON.parse(text)
63
+ end
64
+
65
+ def dispose
66
+ @request.channel.send_message_to_server("disposeAPIResponse", fetchUid: fetch_uid)
67
+ end
68
+
69
+ private def fetch_uid
70
+ @initializer['fetchUid']
71
+ end
72
+ end
73
+ end
@@ -1,4 +1,236 @@
1
+ require 'base64'
2
+
1
3
  module Playwright
2
4
  define_channel_owner :APIRequestContext do
5
+ def dispose
6
+ @channel.send_message_to_server('dispose')
7
+ end
8
+
9
+ def delete(
10
+ url,
11
+ data: nil,
12
+ failOnStatusCode: nil,
13
+ form: nil,
14
+ headers: nil,
15
+ ignoreHTTPSErrors: nil,
16
+ multipart: nil,
17
+ params: nil,
18
+ timeout: nil)
19
+ fetch(
20
+ url,
21
+ method: 'DELETE',
22
+ data: data,
23
+ failOnStatusCode: failOnStatusCode,
24
+ form: form,
25
+ headers: headers,
26
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
27
+ multipart: multipart,
28
+ params: params,
29
+ timeout: timeout,
30
+ )
31
+ end
32
+
33
+ def head(
34
+ url,
35
+ failOnStatusCode: nil,
36
+ headers: nil,
37
+ ignoreHTTPSErrors: nil,
38
+ params: nil,
39
+ timeout: nil)
40
+ fetch(
41
+ url,
42
+ method: 'HEAD',
43
+ failOnStatusCode: failOnStatusCode,
44
+ headers: headers,
45
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
46
+ params: params,
47
+ timeout: timeout,
48
+ )
49
+ end
50
+
51
+ def get(
52
+ url,
53
+ failOnStatusCode: nil,
54
+ headers: nil,
55
+ ignoreHTTPSErrors: nil,
56
+ params: nil,
57
+ timeout: nil)
58
+ fetch(
59
+ url,
60
+ method: 'GET',
61
+ failOnStatusCode: failOnStatusCode,
62
+ headers: headers,
63
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
64
+ params: params,
65
+ timeout: timeout,
66
+ )
67
+ end
68
+
69
+ def patch(
70
+ url,
71
+ data: nil,
72
+ failOnStatusCode: nil,
73
+ form: nil,
74
+ headers: nil,
75
+ ignoreHTTPSErrors: nil,
76
+ multipart: nil,
77
+ params: nil,
78
+ timeout: nil)
79
+ fetch(
80
+ url,
81
+ method: 'PATCH',
82
+ data: data,
83
+ failOnStatusCode: failOnStatusCode,
84
+ form: form,
85
+ headers: headers,
86
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
87
+ multipart: multipart,
88
+ params: params,
89
+ timeout: timeout,
90
+ )
91
+ end
92
+
93
+ def put(
94
+ url,
95
+ data: nil,
96
+ failOnStatusCode: nil,
97
+ form: nil,
98
+ headers: nil,
99
+ ignoreHTTPSErrors: nil,
100
+ multipart: nil,
101
+ params: nil,
102
+ timeout: nil)
103
+ fetch(
104
+ url,
105
+ method: 'PUT',
106
+ data: data,
107
+ failOnStatusCode: failOnStatusCode,
108
+ form: form,
109
+ headers: headers,
110
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
111
+ multipart: multipart,
112
+ params: params,
113
+ timeout: timeout,
114
+ )
115
+ end
116
+
117
+ def post(
118
+ url,
119
+ data: nil,
120
+ failOnStatusCode: nil,
121
+ form: nil,
122
+ headers: nil,
123
+ ignoreHTTPSErrors: nil,
124
+ multipart: nil,
125
+ params: nil,
126
+ timeout: nil)
127
+ fetch(
128
+ url,
129
+ method: 'POST',
130
+ data: data,
131
+ failOnStatusCode: failOnStatusCode,
132
+ form: form,
133
+ headers: headers,
134
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
135
+ multipart: multipart,
136
+ params: params,
137
+ timeout: timeout,
138
+ )
139
+ end
140
+
141
+ def fetch(
142
+ urlOrRequest,
143
+ data: nil,
144
+ failOnStatusCode: nil,
145
+ form: nil,
146
+ headers: nil,
147
+ ignoreHTTPSErrors: nil,
148
+ method: nil,
149
+ multipart: nil,
150
+ params: nil,
151
+ timeout: nil)
152
+
153
+ if [ChannelOwners::Request, String].none? { |type| urlOrRequest.is_a?(type) }
154
+ raise ArgumentError.new("First argument must be either URL string or Request")
155
+ end
156
+ if [data, form, multipart].compact.count > 1
157
+ raise ArgumentError.new("Only one of 'data', 'form' or 'multipart' can be specified")
158
+ end
159
+
160
+ request = urlOrRequest.is_a?(ChannelOwners::Request) ? urlOrRequest : nil
161
+ headers_obj = headers || request&.headers
162
+ fetch_params = {
163
+ url: request&.url || urlOrRequest,
164
+ params: object_to_array(params),
165
+ method: method || request&.method || 'GET',
166
+ headers: headers_obj ? HttpHeaders.new(headers_obj).as_serialized : nil,
167
+ }
168
+
169
+ json_data = nil
170
+ form_data = nil
171
+ multipart_data = nil
172
+ post_data_buffer = nil
173
+ if data
174
+ case data
175
+ when String
176
+ if headers_obj&.any? { |key, value| key.downcase == 'content-type' && value == 'application/json' }
177
+ json_data = data
178
+ else
179
+ post_data_buffer = data
180
+ end
181
+ when Hash, Array, Numeric, true, false
182
+ json_data = data
183
+ else
184
+ raise ArgumentError.new("Unsupported 'data' type: #{data.class}")
185
+ end
186
+ elsif form
187
+ form_data = object_to_array(form)
188
+ elsif multipart
189
+ multipart_data = multipart.map do |name, value|
190
+ if file_payload?(value)
191
+ { name: name, file: file_payload_to_json(value) }
192
+ else
193
+ { name: name, value: value.to_s }
194
+ end
195
+ end
196
+ end
197
+
198
+ if !json_data && !form_data && !multipart_data
199
+ post_data_buffer ||= request&.post_data_buffer
200
+ end
201
+ if post_data_buffer
202
+ fetch_params[:postData] = Base64.strict_encode64(post_data_buffer)
203
+ end
204
+
205
+ fetch_params[:jsonData] = json_data
206
+ fetch_params[:formData] = form_data
207
+ fetch_params[:multipartData] = multipart_data
208
+ fetch_params[:timeout] = timeout
209
+ fetch_params[:failOnStatusCode] = failOnStatusCode
210
+ fetch_params[:ignoreHTTPSErrors] = ignoreHTTPSErrors
211
+ fetch_params.compact!
212
+ response = @channel.send_message_to_server('fetch', fetch_params)
213
+
214
+ APIResponseImpl.new(self, response)
215
+ end
216
+
217
+ private def file_payload?(value)
218
+ value.is_a?(Hash) &&
219
+ %w(name mimeType buffer).all? { |key| value.has_key?(key) || value.has_key?(key.to_sym) }
220
+ end
221
+
222
+ private def file_payload_to_json(payload)
223
+ {
224
+ name: payload[:name] || payload['name'],
225
+ mimeType: payload[:mimeType] || payload['mimeType'],
226
+ buffer: Base64.strict_encode64(payload[:buffer] || payload['buffer'])
227
+ }
228
+ end
229
+
230
+ private def object_to_array(hash)
231
+ hash&.map do |key, value|
232
+ { name: key, value: value.to_s }
233
+ end
234
+ end
3
235
  end
4
236
  end
@@ -4,7 +4,7 @@ module Playwright
4
4
  include Utils::Errors::SafeCloseError
5
5
  attr_accessor :browser
6
6
  attr_writer :owner_page, :options
7
- attr_reader :tracing
7
+ attr_reader :tracing, :request
8
8
 
9
9
  private def after_initialize
10
10
  @pages = Set.new
@@ -15,6 +15,8 @@ module Playwright
15
15
  @background_pages = Set.new
16
16
 
17
17
  @tracing = TracingImpl.new(@channel, self)
18
+ @request = ChannelOwners::APIRequestContext.from(@initializer['APIRequestContext'])
19
+
18
20
  @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
19
21
  @channel.once('close', ->(_) { on_close })
20
22
  @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
@@ -733,6 +733,10 @@ module Playwright
733
733
  @workers.to_a
734
734
  end
735
735
 
736
+ def request
737
+ @browser_context.request
738
+ end
739
+
736
740
  def pause
737
741
  @browser_context.send(:pause)
738
742
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '1.17.0'
5
- COMPATIBLE_PLAYWRIGHT_VERSION = '1.17.0'
4
+ VERSION = '1.17.1'
5
+ COMPATIBLE_PLAYWRIGHT_VERSION = '1.17.2'
6
6
  end
@@ -36,12 +36,6 @@ module Playwright
36
36
  end
37
37
  alias_method :default_timeout=, :set_default_timeout
38
38
 
39
- # -- inherited from EventEmitter --
40
- # @nodoc
41
- def off(event, callback)
42
- event_emitter_proxy.off(event, callback)
43
- end
44
-
45
39
  # -- inherited from EventEmitter --
46
40
  # @nodoc
47
41
  def once(event, callback)
@@ -54,6 +48,12 @@ module Playwright
54
48
  event_emitter_proxy.on(event, callback)
55
49
  end
56
50
 
51
+ # -- inherited from EventEmitter --
52
+ # @nodoc
53
+ def off(event, callback)
54
+ event_emitter_proxy.off(event, callback)
55
+ end
56
+
57
57
  private def event_emitter_proxy
58
58
  @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
59
59
  end