SlideShare una empresa de Scribd logo
1 de 47
Building high-performance APIs for
the video game industry with Goliath,
Grape, and EventMachine
Matt E. Patterson
Digimonkey Studios
Monday, June 10, 13
Matt Patterson
Software Consultant
(Ruby, Rails, Agile, etc.)
Web software since 1998 (ColdFusion and
PHP)
Ruby and Rails since 2006 (Rails 1.0)
Monday, June 10, 13
Game Web Services
Must be fast.
Must be reliable.
Must be scalable...
...and able to handle sudden bursts
(launches, new DLC, etc.)
Monday, June 10, 13
Goliath
open-source, non-blocking, asychronous Ruby web server
framework from postrank-labs
EventMachine reactor.
HTTP parser, Rack, Ruby 1.9+, Ruby Fibers
each request executes in its own Fiber
Monday, June 10, 13
Ubiquitous Goliath
“Hello, world” example
require 'goliath'
class Hello < Goliath::API
def response(env)
[ 200, {}, "Hello, world!" ]
end
end
Monday, June 10, 13
Ubiquitous Goliath
“Hello, world” example
require 'goliath'
class Hello < Goliath::API
def response(env)
[ 200, {}, "Hello, world!" ]
end
end
$ ruby hello.rb -sv -p 8000
Monday, June 10, 13
Ubiquitous Goliath
“Hello, world” example
require 'goliath'
class Hello < Goliath::API
def response(env)
[ 200, {}, "Hello, world!" ]
end
end
$ ruby hello.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
Monday, June 10, 13
Ubiquitous Goliath
“Hello, world” example
require 'goliath'
class Hello < Goliath::API
def response(env)
[ 200, {}, "Hello, world!" ]
end
end
$ ruby hello.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
$ curl http://lvh.me:8000/
Monday, June 10, 13
Ubiquitous Goliath
“Hello, world” example
require 'goliath'
class Hello < Goliath::API
def response(env)
[ 200, {}, "Hello, world!" ]
end
end
$ ruby hello.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
$ curl http://lvh.me:8000/
Hello, world!
Monday, June 10, 13
What I needed...
a full-featured API
versioned endpoints
multiple resources
standard RESTful CRUD
JSON requests / responses
secured requests
logging
localization / translation
file attachments with S3
storage
asynchronous MySQL
queries
tests!
Monday, June 10, 13
What I used...
Grape
REST-like API micro-
framework for Ruby
Carrierwave, Fog, MiniMagick
file uploads and S3 support
Rspec + FactoryGirl
testing
Rabl + MultiJson
JSON requests / responses
em-synchrony + mysql2
async MySQL
globalize3
localization / translation
standalone_migrations
Rails-style migrations
capistrano
deployment stuff
Monday, June 10, 13
Simple Goliath + Grape
./app.rb
require 'rubygems'
require 'bundler/setup'
require 'goliath'
require 'em-synchrony/activerecord'
require 'grape'
Dir["./app/models/*.rb"].each { |f| require f }
require './app/api'
class Application < Goliath::API
def response(env)
::API.call(env)
end
end
Monday, June 10, 13
Simple Goliath + Grape
./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
mount APIv1::Unlocks
resource 'servicehealth' do
desc "Returns a basic status report."
get "/" do
MultiJson.dump({
status: 'OK',
environment: Goliath::env })
end
end
end
Monday, June 10, 13
Simple Goliath + Grape
./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
mount APIv1::Unlocks
resource 'servicehealth' do
desc "Returns a basic status report."
get "/" do
MultiJson.dump({
status: 'OK',
environment: Goliath::env })
end
end
end
Monday, June 10, 13
Simple Goliath + Grape
./app/apis/v1/unlocks.rb
class APIv1
class Unlocks < Grape::API
version 'v1', using: :path, format: :json
resource :unlocks do
# GET /unlocks/1.json
desc "Returns a single Unlock record by ID"
get "/:id" do
unlock = Unlock.find(params[:id])
custom_render "api_v1/unlocks/show", unlock, 200
end
end
end
end
Monday, June 10, 13
Simple Goliath + Grape
./app/apis/v1/unlocks.rb
class APIv1
class Unlocks < Grape::API
version 'v1', using: :path, format: :json
resource :unlocks do
# GET /unlocks/1.json
desc "Returns a single Unlock record by ID"
get "/:id" do
unlock = Unlock.find(params[:id])
custom_render "api_v1/unlocks/show", unlock, 200
end
end
end
end
Wait, what?
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
back to our ./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
helpers do
def custom_render(rabl_template, object, status, args={})
args[:format] ||= 'json'
args[:success] ||= true
render_options = { format: args[:format] }
render_options[:locals] = args[:locals] if args[:locals]
data = Rabl::Renderer.new(rabl_template, object, render_options).render
%({ "success": #{args[:success]}, "data": #{data} })
end
end
mount APIv1::Unlocks
...
Monday, June 10, 13
Simple Goliath + Grape
./app/views/api_v1/unlocks/show.json.rabl
attributes :id, :name, :code, :description, :created_at
node(:unique_tags) { |unlock| unlock.tags.uniq }
child :images => :images do
attributes :id, :caption, :mime_type, :url
end
Monday, June 10, 13
Simple Goliath + Grape
./app/views/api_v1/unlocks/show.json.rabl
attributes :id, :name, :code, :description, :created_at
node(:unique_tags) { |unlock| unlock.tags.uniq }
child :images => :images do
attributes :id, :caption, :mime_type, :url
end
Monday, June 10, 13
Simple Goliath + Grape
./app/views/api_v1/unlocks/show.json.rabl
attributes :id, :name, :code, :description, :created_at
node(:unique_tags) { |unlock| unlock.tags.uniq }
child :images => :images do
attributes :id, :caption, :mime_type, :url
end
renders =>
Monday, June 10, 13
Simple Goliath + Grape
./app/views/api_v1/unlocks/show.json.rabl
attributes :id, :name, :code, :description, :created_at
node(:unique_tags) { |unlock| unlock.tags.uniq }
child :images => :images do
attributes :id, :caption, :mime_type, :url
end
renders =>
{ "id":1,"name":"Fancy
Thing","code":"001FT","description":"Enim doloribus id
minima.","unique_tags":["foo","bar","woot"],"images":
[{"image":{"id":"1","caption":"dolor sit
amet","mime_type":"image/jpg","url":"http://s3.amazon.com/
whatever.jpg"}}],"created_at":1364916167.000000000 }
Monday, June 10, 13
Simple Goliath + Grape
./app/views/api_v1/unlocks/show.json.rabl
attributes :id, :name, :code, :description, :created_at
node(:unique_tags) { |unlock| unlock.tags.uniq }
child :images => :images do
attributes :id, :caption, :mime_type, :url
end
renders =>
{ "success": true, "data": {"id":1,"name":"Fancy
Thing","code":"001FT","description":"Enim doloribus id
minima.","unique_tags":["foo","bar","woot"],"images":
[{"image":{"id":"1","caption":"dolor sit
amet","mime_type":"image/jpg","url":"http://s3.amazon.com/
whatever.jpg"}}],"created_at":1364916167.000000000} }
Monday, June 10, 13
Simple Goliath + Grape
./app/models/unlock.rb
Well, that would have worked, if we had an Unlock model...
Monday, June 10, 13
Simple Goliath + Grape
./app/models/unlock.rb
Well, that would have worked, if we had an Unlock model...
require 'rocket_tag'
class Unlock < ActiveRecord::Base
has_many :images, as: :image_attachable, dependent: :destroy
attr_taggable :tags
validates :name, presence: true
validates :code, presence: true
end
Monday, June 10, 13
Simple Goliath + Grape
./app/models/unlock.rb
Well, that would have worked, if we had an Unlock model...
require 'rocket_tag'
class Unlock < ActiveRecord::Base
has_many :images, as: :image_attachable, dependent: :destroy
attr_taggable :tags
validates :name, presence: true
validates :code, presence: true
end
Ordinary ActiveRecord like you’re accustomed to...
Monday, June 10, 13
Simple Goliath + Grape
Try it out!
Monday, June 10, 13
Simple Goliath + Grape
Try it out!
$ ruby app.rb -sv -p 8000
Monday, June 10, 13
Simple Goliath + Grape
Try it out!
$ ruby app.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
Monday, June 10, 13
Simple Goliath + Grape
Try it out!
$ ruby app.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
$ curl http://lvh.me:8000/v1/unlocks/1
Monday, June 10, 13
Simple Goliath + Grape
Try it out!
$ ruby app.rb -sv -p 8000
[45584:INFO] 2013-05-28 11:52:52 :: Starting server on
0.0.0.0:8000 in development mode. Watch out for stones.
$ curl http://lvh.me:8000/v1/unlocks/1
{ "success": true, "data": {"id":1,"name":"Fancy
Thing","code":"001FT","description":"Enim doloribus id
minima.","unique_tags":["foo","bar","woot"],"images":
[{"image":{"id":"1","caption":"dolor sit
amet","mime_type":"image/jpg","url":"http://s3.amazon.com/
whatever.jpg"}}],"created_at":1364916167.000000000}}} }
Monday, June 10, 13
Simple Goliath + Grape
“Could the API
give us users
too? That’d be
great...”
Monday, June 10, 13
Simple Goliath + Grape
./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
mount APIv1::Unlocks
resource 'servicehealth' do
desc "Returns a basic status report."
get "/" do
MultiJson.dump({
status: 'OK',
environment: Goliath::env })
end
end
end
Monday, June 10, 13
Simple Goliath + Grape
./app/api.rb
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
mount APIv1::Unlocks
resource 'servicehealth' do
desc "Returns a basic status report."
get "/" do
MultiJson.dump({
status: 'OK',
environment: Goliath::env })
end
end
end
Remember me?
Monday, June 10, 13
Simple Goliath + Grape
./app/api.rb
Remember me?
Dir["./app/apis/v1/*.rb"].each { |f| require f }
class API < Grape::API
mount APIv1::Unlocks
mount APIv1::Users
resource 'servicehealth' do
desc "Returns a basic status report."
get "/" do
MultiJson.dump({
status: 'OK',
environment: Goliath::env })
end
end
end
Monday, June 10, 13
Need communication?
Build a Client Gem!
Monday, June 10, 13
Need communication?
Build a Client Gem!
require 'virtus'
require 'rest_client'
require 'multi_json'
module UnlocksClient
class Unlock
include Virtus
attribute :id
attribute :name
attribute :code
attribute :description
attribute :tags
attr_accessor :media_rewards
def self.find(id, params={})
client = RestClient::Resource.new("#{BASE_URL)}/unlocks/#{id}")
response = client.get({params: params})
data = MultiJson.load(response)["data"]
return nil if !response || data.empty?
new(params)
end
end
end
Monday, June 10, 13
Asynch MySQL Gotcha
Used to Rails?
If you’re not paying attention, you might do this...
Monday, June 10, 13
Asynch MySQL Gotcha
Used to Rails?
If you’re not paying attention, you might do this...
development:
host: localhost
adapter: mysql2
database: unlocks_dev
pool: 20
timeout: 5000
reconnect: true
username: dbuser
password: 123whatever
Monday, June 10, 13
Asynch MySQL Gotcha
Used to Rails?
If you’re not paying attention, you might do this...
development:
host: localhost
adapter: em_mysql2
database: unlocks_dev
pool: 20
timeout: 5000
reconnect: true
username: dbuser
password: 123whatever
Monday, June 10, 13
Asynch MySQL Gotcha
Used to Rails?
If you’re not paying attention, you might do this...
development:
host: localhost
adapter: em_mysql2
database: unlocks_dev
pool: 20
timeout: 5000
reconnect: true
username: dbuser
password: 123whatever
YMMV
Monday, June 10, 13
Goliath Tips
curl is your friend on the command line.
Console mode!
ruby app.rb -svC
using Pry to debug stuff: Just add binding.pry
Monday, June 10, 13
Links
Goliath / Grape / EM Stuff
https://github.com/
postrank-labs/goliath
https://github.com/
igrigorik/em-synchrony
https://github.com/
intridea/grape
Matt Stuff
code.digimonkey.com
mepatterson.net
github.com/mepatterson
twitter.com/mepatterson
Monday, June 10, 13

Más contenido relacionado

Último

A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Karmanjay Verma
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Mark Simos
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...itnewsafrica
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...BookNet Canada
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsYoss Cohen
 
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security ObservabilityGlenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security Observabilityitnewsafrica
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 

Último (20)

A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
Tampa BSides - The No BS SOC (slides from April 6, 2024 talk)
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platforms
 
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security ObservabilityGlenn Lazarus- Why Your Observability Strategy Needs Security Observability
Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 

Destacado

Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...DevGAMM Conference
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationErica Santiago
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellSaba Software
 

Destacado (20)

Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
 

Building high-performance APIs for the video game industry with Goliath, Grape and EventMachine

  • 1. Building high-performance APIs for the video game industry with Goliath, Grape, and EventMachine Matt E. Patterson Digimonkey Studios Monday, June 10, 13
  • 2. Matt Patterson Software Consultant (Ruby, Rails, Agile, etc.) Web software since 1998 (ColdFusion and PHP) Ruby and Rails since 2006 (Rails 1.0) Monday, June 10, 13
  • 3. Game Web Services Must be fast. Must be reliable. Must be scalable... ...and able to handle sudden bursts (launches, new DLC, etc.) Monday, June 10, 13
  • 4. Goliath open-source, non-blocking, asychronous Ruby web server framework from postrank-labs EventMachine reactor. HTTP parser, Rack, Ruby 1.9+, Ruby Fibers each request executes in its own Fiber Monday, June 10, 13
  • 5. Ubiquitous Goliath “Hello, world” example require 'goliath' class Hello < Goliath::API def response(env) [ 200, {}, "Hello, world!" ] end end Monday, June 10, 13
  • 6. Ubiquitous Goliath “Hello, world” example require 'goliath' class Hello < Goliath::API def response(env) [ 200, {}, "Hello, world!" ] end end $ ruby hello.rb -sv -p 8000 Monday, June 10, 13
  • 7. Ubiquitous Goliath “Hello, world” example require 'goliath' class Hello < Goliath::API def response(env) [ 200, {}, "Hello, world!" ] end end $ ruby hello.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. Monday, June 10, 13
  • 8. Ubiquitous Goliath “Hello, world” example require 'goliath' class Hello < Goliath::API def response(env) [ 200, {}, "Hello, world!" ] end end $ ruby hello.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. $ curl http://lvh.me:8000/ Monday, June 10, 13
  • 9. Ubiquitous Goliath “Hello, world” example require 'goliath' class Hello < Goliath::API def response(env) [ 200, {}, "Hello, world!" ] end end $ ruby hello.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. $ curl http://lvh.me:8000/ Hello, world! Monday, June 10, 13
  • 10. What I needed... a full-featured API versioned endpoints multiple resources standard RESTful CRUD JSON requests / responses secured requests logging localization / translation file attachments with S3 storage asynchronous MySQL queries tests! Monday, June 10, 13
  • 11. What I used... Grape REST-like API micro- framework for Ruby Carrierwave, Fog, MiniMagick file uploads and S3 support Rspec + FactoryGirl testing Rabl + MultiJson JSON requests / responses em-synchrony + mysql2 async MySQL globalize3 localization / translation standalone_migrations Rails-style migrations capistrano deployment stuff Monday, June 10, 13
  • 12. Simple Goliath + Grape ./app.rb require 'rubygems' require 'bundler/setup' require 'goliath' require 'em-synchrony/activerecord' require 'grape' Dir["./app/models/*.rb"].each { |f| require f } require './app/api' class Application < Goliath::API def response(env) ::API.call(env) end end Monday, June 10, 13
  • 13. Simple Goliath + Grape ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API mount APIv1::Unlocks resource 'servicehealth' do desc "Returns a basic status report." get "/" do MultiJson.dump({ status: 'OK', environment: Goliath::env }) end end end Monday, June 10, 13
  • 14. Simple Goliath + Grape ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API mount APIv1::Unlocks resource 'servicehealth' do desc "Returns a basic status report." get "/" do MultiJson.dump({ status: 'OK', environment: Goliath::env }) end end end Monday, June 10, 13
  • 15. Simple Goliath + Grape ./app/apis/v1/unlocks.rb class APIv1 class Unlocks < Grape::API version 'v1', using: :path, format: :json resource :unlocks do # GET /unlocks/1.json desc "Returns a single Unlock record by ID" get "/:id" do unlock = Unlock.find(params[:id]) custom_render "api_v1/unlocks/show", unlock, 200 end end end end Monday, June 10, 13
  • 16. Simple Goliath + Grape ./app/apis/v1/unlocks.rb class APIv1 class Unlocks < Grape::API version 'v1', using: :path, format: :json resource :unlocks do # GET /unlocks/1.json desc "Returns a single Unlock record by ID" get "/:id" do unlock = Unlock.find(params[:id]) custom_render "api_v1/unlocks/show", unlock, 200 end end end end Wait, what? Monday, June 10, 13
  • 17. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 18. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 19. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 20. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 21. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 22. Simple Goliath + Grape back to our ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API helpers do def custom_render(rabl_template, object, status, args={}) args[:format] ||= 'json' args[:success] ||= true render_options = { format: args[:format] } render_options[:locals] = args[:locals] if args[:locals] data = Rabl::Renderer.new(rabl_template, object, render_options).render %({ "success": #{args[:success]}, "data": #{data} }) end end mount APIv1::Unlocks ... Monday, June 10, 13
  • 23. Simple Goliath + Grape ./app/views/api_v1/unlocks/show.json.rabl attributes :id, :name, :code, :description, :created_at node(:unique_tags) { |unlock| unlock.tags.uniq } child :images => :images do attributes :id, :caption, :mime_type, :url end Monday, June 10, 13
  • 24. Simple Goliath + Grape ./app/views/api_v1/unlocks/show.json.rabl attributes :id, :name, :code, :description, :created_at node(:unique_tags) { |unlock| unlock.tags.uniq } child :images => :images do attributes :id, :caption, :mime_type, :url end Monday, June 10, 13
  • 25. Simple Goliath + Grape ./app/views/api_v1/unlocks/show.json.rabl attributes :id, :name, :code, :description, :created_at node(:unique_tags) { |unlock| unlock.tags.uniq } child :images => :images do attributes :id, :caption, :mime_type, :url end renders => Monday, June 10, 13
  • 26. Simple Goliath + Grape ./app/views/api_v1/unlocks/show.json.rabl attributes :id, :name, :code, :description, :created_at node(:unique_tags) { |unlock| unlock.tags.uniq } child :images => :images do attributes :id, :caption, :mime_type, :url end renders => { "id":1,"name":"Fancy Thing","code":"001FT","description":"Enim doloribus id minima.","unique_tags":["foo","bar","woot"],"images": [{"image":{"id":"1","caption":"dolor sit amet","mime_type":"image/jpg","url":"http://s3.amazon.com/ whatever.jpg"}}],"created_at":1364916167.000000000 } Monday, June 10, 13
  • 27. Simple Goliath + Grape ./app/views/api_v1/unlocks/show.json.rabl attributes :id, :name, :code, :description, :created_at node(:unique_tags) { |unlock| unlock.tags.uniq } child :images => :images do attributes :id, :caption, :mime_type, :url end renders => { "success": true, "data": {"id":1,"name":"Fancy Thing","code":"001FT","description":"Enim doloribus id minima.","unique_tags":["foo","bar","woot"],"images": [{"image":{"id":"1","caption":"dolor sit amet","mime_type":"image/jpg","url":"http://s3.amazon.com/ whatever.jpg"}}],"created_at":1364916167.000000000} } Monday, June 10, 13
  • 28. Simple Goliath + Grape ./app/models/unlock.rb Well, that would have worked, if we had an Unlock model... Monday, June 10, 13
  • 29. Simple Goliath + Grape ./app/models/unlock.rb Well, that would have worked, if we had an Unlock model... require 'rocket_tag' class Unlock < ActiveRecord::Base has_many :images, as: :image_attachable, dependent: :destroy attr_taggable :tags validates :name, presence: true validates :code, presence: true end Monday, June 10, 13
  • 30. Simple Goliath + Grape ./app/models/unlock.rb Well, that would have worked, if we had an Unlock model... require 'rocket_tag' class Unlock < ActiveRecord::Base has_many :images, as: :image_attachable, dependent: :destroy attr_taggable :tags validates :name, presence: true validates :code, presence: true end Ordinary ActiveRecord like you’re accustomed to... Monday, June 10, 13
  • 31. Simple Goliath + Grape Try it out! Monday, June 10, 13
  • 32. Simple Goliath + Grape Try it out! $ ruby app.rb -sv -p 8000 Monday, June 10, 13
  • 33. Simple Goliath + Grape Try it out! $ ruby app.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. Monday, June 10, 13
  • 34. Simple Goliath + Grape Try it out! $ ruby app.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. $ curl http://lvh.me:8000/v1/unlocks/1 Monday, June 10, 13
  • 35. Simple Goliath + Grape Try it out! $ ruby app.rb -sv -p 8000 [45584:INFO] 2013-05-28 11:52:52 :: Starting server on 0.0.0.0:8000 in development mode. Watch out for stones. $ curl http://lvh.me:8000/v1/unlocks/1 { "success": true, "data": {"id":1,"name":"Fancy Thing","code":"001FT","description":"Enim doloribus id minima.","unique_tags":["foo","bar","woot"],"images": [{"image":{"id":"1","caption":"dolor sit amet","mime_type":"image/jpg","url":"http://s3.amazon.com/ whatever.jpg"}}],"created_at":1364916167.000000000}}} } Monday, June 10, 13
  • 36. Simple Goliath + Grape “Could the API give us users too? That’d be great...” Monday, June 10, 13
  • 37. Simple Goliath + Grape ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API mount APIv1::Unlocks resource 'servicehealth' do desc "Returns a basic status report." get "/" do MultiJson.dump({ status: 'OK', environment: Goliath::env }) end end end Monday, June 10, 13
  • 38. Simple Goliath + Grape ./app/api.rb Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API mount APIv1::Unlocks resource 'servicehealth' do desc "Returns a basic status report." get "/" do MultiJson.dump({ status: 'OK', environment: Goliath::env }) end end end Remember me? Monday, June 10, 13
  • 39. Simple Goliath + Grape ./app/api.rb Remember me? Dir["./app/apis/v1/*.rb"].each { |f| require f } class API < Grape::API mount APIv1::Unlocks mount APIv1::Users resource 'servicehealth' do desc "Returns a basic status report." get "/" do MultiJson.dump({ status: 'OK', environment: Goliath::env }) end end end Monday, June 10, 13
  • 40. Need communication? Build a Client Gem! Monday, June 10, 13
  • 41. Need communication? Build a Client Gem! require 'virtus' require 'rest_client' require 'multi_json' module UnlocksClient class Unlock include Virtus attribute :id attribute :name attribute :code attribute :description attribute :tags attr_accessor :media_rewards def self.find(id, params={}) client = RestClient::Resource.new("#{BASE_URL)}/unlocks/#{id}") response = client.get({params: params}) data = MultiJson.load(response)["data"] return nil if !response || data.empty? new(params) end end end Monday, June 10, 13
  • 42. Asynch MySQL Gotcha Used to Rails? If you’re not paying attention, you might do this... Monday, June 10, 13
  • 43. Asynch MySQL Gotcha Used to Rails? If you’re not paying attention, you might do this... development: host: localhost adapter: mysql2 database: unlocks_dev pool: 20 timeout: 5000 reconnect: true username: dbuser password: 123whatever Monday, June 10, 13
  • 44. Asynch MySQL Gotcha Used to Rails? If you’re not paying attention, you might do this... development: host: localhost adapter: em_mysql2 database: unlocks_dev pool: 20 timeout: 5000 reconnect: true username: dbuser password: 123whatever Monday, June 10, 13
  • 45. Asynch MySQL Gotcha Used to Rails? If you’re not paying attention, you might do this... development: host: localhost adapter: em_mysql2 database: unlocks_dev pool: 20 timeout: 5000 reconnect: true username: dbuser password: 123whatever YMMV Monday, June 10, 13
  • 46. Goliath Tips curl is your friend on the command line. Console mode! ruby app.rb -svC using Pry to debug stuff: Just add binding.pry Monday, June 10, 13
  • 47. Links Goliath / Grape / EM Stuff https://github.com/ postrank-labs/goliath https://github.com/ igrigorik/em-synchrony https://github.com/ intridea/grape Matt Stuff code.digimonkey.com mepatterson.net github.com/mepatterson twitter.com/mepatterson Monday, June 10, 13