The Art of Ruby
Upcoming SlideShare
Loading in...5
×
 

The Art of Ruby

on

  • 395 views

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

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.

Statistics

Views

Total Views
395
Views on SlideShare
379
Embed Views
16

Actions

Likes
0
Downloads
0
Comments
0

2 Embeds 16

https://twitter.com 15
http://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

The Art of Ruby The Art of Ruby Presentation Transcript

  • 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 or sculpture, producing works to be appreciated primarily for their beauty or emotional power.
  • /ä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.
  • 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 else puts "Wrong!" end end if !@won puts "You lost. The actual number is #{@actual_number}." end end
  • 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
  • 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 puts "Wrong!" end end
  • 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
  • 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 else puts "Wrong!" end end if !@won puts "You lost. The actual number is #{@actual_number}." end end
  • 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 puts "Wrong!" end end
  • 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
  • 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!" false end def correct_guess puts "Correct" false end
  • 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
  • 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
  • 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_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
  • 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
  • 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
  • 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
  • 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
  • def self.prefix_matcher keys = unit_prefixes.keys.map(&:to_s). sort{ |a,b| b.length <=> a.length } %r{^(#{keys.join('|')})?(.+)} end
  • [: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]
  • /^(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)?(.+)/
  • Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. Jamie Zawinski
  • 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.length } %r{^(#{prefix_keys.join('|')})?(.+)} end end
  • 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 def self.load_all_categories library.load_all_categories end def self.load_category category library.load_category category end end
  • 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
  • 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
  • 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
  • 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
  • [success_kid.jpg]
  • #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];
  • 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
  • ?