How to write good code.

How to write good code.

My presentation at VarnaConf.
Code from the presentation: https://github.com/RStankov/varna-conf-2012
Video from the presentation: https://www.youtube.com/watch?v=avhljVTlHZE

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

August 11, 2012
Tweet

Transcript

  1. Radoslav Stankov VarnaConf 11/08/2012 How to write good code

  2. How am I? @rstankov http://rstankov.com http://github.com/rstankov

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. Huey

  10. Huey Dewey

  11. Huey Dewey Louie

  12. None
  13. None
  14. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  15. $('.accordion-menu .item a').each(function() { $(this).bind('click', function() { var title =

    this.className; var theul = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } else { } }); });
  16. $('.accordion-menu .item a').each(function() { $(this).bind('click', function() { var title =

    this.className; var theul = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } else { } }); });
  17. $('.accordion-menu .item a').each(function() { $(this).bind('click', function() { var title =

    this.className; var theul = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } }); });
  18. $('.accordion-menu .item a').each(function() { $(this).bind('click', function() { var title =

    this.className; var theul = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } }); });
  19. $('.accordion-menu .item a').click(function() { var title = this.className; var theul

    = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  20. $('.accordion-menu .item a').click(function() { var title = this.className; var theul

    = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  21. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  22. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  23. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  24. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  25. <ul class="accordion-menu"> <li class="item"> <a href="#" class="section-1">Section 1</a> <ul id="section-1">

    <li>...</li> <li>...</li> <li>...</li> </ul> </li> </ul>
  26. $('.accordion-menu .item a').click(function() { var title = this.className; var theul

    = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  27. $('.accordion-menu .item a').click(function() { var title = this.className; var theul

    = $('#' + title); if (theul.length > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  28. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul'); if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  29. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul'); if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  30. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul'); if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } else { theul.slideUp(); var theli = dropa.parent('li'); theli.animate({'padding-bottom': '20px'}); } } });
  31. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul'); if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown() } else { theul.slideUp() } var theli = dropa.parent('li') theli.animate({'padding-bottom': '20px'}); } });
  32. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul'); if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown() } else { theul.slideUp() } var theli = dropa.parent('li') theli.animate({'padding-bottom': '20px'}); } });
  33. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown() } else { theul.slideUp() } } });
  34. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { if (theul.css('display') == 'none') { theul.slideDown() } else { theul.slideUp() } } });
  35. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { if (theul.is(':visible')) { theul.slideDown() } else { theul.slideUp() } } });
  36. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { if (theul.is(':visible')) { theul.slideDown() } else { theul.slideUp() } } });
  37. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { if (theul.is(':visible')) { theul.slideDown() } else { theul.slideUp() } } });
  38. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { theul.slideToggle(); } });
  39. $('.accordion-menu .item a').click(function() { var theul = $(this).next('ul') if (theul.length

    > 0) { theul.slideToggle(); } });
  40. $('.accordion-menu .item a').click(function() { $(this).next('ul').slideToggle(); });

  41. $('.accordion-menu .item a').click(function() { $(this).next('ul').slideToggle(); });

  42. $('.accordion-menu .item a').click(function() { $(this).next('ul').slideToggle(); });

  43. $('.accordion-menu .item a').live('click', function() { $(this).next('ul').slideToggle(); });

  44. $('.accordion-menu .item a').live('click', function() { $(this).next('ul').slideToggle(); });

  45. $('.accordion-menu .item a').live('click', function() { $(this).next('ul').slideToggle(); });

  46. $('.accordion-menu .item a').click(function() { $(this).next('ul').slideToggle(); });

  47. $(document).on('click', '.accordion-menu .item a', function() { $(this).next('ul').slideToggle(); });

  48. $(document).on('click', '.js-accordion-item a', function() { $(this).next('js-accordion-section').slideToggle(); });

  49. $(document).on('click', '[data-accordion="item"]', function() { $(this).next('[data-accordion="section"]').slideToggle(); });

  50. Know your tools

  51. None
  52. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  53. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  54. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  55. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  56. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  57. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  58. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  59. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  60. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  61. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  62. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page]) Copy & Paste
  63. class MostLikedFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('votes_count') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end MostLikedFriendImages.new(current_user).images(params[:page])
  64. class MostLikedFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('votes_count') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end MostLikedFriendImages.new(current_user).images(params[:page])
  65. class MostLikedFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('votes_count') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end MostLikedFriendImages.new(current_user).images(params[:page])
  66. class MostLikedFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('votes_count') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end MostLikedFriendImages.new(current_user).images(params[:page])
  67. class MostLikedFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('votes_count') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end MostLikedFriendImages.new(current_user).images(params[:page])
  68. DRY don’t repeat yourself

  69. Every piece of knowledge must have a single, unambiguous, authoritative

    representation within a system.
  70. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  71. class NewsestFriendImages def initialize(user) @user = user end def images(page)

    friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end NewsestFriendImages.new(current_user).images(params[:page])
  72. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  73. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  74. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  75. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  76. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  77. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  78. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end FriendImages.new(current_user).images('created_at', params[:page]) FriendImages.new(current_user).images('likes_count', params[:page])
  79. The bad side of DRY

  80. http://bit.ly/O5jSLV

  81. API_GET = '/api/:source_type/:id' API_GET_DETAILS = '/api/:source_type/:id/details' API_GET_VERSION = '/api/:source_type/:id/on/:year/:date/:month' API_GET_SUBTYPE

    = '/api/:source_type/:id/sub/:subtype/:subtype_id'
  82. API = 'api' SOURCE_TYPE = '/:source_type' SOURCE_ID = '/:id' SOURCE_DATE

    = '/:year/:month/:day' SUBTYPE = '/:subtype' SUBID = '/:subtype_id' API_GET = API + SOURCE_TYPE + SOURCE_ID API_GET_DETAILS = API + SOURCE_TYPE + SOURCE_ID + '/details' API_GET_VERSION = API + SOURCE_TYPE + SOURCE_ID + '/on' + SOURCE_DATE API_GET_SUBTYPE = API + SOURCE_TYPE + SOURCE_ID + '/sub' + SUBTYPE + SUBID
  83. def api_path(rest = '') BASE_PATH + rest end def token(string)

    SEPARATOR + string end SEPARATOR = '/' API = 'api' SOURCE_TYPE = ':source_type' SOURCE_ID = ':id' YEAR = ':year' MONTH = ':month' DAY = ':day' SUBTYPE = ':subtype' SUBID = ':subtype_id' DETAILS = 'details' ON = 'on' SUB = 'sub' BASE_PATH = token(API) + token(SOURCE_TYPE) + token(SOURCE_ID) DATE_PART = token(YEAR) + token(MONTH) + token(DAY) SUB_PART = token(SUBTYPE) + token(SUBID) API_GET = api_path() API_GET_DETAILS = api_path(token(DETAILS)) API_GET_VERSION = api_path(token(ON) + token(DATE_PART)) API_GET_SUBTYPE = api_path(token(SUB) + token(SUB_PART))
  84. None
  85. None
  86. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end
  87. SLAP Single Level of Abstraction Principle

  88. Keep all lines of code in a method at the

    same level of abstraction.
  89. class FriendImages def initialize(user) @user = user end def images(sorting,

    page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end end
  90. def images(sorting, page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id:

    friends, visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order(sorting) images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end
  91. def images(sorting, page) images = find_images_of(friends) images = apply_age_restritions_to(images) images

    = order_by(images, sorting) images = paginate(images, page) decorate(images) end
  92. def friends Friendship.where(user_id: @user.id).pluck('friend_id') end def find_images_of(friends) Image.where(user_id: friends, visible_to_friends:

    true) end def apply_age_restritions_to(images) images.where('minimum_age >= ?', @user.age) end def order_by(sorting) images.order(sorting) end def paginate(page) images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) end def decorate(images) images.map { |image| ImageDecorator.new(image) } end
  93. Law of Demeter

  94. • You can play with yourself. • 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.
  95. • Your method can call other methods in its class

    directly • Your method can call methods on its own fields directly (but not on the fields' fields) • When your method takes parameters, your method can call methods on those parameters directly. • When your method creates local objects, that method can call methods on the local objects.
  96. def friends Friendship.where(user_id: @user.id).pluck('friend_id') end def find_images_of(friends) Image.where(user_id: friends, visible_to_friends:

    true) end def apply_age_restritions_to(images) images.where('minimum_age >= ?', @user.age) end def order_by(sorting) images.order(sorting) end def paginate(page) images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) end def decorate(images) images.map { |image| ImageDecorator.new(image) } end
  97. def friends Friendship.friend_ids_of(@user) end def find_images_of(friends) Image.of(friends) end def apply_age_restritions_to(images)

    images.appropriate_for(@user.age) end def order_by(sorting) images.order(sorting) end def paginate(page) images.paginate(page) end def decorate(images) images.map { |image| ImageDecorator.new(image) } end
  98. def images(sorting, page) images = find_images_of(friends) images = apply_age_restritions_to(images) images

    = order_by(images, sorting) images = paginate(images, page) decorate(images) end
  99. def images(sorting, page) decorate Image .of(friends) .appropriate_for(@user.age) .order(sorting) .paginate(page) end

  100. def images(page) friends = Friendship.where(user_id: @user.id).pluck('friend_id') images = Image.where(user_id: friends,

    visible_to_friends: true) images = images.where('minimum_age >= ?', @user.age) images = images.order('created_at') images = images.limit(PER_PAGE).offset(page.to_i.abs * PER_PAGE) images.map { |image| ImageDecorator.new(image) } end
  101. def images(sorting, page) decorate Image .of(friends) .appropriate_for(@user.age) .order(sorting) .paginate(page) end

  102. Code smells

  103. • Duplicated Code • Long Method • Large Class •

    Long Parameter List • Shotgun Surgery • Feature Envy • Data Clumps • Case Statements • Parallel Inheritance Hierarchies • Speculative Generality • Temporary Field • Message Chains • Middle Man • Alternative Classes with Different Interfaces • Refused Bequest • Comments • Repetitive Boilerplate • и т.н....
  104. Refactoring: Improving the Design of Existing Code. Martin Fowler

  105. Out side of the box

  106. Retrospection

  107. Retrospection ✓ DRY

  108. Retrospection ✓ DRY ✓ SLAP

  109. Retrospection ✓ DRY ✓ SLAP ✓ Law of demeter

  110. Retrospection ✓ DRY ✓ SLAP ✓ Law of demeter ✓

    Tools
  111. Retrospection ✓ DRY ✓ SLAP ✓ Law of demeter ✓

    Tools ✓ Code smells
  112. Retrospection ✓ DRY ✓ SLAP ✓ Law of demeter ✓

    Tools ✓ Code smells ✓ Out side the box
  113. None
  114. Code, step by step https://github.com/RStankov/varna-conf-2012

  115. None
  116. @rstankov Thanks for listening :)

  117. Questions?