Class: DuoRuby::Server
- Defined in:
- lib/duoruby/server.rb,
lib/duoruby/server/frontend_compiler.rb
Overview
Application server built on Falcon and Async.
Server handles three HTTP routes:
-
GET / — serves an HTML shell page that loads the frontend script
-
GET /duoruby/app.js — compiles and serves the Opal frontend on demand
-
GET /duoruby/socket — upgrades to a WebSocket and drives message handlers
Subclasses can declare message handlers with on and can override #call for custom HTTP routes before delegating to super.
Defined Under Namespace
Classes: FrontendCompiler
Constant Summary collapse
- SOCKET_PATH =
Path that the browser WebSocket connects to.
"/duoruby/socket"- SCRIPT_PATH =
Path from which the compiled frontend JavaScript is served.
"/duoruby/app.js"
Instance Attribute Summary collapse
-
#groups ⇒ Hash{Symbol => Group}
readonly
All groups that have been accessed on this server.
-
#host ⇒ String
readonly
The bind host.
-
#port ⇒ Integer
readonly
The bind port.
-
#root ⇒ String
readonly
The expanded application root directory.
Attributes inherited from Channel
Class Method Summary collapse
Instance Method Summary collapse
- #authenticate(_client) ⇒ Object
- #broadcast(group_name, event, **params) ⇒ Object
-
#call(request) ⇒ Protocol::HTTP::Response
Rack-compatible request handler.
- #configure(root:, host:, port:) ⇒ Object
- #connect(id:, writer: nil, metadata: {}, &writer_block) ⇒ Object
- #disconnect(client) ⇒ Object
-
#frontend_javascript ⇒ String
Compiles the Opal frontend to a JavaScript string.
- #group(name) ⇒ Object
-
#initialize(root: Dir.pwd, host: "127.0.0.1", port: 9292) ⇒ Server
constructor
A new instance of Server.
- #receive(client, message) ⇒ Object
-
#run(output: $stdout) ⇒ Object
Starts the Falcon server and blocks until it exits.
Methods inherited from Channel
#channel, #dispatch, #handler_for, inherited, #off, #on, #one
Methods included from Channel::HandlerMethods
#channel, #handlers, included, #included, #off, #on, #one
Constructor Details
#initialize(root: Dir.pwd, host: "127.0.0.1", port: 9292) ⇒ Server
Returns a new instance of Server.
53 54 55 56 57 58 |
# File 'lib/duoruby/server.rb', line 53 def initialize(root: Dir.pwd, host: "127.0.0.1", port: 9292) super() configure(root: root, host: host, port: port) @groups = {} @next_client_id = 0 end |
Instance Attribute Details
#groups ⇒ Hash{Symbol => Group} (readonly)
Returns all groups that have been accessed on this server.
48 49 50 |
# File 'lib/duoruby/server.rb', line 48 def groups @groups end |
#host ⇒ String (readonly)
Returns the bind host.
42 43 44 |
# File 'lib/duoruby/server.rb', line 42 def host @host end |
#port ⇒ Integer (readonly)
Returns the bind port.
45 46 47 |
# File 'lib/duoruby/server.rb', line 45 def port @port end |
#root ⇒ String (readonly)
Returns the expanded application root directory.
39 40 41 |
# File 'lib/duoruby/server.rb', line 39 def root @root end |
Class Method Details
.build(root: Dir.pwd, host: "127.0.0.1", port: 9292) ⇒ Object
60 61 62 63 64 65 |
# File 'lib/duoruby/server.rb', line 60 def self.build(root: Dir.pwd, host: "127.0.0.1", port: 9292) root = File.(root) server = DuoRuby.load_app(:backend, root: root) || new(root: root, host: host, port: port) server.configure(root: root, host: host, port: port) server end |
Instance Method Details
#authenticate(_client) ⇒ Object
136 137 138 |
# File 'lib/duoruby/server.rb', line 136 def authenticate(_client) true end |
#broadcast(group_name, event, **params) ⇒ Object
151 152 153 |
# File 'lib/duoruby/server.rb', line 151 def broadcast(group_name, event, **params) group(group_name).send(event, **params) end |
#call(request) ⇒ Protocol::HTTP::Response
Rack-compatible request handler. Routes to the appropriate private handler or returns a 404. Catches StandardError and responds with a 500.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/duoruby/server.rb', line 83 def call(request) path = request.path.to_s.split("?", 2).first case path when SOCKET_PATH websocket(request) || not_found("websocket endpoint") when SCRIPT_PATH javascript when "/", "" html else not_found(path) end rescue StandardError => error text(500, "#{error.class}: #{error.}\n") end |
#configure(root:, host:, port:) ⇒ Object
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/duoruby/server.rb', line 67 def configure(root:, host:, port:) @root = File.(root) config_path = File.join(@root, "duoruby.rb") load config_path if File.file?(config_path) @host = host @port = Integer(port) DuoRuby.config.host = @host DuoRuby.config.port = @port self end |
#connect(id:, writer: nil, metadata: {}, &writer_block) ⇒ Object
128 129 130 131 132 133 134 |
# File 'lib/duoruby/server.rb', line 128 def connect(id:, writer: nil, metadata: {}, &writer_block) client = Client.new(id: id, writer: writer, metadata: , &writer_block) return client.reject unless authenticate(client) dispatch(:$connect, client) client end |
#disconnect(client) ⇒ Object
140 141 142 143 144 145 |
# File 'lib/duoruby/server.rb', line 140 def disconnect(client) dispatch(:$disconnect, client) client.cancel_pending_calls client.groups.values.each { |group| group.remove(client) } client end |
#frontend_javascript ⇒ String
Compiles the Opal frontend to a JavaScript string.
Resets Opal’s global path state, adds configured frontend gems, then builds the opal runtime followed by setup/frontend.
Note: this method mutates global Opal state (Opal.reset_paths!) and is not safe to call concurrently.
124 125 126 |
# File 'lib/duoruby/server.rb', line 124 def frontend_javascript FrontendCompiler.new(root).call end |
#group(name) ⇒ Object
147 148 149 |
# File 'lib/duoruby/server.rb', line 147 def group(name) groups[name.to_sym] ||= Group.new(name) end |
#receive(client, message) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/duoruby/server.rb', line 155 def receive(client, ) = Message.coerce() return client.resolve_call() if .event == Message::REPLY_EVENT return client.reject_call() if .event == Message::ERROR_EVENT && .reply_to results = dispatch(.event, client, **.params) client.deliver(Message.reply(.id, results.last)) if .id results rescue StandardError => error raise unless &.id client.deliver(Message.error(code: error.class.name, message: error., reply_to: .id)) end |
#run(output: $stdout) ⇒ Object
Starts the Falcon server and blocks until it exits.
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/duoruby/server.rb', line 103 def run(output: $stdout) endpoint = Async::HTTP::Endpoint.parse("http://#{host}:#{port}") Sync do task = Falcon::Server.new(self, endpoint).run output.puts "serving http://#{host}:#{port}" task.wait ensure task&.stop end end |