bowser 0.4.3 → 0.5.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.
@@ -0,0 +1,49 @@
1
+ require 'bowser/service_worker/response'
2
+ require 'bowser/service_worker/promise'
3
+
4
+ module Bowser
5
+ module ServiceWorker
6
+ class CacheStorage
7
+ def initialize
8
+ @native = `caches`
9
+ end
10
+
11
+ def match request, options={}
12
+ Promise.from_native(`
13
+ #@native.match(#{request.to_n}, #{options.to_n})
14
+ .then(#{proc { |value| value && Response.from_native(value) }})
15
+ `)
16
+ end
17
+
18
+ def has name
19
+ Promise.from_native(`#@native.has(name)`)
20
+ end
21
+
22
+ def open name
23
+ Promise.from_native(`
24
+ #@native.open(name)
25
+ .then(#{proc { |native| Cache.new(native) }})
26
+ .catch(#{proc { |native| `console.error(native)` }})
27
+ `)
28
+ end
29
+
30
+ class Cache
31
+ def initialize native
32
+ @native = native
33
+ end
34
+
35
+ def add_all requests
36
+ Promise.from_native(`#@native.addAll(#{requests.map(&:to_n)})`)
37
+ end
38
+
39
+ def put request, response
40
+ Promise.from_native(`#@native.put(#{request.to_n}, #{response.to_n})`)
41
+ end
42
+
43
+ def to_n
44
+ @native
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ require 'bowser/service_worker/promise'
2
+
3
+ module Bowser
4
+ module ServiceWorker
5
+ class Clients
6
+ def initialize native
7
+ @native = native
8
+ end
9
+
10
+ def claim
11
+ Promise.from_native(`#@native.claim()`)
12
+ end
13
+
14
+ def to_n
15
+ @native
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,71 @@
1
+ require 'opal'
2
+ require 'native'
3
+ require 'bowser/service_worker/extendable_event'
4
+ require 'bowser/service_worker/fetch_event'
5
+ require 'bowser/service_worker/cache_storage'
6
+ require 'bowser/service_worker/request'
7
+ require 'bowser/service_worker/response'
8
+ require 'bowser/service_worker/promise'
9
+ require 'bowser/service_worker/clients'
10
+
11
+ # Opal uses `self` internally, but we can use `this` to get the worker reference
12
+ %x{ var worker = this; }
13
+
14
+ module Bowser
15
+ module ServiceWorker
16
+ class Context
17
+ def initialize native
18
+ @native = native
19
+ end
20
+
21
+ def on event_name, &block
22
+ event_type = EVENT_TYPES.fetch(event_name) { ExtendableEvent }
23
+ handler = proc { |event| block.call(event_type.new(event)) }
24
+
25
+ %x{#@native.addEventListener(event_name, handler);}
26
+
27
+ self
28
+ end
29
+
30
+ def to_n
31
+ @native
32
+ end
33
+
34
+ def caches
35
+ @caches ||= CacheStorage.new
36
+ end
37
+
38
+ def fetch url
39
+ Promise.new do |promise|
40
+ %x{
41
+ fetch(#{url.to_n})
42
+ .then(#{proc { |response|
43
+ promise.resolve Response.from_native(response)
44
+ }})
45
+ .catch(#{proc { |error| promise.reject error }})
46
+ }
47
+ end
48
+ end
49
+
50
+ def skip_waiting
51
+ Promise.from_native(`#{to_n}.skipWaiting()`)
52
+ end
53
+
54
+ def clients
55
+ @clients ||= Clients.new(`#{to_n}.clients`)
56
+ end
57
+
58
+ EVENT_TYPES = {
59
+ fetch: FetchEvent,
60
+ }
61
+ end
62
+ end
63
+ end
64
+
65
+ def self.worker
66
+ Bowser::ServiceWorker::Context.new(`worker`)
67
+ end
68
+
69
+ def self.method_missing(*args, &block)
70
+ worker.send(*args, &block)
71
+ end
@@ -0,0 +1,19 @@
1
+ module Bowser
2
+ module ServiceWorker
3
+ class ExtendableEvent
4
+ def initialize native
5
+ @native = native
6
+ end
7
+
8
+ def wait_until promise
9
+ `#@native.waitUntil(#{promise.to_n})`
10
+
11
+ promise
12
+ end
13
+
14
+ def to_n
15
+ @native
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'bowser/service_worker/request'
2
+ require 'bowser/service_worker/extendable_event'
3
+
4
+ module Bowser
5
+ module ServiceWorker
6
+ class FetchEvent < ExtendableEvent
7
+ def url
8
+ `#@native.request.url`
9
+ end
10
+
11
+ def reload?
12
+ `#@native.isReload`
13
+ end
14
+
15
+ def client_id
16
+ `#@native.clientId`
17
+ end
18
+
19
+ def request
20
+ @request ||= Request.from_native(`#@native.request`)
21
+ end
22
+
23
+ def respond_with promise
24
+ `#@native.respondWith(#{promise.then(&:to_n).to_n})`
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ module Bowser
2
+ module ServiceWorker
3
+ class Promise
4
+ attr_reader :value, :failure
5
+
6
+ def initialize &block
7
+ @native = `new Promise(function(resolve, reject) {
8
+ #{@resolve = `resolve`};
9
+ #{@reject = `reject`};
10
+ #{block.call(self) if block_given?};
11
+ })`
12
+ end
13
+
14
+ def self.from_native promise
15
+ p = allocate
16
+ p.instance_exec { @native = promise }
17
+ p
18
+ end
19
+
20
+ def self.all promises
21
+ from_native `Promise.all(#{promises.map(&:to_n)})`
22
+ end
23
+
24
+ def self.race promises
25
+ from_native `Promise.race(#{promises.map(&:to_n)})`
26
+ end
27
+
28
+ def self.reject reason
29
+ new.reject reason
30
+ end
31
+
32
+ def self.resolve value
33
+ new.resolve value
34
+ end
35
+
36
+ def then &block
37
+ Promise.from_native `#@native.then(block)`
38
+ end
39
+
40
+ def fail &block
41
+ Promise.from_native `#@native.catch(block)`
42
+ end
43
+
44
+ def always &block
45
+ Promise.from_native `#@native.then(block).fail(block)`
46
+ end
47
+
48
+ def resolve value
49
+ return self if resolved?
50
+ if rejected?
51
+ `console.warn(#{self}, #{"tried to resolve, already #{resolved? ? 'resolved' : 'rejected'} with"}, #{@value || @failure})`
52
+ end
53
+
54
+ @value = value
55
+ @resolve.call value
56
+ self
57
+ end
58
+
59
+ def reject failure
60
+ return self if rejected?
61
+ if resolved?
62
+ `console.warn(#{self}, #{"tried to reject, already #{resolved? ? 'resolved' : 'rejected'} with"}, #{@value || @failure})`
63
+ end
64
+
65
+ @failure = failure
66
+ @reject.call failure
67
+ self
68
+ end
69
+
70
+ def realized?
71
+ resolved? || rejected?
72
+ end
73
+
74
+ def resolved?
75
+ !value.nil?
76
+ end
77
+
78
+ def rejected?
79
+ !failure.nil?
80
+ end
81
+
82
+ def to_n
83
+ @native
84
+ end
85
+
86
+ %x{
87
+ Opal.defn(self, 'then', function(callback) {
88
+ var self = this;
89
+
90
+ #{self.then(&`callback`)};
91
+ });
92
+
93
+ Opal.defn(self, 'catch', function(callback) {
94
+ var self = this;
95
+
96
+ #{self.fail(&`callback`)};
97
+ });
98
+ }
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,33 @@
1
+ module Bowser
2
+ module ServiceWorker
3
+ class Request
4
+ def initialize url, options={}
5
+ @url = url
6
+ @options = options
7
+ @native = `new Request(url, #{options.to_n})`
8
+ end
9
+
10
+ def self.from_native native
11
+ request = allocate
12
+ request.instance_exec { @native = native }
13
+ request
14
+ end
15
+
16
+ def inspect
17
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} @url=#{url.inspect}>"
18
+ end
19
+
20
+ def to_s
21
+ inspect
22
+ end
23
+
24
+ def url
25
+ @url || `#@native.url`
26
+ end
27
+
28
+ def to_n
29
+ @native
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ module Bowser
2
+ module ServiceWorker
3
+ class Response
4
+ def self.from_native native
5
+ new(native) if `#{native} != null`
6
+ end
7
+
8
+ def initialize native
9
+ @native = native
10
+ end
11
+
12
+ def json
13
+ @json ||= `#@native.json()`
14
+ end
15
+
16
+ def text
17
+ @text ||= `#@native.text()`
18
+ end
19
+
20
+ def to_n
21
+ @native
22
+ end
23
+
24
+ def url
25
+ `#@native.url`
26
+ end
27
+
28
+ def status
29
+ `#@native.status`
30
+ end
31
+
32
+ def inspect
33
+ "#<#{self.class.name}:0x#{(object_id * 2).to_s(16)} @url=#{url.inspect} @status=#{status.inspect}>"
34
+ end
35
+
36
+ def to_s
37
+ inspect
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,7 @@ require 'bowser/window'
2
2
  require 'json'
3
3
 
4
4
  module Bowser
5
+ # A Ruby WebSocket class
5
6
  class WebSocket
6
7
  EVENT_NAMES = %w(
7
8
  open
@@ -10,6 +11,8 @@ module Bowser
10
11
  close
11
12
  )
12
13
 
14
+ # param url [String] the URL to connect to
15
+ # keywordparam native [JS] the native WebSocket connection
13
16
  def initialize url, native: `new WebSocket(url)`
14
17
  @url = url
15
18
  @native = native
@@ -20,6 +23,9 @@ module Bowser
20
23
  on(:close) { @connected = false }
21
24
  end
22
25
 
26
+ # Attach the given block as a handler for the specified event
27
+ #
28
+ # @param event_target [String] the name of the event to handle
23
29
  def on event_name, &block
24
30
  if EVENT_NAMES.include? event_name
25
31
  @handlers[event_name] << block
@@ -28,16 +34,23 @@ module Bowser
28
34
  end
29
35
  end
30
36
 
37
+ # Send the given message across the connection
38
+ #
39
+ # @param msg [Hash, Array] the message to send. Should be a JSON-serializable structure.
31
40
  def send_message msg
32
41
  `#@native.send(#{JSON.dump(msg)})`
33
42
  self
34
43
  end
35
44
 
45
+ # @return [Boolean] true if this socket is connected, false otherwise
36
46
  def connected?
37
47
  @connected
38
48
  end
39
49
 
40
50
  # Reconnect the websocket after a short delay if it is interrupted
51
+ #
52
+ # @keywordparam delay [Numeric] the number of seconds to wait before
53
+ # reconnecting. Defaults to 1 second.
41
54
  def autoreconnect!(delay: 1)
42
55
  return if @autoreconnect
43
56
  @autoreconnect = true
@@ -47,6 +60,9 @@ module Bowser
47
60
  end
48
61
  end
49
62
 
63
+ # Close the current connection
64
+ #
65
+ # @param reason [String, nil] the reason this socket is being closed
50
66
  def close reason=`undefined`
51
67
  `#@native.close(reason)`
52
68
  end
@@ -113,6 +129,8 @@ module Bowser
113
129
 
114
130
  module_function
115
131
 
132
+ # @param url [String] the URL to connect to
133
+ # @return [Bowser::WebSocket] a websocket connection to the given URL
116
134
  def websocket *args
117
135
  WebSocket.new(*args)
118
136
  end
@@ -11,6 +11,9 @@ module Bowser
11
11
  module_function
12
12
 
13
13
  if `#@native.requestAnimationFrame !== undefined`
14
+ # Add the given block to the current iteration of the event loop. If this
15
+ # is called from another `animation_frame` call, the block is run in the
16
+ # following iteration of the event loop.
14
17
  def animation_frame &block
15
18
  `requestAnimationFrame(function() { #{block.call} })`
16
19
  self
@@ -22,60 +25,84 @@ module Bowser
22
25
  end
23
26
  end
24
27
 
28
+ # Run the given block after the specified number of seconds has passed.
29
+ #
30
+ # @param duration [Numeric] the number of seconds to wait
25
31
  def delay duration, &block
26
32
  `setTimeout(function() { #{block.call} }, duration * 1000)`
27
33
  self
28
34
  end
29
35
 
36
+ # Run the given block every `duration` seconds
37
+ #
38
+ # @param duration [Numeric] the number of seconds between runs
30
39
  def interval duration, &block
31
40
  `setInterval(function() { #{block.call} }, duration * 1000)`
32
41
  self
33
42
  end
34
43
 
44
+ # @return [Location] the browser's Location object
35
45
  def location
36
46
  Location
37
47
  end
38
48
 
49
+ # Scroll to the specified (x,y) coordinates
39
50
  def scroll x, y
40
51
  `window.scrollTo(x, y)`
41
52
  end
42
53
 
54
+ # Wrapper for the browser's Location object
43
55
  module Location
44
56
  module_function
45
57
 
58
+ # The "hash" of the current URL
46
59
  def hash
47
60
  `window.location.hash`
48
61
  end
49
62
 
63
+ # Set the hash of the URL
64
+ #
65
+ # @param hash [String] the new value of the URL hash
50
66
  def hash= hash
51
67
  `window.location.hash = hash`
52
68
  end
53
69
 
70
+ # The path of the current URL
54
71
  def path
55
72
  `window.location.pathname`
56
73
  end
57
74
 
75
+ # The full current URL
58
76
  def href
59
77
  `window.location.href`
60
78
  end
61
79
 
80
+ # Set the current URL
81
+ #
82
+ # @param href [String] the URL to navigate to
62
83
  def href= href
63
84
  `window.location.href = href`
64
85
  end
65
86
  end
66
87
 
88
+ # The browser's history object
67
89
  module History
68
90
  module_function
69
91
 
92
+ # @return [Boolean] true if the browser supports pushState, false otherwise
70
93
  def has_push_state?
71
94
  `!!window.history.pushState`
72
95
  end
73
96
 
97
+ # Navigate to the specified path without triggering a full page reload
98
+ #
99
+ # @param path [String] the path to navigate to
74
100
  def push path
75
101
  `window.history.pushState({}, '', path)`
76
102
  end
77
103
  end
78
104
 
105
+ # return [History] the browser's History object
79
106
  def history
80
107
  History
81
108
  end
@@ -83,6 +110,7 @@ module Bowser
83
110
 
84
111
  module_function
85
112
 
113
+ # return [Window] the browser's Window object
86
114
  def window
87
115
  Window
88
116
  end