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

RDRC 2014: Safety Nets: Learn to code with conf...

RDRC 2014: Safety Nets: Learn to code with confidence

Ruby gives you a great power, such as easy Duck Typing. As the saying goes, "With great power there must also comes great responsibility!" It comes at a price. We cannot afford to blow off everything when shipping. That's why it's important to put in place different strategies to help us to catch errors asap, but also to avoid the cruft long term. Like a safety net, they allow you to go forward with more confidence.

presented at RedDotRubyConference 2014 http://www.reddotrubyconf.com/

Code available at https://github.com/8thcolor/rdrc2014-safetynets
Video available at http://www.confreaks.com/videos/4106-rdrc2014-safety-nets-learn-to-code-with-confidence

Christophe Philemotte

June 27, 2014
Tweet

More Decks by Christophe Philemotte

Other Decks in Programming

Transcript

  1. BUT

  2. describe Invoice do let(:item1) { Item.new(20.0, 3) } let(:item2) {

    Item.new(15.0, 2) } let(:empty_invoice) { Invoice.new } let(:invoice) do tmp = Invoice.new tmp.add_item(item1) tmp.add_item(item2) tmp end
  3. it 'returns a zero total when empty' do assert_equal 0,

    empty_invoice.total end it 'returns a correct total' do assert_equal 90.0, invoice.total end
  4. it 'returns a correct total even after updating a previously

    added item' do assert_equal 90.0, invoice.total item2.quantity = 3 assert_equal 105, invoice.total end end
  5. #... def total total = 0 @items.each do |item| total

    += item.price * item.quantity end total end end
  6. class Invoice def initialize @items = [] @total = 0

    end def add_item(item) @items << item end #...
  7. #... def total @total = 0 @items.each do |item| @total

    += item.price * item.quantity end @total end end
  8. class Invoice #... def total @total = 0 @items.each do

    |item| @total += item.price * item.quantity end
  9. if @country_code == 'BE' vat = 0.21 * @total elsif

    ['IT','FR','NL','LU','DE'].include?(@country_code) if valid_vat_number? vat = 0 else vat = 0.21 * @total end else vat = 0 end
  10. class Quote #... def total if @country_code == 'BE' vat

    = 0.21 * @total elsif ['IT','FR','NL','LU','DE'].include?(@country_code) if valid_vat_number? vat = 0 else vat = 0.21 * @total end
  11. $ flay lib Total score (lower is better) = 116

    1) IDENTICAL code found in :if (mass*2 = 116) lib/invoice.rb:21 lib/quote.rb:10
  12. class Invoice #... def total @total = 0 @items.each do

    |item| @total += item.price * item.quantity end
  13. $ ruby -w lib/invoice.rb lib/invoice.rb:41: warning: shadowing outer local variable

    - total lib/invoice.rb:40: warning: assigned but unused variable - total
  14. $ rubocop lib/invoice.rb Inspecting 1 file W Offenses: ------8<------ lib/invoice.rb:40:5:

    W: Useless assignment to variable - total. total = 0 ^^^^^
  15. lib/invoice.rb:41:26: W: Shadowing outer local variable - total. @items.reduce(0) do

    |total, item| ^^^^^ lib/invoice.rb:42:7: W: Useless assignment to variable - total. Use just operator +. total += item.price * item.quantity ^^^^^ 1 file inspected, 10 offenses detected
  16. It is common to take a sort of smug satisfaction

    in reports of colossal failures of automatic systems, but for every failure of automation, the failures of humans are legion. Exhortations to “write better code” plans for more code reviews, pair programming, and so on just don’t cut it, especially in an environment with dozens of programmers under a lot of time pressure. The value in catching even the small subset of errors that are tractable to static analysis every single time is huge. John Carmack
  17. module Vat def self.rate(country_code = '', vat_number = '') if

    country_code == 'BE' return 0.21 elsif %w(IT FR NL LU DE).include?(country_code) if is_valid?(vat_number) return 0 else return 0.21 end
  18. class Invoice # ... def total @total = calculate_subtotal vat

    = Vat::rate(@country_code, @vat_number) * @total @total += vat end # ... end
  19. class Country # ... def belgium? @code == 'BE' end

    def europe? %w(IT FR NL LU DE).include? @code end end
  20. module Vat def self.rate(country, vat_number = '') if country.belgium? return

    0.21 elsif country.europe? if is_valid?(vat_number) return 0 else return 0.21 end # ... end
  21. 34.7: flog total 2.9: flog/method average 7.0: Invoice#calculate_subtotal lib/invoice.rb:28 6.7:

    Invoice#initialize lib/invoice.rb:5 5.7: Invoice#total lib/invoice.rb:16 4.8: Vat::rate lib/vat.rb:2