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

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

Radoslav Stankov

August 11, 2012
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. Huey

    View Slide

  10. Huey Dewey

    View Slide

  11. Huey Dewey Louie

    View Slide

  12. View Slide

  13. View Slide



  14. Section 1

    ...
    ...
    ...



    View Slide

  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 {
    }
    });
    });

    View Slide

  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 {
    }
    });
    });

    View Slide

  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'});
    }
    }
    });
    });

    View Slide

  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'});
    }
    }
    });
    });

    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    }
    });

    View Slide



  21. Section 1

    ...
    ...
    ...



    View Slide



  22. Section 1

    ...
    ...
    ...



    View Slide



  23. Section 1

    ...
    ...
    ...



    View Slide



  24. Section 1

    ...
    ...
    ...



    View Slide



  25. Section 1

    ...
    ...
    ...



    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    }
    });

    View Slide

  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'});
    }
    });

    View Slide

  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'});
    }
    });

    View Slide

  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()
    }
    }
    });

    View Slide

  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()
    }
    }
    });

    View Slide

  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()
    }
    }
    });

    View Slide

  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()
    }
    }
    });

    View Slide

  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()
    }
    }
    });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  50. Know your tools

    View Slide

  51. View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  68. DRY
    don’t repeat yourself

    View Slide

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

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  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])

    View Slide

  79. The bad side of DRY

    View Slide

  80. http://bit.ly/O5jSLV

    View Slide

  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'

    View Slide

  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

    View Slide

  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))

    View Slide

  84. View Slide

  85. View Slide

  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

    View Slide

  87. SLAP
    Single Level of Abstraction Principle

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  93. Law of Demeter

    View Slide

  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.

    View Slide

  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.

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  102. Code smells

    View Slide

  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
    • и т.н....

    View Slide

  104. Refactoring:
    Improving the Design of Existing Code.
    Martin Fowler

    View Slide

  105. Out side of the box

    View Slide

  106. Retrospection

    View Slide

  107. Retrospection
    ✓ DRY

    View Slide

  108. Retrospection
    ✓ DRY
    ✓ SLAP

    View Slide

  109. Retrospection
    ✓ DRY
    ✓ SLAP
    ✓ Law of demeter

    View Slide

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

    View Slide

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

    View Slide

  112. Retrospection
    ✓ DRY
    ✓ SLAP
    ✓ Law of demeter
    ✓ Tools
    ✓ Code smells
    ✓ Out side the box

    View Slide

  113. View Slide

  114. Code, step by step
    https://github.com/RStankov/varna-conf-2012

    View Slide

  115. View Slide

  116. @rstankov
    Thanks for listening :)

    View Slide

  117. Questions?

    View Slide