SlideShare uma empresa Scribd logo
1 de 33
Baixar para ler offline
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

Mais conteúdo relacionado

Mais procurados

Abusing text/template for data transformation
Abusing text/template for data transformationAbusing text/template for data transformation
Abusing text/template for data transformationArnaud 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]RIEleanor McHugh
 
Encrypt all transports
Encrypt all transportsEncrypt all transports
Encrypt all transportsEleanor McHugh
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
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 VersionIan Barber
 
Introdução ao Perl 6
Introdução ao Perl 6Introdução ao Perl 6
Introdução ao Perl 6garux
 
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 heureAmaury Bouchard
 
Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015Code Generation in PHP - PHPConf 2015
Code Generation in PHP - PHPConf 2015Lin Yo-An
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The AnswerIan Barber
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleIan Barber
 

Mais procurados (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]
 

Semelhante a 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 part3masahiroookubo
 
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 JSMartin 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 SupportBen Scofield
 
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 QueueSam Hennessy
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan 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 WireAndreas Nedbal
 
Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joinedennui2342
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2markstory
 
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
 

Semelhante a 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)
 

Mais de 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.pdfEleanor McHugh
 
Generics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsGenerics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsEleanor 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 IntegrityEleanor 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 goEleanor 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 reduxEleanor McHugh
 
Identity & trust in Monitored Spaces
Identity & trust in Monitored SpacesIdentity & trust in Monitored Spaces
Identity & trust in Monitored SpacesEleanor 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 DesignEleanor 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 designEleanor McHugh
 
Anonymity, identity, trust
Anonymity, identity, trustAnonymity, identity, trust
Anonymity, identity, trustEleanor 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 GoEleanor McHugh
 
Distributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleDistributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleEleanor 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 editionEleanor 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 GoEleanor 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 goEleanor McHugh
 
Anonymity, trust, accountability
Anonymity, trust, accountabilityAnonymity, trust, accountability
Anonymity, trust, accountabilityEleanor McHugh
 
Implementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & CImplementing Virtual Machines in Go & C
Implementing Virtual Machines in Go & CEleanor McHugh
 
Implementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & CImplementing Virtual Machines in Ruby & C
Implementing Virtual Machines in Ruby & CEleanor 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 GoEleanor McHugh
 

Mais de 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
 

Último

A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 

Último (20)

A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 

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