Matthew Mongeau
halogenandtoast
goose@thoughtbot.com
The Art of Ruby
A programmer who subconsciously
views himself as an artist will enjoy
what he does and will do it better.
Donald Knuth
What is Art?
/ärt/

the expression or application of
human creative skill and
imagination, typically in a visual
form such as painting ...
/ärt/

the expression or application of
human creative skill and
imagination, typically in a visual
form such as painting ...
What is Art
What is Art
It’s beautiful.
What is Art
It’s beautiful.
It’s creative.
What is Art
It’s beautiful.
It’s creative.
It’s expressive.
What is Programming
The art of
writing code.
Code is Art
Code is Art
It’s beautiful.
Code is Art
It’s beautiful.
It’s creative.
Code is Art
It’s beautiful.
It’s creative.
It’s expressive.
Writing beautiful Code
Simplicity
Code should be clear
def play
puts "Welcome to the guessing game."
3.times do
guess = get_guess
if won? guess
@won = true
puts "You win!"
break...
def play
puts "Welcome to the guessing game."
3.times do
guess = get_guess
if won? guess
@won = true
puts "You win!"
break...
Extract
Method
Pattern

def play
puts "Welcome to the guessing game."
play_rounds
print_results
end
Single Level
of Abstraction
Principle

def play
print_header
play_rounds
print_results
end
Complexity
Balance
def play_rounds
round_count.times do
if correct_guess? get_guess
@won = true
break
else
puts "Wrong!"
end
end
end
def play_rounds
@won = round_count.times.detect { play_round }
end
def play_round
if correct_guess? get_guess
true
else
pu...
def play_rounds
@won = round_count.times.detect { play_round }
end
def play_round
if correct_guess? get_guess
true
else
pu...
Constraints
The more constraints one imposes,
the more one frees one's self.
Igor Stravinsky
Code Constraints
Code Constraints
5 line methods
Code Constraints
5 line methods
100 line classes
Code Constraints
5 line methods
100 line classes
No classes in public methods
def play
puts "Welcome to the guessing game."
3.times do
guess = get_guess
if won? guess
@won = true
puts "You win!"
break...
def play
print_header
play_rounds
print_results
end
def play_rounds
round_count.times do
if correct_guess? get_guess
@won = true
break
else
puts "Wrong!"
end
end
end
def play_rounds
@won = round_count.times.detect { play_round }
end
def play_round
if correct_guess? get_guess
true
else
pu...
def play_rounds
@won = round_count.times.detect { play_round }
end
def play_round
if correct_guess? get_guess
true
else
pu...
Breaking the Rules
Pablo
Learn the rules like a
pro, so you can break
them like an artist.

Picasso
def play_round
if correct_guess? get_guess
true
else
puts "Wrong!"
end
end
def play_round
if correct_guess? get_guess
puts "Correct"
true
else
puts "Wrong!"
false
end
end
def play_round
if correct_guess? get_guess
correct_guess
else
incorrect_guess
end
end
def incorrect_guess
puts "Wrong!"
fa...
def play_round
if correct_guess? get_guess
correct_guess
else
incorrect_guess
end
end
def incorrect_guess
puts "Wrong!"
fa...
class Guess
def initialize actual_number
@actual_number = actual_number
@guess = get_guess
end

Extract
Class
Pattern

def...
Extract
Class
Pattern

def play_round
guess = Guess.new(actual_number)
puts guess.display_status
guess.correct?
end
1
Extract Method
1 2
Extract Method

Extract Class
1 2 3
Extract Method

Extract Class

Extract Gem
or App
You Win
Performance
If you’re willing to restrict the
flexibility of your approach, you can
almost always do something better.
John Carmack
module Alchemist
module Conversion
def method_missing unit_name, *args, &block
exponent, unit_name = Alchemist.parse_prefi...
module Alchemist
module Conversion
def method_missing unit_name, *args, &block
exponent, unit_name = Alchemist.parse_prefi...
def self.parse_prefix(unit)
matches = unit.to_s.match(prefix_matcher)
prefix, parsed_unit = matches.captures
if prefix && ...
def self.parse_prefix(unit)
matches = unit.to_s.match(prefix_matcher)
prefix, parsed_unit = matches.captures
if prefix && ...
def self.parse_prefix(unit)
matches = unit.to_s.match(prefix_matcher)
prefix, parsed_unit = matches.captures
if prefix && ...
def self.prefix_matcher
keys = unit_prefixes.keys.map(&:to_s).
sort{ |a,b| b.length <=> a.length }
%r{^(#{keys.join('|')})...
[:googol, :yotta, :Y, :zetta, :Z,
:exa, :E, :peta, :P, :tera, :T,
:giga, :G, :mega, :M, :kilo, :k,
:hecto, :h, :deca, :da,...
/^(googol|yotta|femto|zetta|zepto|micro|
milli|centi|hecto|yocto|exbi|giga|tebi|
mega|pebi|kilo|atto|tera|kibi|deca|yobi|
...
Some people, when confronted
with a problem, think "I know, I'll
use regular expressions." Now
they have two problems.
Jam...
I thought
"I know, I'll use regular expressions."
I now had two problems
but
def self.prefix_matcher
@prefix_matcher ||= begin
prefix_keys = unit_prefixes.keys.map(&:to_s).
sort{ |a,b| b.length <=> a...
2.0
1.9
Constraints
Keep existing interface
Constraints
Keep existing interface
No method_missing
Constraints
Keep existing interface
No method_missing
Increase performance
module Alchemist
def self.setup category = nil
if category
load_category category
else
load_all_categories
end
end
private...
module Alchemist
class ModuleBuilder
def initialize category
@category = category
end
def build
Module.new.tap do |categor...
private
attr_reader :category
def library
Alchemist.library
end
def category_methods
unit_names.map do |name|
%(define_met...
module Alchemist
class ModuleBuilder
def initialize category
@category = category
end
def build
Module.new.tap do |categor...
module Alchemist
class ModuleBuilder
def initialize category
@category = category
end
def build
build_module do |category_...
[success_kid.jpg]
#include <ruby.h>
#include <string.h>

the c-level

VALUE cParenParser;
VALUE paren_parser_parse(VALUE self, VALUE str) {
...
How to be an artist
Have a toolbox
Be inspired by others
Surround yourself with
talented artists
Surround yourself with
talented artists
Use well known
techniques
Use well known
techniques
Measure improvement
PULLREVIEW
Have feelings
Know when to break
the rules
Have fun.
Prime
learn.thoughtbot.com/prime
THISISMETIS.COM
?
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
The Art of Ruby
Upcoming SlideShare
Loading in …5
×

The Art of Ruby

564 views
475 views

Published on

The Art of Ruby: The spiritual successor to the Origamist's Ruby

Learn how to write beautiful code and how programming is a form of art.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
564
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

The Art of Ruby

  1. 1. Matthew Mongeau halogenandtoast goose@thoughtbot.com
  2. 2. The Art of Ruby
  3. 3. A programmer who subconsciously views himself as an artist will enjoy what he does and will do it better. Donald Knuth
  4. 4. What is Art?
  5. 5. /ärt/ the expression or application of human creative skill and imagination, typically in a visual form such as painting or sculpture, producing works to be appreciated primarily for their beauty or emotional power.
  6. 6. /ärt/ the expression or application of human creative skill and imagination, typically in a visual form such as painting or sculpture, producing works to be appreciated primarily for their beauty or emotional power.
  7. 7. What is Art
  8. 8. What is Art It’s beautiful.
  9. 9. What is Art It’s beautiful. It’s creative.
  10. 10. What is Art It’s beautiful. It’s creative. It’s expressive.
  11. 11. What is Programming
  12. 12. The art of writing code.
  13. 13. Code is Art
  14. 14. Code is Art It’s beautiful.
  15. 15. Code is Art It’s beautiful. It’s creative.
  16. 16. Code is Art It’s beautiful. It’s creative. It’s expressive.
  17. 17. Writing beautiful Code
  18. 18. Simplicity
  19. 19. Code should be clear
  20. 20. def play puts "Welcome to the guessing game." 3.times do guess = get_guess if won? guess @won = true puts "You win!" break else puts "Wrong!" end end if !@won puts "You lost. The actual number is #{@actual_number}." end end
  21. 21. def play puts "Welcome to the guessing game." 3.times do guess = get_guess if won? guess @won = true puts "You win!" break else puts "Wrong!" end end if !@won puts "You lost. The actual number is #{@actual_number}." end end
  22. 22. Extract Method Pattern def play puts "Welcome to the guessing game." play_rounds print_results end
  23. 23. Single Level of Abstraction Principle def play print_header play_rounds print_results end
  24. 24. Complexity
  25. 25. Balance
  26. 26. def play_rounds round_count.times do if correct_guess? get_guess @won = true break else puts "Wrong!" end end end
  27. 27. def play_rounds @won = round_count.times.detect { play_round } end def play_round if correct_guess? get_guess true else puts "Wrong!" end end
  28. 28. def play_rounds @won = round_count.times.detect { play_round } end def play_round if correct_guess? get_guess true else puts "Wrong!" # this returns nil which is falsey end end
  29. 29. Constraints
  30. 30. The more constraints one imposes, the more one frees one's self. Igor Stravinsky
  31. 31. Code Constraints
  32. 32. Code Constraints 5 line methods
  33. 33. Code Constraints 5 line methods 100 line classes
  34. 34. Code Constraints 5 line methods 100 line classes No classes in public methods
  35. 35. def play puts "Welcome to the guessing game." 3.times do guess = get_guess if won? guess @won = true puts "You win!" break else puts "Wrong!" end end if !@won puts "You lost. The actual number is #{@actual_number}." end end
  36. 36. def play print_header play_rounds print_results end
  37. 37. def play_rounds round_count.times do if correct_guess? get_guess @won = true break else puts "Wrong!" end end end
  38. 38. def play_rounds @won = round_count.times.detect { play_round } end def play_round if correct_guess? get_guess true else puts "Wrong!" end end
  39. 39. def play_rounds @won = round_count.times.detect { play_round } end def play_round if correct_guess? get_guess true else puts "Wrong!" # this is still bothering me end end
  40. 40. Breaking the Rules
  41. 41. Pablo Learn the rules like a pro, so you can break them like an artist. Picasso
  42. 42. def play_round if correct_guess? get_guess true else puts "Wrong!" end end
  43. 43. def play_round if correct_guess? get_guess puts "Correct" true else puts "Wrong!" false end end
  44. 44. def play_round if correct_guess? get_guess correct_guess else incorrect_guess end end def incorrect_guess puts "Wrong!" false end def correct_guess puts "Correct" false end
  45. 45. def play_round if correct_guess? get_guess correct_guess else incorrect_guess end end def incorrect_guess puts "Wrong!" false end def correct_guess puts "Correct" false end
  46. 46. class Guess def initialize actual_number @actual_number = actual_number @guess = get_guess end Extract Class Pattern def status if correct? "Correct" else "Incorrect" end end def correct? guess == actual_number end private attr_reader :guess, :actual_number def get_guess print "What is your guess: " gets.to_i end end
  47. 47. Extract Class Pattern def play_round guess = Guess.new(actual_number) puts guess.display_status guess.correct? end
  48. 48. 1 Extract Method
  49. 49. 1 2 Extract Method Extract Class
  50. 50. 1 2 3 Extract Method Extract Class Extract Gem or App
  51. 51. You Win
  52. 52. Performance
  53. 53. If you’re willing to restrict the flexibility of your approach, you can almost always do something better. John Carmack
  54. 54. module Alchemist module Conversion def method_missing unit_name, *args, &block exponent, unit_name = Alchemist.parse_prefix(unit_name) if Alchemist.has_measurement?(unit_name) Alchemist.measurement self, unit_name, exponent else super( unit_name, *args, &block ) end end end end class Numeric include Alchemist::Conversion end
  55. 55. module Alchemist module Conversion def method_missing unit_name, *args, &block exponent, unit_name = Alchemist.parse_prefix(unit_name) if Alchemist.has_measurement?(unit_name) Alchemist.measurement self, unit_name, exponent else super( unit_name, *args, &block ) end end end end class Numeric include Alchemist::Conversion end
  56. 56. def self.parse_prefix(unit) matches = unit.to_s.match(prefix_matcher) prefix, parsed_unit = matches.captures if prefix && si_units.include?(parsed_unit) value = prefixed_value_for(prefix.to_sym, parsed_unit) [value, parsed_unit.to_sym] else [1, unit] end end
  57. 57. def self.parse_prefix(unit) matches = unit.to_s.match(prefix_matcher) prefix, parsed_unit = matches.captures if prefix && si_units.include?(parsed_unit) value = prefixed_value_for(prefix.to_sym, parsed_unit) [value, parsed_unit.to_sym] else [1, unit] end end
  58. 58. def self.parse_prefix(unit) matches = unit.to_s.match(prefix_matcher) prefix, parsed_unit = matches.captures if prefix && si_units.include?(parsed_unit) value = prefixed_value_for(prefix.to_sym, parsed_unit) [value, parsed_unit.to_sym] else [1, unit] end end
  59. 59. def self.prefix_matcher keys = unit_prefixes.keys.map(&:to_s). sort{ |a,b| b.length <=> a.length } %r{^(#{keys.join('|')})?(.+)} end
  60. 60. [:googol, :yotta, :Y, :zetta, :Z, :exa, :E, :peta, :P, :tera, :T, :giga, :G, :mega, :M, :kilo, :k, :hecto, :h, :deca, :da, :deci, :d, :centi, :c, :milli, :m, :micro, :u, :nano, :n, :pico, :p, :femto, :f, :atto, :a, :zepto, :z, :yocto, :y, :kibi, :Ki, :mebi, :Mi, :gibi, :Gi, :tebi, :Ti, :pebi, :Pi, :exbi, :Ei, :zebi, :Zi, :yobi, :Yi]
  61. 61. /^(googol|yotta|femto|zetta|zepto|micro| milli|centi|hecto|yocto|exbi|giga|tebi| mega|pebi|kilo|atto|tera|kibi|deca|yobi| deci|pico|nano|gibi|zebi|mebi|peta|exa|Ki| Mi|Gi|Zi|da|Ei|Ti|Pi|Yi|z|a|y|f|p|n|m|c|d| h|k|M|G|T|P|E|Z|Y|u)?(.+)/
  62. 62. Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. Jamie Zawinski
  63. 63. I thought "I know, I'll use regular expressions."
  64. 64. I now had two problems
  65. 65. but
  66. 66. def self.prefix_matcher @prefix_matcher ||= begin prefix_keys = unit_prefixes.keys.map(&:to_s). sort{ |a,b| b.length <=> a.length } %r{^(#{prefix_keys.join('|')})?(.+)} end end
  67. 67. 2.0
  68. 68. 1.9
  69. 69. Constraints Keep existing interface
  70. 70. Constraints Keep existing interface No method_missing
  71. 71. Constraints Keep existing interface No method_missing Increase performance
  72. 72. module Alchemist def self.setup category = nil if category load_category category else load_all_categories end end private def self.load_all_categories library.load_all_categories end def self.load_category category library.load_category category end end
  73. 73. module Alchemist class ModuleBuilder def initialize category @category = category end def build Module.new.tap do |category_module| category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) category_module.class_eval category_methods end end
  74. 74. private attr_reader :category def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "n" + prefixed_methods(name) end.join("n") end def unit_names library.unit_names(category) end def prefixes_with_value(name) if library.si_units.include?(name.to_s) library.unit_prefixes else [] end end def prefixed_methods(name) prefixes_with_value(name).map do |prefix, value| %(define_method("#{prefix}#{name}") { Alchemist.measure self, :#{name}, #{value} }) end.join("n") end
  75. 75. module Alchemist class ModuleBuilder def initialize category @category = category end def build Module.new.tap do |category_module| category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) category_module.class_eval category_methods end end
  76. 76. module Alchemist class ModuleBuilder def initialize category @category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end
  77. 77. [success_kid.jpg]
  78. 78. #include <ruby.h> #include <string.h> the c-level VALUE cParenParser; VALUE paren_parser_parse(VALUE self, VALUE str) { const char *c_str = RSTRING_PTR(str); char *temp = (char *)malloc(sizeof(char) * RSTRING_LEN(str) + 1); int temp_pos = 1; int c_str_pos = 0; temp[0] = c_str[0]; while(c_str[c_str_pos++] != '0') { if(temp_pos > 0 && temp[temp_pos-1] == ')' && c_str[c_str_pos] == '(') { temp_pos--; } else { temp[temp_pos++] = c_str[c_str_pos];
  79. 79. How to be an artist
  80. 80. Have a toolbox
  81. 81. Be inspired by others
  82. 82. Surround yourself with talented artists
  83. 83. Surround yourself with talented artists
  84. 84. Use well known techniques
  85. 85. Use well known techniques
  86. 86. Measure improvement
  87. 87. PULLREVIEW
  88. 88. Have feelings
  89. 89. Know when to break the rules
  90. 90. Have fun.
  91. 91. Prime learn.thoughtbot.com/prime
  92. 92. THISISMETIS.COM
  93. 93. ?

×