Slide 1

Slide 1 text

Fractal Design Ben Scofield / @bscofield / bscofi[email protected] 27 Aug 2011 / Ruby Hoedown

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Fractals

Slide 5

Slide 5 text

Self-similarity

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Simple, Repeated Rules

Slide 8

Slide 8 text

z = z + c n+1 n 2

Slide 9

Slide 9 text

z = z + c n+1 n 2

Slide 10

Slide 10 text

z = z + c n+1 n 2

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Software

Slide 13

Slide 13 text

Methods def add_messages
 delivery_lists.each do |list_id|
 ponies = ponies_in_batches(:list_id => list_id)
 
 while ponies.any?
 create_messages_for_ ponies(ponies).each { |message| add_message(message) }
 ponies = ponies_in_batches(:last_id => ponies.last.id, :list_id => list_id)
 end
 end
 end


Slide 14

Slide 14 text

Classes class Pony
 def initialize(options = {})
 # ...
 end
 
 def prance
 # ...
 end
 
 # ...
 end


Slide 15

Slide 15 text

Libraries Gem::Specification.new do |s|
 s.name = %q{brohoof}
 s.version = "0.3.0"
 
 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? ... s.authors = ["Rainbow Dash"]
 s.date = %q{2011-08-27}
 s.description = %q{Brohoof gives hoof-fives to your fellow bronies.}
 s.email = %q{[email protected]}
 s.extra_rdoc_files = [
 "LICENSE.txt",
 "README.md"
 ]
 
 # ...
 end

Slide 16

Slide 16 text

Libraries Frameworks Tools

Slide 17

Slide 17 text

Applications # This file is used by Rack-based servers to start the application.
 
 require ::File.expand_path('../config/environment', __FILE__)
 run Celestia::Application


Slide 18

Slide 18 text

Applications Standalone Ecosystems

Slide 19

Slide 19 text

Data CREATE TABLE `cities` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 `created_at` datetime DEFAULT NULL,
 `updated_at` datetime DEFAULT NULL,
 `latitude` double DEFAULT NULL,
 `longitude` double DEFAULT NULL,
 `seo_name` varchar(255) DEFAULT NULL,
 `country_id` int(11) DEFAULT NULL,
 `inhabitants` int(11) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 UNIQUE KEY `index_cities_on_seo_name` (`seo_name`),
 KEY `index_cities_on_country_id` (`country_id`),
 ) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8;

Slide 20

Slide 20 text

Tests class UnicornTest < ActiveSupport::TestCase
 test "unicorns should have magic" do
 trixie = Unicorn.new
 
 assert trixie.magical?
 end
 end

Slide 21

Slide 21 text

Insanity is doing the same thing over and over again but expecting different results

Slide 22

Slide 22 text

Context!

Slide 23

Slide 23 text

Principles

Slide 24

Slide 24 text

Examples

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Test-Driven Design

Slide 29

Slide 29 text

Methods Unit testing

Slide 30

Slide 30 text

Systems Integration testing

Slide 31

Slide 31 text

Applications Acceptance testing

Slide 32

Slide 32 text

Database Changes class AddVelocityToPonies
 def self.up
 add_column :ponies, :max_velocity, :float
 end
 
 def self.down
 remove_column :ponies, :max_velocity
 end
 end

Slide 33

Slide 33 text

Single Responsibility Principle

Slide 34

Slide 34 text

Methods def plan_party(date)
 if is_a_good_day_for_a_party?(date)
 possible_venues = Venue.available_on(date)
 venue = possible_venues.first
 
 bands = Band.order('awesome DESC').all
 bands.each do |band|
 break if band.book(date)
 end
 
 buy_food
 
 invite_ponies(date, venue)
 end
 end

Slide 35

Slide 35 text

ActiveRecord::Base Persistence Business logic

Slide 36

Slide 36 text

Applications $ cat config/routes.rb | grep -c namespace
 7


Slide 37

Slide 37 text

Bitfields class AddPowersToPonies
 def self.up
 add_column :ponies, :powers, :int
 end
 
 def self.down
 remove_column :ponies, :powers
 end
 end Pony.powers << :magic
 Pony.powers << :flight
 
 # Time passes
 
 Pony.powers << :likes_to_plan_parties
 Pony.powers << :pink_mane

Slide 38

Slide 38 text

Interface Segregation Principle

Slide 39

Slide 39 text

Modules module PonyUtilities
 def comb_mane
 # ...
 end
 
 def cleanse_parasprites
 # ...
 end
 
 def expose_impostor
 # ...
 end
 end

Slide 40

Slide 40 text

Helpers $ ls app/helpers
 application_helper.rb
 authors_helper.rb
 categories_helper.rb
 comments_helper.rb
 posts_helper.rb
 tags_helper.rb

Slide 41

Slide 41 text

ActiveSupport require 'active_support/time'
 require 'active_support/ordered_hash' require 'active_support/core_ext/array'
 require 'active_support/core_ext/string'


Slide 42

Slide 42 text

JavaScript $ cat public/javascripts/application.js | wc -l
 7333

Slide 43

Slide 43 text

CSS $ cat public/stylesheets/base.css | wc -l
 1740

Slide 44

Slide 44 text

Dependency Inversion Principle

Slide 45

Slide 45 text

Mocks and Stubs test "a unicorn should be able to ask her friends for advice" do
 spike = stub(:name => 'Spike', :advice => "Keep trying!")
 twilight_sparkle = Unicorn.new
 twilight_sparkle.friends << spike
 
 assert_equal ['Spike: Keep trying!'], twilight_sparkle.ask_for_advice
 end

Slide 46

Slide 46 text

Libraries Context of use

Slide 47

Slide 47 text

App Ecosystems Services

Slide 48

Slide 48 text

Resource Orientation

Slide 49

Slide 49 text

Web Applications REST

Slide 50

Slide 50 text

Classes and Methods OOP

Slide 51

Slide 51 text

...

Slide 52

Slide 52 text

Single Level of Abstraction

Slide 53

Slide 53 text

Methods def right_pony_for_the_job(pony, job)
 case job.class.name
 when 'Party': pony == PINKIE_PIE
 when 'Magic': pony.is_a?(Unicorn)
 when 'AnimalSoothing': pony == FLUTTERSHY
 when 'Flight': pony.can_fly?
 else false
 end
 end


Slide 54

Slide 54 text

Tests Behavior vs. implementation

Slide 55

Slide 55 text

Law of Demeter

Slide 56

Slide 56 text

Classes Fluttershy.pets.first.feed
 Fluttershy.pets.select { |pet| pet.devotion > 10 }.map(&:adopted_at)

Slide 57

Slide 57 text

App Ecosystems Enforced by APIs

Slide 58

Slide 58 text

Don’t Repeat Yourself

Slide 59

Slide 59 text

Methods Extract method

Slide 60

Slide 60 text

Classes Extract module

Slide 61

Slide 61 text

Libraries $ jeweler ponyville-police

Slide 62

Slide 62 text

Markup Generation Handlebars.js

Slide 63

Slide 63 text

Applications $ rails new friendship-magic

Slide 64

Slide 64 text

Asynchronicity

Slide 65

Slide 65 text

AJAX $.ajax('/friendship-lessons', {
 error: function(xhr, status, err) {
 // ...
 },
 success: function(data, status, xhr) {
 alert("Here's what I learned...");
 }
 });

Slide 66

Slide 66 text

Queuing

Slide 67

Slide 67 text

EventMachine class Orchard
 def initialize(owner)
 request = EM::HttpRequest.new('http://orchards.com').get
 
 request.callback {
 if request.response_header.status == 200
 data = JSON.parse(request.response)["responseData"]
 self.succeed data['ripe_apple_count']
 else
 self.fail("Orchard retrieval error")
 end
 }
 end
 end
 
 EM.run {
 orchard = Orchard.new('Applejack')
 orchard.callback { |count| puts "We’ve got #{count} ripe apples!" }
 }

Slide 68

Slide 68 text

On-Site Customer

Slide 69

Slide 69 text

Applications On-site customer

Slide 70

Slide 70 text

Libraries Client code Developers

Slide 71

Slide 71 text

RubyGems Library developers End developers

Slide 72

Slide 72 text

JavaScript Your mom

Slide 73

Slide 73 text

Can a given design principle be applied usefully at other levels of software design?

Slide 74

Slide 74 text

Questions?

Slide 75

Slide 75 text

Thank you! Ben Scofield / @bscofield / bscofi[email protected] 27 Aug 2011 / Ruby Hoedown

Slide 76

Slide 76 text

Thank you! Ben Scofield / @bscofield / bscofi[email protected] 27 Aug 2011 / Ruby Hoedown