Nordic Ruby 2011

Post on 19-May-2015

3.242 views 10 download

Tags:

Transcript of Nordic Ruby 2011

Saturday, June 18, 2011

Saturday, June 18, 2011

bitcoin==

con-currency?

Saturday, June 18, 2011

Saturday, June 18, 2011

Me!José! ♥Saturday, June 18, 2011

WWFMD?Saturday, June 18, 2011

Public Service Announcements

Saturday, June 18, 2011

SIGSEGV is bad

Saturday, June 18, 2011

neversaydie

Saturday, June 18, 2011

begin # something dangerousrescue NeverSayDie # fix memoryend

Saturday, June 18, 2011

"Not working in production mode"

Saturday, June 18, 2011

DO NOT USE THIS SOFTWARE

Saturday, June 18, 2011

ruby -w

Saturday, June 18, 2011

def hello x = Object.new # warning 10 + 10end

Saturday, June 18, 2011

warning: assigned but unused variable - x

Saturday, June 18, 2011

How deprecation notices SHOULD be

written

Saturday, June 18, 2011

class Foo def deprecated if $VERBOSE warn "hey bro, don't call this" end 10 endend

Saturday, June 18, 2011

RSpec Problem

Saturday, June 18, 2011

warning: useless use of == in void context

Saturday, June 18, 2011

it "passes" do 10.should == 10 11.should == 11end

Saturday, June 18, 2011

def check(thing)end

def deprecated check 10.should == 10 check 11.should == 10end

Saturday, June 18, 2011

@tenderlove

Saturday, June 18, 2011

AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.

Saturday, June 18, 2011

Ruby Core Team

Saturday, June 18, 2011

Rails Core Team

Saturday, June 18, 2011

Saturday, June 18, 2011

White Guy

Saturday, June 18, 2011

Story about Geethika and pinochle, java, etc

Saturday, June 18, 2011

It's TYRA!

Story about Geethika and pinochle, java, etc

Saturday, June 18, 2011

<3 <3 <3

Saturday, June 18, 2011

"Congratulations, you just made us not consider Rails

for anything anymore despite our coders liking it."

Saturday, June 18, 2011

SWEDEN!

Saturday, June 18, 2011

IKEA!Saturday, June 18, 2011

MEATBALLS!Saturday, June 18, 2011

SWEDISH FISH!Saturday, June 18, 2011

Made in Canada!Saturday, June 18, 2011

Made in Canada!Saturday, June 18, 2011

How do you catch such tiny fish?

Saturday, June 18, 2011

Will you share your Swedish FishFishing spot?

Saturday, June 18, 2011

Mountain Dewand my

Trail of Tears

Talk about Rails,Talk about work projects

Saturday, June 18, 2011

Legacy Code

Saturday, June 18, 2011

What isLegacy Code?

Saturday, June 18, 2011

Untested

Saturday, June 18, 2011

Old

Saturday, June 18, 2011

Not Understood

Maintainers are gone

Saturday, June 18, 2011

Importance of Legacy Code

Saturday, June 18, 2011

Old code containsKnowledge

Saturday, June 18, 2011

Solves Today's Problems

Saturday, June 18, 2011

All Legacy Code isNot Equal

Saturday, June 18, 2011

TenderloveTear Formula

Saturday, June 18, 2011

Code BurdenTears

Time

Saturday, June 18, 2011

Tears cried at time T

Saturday, June 18, 2011

Volume at time T

Saturday, June 18, 2011

Last Week

Saturday, June 18, 2011

I LOVE hackingold code

Saturday, June 18, 2011

Techniques

Saturday, June 18, 2011

Universal

Techniques

Extending

Testing

Saturday, June 18, 2011

Universal

Saturday, June 18, 2011

Liskov substitution principle

Saturday, June 18, 2011

A subclass can be used in place of it's

superclass

Saturday, June 18, 2011

Object

Animal

Person

Saturday, June 18, 2011

class Animalend

class Person < Animalend

Saturday, June 18, 2011

def leg_count(animal) animal.legsend

def account_number(person) person.account_numberend

Saturday, June 18, 2011

Single Responsibility Principal

Saturday, June 18, 2011

Each class has one and only one responsibility

Saturday, June 18, 2011

Each class has one reason to change

Saturday, June 18, 2011

Method Extraction

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url http_get end

private def http_get Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url http_get(url.host, url.path) end

private def http_get(host, path) Net::HTTP.get(host, path) endend

I don't like it, but it helps us to reason

Saturday, June 18, 2011

Object#extend

Saturday, June 18, 2011

class Foo def metaclass class << self; self; end endend

x = Foo.newp x.metaclass.ancestors

x.extend(Module.new { })p x.metaclass.ancestors

Saturday, June 18, 2011

[Foo, Object, Kernel, BasicObject]

[#<Module:0x81c98>, Foo, Object, Kernel, BasicObject]

Saturday, June 18, 2011

Code Seams

Saturday, June 18, 2011

Testing

Saturday, June 18, 2011

Load Path Hacking

Saturday, June 18, 2011

$LOAD_PATH

Saturday, June 18, 2011

ruby -Ifoo script.rb

Saturday, June 18, 2011

require 'net/http'

class WebClient def get(url) url = URI.parse url Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

[aaron@higgins project (master)]$ find .../test./test/lib./test/lib/net./test/lib/net/http.rb./web.rb

Saturday, June 18, 2011

module Net class HTTP def self.get(host, path) "hello world" end endend

test/lib/net/http.rb

Saturday, June 18, 2011

ruby -I test/lib web.rb

Saturday, June 18, 2011

require 'web'require 'minitest/autorun'

class WebTest < MiniTest::Unit::TestCase def test_get client = WebClient.new response = client.get 'http://www.reddit.com/r/ruby' assert_equal 'hello world', response endend

Saturday, June 18, 2011

require File.expand_path( File.join('..', 'foo'))

People don't know about -IRSpec and test/unit will set your -I for you

Saturday, June 18, 2011

Heavy Handed

Saturday, June 18, 2011

Good for Small APIs

Saturday, June 18, 2011

Constant Hacking

Saturday, June 18, 2011

require 'net/http'

class WebClient def get(url) url = URI.parse url Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

class MyHTTP def self.get(host, path) "hello world" endend

WebClient.const_set(:Net, Module.new)WebClient::Net.const_set(:HTTP, MyHTTP)

Saturday, June 18, 2011

class WebTest < MiniTest::Unit::TestCase def setup WebClient.const_set(:Net, Module.new) WebClient::Net.const_set(:HTTP, MyHTTP) end

def teardown WebClient.send(:remove_const, :Net) end

def test_get client = WebClient.new response = client.get 'http://www.reddit.com/r/ruby' assert_equal 'hello world', response endend

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url ::Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

Heavy Handed

Localized changes to one class

Still must mock entire API

Saturday, June 18, 2011

Subclass Testing

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url Net::HTTP.get(url.host, url.path) endend

Saturday, June 18, 2011

require 'net/http'

class WebClient def get(url) url = URI.parse url http_get(url.host, url.path) end

private def http_get(host, path) Net::HTTP.get(host, path) endend

Saturday, June 18, 2011

Class.new

Saturday, June 18, 2011

class WebTest < MiniTest::Unit::TestCase def test_get client = Class.new(WebClient) { def http_get(host, path) 'hello world' end }.new

response = client.get 'http://www.reddit.com/r/ruby' assert_equal 'hello world', response endend

Saturday, June 18, 2011

Annoying Constructors

Saturday, June 18, 2011

class Column def initialize(name, default, sql_type = nil, null = true) @name = name @sql_type = sql_type @null = null @limit = extract_limit(sql_type) @precision = extract_precision(sql_type) @scale = extract_scale(sql_type) @type = simplified_type(sql_type) @default = extract_default(default) @primary = nil @coder = nil end ....end

Many parameters,

don't care about some of them

Saturday, June 18, 2011

Just Pass nil

Saturday, June 18, 2011

def test_type_cast_true c = Column.new(nil, 1, 'int')

assert_equal 't', @conn.type_cast(true, nil) assert_equal 1, @conn.type_cast(true, c)end

Saturday, June 18, 2011

Not Constructible

Can't construct, but want to break dependencies

Saturday, June 18, 2011

pool = ActiveRecord::Base.connection_pool

Too hard to construct

Saturday, June 18, 2011

pool = ActiveRecord::Base.connection_pool.dup

pool.extend(Module.new { def checkin conn @checkins << conn conn.object_id end})

Saturday, June 18, 2011

Detecting Changes

Saturday, June 18, 2011

Measure http_getclass WebClient def get(url) url = URI.parse url http_get(url.host, url.path) end

private def http_get(host, path) Net::HTTP.get(host, path) endend

Saturday, June 18, 2011

via Class.new

Saturday, June 18, 2011

def test_http_get_count call_count = 0 client = Class.new(WebClient) { define_method(:http_get) { |host, path| call_count += 1 "hello world" } }.new

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Saturday, June 18, 2011

def test_http_get_count call_count = 0 client = Class.new(WebClient) { define_method(:http_get) { |host, path| call_count += 1 "hello world" } }.new

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Saturday, June 18, 2011

def test_http_get_count call_count = 0 client = Class.new(WebClient) { define_method(:http_get) { |host, path| call_count += 1 "hello world" } }.new

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Decide to call super or not

Saturday, June 18, 2011

via Module.new

Saturday, June 18, 2011

def test_http_get_count client = WebClient.new

call_count = 0 client.extend(Module.new { define_method(:http_get) { |host, path| call_count += 1 "hello world" } })

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Saturday, June 18, 2011

def test_http_get_count client = WebClient.new

call_count = 0 client.extend(Module.new { define_method(:http_get) { |host, path| call_count += 1 "hello world" } })

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Saturday, June 18, 2011

def test_http_get_count client = WebClient.new

call_count = 0 client.extend(Module.new { define_method(:http_get) { |host, path| call_count += 1 "hello world" } })

assert_equal 0, call_count client.get 'http://www.reddit.com/r/ruby' assert_equal 1, call_countend

Saturday, June 18, 2011

Extending

Saturday, June 18, 2011

Huge Methods!

Saturday, June 18, 2011

def table_rows rows[table_name] = fixtures.map do |label, fixture| if model_class && model_class < ActiveRecord::Base reflection_class.reflect_on_all_associations.each do |association| case association.macro when :belongs_to # Do not replace association name with association foreign key if they are named the same fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s

if association.name.to_s != fk_name && value = row.delete(association.name.to_s) if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "") # support polymorphic belongs_to as "label (Type)" row[association.foreign_type] = $1 end

row[fk_name] = ActiveRecord::Fixtures.identify(value) end when :has_and_belongs_to_many if (targets = row.delete(association.name.to_s)) targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) table_name = association.options[:join_table] rows[table_name].concat targets.map { |target| { association.foreign_key => row[primary_key_name], association.association_foreign_key => ActiveRecord::Fixtures.identify(target) } } end end end end row end rowsend

Saturday, June 18, 2011

Extract Methods

Saturday, June 18, 2011

def belongs_to_row(association, row) # Do not replace association name with association foreign key if they are named the same fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s

if association.name.to_s != fk_name && value = row.delete(association.name.to_s) if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "") # support polymorphic belongs_to as "label (Type)" row[association.foreign_type] = $1 end

row[fk_name] = ActiveRecord::Fixtures.identify(value)end

Saturday, June 18, 2011

def habtm_row(association, row) if (targets = row.delete(association.name.to_s)) targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) table_name = association.options[:join_table] rows[table_name].concat targets.map { |target| { association.foreign_key => row[primary_key_name], association.association_foreign_key => ActiveRecord::Fixtures.identify(target) } } endend

Saturday, June 18, 2011

def table_rows rows[table_name] = fixtures.map do |label, fixture| row = fixture.to_hash

if model_class && model_class < ActiveRecord::Base

# If STI is used, find the correct subclass for association reflection reflection_class = if row.include?(inheritance_column_name) row[inheritance_column_name].constantize rescue model_class else model_class end

reflection_class.reflect_on_all_associations.each do |association| case association.macro when :belongs_to belongs_to_row(association, row) when :has_and_belongs_to_many habtm_row(association, row) end end end

row end rowsend

Saturday, June 18, 2011

Extract Object

Saturday, June 18, 2011

class RowFilter def rows fixtures.map do |label, fixture| row = fixture.to_hash if model_class && model_class < ActiveRecord::Base # If STI is used, find the correct subclass for association reflection reflection_class = if row.include?(inheritance_column_name) row[inheritance_column_name].constantize rescue model_class else model_class end

reflection_class.reflect_on_all_associations.each do |association| case association.macro when :belongs_to belongs_to_row(association, row) when :has_and_belongs_to_many habtm_row(association, row) end end end row end endend

Saturday, June 18, 2011

class RowFilter attr_reader :fixtures, :model_class

def initialize(fixtures, model_class) @fixtures = fixtures @model_class = model_class endend

Saturday, June 18, 2011

def table_rows filter = RowFilter.new(fixtures, model_class) rows[table_name] = filter.rows rowsend

Saturday, June 18, 2011

Huge Classes!

Saturday, June 18, 2011

Look at Method Names

Saturday, June 18, 2011

Look at shared instance variables

Saturday, June 18, 2011

Group Similar Methods

Saturday, June 18, 2011

Use SRP to create a new class

Saturday, June 18, 2011

Then Delegate.

Saturday, June 18, 2011

All API calls

Saturday, June 18, 2011

class WebClient def get(url) url = URI.parse url http_get(url.host, url.path) end

private def http_get(host, path) Net::HTTP.get(host, path) endend

Once we had extracted the HTTP methods to their own functions, could reason about HTTP api.

Saturday, June 18, 2011

class WebClient def initialize(client = Net::HTTP) @client = client end

def get(url) url = URI.parse url http_get(url.host, url.path) end

private def http_get(host, path) @client.get(host, path) endend

Saturday, June 18, 2011

class TestHTTP < Struct.new(:data) def get(host, path) data[[host, path]] endend

Saturday, June 18, 2011

class WebTest < Test::Unit::TestCase def test_get_home mockhttp = TestHTTP.new({ ['localhost', '/~aaron/'] => 'hello' }) wc = WebClient.new mockhttp assert_equal 'hello', wc.get('http://localhost/~aaron/') endend

Saturday, June 18, 2011

We define expectations

Saturday, June 18, 2011

Not Bound to HTTP

Saturday, June 18, 2011

Legacy code can make you cry

Saturday, June 18, 2011

But it doesn't have to

Saturday, June 18, 2011

Don't be afraid!

Saturday, June 18, 2011

Ruby is your mocking framework

Saturday, June 18, 2011

Credits & More Info

Saturday, June 18, 2011

Open Questions?

Saturday, June 18, 2011

Do you like mocking / stubbing?

Saturday, June 18, 2011

How do these testing tools impact your API?

Saturday, June 18, 2011

Do you like hearts?

Saturday, June 18, 2011

How about kittens?

Saturday, June 18, 2011

<3 <3 <3

Saturday, June 18, 2011