“ Genius is the gold in the mine; talent is  the miner who works and brings it out. ” Lady Marguerite Blessington         ...
@akitaonrails      Ruby e Ruby on Rails - 2006Rails Summit Latin America - 2008           RubyConf Brasil - 2010
Patterns e         Anti-Patterns         em RubyFabio Akita (@akitaonrails)
Pattern   PADRÃO
STANDARD Pattern   PADRÃO Default
“Pattern”
“Standard”
Christopher Alexander
"...each pattern represents our current bestguess ... to solve the problem presented. ..., thepatterns are still hypothese...
require erubis def render_erb(filepath)   content = File.read(filepath)   template = Erubis::Eruby.new(content)   context ...
<html>   <head><title><%= @title %></title></head>   <body>     <p><%= @paragraph %></p>   </body> </html> <html>   <head>...
require rdiscount def render_markdown(filepath)   content = File.read(filepath)   template = RDiscount.new(content)   outp...
# Hello World This is a Markdown example * [Markdown](http://daringfireball.net/projects/markdown/ syntax/) <h1>Hello Worl...
def render(filepath)  case filepath  when /erb$/ then render_erb(filepath)  when /md$/ then render_markdown(filepath)  whe...
Strategy
ENGINE                       FILE EXTENSIONS           REQUIRED LIBRARIES  --------------------------   ------------------...
require tilt  template = Tilt.new("sample.erb")  context = { title: "Hello World",    paragraph: "This is an ERB template....
require tilt  template = Tilt::ErubisTemplate.new("sample.erb")  context = { title: "Hello World",    paragraph: "This is ...
module Tilt     ...     @preferred_mappings = Hash.new     @template_mappings = Hash.new { |h, k| h[k] = [] }     # Hash o...
require tilt/string  register StringTemplate, str  require tilt/erb  register ERBTemplate,    erb, rhtml  register ErubisT...
> Tilt["erb"]  => Tilt::ErubisTemplate  > Tilt["md"]  => Tilt::RDiscountTemplate  > Tilt["haml"]  => Tilt::HamlTemplate  >...
module Tilt    ...    # Create a new template for the given    # file using the files extension    # to determine the the ...
Factory
module Tilt    # Base class for template implementations.    # Subclasses must implement the #prepare method and one    # ...
module Tilt    # RedCloth implementation. See:    # http://redcloth.org/    class RedClothTemplate < Template      def sel...
Adapter
Rails         Mongrel         Sinatra     CGI    Thin        Ramaze             WebrickSimple Web
Rails         Mongrel         Sinatra     CGI    Thin        Ramaze             WebrickSimple Web
Rails          Mongrel         Sinatra     Rack                     CGI     Thin        Ramaze              WebrickSimple ...
class HelloWorld    def call(env)      [200,        {"Content-Type" => "text/plain"},        ["Hello world!"]]    end  end...
hello_world = ->(env) {    [200,      {"Content-Type" => "text/html"},      ["<h1>Hello world!</h1>"]]  }  use Rack::Conte...
module Rack    class ContentType      include Rack::Utils      def initialize(app, content_type = "text/html")        @app...
use   ActionDispatch::Static  use   Rack::Lock  ...  use   Rack::Runtime  use   Rack::MethodOverride  use   ActionDispatch...
Chain of Responsibility
class Relationship  attr_accessor :state  def initialize    @state = :dating  end  def get_married    make_vows    @state ...
class Relationship  attr_accessor :state  def initialize    @state = :dating  end  def get_married    raise "Must date bef...
class Relationship  include AASM  aasm do    state :dating, initial: true    state :married    state :divorced    event :g...
class Relationship  attr_accessor :dating, :married, :divorced  def initialize    @dating, @married, @divorced = true, fal...
State
class SaleOrder  attr_accessor :items, :value, :checkout_date  def initialize(*args)    @items, @value, @checkout_date = a...
require moneyrequire timeclass SaleOrder  attr_reader :value, :checkout_date  def initialize(options = {})    @items, @val...
Primitive Obsession
def slug(title)    # 1    if title.nil?      title.strip.downcase.tr_s(^[a-z0-9], -)    end     # 2     title.strip.downca...
class Object    def try(*a, &b)      if a.empty? && block_given?        yield self      else        public_send(*a, &b)   ...
require active_support/core_ext/object/try  def slug(title)    title.try(:strip).try(:downcase).      try(:tr_s, ^[a-z0-9]...
class NullObject    def method_missing(*args, &block)      self    end    def   to_a;   []; end    def   to_s;   ""; end  ...
def slug(title)    Maybe(title).strip.downcase.tr_s(^[a-z0-9], -)  end  def slug(title)    title = Maybe(title)    if titl...
git clone git://github.com/akitaonrails/null.git  cd null  gem build null.gemspec  gem install null-0.1.gem  > require nul...
Null Object
class SalesOrder < Struct.new(:products, :total, :buyer_name)  def html_receipt    html_items = products.inject("") do |ht...
> order.html_receipt=> "<h1>Thanks for the Purchase John Doe!</h1>    <p>You purchased:</p>    <ul>    <li>Bacon</li><li>C...
class SalesOrder < Struct.new(:products, :total, :buyer_name)  def html_receipt    html_items = products.inject("") do |ht...
class SalesOrder < Struct.new(:products, :total, :buyer_name)endclass SalesOrderDecorator < SimpleDelegator  def html_rece...
> decorated_order.html_receipt=> "<h1>Thanks for the Purchase John Doe!</h1>    <p>You purchased:</p>    <ul>    <li>Bacon...
# originalorder = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )decorated_order = SalesOrderDecorator.new(order)# ...
Decorator/Presenter
Strategy       FactoryAdapter (Proxy, Bridge)Chain of Responsibility        State Primitive Obsession      Null ObjectDeco...
+55 11 3729 14 22                              www.codeminer42.com.br                              contact@codeminer42.com...
MUITO OBRIGADO                              +55 11 3729 14 22                              www.codeminer42.com.br         ...
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
Upcoming SlideShare
Loading in...5
×

TDC 2012 - Patterns e Anti-Patterns em Ruby

2,549

Published on

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.

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,549
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
16
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

TDC 2012 - Patterns e Anti-Patterns em Ruby

  1. 1. “ Genius is the gold in the mine; talent is the miner who works and brings it out. ” Lady Marguerite Blessington Donets Basin Mina de carvão-Ucrânia
  2. 2. @akitaonrails Ruby e Ruby on Rails - 2006Rails Summit Latin America - 2008 RubyConf Brasil - 2010
  3. 3. Patterns e Anti-Patterns em RubyFabio Akita (@akitaonrails)
  4. 4. Pattern PADRÃO
  5. 5. STANDARD Pattern PADRÃO Default
  6. 6. “Pattern”
  7. 7. “Standard”
  8. 8. Christopher Alexander
  9. 9. "...each pattern represents our current bestguess ... to solve the problem presented. ..., thepatterns are still hypotheses, all ... of them -andare therefore all tentative, all free to evolve underthe impact of new experience and observation" —Christopher Alexander, A Pattern Language, p. xv
  10. 10. 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")ERB
  11. 11. <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>ERB
  12. 12. require rdiscount def render_markdown(filepath) content = File.read(filepath) template = RDiscount.new(content) output = template.to_html end puts render_markdown("sample.md")Markdown
  13. 13. # 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>Markdown
  14. 14. def render(filepath) case filepath when /erb$/ then render_erb(filepath) when /md$/ then render_markdown(filepath) when /sass$/ then render_sass(filepath) ... endendputs render("sample.erb")
  15. 15. Strategy
  16. 16. ENGINE FILE EXTENSIONS REQUIRED LIBRARIES -------------------------- ----------------------- ---------------------------- ERB .erb, .rhtml none (included ruby stdlib) Interpolated String .str none (included ruby core) Erubis .erb, .rhtml, .erubis erubis Haml .haml haml Sass .sass haml (< 3.1) or sass (>= 3.1) Scss .scss haml (< 3.1) or sass (>= 3.1) Less CSS .less less Builder .builder builder Liquid .liquid liquid RDiscount .markdown, .mkd, .md rdiscount Redcarpet .markdown, .mkd, .md redcarpet BlueCloth .markdown, .mkd, .md bluecloth Kramdown .markdown, .mkd, .md kramdown Maruku .markdown, .mkd, .md maruku RedCloth .textile redcloth RDoc .rdoc rdoc Radius .radius radius Markaby .mab markaby Nokogiri .nokogiri nokogiri CoffeeScript .coffee coffee-script (+ javascript) Creole (Wiki markup) .wiki, .creole creole WikiCloth (Wiki markup) .wiki, .mediawiki, .mw wikicloth Yajl .yajl yajl-rubyTemplate Engines supported by Tilt
  17. 17. 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 outputERB and Markdown through Tilt
  18. 18. 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 outputSame example as previously
  19. 19. 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 ...tilt.rb
  20. 20. 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 ...Tilt#register
  21. 21. > Tilt["erb"] => Tilt::ErubisTemplate > Tilt["md"] => Tilt::RDiscountTemplate > Tilt["haml"] => Tilt::HamlTemplate > Tilt[coffee] => Tilt::CoffeeScriptTemplateTilt#[]
  22. 22. module Tilt ... # Create a new template for the given # file using the files 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 endTilt overrided #new
  23. 23. Factory
  24. 24. 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 end endTilt::Template Lint
  25. 25. 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 end endTilt::RedClothTemplate Implementation
  26. 26. Adapter
  27. 27. Rails Mongrel Sinatra CGI Thin Ramaze WebrickSimple Web
  28. 28. Rails Mongrel Sinatra CGI Thin Ramaze WebrickSimple Web
  29. 29. Rails Mongrel Sinatra Rack CGI Thin Ramaze WebrickSimple Web
  30. 30. class HelloWorld def call(env) [200, {"Content-Type" => "text/plain"}, ["Hello world!"]] end end hello_world = ->(env) { [200, {"Content-Type" => "text/plain"}, ["Hello world!"]] }Minimal Rack Compliant
  31. 31. hello_world = ->(env) { [200, {"Content-Type" => "text/html"}, ["<h1>Hello world!</h1>"]] } use Rack::ContentType, "text/html" use Rack::ShowExceptions use Rack::Auth::Basic, "Rack Demo" do |username, password| secret == password end # Setup Rack run Rack::URLMap.new( { "/hello" => hello_world, "/" => Rack::File.new( "index.html" ) } )rackup app.ru
  32. 32. 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 end endrackup app.ru
  33. 33. use ActionDispatch::Static use Rack::Lock ... use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use ActionDispatch::Head use Rack::ConditionalGet use Rack::ETag use ActionDispatch::BestStandardsSupport use Warden::Manager use OmniAuth::Strategies::Twitter use OmniAuth::Strategies::Facebook run Rubyconf2012::Application.routesrake middleware (Rails)
  34. 34. Chain of Responsibility
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. 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
  39. 39. State
  40. 40. class SaleOrder attr_accessor :items, :value, :checkout_date def initialize(*args) @items, @value, @checkout_date = args endendsale = SaleOrder.new( [Biscuit, Cheese], 15.0, "2012-07-07")
  41. 41. require moneyrequire timeclass 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 endendsale = SaleOrder.new(items: [Biscuit, Cheese], value: 15.0, checkout_date: "2012-07-07")
  42. 42. Primitive Obsession
  43. 43. 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 nil endhttp:/ /u.akita.ws/avdi-null
  44. 44. class Object def try(*a, &b) if a.empty? && block_given? yield self else public_send(*a, &b) end end end class NilClass def try(*args) nil end endActiveSupport - Try
  45. 45. 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) %>http:/ /u.akita.ws/avdi-null
  46. 46. 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 Maybe(value) value.nil? ? NullObject.new : value def tap; self; end end def to_value; nil; end end class Object def to_value self end endhttp:/ /u.akita.ws/avdi-null
  47. 47. 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], -) endhttp:/ /u.akita.ws/avdi-null
  48. 48. git clone git://github.com/akitaonrails/null.git cd null gem build null.gemspec gem 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 useful endhttp:/ /u.akita.ws/avdi-null
  49. 49. Null Object
  50. 50. 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>} endendorder = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )
  51. 51. > 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>"
  52. 52. 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>} endendorder = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )
  53. 53. class SalesOrder < Struct.new(:products, :total, :buyer_name)endclass 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>} endendorder = SalesOrder.new(["Bacon", "Cheese"], 10.0, "John Doe" )decorated_order = SalesOrderDecorator.new(order)
  54. 54. > 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"]
  55. 55. # 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 ...enddecorated_order = SalesOrderDecorator.new(["Bacon", "Cheese"], 10.0,"John Doe" )decorated_order.html_receipt
  56. 56. Decorator/Presenter
  57. 57. Strategy FactoryAdapter (Proxy, Bridge)Chain of Responsibility State Primitive Obsession Null ObjectDecorator (Presenter)
  58. 58. +55 11 3729 14 22 www.codeminer42.com.br contact@codeminer42.com.brslideshare.net/akitaonrails
  59. 59. MUITO OBRIGADO +55 11 3729 14 22 www.codeminer42.com.br contact@codeminer42.com.brslideshare.net/akitaonrails
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×