webconf_riga_confessions of-a-traitor_krzsysztof szafranek
Post on 20-Oct-2014
3.014 views
DESCRIPTION
TRANSCRIPT
![Page 1: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/1.jpg)
Confessions
@szafranek
of a TraitorHow Objective C Made Me a Better JavaScript Programmer
![Page 2: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/2.jpg)
Front-end developer, manager at Roche
Front-end architectat Nokia
Game developerat Wooga
![Page 3: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/3.jpg)
github.com/wooga/Pocket-Island
The last HTML5 project I was a part of is now available as open source.
![Page 4: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/4.jpg)
In May 2012, after 7 years of professional front-end development, I started to work on native iOS game.
![Page 5: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/5.jpg)
[self learn:@"Objective C"];
![Page 6: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/6.jpg)
We make our tools,our tools make us
![Page 7: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/7.jpg)
• C with class-based OOP• [syntax awkward:YES];• Compiled• No garbage collection!
![Page 8: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/8.jpg)
[Disclaimer]
I will not try to convince you that JavaScript should be more like other languages.Yet we can be more effective JS developers by learning something from others.
![Page 9: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/9.jpg)
JavaScript AD 2004
![Page 10: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/10.jpg)
-----------------------------------------------------------------Language files blank comment code-----------------------------------------------------------------Javascript 100 3764 1858 22258HTML 9 73 23 690Ruby 4 90 15 237Bourne Shell 2 13 0 27-----------------------------------------------------------------SUM: 115 3940 1896 23212-----------------------------------------------------------------
Codebase size of Pocket Island. The project didn’t use external libraries.
![Page 11: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/11.jpg)
-----------------------------------------------------------------Language files blank comment code-----------------------------------------------------------------Javascript 717 79132 184499 425289HTML 452 84159 4819 198471XML 133 2155 1391 122219Java 527 10215 6633 42287CSS 94 3067 2101 23727Ruby 33 599 146 1794XSLT 1 57 29 1453Bourne Shell 47 285 293 865Python 5 244 256 828XSD 2 0 2 244JSP 4 7 0 92DTD 1 37 32 92Perl 2 26 0 55DOS Batch 4 4 1 18-----------------------------------------------------------------SUM: 2022 179987 200202 817434-----------------------------------------------------------------
Codebase size of my previous JavaScript project. That one included several external libraries.
![Page 12: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/12.jpg)
JavaScript AD 2004
![Page 13: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/13.jpg)
JavaScript AD 2012
![Page 14: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/14.jpg)
Building
![Page 15: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/15.jpg)
![Page 16: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/16.jpg)
• minification• linting• unit testing• deployment• template processing• much more
Currently Grunt offers over 200 plugins.
![Page 17: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/17.jpg)
Automate
* Let machines do what they're best at: * catching silly mistakes * optimization * boring but necessary tasks
![Page 18: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/18.jpg)
![Page 19: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/19.jpg)
Unit tests
![Page 20: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/20.jpg)
Everybody has heard about unit tests, but have you actually seen or wrote them?
![Page 21: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/21.jpg)
Bust
er.JS
Cross
chec
kDO
HEn
hanc
e JS
Fire
Unit
J3Uni
tJS
NUni
tJS
Spec
JSTe
stJS
Test
.NET
JSUni
tJS
pec
JasU
nit
Jasm
ine
Js-te
st-d
river
Js-te
st-r
unne
rM
ocha
Nod
euni
tQ
Unit
RhUni
tRh
inoU
nit
SOAte
stSi
non.
jsSu
itest
Test
.Mor
eTe
st.S
impl
eTe
stCas
eTe
stIt
Tyrt
leUni
tTes
ting
Vows
YUI T
est
jsUni
tTes
tjsU
nity
scre
w-u
nit
wru
Is the number of JS unit testing frameworks higher than the number of JavaScript developers writing tests?
![Page 22: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/22.jpg)
In Objective C world unit testing is supported out of the box.
![Page 23: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/23.jpg)
“No one pays you for testing”
The above quote comes from a PHP talk I listened to recently.
* Don’t ask your boss or client if you can write tests – tests are part of the code, not something extra* The point of writing tests is to make you faster at delivering quality product, not slower.* Tests enforce better coding style.
![Page 24: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/24.jpg)
Getting started
![Page 25: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/25.jpg)
1) Pick any framework
buster.js, js-test-driver, QUnit, Jasmine are all good places to start.
![Page 26: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/26.jpg)
2) Start writing tests
![Page 27: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/27.jpg)
Unit test is a function that tests
another function
![Page 28: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/28.jpg)
function factorial(n) { if (n == 1) { return 1; } return n * factorial(n-1);}
function testFactorial() { assert.equals(120, factorial(5));}
test
implementation
![Page 29: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/29.jpg)
3) Automate with test runner
If you haven’t already picked a framework with integrated runner, like buster.js or js-test-driver, use testem to run your tests.
You will NOT keep writing tests if you don’t automate running them with Continous Integration tool like Jenkins.
![Page 30: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/30.jpg)
Test runner
Unit testing framework
vs.
![Page 31: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/31.jpg)
Modern test runners can execute your tests in a variety of JS environments.
![Page 32: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/32.jpg)
buster.jsFramework and runner... that doesn’t run on Windows. Yet.
Authors are working on it. Version 1.0 will run on Windows.
![Page 33: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/33.jpg)
Cross-platform test runner
Needs test framework
testem
![Page 34: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/34.jpg)
Test framowork used by jQuery
QUnit
![Page 35: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/35.jpg)
http://szafranek.net/works/articles/javascript-unit-testing/
![Page 36: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/36.jpg)
http://bit.ly/writing-testable-javascript
Presentation contains practical example of making typical jQuery-based code testable.
![Page 37: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/37.jpg)
Static analysis
![Page 38: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/38.jpg)
Early warning system in an editor.
![Page 39: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/39.jpg)
jshint
jslint
![Page 40: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/40.jpg)
Your sadly pathetic bleatings are harshing my mellow.
Douglas Crockford commenting on complaints about jslint being too restrictive.This quote was one of the reasons to create jshint.
![Page 41: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/41.jpg)
1) Make your intent obvious
![Page 42: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/42.jpg)
Anything that isn’t crystal clear to a
static analysis tool probably isn’t clear
to your fellow programmers,
either.
Highly recommended article on the value of static analysis, by John Carmack:http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
![Page 43: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/43.jpg)
2) Catch mistakes early
![Page 44: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/44.jpg)
Integrate jslint or jshint with your editor.
![Page 45: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/45.jpg)
Automate
Use continuous integration to run static analysis checks on your code.
![Page 46: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/46.jpg)
Refactoring
![Page 47: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/47.jpg)
Improvingthe design of codewithout changing its external behavior
![Page 48: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/48.jpg)
Improvingthe design of codewithout changing its external behavior
![Page 49: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/49.jpg)
Goals
Before starting refactoring, think for a moment what you want to achieve.
![Page 50: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/50.jpg)
Remove duplication
Extract method, extract variable
![Page 51: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/51.jpg)
if (player.coins < budget.getMin()) { offerCredit();}if (friend.coins < budget.getMin()) { offerFriendCredit();}
var min = budget.getMin();if (player.coins < min) { offerCredit();}if (friend.coins < min) { offerFriendCredit();}
![Page 52: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/52.jpg)
Make your intentclear obvious
![Page 53: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/53.jpg)
[taskShortcut performSelector:selector withObject:param];
Named parameters in Objective C encourage explicit naming.
![Page 54: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/54.jpg)
data, t, managerare not good names
formData, timeLeft, urlRouter are better
![Page 55: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/55.jpg)
Explanatory variables
![Page 56: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/56.jpg)
button.setPosition(0, parseInt(parentDiv.height, 10) / 2 - parseInt(border.size, 10) / 2);
var verticalCenter = parseInt(parentDiv.height, 10) / 2 - parseInt(border.size, 10) / 2;
button.setPosition(0, verticalCenter);
It’s worth to introduce a variable to communicate intention clearly, even if it doesn’t reduce duplication.
![Page 57: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/57.jpg)
Design patterns
Design patterns make the intent of the design easy to see.If you are using a pattern, make sure it’s communicated through naming.
![Page 58: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/58.jpg)
Clean code first,optimization second
![Page 59: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/59.jpg)
Premature optimization is the root of all evil.
![Page 60: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/60.jpg)
Improvingthe design of codewithout changing its external behavior
![Page 61: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/61.jpg)
Tests!
How do you verify that behavior didn’t change?
![Page 62: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/62.jpg)
Tests?Refactoring without tests (aka “happy development”) is all too common.It’s not a very responsible thing to do.
![Page 63: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/63.jpg)
IDE
The problem with refactoring JavaScript:poor tool support -> makes refactoring require courage -> makes refactoring less likely to happen.
Search and replace doesn’t constitute “tool support” for refactoring – it’s only text-based and doesn’t guarantee that the semantics of your program will be preserved.
![Page 64: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/64.jpg)
My reaction to the word “IDE”...
![Page 65: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/65.jpg)
... was deeply rooted.
![Page 66: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/66.jpg)
WebStorm IDE has good support for essential refactorings.
![Page 67: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/67.jpg)
![Page 68: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/68.jpg)
•Web Storm•...•...•...•Cloud9•...?
We need better and more tools for refactoring JavaScript!
![Page 69: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/69.jpg)
Automate
Refactoring has to be performed by a human, but good tool (like WebStorm) can make it much safer and easier.
![Page 70: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/70.jpg)
![Page 71: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/71.jpg)
Refactoring
Unit testing
Static analysis
automate
automate
automate
@szafranek
![Page 72: WebConf_Riga_Confessions of-a-traitor_Krzsysztof Szafranek](https://reader038.vdocuments.net/reader038/viewer/2022110301/5445117fb1af9f4b6c8b46a1/html5/thumbnails/72.jpg)
Image credits:
eclectic echoes
The Facey Family
chrisnb20
Esther Gibbons
Thank you!
Philippe Semeria
Nick Treby
QuakeCon