roda-sse 0.2.0 → 0.3.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: 5e98e9c2879bb55ccadf0400d243d97ecff39fa4d6535a044cae739e3410878c
4
- data.tar.gz: aa3d255a8cb4ecab1ddb9b5d71c916f6907c36529a934c38467ca57b4e3cb1a7
3
+ metadata.gz: e63c433081f53c50061c380222d40ba9ea30bedd06b5d95982c3dcf9bbd15fa3
4
+ data.tar.gz: 5bd7d45309f2903e6a953d59b9c02e58c6e965d758859f990ba435eed1372f23
5
5
  SHA512:
6
- metadata.gz: dee16a19a420769ed068a7bf977b4ef3a05b1d5425e1f9bf4ab2899697e0e13ac2255dc96af84178cb18ada3d6e85645ce91b5b7b4d91508213ecb422fc9a08b
7
- data.tar.gz: 946654c542c416ae617e641e5dff7b3cb16d9fa8c5873946db293f5ff98b1d6043fdf9e71c6caa16acdf9f655affe0a14b38cf10fa89edeea0c774ebe1af02c8
6
+ metadata.gz: 18b52b5dd1fc76864d52c74ee81ede34cdc266f3dde68e83c7235aae3f96fdb0a9af1c6451afe6be0a63ee4bd68bc830b275ae371d7cb6ae3fb81b56bc2bf882
7
+ data.tar.gz: b862451ef7df47f104892033286e14de121a5716c2857f8b31ce3bda2db1d66d130cbd6bcef22ddeedfdcd166372ca6c2505704cf2750efd752362ecf371027c
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # roda-sse
2
2
 
3
- The roda-sse Roda plugin adds SSE headers and provides Rack 3 streaming for you to send your own events.
3
+ The roda-sse Roda plugin adds SSE provides a streaming interface for
4
+ server-sent events. Each stream is wrapped in an [Async](https://github.com/socketry/async) reactor
5
+ and events are sent asyncronously via tasks. The roda-sse plugin sets
6
+ appropriate SSE headers, handles disconnection errors, ensures
7
+ streams are properly closed and provies a `last_event_id` helper.
4
8
 
5
9
  ## Installation
6
10
 
@@ -15,6 +19,8 @@ https://github.com/havenwood/roda-sse
15
19
 
16
20
  ## Usage
17
21
 
22
+ See the [examples/](https://github.com/havenwood/roda-sse/tree/main/example) directory for an example app.
23
+
18
24
  roda-sse is a Roda plugin, so you need to load it into your Roda
19
25
  application similar to other plugins:
20
26
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'async'
4
- require 'async/barrier'
5
4
 
6
5
  class Roda
7
6
  module RodaPlugins
@@ -18,22 +17,15 @@ class Roda
18
17
  # end
19
18
  # end
20
19
  module SSE
21
- class Stream
22
- def initialize(&block)
23
- @block = block
24
- @barrier = Async::Barrier.new
20
+ class Output
21
+ def initialize(stream)
22
+ @stream = stream
25
23
  end
26
24
 
27
25
  def write(message)
28
26
  data = message.to_s
29
-
30
- @barrier.async do
31
- @stream.write(data)
32
- rescue Errno::ECONNRESET, Errno::EPIPE
33
- @barrier.close
34
- end
35
-
36
- data.bytesize
27
+ @stream.write(data)
28
+ return data.bytesize
37
29
  end
38
30
 
39
31
  def <<(message)
@@ -41,33 +33,37 @@ class Roda
41
33
  self
42
34
  end
43
35
 
44
- def call(stream)
45
- Sync do
46
- @stream = stream
47
- @block.call(stream)
48
- ensure
49
- close
36
+ def close(error = nil)
37
+ if stream = @stream
38
+ @stream = nil
39
+ stream.close_write(error)
50
40
  end
51
41
  end
42
+ end
52
43
 
53
- def close
54
- return if @closed
55
-
56
- @barrier.stop
57
- @stream&.close
58
- @closed = true
44
+ class Body
45
+ def initialize(block)
46
+ @block = block
47
+ end
48
+
49
+ def call(stream)
50
+ output = Output.new(stream)
51
+ @block.call(output)
52
+ rescue => error
53
+ ensure
54
+ stream.close_write(error)
59
55
  end
60
-
61
- def closed? = @closed
62
56
  end
63
57
 
64
58
  module RequestMethods
59
+ HEADERS = {
60
+ 'content-type' => 'text/event-stream',
61
+ 'cache-control' => 'no-cache',
62
+ }.freeze
63
+
65
64
  def sse(&block)
66
65
  get do
67
- response['Content-Type'] = 'text/event-stream'
68
- response['Cache-Control'] = 'no-cache'
69
-
70
- halt response.finish_with_body(Stream.new(&block))
66
+ halt [200, HEADERS.dup, Body.new(block)]
71
67
  end
72
68
  end
73
69
  end
@@ -51,37 +51,13 @@ describe 'roda-sse plugin' do
51
51
 
52
52
  stream = Minitest::Mock.new
53
53
  stream.expect(:write, nil, ["data: hola\n\n"])
54
- stream.expect(:close, nil)
54
+ stream.expect(:close_write, nil, [nil])
55
55
  response_body = last_response.instance_variable_get(:@body)
56
- assert_instance_of Roda::RodaPlugins::SSE::Stream, response_body
56
+ assert_instance_of Roda::RodaPlugins::SSE::Body, response_body
57
57
 
58
58
  response_body.call(stream)
59
59
 
60
60
  stream.verify
61
61
  end
62
62
  end
63
-
64
- describe 'stream class' do
65
- before do
66
- @stream = Roda::RodaPlugins::SSE::Stream.new do |stream|
67
- stream << 42
68
- stream.write(43)
69
- end
70
- end
71
-
72
- it 'opens' do
73
- refute @stream.closed?
74
- end
75
-
76
- it 'streams and closes' do
77
- stream = Minitest::Mock.new
78
- stream.expect(:<<, nil, [42])
79
- stream.expect(:write, nil, [43])
80
- stream.expect(:close, nil)
81
- @stream.call(stream)
82
- assert_instance_of Roda::RodaPlugins::SSE::Stream, @stream
83
-
84
- stream.verify
85
- end
86
- end
87
63
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda-sse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shannon Skipper
8
+ - Samuel Williams
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2024-11-01 00:00:00.000000000 Z
11
+ date: 2024-11-04 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: roda