SlideShare a Scribd company logo
Eleanor McHugh


http://slideshare.net/feyeleanor


http://github.com/feyeleanor/TheBrowserEnvironmentSinatra
The Browser Environment
A Systems Programmer's Perspective


[Sinatra and Ruby]
pay attention!
all code is BSD 2-clause licensed


any resemblance to actual code &
conceptstm, living or dead, is probably
your imagination playing tricks on you


if you can make money from it you're
doing a damn sight better than we are!
Sinatra
serving web content with Ruby
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :message, ARGV[0] || "Hello World"


get '/' do


settings.message


end
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :message, ARGV[0] || "Hello World"


get '/' do


erb :html_02, locals: { message: settings.message }


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby HTML</title>


</head>


<body class='container'>


<h1>TBE:Ruby HTML</h1>


<div>


<%= message %>


</div>


</body>


</html>
Microsoft's Gift


to Humanity
the XMLHttpRequest object
aka


AJAX
asynchronous javascript and XML
AJAX
Asynchronous JavaScript and XML
•JavaScript is a single-threaded language


•but browsers are event-driven environments


•so JavaScript runtimes normally have three basic threads


•one to run the main script


•one to run scripts for high priority events


•one to run scripts for low priority events


•and each event can have callbacks de
fi
ned for it
AJAX
Asynchronous JavaScript and XML
•XMLHttpRequest
fi
rst appeared in MSXML


•available in IE5 as an ActiveX component from 1999


•similar functionality in other browsers from 2000 onwards


•fully supported in IE 7 2006


•despite its name it isn't restricted to XML


•most modern uses involve JSON
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_03, locals: { commands: settings.commands }


end


get '/:command' do


params[:command]


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby AJAX</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


<% commands.each do |c, v| %>


function <%= c %>() {


var xhttp = new XMLHttpRequest();


	 	 	 	
xhttp.onreadystatechange = function() {


if (this.readyState == 4 && this.status == 200) {


print(this.responseText);


}


};


xhttp.open("GET", "<%= c %>", true);


xhttp.send();


}


<% end %>


</script>


</head>


<body>


<h1>TBE:Ruby AJAX</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="<%= c %>();"><%= c %></button>


</span>


<% end %>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_04, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


params[:command]


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby AJAX</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


function sendCommand(c) {


var xhttp = new XMLHttpRequest();


xhttp.onreadystatechange = function() {


if (this.readyState == 4) {


if (this.status == 200) {


print(this.responseText);


} else {


print(`Request Failed: ${this.status}`);


}


}


};


xhttp.open("GET", c, true);


xhttp.send();


}


</script>


</head>


<body>


<h1>TBE:Ruby AJAX</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="sendCommand('<%= c %>');"><%= c %></button>


</span>


<% end %>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_05, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


params[:command]


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby AJAX</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


function sendCommand(c) {


var xhttp = new XMLHttpRequest();


xhttp.open("GET", c, false);


xhttp.send();


if (xhttp.status == 200) {


print(xhttp.responseText);


} else {


print(`Request Failed: ${xhttp.status}`);


}


}


</script>


</head>


<body>


<h1>TBE:Ruby AJAX</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="sendCommand('<%= c %>');"><%= c %></button>


</span>


<% end %>


<span>


<button type="button" onclick="sendCommand('D');">D</button>


</span>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
fetch
a promise of things to come
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_06, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


params[:command]


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Fetch</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


async function sendCommand(c) {


let response = await fetch(c);


if (response.ok) {


let body = await response.text();


print(body);


} else {


print(`Request Failed: ${response.status}`);


}


}


</script>


</head>


<body>


<h1>TBE:Ruby Fetch</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="sendCommand('<%= c %>');"><%= c %></
button>


</span>


<% end %>


<span>


<button type="button" onclick="sendCommand('D');">D</button>


</span>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_07, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


params[:command]


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Fetch</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


async function sendCommand(c) {


let response = await fetch(c);


if (response.ok) {


let body = await response.text();


print(body);


} else {


print(`Request Failed: ${response.status}`);


}


}


</script>


</head>


<body>


<h1>TBE:Ruby Fetch</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="sendCommand('<%= c %>');"><%= c %></
button>


</span>


<% end %>


<span>


<button type="button" onclick="sendCommand('D');">D</button>


</span>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
DOM
document object model
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, {


"A": ["B", "C"],


"B": ["A", "C"],


"C": ["A", "B"]


}


get '/' do


erb :html_08, locals: { commands: settings.commands }


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Fetch</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const hide = e => {


let el = element(e);


el.disabled = true;


el.innerHTML = "X";


};


const show = (...n) => n.forEach(e => {


let el = element(e);


el.disabled = false;


el.innerHTML = e;


});


function toggle(name) {


print(name);


hide(name);


switch(name) {


<% commands.each do |c, v| %>


case '<%= c %>':


show(<%= v.map {|x| "'#{x}'" }.join(',') %>);


break;


<% end %>


};


}


</script>


</head>


<body>


<h1>TBE:Ruby Fetch</h1>


<h2>Actions</h2>


<div>


<% commands.each do |c, v| %>


<span>


<button type="button" onclick="toggle('<%= c %>');"><%= c %></button>


</span>


<% end %>


</div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


get '/' do


erb :html_09


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby DOM</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const hide = e => {


e.innerHTML = "X";


e.disabled = true;


};


const show = (...n) => n.forEach(e => {


e.innerHTML = e.id;


e.disabled = false;


});


var buttons = [];


function toggle(e) {


hide(e);


switch(e.id) {


case 'A':


show(buttons[1], buttons[0]);


break;


case 'B':


show(buttons[2], buttons[0]);


break;


case 'C':


show(buttons[2], buttons[1]);


break;


};


}


function newButton(id) {


let b = document.createElement("BUTTON");


b.id = id;


b.onclick = new Function("toggle(this);");


b.appendChild(document.createTextNode(id));


return b;


}


window.onload = () => {


['A', 'B', 'C'].forEach(n => {


buttons.unshift(newButton(n));


element("action_buttons").appendChild(buttons[0]);


});


};


</script>


</head>


<body>


<h1>TBE:Ruby DOM</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, ["A", "B", "C"]


get '/' do


erb :html_10, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


params[:command]


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby DOM Fetch/title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


async function sendCommand(c) {


fetch(c)


.then(response => {


if (response.status != 200) {


throw new Error(response.status)


}


return response.text()


})


.then(text => print(text))


.catch(e => print(`Request Failed: ${e}`));


}


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.onclick = c;


b.appendChild(document.createTextNode(n));


return b;


}


window.onload = () => {


<% commands.each do |c, v| %>


element("action_buttons").


appendChild(


newButton('<%= c %>', () => sendCommand('<%= c %>')));


<% end %>


};


</script>


</head>


<body>


<h1>TBE:Ruby DOM Fetch</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
setInterval
marking time
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


get '/' do


erb :html_11


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Timer/title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


window.onload = () => {


let i = window.setInterval(() => {


print(`setInterval ${i} triggered`);


}, 500);


let j = window.setTimeout(() => {


print(`setTimeout ${j} triggered`);


let k = window.setTimeout(() => {


window.clearInterval(i);


print(`setInterval ${k} cancelled`);


}, 2000);


}, 3000);


};


</script>


</head>


<body>


<h1>TBE:Ruby Timer</h1>


<h2>Timer Events</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


get '/' do


erb :html_12


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Timer/title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const doAfter = (i, f) => { return window.setTimeout(f, i) };


const doEvery = (i, f) => { return window.setInterval(f, i) };


window.onload = () => {


let i = doEvery(500, () => {


print(`doEvery ${i} triggered`);


});


let j = doAfter(3000, () => {


print(`doAfter ${j} triggered`);


let k = doAfter(2000, () => {


window.clearInterval(i);


print(`doAfter ${k} triggered cancelling ${i}`);


});


});


}


</script>


</head>


<body>


<h1>TBE:Ruby Timer</h1>


<h2>Timer Events</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, [


["interval", 3000],


["timeout", 5000],


["timeout", 7500],


["interval", 1000]


]


get '/' do


erb :html_13, locals: { commands: settings.commands }


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Timer/title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) };


const doAfter = (i, f) => { return window.setTimeout(f, i) };


const doEvery = (i, f) => { return window.setInterval(f, i) };


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.innerHTML = n;


b.onclick = c(b);


return b;


}


const cancelButton = (b, n, i, f) => {


print(`timer ${i} cancelled`);


b.innerHTML = n;


b.onclick = f;


}


window.onload = () => {


<% count = commands.length


commands.each do |c, i|


case c


when "interval" %>


<% count -= 1 %>


let f_<%= count %> = b => {


return () => {


var count = 1;


let i = doEvery(<%= i %>, () => {


print(`doEvery ${i} triggered ${count}`);


count++;


});


print(`doEvery ${i} cued`);


b.innerHTML = `cancel ${i}`;


b.onclick = () => {


window.clearInterval(i);


cancelButton(b, '<%= c %>', i, f_<%= count %>(b));


};


};


;


addButton('<%= c %>', f_<%= count %>);


<% when "timeout" %>


<% count -= 1 %>


let f_<%= count %> = b => {


return () => {


let i = doAfter(<%= i %>, () => {


print(`doAfter ${i} completed`);


b.innerHTML = '<%= c %>'


b.onclick = f_<%= count %>(b);


});


print(`doAfter ${i} cued`);


b.innerHTML = `cancel ${i}`;


b.onclick = () => {


window.clearTimeout(i);


cancelButton(b, '<%= c %>', i, f_<%= count %>(b));


};


};


};


addButton('<%= c %>', f_<%= count %>);


<% end


end %>


}


</script>


</head>


<body>


<h1>TBE:Ruby Timer</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


<h2>Timer Events</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :commands, {


"A" => 300,


"B" => 700,


"C" => 500


}


get '/' do


erb :html_14, locals: { commands: settings.commands }


end


get '/:command' do


if settings.commands.include? params[:command]


settings.commands[params[:command]].to_s


else


halt 404


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby Timer Fetch</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) };


const doEvery = (i, f) => { return window.setInterval(f, i) };


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.onclick = c;


b.appendChild(document.createTextNode(n));


return b;


}


var timers = {}


const clearTimer = t => {


window.clearInterval(timers[t]);


print(`no longer polling ${t}`);


timers[t] = null;


}


function doCommand(c, i) {


if (timers[c]) {


clearTimer(c);


} else {


print(`poll ${c}`);


timers[c] = doEvery(i, () => {


fetch(c)


.then(response => response.text())


.then(text => print(`polling ${c}: ${text}`))


.catch(e => print(`Request Failed: ${e}`));


});


}


}


window.onload = () => {


<% commands.each do |c, v| %>


addButton('<%= c %>', () => doCommand('<%= c %>', '<%= v %>'));


<% end %>


addButton("cancel", () => {


for (const t in timers) { clearTimer(t) }});


}


</script>


</head>


<body>


<h1>TBE:Ruby Timer Fetch</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
WebSocket
bidirectional communication
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, ["A", "B", "C"]


get '/' do


if !request.websocket?


erb :html_15, locals: {


url: settings.socket_url,


commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket connected"


ws.send "connection established"


end


ws.onmessage do |m|


warn "received: #{m}n"


if settings.commands.include? m


ws.send m


else


ws.send "unknown request #{m}"


end


end


ws.onclose do


warn "socket closed #{ws}"


end


end


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby WebSocket</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const addButton = (n, c) => {


element("action_buttons").appendChild(newButton(n, c)) };


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.onclick = c;


b.appendChild(document.createTextNode(n));


return b;


}


window.onload = () => {


var socket = new WebSocket(`ws://${location.host}<%= url %>`);


socket.onopen = (e) => print("opening socket: <%= url %>");


socket.onclose = (e) => print("closing socket: <%= url %>");


socket.onerror = (e) => print(e.message);


socket.onmessage = (m) => { print(m.data) };


<% commands.each do |c, v| %>


addButton('<%= c %>', () => socket.send('<%= c %>'));


<% end %>


addButton('D', () => socket.send('D'));


</script>


</head>


<body>


<h1>TBE:Ruby WebSocket</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, ["A", "B", "C"]


set :sockets, []


get '/' do


if !request.websocket?


erb :html_16, locals: { url: settings.socket_url, commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket #{ws} connected"


m = "connection opened: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


settings.sockets << ws


ws.send "connection established: #{ws.object_id}"


end


ws.onmessage do |m|


if settings.commands.include? m


EM.next_tick do


ws.send "message sent: #{m}"


m = "message received from #{ws.object_id}: #{m}"


settings.sockets.each { |s| s.send m unless s == ws }


end


else


ws.send "unknown command: #{m}"


end


end


ws.onclose do


warn "socket closed #{ws}"


settings.sockets.delete ws


m = "connection closed: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


end


end


end


end


<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>TBE:Ruby WebSocket</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const addButton = (n, c) => {


element("action_buttons").appendChild(newButton(n, c)) };


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.onclick = c;


b.appendChild(document.createTextNode(n));


return b;


}


window.onload = () => {


var socket = new WebSocket(`ws://${location.host}<%= url %>`);


socket.onopen = (e) => print("opening socket: <%= url %>");


socket.onclose = (e) => print("closing socket: <%= url %>");


socket.onerror = (e) => print(e.message);


socket.onmessage = (m) => { print(m.data) };


<% commands.each do |c, v| %>


addButton('<%= c %>', () => socket.send('<%= c %>'));


<% end %>


addButton('D', () => socket.send('D'));


</script>


</head>


<body>


<h1>TBE:Ruby WebSocket</h1>


<h2>Actions</h2>


<div id="action_buttons"></div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, ["A", "B", "C"]


set :sockets, []


get '/' do


if !request.websocket?


erb :html_17, locals: { url: settings.socket_url, commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket #{ws} connected"


m = "connection opened: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


settings.sockets << ws


ws.send "connection established: #{ws.object_id}"


end


ws.onmessage do |m|


if settings.commands.include? m


EM.next_tick do


ws.send "message sent: #{m}"


m = "message received from #{ws.object_id}: #{m}"


settings.sockets.each { |s| s.send m unless s == ws }


end


else


ws.send "unknown command: #{m}"


end


end


ws.onclose do


warn "socket closed #{ws}"


settings.sockets.delete ws


m = "connection closed: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


end


end


end


end


require 'websocket-eventmachine-client'


server = ENV['SERVER'] || '127.0.0.1:3000'


server_url = "ws://#{server}"


EM.run do


puts "connecting to server: #{server_url}"


ws = WebSocket::EventMachine::Client.connect :uri => server_url


puts ws


ws.onopen do


puts "connected: #{server}"


end


ws.onmessage do |m, t|


puts m


end


ws.onerror do |e|


puts "error: #{e}"


end


ws.onclose do |c, m|


puts "disconnected: #{server} (#{c}, #{m})"


exit


end


end
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, ["A", "B", "C"]


set :sockets, []


get '/' do


if !request.websocket?


erb :html_18, locals: { url: settings.socket_url, commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket #{ws} connected"


m = "connection opened: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


settings.sockets << ws


ws.send "connection established: #{ws.object_id}"


end


ws.onmessage do |m|


if settings.commands.include? m


EM.next_tick do


ws.send "message sent: #{m}"


m = "message received from #{ws.object_id}: #{m}"


settings.sockets.each { |s| s.send m unless s == ws }


end


else


ws.send "unknown command: #{m}"


end


end


ws.onclose do


warn "socket closed #{ws}"


settings.sockets.delete ws


m = "connection closed: #{ws.object_id}"


settings.sockets.each { |s| s.send m }


end


end


end


end


require 'websocket-eventmachine-client'


server = ENV['SERVER'] || 'localhost:3000'


heartbeat = ENV['HEARTBEAT'] || 2


commands = ["A", "B", "C", "D"]


server_url = "ws://#{server}"


connected = false


def rotate_buffer b


r = b.shift


b << r


r


end


EM.run do


puts "connecting to server: #{server_url}"


ws = WebSocket::EventMachine::Client.connect(:uri => "ws://127.0.0.1:3000")


puts ws


ws.onopen do


puts "connected: #{server}"


connected = true


end


ws.onmessage do |m, t|


puts m


end


ws.onerror do |e|


puts "error: #{e}"


end


ws.onclose do |c, m|


puts "disconnected: #{server} (#{c}, #{m})"


end


timer = EventMachine::PeriodicTimer.new(heartbeat) do


ws.send rotate_buffer(commands) if connected


end


end
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, [:HEARTBEAT, :PRINT, :ERROR]


set :sockets, []


set :heartbeat, 0


def message id, a, m, e = nil


{ "id" => id, "action" => a, "message" => m, "error" => e }


end


get '/' do


if !request.websocket?


erb :html_19, locals: { url: settings.socket_url, commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket #{ws} connected"


m = message 0, :PRINT, "connection opened: #{ws.object_id}"


settings.sockets.each { |s| s.send m.to_json }


ws.send message(0, :PRINT, "connection established: #{ws.object_id}").to_json


ws.send message(0, :HEARTBEAT, settings.heartbeat).to_json


settings.sockets << ws


end


ws.onmessage do |m|


m = JSON.parse m


m.merge! "id" => ws.object_id


case m["action"].to_sym


when :HEARTBEAT


settings.heartbeat = m["message"].to_i


EM.next_tick do


settings.sockets.each { |s| s.send m.to_json }


end


when :PRINT


EM.next_tick do


settings.sockets.each { |s| s.send m.to_json if s != ws }


end


else


ws.send message(0, :ERROR, m["message"], m["action"]).to_json


end


end


ws.onclose do


warn "socket closed #{ws}"


settings.sockets.delete ws


m = message(0, :PRINT, "connection closed: #{ws.object_id}").to_json


settings.sockets.each { |s| s.send m }


end


end


end


end
<!DOCTYPE html>


<html>


<head>


<meta charset='UTF-8' />


<title>WebSocket EXAMPLE</title>


<script>


const element = (e = "event_log") => { return document.getElementById(e) };


const print = m => { element().innerHTML += `<div>${m}</div>` };


const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) };


const sendMessage = (s, a, m) => {


s.send(JSON.stringify({ id: null, action: a, message: m }))


};


function newButton(n, c) {


let b = document.createElement("BUTTON");


b.onclick = c;


b.appendChild(document.createTextNode(n));


return b;


}


window.onload = () => {


var heartbeat = 0;


var socket = new WebSocket(`ws://${location.host}<%= url %>`);


socket.onopen = (e) => print("opening socket: <%= url %>");


socket.onclose = (e) => print("closing socket: <%= url %>");


socket.onerror = (e) => print(e.message);


socket.onmessage = (m) => {


let d = JSON.parse(m.data);


switch (d.action.toUpperCase()) {


case 'HEARTBEAT':


heartbeat = d.message;


element("counter").innerHTML = heartbeat;


print(`${d.id}: ${d.action} ${heartbeat}`);


break;


case 'PRINT':


print(`${d.id}: ${d.message}`);


break;


}


};


[0, 10, 100].forEach(i => {


addButton(`HEARTBEAT ${i}`, () => sendMessage(socket, 'HEARTBEAT', i));


});


}


</script>


</head>


<body>


<h1>WebSocket EXAMPLE</h1>


<h2>Actions</h2>


<div id="heartbeat">


HEARTBEAT: <span id="counter">0</span>


</div>


<div id="action_buttons"></div>


<h2>Server Output</h2>


<div id='event_log'></div>


</body>


</html>
require 'sinatra'


require 'sinatra-websocket'


set :server, %w[ thin mongrel webrick ]


set :bind, ENV['IP_ADDRESS'] || '0.0.0.0'


set :port, ENV['PORT'] || 3000


set :socket_url, ENV['SOCKET'] || '/'


set :commands, [:HEARTBEAT, :PRINT, :ERROR]


set :sockets, []


set :heartbeat, 0


def message id, a, m, e = nil


{ "id" => id, "action" => a, "message" => m, "error" => e }


end


get '/' do


if !request.websocket?


erb :html_19, locals: { url: settings.socket_url, commands: settings.commands }


else


request.websocket do |ws|


ws.onopen do


warn "websocket #{ws} connected"


m = message 0, :PRINT, "connection opened: #{ws.object_id}"


settings.sockets.each { |s| s.send m.to_json }


ws.send message(0, :PRINT, "connection established: #{ws.object_id}").to_json


ws.send message(0, :HEARTBEAT, settings.heartbeat).to_json


settings.sockets << ws


end


ws.onmessage do |m|


m = JSON.parse m


m.merge! "id" => ws.object_id


case m["action"].to_sym


when :HEARTBEAT


settings.heartbeat = m["message"].to_i


EM.next_tick do


settings.sockets.each { |s| s.send m.to_json }


end


when :PRINT


EM.next_tick do


settings.sockets.each { |s| s.send m.to_json if s != ws }


end


else


ws.send message(0, :ERROR, m["message"], m["action"]).to_json


end


end


ws.onclose do


warn "socket closed #{ws}"


settings.sockets.delete ws


m = message(0, :PRINT, "connection closed: #{ws.object_id}").to_json


settings.sockets.each { |s| s.send m }


end


end


end


end
require 'websocket-eventmachine-client'


require 'json'


server = ENV['SERVER'] || '127.0.0.1:3000'


heartbeat = ENV['HEARTBEAT'] || 1


server_url = "ws://#{server}"


connected = false


count = 0


EM.run do


puts "connecting to server: #{server_url}"


ws = WebSocket::EventMachine::Client.connect(:uri => server_url)


puts ws


ws.onopen do


puts "connected: #{server}"


connected = true


end


ws.onmessage do |m, t|


v = JSON.parse(m)


case v["action"].to_sym


when :PRINT


puts "#{v["id"]}: #{v["message"]}"


when :HEARTBEAT


count = v["message"].to_i


puts "#{v["id"]}: HEARTBEAT ==> #{count}"


when :ERROR


puts "ERROR: #{v["error"]} ==> #{v["message"]}"


end


end


ws.onerror do |e|


puts "error: #{e}"


end


ws.onclose do |c, m|


puts "disconnected: #{server} (#{c}, #{m})"


exit


end


timer = EventMachine::PeriodicTimer.new(heartbeat) do


count += 1


ws.send({ "action" => 'HEARTBEAT', "message" => count }.to_json) if connected


end


end
•http://github.com/feyeleanor


•http://slideshare.net/feyeleanor


•http://leanpub.com/GoNotebook

More Related Content

What's hot

C99.php
C99.phpC99.php
C99.php
veng33k
 
Abusing text/template for data transformation
Abusing text/template for data transformationAbusing text/template for data transformation
Abusing text/template for data transformation
Arnaud Porterie
 
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RIThe Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
Eleanor McHugh
 
Encrypt all transports
Encrypt all transportsEncrypt all transports
Encrypt all transports
Eleanor McHugh
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
Whispered secrets
Whispered secretsWhispered secrets
Whispered secrets
Eleanor McHugh
 
OSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersOSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersLin Yo-An
 
ZeroMQ Is The Answer: DPC 11 Version
ZeroMQ Is The Answer: DPC 11 VersionZeroMQ Is The Answer: DPC 11 Version
ZeroMQ Is The Answer: DPC 11 Version
Ian Barber
 
Introdução ao Perl 6
Introdução ao Perl 6Introdução ao Perl 6
Introdução ao Perl 6
garux
 
Créer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heureCréer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heure
Amaury Bouchard
 
Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015
Lin Yo-An
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The Answer
Ian Barber
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made Simple
Ian Barber
 
The most exciting features of PHP 7.1
The most exciting features of PHP 7.1The most exciting features of PHP 7.1
The most exciting features of PHP 7.1
Zend by Rogue Wave Software
 

What's hot (19)

C99.php
C99.phpC99.php
C99.php
 
Abusing text/template for data transformation
Abusing text/template for data transformationAbusing text/template for data transformation
Abusing text/template for data transformation
 
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RIThe Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
The Ruby Guide to *nix Plumbing: on the quest for efficiency with Ruby [M|K]RI
 
Encrypt all transports
Encrypt all transportsEncrypt all transports
Encrypt all transports
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Whispered secrets
Whispered secretsWhispered secrets
Whispered secrets
 
OSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersOSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP haters
 
ZeroMQ Is The Answer: DPC 11 Version
ZeroMQ Is The Answer: DPC 11 VersionZeroMQ Is The Answer: DPC 11 Version
ZeroMQ Is The Answer: DPC 11 Version
 
Introdução ao Perl 6
Introdução ao Perl 6Introdução ao Perl 6
Introdução ao Perl 6
 
Créer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heureCréer une base NoSQL en 1 heure
Créer une base NoSQL en 1 heure
 
zinno
zinnozinno
zinno
 
08 php-files
08 php-files08 php-files
08 php-files
 
Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015
 
Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The Answer
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made Simple
 
The most exciting features of PHP 7.1
The most exciting features of PHP 7.1The most exciting features of PHP 7.1
The most exciting features of PHP 7.1
 
C99
C99C99
C99
 
C99[2]
C99[2]C99[2]
C99[2]
 

Similar to The Browser Environment - A Systems Programmer's Perspective [sinatra edition]

RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”
apostlion
 
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Brian Sam-Bodden
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwrdeimos
 
Implementation of GUI Framework part3
Implementation of GUI Framework part3Implementation of GUI Framework part3
Implementation of GUI Framework part3
masahiroookubo
 
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
dantleech
 
Rails GUI Development with Ext JS
Rails GUI Development with Ext JSRails GUI Development with Ext JS
Rails GUI Development with Ext JS
Martin Rehfeld
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access RunbookTaha Shakeel
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
Ben Scofield
 
Blog Hacks 2011
Blog Hacks 2011Blog Hacks 2011
Blog Hacks 2011
Yusuke Wada
 
Html5 For Jjugccc2009fall
Html5 For Jjugccc2009fallHtml5 For Jjugccc2009fall
Html5 For Jjugccc2009fall
Shumpei Shiraishi
 
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job QueueTask Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
Sam Hennessy
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
Un-Framework - Delivering Dynamic Experiences with HTML over the Wire
Un-Framework - Delivering Dynamic Experiences with HTML over the WireUn-Framework - Delivering Dynamic Experiences with HTML over the Wire
Un-Framework - Delivering Dynamic Experiences with HTML over the Wire
Andreas Nedbal
 
Learning Svelte
Learning SvelteLearning Svelte
Learning Svelte
Christoffer Noring
 
Puppet Camp 2012
Puppet Camp 2012Puppet Camp 2012
Puppet Camp 2012
Server Density
 
Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joined
ennui2342
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
markstory
 
HTML5 - Pedro Rosa
HTML5 - Pedro RosaHTML5 - Pedro Rosa
HTML5 - Pedro Rosa
Comunidade NetPonto
 
WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)
MichaelBontyes
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)danwrong
 

Similar to The Browser Environment - A Systems Programmer's Perspective [sinatra edition] (20)

RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”
 
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
 
Implementation of GUI Framework part3
Implementation of GUI Framework part3Implementation of GUI Framework part3
Implementation of GUI Framework part3
 
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
 
Rails GUI Development with Ext JS
Rails GUI Development with Ext JSRails GUI Development with Ext JS
Rails GUI Development with Ext JS
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access Runbook
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
Blog Hacks 2011
Blog Hacks 2011Blog Hacks 2011
Blog Hacks 2011
 
Html5 For Jjugccc2009fall
Html5 For Jjugccc2009fallHtml5 For Jjugccc2009fall
Html5 For Jjugccc2009fall
 
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job QueueTask Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
Task Scheduling and Asynchronous Processing Evolved. Zend Server Job Queue
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Un-Framework - Delivering Dynamic Experiences with HTML over the Wire
Un-Framework - Delivering Dynamic Experiences with HTML over the WireUn-Framework - Delivering Dynamic Experiences with HTML over the Wire
Un-Framework - Delivering Dynamic Experiences with HTML over the Wire
 
Learning Svelte
Learning SvelteLearning Svelte
Learning Svelte
 
Puppet Camp 2012
Puppet Camp 2012Puppet Camp 2012
Puppet Camp 2012
 
Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joined
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
 
HTML5 - Pedro Rosa
HTML5 - Pedro RosaHTML5 - Pedro Rosa
HTML5 - Pedro Rosa
 
WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)
 

More from Eleanor McHugh

[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
Eleanor McHugh
 
Generics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsGenerics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient Collections
Eleanor McHugh
 
The Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data IntegrityThe Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data Integrity
Eleanor McHugh
 
An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]
Eleanor McHugh
 
An introduction to functional programming with go
An introduction to functional programming with goAn introduction to functional programming with go
An introduction to functional programming with go
Eleanor McHugh
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 redux
Eleanor McHugh
 
Identity & trust in Monitored Spaces
Identity & trust in Monitored SpacesIdentity & trust in Monitored Spaces
Identity & trust in Monitored Spaces
Eleanor McHugh
 
Don't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By DesignDon't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By Design
Eleanor McHugh
 
Don't ask, don't tell the virtues of privacy by design
Don't ask, don't tell   the virtues of privacy by designDon't ask, don't tell   the virtues of privacy by design
Don't ask, don't tell the virtues of privacy by design
Eleanor McHugh
 
Anonymity, identity, trust
Anonymity, identity, trustAnonymity, identity, trust
Anonymity, identity, trust
Eleanor McHugh
 
Going Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google GoGoing Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google Go
Eleanor McHugh
 
Distributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleDistributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at Scale
Eleanor McHugh
 
Hello Go
Hello GoHello Go
Hello Go
Eleanor McHugh
 
Go for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd editionGo for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd edition
Eleanor McHugh
 
Going Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with GoGoing Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with Go
Eleanor McHugh
 
Finding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in goFinding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in go
Eleanor McHugh
 
Anonymity, trust, accountability
Anonymity, trust, accountabilityAnonymity, trust, accountability
Anonymity, trust, accountability
Eleanor McHugh
 
Implementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & CImplementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & C
Eleanor McHugh
 
Implementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & CImplementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & C
Eleanor McHugh
 
Implementing Software Machines in C and Go
Implementing Software Machines in C and GoImplementing Software Machines in C and Go
Implementing Software Machines in C and Go
Eleanor McHugh
 

More from Eleanor McHugh (20)

[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
 
Generics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsGenerics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient Collections
 
The Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data IntegrityThe Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data Integrity
 
An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]
 
An introduction to functional programming with go
An introduction to functional programming with goAn introduction to functional programming with go
An introduction to functional programming with go
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 redux
 
Identity & trust in Monitored Spaces
Identity & trust in Monitored SpacesIdentity & trust in Monitored Spaces
Identity & trust in Monitored Spaces
 
Don't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By DesignDon't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By Design
 
Don't ask, don't tell the virtues of privacy by design
Don't ask, don't tell   the virtues of privacy by designDon't ask, don't tell   the virtues of privacy by design
Don't ask, don't tell the virtues of privacy by design
 
Anonymity, identity, trust
Anonymity, identity, trustAnonymity, identity, trust
Anonymity, identity, trust
 
Going Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google GoGoing Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google Go
 
Distributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleDistributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at Scale
 
Hello Go
Hello GoHello Go
Hello Go
 
Go for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd editionGo for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd edition
 
Going Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with GoGoing Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with Go
 
Finding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in goFinding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in go
 
Anonymity, trust, accountability
Anonymity, trust, accountabilityAnonymity, trust, accountability
Anonymity, trust, accountability
 
Implementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & CImplementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & C
 
Implementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & CImplementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & C
 
Implementing Software Machines in C and Go
Implementing Software Machines in C and GoImplementing Software Machines in C and Go
Implementing Software Machines in C and Go
 

Recently uploaded

GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
Matthew Sinclair
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
GridMate - End to end testing is a critical piece to ensure quality and avoid...
GridMate - End to end testing is a critical piece to ensure quality and avoid...GridMate - End to end testing is a critical piece to ensure quality and avoid...
GridMate - End to end testing is a critical piece to ensure quality and avoid...
ThomasParaiso2
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Nexer Digital
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
DianaGray10
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 

Recently uploaded (20)

GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
GridMate - End to end testing is a critical piece to ensure quality and avoid...
GridMate - End to end testing is a critical piece to ensure quality and avoid...GridMate - End to end testing is a critical piece to ensure quality and avoid...
GridMate - End to end testing is a critical piece to ensure quality and avoid...
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 

The Browser Environment - A Systems Programmer's Perspective [sinatra edition]

  • 2. pay attention! all code is BSD 2-clause licensed any resemblance to actual code & conceptstm, living or dead, is probably your imagination playing tricks on you if you can make money from it you're doing a damn sight better than we are!
  • 3.
  • 5. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :message, ARGV[0] || "Hello World" get '/' do settings.message end
  • 6. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :message, ARGV[0] || "Hello World" get '/' do erb :html_02, locals: { message: settings.message } end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby HTML</title> </head> <body class='container'> <h1>TBE:Ruby HTML</h1> <div> <%= message %> </div> </body> </html>
  • 7. Microsoft's Gift to Humanity the XMLHttpRequest object
  • 9. AJAX Asynchronous JavaScript and XML •JavaScript is a single-threaded language •but browsers are event-driven environments •so JavaScript runtimes normally have three basic threads •one to run the main script •one to run scripts for high priority events •one to run scripts for low priority events •and each event can have callbacks de fi ned for it
  • 10. AJAX Asynchronous JavaScript and XML •XMLHttpRequest fi rst appeared in MSXML •available in IE5 as an ActiveX component from 1999 •similar functionality in other browsers from 2000 onwards •fully supported in IE 7 2006 •despite its name it isn't restricted to XML •most modern uses involve JSON
  • 11. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_03, locals: { commands: settings.commands } end get '/:command' do params[:command] end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby AJAX</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; <% commands.each do |c, v| %> function <%= c %>() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { print(this.responseText); } }; xhttp.open("GET", "<%= c %>", true); xhttp.send(); } <% end %> </script> </head> <body> <h1>TBE:Ruby AJAX</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="<%= c %>();"><%= c %></button> </span> <% end %> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 12. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_04, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] params[:command] else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby AJAX</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; function sendCommand(c) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { print(this.responseText); } else { print(`Request Failed: ${this.status}`); } } }; xhttp.open("GET", c, true); xhttp.send(); } </script> </head> <body> <h1>TBE:Ruby AJAX</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="sendCommand('<%= c %>');"><%= c %></button> </span> <% end %> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 13. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_05, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] params[:command] else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby AJAX</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; function sendCommand(c) { var xhttp = new XMLHttpRequest(); xhttp.open("GET", c, false); xhttp.send(); if (xhttp.status == 200) { print(xhttp.responseText); } else { print(`Request Failed: ${xhttp.status}`); } } </script> </head> <body> <h1>TBE:Ruby AJAX</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="sendCommand('<%= c %>');"><%= c %></button> </span> <% end %> <span> <button type="button" onclick="sendCommand('D');">D</button> </span> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 14. fetch a promise of things to come
  • 15. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_06, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] params[:command] else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Fetch</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; async function sendCommand(c) { let response = await fetch(c); if (response.ok) { let body = await response.text(); print(body); } else { print(`Request Failed: ${response.status}`); } } </script> </head> <body> <h1>TBE:Ruby Fetch</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="sendCommand('<%= c %>');"><%= c %></ button> </span> <% end %> <span> <button type="button" onclick="sendCommand('D');">D</button> </span> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 16. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_07, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] params[:command] else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Fetch</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; async function sendCommand(c) { let response = await fetch(c); if (response.ok) { let body = await response.text(); print(body); } else { print(`Request Failed: ${response.status}`); } } </script> </head> <body> <h1>TBE:Ruby Fetch</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="sendCommand('<%= c %>');"><%= c %></ button> </span> <% end %> <span> <button type="button" onclick="sendCommand('D');">D</button> </span> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 18. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, { "A": ["B", "C"], "B": ["A", "C"], "C": ["A", "B"] } get '/' do erb :html_08, locals: { commands: settings.commands } end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Fetch</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const hide = e => { let el = element(e); el.disabled = true; el.innerHTML = "X"; }; const show = (...n) => n.forEach(e => { let el = element(e); el.disabled = false; el.innerHTML = e; }); function toggle(name) { print(name); hide(name); switch(name) { <% commands.each do |c, v| %> case '<%= c %>': show(<%= v.map {|x| "'#{x}'" }.join(',') %>); break; <% end %> }; } </script> </head> <body> <h1>TBE:Ruby Fetch</h1> <h2>Actions</h2> <div> <% commands.each do |c, v| %> <span> <button type="button" onclick="toggle('<%= c %>');"><%= c %></button> </span> <% end %> </div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 19. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 get '/' do erb :html_09 end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby DOM</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const hide = e => { e.innerHTML = "X"; e.disabled = true; }; const show = (...n) => n.forEach(e => { e.innerHTML = e.id; e.disabled = false; }); var buttons = []; function toggle(e) { hide(e); switch(e.id) { case 'A': show(buttons[1], buttons[0]); break; case 'B': show(buttons[2], buttons[0]); break; case 'C': show(buttons[2], buttons[1]); break; }; } function newButton(id) { let b = document.createElement("BUTTON"); b.id = id; b.onclick = new Function("toggle(this);"); b.appendChild(document.createTextNode(id)); return b; } window.onload = () => { ['A', 'B', 'C'].forEach(n => { buttons.unshift(newButton(n)); element("action_buttons").appendChild(buttons[0]); }); }; </script> </head> <body> <h1>TBE:Ruby DOM</h1> <h2>Actions</h2> <div id="action_buttons"></div> </body> </html>
  • 20. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, ["A", "B", "C"] get '/' do erb :html_10, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] params[:command] else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby DOM Fetch/title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; async function sendCommand(c) { fetch(c) .then(response => { if (response.status != 200) { throw new Error(response.status) } return response.text() }) .then(text => print(text)) .catch(e => print(`Request Failed: ${e}`)); } function newButton(n, c) { let b = document.createElement("BUTTON"); b.onclick = c; b.appendChild(document.createTextNode(n)); return b; } window.onload = () => { <% commands.each do |c, v| %> element("action_buttons"). appendChild( newButton('<%= c %>', () => sendCommand('<%= c %>'))); <% end %> }; </script> </head> <body> <h1>TBE:Ruby DOM Fetch</h1> <h2>Actions</h2> <div id="action_buttons"></div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 22. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 get '/' do erb :html_11 end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Timer/title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; window.onload = () => { let i = window.setInterval(() => { print(`setInterval ${i} triggered`); }, 500); let j = window.setTimeout(() => { print(`setTimeout ${j} triggered`); let k = window.setTimeout(() => { window.clearInterval(i); print(`setInterval ${k} cancelled`); }, 2000); }, 3000); }; </script> </head> <body> <h1>TBE:Ruby Timer</h1> <h2>Timer Events</h2> <div id='event_log'></div> </body> </html>
  • 23. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 get '/' do erb :html_12 end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Timer/title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const doAfter = (i, f) => { return window.setTimeout(f, i) }; const doEvery = (i, f) => { return window.setInterval(f, i) }; window.onload = () => { let i = doEvery(500, () => { print(`doEvery ${i} triggered`); }); let j = doAfter(3000, () => { print(`doAfter ${j} triggered`); let k = doAfter(2000, () => { window.clearInterval(i); print(`doAfter ${k} triggered cancelling ${i}`); }); }); } </script> </head> <body> <h1>TBE:Ruby Timer</h1> <h2>Timer Events</h2> <div id='event_log'></div> </body> </html>
  • 24. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, [ ["interval", 3000], ["timeout", 5000], ["timeout", 7500], ["interval", 1000] ] get '/' do erb :html_13, locals: { commands: settings.commands } end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Timer/title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) }; const doAfter = (i, f) => { return window.setTimeout(f, i) }; const doEvery = (i, f) => { return window.setInterval(f, i) }; function newButton(n, c) { let b = document.createElement("BUTTON"); b.innerHTML = n; b.onclick = c(b); return b; } const cancelButton = (b, n, i, f) => { print(`timer ${i} cancelled`); b.innerHTML = n; b.onclick = f; } window.onload = () => { <% count = commands.length commands.each do |c, i| case c when "interval" %> <% count -= 1 %> let f_<%= count %> = b => { return () => { var count = 1; let i = doEvery(<%= i %>, () => { print(`doEvery ${i} triggered ${count}`); count++; }); print(`doEvery ${i} cued`); b.innerHTML = `cancel ${i}`; b.onclick = () => { window.clearInterval(i); cancelButton(b, '<%= c %>', i, f_<%= count %>(b)); }; }; ; addButton('<%= c %>', f_<%= count %>); <% when "timeout" %> <% count -= 1 %> let f_<%= count %> = b => { return () => { let i = doAfter(<%= i %>, () => { print(`doAfter ${i} completed`); b.innerHTML = '<%= c %>' b.onclick = f_<%= count %>(b); }); print(`doAfter ${i} cued`); b.innerHTML = `cancel ${i}`; b.onclick = () => { window.clearTimeout(i); cancelButton(b, '<%= c %>', i, f_<%= count %>(b)); }; }; }; addButton('<%= c %>', f_<%= count %>); <% end end %> } </script> </head> <body> <h1>TBE:Ruby Timer</h1> <h2>Actions</h2> <div id="action_buttons"></div> <h2>Timer Events</h2> <div id='event_log'></div> </body> </html>
  • 25. require 'sinatra' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :commands, { "A" => 300, "B" => 700, "C" => 500 } get '/' do erb :html_14, locals: { commands: settings.commands } end get '/:command' do if settings.commands.include? params[:command] settings.commands[params[:command]].to_s else halt 404 end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby Timer Fetch</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) }; const doEvery = (i, f) => { return window.setInterval(f, i) }; function newButton(n, c) { let b = document.createElement("BUTTON"); b.onclick = c; b.appendChild(document.createTextNode(n)); return b; } var timers = {} const clearTimer = t => { window.clearInterval(timers[t]); print(`no longer polling ${t}`); timers[t] = null; } function doCommand(c, i) { if (timers[c]) { clearTimer(c); } else { print(`poll ${c}`); timers[c] = doEvery(i, () => { fetch(c) .then(response => response.text()) .then(text => print(`polling ${c}: ${text}`)) .catch(e => print(`Request Failed: ${e}`)); }); } } window.onload = () => { <% commands.each do |c, v| %> addButton('<%= c %>', () => doCommand('<%= c %>', '<%= v %>')); <% end %> addButton("cancel", () => { for (const t in timers) { clearTimer(t) }}); } </script> </head> <body> <h1>TBE:Ruby Timer Fetch</h1> <h2>Actions</h2> <div id="action_buttons"></div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 27. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, ["A", "B", "C"] get '/' do if !request.websocket? erb :html_15, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket connected" ws.send "connection established" end ws.onmessage do |m| warn "received: #{m}n" if settings.commands.include? m ws.send m else ws.send "unknown request #{m}" end end ws.onclose do warn "socket closed #{ws}" end end end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby WebSocket</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) }; function newButton(n, c) { let b = document.createElement("BUTTON"); b.onclick = c; b.appendChild(document.createTextNode(n)); return b; } window.onload = () => { var socket = new WebSocket(`ws://${location.host}<%= url %>`); socket.onopen = (e) => print("opening socket: <%= url %>"); socket.onclose = (e) => print("closing socket: <%= url %>"); socket.onerror = (e) => print(e.message); socket.onmessage = (m) => { print(m.data) }; <% commands.each do |c, v| %> addButton('<%= c %>', () => socket.send('<%= c %>')); <% end %> addButton('D', () => socket.send('D')); </script> </head> <body> <h1>TBE:Ruby WebSocket</h1> <h2>Actions</h2> <div id="action_buttons"></div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 28. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, ["A", "B", "C"] set :sockets, [] get '/' do if !request.websocket? erb :html_16, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket #{ws} connected" m = "connection opened: #{ws.object_id}" settings.sockets.each { |s| s.send m } settings.sockets << ws ws.send "connection established: #{ws.object_id}" end ws.onmessage do |m| if settings.commands.include? m EM.next_tick do ws.send "message sent: #{m}" m = "message received from #{ws.object_id}: #{m}" settings.sockets.each { |s| s.send m unless s == ws } end else ws.send "unknown command: #{m}" end end ws.onclose do warn "socket closed #{ws}" settings.sockets.delete ws m = "connection closed: #{ws.object_id}" settings.sockets.each { |s| s.send m } end end end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>TBE:Ruby WebSocket</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) }; function newButton(n, c) { let b = document.createElement("BUTTON"); b.onclick = c; b.appendChild(document.createTextNode(n)); return b; } window.onload = () => { var socket = new WebSocket(`ws://${location.host}<%= url %>`); socket.onopen = (e) => print("opening socket: <%= url %>"); socket.onclose = (e) => print("closing socket: <%= url %>"); socket.onerror = (e) => print(e.message); socket.onmessage = (m) => { print(m.data) }; <% commands.each do |c, v| %> addButton('<%= c %>', () => socket.send('<%= c %>')); <% end %> addButton('D', () => socket.send('D')); </script> </head> <body> <h1>TBE:Ruby WebSocket</h1> <h2>Actions</h2> <div id="action_buttons"></div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 29. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, ["A", "B", "C"] set :sockets, [] get '/' do if !request.websocket? erb :html_17, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket #{ws} connected" m = "connection opened: #{ws.object_id}" settings.sockets.each { |s| s.send m } settings.sockets << ws ws.send "connection established: #{ws.object_id}" end ws.onmessage do |m| if settings.commands.include? m EM.next_tick do ws.send "message sent: #{m}" m = "message received from #{ws.object_id}: #{m}" settings.sockets.each { |s| s.send m unless s == ws } end else ws.send "unknown command: #{m}" end end ws.onclose do warn "socket closed #{ws}" settings.sockets.delete ws m = "connection closed: #{ws.object_id}" settings.sockets.each { |s| s.send m } end end end end require 'websocket-eventmachine-client' server = ENV['SERVER'] || '127.0.0.1:3000' server_url = "ws://#{server}" EM.run do puts "connecting to server: #{server_url}" ws = WebSocket::EventMachine::Client.connect :uri => server_url puts ws ws.onopen do puts "connected: #{server}" end ws.onmessage do |m, t| puts m end ws.onerror do |e| puts "error: #{e}" end ws.onclose do |c, m| puts "disconnected: #{server} (#{c}, #{m})" exit end end
  • 30. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, ["A", "B", "C"] set :sockets, [] get '/' do if !request.websocket? erb :html_18, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket #{ws} connected" m = "connection opened: #{ws.object_id}" settings.sockets.each { |s| s.send m } settings.sockets << ws ws.send "connection established: #{ws.object_id}" end ws.onmessage do |m| if settings.commands.include? m EM.next_tick do ws.send "message sent: #{m}" m = "message received from #{ws.object_id}: #{m}" settings.sockets.each { |s| s.send m unless s == ws } end else ws.send "unknown command: #{m}" end end ws.onclose do warn "socket closed #{ws}" settings.sockets.delete ws m = "connection closed: #{ws.object_id}" settings.sockets.each { |s| s.send m } end end end end require 'websocket-eventmachine-client' server = ENV['SERVER'] || 'localhost:3000' heartbeat = ENV['HEARTBEAT'] || 2 commands = ["A", "B", "C", "D"] server_url = "ws://#{server}" connected = false def rotate_buffer b r = b.shift b << r r end EM.run do puts "connecting to server: #{server_url}" ws = WebSocket::EventMachine::Client.connect(:uri => "ws://127.0.0.1:3000") puts ws ws.onopen do puts "connected: #{server}" connected = true end ws.onmessage do |m, t| puts m end ws.onerror do |e| puts "error: #{e}" end ws.onclose do |c, m| puts "disconnected: #{server} (#{c}, #{m})" end timer = EventMachine::PeriodicTimer.new(heartbeat) do ws.send rotate_buffer(commands) if connected end end
  • 31. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, [:HEARTBEAT, :PRINT, :ERROR] set :sockets, [] set :heartbeat, 0 def message id, a, m, e = nil { "id" => id, "action" => a, "message" => m, "error" => e } end get '/' do if !request.websocket? erb :html_19, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket #{ws} connected" m = message 0, :PRINT, "connection opened: #{ws.object_id}" settings.sockets.each { |s| s.send m.to_json } ws.send message(0, :PRINT, "connection established: #{ws.object_id}").to_json ws.send message(0, :HEARTBEAT, settings.heartbeat).to_json settings.sockets << ws end ws.onmessage do |m| m = JSON.parse m m.merge! "id" => ws.object_id case m["action"].to_sym when :HEARTBEAT settings.heartbeat = m["message"].to_i EM.next_tick do settings.sockets.each { |s| s.send m.to_json } end when :PRINT EM.next_tick do settings.sockets.each { |s| s.send m.to_json if s != ws } end else ws.send message(0, :ERROR, m["message"], m["action"]).to_json end end ws.onclose do warn "socket closed #{ws}" settings.sockets.delete ws m = message(0, :PRINT, "connection closed: #{ws.object_id}").to_json settings.sockets.each { |s| s.send m } end end end end <!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <title>WebSocket EXAMPLE</title> <script> const element = (e = "event_log") => { return document.getElementById(e) }; const print = m => { element().innerHTML += `<div>${m}</div>` }; const addButton = (n, c) => { element("action_buttons").appendChild(newButton(n, c)) }; const sendMessage = (s, a, m) => { s.send(JSON.stringify({ id: null, action: a, message: m })) }; function newButton(n, c) { let b = document.createElement("BUTTON"); b.onclick = c; b.appendChild(document.createTextNode(n)); return b; } window.onload = () => { var heartbeat = 0; var socket = new WebSocket(`ws://${location.host}<%= url %>`); socket.onopen = (e) => print("opening socket: <%= url %>"); socket.onclose = (e) => print("closing socket: <%= url %>"); socket.onerror = (e) => print(e.message); socket.onmessage = (m) => { let d = JSON.parse(m.data); switch (d.action.toUpperCase()) { case 'HEARTBEAT': heartbeat = d.message; element("counter").innerHTML = heartbeat; print(`${d.id}: ${d.action} ${heartbeat}`); break; case 'PRINT': print(`${d.id}: ${d.message}`); break; } }; [0, 10, 100].forEach(i => { addButton(`HEARTBEAT ${i}`, () => sendMessage(socket, 'HEARTBEAT', i)); }); } </script> </head> <body> <h1>WebSocket EXAMPLE</h1> <h2>Actions</h2> <div id="heartbeat"> HEARTBEAT: <span id="counter">0</span> </div> <div id="action_buttons"></div> <h2>Server Output</h2> <div id='event_log'></div> </body> </html>
  • 32. require 'sinatra' require 'sinatra-websocket' set :server, %w[ thin mongrel webrick ] set :bind, ENV['IP_ADDRESS'] || '0.0.0.0' set :port, ENV['PORT'] || 3000 set :socket_url, ENV['SOCKET'] || '/' set :commands, [:HEARTBEAT, :PRINT, :ERROR] set :sockets, [] set :heartbeat, 0 def message id, a, m, e = nil { "id" => id, "action" => a, "message" => m, "error" => e } end get '/' do if !request.websocket? erb :html_19, locals: { url: settings.socket_url, commands: settings.commands } else request.websocket do |ws| ws.onopen do warn "websocket #{ws} connected" m = message 0, :PRINT, "connection opened: #{ws.object_id}" settings.sockets.each { |s| s.send m.to_json } ws.send message(0, :PRINT, "connection established: #{ws.object_id}").to_json ws.send message(0, :HEARTBEAT, settings.heartbeat).to_json settings.sockets << ws end ws.onmessage do |m| m = JSON.parse m m.merge! "id" => ws.object_id case m["action"].to_sym when :HEARTBEAT settings.heartbeat = m["message"].to_i EM.next_tick do settings.sockets.each { |s| s.send m.to_json } end when :PRINT EM.next_tick do settings.sockets.each { |s| s.send m.to_json if s != ws } end else ws.send message(0, :ERROR, m["message"], m["action"]).to_json end end ws.onclose do warn "socket closed #{ws}" settings.sockets.delete ws m = message(0, :PRINT, "connection closed: #{ws.object_id}").to_json settings.sockets.each { |s| s.send m } end end end end require 'websocket-eventmachine-client' require 'json' server = ENV['SERVER'] || '127.0.0.1:3000' heartbeat = ENV['HEARTBEAT'] || 1 server_url = "ws://#{server}" connected = false count = 0 EM.run do puts "connecting to server: #{server_url}" ws = WebSocket::EventMachine::Client.connect(:uri => server_url) puts ws ws.onopen do puts "connected: #{server}" connected = true end ws.onmessage do |m, t| v = JSON.parse(m) case v["action"].to_sym when :PRINT puts "#{v["id"]}: #{v["message"]}" when :HEARTBEAT count = v["message"].to_i puts "#{v["id"]}: HEARTBEAT ==> #{count}" when :ERROR puts "ERROR: #{v["error"]} ==> #{v["message"]}" end end ws.onerror do |e| puts "error: #{e}" end ws.onclose do |c, m| puts "disconnected: #{server} (#{c}, #{m})" exit end timer = EventMachine::PeriodicTimer.new(heartbeat) do count += 1 ws.send({ "action" => 'HEARTBEAT', "message" => count }.to_json) if connected end end