tdc 2012 - patterns e anti-patterns em ruby

78
Donets Basin Mina de carvão-Ucrânia Genius is the gold in the mine; talent is the miner who works and brings it out. Lady Marguerite Blessington

Upload: fabio-akita

Post on 18-May-2015

2.506 views

Category:

Technology


5 download

DESCRIPTION

Palestra apresentada no The Developers Conference 2012 em São Paulo. Explicação sobre Patterns e Anti-Patterns em Ruby para quem está iniciando a aprender a linguagem.

TRANSCRIPT

Page 1: TDC 2012 - Patterns e Anti-Patterns em Ruby

Donets BasinMina de carvão-Ucrânia

Genius is the gold in the mine; talent is the miner who works and brings it out.Lady Marguerite Blessington

“”

Page 2: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 3: TDC 2012 - Patterns e Anti-Patterns em Ruby

@akitaonrails

Ruby e Ruby on Rails - 2006

Rails Summit Latin America - 2008

RubyConf Brasil - 2010

Page 4: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 5: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 6: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 7: TDC 2012 - Patterns e Anti-Patterns em Ruby

Patterns e Anti-Patternsem Ruby

Fabio Akita (@akitaonrails)

Page 8: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 9: TDC 2012 - Patterns e Anti-Patterns em Ruby

Pattern PADRÃO

Page 10: TDC 2012 - Patterns e Anti-Patterns em Ruby

Pattern PADRÃO

Default

STANDARD

Page 11: TDC 2012 - Patterns e Anti-Patterns em Ruby

“Pattern”

Page 12: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 13: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 14: TDC 2012 - Patterns e Anti-Patterns em Ruby

“Standard”

Page 15: TDC 2012 - Patterns e Anti-Patterns em Ruby

Christopher Alexander

Page 16: TDC 2012 - Patterns e Anti-Patterns em Ruby

"...each pattern represents our current best guess ... to solve the problem presented. ..., the patterns are still hypotheses, all ... of them -and are therefore all tentative, all free to evolve under the impact of new experience and observation"

—Christopher Alexander, A Pattern Language, p. xv

Page 17: TDC 2012 - Patterns e Anti-Patterns em Ruby

ERB

require 'erubis'

def render_erb(filepath) content = File.read(filepath) template = Erubis::Eruby.new(content) context = { title: "Hello World", paragraph: "This is an ERB template." } output = template.evaluate(context)end

puts render_erb("sample.erb")

Page 18: TDC 2012 - Patterns e Anti-Patterns em Ruby

ERB

<html> <head><title><%= @title %></title></head> <body> <p><%= @paragraph %></p> </body></html>

<html> <head><title>Hello World</title></head> <body> <p>This is an ERB template.</p> </body></html>

Page 19: TDC 2012 - Patterns e Anti-Patterns em Ruby

Markdown

require 'rdiscount'

def render_markdown(filepath) content = File.read(filepath) template = RDiscount.new(content) output = template.to_htmlend

puts render_markdown("sample.md")

Page 20: TDC 2012 - Patterns e Anti-Patterns em Ruby

Markdown

# Hello World

This is a Markdown example

* [Markdown](http://daringfireball.net/projects/markdown/syntax/)

<h1>Hello World</h1>

<p>This is a Markdown example</p>

<ul><li><a href="http://daringfireball.net/projects/markdown/syntax/">Markdown</a></li></ul>

Page 21: TDC 2012 - Patterns e Anti-Patterns em Ruby

def render(filepath) case filepath when /erb$/ then render_erb(filepath) when /md$/ then render_markdown(filepath) when /sass$/ then render_sass(filepath) ... endend

puts render("sample.erb")

Page 22: TDC 2012 - Patterns e Anti-Patterns em Ruby

Strategy

Page 23: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 24: TDC 2012 - Patterns e Anti-Patterns em Ruby

Template Engines supported by Tilt

ENGINE FILE EXTENSIONS REQUIRED LIBRARIES-------------------------- ----------------------- ----------------------------ERB .erb, .rhtml none (included ruby stdlib)Interpolated String .str none (included ruby core)Erubis .erb, .rhtml, .erubis erubisHaml .haml hamlSass .sass haml (< 3.1) or sass (>= 3.1)Scss .scss haml (< 3.1) or sass (>= 3.1)Less CSS .less lessBuilder .builder builderLiquid .liquid liquidRDiscount .markdown, .mkd, .md rdiscountRedcarpet .markdown, .mkd, .md redcarpetBlueCloth .markdown, .mkd, .md blueclothKramdown .markdown, .mkd, .md kramdownMaruku .markdown, .mkd, .md marukuRedCloth .textile redclothRDoc .rdoc rdocRadius .radius radiusMarkaby .mab markabyNokogiri .nokogiri nokogiriCoffeeScript .coffee coffee-script (+ javascript)Creole (Wiki markup) .wiki, .creole creoleWikiCloth (Wiki markup) .wiki, .mediawiki, .mw wikiclothYajl .yajl yajl-ruby

Page 25: TDC 2012 - Patterns e Anti-Patterns em Ruby

ERB and Markdown through Tilt

require 'tilt'

template = Tilt.new("sample.erb")context = { title: "Hello World", paragraph: "This is an ERB template." }output = template.render(context)

puts output

template = Tilt.new("sample.md")output = template.render

puts output

Page 26: TDC 2012 - Patterns e Anti-Patterns em Ruby

Same example as previously

require 'tilt'

template = Tilt::ErubisTemplate.new("sample.erb")context = { title: "Hello World", paragraph: "This is an ERB template." }output = template.render(context)

puts output

template = Tilt::RDiscountTemplate.new("sample.md")output = template.render

puts output

Page 27: TDC 2012 - Patterns e Anti-Patterns em Ruby

tilt.rb

module Tilt ... @preferred_mappings = Hash.new @template_mappings = Hash.new { |h, k| h[k] = [] }

# Hash of template path pattern => template implementation class mappings. def self.mappings @template_mappings end ... # Register a template implementation by file extension. def self.register(template_class, *extensions) if template_class.respond_to?(:to_str) # Support register(ext, template_class) too extensions, template_class = [template_class], extensions[0] end

extensions.each do |ext| ext = normalize(ext) mappings[ext].unshift(template_class).uniq! end end ...

Page 28: TDC 2012 - Patterns e Anti-Patterns em Ruby

Tilt#register

require 'tilt/string'register StringTemplate, 'str'

require 'tilt/erb'register ERBTemplate, 'erb', 'rhtml'register ErubisTemplate, 'erb', 'rhtml', 'erubis'

require 'tilt/haml'register HamlTemplate, 'haml'

require 'tilt/css'register SassTemplate, 'sass'register ScssTemplate, 'scss'register LessTemplate, 'less'

...

Page 29: TDC 2012 - Patterns e Anti-Patterns em Ruby

Tilt#[]

> Tilt["erb"]=> Tilt::ErubisTemplate

> Tilt["md"]=> Tilt::RDiscountTemplate

> Tilt["haml"]=> Tilt::HamlTemplate

> Tilt['coffee']=> Tilt::CoffeeScriptTemplate

Page 30: TDC 2012 - Patterns e Anti-Patterns em Ruby

Tilt overrided #new

module Tilt ... # Create a new template for the given # file using the file's extension # to determine the the template mapping. def self.new(file, line=nil, options={}, &block) if template_class = self[file] template_class.new(file, line, options, &block) else fail "No template engine registered for #{File.basename(file)}" end end

Page 31: TDC 2012 - Patterns e Anti-Patterns em Ruby

Factory

Page 32: TDC 2012 - Patterns e Anti-Patterns em Ruby

Tilt::Template Lint

module Tilt # Base class for template implementations. # Subclasses must implement the #prepare method and one # of the #evaluate or #precompiled_template methods. class Template ... def render(scope=Object.new, locals={}, &block) evaluate scope, locals || {}, &block end

protected def prepare; ... end def evaluate(scope, locals, &block); ... end def precompiled(locals); ... end def precompiled_template(locals); ... end def precompiled_preamble(locals); ... end def precompiled_postamble(locals); ''; end def compiled_method(locals_keys); ... end endend

Page 33: TDC 2012 - Patterns e Anti-Patterns em Ruby

Tilt::RedClothTemplate Implementation

module Tilt # RedCloth implementation. See: # http://redcloth.org/ class RedClothTemplate < Template def self.engine_initialized? defined? ::RedCloth end

def initialize_engine require_template_library 'redcloth' end

def prepare @engine = RedCloth.new(data) @output = nil end

def evaluate(scope, locals, &block) @output ||= @engine.to_html end endend

Page 34: TDC 2012 - Patterns e Anti-Patterns em Ruby

Adapter

Page 35: TDC 2012 - Patterns e Anti-Patterns em Ruby

Simple Web

Mongrel

Thin

Webrick

Rails

Sinatra

Ramaze

CGI

Page 36: TDC 2012 - Patterns e Anti-Patterns em Ruby

Simple Web

Mongrel

Thin

Webrick

Rails

Sinatra

Ramaze

CGI

Page 37: TDC 2012 - Patterns e Anti-Patterns em Ruby

Simple Web

Mongrel

Thin

Webrick

Rails

Sinatra

Ramaze

CGIRack

Page 38: TDC 2012 - Patterns e Anti-Patterns em Ruby

Minimal Rack Compliant

class HelloWorld def call(env) [200, {"Content-Type" => "text/plain"}, ["Hello world!"]] endend

hello_world = ->(env) { [200, {"Content-Type" => "text/plain"}, ["Hello world!"]] }

Page 39: TDC 2012 - Patterns e Anti-Patterns em Ruby

rackup app.ru

hello_world = ->(env) { [200, {"Content-Type" => "text/html"}, ["<h1>Hello world!</h1>"]] }

use Rack::ContentType, "text/html"use Rack::ShowExceptionsuse Rack::Auth::Basic, "Rack Demo" do |username, password| 'secret' == passwordend

# Setup Rackrun Rack::URLMap.new( { "/hello" => hello_world, "/" => Rack::File.new( "index.html" )} )

Page 40: TDC 2012 - Patterns e Anti-Patterns em Ruby

rackup app.ru

module Rack class ContentType include Rack::Utils

def initialize(app, content_type = "text/html") @app, @content_type = app, content_type end

def call(env) status, headers, body = @app.call(env) headers = Utils::HeaderHash.new(headers)

unless STATUS_WITH_NO_ENTITY_BODY.include?(status) headers['Content-Type'] ||= @content_type end

[status, headers, body] end endend

Page 41: TDC 2012 - Patterns e Anti-Patterns em Ruby

rake middleware (Rails)

use ActionDispatch::Staticuse Rack::Lock...use Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacheuse ActionDispatch::Cookiesuse ActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETaguse ActionDispatch::BestStandardsSupportuse Warden::Manageruse OmniAuth::Strategies::Twitteruse OmniAuth::Strategies::Facebookrun Rubyconf2012::Application.routes

Page 42: TDC 2012 - Patterns e Anti-Patterns em Ruby

Chain of Responsibility

Page 43: TDC 2012 - Patterns e Anti-Patterns em Ruby

class Relationship attr_accessor :state def initialize @state = :dating end def get_married make_vows @state = :married eat_wedding_cake end def get_divorced @state = :divorced end

def make_vows; "I do"; end def eat_wedding_cake; "Yummy"; endend

Page 44: TDC 2012 - Patterns e Anti-Patterns em Ruby

class Relationship attr_accessor :state def initialize @state = :dating end def get_married raise "Must date before marry" unless @state == :dating make_vows @state = :married eat_wedding_cake end def get_divorced raise "Must be married before divorce" unless @state == :married @state = :divorced end

def make_vows; "I do"; end def eat_wedding_cake; "Yummy"; endend

Page 45: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 46: TDC 2012 - Patterns e Anti-Patterns em Ruby

class Relationship include AASM

aasm do state :dating, initial: true state :married state :divorced

event :get_married, :before => :make_vows, :after => :eat_wedding_cake do transitions from: [:dating], to: :married end event :get_divorced do transitions from: [:married], to: :divorced end end def make_vows; "I do"; end def eat_wedding_cake; "Yummy"; endend

Page 47: TDC 2012 - Patterns e Anti-Patterns em Ruby

class Relationship attr_accessor :dating, :married, :divorced def initialize @dating, @married, @divorced = true, false, false end def get_married raise "Must date before marry" unless dating make_vows @dating, @married = false, true eat_wedding_cake end def get_divorced raise "Must be married before divorce" unless married @married, @divorced = false, true end

def make_vows; "I do"; end def eat_wedding_cake; "Yummy"; endend

Page 48: TDC 2012 - Patterns e Anti-Patterns em Ruby

State

Page 49: TDC 2012 - Patterns e Anti-Patterns em Ruby

class SaleOrder attr_accessor :items, :value, :checkout_date def initialize(*args) @items, @value, @checkout_date = args endend

sale = SaleOrder.new( ['Biscuit', 'Cheese'], 15.0, "2012-07-07")

Page 50: TDC 2012 - Patterns e Anti-Patterns em Ruby

require 'money'require 'time'class SaleOrder attr_reader :value, :checkout_date def initialize(options = {}) @items, @value, @checkout_date = [], Money.new(0.0, "USD"), nil self.items = options[:items] || [] self.value = options[:value] || 0.0 self.checkout_date = options[:checkout_date] end def items=(items); @items += items.dup; end def items(index); @items[index]; end def value=(value); @value = Money.new(value.to_f, "USD"); end def checkout_date=(date) @checkout_date = Date.parse(date) if date endend

sale = SaleOrder.new(items: ['Biscuit', 'Cheese'], value: 15.0, checkout_date: "2012-07-07")

Page 51: TDC 2012 - Patterns e Anti-Patterns em Ruby

Primitive Obsession

Page 52: TDC 2012 - Patterns e Anti-Patterns em Ruby

http://u.akita.ws/avdi-null

def slug(title) # 1 if title.nil? title.strip.downcase.tr_s('^[a-z0-9]', '-') end

# 2 title.strip.downcase.tr_s('^[a-z0-9]', '-') if title

# 3 (title || "").strip.downcase.tr_s('^[a-z0-9]', '-')

# 4 title.strip.downcase.tr_s('^[a-z0-9]', '-') rescue nilend

Page 53: TDC 2012 - Patterns e Anti-Patterns em Ruby

ActiveSupport - Try

class Object def try(*a, &b) if a.empty? && block_given? yield self else public_send(*a, &b) end endend

class NilClass def try(*args) nil endend

Page 54: TDC 2012 - Patterns e Anti-Patterns em Ruby

http://u.akita.ws/avdi-null

require 'active_support/core_ext/object/try'

def slug(title) title.try(:strip).try(:downcase). try(:tr_s, '^[a-z0-9]', '-')end

<%= title ? title.downcase : "" %>

<%= title.try(:downcase) %>

Page 55: TDC 2012 - Patterns e Anti-Patterns em Ruby

http://u.akita.ws/avdi-null

class NullObject def method_missing(*args, &block) self end def to_a; []; end def to_s; ""; end def to_f; 0.0; end def to_i; 0; end def tap; self; end def to_value; nil; endend

def Maybe(value) value.nil? ? NullObject.new : valueend

class Object def to_value self endend

Page 56: TDC 2012 - Patterns e Anti-Patterns em Ruby

http://u.akita.ws/avdi-null

def slug(title) Maybe(title).strip.downcase.tr_s('^[a-z0-9]', '-')end

def slug(title) title = Maybe(title) if title.to_value # do something useful end title.strip.downcase.tr_s('^[a-z0-9]', '-')end

Page 57: TDC 2012 - Patterns e Anti-Patterns em Ruby

http://u.akita.ws/avdi-null

git clone git://github.com/akitaonrails/null.gitcd nullgem build null.gemspecgem install null-0.1.gem

> require 'null'> Maybe(someobj).foo.bar.something#=> null

> (100.0 / (NULL * 15.5) - 150)#=> null

object = Maybe(someobj)if object.truthy? # something usefulend

Page 58: TDC 2012 - Patterns e Anti-Patterns em Ruby

Null Object

Page 59: TDC 2012 - Patterns e Anti-Patterns em Ruby

class SalesOrder < Struct.new(:products, :total, :buyer_name) def html_receipt html_items = products.inject("") do |html, item| html += "<li>#{item}</li>" end

html = %{<h1>Thanks for the Purchase #{buyer_name}!</h1> <p>You purchased:</p> <ul> #{html_items} </ul> <p>Total: $#{total}</p>} end end

order = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )

Page 60: TDC 2012 - Patterns e Anti-Patterns em Ruby

> order.html_receipt

=> "<h1>Thanks for the Purchase John Doe!</h1> <p>You purchased:</p> <ul> <li>Bacon</li><li>Cheese</li> </ul> <p>Total: $10.0</p>"

Page 61: TDC 2012 - Patterns e Anti-Patterns em Ruby

class SalesOrder < Struct.new(:products, :total, :buyer_name) def html_receipt html_items = products.inject("") do |html, item| html += "<li>#{item}</li>" end

html = %{<h1>Thanks for the Purchase #{buyer_name}!</h1> <p>You purchased:</p> <ul> #{html_items} </ul> <p>Total: $#{total}</p>} end end

order = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )

Page 62: TDC 2012 - Patterns e Anti-Patterns em Ruby

class SalesOrder < Struct.new(:products, :total, :buyer_name)end

class SalesOrderDecorator < SimpleDelegator def html_receipt html_items = products.inject("") do |html, item| html += "<li>#{item}</li>" end

html = %{<h1>Thanks for the Purchase #{buyer_name}!</h1> <p>You purchased:</p> <ul> #{html_items} </ul> <p>Total: $#{total}</p>} end end

order = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )decorated_order = SalesOrderDecorator.new(order)

Page 63: TDC 2012 - Patterns e Anti-Patterns em Ruby

> decorated_order.html_receipt

=> "<h1>Thanks for the Purchase John Doe!</h1> <p>You purchased:</p> <ul> <li>Bacon</li><li>Cheese</li> </ul> <p>Total: $10.0</p>" > decorated_order.total

=> 10.0

> decorated_order.products

=> ["Bacon", "Cheese"]

Page 64: TDC 2012 - Patterns e Anti-Patterns em Ruby

# originalorder = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )decorated_order = SalesOrderDecorator.new(order)

# newclass SalesOrderDecorator < SimpleDelegator def initialize(*args) if args.first.is_a?(SalesOrder) super(args.first) else order = SalesOrder.new(*args) super(order) end end ... end

decorated_order = SalesOrderDecorator.new(["Bacon", "Cheese"], 10.0, "John Doe" )decorated_order.html_receipt

Page 65: TDC 2012 - Patterns e Anti-Patterns em Ruby

Decorator/Presenter

Page 66: TDC 2012 - Patterns e Anti-Patterns em Ruby

Strategy

Factory

Adapter (Proxy, Bridge)

Chain of Responsibility

State

Primitive Obsession

Null Object

Decorator (Presenter)

Page 67: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 68: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 69: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 70: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 71: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 72: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 73: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 74: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 75: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 76: TDC 2012 - Patterns e Anti-Patterns em Ruby
Page 77: TDC 2012 - Patterns e Anti-Patterns em Ruby

[email protected]

www.codeminer42.com.br+55 11 3729 14 22

slideshare.net/akitaonrails

Page 78: TDC 2012 - Patterns e Anti-Patterns em Ruby

[email protected]

www.codeminer42.com.br+55 11 3729 14 22

slideshare.net/akitaonrails

MUITO OBRIGADO