Don’t Hang Me Out To DRY

Don’t Hang Me Out To DRY

Close your eyes and imagine the perfect codebase to work on. I bet you’ll say it has complete test coverage. It’s fully-optimized, both in terms of performance and architectural design. And, of course, it contains only DRY code. Surely we can all agree that this is an aspirational situation. But...do we really want that?

Don’t get me wrong; these qualities are all beneficial. However, if we also think we should value everything in moderation, when should we push back on these ideals? What problems can they introduce? Let’s talk about the exceptions to some of the “rules” we all hold dear.

B2f82edebc6e840ed97c8606700d123a?s=128

Kevin Murphy

October 22, 2019
Tweet

Transcript

  1. 15.
  2. 25.

    @kevin_j_m it "shows 4 testimonials if mercury is in retrograde"

    do allow(Mercury).to receive(:in_retrograde?).and_return(true) end
  3. 26.

    @kevin_j_m it "shows 4 testimonials if mercury is in retrograde"

    do allow(Mercury).to receive(:in_retrograde?).and_return(true) display = TestimonialDisplay.new end
  4. 27.

    @kevin_j_m it "shows 4 testimonials if mercury is in retrograde"

    do allow(Mercury).to receive(:in_retrograde?).and_return(true) display = TestimonialDisplay.new expect(display.number_testimonials).to eq 4 end
  5. 41.

    @kevin_j_m Randomized with seed 46917 .F.. Failures: 1) TestimonialDisplay#number_testimonials will

    show 0 or 1 testimonial if no other conditions are met Failure/Error: expect(results.uniq).to match_array [0, 1] expected collection contained: [0, 1] actual collection contained: [1] the missing elements were: [0]
  6. 46.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] end
  7. 47.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new end
  8. 48.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do end end
  9. 49.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 2.times do end end end
  10. 50.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 2.times do results << display.number_testimonials end end end
  11. 51.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 2.times do results << display.number_testimonials end end expect(results.uniq).to match_array [0, 1] end
  12. 52.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 2.times do results << display.number_testimonials end end expect(results.uniq).to match_array [0, 1] end
  13. 53.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 2.times do results << display.number_testimonials end end expect(results.uniq).to match_array [0, 1] end
  14. 54.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) results = [] display = TestimonialDisplay.new travel_to waning_monday do 200.times do results << display.number_testimonials end end expect(results.uniq).to match_array [0, 1] end
  15. 55.

    @kevin_j_m it "will show 0 or 1 testimonial" do waning_monday

    = Date.new(2019, 9, 16) display = TestimonialDisplay.new travel_to waning_monday do expect(results.uniq).to be_in [0, 1] end end
  16. 70.

    @kevin_j_m it "does not provide a company key" do generator

    = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) end
  17. 71.

    @kevin_j_m it "does not provide a company key" do generator

    = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(COMPANY_REGEX)).not_to eq true end
  18. 72.

    @kevin_j_m it "appends the user id if the user's id

    is odd" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :user, user_id: 1) expect(key).to end_with("-1") end it "does not provide a company key" do generator = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(COMPANY_REGEX)).not_to eq true end it "raises an exception if it doesn't understand the accessor type" do generator = AccessKeyGenerator.new expect { generator.access_key(accessor_type: :foo) } .to raise_error UnknownAccessorType end end end
  19. 73.

    @kevin_j_m it "appends the user id if the user's id

    is odd" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :user, user_id: 1) expect(key).to end_with("-1") end it "does not provide a company key" do generator = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(COMPANY_REGEX)).not_to eq true end it "raises an exception if it doesn't understand the accessor type" do generator = AccessKeyGenerator.new expect { generator.access_key(accessor_type: :foo) } .to raise_error UnknownAccessorType end end end
  20. 74.

    @kevin_j_m RSpec.describe AccessKeyGenerator do UUID_REGEX = /[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/ COMPANY_REGEX = /CO-[0-9a-fA-F]{8}/

    describe "#access_key" do it "provides a base-64 encoded key if the key is for an acquired company" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company, acquired_company: true) expect(key).to eq Base64.strict_encode64(Base64.decode64(key)) end it "creates an ivory tower company access key if it's a company not acquired" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company, acquired_company: false) expect(key.match?(COMPANY_REGEX)).to eq true end it "does not provide a full UUID if the key is for a company" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company)
  21. 75.

    @kevin_j_m RSpec.describe AccessKeyGenerator do UUID_REGEX = /[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/ COMPANY_REGEX = /CO-[0-9a-fA-F]{8}/

    describe "#access_key" do it "provides a base-64 encoded key if the key is for an acquired company" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company, acquired_company: true) expect(key).to eq Base64.strict_encode64(Base64.decode64(key)) end it "creates an ivory tower company access key if it's a company not acquired" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company, acquired_company: false) expect(key.match?(COMPANY_REGEX)).to eq true end it "does not provide a full UUID if the key is for a company" do generator = AccessKeyGenerator.new key = generator.access_key(accessor_type: :company)
  22. 78.

    @kevin_j_m it "does not provide a company key" do generator

    = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(COMPANY_REGEX)).not_to eq true end
  23. 79.

    @kevin_j_m it "does not provide a company key" do company_regex

    = /CO-[0-9a-fA-F]{8}/ generator = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(COMPANY_REGEX)).not_to eq true end
  24. 80.

    @kevin_j_m it "does not provide a company key" do company_regex

    = /CO-[0-9a-fA-F]{8}/ generator = AccessKeyGenerator.new key = generator.access_key(accessor: :user, user_id: 0) expect(key.match?(company_regex)).not_to eq true end
  25. 82.

    @kevin_j_m class AccessKeyGenerator def access_key(accessor:, user_id: nil) if accessor ==

    :company "CO-#{SecureRandom.hex(8)}" elsif accessor == :user uuid = SecureRandom.uuid user_id.odd? ? uuid << “-#{user_id}" : uuid end end end
  26. 83.

    @kevin_j_m class AccessKeyGenerator def access_key(accessor:, acquired_co: nil, user_id: nil) if

    accessor == :company "CO-#{SecureRandom.hex(8)}" elsif accessor == :user uuid = SecureRandom.uuid user_id.odd? ? uuid << “-#{user_id}" : uuid end end end
  27. 84.

    @kevin_j_m class AccessKeyGenerator def access_key(accessor:, acquired_co: nil, user_id: nil) if

    accessor == :company if acquired_co SecureRandom.base64 else “CO-#{SecureRandom.hex(8)}” end elsif accessor == :user uuid = SecureRandom.uuid user_id.odd? ? uuid << “-#{user_id}" : uuid end end end
  28. 90.

    @kevin_j_m class AccessKeyGenerator def access_key(accessor:, user_id: nil) if accessor ==

    :company "CO-#{SecureRandom.hex(8)}" elsif accessor == :user uuid = SecureRandom.uuid user_id.odd? ? uuid << “-#{user_id}" : uuid end end end
  29. 94.

    @kevin_j_m class User < ApplicationRecord def generate_access_key SecureRandom.uuid end end

    class Company < ApplicationRecord def generate_access_key SecureRandom.uuid end end
  30. 96.

    @kevin_j_m class User < ApplicationRecord def generate_access_key if id.odd? "#{SecureRandom.uuid}-#{id}"

    else SecureRandom.uuid end end end class Company < ApplicationRecord def generate_access_key if acquisition? SecureRandom.base64 else "CO-#{SecureRandom.hex(8)}" end end end
  31. 106.
  32. 107.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { }) } }
  33. 108.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), }) } }
  34. 109.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), }) } }
  35. 110.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), }) } }
  36. 111.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (2, 4) => "Buzz".to_string(), }) } }
  37. 112.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (2, 4) => "Buzz".to_string(), (_, _) => x.to_string(), }) } }
  38. 113.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (2, 4) => "Buzz".to_string(), (_, _) => x.to_string(), }) } results }
  39. 116.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (2, 4) => "Buzz".to_string(), (_, _) => x.to_string(), }) } results }
  40. 117.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (2, 4) => "Buzz".to_string(), (_, _) => x.to_string(), }) } results }
  41. 118.

    @kevin_j_m def generate(bound: i32) -> Vec<String> { let mut results

    = Vec::new(); for x in 1..(bound+1) { results.push(match(x % 3, x % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), (_, _) => x.to_string(), }) } results }