2012 Github Ruby Patterns
-
Upload
brian-trust -
Category
Documents
-
view
70 -
download
0
Transcript of 2012 Github Ruby Patterns
from github’s codebase
Ruby Patterns
@holman
Like everyone, we have some really dumb ideas.
But we have somegood ideas too! Honest!
So let’s talk about a few.
bootstrappingmaking your app newbie-proof™
your company is going to have
TONS OF SUCCESS
which means you’ll have to hire
TONS OF PEOPLE
GitHub has 200+ repositories.and 60+ employees.and 20+ languages.
and about a billion egos.
It needs to be QUICK AND EASY to step into an unfamiliar app
script/bootstrap
Most of our Ruby projects have a file called in the directory.
bootstrap script/
Easily jump into a project and start contributing.
{script/bootstrap
static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migration
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migration
is mysql installed?(here’s how to install it)
is redis running?(here’s how to run it)
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migration
bundle install \ --binstubs \ --local \ --path=vendor/gems \ --without=production
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migrationrake db:create
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migrationrake db:migrate
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migrationscript/replicate-repo(see rtomayko/replicate)
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migration404, 500
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migrationpython, c, erlang...
{static page compilation
language compilation
db seeding
dependency checks
bundler
db creation
db migrationt
cache bootstrap results
md5 << File.read('Gemfile') checksum = md5.hexdigest installed = File.read('.bundle/checksum').strip
Check if we need to bundle install
should be fast, straightforward, and require no knowledge of the underlying language or app setup
script/bootstrap
You can also run during any process that loads the environment, like
script/bootstrap
script/server
You can do the same with your testing environment, too.
Our CI server only needs to ever run .script/cibuild
We add to most projects. Lets us keep test config in the repository.
script/cibuild
{script/cibuild
export RACK_ROOT=["..."] export RACK_ENV="test"
script/bootstrap
bin/rake
QUICK AND EASY Make sure your environment is
to set up.
conditional monkeypatchingjust like a good rubyist
- GitHub is a Rails 2.3(ish) app
PROBLEM:
- Upgrading to Rails 3.x sucks- Biggest concern is escaping-by-default- We wanted incremental rollout
JOSH PEEK Yo let’s just make itstaff-only like
everything else we do
def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end
def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end
def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end
(#enable_escaping is just a patch on SafeBuffer)
Use Ruby’s strengths to limit your liability during big code changes.
Also, use partial deployments to further limit your exposure.
api designgithub api v3
Sinatra is (naturally) great for an API.
get “/” do “an web app” end
translateseasily toGET / HTTP/1.1
GITHUB API V3
~30 Sinatra apps
located in app/api in our Rails app
mounted at api.github.com/(version in headers)
routed in rack with path regex
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
control_accessverifies user has permission for the request
pulls user data from currently-authenticated user
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
find_userauto-populated based on params[:user]
manages error handling for nonexistent users
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
paginationauto-populated based on per_page & page params
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
deliverhandles nil objects (404)
appends OAuth scope & rate limiting headers
encodes as JSON and sets Content-Type
handles JSONP callbacks
USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end
USE HELPERS, SIMPLIFY ROUTES
Extract all these little methods out
Your API becomes extremely
Your API becomes extremely
readable
testable
inner-app projectsfor services that aren’t yet services
Big apps suck, services are great.
Single responsibility principle and all that jazz.
But it’s harder to do!
LET ME JUST THROWTHIS IN THE MODEL!!!!!!111
Compromise: library-in-an-app
lib/#{project}test/#{project}
INNER-APP READMEGitHub displays READMEs for every directory we find
Clear area to add documentation
Easy access to your models
y
(Potentially) easier to extract
TEST HELPERS
Separate your project’s helper methods
Fixtures, too
As you grow, it’s harder to mentally grasp your entire app. Splitting into well-documented chunks can really help.
github hd™zooooooooooooooooooooooom
Okay this totally isn’t Ruby, but it’s cool.
⌘+ ⌘-ZOOM IN ZOOM OUT
<img src=”logo-4x.png” width=”70” height=”30” />
280px
120px
SCALE IN-BROWSER
This is great for iPhones and iPads, too.
documentationreally doesn’t have to suck. honest.
As you grow larger, it’s harder to understand your entire app.
(I’m totally a broken record)
DOCUMENTATION!TO THE RESCUE!
We TomDoc theshit out of everything
Pretty much every method has 3-6 lines of documentation
Sure, you can laugh.
But working on highly-documented code is AMAZING.
...particularly in Ruby, because following this sucks:
def mailing_address
full_name + “\n”
street_address_1 + “\n” +
street_address_2 + “\n” +
“#{city}, #{state} #{zip}”
end def street_address_1 house_with_street.formatted end
def house_with_street
StreetAddress.new(data)
end
Debugging a STRONGLY DYNAMICMETAPROGRAMMING-HAPPYFLEXIBLE LANGUAGE is hard.
SPEC:
TOMDOC
GOAL: internal API documentation for your developers
tomdoc.org
(generated docs not a priority)
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account. # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account. # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end
DON’T WORRY, IT’S NOT VERBOSE:Most of your TomDoc will only be two lines:
# EXPLANATION## RETURNS
Complicated methods (like the previous example) will require more detailed TomDoc.
IT’S FLEXIBLE:Document as you see fit; it’s a loose spec.
IT’S ALWAYS UP-TO-DATE:If you update the code, update the fucking docs.
IT’S OUTRAGEOUSLY HELPFULHumans will always read English easier than code.
IT HELPS WITH TESTSYou’re detailing input + output already.
TOMDOC IS NOT REQUIREDIf you don’t dig TomDoc, choose something to document your code.
LONG-FORM IS GREAT TOOInner-app READMEs, wiki entries, internal posts.
DOCUMENT EVERYTHING
DOCUMENT EVERYTHING
— no, really —
beware noveltycustom vs. stock
GitHub has opinionated employees.
Custom hacks become difficult to deal with as you grow larger.
jump to Rails 3.x
speed of new hires
possible reliabilityIMPACTS OUR
“Doing It Yourself” isn’t wrong...but be wary of the implications.
maintainability,A lot of this is focused on
especially as you grow.
Technical scaling is sometimes easier than organizational scaling.
Keep that in mind as you build your Next Big Thing.
Thanks.
Zach Holmanzachholman.com/talks@holman