$30 off During Our Annual Pro Sale. View Details »

The Art Of Self-Defense - How To Protect Yourself From Nils

The Art Of Self-Defense - How To Protect Yourself From Nils

Presentation given on Łódź Ruby User Group.

karol.galanciak

January 13, 2016
Tweet

More Decks by karol.galanciak

Other Decks in Programming

Transcript

  1. The art of self-defense - how
    to protect yourself from nils
    Karol Galanciak - full-stack developer at
    Twitter: @Azdaroth

    View Slide

  2. NoMethodError: undefined
    method 'name' for nil:NilClass

    View Slide

  3. 1. Use right set of methods

    View Slide

  4. 1. Use right set of methods
    1. Prefer Hash#fetch over Hash#[]
    name = params[:name]
    name = params.fetch(:name)

    View Slide

  5. 1. Use right set of methods
    2. Use bang finder methods for required objects
    user = User.find_by(name: name)
    user = User.find_by!(name: name)

    View Slide

  6. 1. Use right set of methods
    3. Use Hash#dig for hashes from input you don’t own
    payload = {
    data: {
    type: "users",
    attributes: {
    email: "[email protected]"
    },
    relationships: {
    source: {
    data: {
    type: "sources",
    id: 20
    }
    }
    }
    }
    }
    source_id = payload[:data][:relationships][:source][:data][:id]
    source_id = payload.dig(:data, :relationships, :source, :data, :id)

    View Slide

  7. 2. Null Object Pattern

    View Slide

  8. 2. Null Object Pattern
    # controller
    def current_user
    @current_user ||= User.find_by(id: user_id) || GuestUser.new
    end
    # model
    class GuestUser
    def name
    "Anonymous user"
    end
    end
    # view
    <%= current_user.name %>

    View Slide

  9. 2. Null Object Pattern
    class GuestUser
    def name
    "Anonymous user"
    end
    def comments
    Comment.none
    end
    end

    View Slide

  10. 2. Null Object Pattern
    class Comment::Persistence
    attr_reader :logger
    private :logger
    def initialize(logger: nil)
    @logger = logger
    end
    def save!(comment)
    comment.save!
    logger.log("comment: #{comment.attributes} created!") if logger
    end
    end

    View Slide

  11. 2. Null Object Pattern
    class Comment::Persistence
    attr_reader :logger
    private :logger
    def initialize(logger: NullLoger)
    @logger = logger
    end
    def save!(comment)
    comment.save!
    logger.log("comment: #{comment.attributes} created!")
    end
    module NullLogger
    def self.log(*)
    end
    end
    end

    View Slide

  12. 3. Do Not Make Demeter Sad

    View Slide

  13. 3. Do Not Make Demeter Sad
    comment.author.name

    View Slide

  14. 3. Do Not Make Demeter Sad
    class Comment < ActiveRecord::Base
    belongs_to :author
    delegate :name, to: :author, prefix: true, allow_nil: true
    end
    class Comment < ActiveRecord::Base
    belongs_to :author
    def author_name
    author && author.name || "Anonymous"
    end
    end
    comment.author_name

    View Slide

  15. 4. Apply Explicit Conversions
    class Tax < ActiveRecord::Base
    # percentage
    def calculate(value)
    value * percentage
    end
    end

    View Slide

  16. 4. Apply Explicit Conversions
    class Tax < ActiveRecord::Base
    # percentage
    def calculate(value)
    value.to_d * percentage
    end
    end

    View Slide

  17. 4. Apply Explicit Conversions
    class Tax < ActiveRecord::Base
    # percentage
    def calculate(value)
    value.to_s.to_d * percentage
    end
    end

    View Slide

  18. 5. Use proper database constraints
    class CreateComments < ActiveRecord::Migration
    def change
    create_table :comments do |t|
    t.integer :author_id, null: false
    t.text :content, null: false
    t.timestamps
    end
    add_foreign_key :comments, :authors
    end
    end

    View Slide

  19. Bonus: Never, ever, use Object#try
    and safe navigation operator &.

    View Slide

  20. Bonus: Never, ever, use Object#try
    and safe navigation operator &.
    comment.try(:author).try(:address).try(:primary)
    .try(:city).try(:region).try(:country)
    comment&.author&.address&.primary&.city&.region
    &.country
    http://www.reactionface.info/face/troll-face

    View Slide

  21. http://www.jasondemakis.com/wp-content/uploads/2015/09/yoda-there-is-no-try1.png

    View Slide