wunderbar 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +35 -0
- data/lib/wunderbar/builder.rb +4 -2
- data/lib/wunderbar/environment.rb +5 -0
- data/lib/wunderbar/server.rb +1 -0
- data/lib/wunderbar/version.rb +1 -1
- data/lib/wunderbar/websocket.rb +112 -39
- data/wunderbar.gemspec +2 -2
- metadata +4 -4
data/README.md
CHANGED
@@ -156,6 +156,10 @@ output. This is accomplished by providing one or more of the following:
|
|
156
156
|
code
|
157
157
|
end
|
158
158
|
|
159
|
+
_websocket do
|
160
|
+
code
|
161
|
+
end
|
162
|
+
|
159
163
|
Arbitrary Ruby code can be placed in each. Form parameters are made available
|
160
164
|
as instance variables (e.g., `@name`). Host environment (CGI, Rack, Sinatra)
|
161
165
|
values are accessible as methods of the `_` object: for example `_.headers`
|
@@ -286,6 +290,37 @@ output stream, which provides access to other useful methods, for example:
|
|
286
290
|
_.print 'foo'
|
287
291
|
_.printf "Hello %s!\n", 'world'
|
288
292
|
|
293
|
+
Methods provided to Wunderbar.websocket
|
294
|
+
---
|
295
|
+
|
296
|
+
WebSocket support requires `em-websocket` to be installed.
|
297
|
+
|
298
|
+
A web socket is a bidrectional channel. `_.send` or `_.push` can be used to
|
299
|
+
send arbitrary strings. More commonly, the JSON array methods described above
|
300
|
+
can be all be used, the important difference is that the individual entries
|
301
|
+
are sent individually and as they are produced.
|
302
|
+
|
303
|
+
`_.recv` or `_.pop` can be used to receive arbitrary strings. More commonly,
|
304
|
+
`_.subscribe` is used to register a block that is used as a callback.
|
305
|
+
|
306
|
+
`_.system` will run an aritrary command. Lines of output are sent across the
|
307
|
+
websocket as they are received as JSON encoded hashes with two values: `type`
|
308
|
+
is one of `stdin`, `stdout` or `stderr`; and `line` which contains the line
|
309
|
+
itself.
|
310
|
+
|
311
|
+
Options to `_websocket` are provided as a hash:
|
312
|
+
|
313
|
+
* `:port` will chose a port number, with the default being that an
|
314
|
+
available one is picked for you.
|
315
|
+
* `:sync` set to `false` will cause the WebSocket server to be run as a
|
316
|
+
daemon process. This defaults to `true` when run from the command line and
|
317
|
+
to `false` when run as CGI.
|
318
|
+
* `buffer_limit` will limit the amount of entries retained and sent to
|
319
|
+
new clients on open requests. Default is `1`. A value of zero will disable
|
320
|
+
buffering. A value of `nil` will result in unlimited buffering. Note:
|
321
|
+
buffering is effectively unlimited until the first client connects.
|
322
|
+
|
323
|
+
|
289
324
|
Secure by default
|
290
325
|
---
|
291
326
|
|
data/lib/wunderbar/builder.rb
CHANGED
@@ -338,8 +338,10 @@ module Wunderbar
|
|
338
338
|
result = {}
|
339
339
|
args.each {|arg| result[arg.to_s] = object.send arg}
|
340
340
|
else
|
341
|
-
result =
|
342
|
-
|
341
|
+
result = []
|
342
|
+
result = @_target if name.empty? and @_target.respond_to? :<<
|
343
|
+
object.each do |item|
|
344
|
+
result << Hash[args.map {|arg| [arg.to_s, item.send(arg)]}]
|
343
345
|
end
|
344
346
|
end
|
345
347
|
end
|
data/lib/wunderbar/server.rb
CHANGED
data/lib/wunderbar/version.rb
CHANGED
data/lib/wunderbar/websocket.rb
CHANGED
@@ -6,54 +6,104 @@ require 'socket'
|
|
6
6
|
begin
|
7
7
|
require 'em-websocket'
|
8
8
|
rescue LoadError
|
9
|
-
module EM
|
10
|
-
class Channel
|
11
|
-
def initialize
|
12
|
-
require 'em-websocket'
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
9
|
end
|
17
10
|
|
18
11
|
module Wunderbar
|
19
|
-
class Channel
|
20
|
-
attr_reader :port
|
12
|
+
class Channel
|
13
|
+
attr_reader :port, :connected, :complete
|
14
|
+
|
15
|
+
def initialize(port, limit)
|
16
|
+
# verify that the port is available
|
17
|
+
TCPServer.new('0.0.0.0', port).close
|
21
18
|
|
22
|
-
def initialize(port, limit=nil)
|
23
|
-
TCPSocket.new('localhost', port).close
|
24
|
-
raise ArgumentError.new "Socket #{port} is not available"
|
25
|
-
rescue Errno::ECONNREFUSED
|
26
19
|
super()
|
27
20
|
@port = port
|
21
|
+
@connected = @complete = false
|
22
|
+
@channel1 = EM::Channel.new
|
23
|
+
@channel2 = EM::Channel.new
|
28
24
|
@memory = []
|
29
|
-
@memory_channel = subscribe do |msg|
|
25
|
+
@memory_channel = @channel1.subscribe do |msg|
|
30
26
|
@memory << msg.chomp unless Symbol === msg
|
31
|
-
@memory.shift while limit and @memory.length > limit
|
27
|
+
@memory.shift while @connected and limit and @memory.length > limit
|
32
28
|
end
|
33
29
|
websocket.run
|
34
30
|
end
|
35
31
|
|
36
32
|
def websocket
|
37
|
-
@websocket
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
ws.send msg
|
33
|
+
return @websocket if @websocket
|
34
|
+
ready = false
|
35
|
+
@websocket = Thread.new do
|
36
|
+
EM.epoll
|
37
|
+
EM.run do
|
38
|
+
connection = EventMachine::WebSocket::Connection
|
39
|
+
EM.start_server('0.0.0.0', @port, connection, {}) do |ws|
|
40
|
+
ws.onopen do
|
41
|
+
@memory.each {|msg| ws.send msg }
|
42
|
+
@connected = true
|
43
|
+
ws.close_websocket if complete
|
46
44
|
end
|
45
|
+
|
46
|
+
sid = @channel1.subscribe do |msg|
|
47
|
+
if msg == :shutdown
|
48
|
+
ws.close_websocket
|
49
|
+
else
|
50
|
+
ws.send msg
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ws.onmessage {|msg| @channel2.push msg}
|
55
|
+
|
56
|
+
ws.onclose {@channel1.unsubscribe sid}
|
47
57
|
end
|
48
|
-
|
49
|
-
|
58
|
+
EM.add_timer(0.1) {ready = true}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
sleep 0.2 until ready
|
62
|
+
@websocket
|
63
|
+
end
|
64
|
+
|
65
|
+
def subscribe(*args, &block)
|
66
|
+
@channel2.subscribe(*args, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def unsubscribe(*args, &block)
|
70
|
+
@channel2.unsubscribe(*args, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def push(*args)
|
74
|
+
@channel1.push(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def send(*args)
|
78
|
+
@channel1.push(*args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def pop(*args)
|
82
|
+
@channel2.pop(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def recv(*args)
|
86
|
+
@channel2.pop(*args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def _(*args, &block)
|
90
|
+
if block or args.length > 1
|
91
|
+
begin
|
92
|
+
builder = Wunderbar::JsonBuilder.new(Struct.new(:params).new({}))
|
93
|
+
builder._! self
|
94
|
+
builder._(*args, &block)
|
95
|
+
rescue Exception => e
|
96
|
+
self << {:type=>'stderr', :line=>e.inspect}
|
50
97
|
end
|
98
|
+
elsif args.length == 1
|
99
|
+
@channel1.push(args.first.to_json)
|
100
|
+
else
|
101
|
+
self
|
51
102
|
end
|
52
103
|
end
|
53
104
|
|
54
|
-
def
|
55
|
-
|
56
|
-
push(msg.to_json)
|
105
|
+
def <<(value)
|
106
|
+
@channel1.push(value.to_json)
|
57
107
|
end
|
58
108
|
|
59
109
|
def system(command)
|
@@ -73,26 +123,39 @@ module Wunderbar
|
|
73
123
|
end
|
74
124
|
end
|
75
125
|
|
126
|
+
def complete=(value)
|
127
|
+
@channel1.push :shutdown if value
|
128
|
+
@complete = value
|
129
|
+
end
|
130
|
+
|
76
131
|
def close
|
77
|
-
unsubscribe @memory_channel if @memory_channel
|
78
|
-
push :shutdown
|
79
|
-
sleep 1
|
132
|
+
@channel1.unsubscribe @memory_channel if @memory_channel
|
80
133
|
EM::WebSocket.stop
|
81
134
|
websocket.join
|
82
135
|
end
|
83
136
|
end
|
84
137
|
|
85
138
|
if defined? EventMachine::WebSocket
|
86
|
-
def self.websocket(
|
139
|
+
def self.websocket(opts={}, &block)
|
140
|
+
opts = {:port => opts} if Fixnum === opts
|
141
|
+
port = opts[:port]
|
142
|
+
buffer = opts.fetch(:buffer,1)
|
143
|
+
|
87
144
|
if not port
|
88
145
|
socket = TCPServer.new(0)
|
89
146
|
port = Socket.unpack_sockaddr_in(socket.getsockname).first
|
90
147
|
socket.close
|
91
148
|
end
|
92
149
|
|
93
|
-
|
150
|
+
sock1 = nil
|
151
|
+
|
152
|
+
proc = Proc.new do
|
94
153
|
begin
|
95
|
-
channel = Wunderbar::Channel.new(port)
|
154
|
+
channel = Wunderbar::Channel.new(port, buffer)
|
155
|
+
if sock1
|
156
|
+
sock1.send('x',0)
|
157
|
+
sock1.close
|
158
|
+
end
|
96
159
|
channel.instance_eval &block
|
97
160
|
rescue Exception => exception
|
98
161
|
channel._ :type=>:stderr, :line=>exception.inspect
|
@@ -101,13 +164,23 @@ module Wunderbar
|
|
101
164
|
channel._ :type=>:stderr, :line=>" #{frame}"
|
102
165
|
end
|
103
166
|
ensure
|
104
|
-
channel
|
105
|
-
|
106
|
-
|
167
|
+
if channel
|
168
|
+
channel.complete = true
|
169
|
+
sleep 5
|
170
|
+
sleep 60 unless channel.connected
|
171
|
+
channel.close
|
172
|
+
end
|
107
173
|
end
|
108
174
|
end
|
109
175
|
|
110
|
-
|
176
|
+
if opts[:sync]
|
177
|
+
instance_eval &proc
|
178
|
+
else
|
179
|
+
sock1, sock2 = UNIXSocket.pair
|
180
|
+
submit &proc
|
181
|
+
sleep 0.3 while sock2.recv(1) != 'x'
|
182
|
+
sock2.close
|
183
|
+
end
|
111
184
|
|
112
185
|
port
|
113
186
|
end
|
data/wunderbar.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "wunderbar"
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.14.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Sam Ruby"]
|
9
|
-
s.date = "2012-04-
|
9
|
+
s.date = "2012-04-30"
|
10
10
|
s.description = " Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode\n (utf-8), consistently indented, readable applications. This includes\n output that conforms to the Polyglot specification and the emerging\n results from the XML Error Recovery Community Group.\n"
|
11
11
|
s.email = "rubys@intertwingly.net"
|
12
12
|
s.files = ["wunderbar.gemspec", "README.md", "COPYING", "lib/wunderbar.rb", "lib/wunderbar", "lib/wunderbar/installation.rb", "lib/wunderbar/html-methods.rb", "lib/wunderbar/job-control.rb", "lib/wunderbar/server.rb", "lib/wunderbar/logger.rb", "lib/wunderbar/rack.rb", "lib/wunderbar/builder.rb", "lib/wunderbar/websocket.rb", "lib/wunderbar/sinatra.rb", "lib/wunderbar/environment.rb", "lib/wunderbar/rails.rb", "lib/wunderbar/cgi-methods.rb", "lib/wunderbar/cssproxy.rb", "lib/wunderbar/version.rb"]
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wunderbar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 39
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 14
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.14.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Ruby
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-04-
|
18
|
+
date: 2012-04-30 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|