Nordic Ruby 2011
-
Upload
aaron-patterson -
Category
Technology
-
view
3.242 -
download
10
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