async-websocket 0.4.1 → 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/.editorconfig +4 -0
- data/.travis.yml +5 -5
- data/README.md +1 -1
- data/async-websocket.gemspec +2 -2
- data/examples/chat/client.rb +6 -6
- data/examples/middleware/client.rb +45 -0
- data/examples/middleware/config.ru +77 -0
- data/examples/utopia/.bowerrc +4 -0
- data/examples/utopia/.gitignore +9 -0
- data/examples/utopia/.rspec +4 -0
- data/examples/utopia/Gemfile +35 -0
- data/examples/utopia/Guardfile +29 -0
- data/examples/utopia/README.md +16 -0
- data/examples/utopia/Rakefile +8 -0
- data/examples/utopia/config.ru +47 -0
- data/examples/utopia/config/README.md +7 -0
- data/examples/utopia/config/environment.rb +8 -0
- data/examples/utopia/lib/readme.txt +1 -0
- data/examples/utopia/pages/_heading.xnode +2 -0
- data/examples/utopia/pages/_page.xnode +30 -0
- data/examples/utopia/pages/client/client.js +28 -0
- data/examples/utopia/pages/client/controller.rb +0 -0
- data/examples/utopia/pages/client/index.xnode +8 -0
- data/examples/utopia/pages/errors/exception.xnode +5 -0
- data/examples/utopia/pages/errors/file-not-found.xnode +5 -0
- data/examples/utopia/pages/links.yaml +2 -0
- data/examples/utopia/pages/server/controller.rb +21 -0
- data/examples/utopia/public/_static/icon.png +0 -0
- data/examples/utopia/public/_static/site.css +205 -0
- data/examples/utopia/public/_static/utopia-background.svg +1 -0
- data/examples/utopia/public/_static/utopia.svg +1 -0
- data/examples/utopia/public/readme.txt +1 -0
- data/examples/utopia/spec/spec_helper.rb +31 -0
- data/examples/utopia/spec/website_context.rb +11 -0
- data/examples/utopia/spec/website_spec.rb +56 -0
- data/examples/utopia/tasks/bower.rake +45 -0
- data/examples/utopia/tasks/deploy.rake +13 -0
- data/examples/utopia/tasks/development.rake +34 -0
- data/examples/utopia/tasks/environment.rake +17 -0
- data/examples/utopia/tasks/log.rake +17 -0
- data/examples/utopia/tasks/static.rake +43 -0
- data/lib/async/websocket.rb +2 -0
- data/lib/async/websocket/connection.rb +11 -1
- data/lib/async/websocket/version.rb +1 -1
- metadata +51 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c110468d58034b4bbdd08e4d39482bf24cf99207dba5f5fa478251c8751f3dbe
|
4
|
+
data.tar.gz: cd992b36fb505345834c51ae31523e35e9058d846bd1b4a85ddf43143eebc17a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36c2509873572672d504f0de96785840b52d3993b3cc7febca16d9f08ed85f7e3e73c4995380aea74de3a2d431f4db772ed4c1b4bc268c698fea7ecf7445cf01
|
7
|
+
data.tar.gz: d226a93fa0f972789b5e1f9d842fe0ec1dcffefff3b846b2e977555fc29cafa23ffe2c20782f28d39dbdea6d67bccce5fc00120e0829fe0876019f08963c39ba
|
data/.editorconfig
ADDED
data/.travis.yml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
language: ruby
|
2
|
-
sudo: false
|
3
|
-
dist: trusty
|
4
2
|
cache: bundler
|
5
3
|
|
4
|
+
before_script:
|
5
|
+
- gem update --system
|
6
|
+
- gem install bundler
|
7
|
+
|
6
8
|
matrix:
|
7
9
|
include:
|
8
|
-
- rvm: 2.0
|
9
|
-
- rvm: 2.1
|
10
|
-
- rvm: 2.2
|
11
10
|
- rvm: 2.3
|
12
11
|
- rvm: 2.4
|
12
|
+
- rvm: 2.5
|
13
13
|
- rvm: jruby-head
|
14
14
|
env: JRUBY_OPTS="--debug -X+O"
|
15
15
|
- rvm: ruby-head
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ run lambda {|env|
|
|
46
46
|
}
|
47
47
|
```
|
48
48
|
|
49
|
-
And [here is a client program](
|
49
|
+
And [here is a client program](examples/chat/client.rb) which can read input from `stdin` and send messages to the server.
|
50
50
|
|
51
51
|
## Contributing
|
52
52
|
|
data/async-websocket.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_dependency "async-io"
|
21
21
|
|
22
22
|
spec.add_development_dependency "async-rspec"
|
23
|
-
|
23
|
+
spec.add_development_dependency "falcon", "~> 0.15.0"
|
24
|
+
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.6"
|
25
26
|
spec.add_development_dependency "rspec", "~> 3.6"
|
26
27
|
spec.add_development_dependency "rake"
|
27
|
-
spec.add_development_dependency "falcon"
|
28
28
|
end
|
data/examples/chat/client.rb
CHANGED
@@ -5,10 +5,14 @@ require 'async/io/stream'
|
|
5
5
|
require 'async/http/url_endpoint'
|
6
6
|
require 'async/websocket/client'
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
USER = ARGV.pop || "anonymous"
|
9
|
+
URL = ARGV.pop || "ws://localhost:9292"
|
10
10
|
|
11
11
|
Async::Reactor.run do |task|
|
12
|
+
stdin = Async::IO::Stream.new(
|
13
|
+
Async::IO::Generic.new($stdin)
|
14
|
+
)
|
15
|
+
|
12
16
|
endpoint = Async::HTTP::URLEndpoint.parse(URL)
|
13
17
|
|
14
18
|
endpoint.connect do |socket|
|
@@ -20,10 +24,6 @@ Async::Reactor.run do |task|
|
|
20
24
|
})
|
21
25
|
|
22
26
|
task.async do
|
23
|
-
stdin = Async::IO::Stream.new(
|
24
|
-
Async::IO::Generic.new($stdin)
|
25
|
-
)
|
26
|
-
|
27
27
|
puts "Waiting for input..."
|
28
28
|
while line = stdin.read_until("\n")
|
29
29
|
puts "Sending text: #{line}"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async/reactor'
|
4
|
+
require 'async/io/stream'
|
5
|
+
require 'async/http/url_endpoint'
|
6
|
+
require 'async/websocket/client'
|
7
|
+
|
8
|
+
USER = ARGV.pop || "anonymous"
|
9
|
+
URL = ARGV.pop || "ws://localhost:9292"
|
10
|
+
|
11
|
+
Async::Reactor.run do |task|
|
12
|
+
stdin = Async::IO::Stream.new(
|
13
|
+
Async::IO::Generic.new($stdin)
|
14
|
+
)
|
15
|
+
|
16
|
+
endpoint = Async::HTTP::URLEndpoint.parse(URL)
|
17
|
+
|
18
|
+
endpoint.connect do |socket|
|
19
|
+
connection = Async::WebSocket::Client.new(socket, URL)
|
20
|
+
|
21
|
+
connection.send_message({
|
22
|
+
user: USER,
|
23
|
+
status: "connected",
|
24
|
+
})
|
25
|
+
|
26
|
+
task.async do
|
27
|
+
puts "Waiting for input..."
|
28
|
+
begin
|
29
|
+
while line = stdin.read_until("\n")
|
30
|
+
puts "Sending text: #{line}"
|
31
|
+
connection.send_message({
|
32
|
+
user: USER,
|
33
|
+
text: line,
|
34
|
+
})
|
35
|
+
end
|
36
|
+
rescue
|
37
|
+
puts "Client error: #{$!}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
while message = connection.next_message
|
42
|
+
puts "From server: #{message.inspect}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env falcon --verbose serve --concurrency 1 -c
|
2
|
+
|
3
|
+
require 'async/websocket/server'
|
4
|
+
|
5
|
+
Async.logger.level = Logger::DEBUG
|
6
|
+
|
7
|
+
class RackUpgrade
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
if ::WebSocket::Driver.websocket?(env)
|
14
|
+
env['rack.upgrade?'] = :websocket
|
15
|
+
|
16
|
+
response = @app.call(env)
|
17
|
+
|
18
|
+
if handler = env['rack.upgrade']
|
19
|
+
Async::WebSocket::Server.open(env) do |connection|
|
20
|
+
begin
|
21
|
+
while event = connection.next_event
|
22
|
+
if event.is_a? ::WebSocket::Driver::OpenEvent
|
23
|
+
handler.on_open(connection) if handler.respond_to? :on_open
|
24
|
+
elsif event.is_a? ::WebSocket::Driver::MessageEvent
|
25
|
+
handler.on_message(connection, JSON.parse(event.data))
|
26
|
+
elsif event.is_a? ::WebSocket::Driver::CloseEvent
|
27
|
+
handler.on_close(connection) if handler.respond_to? :on_close
|
28
|
+
end
|
29
|
+
end
|
30
|
+
ensure
|
31
|
+
handler.on_shutdown(connection) if handler.respond_to? :on_shutdown
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
return @app.call(env)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Chatty
|
42
|
+
def initialize
|
43
|
+
@connections = Set.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_open(connection)
|
47
|
+
Async.logger.info(self) {"on_open: #{connection}"}
|
48
|
+
@connections << connection
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_message(connection, message)
|
52
|
+
Async.logger.info(self) {"on_message: #{connection} -> #{message}"}
|
53
|
+
|
54
|
+
@connections.each do |connection|
|
55
|
+
connection.send_message(message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_shutdown(connection)
|
60
|
+
Async.logger.info(self) {"on_shutdown: #{connection}"}
|
61
|
+
@connections.delete(connection)
|
62
|
+
|
63
|
+
@connections.each do |connection|
|
64
|
+
connection.send_message(message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
use RackUpgrade
|
70
|
+
|
71
|
+
CHATTY = Chatty.new
|
72
|
+
|
73
|
+
run lambda {|env|
|
74
|
+
env['rack.upgrade'] = CHATTY
|
75
|
+
|
76
|
+
return [200, {}, []]
|
77
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
source "https://rubygems.org"
|
3
|
+
|
4
|
+
gem "utopia", "~> 2.3.0"
|
5
|
+
# gem "utopia-gallery"
|
6
|
+
# gem "utopia-analytics"
|
7
|
+
|
8
|
+
gem "rake"
|
9
|
+
gem "bundler"
|
10
|
+
|
11
|
+
gem 'async-websocket'
|
12
|
+
|
13
|
+
gem "rack-freeze", "~> 1.2"
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
# For `rake server`:
|
17
|
+
gem "guard-falcon", require: false
|
18
|
+
gem 'guard-rspec', require: false
|
19
|
+
|
20
|
+
# For `rake console`:
|
21
|
+
gem "pry"
|
22
|
+
gem "rack-test"
|
23
|
+
|
24
|
+
# For `rspec` testing:
|
25
|
+
gem "rspec"
|
26
|
+
gem "simplecov"
|
27
|
+
|
28
|
+
# For testing:
|
29
|
+
gem 'async-rspec'
|
30
|
+
end
|
31
|
+
|
32
|
+
group :production do
|
33
|
+
# Used for passenger-config to restart server after deployment:
|
34
|
+
gem "passenger"
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
group :development do
|
3
|
+
guard :falcon, port: 9292 do
|
4
|
+
watch('Gemfile.lock')
|
5
|
+
watch('config.ru')
|
6
|
+
watch(%r{^config|lib|pages/.*})
|
7
|
+
|
8
|
+
notification :off
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
guard :rspec, cmd: 'rspec' do
|
14
|
+
# Notifications can get a bit tedious:
|
15
|
+
# notification :off
|
16
|
+
|
17
|
+
# Re-run specs if they are changed:
|
18
|
+
watch(%r{^spec/.+_spec\.rb$})
|
19
|
+
watch('spec/spec_helper.rb') {'spec'}
|
20
|
+
|
21
|
+
# Run relevent specs if files in `lib/` or `pages/` are changed:
|
22
|
+
watch(%r{^lib/(.+)\.rb$}) {|match| "spec/lib/#{match[1]}_spec.rb" }
|
23
|
+
watch(%r{^pages/(.+)\.(rb|xnode)$}) {|match| "spec/pages/#{match[1]}_spec.rb"}
|
24
|
+
watch(%r{^pages/(.+)controller\.rb$}) {|match| Dir.glob("spec/pages/#{match[1]}*_spec.rb")}
|
25
|
+
|
26
|
+
# If any files in pages changes, ensure the website still works:
|
27
|
+
watch(%r{^pages/.*}) {'spec/website_spec.rb'}
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Example WebSocket Chat Server
|
2
|
+
|
3
|
+
This is a simple chat client/server implementation with specs.
|
4
|
+
|
5
|
+
## Starting Development Server
|
6
|
+
|
7
|
+
To start the development server, simply execute
|
8
|
+
|
9
|
+
> rake
|
10
|
+
Generating transient session key for development...
|
11
|
+
20:57:36 - INFO - Starting Falcon HTTP server on localhost:9292
|
12
|
+
20:57:36 - INFO - Guard::RSpec is running
|
13
|
+
20:57:36 - INFO - Guard is now watching at '...'
|
14
|
+
[1] guard(main)>
|
15
|
+
|
16
|
+
Then browse http://localhost:9292 (or as specified) to see your new site.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rackup
|
2
|
+
|
3
|
+
require_relative 'config/environment'
|
4
|
+
|
5
|
+
require 'rack/freeze'
|
6
|
+
|
7
|
+
if RACK_ENV == :production
|
8
|
+
# Handle exceptions in production with a error page and send an email notification:
|
9
|
+
use Utopia::Exceptions::Handler
|
10
|
+
use Utopia::Exceptions::Mailer
|
11
|
+
else
|
12
|
+
# We want to propate exceptions up when running tests:
|
13
|
+
use Rack::ShowExceptions unless RACK_ENV == :test
|
14
|
+
|
15
|
+
# Serve the public directory in a similar way to the web server:
|
16
|
+
use Utopia::Static, root: 'public'
|
17
|
+
end
|
18
|
+
|
19
|
+
use Rack::Sendfile
|
20
|
+
|
21
|
+
use Utopia::ContentLength
|
22
|
+
|
23
|
+
use Utopia::Redirection::Rewrite,
|
24
|
+
'/' => '/client/index'
|
25
|
+
|
26
|
+
use Utopia::Redirection::DirectoryIndex
|
27
|
+
|
28
|
+
use Utopia::Redirection::Errors,
|
29
|
+
404 => '/errors/file-not-found'
|
30
|
+
|
31
|
+
use Utopia::Localization,
|
32
|
+
:default_locale => 'en',
|
33
|
+
:locales => ['en', 'de', 'ja', 'zh']
|
34
|
+
|
35
|
+
require 'utopia/session'
|
36
|
+
use Utopia::Session,
|
37
|
+
:expires_after => 3600 * 24,
|
38
|
+
:secret => ENV['UTOPIA_SESSION_SECRET']
|
39
|
+
|
40
|
+
use Utopia::Controller
|
41
|
+
|
42
|
+
use Utopia::Static
|
43
|
+
|
44
|
+
# Serve dynamic content
|
45
|
+
use Utopia::Content
|
46
|
+
|
47
|
+
run lambda { |env| [404, {}, []] }
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Utopia Config
|
2
|
+
|
3
|
+
This directory contains `environment.rb` which is used to initialize the running environment for tasks and servers.
|
4
|
+
|
5
|
+
## Setting Environment Variables
|
6
|
+
|
7
|
+
If you wish to set environment variables on a per-deployment basis, you can do so by creating an `config/environment.yaml` and populating it with key-value pairs.
|
@@ -0,0 +1 @@
|
|
1
|
+
You can add additional code for your application in this directory, and require it directly from the config.ru.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<?r response.cache! ?>
|
5
|
+
|
6
|
+
<?r if title = self[:title] ?>
|
7
|
+
<title>#{title.gsub(/<.*?>/, "")} - Utopia</title>
|
8
|
+
<?r else ?>
|
9
|
+
<title>Utopia</title>
|
10
|
+
<?r end ?>
|
11
|
+
|
12
|
+
<base href="#{first.node.uri_path}"/>
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
14
|
+
|
15
|
+
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous" />
|
16
|
+
|
17
|
+
<link rel="icon" type="image/png" href="/_static/icon.png" />
|
18
|
+
<link rel="stylesheet" href="/_static/site.css" type="text/css" media="screen" />
|
19
|
+
</head>
|
20
|
+
|
21
|
+
<body class="#{attributes[:class]}">
|
22
|
+
<header>
|
23
|
+
<img src="/_static/utopia.svg" />
|
24
|
+
</header>
|
25
|
+
|
26
|
+
<div id="page">
|
27
|
+
<utopia:content/>
|
28
|
+
</div>
|
29
|
+
</body>
|
30
|
+
</html>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
var url = new URL('/server/connect', window.location.href);
|
3
|
+
url.protocol = url.protocol.replace('http', 'ws');
|
4
|
+
|
5
|
+
console.log("Connecting to server", url);
|
6
|
+
var server = new WebSocket(url.href);
|
7
|
+
console.log("Connected to", server);
|
8
|
+
|
9
|
+
server.onopen = function(event) {
|
10
|
+
chat.onkeypress = function(event) {
|
11
|
+
if (event.keyCode == 13) {
|
12
|
+
server.send(JSON.stringify({text: chat.value}));
|
13
|
+
|
14
|
+
chat.value = "";
|
15
|
+
}
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
server.onmessage = function(event) {
|
20
|
+
console.log("Got message", event);
|
21
|
+
|
22
|
+
var message = JSON.parse(event.data);
|
23
|
+
|
24
|
+
var pre = document.createElement('pre');
|
25
|
+
pre.innerText = message.text;
|
26
|
+
|
27
|
+
response.appendChild(pre);
|
28
|
+
};
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
prepend Actions
|
3
|
+
|
4
|
+
require 'async/websocket/server'
|
5
|
+
|
6
|
+
$connections = []
|
7
|
+
|
8
|
+
on 'connect' do |request|
|
9
|
+
Async::WebSocket::Server.open(request.env) do |connection|
|
10
|
+
$connections << connection
|
11
|
+
|
12
|
+
while message = connection.next_message
|
13
|
+
$connections.each do |connection|
|
14
|
+
puts "Server sending message: #{message.inspect}"
|
15
|
+
connection.send_message(message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
succeed!
|
21
|
+
end
|
Binary file
|
@@ -0,0 +1,205 @@
|
|
1
|
+
|
2
|
+
html {
|
3
|
+
font-family: "PT Sans", Verdana, Helvetica, Arial, sans-serif;
|
4
|
+
font-size: 16px;
|
5
|
+
}
|
6
|
+
|
7
|
+
pre {
|
8
|
+
tab-size: 2;
|
9
|
+
}
|
10
|
+
|
11
|
+
@media (min-width: 40em) {
|
12
|
+
html {
|
13
|
+
font-size: 18px;
|
14
|
+
}
|
15
|
+
|
16
|
+
pre {
|
17
|
+
tab-size: 4;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
@media (min-width: 80em) {
|
22
|
+
html {
|
23
|
+
font-size: 20px;
|
24
|
+
}
|
25
|
+
|
26
|
+
pre {
|
27
|
+
tab-size: 4;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
body {
|
32
|
+
padding: 0;
|
33
|
+
margin: 0;
|
34
|
+
|
35
|
+
background-color: #fafafa;
|
36
|
+
}
|
37
|
+
|
38
|
+
body > header {
|
39
|
+
margin: 1rem 0 1rem 0;
|
40
|
+
|
41
|
+
background-color: white;
|
42
|
+
|
43
|
+
background-image: url(utopia-background.svg);
|
44
|
+
|
45
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
46
|
+
}
|
47
|
+
|
48
|
+
body > header img {
|
49
|
+
display: block;
|
50
|
+
margin: auto;
|
51
|
+
height: 4rem;
|
52
|
+
}
|
53
|
+
|
54
|
+
p, ul, ol {
|
55
|
+
color: #555;
|
56
|
+
}
|
57
|
+
|
58
|
+
p strong {
|
59
|
+
color: #222;
|
60
|
+
}
|
61
|
+
|
62
|
+
h1, h2, h3, h4, h5, h6 {
|
63
|
+
margin: 2rem 1rem 1rem 1rem;
|
64
|
+
color: #4E8DD9;
|
65
|
+
}
|
66
|
+
|
67
|
+
h1 {
|
68
|
+
margin-bottom: 4rem;
|
69
|
+
}
|
70
|
+
|
71
|
+
h2 {
|
72
|
+
margin-top: 6rem;
|
73
|
+
}
|
74
|
+
|
75
|
+
img {
|
76
|
+
border: none;
|
77
|
+
}
|
78
|
+
|
79
|
+
a {
|
80
|
+
color: #33a;
|
81
|
+
}
|
82
|
+
|
83
|
+
a:hover {
|
84
|
+
color: #55c;
|
85
|
+
}
|
86
|
+
|
87
|
+
p, ul, ol, dl, h3 {
|
88
|
+
margin: 2rem;
|
89
|
+
}
|
90
|
+
|
91
|
+
li {
|
92
|
+
margin: 0.2rem;
|
93
|
+
}
|
94
|
+
|
95
|
+
li > ul, li > ol {
|
96
|
+
margin: 0;
|
97
|
+
}
|
98
|
+
|
99
|
+
pre {
|
100
|
+
overflow: auto;
|
101
|
+
|
102
|
+
padding: 1rem 2rem;
|
103
|
+
font-size: 0.8rem;
|
104
|
+
|
105
|
+
border-top: 1px solid #ccc;
|
106
|
+
border-bottom: 1px solid #ccc;
|
107
|
+
|
108
|
+
background-color: #eee;
|
109
|
+
}
|
110
|
+
|
111
|
+
h3 {
|
112
|
+
border-bottom: 1px solid #ccf;
|
113
|
+
}
|
114
|
+
|
115
|
+
ul {
|
116
|
+
margin-bottom: 1rem;
|
117
|
+
}
|
118
|
+
|
119
|
+
h2, h3, h4, h5, h6 {
|
120
|
+
font-weight: normal;
|
121
|
+
}
|
122
|
+
|
123
|
+
body.front h1 {
|
124
|
+
font-weight: normal;
|
125
|
+
font-size: 300%;
|
126
|
+
color: #F89432;
|
127
|
+
|
128
|
+
text-align: center;
|
129
|
+
}
|
130
|
+
|
131
|
+
footer {
|
132
|
+
text-align: right;
|
133
|
+
margin: 2rem;
|
134
|
+
font-size: 0.65rem;
|
135
|
+
color: #aaa;
|
136
|
+
}
|
137
|
+
|
138
|
+
nav {
|
139
|
+
position: absolute;
|
140
|
+
margin: 2.5rem;
|
141
|
+
font-size: 0.8rem;
|
142
|
+
color: #aaa;
|
143
|
+
}
|
144
|
+
|
145
|
+
section.features {
|
146
|
+
display: flex;
|
147
|
+
flex-wrap: wrap;
|
148
|
+
justify-content: space-around;
|
149
|
+
|
150
|
+
margin: 1rem;
|
151
|
+
}
|
152
|
+
|
153
|
+
section.features > div {
|
154
|
+
box-sizing: border-box;
|
155
|
+
|
156
|
+
flex-basis: 20rem;
|
157
|
+
flex-grow: 1;
|
158
|
+
|
159
|
+
color: #171e42;
|
160
|
+
margin: 1rem;
|
161
|
+
padding: 1rem;
|
162
|
+
|
163
|
+
padding-left: 3rem;
|
164
|
+
|
165
|
+
position: relative;
|
166
|
+
}
|
167
|
+
|
168
|
+
section.features > div i {
|
169
|
+
position: absolute;
|
170
|
+
left: 0rem;
|
171
|
+
|
172
|
+
font-size: 1.5rem;
|
173
|
+
text-align: center;
|
174
|
+
|
175
|
+
width: 3rem;
|
176
|
+
color: #fafafa;
|
177
|
+
text-shadow: 0px 0px 1px #000;
|
178
|
+
}
|
179
|
+
|
180
|
+
section.features p {
|
181
|
+
margin: 0;
|
182
|
+
maring-bottom: 1rem;
|
183
|
+
font-size: 80%;
|
184
|
+
}
|
185
|
+
|
186
|
+
section.features h2 {
|
187
|
+
margin: 0;
|
188
|
+
font-size: 1.1rem;
|
189
|
+
padding: 0;
|
190
|
+
}
|
191
|
+
|
192
|
+
form fieldset {
|
193
|
+
border: 0;
|
194
|
+
}
|
195
|
+
|
196
|
+
form fieldset textarea {
|
197
|
+
box-sizing: border-box;
|
198
|
+
|
199
|
+
width: 100%;
|
200
|
+
height: 10rem;
|
201
|
+
}
|
202
|
+
|
203
|
+
form fieldset.footer {
|
204
|
+
text-align: right;
|
205
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 10 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><rect x="0" y="0" width="10" height="56" style="fill:#f79433;"/><rect x="0" y="56" width="10" height="24" style="fill:#4e8dd8;"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 420 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g><rect x="0" y="0" width="420" height="56" style="fill:#f79433;"/><rect x="0" y="56" width="420" height="24" style="fill:#4e8dd8;"/><g><path d="M75.145,70.819c2.37,-3.097 4.173,-6.921 5.111,-11.365c0.91,-4.318 1.498,-9.261 1.498,-14.692l0,-44.762l-62.754,0l0,44.762c0,2.628 0.244,5.333 0.407,8.035c0.168,2.782 0.674,5.515 1.345,8.118c0.68,2.644 1.739,5.173 3.067,7.517c1.363,2.405 3.263,4.526 5.609,6.303c2.319,1.755 5.245,3.163 8.677,4.172c1.617,0.478 3.416,1.093 5.354,1.093l13.856,0c3.071,0 5.797,-1.058 8.131,-2.001c4.042,-1.631 7.305,-4.049 9.699,-7.18Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M151.481,18.701l0,-18.701l-62.754,-0.022l0,18.723l22.246,0l0.02,61.299l17.93,0l-0.02,-61.299l22.578,0Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M229.926,39.999c0,-22.051 -16.979,-39.992 -37.852,-39.992c-20.872,0 -37.851,17.942 -37.851,39.992c0,22.054 16.979,39.994 37.851,39.994c20.873,0 37.852,-17.94 37.852,-39.994Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M269.238,50.909c9.717,0 17.181,-2.066 22.183,-6.395c5.087,-4.399 7.667,-10.942 7.667,-19.575c0,-3.257 -0.393,-5.962 -1.167,-8.476c-0.778,-2.528 -1.883,-4.934 -3.281,-6.814c-1.401,-1.882 -3.098,-3.458 -5.045,-4.703c-1.895,-1.21 -4.003,-2.198 -6.264,-2.943c-2.239,-0.737 -4.64,-1.263 -7.139,-1.56c-2.464,-0.292 -5.016,-0.443 -7.587,-0.443l-29.468,0l0,80l17.93,0l0,-29.091l12.171,0Z" style="fill:#fff;fill-rule:nonzero;"/><rect x="304.879" y="0" width="17.93" height="80" style="fill:#fff;"/><path d="M362.589,0l-29.477,80l75.888,0l-31.247,-80l-15.164,0Z" style="fill:#fff;fill-rule:nonzero;"/></g></g></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
This directory is required by Apache/Phusion Passenger and contains static assets that are typically served using sendfile.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
if ENV['COVERAGE']
|
3
|
+
begin
|
4
|
+
require 'simplecov'
|
5
|
+
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter "/spec/"
|
8
|
+
end
|
9
|
+
|
10
|
+
if ENV['TRAVIS']
|
11
|
+
require 'coveralls'
|
12
|
+
Coveralls.wear!
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
warn "Could not load simplecov: #{$!}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'bundler/setup'
|
20
|
+
require 'utopia'
|
21
|
+
|
22
|
+
require 'async/rspec'
|
23
|
+
|
24
|
+
RSpec.configure do |config|
|
25
|
+
# Enable flags like --only-failures and --next-failure
|
26
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
27
|
+
|
28
|
+
config.expect_with :rspec do |c|
|
29
|
+
c.syntax = :expect
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
RSpec.shared_context "website" do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
let(:rackup_path) {File.expand_path('../config.ru', __dir__)}
|
8
|
+
let(:rackup_directory) {File.dirname(rackup_path)}
|
9
|
+
|
10
|
+
let(:app) {Rack::Builder.parse_file(rackup_path).first}
|
11
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
require_relative 'website_context'
|
3
|
+
|
4
|
+
require 'falcon/server'
|
5
|
+
require 'falcon/adapters/rack'
|
6
|
+
|
7
|
+
require 'async/http/url_endpoint'
|
8
|
+
require 'async/websocket/client'
|
9
|
+
|
10
|
+
# Learn about best practice specs from http://betterspecs.org
|
11
|
+
RSpec.describe "my website" do
|
12
|
+
include_context "website"
|
13
|
+
|
14
|
+
it "should have an accessible front page" do
|
15
|
+
get "/"
|
16
|
+
|
17
|
+
follow_redirect!
|
18
|
+
|
19
|
+
expect(last_response.status).to be == 200
|
20
|
+
end
|
21
|
+
|
22
|
+
context "websockets" do
|
23
|
+
include_context Async::RSpec::Reactor
|
24
|
+
|
25
|
+
let(:endpoint) {Async::HTTP::URLEndpoint.parse("http://localhost:9282")}
|
26
|
+
let(:server) {Falcon::Server.new(Falcon::Adapters::Rack.new(app), endpoint)}
|
27
|
+
|
28
|
+
let(:hello_message) do
|
29
|
+
{
|
30
|
+
"user" => "test",
|
31
|
+
"text" => "Hello World",
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
let!(:server_task) do
|
36
|
+
server_task = reactor.async do
|
37
|
+
server.run
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
after(:each) do
|
42
|
+
server_task.stop
|
43
|
+
end
|
44
|
+
|
45
|
+
it "can connect to server" do
|
46
|
+
endpoint.connect do |socket|
|
47
|
+
connection = Async::WebSocket::Client.new(socket, "ws://localhost/server/connect")
|
48
|
+
|
49
|
+
connection.send_message(hello_message)
|
50
|
+
|
51
|
+
message = connection.next_message
|
52
|
+
expect(message).to be == hello_message
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
namespace :bower do
|
3
|
+
desc 'Load the .bowerrc file and setup the environment for other tasks.'
|
4
|
+
task :bowerrc do
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
bowerrc_path = SITE_ROOT + ".bowerrc"
|
8
|
+
bowerrc = JSON.load(File.read(bowerrc_path))
|
9
|
+
|
10
|
+
@bower_package_root = SITE_ROOT + bowerrc['directory']
|
11
|
+
@bower_install_root = SITE_ROOT + bowerrc['public']
|
12
|
+
@bower_install_method = (bowerrc['install'] || :copy).to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Update the bower packages and link into the public directory.'
|
16
|
+
task :update => :bowerrc do
|
17
|
+
require 'fileutils'
|
18
|
+
require 'utopia/path'
|
19
|
+
|
20
|
+
#sh %W{bower update}
|
21
|
+
|
22
|
+
@bower_package_root.children.select(&:directory?).collect(&:basename).each do |package_directory|
|
23
|
+
install_path = @bower_install_root + package_directory
|
24
|
+
package_path = @bower_package_root + package_directory
|
25
|
+
dist_path = package_path + 'dist'
|
26
|
+
|
27
|
+
FileUtils::Verbose.rm_rf install_path
|
28
|
+
FileUtils::Verbose.mkpath(install_path.dirname)
|
29
|
+
|
30
|
+
# If a package has a dist directory, we only symlink that... otherwise we have to do the entire package, and hope that bower's ignore was setup correctly:
|
31
|
+
if File.exist? dist_path
|
32
|
+
link_path = Utopia::Path.shortest_path(dist_path, install_path)
|
33
|
+
else
|
34
|
+
link_path = Utopia::Path.shortest_path(package_path, install_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
if @bower_install_method == :symlink
|
38
|
+
# This is useful for some
|
39
|
+
FileUtils::Verbose.ln_s link_path, install_path
|
40
|
+
else
|
41
|
+
FileUtils::Verbose.cp_r File.expand_path(link_path, install_path), install_path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
desc 'Run by git post-update hook when deployed to a web server'
|
3
|
+
task :deploy do
|
4
|
+
# This task is typiclly run after the site is updated but before the server is restarted.
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Restart the application server'
|
8
|
+
task :restart do
|
9
|
+
# This task is run after the deployment task above.
|
10
|
+
if passenger_config = `which passenger-config`.chomp!
|
11
|
+
sh(passenger_config, 'restart-app', '--ignore-passenger-not-running', SITE_ROOT.to_s)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:test) do |task|
|
5
|
+
task.rspec_opts = %w{--require simplecov} if ENV['COVERAGE']
|
6
|
+
end
|
7
|
+
|
8
|
+
task :coverage do
|
9
|
+
ENV['COVERAGE'] = 'y'
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Start the development server.'
|
13
|
+
task :server => :environment do
|
14
|
+
exec('guard', '-g', 'development')
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Start the development environment which includes web server and tests.'
|
18
|
+
task :development => :environment do
|
19
|
+
exec('guard', '-g', 'development,test')
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Start an interactive console for your web application'
|
23
|
+
task :console => :environment do
|
24
|
+
require 'pry'
|
25
|
+
require 'rack/test'
|
26
|
+
|
27
|
+
include Rack::Test::Methods
|
28
|
+
|
29
|
+
def app
|
30
|
+
@app ||= Rack::Builder.parse_file(SITE_ROOT + 'config.ru').first
|
31
|
+
end
|
32
|
+
|
33
|
+
Pry.start
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
desc 'Set up the environment for running your web application'
|
3
|
+
task :environment do |task|
|
4
|
+
require SITE_ROOT + 'config/environment'
|
5
|
+
|
6
|
+
# We ensure this is part of the shell environment so if other commands are invoked they will work correctly.
|
7
|
+
ENV['RACK_ENV'] = RACK_ENV.to_s if defined?(RACK_ENV)
|
8
|
+
ENV['DATABASE_ENV'] = DATABASE_ENV.to_s if defined?(DATABASE_ENV)
|
9
|
+
|
10
|
+
# This generates a consistent session secret if one was not already provided:
|
11
|
+
if ENV['UTOPIA_SESSION_SECRET'].nil?
|
12
|
+
require 'securerandom'
|
13
|
+
|
14
|
+
warn 'Generating transient session key for development...'
|
15
|
+
ENV['UTOPIA_SESSION_SECRET'] = SecureRandom.hex(32)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
task :log do
|
3
|
+
require 'utopia/logger'
|
4
|
+
LOGGER = Utopia::Logger.new
|
5
|
+
end
|
6
|
+
|
7
|
+
namespace :log do
|
8
|
+
desc "Increase verbosity of logger to info."
|
9
|
+
task :info => :log do
|
10
|
+
LOGGER.level = Logger::INFO
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Increase verbosity of global debug."
|
14
|
+
task :debug => :log do
|
15
|
+
LOGGER.level = Logger::DEBUG
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
namespace :static do
|
3
|
+
task :static_environment do
|
4
|
+
RACK_ENV ||= :static
|
5
|
+
DATABASE_ENV ||= :production
|
6
|
+
SERVER_PORT ||= 9291
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Generate a static copy of the site."
|
10
|
+
task :generate => [:static_environment, :environment] do
|
11
|
+
require 'falcon/server'
|
12
|
+
require 'async/io'
|
13
|
+
require 'async/container'
|
14
|
+
|
15
|
+
config_path = SITE_ROOT + 'config.ru'
|
16
|
+
container_class = Async::Container::Threaded
|
17
|
+
|
18
|
+
app, options = Rack::Builder.parse_file(config_path.to_s)
|
19
|
+
|
20
|
+
container = container_class.new(concurrency: 2) do
|
21
|
+
server = Falcon::Server.new(app, [
|
22
|
+
Async::IO::Endpoint.parse("tcp://localhost:#{SERVER_PORT}", reuse_port: true)
|
23
|
+
])
|
24
|
+
|
25
|
+
server.run
|
26
|
+
end
|
27
|
+
|
28
|
+
output_path = ENV.fetch('OUTPUT_PATH') {SITE_ROOT + 'static'}
|
29
|
+
|
30
|
+
# Delete any existing stuff:
|
31
|
+
FileUtils.rm_rf(output_path)
|
32
|
+
|
33
|
+
# Copy all public assets:
|
34
|
+
Dir.glob(SITE_ROOT + 'public/*').each do |path|
|
35
|
+
FileUtils.cp_r(path, output_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate HTML pages:
|
39
|
+
system("wget", "--mirror", "--recursive", "--continue", "--convert-links", "--adjust-extension", "--no-host-directories", "--directory-prefix", output_path.to_s, "http://localhost:#{SERVER_PORT}")
|
40
|
+
|
41
|
+
container.stop
|
42
|
+
end
|
43
|
+
end
|
data/lib/async/websocket.rb
CHANGED
@@ -21,10 +21,14 @@
|
|
21
21
|
require 'websocket/driver'
|
22
22
|
require 'json'
|
23
23
|
|
24
|
+
require 'async/io/stream'
|
25
|
+
|
24
26
|
module Async
|
25
27
|
module WebSocket
|
26
28
|
# This is a basic synchronous websocket client:
|
27
29
|
class Connection
|
30
|
+
BLOCK_SIZE = Async::IO::Stream::BLOCK_SIZE
|
31
|
+
|
28
32
|
EVENTS = [:open, :message, :close]
|
29
33
|
|
30
34
|
def initialize(socket, driver)
|
@@ -50,8 +54,10 @@ module Async
|
|
50
54
|
attr :url
|
51
55
|
|
52
56
|
def next_event
|
57
|
+
@socket.flush
|
58
|
+
|
53
59
|
while @queue.empty?
|
54
|
-
data = @socket.
|
60
|
+
data = @socket.readpartial(BLOCK_SIZE)
|
55
61
|
|
56
62
|
if data and !data.empty?
|
57
63
|
@driver.parse(data)
|
@@ -75,6 +81,10 @@ module Async
|
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
84
|
+
def send_text(text)
|
85
|
+
@driver.text(text)
|
86
|
+
end
|
87
|
+
|
78
88
|
def send_message(message)
|
79
89
|
@driver.text(JSON.dump(message))
|
80
90
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-websocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-driver
|
@@ -53,49 +53,49 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: falcon
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.15.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.15.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '1.6'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '1.6'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '3.6'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '3.6'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -115,6 +115,7 @@ executables: []
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
+
- ".editorconfig"
|
118
119
|
- ".gitignore"
|
119
120
|
- ".rspec"
|
120
121
|
- ".travis.yml"
|
@@ -124,6 +125,42 @@ files:
|
|
124
125
|
- async-websocket.gemspec
|
125
126
|
- examples/chat/client.rb
|
126
127
|
- examples/chat/config.ru
|
128
|
+
- examples/middleware/client.rb
|
129
|
+
- examples/middleware/config.ru
|
130
|
+
- examples/utopia/.bowerrc
|
131
|
+
- examples/utopia/.gitignore
|
132
|
+
- examples/utopia/.rspec
|
133
|
+
- examples/utopia/Gemfile
|
134
|
+
- examples/utopia/Guardfile
|
135
|
+
- examples/utopia/README.md
|
136
|
+
- examples/utopia/Rakefile
|
137
|
+
- examples/utopia/config.ru
|
138
|
+
- examples/utopia/config/README.md
|
139
|
+
- examples/utopia/config/environment.rb
|
140
|
+
- examples/utopia/lib/readme.txt
|
141
|
+
- examples/utopia/pages/_heading.xnode
|
142
|
+
- examples/utopia/pages/_page.xnode
|
143
|
+
- examples/utopia/pages/client/client.js
|
144
|
+
- examples/utopia/pages/client/controller.rb
|
145
|
+
- examples/utopia/pages/client/index.xnode
|
146
|
+
- examples/utopia/pages/errors/exception.xnode
|
147
|
+
- examples/utopia/pages/errors/file-not-found.xnode
|
148
|
+
- examples/utopia/pages/links.yaml
|
149
|
+
- examples/utopia/pages/server/controller.rb
|
150
|
+
- examples/utopia/public/_static/icon.png
|
151
|
+
- examples/utopia/public/_static/site.css
|
152
|
+
- examples/utopia/public/_static/utopia-background.svg
|
153
|
+
- examples/utopia/public/_static/utopia.svg
|
154
|
+
- examples/utopia/public/readme.txt
|
155
|
+
- examples/utopia/spec/spec_helper.rb
|
156
|
+
- examples/utopia/spec/website_context.rb
|
157
|
+
- examples/utopia/spec/website_spec.rb
|
158
|
+
- examples/utopia/tasks/bower.rake
|
159
|
+
- examples/utopia/tasks/deploy.rake
|
160
|
+
- examples/utopia/tasks/development.rake
|
161
|
+
- examples/utopia/tasks/environment.rake
|
162
|
+
- examples/utopia/tasks/log.rake
|
163
|
+
- examples/utopia/tasks/static.rake
|
127
164
|
- lib/async/websocket.rb
|
128
165
|
- lib/async/websocket/client.rb
|
129
166
|
- lib/async/websocket/connection.rb
|