real cra smen create eir - zenspider.com · cascadia ruby 2013, ... create "eir own tools can)...
TRANSCRIPT
Craftsmen Create Their Own Tools
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rb
Real Cra!smen Create "eir Own Tools
(can)
Craftsmen Create Their Own Tools
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rb
akaMake Your OwnCode Climate!
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Setting Expectations
• ~150 Slides in 30 min. ~5 spm
• Semi-Technical Talk
• Slides at: http://www.zenspider.com/presentations
• Twit me @the_zenspider
TODO
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Let's get this out of the
way…
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
There will be more
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Youare NOT
your code!!!
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Let's Get Started
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
90+% of the time
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
SoftwareEngineering
is a
Lie
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Rocket Surgery?
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
☑ Engineering
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
☒Engineering
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
We may write software
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Not Even Close to
"Engineering"
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
NotInsulting
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Quitethe
Opposite
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Engineeringcan be really
Boring
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
We're Craftsmen.
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
We Craft Software.
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
craftsmannoun ( pl. craftsmen )a person who is skilled in a particular craft.
craftswomannoun ( pl. craftswomen )a woman who is skilled in a particular craft.
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Fitit
Together
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
like good woodworking
http://www.woodthatworks.com
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Each Piece is Unique.
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
WhenCrafting
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Specific Task
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Uniquely Suited
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
To how You Work
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
http://www.flickr.com/photos/wsdot/8260834957/
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
You make these tools…
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
at least,You ShouldBe Able To
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Not Widely Adopted Practice
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Or, the history of Algol-based
languages
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Algol Matrix abs maxproc abs max = ([,]real a, ref real y, ref int i, k)real:comment The absolute greatest element of the matrix a, of size ⌈a by 2⌈a is transferred to y, and the subscripts of this element to i and k; commentbegin real y := 0; i := ⌊a; k := 2⌊a; for p from ⌊a to ⌈a do for q from 2⌊a to 2⌈a do if abs a[p, q] > y then y := abs a[p, q]; i := p; k := q fi od od; yend # abs max #
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
C Quicksortvoid quick_sort (int *a, int n) { if (n < 2) return; int p = a[n / 2]; int *l, *r, t = a, a + n - 1, 0; while (l <= r) { if (*l < p) { l++; continue; } if (*r > p) { r--; continue; } t = *l; *l++ = *r; *r-- = t; } quick_sort(a, r - a + 1); quick_sort(l, a + n - l);}
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Static a Opaque
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Complex Grammars
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Complex Semantics
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Complex Memory Models
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Disempowering
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Tool-Making is for the Elite
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Do You Extend Them?
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Do You Write Tools to Help
You Code?
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
I'm Here to Help
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Language Tools
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Flog and Flayh!p://ruby.sadi.st/
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
AnalyzesComplexity
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
class LameExample def blah a = eval "1+1" # 1.2 + 6.0 + if a == 2 then # 1.2 + 1.2 + 0.4 + puts "yay" # 1.2 end # = 11.2 endend
Example
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
FindsSimilarCode
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Structurally Similardef x(n) n + 1end
def y m m + 2end
Names
(Styles)
Literals
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
"Fuzzily" Similardef a f1; f2; f3; f4; f5; f6end
def b f2; f3; f4; f5; f6end
def c f2; f3; f4; f5; f6; f7end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Basic Ar"#e$ure
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Notfor
Regexps
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Needto know
the Language
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Builda model
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Walkthe model
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
"3+4*2"
(3 + (4 * 2))
["3", "+", "4","*", "2"]
call: +
lit: 3 call: *
lit: 4 lit: 2
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
RubyParser.new.parse " 3+4*2"
Converts This:
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
To This::call
:lit
3
:+ :call
:lit
4
:* :lit
2
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
s(:call, s(:lit, 3), :+, s(:call, s(:lit, 4), :*, s(:lit, 2)))
Or This:
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Sexp Processor
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
SexpProcessorclass MyLanguageTool def process_lit s # ... end def process_call s # ... endend
call: +
lit: 3 call: *
lit: 4 lit: 2
TODO:add interpreterslide
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Flog Working Details
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Knowledgable ScoringSCORES = Hash.new 1SCORES.merge! { :module_eval => 5, :define_method => 5, :class_eval => 5, :send => 3, :method_defined? => 2, :eval => 5, :extend => 2, :instance_eval => 5, :inject => 2, # ...}
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
process_ifdef process_if exp add_to_score :branch
process exp.shift # cond
penalize_by 0.1 do process exp.shift # true process exp.shift # false end
s()end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
process_calldef process_call exp penalize_by 0.2 do process exp.shift # recv end
name = exp.shift
penalize_by 0.2 do process_until_empty exp end
add_to_score name, SCORES[name] s()end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Flay Working Details
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
The Algorithm
sexps[sexp.structure.hash] << sexp
…really
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
sexps[sexp.structure.hash] << sexp
call: +
lit: 3 call: *
lit: 4 lit: 2
42
call
lit call
lit lit
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
42 => [
]
sexps[sexp.structure.hash] << sexp
sexps
call
lit call
lit lit
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
42 => [
]
call
lit call
lit lit
call
lit call
lit lit
call
lit call
lit lit
sexps[sexp.structure.hash] << sexp
sexps
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Make Your Own
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
ReportSingle CharVariables
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Test Infrastructurerequire "minitest/autorun"require "lint"
describe Lint do # nothing yetend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Premature Refactoringdescribe Lint do def assert_lint src, out = "" lint = Lint.new rp = RubyParser.new sexp = rp.parse src, "file.rb"
assert_output out, "" do lint.process sexp end endend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Happy Pathit "should be silent on compliant code" do assert_lint "1+1"end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Sad Pathit "should be bitch about small var names" do assert_lint("a=1", "Short variable: 'a' on file.rb:1\n")end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Infrastructurerequire "sexp_processor"require "ruby_parser"
class Lint < MethodBasedSexpProcessor # nothing yetend
if $0 == __FILE__ then lint = Lint.new
ARGV.each do |path| sexp = RubyParser.new.parse File.read(path), path lint.process sexp endend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Red# Running:
.F
Finished in 0.021296s, 93.9144 runs/s, 187.8287 assertions/s.
1) Failure:Lint#test_0002_should be bitch about small var names [./test_lint.rb:22]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Short variable: 'a' on file.rb:1-"+""
2 runs, 4 assertions, 1 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Implementation, 1class Lint < MethodBasedSexpProcessor def process_lasgn sexp _, name, val = sexp
err = case name.to_s when /^.$/ then "Short variable" end
puts "%s: '%s' on %s:%d" % [err, name, sexp.file, sexp.line] if err
s() # ignore me endend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Green# Running:
..
Finished in 0.002666s, 750.1875 runs/s, 1500.3751 assertions/s.
2 runs, 4 assertions, 0 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
More Sad Pathit "should be bitch about small global names" do assert_lint("$a=1", "Short variable: '$a' on file.rb:1\n")end
it "should be bitch about small ivar names" do assert_lint("@a=1", "Short variable: '@a' on file.rb:1\n")end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Red# Running:
F..F
Finished in 0.029827s, 134.1067 runs/s, 268.2134 assertions/s.
1) Failure:Lint#test_0003_should be bitch about small global names [./test_lint.rb:26]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Short variable: '$a' on file.rb:1-"+""
2) Failure:Lint#test_0004_should be bitch about small ivar names [./test_lint.rb:31]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Short variable: '@a' on file.rb:1-"+""
4 runs, 8 assertions, 2 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Implementation, 2 def process_lasgn sexp _, name, val = sexp
err = case name.to_s when /^(\$|@@?)?.$/ then "Short variable" end
puts "%s: '%s' on %s:%d" % [err, name, sexp.file, sexp.line] if err
s() # ignore me end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Implementation, 2.5class Lint < MethodBasedSexpProcessor def var sexp # ... end
alias :process_lasgn :var # local alias :process_iasgn :var # ivars alias :process_gasgn :var # globalend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
GreenRun options: --seed 8130
# Running:
....
Finished in 0.004117s, 971.5812 runs/s, 1943.1625 assertions/s.
4 runs, 8 assertions, 0 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
ReportcamelCaseVariables
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Add a New Testit "should be bitch about camel case names" do assert_lint("aThing=1", "Bad variable name: 'aThing' on file.rb:1\n")end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
RedRun options: --seed 12656
# Running:
..F
Finished in 0.023611s, 127.0594 runs/s, 254.1188 assertions/s.
1) Failure:Lint#test_0003_should be bitch about camel case names [./test_lint.rb:33]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Bad variable name: 'aThing' on file.rb:1-"+""
3 runs, 6 assertions, 1 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Implementation, 3 def var sexp _, name, val = sexp
err = case name.to_s when /^(\$|@@?)?.$/ then "Short variable" when /^[a-z]+[A-Z]/ then "Bad variable name" end
puts "%s: '%s' on %s:%d" % [err, name, sexp.file, sexp.line] if err
s() end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
GreenRun options: --seed 34815
# Running:
.....
Finished in 0.004650s, 1075.2688 runs/s, 2150.5376 assertions/s.
5 runs, 10 assertions, 0 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Add More Tests it "should be bitch about camel case globals" do assert_lint("$aThing=1", "Bad variable name: '$aThing' on file.rb:1\n") end
it "should be bitch about camel case ivars" do assert_lint("@aThing=1", "Bad variable name: '@aThing' on file.rb:1\n") end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Red# Running:
...F.F.
Finished in 0.041257s, 169.6682 runs/s, 339.3364 assertions/s.
1) Failure:Lint#test_0007_should be bitch about camel case ivars [./test_lint.rb:46]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Bad variable name: '@aThing' on file.rb:1-"+""
2) Failure:Lint#test_0006_should be bitch about camel case globals [./test_lint.rb:41]:In stdout.--- expected+++ actual@@ -1,2 +1 @@-"Bad variable name: '$aThing' on file.rb:1-"+""
7 runs, 14 assertions, 2 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Implementation, 4 def var sexp _, name, val = sexp
err = case name.to_s when /^(\$|@@?)?.$/ then "Short variable" when /^(\$|@@?)?[a-z]+[A-Z]/ then "Bad variable name" end
puts "%s: '%s' on %s:%d" % [err, name, sexp.file, sexp.line] if err
s() end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
GreenRun options: --seed 21752
# Running:
.......
Finished in 0.006121s, 1143.6040 runs/s, 2287.2080 assertions/s.
7 runs, 14 assertions, 0 failures, 0 errors, 0 skips
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
What'd We Do?
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
7 Tests it "should be silent on compliant code" do assert_lint "1+1" end
it "should be bitch about small var names" do assert_lint("a=1", "Short variable: 'a' on file.rb:1\n") end
it "should be bitch about small global names" do assert_lint("$a=1", "Short variable: '$a' on file.rb:1\n") end
it "should be bitch about small ivar names" do assert_lint("@a=1", "Short variable: '@a' on file.rb:1\n") end
it "should be bitch about camel case names" do assert_lint("aThing=1", "Bad variable name: 'aThing' on file.rb:1\n") end
it "should be bitch about camel case globals" do assert_lint("$aThing=1", "Bad variable name: '$aThing' on file.rb:1\n") end
it "should be bitch about camel case ivars" do assert_lint("@aThing=1", "Bad variable name: '@aThing' on file.rb:1\n") end
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
50 LOC Implementationclass Lint < MethodBasedSexpProcessor def var sexp _, name, val = sexp
err = case name.to_s when /^(\$|@@?)?.$/ then "Short variable" when /^(\$|@@?)?[a-z]+[A-Z]/ then "Bad variable name" end
puts "%s: '%s' on %s:%d" % [err, name, sexp.file, sexp.line] if err
s() end
alias :process_lasgn :var alias :process_iasgn :var alias :process_gasgn :varend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Easy to Test
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Easy to Extend
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
ComplexityAnalysis
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Refactoring Analysis
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Dependency Analysis
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Give it a grade A-F
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Sooner or Later
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Take on Code Climate
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Probably Later
Craftsmen Create Their Own Tools
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rb
"ank You…One more thing…
@[email protected]://blog.zenspider.com/
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
2011:Fresh
Black Pepper
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
2012:Ginger Ale
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
2013:Hot Sauce!
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
http://www.flickr.com/photos/dinnerseries/8557776408/
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
5 Minute Hot Sauce350 g hot peppers of your choice, stems removed, roughly chopped7 whole cloves of garlic3 T honey apple cider vinegar
1. Add all ingredients to pressure cooker.2. Add enough apple cider vinegar to cover.3. Seal pressure cooker, bring to 15psi, cook for 1 min.4. Take off heat, let cool naturally until pressure released.5. Transfer to blender and roughly purée.6. (opt) Bring back to stove and reduce to thickness you desire.7. Enjoy the noms.
Adapted from:
http://www.hippressurecooking.com/pressure-cooker-hot-sauce/http://www.bluekaleroad.com/2013/01/homemade-sriracha.html
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rbCraftsmen Create Their Own Tools
Who Wants Some?
Craftsmen Create Their Own Tools
Cascadia Ruby 2013, Portland, OR
Ryan Davis, Seattle.rb
"ank You@the_zenspider
[email protected]://blog.zenspider.com/