bowser 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -9
- data/CHANGELOG.md +7 -0
- data/lib/bowser/version.rb +1 -1
- data/opal/bowser.rb +0 -3
- data/opal/bowser/delegate_native.rb +9 -1
- data/opal/bowser/document.rb +8 -0
- data/opal/bowser/element.rb +57 -4
- data/opal/bowser/event.rb +30 -0
- data/opal/bowser/event_target.rb +10 -0
- data/opal/bowser/file_list.rb +25 -0
- data/opal/bowser/http.rb +30 -0
- data/opal/bowser/http/form_data.rb +4 -0
- data/opal/bowser/http/request.rb +19 -0
- data/opal/bowser/http/response.rb +8 -0
- data/opal/bowser/indexed_db.rb +244 -0
- data/opal/bowser/service_worker.rb +43 -0
- data/opal/bowser/service_worker/cache_storage.rb +49 -0
- data/opal/bowser/service_worker/clients.rb +19 -0
- data/opal/bowser/service_worker/context.rb +71 -0
- data/opal/bowser/service_worker/extendable_event.rb +19 -0
- data/opal/bowser/service_worker/fetch_event.rb +28 -0
- data/opal/bowser/service_worker/promise.rb +101 -0
- data/opal/bowser/service_worker/request.rb +33 -0
- data/opal/bowser/service_worker/response.rb +41 -0
- data/opal/bowser/websocket.rb +18 -0
- data/opal/bowser/window.rb +28 -0
- metadata +13 -3
data/opal/bowser/http.rb
CHANGED
@@ -8,6 +8,13 @@ module Bowser
|
|
8
8
|
module HTTP
|
9
9
|
module_function
|
10
10
|
|
11
|
+
# Send a request to the given URL
|
12
|
+
#
|
13
|
+
# @param url [String] the URL to send the request to
|
14
|
+
# @keywordparam method [String] the HTTP method, defaults to GET
|
15
|
+
# @keywordparam headers [Hash] the HTTP headers to send with the request
|
16
|
+
# @keywordparam data [Hash, String, nil] the data to send with the request,
|
17
|
+
# only useful for POST, PATCH, or PUT requests
|
11
18
|
def fetch(url, method: :get, headers: {}, data: nil, &block)
|
12
19
|
promise = Promise.new
|
13
20
|
request = Request.new(method, url)
|
@@ -20,6 +27,13 @@ module Bowser
|
|
20
27
|
promise
|
21
28
|
end
|
22
29
|
|
30
|
+
# Shorthand method for sending a request with JSON data
|
31
|
+
#
|
32
|
+
# @param url [String] the URL to send the request to
|
33
|
+
# @param data [Hash, String, nil] the data to send with the request,
|
34
|
+
# only useful for POST, PATCH, or PUT requests. Otherwise, use `fetch`.
|
35
|
+
# @keywordparam method [String] the HTTP method, defaults to GET
|
36
|
+
# @keywordparam content_type [String] the MIME type of the request
|
23
37
|
def upload(url, data, content_type: 'application/json', method: :post, &block)
|
24
38
|
promise = Promise.new
|
25
39
|
request = Request.new(method, url)
|
@@ -32,6 +46,15 @@ module Bowser
|
|
32
46
|
promise
|
33
47
|
end
|
34
48
|
|
49
|
+
# Upload files from a file input field
|
50
|
+
#
|
51
|
+
# @param url [String] the URL to send the request to
|
52
|
+
# @param files [Bowser::FileList] the files to attach
|
53
|
+
# @keywordparam key [String] the name to use for the POST param, defaults to `"files"`
|
54
|
+
# @keywordparam key_suffix [String] the suffix for the key, defaults to
|
55
|
+
# `"[]"`. This is how several web frameworks determine that the key should
|
56
|
+
# be treated as an array.
|
57
|
+
# @keywordparam method [String] the HTTP method to use, defaults to `"POST"`
|
35
58
|
def upload_files(url, files, key: 'files', key_suffix: '[]', method: :post, &block)
|
36
59
|
promise = Promise.new
|
37
60
|
request = Request.new(method, url)
|
@@ -49,10 +72,17 @@ module Bowser
|
|
49
72
|
promise
|
50
73
|
end
|
51
74
|
|
75
|
+
# Upload a single file from a file input field
|
76
|
+
#
|
77
|
+
# @param url [String] the URL to send the request to
|
78
|
+
# @param file [Bowser::File] the file to attach
|
79
|
+
# @keywordparam key [String] the name to use for the POST param, defaults to `"files"`
|
80
|
+
# @keywordparam method [String] the HTTP method to use, defaults to `"POST"`
|
52
81
|
def upload_file(url, file, key: 'file', method: :post, &block)
|
53
82
|
upload_files(url, [file], key: key, key_suffix: nil, method: method, &block)
|
54
83
|
end
|
55
84
|
|
85
|
+
# @api private
|
56
86
|
def connect_events_to_promise(request, promise)
|
57
87
|
request.on :load do
|
58
88
|
promise.resolve request.response
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Bowser
|
2
2
|
module HTTP
|
3
|
+
# Data to be attached to a form or sent in with a Bowser::HTTP::Request
|
3
4
|
class FormData
|
5
|
+
# @param attributes [Hash, nil] the attributes to attach
|
4
6
|
def initialize attributes={}
|
5
7
|
@native = `new FormData()`
|
6
8
|
attributes.each do |key, value|
|
@@ -14,6 +16,8 @@ module Bowser
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
# @param key [String] the name of the attribute
|
20
|
+
# @param value [String] the value of the attribute
|
17
21
|
def append key, value
|
18
22
|
data = if `!!value['native']`
|
19
23
|
`value['native']`
|
data/opal/bowser/http/request.rb
CHANGED
@@ -3,6 +3,7 @@ require 'bowser/event_target'
|
|
3
3
|
|
4
4
|
module Bowser
|
5
5
|
module HTTP
|
6
|
+
# A Ruby object representing an HTTP request to a remote server.
|
6
7
|
class Request
|
7
8
|
include EventTarget
|
8
9
|
|
@@ -15,6 +16,8 @@ module Bowser
|
|
15
16
|
LOADING = 3
|
16
17
|
DONE = 4
|
17
18
|
|
19
|
+
# @param method [String] the HTTP method to use
|
20
|
+
# @param url [String] the URL to send the request to
|
18
21
|
def initialize(method, url, native: `new XMLHttpRequest()`)
|
19
22
|
@native = native
|
20
23
|
@method = method
|
@@ -22,6 +25,11 @@ module Bowser
|
|
22
25
|
@response = Response.new(@native)
|
23
26
|
end
|
24
27
|
|
28
|
+
# Send the HTTP request
|
29
|
+
#
|
30
|
+
# @keywordparam data [Hash, Bowser::HTTP::FormData, nil] the data to send
|
31
|
+
# with the request.
|
32
|
+
# @keywordparam headers [Hash] the HTTP headers to attach to the request
|
25
33
|
def send(data: {}, headers: {})
|
26
34
|
`#@native.open(#{method}, #{url})`
|
27
35
|
@data = data
|
@@ -40,6 +48,10 @@ module Bowser
|
|
40
48
|
self
|
41
49
|
end
|
42
50
|
|
51
|
+
# Set the headers to the keys and values of the specified hash
|
52
|
+
#
|
53
|
+
# @param headers [Hash] the hash whose keys and values should be added as
|
54
|
+
# request headers
|
43
55
|
def headers= headers
|
44
56
|
@headers = headers
|
45
57
|
headers.each do |attr, value|
|
@@ -47,30 +59,37 @@ module Bowser
|
|
47
59
|
end
|
48
60
|
end
|
49
61
|
|
62
|
+
# @return [Boolean] true if this request is a POST request, false otherwise
|
50
63
|
def post?
|
51
64
|
method == :post
|
52
65
|
end
|
53
66
|
|
67
|
+
# @return [Boolean] true if this request is a GET request, false otherwise
|
54
68
|
def get?
|
55
69
|
method == :get
|
56
70
|
end
|
57
71
|
|
72
|
+
# @return [Numeric] the numeric readyState of the underlying XMLHttpRequest
|
58
73
|
def ready_state
|
59
74
|
`#@native.readyState`
|
60
75
|
end
|
61
76
|
|
77
|
+
# @return [Boolean] true if this request has been sent, false otherwise
|
62
78
|
def sent?
|
63
79
|
ready_state >= OPENED
|
64
80
|
end
|
65
81
|
|
82
|
+
# @return [Boolean] true if we have received response headers, false otherwise
|
66
83
|
def headers_received?
|
67
84
|
ready_state >= HEADERS_RECEIVED
|
68
85
|
end
|
69
86
|
|
87
|
+
# @return [Boolean] true if we are currently downloading the response body, false otherwise
|
70
88
|
def loading?
|
71
89
|
ready_state == LOADING
|
72
90
|
end
|
73
91
|
|
92
|
+
# @return [Boolean] true if the response has been completed, false otherwise
|
74
93
|
def done?
|
75
94
|
ready_state >= DONE
|
76
95
|
end
|
@@ -2,27 +2,35 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Bowser
|
4
4
|
module HTTP
|
5
|
+
# Ruby class representing an HTTP response from a remote server
|
5
6
|
class Response
|
7
|
+
# @param xhr [JS] a native XMLHttpRequest object
|
6
8
|
def initialize xhr
|
7
9
|
@xhr = xhr
|
8
10
|
end
|
9
11
|
|
12
|
+
# @return [Numeric] the HTTP status code of the response
|
10
13
|
def code
|
11
14
|
`#@xhr.status`
|
12
15
|
end
|
13
16
|
|
17
|
+
# @return [String] the body of the response as a string
|
14
18
|
def body
|
15
19
|
`#@xhr.response`
|
16
20
|
end
|
17
21
|
|
22
|
+
# @return [Hash, Array, String] the response body deserialized from JSON
|
18
23
|
def json
|
19
24
|
@json ||= JSON.parse(body) if `#{body} !== undefined`
|
20
25
|
end
|
21
26
|
|
27
|
+
# @return [Boolean] true if this was a successful response (2xx-3xx), false otherwise
|
22
28
|
def success?
|
23
29
|
(200...400).cover? code
|
24
30
|
end
|
31
|
+
alias ok? success?
|
25
32
|
|
33
|
+
# @return [Boolean] true if this represents a failed response (4xx-5xx)
|
26
34
|
def fail?
|
27
35
|
!success?
|
28
36
|
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'bowser/event_target'
|
2
|
+
require 'promise'
|
3
|
+
|
4
|
+
module Bowser
|
5
|
+
class IndexedDB
|
6
|
+
name = %w(
|
7
|
+
indexedDB
|
8
|
+
mozIndexedDB
|
9
|
+
webkitIndexedDB
|
10
|
+
msIndexedDB
|
11
|
+
).find { |name| `!!window[#{name}]` }
|
12
|
+
NATIVE = `window[#{name}]`
|
13
|
+
|
14
|
+
def initialize name, version: 1
|
15
|
+
@thens = []
|
16
|
+
|
17
|
+
request = Request.new(`#{NATIVE}.open(#{name}, #{version})`)
|
18
|
+
request.on :error do |event|
|
19
|
+
`console.error(event)`
|
20
|
+
end
|
21
|
+
request.on :success do |event|
|
22
|
+
@native = event.target.result
|
23
|
+
|
24
|
+
@thens.each(&:call)
|
25
|
+
@thens.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
request.on :upgradeneeded do |event|
|
29
|
+
puts 'upgradeneeded'
|
30
|
+
@native = event.target.result
|
31
|
+
|
32
|
+
if block_given?
|
33
|
+
yield self
|
34
|
+
puts 'upgraded'
|
35
|
+
else
|
36
|
+
raise ArgumentError, "You must provide a block to `#{self.class}.new` in order to set up the database if the user's browser does not have it."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_object_store name, key_path: `undefined`, unique: false
|
42
|
+
ObjectStore.new(`#@native.createObjectStore(#{name}, { keyPath: #{key_path} })`)
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete_object_store name
|
46
|
+
`#@native.deleteObjectStore(#{name})`
|
47
|
+
rescue `TypeError` => e
|
48
|
+
# If the object store doesn't exist, we do nothing. The store not existing
|
49
|
+
# is the end state we want after this method call anyway.
|
50
|
+
end
|
51
|
+
|
52
|
+
def transaction name, mode=:readonly
|
53
|
+
Transaction.new(`#@native.transaction(#{name}, #{mode})`)
|
54
|
+
end
|
55
|
+
|
56
|
+
def then &block
|
57
|
+
if @native
|
58
|
+
block.call
|
59
|
+
else
|
60
|
+
@thens << block
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def deserialize klass, native
|
65
|
+
`Object.assign(#{klass.allocate}, #{native})`
|
66
|
+
end
|
67
|
+
|
68
|
+
class ObjectStore
|
69
|
+
def initialize native
|
70
|
+
@native = native
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_index name, key_path=name, unique: false
|
74
|
+
`#@native.createIndex(#{name}, #{key_path}, { unique: #{unique} })`
|
75
|
+
end
|
76
|
+
|
77
|
+
def add object
|
78
|
+
`#@native.add(#{object})`
|
79
|
+
end
|
80
|
+
|
81
|
+
def put object
|
82
|
+
`#@native.put(#{object})`
|
83
|
+
end
|
84
|
+
|
85
|
+
def delete key
|
86
|
+
p = Promise.new
|
87
|
+
key = yield Query.new if block_given?
|
88
|
+
|
89
|
+
request = Request.new(`#@native.delete(#{key})`)
|
90
|
+
request.on(:success) { p.resolve }
|
91
|
+
request.on(:error) { |error| p.reject error }
|
92
|
+
|
93
|
+
p
|
94
|
+
end
|
95
|
+
|
96
|
+
def get key
|
97
|
+
p = Promise.new
|
98
|
+
key = yield Query.new if block_given?
|
99
|
+
|
100
|
+
request = Request.new(`#@native.get(#{key})`)
|
101
|
+
request.on :success do |event|
|
102
|
+
js_obj = `#{event.target}.result`
|
103
|
+
if `!!#{js_obj}`
|
104
|
+
p.resolve `Object.assign(#{klass.allocate}, #{js_obj})`
|
105
|
+
else
|
106
|
+
p.resolve nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
request.on(:error) { |event| p.reject event.target.result }
|
110
|
+
|
111
|
+
p
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_all klass, count: `undefined`
|
115
|
+
p = Promise.new
|
116
|
+
query = block_given? ? yield(Query.new) : `undefined`
|
117
|
+
|
118
|
+
request = Request.new(`#@native.getAll(#{query}, #{count})`)
|
119
|
+
request.on :success do |event|
|
120
|
+
p.resolve event.target.result.map { |js_obj|
|
121
|
+
`delete #{js_obj}.$$id` # Remove old Ruby runtime metadata
|
122
|
+
`Object.assign(#{klass.allocate}, #{js_obj})`
|
123
|
+
}
|
124
|
+
end
|
125
|
+
request.on :error do |event|
|
126
|
+
p.reject event.target.result
|
127
|
+
end
|
128
|
+
|
129
|
+
p
|
130
|
+
end
|
131
|
+
|
132
|
+
def index name
|
133
|
+
Index.new(`#@native.index(#{name})`)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Index
|
138
|
+
def initialize native
|
139
|
+
@native = native
|
140
|
+
end
|
141
|
+
|
142
|
+
def get klass, key
|
143
|
+
p = Promise.new
|
144
|
+
key = yield Query.new if block_given?
|
145
|
+
|
146
|
+
request = Request.new(`#@native.get(#{key})`)
|
147
|
+
request.on :success do |event|
|
148
|
+
begin
|
149
|
+
p.resolve `Object.assign(#{klass.allocate}, #{event.target.result})`
|
150
|
+
rescue NoMethodError # Happens when there is no result above :-\
|
151
|
+
p.resolve nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
request.on :error do |event|
|
156
|
+
p.reject event.target.result
|
157
|
+
end
|
158
|
+
|
159
|
+
p
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_all klass, count: `undefined`
|
163
|
+
p = Promise.new
|
164
|
+
|
165
|
+
query = if block_given?
|
166
|
+
yield Query.new
|
167
|
+
else
|
168
|
+
`undefined`
|
169
|
+
end
|
170
|
+
|
171
|
+
request = Request.new(`#@native.getAll(#{query}, #{count})`)
|
172
|
+
request.on :success do |event|
|
173
|
+
p.resolve event.target.result.map { |js_obj|
|
174
|
+
`delete #{js_obj}.$$id` # Remove old Ruby runtime metadata
|
175
|
+
`Object.assign(#{klass.allocate}, #{js_obj})`
|
176
|
+
}
|
177
|
+
end
|
178
|
+
request.on :error do |event|
|
179
|
+
p.reject event.target.result
|
180
|
+
end
|
181
|
+
|
182
|
+
p
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class Query
|
187
|
+
name = %w(
|
188
|
+
IDBKeyRange
|
189
|
+
msIDBKeyRange
|
190
|
+
mozIDBKeyRange
|
191
|
+
webkitIDBKeyRange
|
192
|
+
).find { |name| `!!window[#{name}]` }
|
193
|
+
NATIVE = `window[#{name}]`
|
194
|
+
|
195
|
+
def > value
|
196
|
+
lower_bound value, true
|
197
|
+
end
|
198
|
+
|
199
|
+
def >= value
|
200
|
+
lower_bound value
|
201
|
+
end
|
202
|
+
|
203
|
+
def < value
|
204
|
+
upper_bound value, true
|
205
|
+
end
|
206
|
+
|
207
|
+
def <= value
|
208
|
+
upper_bound value
|
209
|
+
end
|
210
|
+
|
211
|
+
def == value
|
212
|
+
`#{NATIVE}.only(#{value})`
|
213
|
+
end
|
214
|
+
|
215
|
+
def lower_bound value, exclusive=false
|
216
|
+
`#{NATIVE}.lowerBound(value, exclusive)`
|
217
|
+
end
|
218
|
+
|
219
|
+
def upper_bound value, exclusive=false
|
220
|
+
`#{NATIVE}.upperBound(value, exclusive)`
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class Transaction
|
225
|
+
include Bowser::EventTarget
|
226
|
+
|
227
|
+
def initialize native
|
228
|
+
@native = native
|
229
|
+
end
|
230
|
+
|
231
|
+
def object_store name=`#@native.objectStoreNames[0]`
|
232
|
+
ObjectStore.new(`#@native.objectStore(#{name})`)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class Request
|
237
|
+
include Bowser::EventTarget
|
238
|
+
|
239
|
+
def initialize native
|
240
|
+
@native = native
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'promise'
|
2
|
+
|
3
|
+
module Bowser
|
4
|
+
class ServiceWorker
|
5
|
+
NotSupported = Class.new(StandardError)
|
6
|
+
|
7
|
+
def self.register path, options={}
|
8
|
+
p = Promise.new
|
9
|
+
|
10
|
+
if supported?
|
11
|
+
%x{
|
12
|
+
navigator.serviceWorker.register(#{path}, #{options.to_n})
|
13
|
+
.then(#{proc { |reg| p.resolve new(reg) }})
|
14
|
+
.catch(#{proc { |error| p.fail error}});
|
15
|
+
}
|
16
|
+
else
|
17
|
+
p.reject NotSupported.new('Service worker is not supported in this browser')
|
18
|
+
end
|
19
|
+
|
20
|
+
p
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.ready &block
|
24
|
+
`navigator.serviceWorker.ready.then(#{proc { |sw| block.call new(sw) }})`
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.supported?
|
28
|
+
`'serviceWorker' in navigator`
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize native
|
32
|
+
@native = native
|
33
|
+
end
|
34
|
+
|
35
|
+
def scope
|
36
|
+
`#@native.scope`
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_n
|
40
|
+
@native
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|