diff --git a/Nothing-Is-Easy/images/6736130025_ac5e197e22_b.jpg b/Nothing-Is-Easy/images/6736130025_ac5e197e22_b.jpg new file mode 100644 index 0000000..96bae57 Binary files /dev/null and b/Nothing-Is-Easy/images/6736130025_ac5e197e22_b.jpg differ diff --git a/Nothing-Is-Easy/images/Booleans-talk.png b/Nothing-Is-Easy/images/Booleans-talk.png new file mode 100644 index 0000000..289acd5 Binary files /dev/null and b/Nothing-Is-Easy/images/Booleans-talk.png differ diff --git a/Nothing-Is-Easy/images/Bumblebee_Transformer_-_Flickr_-_andrewbasterfield.jpg b/Nothing-Is-Easy/images/Bumblebee_Transformer_-_Flickr_-_andrewbasterfield.jpg new file mode 100644 index 0000000..dd86087 Binary files /dev/null and b/Nothing-Is-Easy/images/Bumblebee_Transformer_-_Flickr_-_andrewbasterfield.jpg differ diff --git a/Nothing-Is-Easy/images/OCR-A_char_Exclamation_Mark.svg b/Nothing-Is-Easy/images/OCR-A_char_Exclamation_Mark.svg new file mode 100644 index 0000000..75fef6d --- /dev/null +++ b/Nothing-Is-Easy/images/OCR-A_char_Exclamation_Mark.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/Nothing-Is-Easy/images/Obsession.png b/Nothing-Is-Easy/images/Obsession.png new file mode 100644 index 0000000..af41712 Binary files /dev/null and b/Nothing-Is-Easy/images/Obsession.png differ diff --git a/Nothing-Is-Easy/images/Thank-you-word-cloud.jpg b/Nothing-Is-Easy/images/Thank-you-word-cloud.jpg new file mode 100644 index 0000000..e06b9f9 Binary files /dev/null and b/Nothing-Is-Easy/images/Thank-you-word-cloud.jpg differ diff --git a/Nothing-Is-Easy/images/background.webp b/Nothing-Is-Easy/images/background.webp new file mode 100644 index 0000000..176f22f Binary files /dev/null and b/Nothing-Is-Easy/images/background.webp differ diff --git a/Nothing-Is-Easy/images/background2.webp b/Nothing-Is-Easy/images/background2.webp new file mode 100644 index 0000000..f0af6fa Binary files /dev/null and b/Nothing-Is-Easy/images/background2.webp differ diff --git a/Nothing-Is-Easy/images/blue-ridge-ruby-logo.svg b/Nothing-Is-Easy/images/blue-ridge-ruby-logo.svg new file mode 100644 index 0000000..0a693b8 --- /dev/null +++ b/Nothing-Is-Easy/images/blue-ridge-ruby-logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Nothing-Is-Easy/images/blue-ridge-ruby.svg b/Nothing-Is-Easy/images/blue-ridge-ruby.svg new file mode 100644 index 0000000..4a53578 --- /dev/null +++ b/Nothing-Is-Easy/images/blue-ridge-ruby.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Nothing-Is-Easy/images/books_bookshelf_computer_science_programming_computer_language_specialist_literature-1066570.jpg b/Nothing-Is-Easy/images/books_bookshelf_computer_science_programming_computer_language_specialist_literature-1066570.jpg new file mode 100644 index 0000000..9a97871 Binary files /dev/null and b/Nothing-Is-Easy/images/books_bookshelf_computer_science_programming_computer_language_specialist_literature-1066570.jpg differ diff --git a/Nothing-Is-Easy/images/fairbank-title-slide.jpg b/Nothing-Is-Easy/images/fairbank-title-slide.jpg new file mode 100644 index 0000000..d2d8b55 Binary files /dev/null and b/Nothing-Is-Easy/images/fairbank-title-slide.jpg differ diff --git a/Nothing-Is-Easy/images/pexels-photo-259943.jpeg b/Nothing-Is-Easy/images/pexels-photo-259943.jpeg new file mode 100644 index 0000000..8682a9a Binary files /dev/null and b/Nothing-Is-Easy/images/pexels-photo-259943.jpeg differ diff --git a/Nothing-Is-Easy/slides.html b/Nothing-Is-Easy/slides.html index e8d820e..3e13eb2 100644 --- a/Nothing-Is-Easy/slides.html +++ b/Nothing-Is-Easy/slides.html @@ -43,21 +43,23 @@ } .remark-slides-area { - background-color: #C6291C; /* Color of pillarbox on sides of slides */ + background-color: #2672B5; /* Color of pillarbox on sides of slides */ } .remark-slide-scaler { background-color: white; } .remark-slide-content { - background-color: #F4F4F4; + background-color: initial; font-size: 26px; line-height: 1.6; width: 100%; } + /* Code */ .remark-code, .remark-inline-code { font-family: monospace; line-height: 1.5; font-size: 16px; } .remark-inline-code { font-size: 24px; } - .remark-code { + pre code.remark-code { counter-reset: code-line-numbers; + background-color: rgba(240, 240, 240, 0.5) !important; } .remark-code .remark-code-line { counter-increment: code-line-numbers; @@ -71,27 +73,27 @@ -webkit-user-select: none; /* The line numbers should not be selectable. */ } - /* Title page */ - .remark-slide .title h1 { - font-size: 70px; - margin-top: 0px; - } - .remark-slide .title li { - list-style-type: none; - font-size: 28px; + .remark-slide .remark-slide-content { + background-image: url(images/background2.webp); } - .remark-slide .title ul { - padding-left: 0px; + + /* Title page */ + .remark-slide .remark-slide-content.title { + background-image: url(images/blue-ridge-ruby.svg), url(images/background2.webp); + background-position: 10% 80px, center; + background-size: 100%, auto; + background-origin: content-box; + } - .remark-slide .title { - background-image: url(images/f5-logo-gradient-rgb.png); - background-position: 150px 400px; + .remark-slide .remark-slide-content.title h1 { + font-size: 72px; + margin-top: 160px; + color: #0C2866; } - .remark-slide .title h3 { - font-size: 40px; - margin-left: 200px; - margin-top: 140px; + .remark-slide .remark-slide-content.title h2 { + color: #0C2866; } + .remark-slide .diagram p { text-align: center; } @@ -169,31 +171,13 @@ background-color: rgba(255, 255, 255, .75); } - .remark-slide .transition.boolean_basics { + .remark-slide .transition.basics { background-image: url(images/pexels-photo-259943.jpeg); } - .remark-slide .transition.boolean_parameters { - background-image: url(images/Param_03.jpg); - } - .remark-slide .transition.connascence { - background-image: url(images/TwinsTwins.JPG); - } - .remark-slide .transition.boolean_states { - background-image: url(images/Balkans_regions_map.png); - } .remark-slide .transition.primitive_obsession { background-image: url(images/Obsession.png); } - .remark-slide .transition.boolean_fields { - background-image: url(images/cornfield_sky_trees_cereals_summer_nature_field_clouds-450526.jpg); - } - .remark-slide .transition.exponential_complexity { - background-image: url(images/Exponential.svg); - } - .remark-slide .transition.boolean_operations { - background-image: url(images/operation.jpg); - } - .remark-slide .transition.boolean_transformations { + .remark-slide .transition.transformations { background-image: url(images/Bumblebee_Transformer_-_Flickr_-_andrewbasterfield.jpg); } .remark-slide .transition.readability { @@ -275,6 +259,18 @@ ## A Talk About Nothing (Nil) +??? + +* This is a talk I'll be giving next Thursday at Blue Ridge Ruby +* My Twitter is in the upper right corner if you want to tweet at/about me. + * I'm also on Mastodon, same username, @ruby.social + * I'm rarely on Twitter any more, nor Mastodon +* AUDIENCE QUESTION: Who knows the costliest mistake in computer programming? + * ANSWER: Null pointers, according to Tony Hoare (who invented them) + * It has probably cost businesses several billion dollars over the years +* AUDIENCE QUESTION: Who has seen a talk on Nil by Sandi Metz or Avdi Grimm? + * I've got some big boots to fill! + --- class: middle @@ -291,14 +287,41 @@ * Hit `P` for presenter notes. * Notes have links to things I reference. * Notes have more info than I'll talk about in some cases. -* My Twitter is in the upper right corner if you want to tweet at/about me. - * I'm also on Mastodon, same username, @ruby.social -* AUDIENCE QUESTION: Who knows the costliest mistake in computer programming? - * ANSWER: Null pointers, according to Tony Hoare (who invented them) - * It has probably cost businesses several billion dollars over the years - * Second most costly mistake is automatic coercion to Booleans -* AUDIENCE QUESTION: Who has seen a talk on Nil by Sandi Metz or Avdi Grimm? - * I've got some big boots to fill! + +--- +class: middle, center, image-only + +![Title slide for Jeremy Fairbank's Elm-Conf 2017 talk](images/fairbank-title-slide.jpg) + +??? + +* One of my favorite conference talks was by Jeremy Fairbank. + * He talked about Booleans + * I wondered how he could talk about true and false for 40 minutes + * It turned out to be a great talk + * His talk was about Elm + +------ + +* I highly recommend watching Jeremy's talk. + * video: [Solving the Boolean Identity Crisis by Jeremy Fairbank](https://www.youtube.com/watch?v=8Af1bh-BVY8) + * slides: [Slides for "Solving the Boolean Identity Crisis"](https://bit.ly/elm-bool) + +--- +class: middle, center, image-only + +![Title slide for Craig Buchek's RailsConf 2018 talk about Booleans in Ruby](images/Booleans-talk.jpg) + + +??? + +* That talk inspired me to give a talk on Booleans +* And **that** talk inspired me to write this talk + +------ + +* In my own opinion, this is the best conference talk I've given +* http://craigbuchek.com/booleans --- class: agenda @@ -310,8 +333,8 @@ * NoMethodError * Ruby's take on NullPointerException * The Billion Dollar Mistake -* Safe Navigation * Other Anti-Patterns +* Safe Navigation * Null Object and Special Case * Refactoring * Type Safety @@ -382,7 +405,7 @@ * true: 2 * nil: 4 ------- +-- * From the `object_id` documentation: "no two objects will share an id". @@ -536,7 +559,7 @@ * It's more *idiomatic* to use something more explicitly intention-revealing that returns a Boolean. * Like `nil?`, `empty?`, or `blank?` ------- +-- * This isn't as big a problem in Ruby as in other languages. * I contend that automatic coercion to Booleans is the 2nd most expensive mistake in computer language design. @@ -573,6 +596,18 @@ # NoMethodError +* Raised when a method is called on an object that does not support it +* Nil has only a few methods + +~~~ ruby +nil.to_s +# => "" + +nil. +~~~ + +??? + --- # Safe Navigation @@ -691,228 +726,44 @@ * TODO: More on the Law of Demeter --- -class: transition, parameters +class: transition, root_causes -# Parameters - -??? - -* Next we'll talk about `nil` used in parameters to methods. +# Root Causes --- -# Parameters - -~~~ ruby -random_object.class -# => User -~~~ - -??? - -* I spend a lot of time in Rails console, IRB, or Pry. -* I'll often want to see the class of an object. - ------- - -* I actually wrote a method to get a random object: - -~~~ ruby -def random_object(cache: true) - return @random_object if cache && @random_object_defined - @random_object_defined = true - random_object_index = Random.rand(ObjectSpace.each_object.count) - @random_object = ObjectSpace.each_object.drop(random_object_index).take(1) -end -~~~ - ---- - -# Parameters - -~~~ ruby -random_object.methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] - -random_object.class.instance_methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* Often, I'll also want to see what methods the object can respond to. -* I'll use a method that's on `Object`, named `methods`. - * Or a method that's on `Class`, named `instance_methods`. -* Notice that these usually return a long list of methods. - * They're running off the right side of the text box here. -* Show of hands - who's familiar with either of these methods? -* How many of you (with your hands up) know that they take an optional parameter? -* How many of you remember whether to pass `true` or `false` to not show methods from superclasses? - ---- - -# Parameters - -~~~ ruby -random_object.methods(false) -# => [:id, :first_name, :last_name] - -random_object.class.instance_methods(false) -# => [:id, :first_name, :last_name] -~~~ - -??? - -* It happens to be `false` for these methods. -* But to use it properly, you have to remember that. -* Can we do better? - ---- - -# Named Parameters - -~~~ ruby -random_object.methods(superclass_methods: false) -# => [:id, :first_name, :last_name] -~~~ - -??? - -* The best way to fix this API is to use a named parameter to *describe* the parameter. -* This method pre-dates named parameters in Ruby, though. -* It also needs to retain backwards compatibility. - ---- - -# Named Parameters - -~~~ ruby -class Object - def methods(options = []) - show_superclass_methods = options && - options.respond_to?(:[]) && - options[:superclass_methods] - if show_superclass_methods - show_all_methods_this_object_responds_to - else - show_methods_from_immediate_class_of_this_object - end - end -end -~~~ - -??? - -* In older Ruby versions, we would have to use an options hash to emulate named parameters. -* We still need to use an options hash to support taking a bare Boolean or a Hash. - ---- - -# Single Responsibility Principle - -~~~ ruby -random_object.methods -# => [:id, :first_name, :last_name] - -random_object.all_methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* Ideally, it'd be best just to have 2 separate methods. - ---- - -# Single Responsibility Principle - -~~~ ruby -random_object.methods(true) -# => [:id, :first_name, :last_name] - -random_object.methods(false) -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* How would you describe what this original method does? - * "Show the methods defined for this object OR the methods only defined by its immediate class." -* Any time you have an "OR" (or "AND") in the description of a method or class, that's a code smell. - * There's probably a violation of the Single Responsibility Principle. - ---- - -# An Example from Rails - -~~~ ruby -user.things(true) - -user.things.reload -~~~ - -??? - -* I came across this a couple months ago, when upgrading to Rails 5. -* These both do the same thing. -* The first one was the original API in Rails to reload an association. -* The 2nd is the current API. -* The original API is deprecated as of Rails 5, and removed as of Rails 5.1. -* Not only is the new one clearer, but the old way can lead to some very subtle bugs. - ---- - -# An Example from Rails - -~~~ ruby -@clients.sales(limit: 10) -~~~ - -??? - -* This is [Rails issue #26413](https://github.com/rails/rails/issues/26413) -* The bug report complains that the sales are being reloaded. -* Show of hands - anyone see the issue? -* The `sales` association doesn't take a hash, it takes a boolean. -* Ruby treats the `limit: 10` hash as `true`, meaning "reload". +# Multiple Meanings +* Nil doesn't have a single meaning +* It's often used to represent multiple things + * Missing value + * Empty value + * False + * Not found + * Not applicable + * Not supported + * Uninitialized / unset variable + * Failure + * Default value + * Sentinel value + * Placeholder value + * Empty set --- class: transition, primitive_obsession # Primitive Obsession -??? - -* The original version of that code contains a code smell named "primitive obsession". - --- # Primitive Obsession -~~~ ruby -class Editor - attr_accessor :editing # boolean - attr_accessor :saving # boolean - attr_accessor :error # boolean - - def render - if editing - render_document - elsif saving - render_saving - elsif error - render_error_message - end - end -end -~~~ +* Using a primitive type when it'd be better to use a more specialized type ??? -* It's using a primitive type when it'd be better to use a more specialized type. - * Example: Using a floating point number to represent money - * Example: Using a string to represent a URL +* Example: Using a floating point number to represent money +* Example: Using a string to represent a URL * In Ruby, we're more likely to abuse strings in this way. * That's often referred to as "stringly typed". * A play on "strongly typed" languages. @@ -922,24 +773,7 @@ # Primitive Obsession -~~~ ruby -class Editor - attr_accessor :state # symbol - - def render - case state - when :editing - render_document - when :saving - render_saving - when :error - render_error_message - else - fail "This shouldn't happen" - end - end -end -~~~ +* ??? @@ -1031,7 +865,7 @@ * One reason I give talks at conferences is to start a conversation. * Please don't hesitate to come talk to me any time during the conference. ------- +-- * I used a tool called [Remark][remark] to create and show these slides. diff --git a/Nothing-Is-Easy/slides.html.erb b/Nothing-Is-Easy/slides.html.erb index bb839e4..f1b9c0f 100644 --- a/Nothing-Is-Easy/slides.html.erb +++ b/Nothing-Is-Easy/slides.html.erb @@ -43,21 +43,23 @@ } .remark-slides-area { - background-color: #C6291C; /* Color of pillarbox on sides of slides */ + background-color: #2672B5; /* Color of pillarbox on sides of slides */ } .remark-slide-scaler { background-color: white; } .remark-slide-content { - background-color: #F4F4F4; + background-color: initial; font-size: 26px; line-height: 1.6; width: 100%; } + /* Code */ .remark-code, .remark-inline-code { font-family: monospace; line-height: 1.5; font-size: 16px; } .remark-inline-code { font-size: 24px; } - .remark-code { + pre code.remark-code { counter-reset: code-line-numbers; + background-color: rgba(240, 240, 240, 0.5) !important; } .remark-code .remark-code-line { counter-increment: code-line-numbers; @@ -71,27 +73,27 @@ -webkit-user-select: none; /* The line numbers should not be selectable. */ } - /* Title page */ - .remark-slide .title h1 { - font-size: 70px; - margin-top: 0px; - } - .remark-slide .title li { - list-style-type: none; - font-size: 28px; + .remark-slide .remark-slide-content { + background-image: url(images/background2.webp); } - .remark-slide .title ul { - padding-left: 0px; + + /* Title page */ + .remark-slide .remark-slide-content.title { + background-image: url(images/blue-ridge-ruby.svg), url(images/background2.webp); + background-position: 10% 80px, center; + background-size: 100%, auto; + background-origin: content-box; + } - .remark-slide .title { - background-image: url(images/f5-logo-gradient-rgb.png); - background-position: 150px 400px; + .remark-slide .remark-slide-content.title h1 { + font-size: 72px; + margin-top: 160px; + color: #0C2866; } - .remark-slide .title h3 { - font-size: 40px; - margin-left: 200px; - margin-top: 140px; + .remark-slide .remark-slide-content.title h2 { + color: #0C2866; } + .remark-slide .diagram p { text-align: center; } @@ -169,31 +171,13 @@ background-color: rgba(255, 255, 255, .75); } - .remark-slide .transition.boolean_basics { + .remark-slide .transition.basics { background-image: url(images/pexels-photo-259943.jpeg); } - .remark-slide .transition.boolean_parameters { - background-image: url(images/Param_03.jpg); - } - .remark-slide .transition.connascence { - background-image: url(images/TwinsTwins.JPG); - } - .remark-slide .transition.boolean_states { - background-image: url(images/Balkans_regions_map.png); - } .remark-slide .transition.primitive_obsession { background-image: url(images/Obsession.png); } - .remark-slide .transition.boolean_fields { - background-image: url(images/cornfield_sky_trees_cereals_summer_nature_field_clouds-450526.jpg); - } - .remark-slide .transition.exponential_complexity { - background-image: url(images/Exponential.svg); - } - .remark-slide .transition.boolean_operations { - background-image: url(images/operation.jpg); - } - .remark-slide .transition.boolean_transformations { + .remark-slide .transition.transformations { background-image: url(images/Bumblebee_Transformer_-_Flickr_-_andrewbasterfield.jpg); } .remark-slide .transition.readability { diff --git a/Nothing-Is-Easy/slides.md b/Nothing-Is-Easy/slides.md index 399ff37..feb3ba0 100644 --- a/Nothing-Is-Easy/slides.md +++ b/Nothing-Is-Easy/slides.md @@ -17,6 +17,18 @@ class: title, middle, center ## A Talk About Nothing (Nil) +??? + +* This is a talk I'll be giving next Thursday at Blue Ridge Ruby +* My Twitter is in the upper right corner if you want to tweet at/about me. + * I'm also on Mastodon, same username, @ruby.social + * I'm rarely on Twitter any more, nor Mastodon +* AUDIENCE QUESTION: Who knows the costliest mistake in computer programming? + * ANSWER: Null pointers, according to Tony Hoare (who invented them) + * It has probably cost businesses several billion dollars over the years +* AUDIENCE QUESTION: Who has seen a talk on Nil by Sandi Metz or Avdi Grimm? + * I've got some big boots to fill! + --- class: middle @@ -33,14 +45,41 @@ class: middle * Hit `P` for presenter notes. * Notes have links to things I reference. * Notes have more info than I'll talk about in some cases. -* My Twitter is in the upper right corner if you want to tweet at/about me. - * I'm also on Mastodon, same username, @ruby.social -* AUDIENCE QUESTION: Who knows the costliest mistake in computer programming? - * ANSWER: Null pointers, according to Tony Hoare (who invented them) - * It has probably cost businesses several billion dollars over the years - * Second most costly mistake is automatic coercion to Booleans -* AUDIENCE QUESTION: Who has seen a talk on Nil by Sandi Metz or Avdi Grimm? - * I've got some big boots to fill! + +--- +class: middle, center, image-only + +![Title slide for Jeremy Fairbank's Elm-Conf 2017 talk](images/fairbank-title-slide.jpg) + +??? + +* One of my favorite conference talks was by Jeremy Fairbank. + * He talked about Booleans + * I wondered how he could talk about true and false for 40 minutes + * It turned out to be a great talk + * His talk was about Elm + +------ + +* I highly recommend watching Jeremy's talk. + * video: [Solving the Boolean Identity Crisis by Jeremy Fairbank](https://www.youtube.com/watch?v=8Af1bh-BVY8) + * slides: [Slides for "Solving the Boolean Identity Crisis"](https://bit.ly/elm-bool) + +--- +class: middle, center, image-only + +![Title slide for Craig Buchek's RailsConf 2018 talk about Booleans in Ruby](images/Booleans-talk.jpg) + + +??? + +* That talk inspired me to give a talk on Booleans +* And **that** talk inspired me to write this talk + +------ + +* In my own opinion, this is the best conference talk I've given +* http://craigbuchek.com/booleans --- class: agenda @@ -52,8 +91,8 @@ class: agenda * NoMethodError * Ruby's take on NullPointerException * The Billion Dollar Mistake -* Safe Navigation * Other Anti-Patterns +* Safe Navigation * Null Object and Special Case * Refactoring * Type Safety @@ -124,7 +163,7 @@ c.object_id * true: 2 * nil: 4 ------- +-- * From the `object_id` documentation: "no two objects will share an id". @@ -278,7 +317,7 @@ v = nil * It's more *idiomatic* to use something more explicitly intention-revealing that returns a Boolean. * Like `nil?`, `empty?`, or `blank?` ------- +-- * This isn't as big a problem in Ruby as in other languages. * I contend that automatic coercion to Booleans is the 2nd most expensive mistake in computer language design. @@ -315,6 +354,18 @@ nil.to_h # NoMethodError +* Raised when a method is called on an object that does not support it +* Nil has only a few methods + +~~~ ruby +nil.to_s +# => "" + +nil. +~~~ + +??? + --- # Safe Navigation @@ -433,228 +484,44 @@ address = params.dig(:account, :owner, :address) * TODO: More on the Law of Demeter --- -class: transition, parameters - -# Parameters +class: transition, root_causes -??? - -* Next we'll talk about `nil` used in parameters to methods. +# Root Causes --- -# Parameters - -~~~ ruby -random_object.class -# => User -~~~ - -??? - -* I spend a lot of time in Rails console, IRB, or Pry. -* I'll often want to see the class of an object. - ------- - -* I actually wrote a method to get a random object: - -~~~ ruby -def random_object(cache: true) - return @random_object if cache && @random_object_defined - @random_object_defined = true - random_object_index = Random.rand(ObjectSpace.each_object.count) - @random_object = ObjectSpace.each_object.drop(random_object_index).take(1) -end -~~~ - ---- - -# Parameters - -~~~ ruby -random_object.methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] - -random_object.class.instance_methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* Often, I'll also want to see what methods the object can respond to. -* I'll use a method that's on `Object`, named `methods`. - * Or a method that's on `Class`, named `instance_methods`. -* Notice that these usually return a long list of methods. - * They're running off the right side of the text box here. -* Show of hands - who's familiar with either of these methods? -* How many of you (with your hands up) know that they take an optional parameter? -* How many of you remember whether to pass `true` or `false` to not show methods from superclasses? - ---- - -# Parameters - -~~~ ruby -random_object.methods(false) -# => [:id, :first_name, :last_name] - -random_object.class.instance_methods(false) -# => [:id, :first_name, :last_name] -~~~ - -??? - -* It happens to be `false` for these methods. -* But to use it properly, you have to remember that. -* Can we do better? - ---- - -# Named Parameters - -~~~ ruby -random_object.methods(superclass_methods: false) -# => [:id, :first_name, :last_name] -~~~ - -??? - -* The best way to fix this API is to use a named parameter to *describe* the parameter. -* This method pre-dates named parameters in Ruby, though. -* It also needs to retain backwards compatibility. - ---- - -# Named Parameters - -~~~ ruby -class Object - def methods(options = []) - show_superclass_methods = options && - options.respond_to?(:[]) && - options[:superclass_methods] - if show_superclass_methods - show_all_methods_this_object_responds_to - else - show_methods_from_immediate_class_of_this_object - end - end -end -~~~ - -??? - -* In older Ruby versions, we would have to use an options hash to emulate named parameters. -* We still need to use an options hash to support taking a bare Boolean or a Hash. - ---- - -# Single Responsibility Principle - -~~~ ruby -random_object.methods -# => [:id, :first_name, :last_name] - -random_object.all_methods -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* Ideally, it'd be best just to have 2 separate methods. - ---- - -# Single Responsibility Principle - -~~~ ruby -random_object.methods(true) -# => [:id, :first_name, :last_name] - -random_object.methods(false) -# => [:can?, :cannot?, :paper_trail_originator, :paper_trail_enabled_for_model?, :whodunnit] -~~~ - -??? - -* How would you describe what this original method does? - * "Show the methods defined for this object OR the methods only defined by its immediate class." -* Any time you have an "OR" (or "AND") in the description of a method or class, that's a code smell. - * There's probably a violation of the Single Responsibility Principle. - ---- - -# An Example from Rails - -~~~ ruby -user.things(true) - -user.things.reload -~~~ - -??? - -* I came across this a couple months ago, when upgrading to Rails 5. -* These both do the same thing. -* The first one was the original API in Rails to reload an association. -* The 2nd is the current API. -* The original API is deprecated as of Rails 5, and removed as of Rails 5.1. -* Not only is the new one clearer, but the old way can lead to some very subtle bugs. - ---- - -# An Example from Rails - -~~~ ruby -@clients.sales(limit: 10) -~~~ - -??? - -* This is [Rails issue #26413](https://github.com/rails/rails/issues/26413) -* The bug report complains that the sales are being reloaded. -* Show of hands - anyone see the issue? -* The `sales` association doesn't take a hash, it takes a boolean. -* Ruby treats the `limit: 10` hash as `true`, meaning "reload". +# Multiple Meanings +* Nil doesn't have a single meaning +* It's often used to represent multiple things + * Missing value + * Empty value + * False + * Not found + * Not applicable + * Not supported + * Uninitialized / unset variable + * Failure + * Default value + * Sentinel value + * Placeholder value + * Empty set --- class: transition, primitive_obsession # Primitive Obsession -??? - -* The original version of that code contains a code smell named "primitive obsession". - --- # Primitive Obsession -~~~ ruby -class Editor - attr_accessor :editing # boolean - attr_accessor :saving # boolean - attr_accessor :error # boolean - - def render - if editing - render_document - elsif saving - render_saving - elsif error - render_error_message - end - end -end -~~~ +* Using a primitive type when it'd be better to use a more specialized type ??? -* It's using a primitive type when it'd be better to use a more specialized type. - * Example: Using a floating point number to represent money - * Example: Using a string to represent a URL +* Example: Using a floating point number to represent money +* Example: Using a string to represent a URL * In Ruby, we're more likely to abuse strings in this way. * That's often referred to as "stringly typed". * A play on "strongly typed" languages. @@ -664,24 +531,7 @@ end # Primitive Obsession -~~~ ruby -class Editor - attr_accessor :state # symbol - - def render - case state - when :editing - render_document - when :saving - render_saving - when :error - render_error_message - else - fail "This shouldn't happen" - end - end -end -~~~ +* ??? @@ -773,7 +623,7 @@ class: thanks, image-only * One reason I give talks at conferences is to start a conversation. * Please don't hesitate to come talk to me any time during the conference. ------- +-- * I used a tool called [Remark][remark] to create and show these slides.