rasti-web 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/lib/rasti/web/channel.rb +35 -0
- data/lib/rasti/web/render.rb +14 -0
- data/lib/rasti/web/server_sent_event.rb +31 -0
- data/lib/rasti/web/stream.rb +39 -0
- data/lib/rasti/web/version.rb +1 -1
- data/lib/rasti/web.rb +5 -0
- data/spec/minitest_helper.rb +1 -0
- data/spec/render_spec.rb +38 -0
- data/spec/streaming_spec.rb +29 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc7120ef16d98aaab088dc1d70dc5605cefce9bf
|
4
|
+
data.tar.gz: eba0f9c11f7df1078c299e10c108b83885eff925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0858476701c4d950f8827374183bed0cb69bbc341cf9755b5b0282a480770bcb8a6c4a81105ae5b4f435b9035a4d8ac53f01181fa2a237243b8a12e0cdeb390
|
7
|
+
data.tar.gz: 03ea9bd24694ad5fa7e38b892d84d52e3b24bf32702133fbcef4414812eeafad52228cc8430ba77508b3fc82bdbb29b4f68c79e7e3ebd4913ac6bf0c84bddbf1
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Rasti
|
2
|
+
module Web
|
3
|
+
class Channel
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@mutex = Mutex.new
|
7
|
+
@streams = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe
|
11
|
+
Stream.new.tap do |stream|
|
12
|
+
@mutex.synchronize do
|
13
|
+
@streams << stream
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish(message)
|
19
|
+
@mutex.synchronize do
|
20
|
+
@streams.delete_if(&:closed?)
|
21
|
+
Rasti::Web.logger.debug(Channel) { "Broadcasting (#{@streams.count} connections) -> #{message}" } unless @streams.empty?
|
22
|
+
@streams.each do |stream|
|
23
|
+
stream.write message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.[](id)
|
29
|
+
@channels ||= Hash.new { |h,k| h[k] = Channel.new }
|
30
|
+
@channels[id]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/rasti/web/render.rb
CHANGED
@@ -40,6 +40,12 @@ module Rasti
|
|
40
40
|
script
|
41
41
|
end
|
42
42
|
|
43
|
+
def css(stylesheet, *args)
|
44
|
+
respond_with extract_status(args),
|
45
|
+
extract_headers(args).merge('Content-Type' => 'text/css; charset=utf-8'),
|
46
|
+
stylesheet
|
47
|
+
end
|
48
|
+
|
43
49
|
def file(filename, *args)
|
44
50
|
content_type = MIME::Types.of(filename).first.content_type
|
45
51
|
body = File.read filename
|
@@ -66,6 +72,14 @@ module Rasti
|
|
66
72
|
layout(layout_template) { view_context.render template, locals }
|
67
73
|
end
|
68
74
|
|
75
|
+
def server_sent_events(channel_id)
|
76
|
+
response.status = 200
|
77
|
+
response['Content-Type'] = 'text/event-stream'
|
78
|
+
response['Cache-Control'] = 'no-cache'
|
79
|
+
response['Connection'] = 'keep-alive'
|
80
|
+
response.body = Channel[channel_id].subscribe
|
81
|
+
end
|
82
|
+
|
69
83
|
private
|
70
84
|
|
71
85
|
def respond_with(status, headers, body)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rasti
|
2
|
+
module Web
|
3
|
+
class ServerSentEvent < String
|
4
|
+
|
5
|
+
attr_reader :data, :id, :event
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def initialize(data, options={})
|
10
|
+
@data = data
|
11
|
+
@id = options[:id]
|
12
|
+
@event = options[:event]
|
13
|
+
|
14
|
+
super serialize
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize
|
18
|
+
serialized_data = data.respond_to?(:to_json) ? data.to_json : data.to_s
|
19
|
+
|
20
|
+
message = ''
|
21
|
+
message << "id: #{id}\n" if id
|
22
|
+
message << "event: #{event}\n" if event
|
23
|
+
serialized_data.split("\n").each do |d|
|
24
|
+
message << "data: #{d}\n"
|
25
|
+
end
|
26
|
+
message << "\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Rasti
|
2
|
+
module Web
|
3
|
+
class Stream
|
4
|
+
|
5
|
+
TIMEOUT = 0.0001
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@queue = Queue.new
|
9
|
+
@closed = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(message)
|
13
|
+
raise 'Closed stream' if closed?
|
14
|
+
@queue << message
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
while opened?
|
19
|
+
message = @queue.pop
|
20
|
+
yield message if message
|
21
|
+
sleep TIMEOUT
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
@closed = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def closed?
|
30
|
+
@closed
|
31
|
+
end
|
32
|
+
|
33
|
+
def opened?
|
34
|
+
!closed?
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/rasti/web/version.rb
CHANGED
data/lib/rasti/web.rb
CHANGED
@@ -4,6 +4,7 @@ require 'json'
|
|
4
4
|
require 'mime-types'
|
5
5
|
require 'class_config'
|
6
6
|
require 'forwardable'
|
7
|
+
require 'logger'
|
7
8
|
|
8
9
|
require_relative 'web/route'
|
9
10
|
require_relative 'web/router'
|
@@ -14,6 +15,9 @@ require_relative 'web/view_context'
|
|
14
15
|
require_relative 'web/render'
|
15
16
|
require_relative 'web/request'
|
16
17
|
require_relative 'web/controller'
|
18
|
+
require_relative 'web/channel'
|
19
|
+
require_relative 'web/stream'
|
20
|
+
require_relative 'web/server_sent_event'
|
17
21
|
require_relative 'web/version'
|
18
22
|
|
19
23
|
module Rasti
|
@@ -26,6 +30,7 @@ module Rasti
|
|
26
30
|
attr_config :template_engines, [:erb]
|
27
31
|
attr_config :default_layout, 'layout'
|
28
32
|
attr_config :helpers, []
|
33
|
+
attr_config :logger, Logger.new(STDOUT)
|
29
34
|
|
30
35
|
after_config do |config|
|
31
36
|
config.helpers.each { |h| ViewContext.send :include, h }
|
data/spec/minitest_helper.rb
CHANGED
data/spec/render_spec.rb
CHANGED
@@ -204,6 +204,44 @@ describe Rasti::Web::Render do
|
|
204
204
|
|
205
205
|
end
|
206
206
|
|
207
|
+
describe 'CSS' do
|
208
|
+
|
209
|
+
it 'Body' do
|
210
|
+
render.css 'body{margin:0}'
|
211
|
+
|
212
|
+
response.status.must_equal 200
|
213
|
+
response['Content-Type'].must_equal 'text/css; charset=utf-8'
|
214
|
+
response.body.must_equal ['body{margin:0}']
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'Body and status' do
|
218
|
+
render.css 'body{margin:0}', 206
|
219
|
+
|
220
|
+
response.status.must_equal 206
|
221
|
+
response['Content-Type'].must_equal 'text/css; charset=utf-8'
|
222
|
+
response.body.must_equal ['body{margin:0}']
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'Body and headers' do
|
226
|
+
render.css 'body{margin:0}', 'Content-Encoding' => 'gzip'
|
227
|
+
|
228
|
+
response.status.must_equal 200
|
229
|
+
response['Content-Type'].must_equal 'text/css; charset=utf-8'
|
230
|
+
response['Content-Encoding'].must_equal 'gzip'
|
231
|
+
response.body.must_equal ['body{margin:0}']
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'Body, status and headers' do
|
235
|
+
render.css 'body{margin:0}', 206, 'Content-Encoding' => 'gzip'
|
236
|
+
|
237
|
+
response.status.must_equal 206
|
238
|
+
response['Content-Type'].must_equal 'text/css; charset=utf-8'
|
239
|
+
response['Content-Encoding'].must_equal 'gzip'
|
240
|
+
response.body.must_equal ['body{margin:0}']
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
207
245
|
describe 'File' do
|
208
246
|
|
209
247
|
let(:filename) { File.expand_path '../sample_file.zip', __FILE__ }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe 'Straming' do
|
4
|
+
|
5
|
+
let(:request) { Rack::Request.new Hash.new }
|
6
|
+
let(:response) { Rack::Response.new }
|
7
|
+
let(:render) { Rasti::Web::Render.new request, response }
|
8
|
+
|
9
|
+
it 'Server sent events' do
|
10
|
+
events = []
|
11
|
+
stream = render.server_sent_events :test_channel
|
12
|
+
|
13
|
+
thread = Thread.new do
|
14
|
+
stream.each { |e| events << e }
|
15
|
+
end
|
16
|
+
|
17
|
+
3.times do |i|
|
18
|
+
data = {text: "Tick #{i}"}
|
19
|
+
event = Rasti::Web::ServerSentEvent.new data, id: i, event: 'tick'
|
20
|
+
Rasti::Web::Channel[:test_channel].publish event
|
21
|
+
end
|
22
|
+
|
23
|
+
while events.count < 3; sleep 0.0001 end
|
24
|
+
stream.close
|
25
|
+
|
26
|
+
events.must_equal 3.times.map { |i| "id: #{i}\nevent: tick\ndata: {\"text\":\"Tick #{i}\"}\n\n" }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasti-web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Naiman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -183,12 +183,15 @@ files:
|
|
183
183
|
- lib/rasti-web.rb
|
184
184
|
- lib/rasti/web.rb
|
185
185
|
- lib/rasti/web/application.rb
|
186
|
+
- lib/rasti/web/channel.rb
|
186
187
|
- lib/rasti/web/controller.rb
|
187
188
|
- lib/rasti/web/endpoint.rb
|
188
189
|
- lib/rasti/web/render.rb
|
189
190
|
- lib/rasti/web/request.rb
|
190
191
|
- lib/rasti/web/route.rb
|
191
192
|
- lib/rasti/web/router.rb
|
193
|
+
- lib/rasti/web/server_sent_event.rb
|
194
|
+
- lib/rasti/web/stream.rb
|
192
195
|
- lib/rasti/web/template.rb
|
193
196
|
- lib/rasti/web/version.rb
|
194
197
|
- lib/rasti/web/view_context.rb
|
@@ -203,6 +206,7 @@ files:
|
|
203
206
|
- spec/route_spec.rb
|
204
207
|
- spec/router_spec.rb
|
205
208
|
- spec/sample_file.zip
|
209
|
+
- spec/streaming_spec.rb
|
206
210
|
- spec/template_spec.rb
|
207
211
|
- spec/views/context_and_locals.erb
|
208
212
|
- spec/views/context_method.erb
|
@@ -230,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
230
234
|
version: '0'
|
231
235
|
requirements: []
|
232
236
|
rubyforge_project:
|
233
|
-
rubygems_version: 2.
|
237
|
+
rubygems_version: 2.4.7
|
234
238
|
signing_key:
|
235
239
|
specification_version: 4
|
236
240
|
summary: Web blocks to build robust applications
|
@@ -245,6 +249,7 @@ test_files:
|
|
245
249
|
- spec/route_spec.rb
|
246
250
|
- spec/router_spec.rb
|
247
251
|
- spec/sample_file.zip
|
252
|
+
- spec/streaming_spec.rb
|
248
253
|
- spec/template_spec.rb
|
249
254
|
- spec/views/context_and_locals.erb
|
250
255
|
- spec/views/context_method.erb
|