Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby & Rails Antipatterns

mrjaba
January 11, 2012

Ruby & Rails Antipatterns

A collection of Ruby/Rails antipatterns presented at @iprug in January 2012

mrjaba

January 11, 2012
Tweet

Other Decks in Programming

Transcript

  1. RUBY & RAILS ANTIPATTERNS Tom Crinson - @mrjaba - IPRUG

    10th Jan 2012 Wednesday, 11 January 12
  2. “In software engineering, an anti-pattern (or antipattern) is a pattern

    that may be commonly used but is ineffective and/or counterproductive in practice” (Wikipedia) God Object Sequential Coupling Object Orgy Wednesday, 11 January 12
  3. THESE ARE NOT LAWS nor are they definitive, most are

    simply opinion - have your pinches of salt* at the ready *flaming pitchforks must wait until the end Wednesday, 11 January 12
  4. UNLESS WITH ELSE def horrible_unless_else unless i_am_a_robot? go_eat_food else go_bend_things

    end end def normal_if_else if i_am_a_robot? go_bend_things else go_eat_food end end Wednesday, 11 January 12
  5. RESCUE NIL def horrible_rescue_nil tweets = find_all_my_tweets rescue nil tweets.detect{|tweet|

    tweet.from ~= /"@dhh"/} if tweets end Wednesday, 11 January 12
  6. RESCUE NIL def handle_exceptions_properly tweets = find_all_my_tweets find_tweets_from_ddh(tweets) rescue Timeout::Error

    => e handle_timeout rescue Exception => e logger.error(e) raise end Wednesday, 11 January 12
  7. CATCHALL EXCEPTIONS def handle_exceptions_properly tweets = find_all_my_tweets find_tweets_from_ddh(tweets) rescue Exception

    => e logger.error(e) raise end def handle_exceptions_properly tweets = find_all_my_tweets find_tweets_from_ddh(tweets) rescue Timeout::Error => e retry rescue Net::HTTPServerError => e wait and retry rescue Exception => e logger.error(e) raise end Wednesday, 11 January 12
  8. IF TRUE ELSE FALSE def horrible_if_else if self.has_cake? && self.eats_it?

    return true else return false end end Wednesday, 11 January 12
  9. IF TRUE ELSE FALSE irb(main):001:0> has_cake = "yes" => "yes"

    irb(main):002:0> eats_it = "hell yes" => "hell yes" irb(main):003:0> has_cake && eats_it => "hell yes" irb(main):004:0> !!(has_cake && eats_it) => true but i need a boolean! Wednesday, 11 January 12
  10. OVERRIDING METHOD_MISSING but not respond_to? class Cake def eat puts

    "mm eating cake" end def method_missing(method, *args, &block) if method.to_s =~ /^cake/ puts "mmm cake" else super end end end puts Cake.new.respond_to? :eat # true puts Cake.new.respond_to? :cake # false Wednesday, 11 January 12
  11. OVERRIDING METHOD_MISSING but not respond_to? class Cake def respond_to?(method) return

    true if method =~ /^cake/ super end def method_missing(method, *args, &block) if method.to_s =~ /^cake/ puts "mmm cake" else super end end end puts Cake.new.respond_to? :cake # false Wednesday, 11 January 12
  12. NOT KNOWING YOUR TOOLS Aka: Use your language features def

    find_all_the_cakes(bakery_produce) cakes = [] bakery_produce.each do |potential_cake| cakes << potential_cake if is_cake?(potential_cake) end cakes end def find_a_cake(bakery_produce) bakery_produce.each do |potential_cake| return potential_cake if is_cake?(potential_cake) end end Wednesday, 11 January 12
  13. NOT KNOWING YOUR TOOLS Aka: Use your language features def

    find_all_the_cakes(bakery_produce) bakery_produce.select{|potential_cake| is_cake?(potential_cake)} end array[0] vs array.first def find_a_cake(bakery_produce) bakery_produce.detect{|potential_cake| is_cake?(potential_cake)} end Wednesday, 11 January 12
  14. VIOLATING DEMETER if cake.ingredients_include?(:vanilla_essence) self.hungry! end Aka: Train Wreck code

    • You can play on your own. • You can play with your own toys (but you can’t take them apart). • You can play with toys that were given to you. • And you can play with toys you’ve made yourself. Wednesday, 11 January 12
  15. LONG METHODS def bake_a_cake mixing_bowl = [] mixing_bowl << :eggs

    mixing_bowl << :flour mixing_bowl << :milk 100.times do mixing_bowl.shuffle end mixing_bowl = mixing_bowl.join("") cake_tin = [] cake_tin << :grease_proof_paper cake_tin << :grease cake_tin << mixing_bowl.drop(mixing_bowl.length) oven = Oven.new oven.open oven.temperature = 180.degrees oven.insert cake_tin sleep(90.minutes) ... end Wednesday, 11 January 12
  16. LONG METHODS def bake_a_cake mix_indgredients prepare_cake_tin prepare_oven put_cake_in_oven eat_cake end

    def mix_ingredients @mixing_bowl = MixingBowl.new(:eggs,:flour,:milk) @mixing_bowl.mix end def prepare_cake_tin @cake_tin = CakeTin.new @cake_tin.line! @cake_tin.grease! end Wednesday, 11 January 12
  17. LOGIC IN VIEWS <%= if @cake && @cake.is_a?( VictoriaSponge )

    %> <h4> Mmm Spongey </h4> <% else %> <h4> Hmm Not so Spongey </h4> <% end %> Wednesday, 11 January 12
  18. LOGIC IN VIEWS <%= spongeyness_level %> def spongeyness(cake) if cake

    && cake.spongeyness_level > 4 content_tag(:h4, "Mmm Spongey") else content_tag(:h4, "Hmm not so Spongey") end end cake.html.erb cake_helper.rb Bonus Points: Presenters (e.g. draper) Wednesday, 11 January 12
  19. “HELPER” SIDE EFFECTS def cake_deliciousness(cake) if cake && cake.eat =~

    /omnomnom/ content_tag(:h4, "Mmm Delicious") else content_tag(:h4, "Average") end end def cake_servings(cake) if cake.slices? content_tag(:h4, cake.slices ) else content_tag(:h4, "The cake has been eated!" ) end end <%= cake_deliciousness(@cake) %> => <h4> Mmm Delicious </h4> <%= cake_servings(@cake) %> => <h4> The cake has been eated! </h4> cake.html.erb cake_helper.rb a cake can only be tested by eating it! Wednesday, 11 January 12
  20. COMPLEX CONTROLLERS class SearchController < ApplicationController def search if params[:advanced]

    context = params[:advanced][:context] search_term = params[:advanced][:term] results = AdvancedSearchService.search(context, search_term) @normalized_results = normalize_results(results) respond_with(@normalized_results) else search_term = params[:simple][:term] @results = SimpleSearchService.search(search_term) respond_with(@results) end end end Wednesday, 11 January 12
  21. COMPLEX CONTROLLERS class SimpleSearchController < ApplicationController def create @search =

    Search.new(params) respond_with @search end end Keep it simple Break out complex logic into models One controller per resource Wednesday, 11 January 12
  22. FAAAT MODELS class Cake < ActiveRecord::Base #Eating Methods def eat_with_fork(person)

    person.munch(self) until self.empty? end #Distribution methods def slices(degrees_per_slice) 360/degrees_per_slice end #Representation Methods def to_xml ... end def to_html ... end def as_json ... end end Wednesday, 11 January 12
  23. FAAAT MODELS class CakePresenter def initialize(cake) @cake = cake end

    def to_xml ... end def to_html ... end def as_json ... end end def CakeSlicer def initialize(cake) @cake = cake end def slices(degrees_per_slice) 360/degrees_per_slice end end Wednesday, 11 January 12
  24. MANY MANY MORE Referring to ActiveRecord classes in migrations [1]

    Data Migrations [2] Fixtures - especial with associations [3] Script like Rake task [4] No Database indexes [5] Wednesday, 11 January 12
  25. RESOURCES Inspired by: ruby rogues: http://rubyrogues.com/ (episode 32) dan manges:

    http://www.dan-manges.com/blog/28 Further Reading: [1,2] http://complicated-simplicity.com/2010/05/using-models-in-rails-migrations/ [3] http://jakescruggs.blogspot.com/2007/06/why-i-hate-using-rails-fixtures-in.html [4] http://blog.jayfields.com/2006/11/ruby-testing-rake-tasks.html [5] http://rails-bestpractices.com/posts/21-always-add-db-index Image: www.flickr.com/photos/fairtradefairy/3708326594/ Wednesday, 11 January 12