<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Avdi Grimm</title>
    <description>The latest articles on DEV Community by Avdi Grimm (@avdi).</description>
    <link>https://dev.to/avdi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F341255%2Fa80d4426-3104-45e4-893d-5088959add84.jpg</url>
      <title>DEV Community: Avdi Grimm</title>
      <link>https://dev.to/avdi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avdi"/>
    <language>en</language>
    <item>
      <title>That’s So Fetch</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 06 Nov 2020 19:41:39 +0000</pubDate>
      <link>https://dev.to/avdi/that-s-so-fetch-89k</link>
      <guid>https://dev.to/avdi/that-s-so-fetch-89k</guid>
      <description>&lt;p&gt;It’s time for another dip into the RubyTapas archives! In this third and last episode on Ruby’s &lt;code&gt;#fetch&lt;/code&gt; family of methods, now free to all, we get into some advanced &lt;code&gt;#fetch&lt;/code&gt; usage. Including: deep fetching, using the name of the missing key in fallbacks code, and why I never use the two-argument form of &lt;code&gt;#fetch&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Director’s commentary:&lt;/strong&gt; This was first published on RubyTapas October 26, 2012. Since then the &lt;code&gt;#dig&lt;/code&gt; method has supplanted some of my deep-fetching needs, although it lacks a fallback block argument.&lt;/p&gt;

&lt;p&gt;Find the original episode script and code below the video.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffast.wistia.com%2Fembed%2Fmedias%2F1v8gcnx3yi%2Fswatch" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffast.wistia.com%2Fembed%2Fmedias%2F1v8gcnx3yi%2Fswatch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is the third and almost certainly the last &lt;a href="https://www.rubytapas.com/2012/10/10/episode-008-fetch-as-an-assertion/#tapas_script" rel="noopener noreferrer"&gt;episode on the &lt;code&gt;#fetch&lt;/code&gt; method&lt;/a&gt;. Today I want to go over some advanced aspects of using &lt;code&gt;#fetch&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  #fetch beyond Hash
&lt;/h2&gt;

&lt;p&gt;First of all, it’s worth noting that while up till now I’ve demonstrated &lt;a href="https://www.rubytapas.com/out/ruby--hash-fetch" rel="noopener noreferrer"&gt;it in the context of &lt;code&gt;Hash&lt;/code&gt;&lt;/a&gt; objects, &lt;a href="https://www.rubytapas.com/out/ruby--array-fetch" rel="noopener noreferrer"&gt;&lt;code&gt;#fetch&lt;/code&gt; isn’t limited&lt;/a&gt; to Hashes. It’s also available on &lt;a href="https://www.rubytapas.com/out/ruby--array" rel="noopener noreferrer"&gt;Arrays&lt;/a&gt;, where it behaves very similar to the Hash version, except that when the key is missing and no default block is supplied, it raises an &lt;code&gt;IndexError&lt;/code&gt; instead of a &lt;code&gt;KeyError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = [:x, :y, :z]
a.fetch(3)
# ~&amp;gt; -:2:in `fetch': index 3 outside of array bounds: -3...3 (IndexError)
# ~&amp;gt; from -:2:in `&amp;lt;main&amp;gt;'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find &lt;code&gt;#fetch&lt;/code&gt; the &lt;a href="https://www.rubytapas.com/out/ruby--env" rel="noopener noreferrer"&gt;&lt;code&gt;ENV&lt;/code&gt; pseudo-hash&lt;/a&gt;. I find this very useful for optional configuration values, for example enabling a port number to be customized via environment variable while still providing a default value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;port = ENV.fetch('PORT'){ 8080 }.to_i
port # =&amp;gt; 8080

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defaults for nested hashes
&lt;/h2&gt;

&lt;p&gt;Sometimes, you may want to get optional values out of a nested hash. Not only do you not know if the values will be present, but you don’t even know if the nested sub-trees will be there. For instance, here’s some configuration data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config1 = {
  database: {
    type: 'mysql',
    host: 'localhost'
  }
}

config2 = {} # empty!  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like to handle data like this by chaining fetch statements together, with empty hashes as the default values for missing subtrees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config2.fetch(:database){{}}.fetch(:type){'sqlite'}
# =&amp;gt; "sqlite"  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generalized default blocks
&lt;/h2&gt;

&lt;p&gt;One thing I haven’t yet shown about &lt;a href="https://www.rubytapas.com/out/ruby--hash-fetch" rel="noopener noreferrer"&gt;#fetch&lt;/a&gt; is that it yields the missing key that was passed in. Here’s some code that demonstrates what I mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{}.fetch(:foo) do |key|
  puts "Missing key: #{key}"
end
# &amp;gt;&amp;gt; Missing key: foo

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One scenario where this could be useful is if we have a lot of calls to fetch which should all handle a missing key the same way. We can define the default block as a &lt;a href="https://www.rubytapas.com/in/lambda" rel="noopener noreferrer"&gt;lambda&lt;/a&gt; taking one argument, and pass the lambda to each call to &lt;code&gt;fetch&lt;/code&gt;. This code prompts the user for missing values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default = -&amp;gt;(key) do
  puts "#{key} not found, please enter it: "
  gets
end

h = {}
name = h.fetch(:name, &amp;amp;default)
email = h.fetch(:email, &amp;amp;default)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two-argument form of #fetch
&lt;/h2&gt;

&lt;p&gt;If you’re already familiar with the &lt;code&gt;#fetch&lt;/code&gt; method you may be wondering why I haven’t used the two-argument form in any of these videos. For those who aren’t familiar, instead of passing a block to &lt;a href="https://www.rubytapas.com/out/ruby--hash-fetch" rel="noopener noreferrer"&gt;#fetch&lt;/a&gt; for the default value, you can pass a second argument instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{}.fetch(:threads, 4) # =&amp;gt; 4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This avoids the slight overhead of executing a block, at the cost of evaluating the default value whether it is needed or not. Personally, I never use the two-argument form. I prefer to always use the block form. Here’s why: let’s say we’re writing a program and we use the two-argument form of fetch in order to avoid that block overhead. Because the default value is used in more than one place, we extract it into a method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def default
  42 # the ultimate answer
end

answers = {}
answers.fetch("How many roads must a man walk down?", default)
# =&amp;gt; 42

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later on, we decide to change the implementation of &lt;code&gt;#default&lt;/code&gt; to a much more expensive computation. Maybe one that has to communicate with an remote service before returning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def default
  # ...some expensive computation
end

answers = {}
answers.fetch("How many roads must a man walk down?", default)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the default is passed as an argument to &lt;code&gt;#fetch&lt;/code&gt;, it is always evaluated whether it is needed or not. Now our expensive &lt;code&gt;#default&lt;/code&gt; code is being executed every time we &lt;code&gt;#fetch&lt;/code&gt; a value, even if the value is present. By our premature optimization, we’ve now introduced a potentially much bigger performance regresssion everywhere our &lt;code&gt;#default&lt;/code&gt; method is used as an argument. If we had used the block form, the expensive computation would only have been triggered when it was actually needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;OK, enough about &lt;a href="https://www.rubytapas.com/out/ruby--hash-fetch" rel="noopener noreferrer"&gt;#fetch&lt;/a&gt;! Happy hacking!&lt;/p&gt;

</description>
      <category>rubytapasfreebies</category>
      <category>idioms</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Re-Use SSH Config Inside Docker Containers with WSL2</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Sat, 24 Oct 2020 20:09:26 +0000</pubDate>
      <link>https://dev.to/avdi/re-use-ssh-config-inside-docker-containers-with-wsl2-5ejo</link>
      <guid>https://dev.to/avdi/re-use-ssh-config-inside-docker-containers-with-wsl2-5ejo</guid>
      <description>&lt;p&gt;Docker and &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/"&gt;WSL&lt;/a&gt; have been getting more and more chummy lately. The &lt;a href="https://docs.docker.com/docker-for-windows/wsl/"&gt;Docker Desktop WSL2 backend&lt;/a&gt; integrates Docker containers with the “official” integrated Windows/Linux ecosystem (boy that still feels a little weird to write…) and gains performance perks in the process.&lt;/p&gt;

&lt;p&gt;However, I ran into some trouble recently getting SSH to work inside of a Docker container. As I’ve written about before, I like to &lt;a href="https://avdi.codes/share-netrc-with-your-devcontainers/"&gt;bind-in my credentials from my host machine&lt;/a&gt;, rather than proliferating credentials into containers. When a container needs to SSH out to the wider world, that means binding in my &lt;code&gt;.ssh&lt;/code&gt; directory from the host, e.g. with a &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    volumes: 
      - type: bind
        source: ${HOME}${USERPROFILE}/.ssh
        target: /home/${DEV_USER:-abapa}/.ssh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only one problem: volumes mounted from the Windows side are mounted via &lt;code&gt;drvfs&lt;/code&gt;, which by default projects all files as having mode 777 on the Linux side. And SSH will refuse to use certificate files that are world-writeable.&lt;/p&gt;

&lt;p&gt;After a bunch of research and some trial-and-error, I found a solution that’s working right now (2020-10-24). Recent versions of &lt;code&gt;drvfs&lt;/code&gt; allow NTFS-hosted files to have fake Linux-side effective permissions attached to them, via NTFS extended attributes. And the &lt;code&gt;docker-desktop&lt;/code&gt; distro that Docker Desktop installs has this feature enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS&amp;gt; wsl -d docker-desktop
# cat /etc/wsl.conf
[automount]
root = /mnt/host
crossDistro = true
options = "metadata"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;options = "metadata"&lt;/code&gt; line in &lt;code&gt;/etc/wsl.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the trick was to open a WSL terminal in that distro and set the desired effective owner and file permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS&amp;gt; wsl -d docker-desktop
caroline:/tmp/docker-desktop-root/mnt/host/c/Users/avdi_000/.ssh# chown -R 1000:1000 .
caroline:/tmp/docker-desktop-root/mnt/host/c/Users/avdi_000/.ssh# chmod -R u=rwX .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These aren’t “real” file attributes, since Windows has a very different permissions model from Linux. But they get saved as file attributes and treated as the effective file permissions inside WSL distros with metadata enabled.&lt;/p&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-R&lt;/code&gt; makes the change recursive through directories.&lt;/li&gt;
&lt;li&gt;User/group 1000 are a common default user/group ID for containers. &lt;/li&gt;
&lt;li&gt;File mode &lt;code&gt;u=rwX&lt;/code&gt; recursively sets files to readable/writeable by owner, and directories read/write/traversable by owner.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>howto</category>
      <category>devcontainers</category>
      <category>docker</category>
      <category>filesystem</category>
    </item>
    <item>
      <title>Controlling superclass argument pass-through in Ruby</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 16 Oct 2020 15:00:13 +0000</pubDate>
      <link>https://dev.to/avdi/controlling-superclass-argument-pass-through-in-ruby-1n22</link>
      <guid>https://dev.to/avdi/controlling-superclass-argument-pass-through-in-ruby-1n22</guid>
      <description>&lt;p&gt;In Ruby class-based programming, superclass versions of subclass methods are always invoked explicitly using the &lt;code&gt;super&lt;/code&gt; keyword. There are some nuances to using &lt;code&gt;super&lt;/code&gt; though, particularly when it comes to passing (or &lt;em&gt;not&lt;/em&gt; passing) arguments and blocks along to the base class. In this sample from from the &lt;a href="https://www.rubytapas.com/"&gt;RubyTapas&lt;/a&gt; archives, we’ll talk about some of those “gotchas”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Director’s commentary:&lt;/strong&gt; This was originally published as &lt;a href="https://www.rubytapas.com/"&gt;RubyTapas&lt;/a&gt;#14 in October 2012. I used to have a habit of saying “as you know” in my scripts, I think because I was afraid my audience would see something they already knew at the start of the episode and think “oh this is nothing new”. I don’t say that kind of thing as much anymore, although I still say “as you &lt;em&gt;may&lt;/em&gt; know” from time to time. I’ve become a lot more comfortable just stating my background material and trusting the viewer to situate themselves with regard to it.&lt;/p&gt;

&lt;p&gt;I’m a bit mad at myself for not coming up with a more “real” example here; I think that makes it harder to follow. This was back in the era of three episodes a week (!) though, so I’ll cut myself some slack for hasty writing.&lt;/p&gt;

&lt;p&gt;Scroll down for the video script and code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--czkypwor--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/hmewmz4ctn/swatch" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--czkypwor--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/hmewmz4ctn/swatch" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s talk about calling superclass methods.&lt;/p&gt;

&lt;p&gt;As you know, when class &lt;code&gt;Child&lt;/code&gt; inherits from class &lt;code&gt;Parent&lt;/code&gt;, and both define a method &lt;code&gt;#hello&lt;/code&gt;, the &lt;code&gt;Child&lt;/code&gt; can reference the &lt;code&gt;Parent&lt;/code&gt;‘s implementation of &lt;code&gt;#hello&lt;/code&gt;, using &lt;code&gt;super&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Parent
  def hello(subject="World")
    puts "Hello, #{subject}"
  end
end


class Child &amp;lt; Parent
  def hello(subject)
    super(subject)
    puts "How are you today?"
  end
end


Child.new.hello("Bob") 


Hello, Bob How are you today?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we simply want to call the parent implementation with the same arguments that were passed to the child implementation, we can omit the arguments to &lt;code&gt;super&lt;/code&gt;. This &lt;strong&gt;only&lt;/strong&gt; works if we leave off the parentheses as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Child &amp;lt; Parent
  def hello(subject)
    puts super
    puts "How are you today?"
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes our code less brittle, because changes to a parent method’s parameter list won’t mean having to hunt around and update every &lt;code&gt;super&lt;/code&gt; call that invokes it.&lt;/p&gt;

&lt;p&gt;Sometimes we may want to force zero arguments to be passed to the superclass method. In that case, it’s important to remember to explicitly supply empty parentheses instead of leaving them off.&lt;/p&gt;

&lt;p&gt;Here’s a version of &lt;code&gt;Child&lt;/code&gt; that takes a special flag to indicate that it should use its default subject. When the flag is passed, it calls &lt;code&gt;super&lt;/code&gt; with empty parentheses, forcing the superclass method to resort to the default value for &lt;code&gt;subject&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Child &amp;lt; Parent
  def hello(subject=:default)
    if subject == :default
      super() 
      puts "How are you today?"
    else
      super(subject)
      puts "How are you today?"
    end
  end
end


Child.new.hello(:default)


Hello, World How are you today?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s a catch to this, though: even with explicit empty parens, calling super will still automatically pass along any block given to the child method.&lt;/p&gt;

&lt;p&gt;To show what I mean, let’s modify the parent class method to take a block, and then pass a block to the call to &lt;code&gt;#hello&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Parent
  def hello(subject="World")
    puts "Hello, #{subject}"
    if block_given?
      yield
      puts "Well, nice seeing you!"
    end
  end
end


Child.new.hello(:default) do
  puts "Hi there, Child!"
end
# &amp;gt;&amp;gt; Hello, World
# &amp;gt;&amp;gt; Hi there, Child!
# &amp;gt;&amp;gt; Well, nice seeing you!
# &amp;gt;&amp;gt; How are you today?


Hello, World Hi there, Child! Well, nice seeing you! How are you today?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the output is a little mixed-up due to the block being unexpectedly passed-through despite the empty argument list to &lt;code&gt;super&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to suppress the block being passed through, we have to use the special argument &lt;code&gt;&amp;amp;nil&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Child &amp;lt; Parent
  def hello(subject=:default)
    if subject == :default
      super(&amp;amp;nil) 
      puts "How are you today?"
    else
      super(subject, &amp;amp;nil)
      puts "How are you today?"
    end
  end
end


Child.new.hello(:default) do
  puts "Hi there, Child"
end


Hello, World How are you today?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This less-than-obvious technique eliminates the possibility of a block being implicitly passed through to the superclass method.&lt;/p&gt;

&lt;p&gt;I have some other tricks involving the &lt;code&gt;super&lt;/code&gt; keyword, but I’ll save them for a &lt;a href="https://www.rubytapas.com/2012/10/29/episode-016-super-in-modules/#tapas_script"&gt;future episode&lt;/a&gt;. &lt;a href="https://www.rubytapas.com/2012/10/29/episode-016-super-in-modules/#tapas_script"&gt;Happy hacking!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rubytapasfreebies</category>
      <category>classes</category>
      <category>objectorienteddesign</category>
      <category>oop</category>
    </item>
    <item>
      <title>Ruby Singleton Objects</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 09 Oct 2020 18:31:39 +0000</pubDate>
      <link>https://dev.to/avdi/ruby-singleton-objects-17dm</link>
      <guid>https://dev.to/avdi/ruby-singleton-objects-17dm</guid>
      <description>&lt;p&gt;Here’s another freebie from the deep &lt;a href="https://www.rubytapas.com"&gt;RubyTapas&lt;/a&gt; stacks. This one is about a truth of object modeling that we don’t often talk about: not every object needs to have state. If an object has no state, there’s no need to have more than one of it. And for stateless objects, having a class just to generate a single instance may be superfluous!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Director’s commentary:&lt;/strong&gt; In retrospect I’d tighten up the pacing on this one. And I hate that the ending monologue on the Singleton pattern has no illustration on the screen. Also, I really hope I don’t sound that bored in more recent videos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s4YpXIti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/ph9yjzc6x8/swatch" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s4YpXIti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/ph9yjzc6x8/swatch" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I was at &lt;a href="https://www.rubytapas.com/out/rubydcamp"&gt;Ruby DCamp&lt;/a&gt; a few weeks ago &lt;a href="https://www.rubytapas.com/out/sandi-metz"&gt;Sandi Metz&lt;/a&gt; asked me to write a version of &lt;a href="https://www.rubytapas.com/out/conways-life"&gt;Conway’s Game of Life&lt;/a&gt; in a semi-functional, stateless style. I decided to represent the concept of a “live cell” and a “dead cell” as two different kinds of object. The game grid would then be represented by a simple array of arrays, populated by live cells and dead cells.&lt;/p&gt;

&lt;p&gt;I was all set to write a class for each…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LiveCell
  # ...
end

class DeadCell
  # ...
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…when it occurred to me: since this is a stateless implementation, there was no real need to have individual &lt;code&gt;LiveCell&lt;/code&gt; objects for every live cell on the board. And the same goes for dead cells. Without any state, every instance would be exactly the same. And since the objects didn’t have any need for initialization, why even bother with classes?&lt;/p&gt;

&lt;p&gt;Instead, I created the objects as singleton instances of the &lt;code&gt;Object&lt;/code&gt; class. I assigned the instances to constants, so they would be available anywhere in the program. Then I used Ruby’s singleton class syntax to add methods to each one. Each object needed two methods: a &lt;code&gt;#to_s&lt;/code&gt; method to represent the cell on an ASCII grid, and a method to determine what the next generation of its grid square would contain: either a live cell or a dead cell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LIVE_CELL = Object.new
class &amp;lt;&amp;lt; LIVE_CELL
  def to_s() 'o' end

  def next_generation(x, y, board)
    case board.neighbors(x,y).count(LIVE_CELL)
    when 2..3 then self
    else DEAD_CELL
    end
  end
end

DEAD_CELL = Object.new
class &amp;lt;&amp;lt; DEAD_CELL
  def to_s() '.' end

  def next_generation(x, y, board)
    case board.neighbors(x,y).count(LIVE_CELL)
    when 3 then LIVE_CELL
    else self
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I could just populate my grid with the same LIVE_CELL and DEAD_CELL  objects, over and over again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
 [DEAD_CELL, LIVE_CELL, LIVE_CELL, DEAD_CELL],
 # ...
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked quite well, but I didn’t like the fact that the singleton objects had to be created in two steps. So I decided to see if I could create the object, assign it to a constant, and define methods on it, all in a single statement.&lt;/p&gt;

&lt;p&gt;To accomplish this feat, I took advantage of the fact that Ruby allows variables and constants to be assigned inside parenthesized sub-expressions of a statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class &amp;lt;&amp;lt; (LIVE_CELL = Object.new)
  def to_s() 'o' end

  def next_generation(x, y, board)
    case board.neighbors(x,y).count(LIVE_CELL)
    when 2..3 then self
    else DEAD_CELL
    end
  end
end

class &amp;lt;&amp;lt; (DEAD_CELL = Object.new)
  def to_s() '.' end

  def next_generation(x, y, board)
    case board.neighbors(x,y).count(LIVE_CELL)
    when 3 then LIVE_CELL
    else self
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting syntax was a bit obscure, but it accomplished my purpose succinctly.&lt;/p&gt;

&lt;p&gt;A class plays two roles in an &lt;a href="https://www.rubytapas.com/t/oop"&gt;OO program&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It provides a container for behavior that’s shared by many objects.&lt;/li&gt;
&lt;li&gt;It acts as an &lt;em&gt;&lt;a href="https://www.rubytapas.com/out/factory"&gt;object factory&lt;/a&gt;&lt;/em&gt;, manufacturing new instances and
ensuring they are initialized correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When we have an object which does not need to share behavior with any others objects, and which requires no initialization, that renders both roles of a class superfluous. In cases like this, it can make more sense to just use a one-off &lt;a href="https://www.rubytapas.com/out/ruby--singleton"&gt;singleton object&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another way to accomplish this is to use a &lt;a href="https://www.rubytapas.com/out/ruby--module"&gt;module&lt;/a&gt; as our singleton object, and only define class-level methods on the module. Let’s update the example to use this approach instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module LiveCell
  def self.to_s() 'o' end

  def self.next_generation(x, y, board)
    case board.neighbors(x,y).count(LiveCell)
    when 2..3 then self
    else DeadCell
    end
  end
end

module DeadCell
  def self.to_s() '.' end

  def self.next_generation(x, y, board)
    case board.neighbors(x,y).count(LiveCell)
    when 3 then LiveCell
    else self
    end
  end
end

[
 [DeadCell, LiveCell, LiveCell, DeadCell],
 # ...
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a perfectly legitimate way to implement singleton objects. Personally, I feel that using a singleton instance of &lt;code&gt;Object&lt;/code&gt; is a little more intention-revealing, since modules are usually used as namespaces and as a means of sharing behavior. But I don’t feel that strongly about it. The main thing I hope to convey in this episode is simply that sometimes a singleton object is all we need, and when that’s all we need, that’s all we should use.&lt;/p&gt;

&lt;p&gt;I should make one last point here: I’m talking about singleton &lt;em&gt;objects&lt;/em&gt;, not the Singleton &lt;em&gt;Pattern&lt;/em&gt;. The &lt;a href="https://www.rubytapas.com/out/singleton"&gt;Singleton Pattern&lt;/a&gt; describes a way to control the construction of a particular class such that only one instance is ever constructed. Singletons following this pattern have often been used to manage a great deal of centralized state, a situation that can lead to a number of problems, including thread contention and proliferation of dependencies. The singleton objects we’ve defined here are stateless and more akin to the immutable singletons that Ruby provides like &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;nil&lt;/code&gt;. As such they are somewhat less prone to abuse.&lt;/p&gt;

&lt;p&gt;Okay, enough for today. Happy hacking!&lt;/p&gt;

</description>
      <category>rubytapasfreebies</category>
      <category>design</category>
      <category>designpatterns</category>
      <category>featured</category>
    </item>
    <item>
      <title>Why and how to use Ruby’s Hash#fetch for default values</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Sun, 04 Oct 2020 18:09:21 +0000</pubDate>
      <link>https://dev.to/avdi/why-and-how-to-use-ruby-s-hash-fetch-for-default-values-280h</link>
      <guid>https://dev.to/avdi/why-and-how-to-use-ruby-s-hash-fetch-for-default-values-280h</guid>
      <description>&lt;p&gt;Here’s another classic from the early days of &lt;a href="https://www.rubytapas.com"&gt;RubyTapas&lt;/a&gt;. Originally published as &lt;a href="https://www.rubytapas.com/2012/10/19/episode-012-fetch-for-defaults/"&gt;Episode #11&lt;/a&gt; in October 2012, it’s a complement to the episode on &lt;a href="https://www.rubytapas.com/2012/10/10/episode-008-fetch-as-an-assertion/#tapas_script"&gt;using &lt;code&gt;fetch&lt;/code&gt; as an assertion&lt;/a&gt;. This episode digs into the difference between using the &lt;code&gt;||&lt;/code&gt; operator for defaults vs. using &lt;code&gt;Hash#fetch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PJp6B95r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/qxvmb0loz7/swatch" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PJp6B95r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/qxvmb0loz7/swatch" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Director’s commentary:&lt;/strong&gt; I can see some tiny advancements in quality here. At this point I’d realized that my original comment color was nearly unreadable on the dark background, and brightened it up a bit.&lt;/p&gt;

&lt;p&gt;But my voiceover still sounds kind of bored. And was still content to have long sections of silence while I put new code on the screen. These days, I try to always accompany coding with simultaneous explanation.&lt;/p&gt;

&lt;p&gt;Read on for the original script and code…&lt;/p&gt;




&lt;p&gt;In a &lt;a href="https://www.rubytapas.com/2012/10/10/episode-008-fetch-as-an-assertion/#tapas_script"&gt;previous episode&lt;/a&gt;, we looked at how the &lt;code&gt;#fetch&lt;/code&gt; method on &lt;code&gt;Hash&lt;/code&gt; can be used to assert that a given hash key is present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth = {
  'uid' =&amp;gt; 12345,
  'info' =&amp;gt; {
  }
}

# ...

email_address = auth['info'].fetch('email')
# ~&amp;gt; -:11:in `fetch': key not found: "email" (KeyError)
# ~&amp;gt; from -:11:in `&amp;lt;main&amp;gt;'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if the &lt;code&gt;KeyError&lt;/code&gt; that Hash raises doesn’t provide enough context for a useful error message?&lt;/p&gt;

&lt;p&gt;Along with the key to fetch, the &lt;code&gt;#fetch&lt;/code&gt; method can also receive an optional block. This block is evaluated if, and only if, the key is &lt;em&gt;not&lt;/em&gt; found.&lt;/p&gt;

&lt;p&gt;Knowing this, we can pass a block to &lt;code&gt;#fetch&lt;/code&gt; which raises a custom exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth['uid'] # =&amp;gt; 12345
auth['info'].fetch('email') do 
  raise "Invalid auth data (missing email)."\
        "See https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema"
end
email_address = auth['info'].fetch('email')
# ~&amp;gt; -:10:in `block in &amp;lt;main&amp;gt;': Invalid auth data (missing email).See https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema (RuntimeError)
# ~&amp;gt; from -:8:in `fetch'
# ~&amp;gt; from -:8:in `&amp;lt;main&amp;gt;'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when this code encounters an unexpectedly missing key, the raised exception will explain both the problem, and where to find more information.&lt;/p&gt;

&lt;p&gt;The block argument to &lt;code&gt;#fetch&lt;/code&gt; isn’t just for raising errors, however. If it doesn’t raise an exception, &lt;code&gt;#fetch&lt;/code&gt; will return the result value of the block to the caller, meaning that &lt;code&gt;#fetch&lt;/code&gt; is also very useful for providing default values. So, for instance, we can provide a default email address when none is specified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email_address = auth['info'].fetch('email'){ 'anonymous@example.org' }
email_address # =&amp;gt; "anonymous@example.org"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you may be wondering: what’s the difference between using &lt;code&gt;#fetch&lt;/code&gt; for defaults, and using the &lt;code&gt;||&lt;/code&gt; &lt;a href="https://www.rubytapas.com/t/operators"&gt;operator&lt;/a&gt; for default values? While these may seem equivalent at first, they actually behave in subtly, but importantly different ways. Let’s explore the differences.&lt;/p&gt;

&lt;p&gt;Here’s an example of using the &lt;code&gt;||&lt;/code&gt; operator for a default. This code receives an options hash, and uses the &lt;code&gt;:logger&lt;/code&gt; key to find a logger object. If the key isn’t specified, it creates a default logger to &lt;code&gt;$stdout&lt;/code&gt;. If the key is &lt;code&gt;nil&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, it disables logging by substituting a &lt;code&gt;NullLogger&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;This works fine when we give it an empty &lt;code&gt;Hash&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'logger'

class NullLogger
  def method_missing(*); end
end

options = {}
logger = options[:logger] || Logger.new($stdout) 
unless logger
  logger = NullLogger.new
end
logger
# =&amp;gt; #&amp;lt;Logger:0x000000030545a8
# @default_formatter=
# #&amp;lt;Logger::Formatter:0x00000003054580 @datetime_format=nil&amp;gt;,
# @formatter=nil,
# @level=0,
# @logdev=
# #&amp;lt;Logger::LogDevice:0x00000003054530
# @dev=#&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;,
# @filename=nil,
# @mutex=
# #&amp;lt;Logger::LogDevice::LogDeviceMutex:0x00000003054508
# @mon_count=0,
# @mon_mutex=#&amp;lt;Mutex:0x000000030544b8&amp;gt;,
# @mon_owner=nil&amp;gt;,
# @shift_age=nil,
# @shift_size=nil&amp;gt;,
# @progname=nil&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But when we pass &lt;code&gt;false&lt;/code&gt; as the value of &lt;code&gt;:logger&lt;/code&gt;, we get a surprise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options = {logger: false}
logger = options[:logger] || Logger.new($stdout) 
unless logger
  logger = NullLogger.new
end
logger
# =&amp;gt; #&amp;lt;Logger:0x000000040bb608
# @default_formatter=
# #&amp;lt;Logger::Formatter:0x000000040bb5e0 @datetime_format=nil&amp;gt;,
# @formatter=nil,
# @level=0,
# @logdev=
# #&amp;lt;Logger::LogDevice:0x000000040bb590
# @dev=#&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;,
# @filename=nil,
# @mutex=
# #&amp;lt;Logger::LogDevice::LogDeviceMutex:0x000000040bb568
# @mon_count=0,
# @mon_mutex=#&amp;lt;Mutex:0x000000040bb518&amp;gt;,
# @mon_owner=nil&amp;gt;,
# @shift_age=nil,
# @shift_size=nil&amp;gt;,
# @progname=nil&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was supposed to be a &lt;code&gt;NullLogger&lt;/code&gt;, not the default logger!&lt;/p&gt;

&lt;p&gt;So what happened here? The problem with using &lt;code&gt;||&lt;/code&gt; with a &lt;code&gt;Hash&lt;/code&gt; for default values is that it can’t differentiate between a &lt;em&gt;missing&lt;/em&gt; key, versus a key whose value is &lt;code&gt;nil&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. Here’s some code to demonstrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{}[:foo] || :default # =&amp;gt; :default
{foo: nil}[:foo] || :default # =&amp;gt; :default
{foo: false}[:foo] || :default # =&amp;gt; :default

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In contrast, &lt;code&gt;#fetch&lt;/code&gt; only resorts to the default when the given key is actually missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{}.fetch(:foo){:default} # =&amp;gt; :default
{foo: nil}.fetch(:foo){:default} # =&amp;gt; nil
{foo: false}.fetch(:foo){:default} # =&amp;gt; false

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we switch to using &lt;code&gt;#fetch&lt;/code&gt; in our logger-defaulting code, it works as intended.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options = {logger: false}
logger = options.fetch(:logger){Logger.new($stdout)}
unless logger
  logger = NullLogger.new
end
logger
# =&amp;gt; #&amp;lt;NullLogger:0x00000003b73858&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you want to provide default value for a missing hash key, consider carefully whether you want an explicitly supplied &lt;code&gt;nil&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; to be treated the same as a missing key. If not, use &lt;code&gt;#fetch&lt;/code&gt; to provide the default value.&lt;/p&gt;

&lt;p&gt;OK, that’s all for today. &lt;a href="https://www.rubytapas.com/2012/10/19/episode-012-fetch-for-defaults/#tapas_script"&gt;Happy hacking!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rubytapasfreebies</category>
      <category>datastructures</category>
      <category>featured</category>
      <category>idioms</category>
    </item>
    <item>
      <title>The ultimate software taboo</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 18 Sep 2020 19:35:28 +0000</pubDate>
      <link>https://dev.to/avdi/the-ultimate-software-taboo-1mln</link>
      <guid>https://dev.to/avdi/the-ultimate-software-taboo-1mln</guid>
      <description>&lt;p&gt;I have written before about &lt;a href="https://avdi.codes/in-grudging-defense-of-structure/"&gt;the hidden dangers of unstructured or “flat” organizations&lt;/a&gt;. A microcosm of the structure wars is the debate over whether Open Source software projects should adopt explicit &lt;a href="http://contributor-covenant.org/"&gt;codes of conduct&lt;/a&gt;. These codes, if not an introduction of true structure, are at least the imposition of a “social API” of sorts.&lt;/p&gt;

&lt;p&gt;One objection I’ve seen raised to codes of conduct is that we shouldn’t discriminate against people who write good code but who happen to be assholes. After all, as the argument goes, not everyone is blessed with social graces. And if we exclude those people, we will miss out on their essential technical contributions.&lt;/p&gt;

&lt;p&gt;With regard to this argument, &lt;a href="http://college.usatoday.com/2015/12/05/toxic-co-workers-harmful-to-organization/"&gt;a study out of Harvard on “toxic employees”&lt;/a&gt; seems apropos:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A worker in the top 1% of work productivity could return $5,303 in cost savings, while avoiding a toxic hire could net an estimated $12,489.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Studies also show that &lt;a href="https://hbr.org/2015/12/proof-that-positive-work-cultures-are-more-productive"&gt;an overall negative culture has tremendously damaging impact on health, productivity, and engagement&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;…a large and growing body of research on positive organizational psychology demonstrates that not only is a cut-throat environment harmful to productivity over time, but that a positive environment will lead to dramatic benefits for employers, employees, and the bottom line.&lt;/p&gt;

&lt;p&gt;Although there’s an assumption that stress and pressure push employees to perform more, better, and faster, what cutthroat organizations fail to recognize is the hidden costs incurred.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;Emma Seppälä and Kim Cameron in Harvard Business Review&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These studies are from the realm of business, but it doesn’t seem like too much of a stretch to suggest that the effects extend to volunteer associations as well.&lt;/p&gt;

&lt;p&gt;And then there’s this. A new study has found that in a medical setting, &lt;a href="http://nymag.com/scienceofus/2015/10/rudeness-in-hospitals-could-kill-patients.html#"&gt;rudeness might literally kill&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;…a rude comment from a third-party doctor decreased performance among doctors and nurses by more than 50 percent […] “We found that rudeness damages your ability to think, manage information, and make decisions,” said Amir Erez, an author on the study and a Huber Hurst professor of management at the University of Florida. “You can be highly motivated to work, but if rudeness damages your cognitive system then you can’t function appropriately in a complex situation. And that hurts patients.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is consistent with what I learned from reading &lt;em&gt;&lt;a href="http://amzn.to/1QFpKw4"&gt;Thinking, Fast and Slow&lt;/a&gt;&lt;/em&gt;: the human mind draws from a single well of energy. This energy can be sapped in many ways, and processing negativity is one of those ways. Having an asshole in the room doesn’t just raise the bar to entry. It ensures that everyone who &lt;em&gt;has&lt;/em&gt; made it into the room won’t be working at their full potential.&lt;/p&gt;

&lt;p&gt;Whenever I hear that it is simply impractical to exclude contributions based on social, political, or ethical objections, I think about September 27, 1983. That’s the day Richard Stallman decided that the sociopolitical structures behind the software of his day were morally untenable, and launched a project to replace every single line of code he and others made use of with &lt;a href="http://www.gnu.org/philosophy/free-sw.en.html"&gt;Free Software&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Discarding millions of lines of existing code. Excluding all of the brilliant programmers who were working within the confines of Free-Software-unfriendly organizations. Clearly, this project was doomed from the start.&lt;/p&gt;

&lt;p&gt;And yet here we are, decades later, and this letter was almost certainly delivered to you with the help of Free Software. Free Software powers millions of servers, phones, and devices. The GNU replacements for the classic UNIX utility stack, re-written from scratch to satisfy ethical constraints, are widely regarded as being superior to the ones that they replaced. Amazingly, counter-intuitively, rejecting existing work and existing contributors did not render the Free Software movement dead on arrival.&lt;/p&gt;

&lt;p&gt;More recently Stallman himself has &lt;a href="https://www.neowin.net/news/richard-stallman-suggests-gnu-kind-as-code-of-conduct-alternative#:~:text=A%20code%20of%20conduct%20was%20met%20with%20resistance,for%20a%20code%20of%20conduct%20from%20some%20people."&gt;resisted codes of conduct&lt;/a&gt;, and has &lt;a href="https://arstechnica.com/tech-policy/2019/09/richard-stallman-leaves-mit-after-controversial-remarks-on-rape/"&gt;conducted himself&lt;/a&gt; in a way that contributes to exactly the sort of unsafe environment that codes of conduct seek to avoid. Should he be given a free pass because of all his contributions?&lt;/p&gt;

&lt;p&gt;I argue no, he shouldn’t. If there are three things that Stallman showed the world, they are: 1) software has political and ethical implications; 2) a software movement built on ethics can survive and even thrive; and 3) eschewing people’s contributions on ethical grounds is no obstacle to progress.&lt;/p&gt;

&lt;p&gt;I am starting to think that the greatest taboo in software development isn’t writing GOTOs, editing code in production, or even using tabs instead of spaces. No, the most dangerous, terrifying, unspeakable idea in programming is this: the suggestion that we—and all of the code we’ve written—might be replaceable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was adapted from &lt;a href="https://dev.to/sigavdi"&gt;SIGAVDI&lt;/a&gt;#10, December 14, 2015.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>articles</category>
      <category>extractedfromsigavdi</category>
      <category>opensource</category>
      <category>techculture</category>
    </item>
    <item>
      <title>Alternative Ruby Symbol Literals</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 18 Sep 2020 14:53:04 +0000</pubDate>
      <link>https://dev.to/avdi/alternative-ruby-symbol-literals-3eb2</link>
      <guid>https://dev.to/avdi/alternative-ruby-symbol-literals-3eb2</guid>
      <description>&lt;p&gt;Periodically I take episodes from the RubyTapas archives and publish them for free. This episode from October 2012 is about symbol literals, and how you can use alternative quoting syntax to embed and interpolate any kind of character sequence in a symbol. And also: why you might &lt;em&gt;not&lt;/em&gt; want to take advantage of this. Enjoy!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p7fmdmre--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/k1pxlthk7o/swatch" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p7fmdmre--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fast.wistia.com/embed/medias/k1pxlthk7o/swatch" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Script
&lt;/h2&gt;

&lt;p&gt;As you know, a &lt;a href="https://www.rubytapas.com/out/ruby--symbol"&gt;Symbol&lt;/a&gt; literal is a word with a colon in front of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:foo

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may also know that symbols aren’t limited to simple words; anything that can go in a &lt;a href="https://www.rubytapas.com/out/ruby--string"&gt;String&lt;/a&gt;, can go in a symbol. But how do we get arbitrary characters, like spaces and dashes, into a symbol?One way is to start with a string and use &lt;code&gt;#to_sym&lt;/code&gt; to convert it to a symbol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"foo-bar (baz)".to_sym # =&amp;gt; :"foo-bar (baz)"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there’s a more concise and idiomatic way to do it. If we precede a quoted string with a colon, it becomes a quoted symbol.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:"foo-bar (baz)" # =&amp;gt; :"foo-bar (baz)"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can interpolate values into the quoted symbol just as you would into a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;post_id = 123
:"post-#{post_id}" # =&amp;gt; :"post-123"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it also works for single-quotes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:'hello, "world"' # =&amp;gt; :"hello, \"world\""

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if that doesn’t satisfy your symbol-quoting needs, there’s also a percent-quote literal syntax. Just like %q for strings, you can quote a symbol with %s followed by any delimiter you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%s{foo:bar} # =&amp;gt; :"foo:bar"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, all these different ways of writing symbols don’t mean you should go nuts with generating symbols. Symbols are best used for constrained vocabularies, like the names of options that may be passed to a method. The &lt;a href="https://www.rubytapas.com/in/ruby-vm"&gt;Ruby virtual machine&lt;/a&gt; has to keep a table in memory of all known symbols, so that it can assign a single unique integer ID to each. That means generating an unbounded set of symbols, from, say, user input can lead to memory leaks. So go easy on the symbol interpolation.That’s it for today. Happy hacking!&lt;/p&gt;

</description>
      <category>rubytapasfreebies</category>
      <category>ruby</category>
      <category>syntax</category>
    </item>
    <item>
      <title>Rubber Duck Session: Rails Polymorphic Attributes with Karim Talek</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Fri, 19 Jun 2020 20:31:21 +0000</pubDate>
      <link>https://dev.to/avdi/rubber-duck-session-rails-polymorphic-attributes-with-karim-talek-3b5o</link>
      <guid>https://dev.to/avdi/rubber-duck-session-rails-polymorphic-attributes-with-karim-talek-3b5o</guid>
      <description>&lt;p&gt;In this public &lt;a href="https://avdi.codes/duck/"&gt;Rubber Duck Session&lt;/a&gt;, my pairing partner Karim wanted to move some logic for editing message-display rules from the frontend to the backend. We settled on using JSONB and Rails polymorphic attributes as our implementation strategy.&lt;/p&gt;

</description>
      <category>videos</category>
      <category>active</category>
      <category>post</category>
      <category>rails</category>
    </item>
    <item>
      <title>An Investigative Debugging Toolbox</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Sun, 24 May 2020 01:29:50 +0000</pubDate>
      <link>https://dev.to/avdi/an-investigative-debugging-toolbox-226e</link>
      <guid>https://dev.to/avdi/an-investigative-debugging-toolbox-226e</guid>
      <description>&lt;p&gt;Last week I ran an experimental &lt;a href="https://avdi.codes/investigative-debugging-workshop/"&gt;workshop on &lt;em&gt;Investigative Debugging&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt; It was a lot of fun and the attendees seemed to get a lot out of it. Afterwards I sent out some extra reference material, including this Linux-oriented list of investigative debugging tools!&lt;/p&gt;

&lt;p&gt;The focus here is on “surveillance tools” – utilities that give you observability into exactly what programs are &lt;em&gt;really&lt;/em&gt; doing at runtime. It’s far from complete, but it represents a ton of research and I hope you find it helpful!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://github.com/iovisor/bcc"&gt;BCC&lt;/a&gt; tools&lt;/td&gt;
&lt;td&gt;A collection of over 70 specialized tracing and performance profiling tools built on BPF.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bpftrace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A Linux tool analogous to DTrace, built on BPF. &lt;code&gt;bpftrace&lt;/code&gt; enables users to compose “questions” at the command-line, using a concise &lt;a href="https://en.wikipedia.org/wiki/AWK"&gt;AWK&lt;/a&gt;-inspired syntax, for which they would once have had to use (or create) a dedicated tracing tool. It is often possible to recreate BCC tools using bpftrace commands. But when a dedicated BCC tool exists for a given query, using it may be easier than hand-rolling a &lt;code&gt;bpftrace&lt;/code&gt; command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;A technology for isolating Linux-based services into &lt;em&gt;containers&lt;/em&gt;, each with their own isolated filesystem, process table, and virtual network. Containers are useful from an investigative debugging standpoint because they enable us to isolate and observe how a process interacts with its environment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a comprehensive list of all files changed within a Docker container.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ftrace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A low-level, “kind of janky” (Julia Evans’ words) interface to Linux kprobe and uprobe tracing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;HTTP_PROXY&lt;/code&gt;, &lt;code&gt;http_proxy&lt;/code&gt;, &lt;code&gt;HTTPS_PROXY&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;These environment variables are &lt;em&gt;sometimes&lt;/em&gt; respected by programs as a way to configure a SOCKS proxy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LD_PRELOAD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An environment variable that can be used to force a program to link to arbitrary libraries before running. This can be used to substitute user-provided functions that override default behavior with e.g. tracing instrumentation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ldd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all the libraries that an ELF executable links to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open filehandles, including network connections.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://lttng.org/"&gt;LTTng&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A linux tracing framework. I believe this is now one of the inputs to BPF?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ltrace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trace library calls.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mitmproxy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An interactive HTTP proxy server that can log and inspect requests and responses. Can be configured as either a SOCKS proxy or a transparent proxy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all the &lt;em&gt;names&lt;/em&gt; (e.g. function names) in an ELF executable/library.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A workspace for experimenting with and documenting HTTP calls, which can also act as a &lt;code&gt;mitmproxy&lt;/code&gt;-like request inspector for other processes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;readelf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A general tool for inspecting and dumping information about ELF executables and libraries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trace system calls.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dump all the static strings found in an ELF executable or library.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tplist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A BCC tool for listing all USDT tracepoints available in a process.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uflow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A BCC tool for tracing method calls and returns in high-level languages like Python, Ruby, and Java. May be installed somewhere weird and not in the path, like &lt;code&gt;/usr/sbin/lib/uflow&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.wireshark.org/"&gt;WireShark&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The preeminent tool for tapping into and dissecting IP network traffic at the packet level.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>resources</category>
      <category>debugging</category>
      <category>investigativedebuggi</category>
    </item>
    <item>
      <title>Share .netrc with your devcontainers</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Sat, 23 May 2020 20:50:09 +0000</pubDate>
      <link>https://dev.to/avdi/share-netrc-with-your-devcontainers-1pdk</link>
      <guid>https://dev.to/avdi/share-netrc-with-your-devcontainers-1pdk</guid>
      <description>&lt;p&gt;I do almost all my development inside devcontainers these days. (I take the term “devcontainer” from VS Code’s &lt;code&gt;.devcontainer/devcontainer.json&lt;/code&gt;. I think it’s a useful term, and I’ve adopted it into my vernacular for the general case of “development-specific container”)&lt;/p&gt;

&lt;p&gt;Some developer service clients, such as Heroku’s CLI tools, store their sensitive API keys in a &lt;code&gt;~/.netrc&lt;/code&gt; file. This is a semi-standardized (&lt;a href="https://ec.haxx.se/usingcurl/usingcurl-netrc"&gt;or at least conventionalized&lt;/a&gt;) file used by a number of UNIX utilities.&lt;/p&gt;

&lt;p&gt;If you’re doing your development in a container, files like &lt;code&gt;~/.netrc&lt;/code&gt; can pose some problems, or at least prompt some considerations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don’t want to have to log in to Heroku (or whatever) every time you spin up or rebuild a devcontainer.&lt;/li&gt;
&lt;li&gt;Even though you’re not using them for production deploys (right??), you may still want to push devcontainer images to a repository for the convenience of your teammates or CI. You don’t want to be capturing sensitive login info in an image that’s proliferating to other machines and potential public.&lt;/li&gt;
&lt;li&gt;You may have a standard set of dotfiles that you fetch into your devcontainers (VSCode has &lt;a href="https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"&gt;a wonderful feature&lt;/a&gt;for this), but you probably keep your dotfiles in a public git repo. Not the sort of place you want to be putting API keys.&lt;/li&gt;
&lt;li&gt;There are lots of good reasons not to bind your entire home directory into a Docker container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using docker-compose, you can use something like the following to bind your host &lt;code&gt;~/.netrc&lt;/code&gt; into your dev container’s home directory at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  app:
    # ...
    volumes:
      - type: bind
        source: ${HOME}${USERPROFILE}/.netrc
        target: /root/.netrc
        # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can log in to Heroku (or whatever) &lt;em&gt;once&lt;/em&gt;, and reuse that login anytime you rebuild this container, as well as in any other containers you configure this way.&lt;/p&gt;

&lt;p&gt;Note the use of trying both &lt;code&gt;HOME&lt;/code&gt; and &lt;code&gt;USERPROFILE&lt;/code&gt; to keep things host platform-neutral. One of the perks of using devcontainers is that you can develop on any host OS!&lt;/p&gt;

&lt;p&gt;This example also makes the assumption that &lt;code&gt;/root&lt;/code&gt; is the user home directory inside the devcontainer. I tend to stick with the root (admin) user inside devcontainers, rather than defining a separate unprivileged account. All the considerations that would normally push me to use unprivileged user accounts are taken care of by the fact that I’m running inside an isolated container.&lt;/p&gt;

&lt;p&gt;(Don’t use docker-compose for your devcontainers? Consider adding it—even for an app that doesn’t depend on any other services! Just as &lt;code&gt;Dockerfile&lt;/code&gt; is a great way to document your app’s dependencies and runtime expectations as versioned code, &lt;code&gt;docker-compose.yml&lt;/code&gt; is a great way to document &lt;em&gt;how to start your devcontainer&lt;/em&gt; as versioned code.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Danger, Will Robinson!
&lt;/h3&gt;

&lt;p&gt;Now, there’s &lt;em&gt;one&lt;/em&gt; thing you need to be careful of when using this configuration: &lt;strong&gt;make sure ~/.netrc exists before you start your devcontainer&lt;/strong&gt;. The reason is that if it doesn’t already exist, Docker will create it… as a &lt;em&gt;directory&lt;/em&gt;. Oops.&lt;/p&gt;

&lt;p&gt;So just be sure to &lt;code&gt;touch ~/.netrc&lt;/code&gt; before you spin up your devcontainer, and you’ll be fine.&lt;/p&gt;

</description>
      <category>howto</category>
      <category>devcontainers</category>
      <category>docker</category>
    </item>
    <item>
      <title>Run Rails 6 System Tests in Docker Using a Host Browser</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Mon, 11 May 2020 18:40:10 +0000</pubDate>
      <link>https://dev.to/avdi/run-rails-6-system-tests-in-docker-using-a-host-browser-1ao4</link>
      <guid>https://dev.to/avdi/run-rails-6-system-tests-in-docker-using-a-host-browser-1ao4</guid>
      <description>&lt;p&gt;The problem: we wanted to get Rails system tests running using a browser on our development machines.&lt;/p&gt;

&lt;p&gt;The complication: we develop our Rails project within Docker, in order to make every aspect of our development environment reproduceable.&lt;/p&gt;

&lt;p&gt;The preference: we want to &lt;strong&gt;see&lt;/strong&gt; the browser-based tests running, but we don’t want to mess around with running browser &lt;em&gt;inside&lt;/em&gt; a container and tunneling its display to an external X-Windows server. We already have perfectly good browsers on our host machines, and we’d like to use them natively.&lt;/p&gt;

&lt;p&gt;The outcome: we can run Rails system tests inside a Docker container. It pops up Chrome windows on the host machine, and exercises our app!&lt;/p&gt;

&lt;p&gt;Coming up with the steps below involved a lot of trial-and-error, research, and debugging. I’m going to skip all that for this write-up, and just break down our working configuration piece by piece.&lt;/p&gt;

&lt;p&gt;I’ve also written up an &lt;a href="https://avdi.codes/rails-6-system-tests-from-top-to-bottom/"&gt;in-depth breakdown of what all these parts are, and how they work together in Rails system tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Disable the webdrivers gem
&lt;/h2&gt;

&lt;p&gt;The webdrivers gem is a lovely convenience for making sure you have the right browser and WebDriver tool installed before running system tests. However, we don’t want to run the browser inside the container. We want the container to talk to a browser either on the Docker host machine, or in a dedicated selenium sidecar container.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;webdrivers&lt;/code&gt; gem was causing system tests to fail with “couldn’t find browser” error. So we disabled it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile
​
group :test do
  # ...
  # webdrivers
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then ran &lt;code&gt;bundle install&lt;/code&gt; again to update &lt;code&gt;Gemfile.lock&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose a stable port for the test server
&lt;/h2&gt;

&lt;p&gt;By default Capybara picks a new random port to run the test web server on every time system tests are run. We want tools &lt;em&gt;outside&lt;/em&gt; the container to be able to consistently communicate with the test server.&lt;/p&gt;

&lt;p&gt;We arbitrarily chose port 3434, and documented that choice with an entry in a brand-new &lt;code&gt;.env&lt;/code&gt; file in the project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CAPYBARA\_SERVER\_PORT=3434
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Configure Docker-Compose for external browser testing
&lt;/h2&gt;

&lt;p&gt;Here’s an elided version of our &lt;code&gt;.devcontainer/docker-compose.yml&lt;/code&gt;, with the important elements commented.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Specify a recent version of docker-compose to ensure we have fancy environment 
# variable expansion
version: '3.2'
services:
  app:
    build: 
      context: .
      dockerfile: Dockerfile
​
    environment:
      # Convey which port Capybara should be starting test servers on; fail loudly
      # if we've neglected to configure it.
      CAPYBARA_SERVER_PORT: "${CAPYBARA_SERVER_PORT:-3434}"
      # The test server WILL NOT BE AVAILABLE from outside the container if it binds 
      # to 127.0.0.1. It must bind to 0.0.0.0 to be exposed to the outside world.
      CAPYBARA_SERVER_HOST: "${CAPYBARA_SERVER_HOST:-0.0.0.0}"

    volumes:
      - type: bind
        source: ..
        target: /workspace
        consistency: cached
​
    ports:
      # Expose the test server port. Fail loudly if CAPYBARA_SERVER_PORT isn't set.
      - "${CAPYBARA_SERVER_PORT:-3434}:${CAPYBARA_SERVER_PORT:-3434}"
      # Also expose the usual app port
      - "3000:3000"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Configure Capybara to use the test server and port from environment
&lt;/h2&gt;

&lt;p&gt;We added two lines to our &lt;code&gt;test/test_helper.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
​
# Use `fetch` to fail loudly if these variables aren't set. We might relax this 
# and set defaults at some point, but for the moment we want to make sure we didn't 
# miss a step.
Capybara.server_host = ENV.fetch("CAPYBARA_SERVER_HOST")
Capybara.server_port = ENV.fetch("CAPYBARA_SERVER_PORT")
​
class ActiveSupport::TestCase
  # Run tests in parallel with specified workers
  parallelize(workers: :number_of_processors)
​
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all
​
  # Add more helper methods to be used by all tests here...
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Update the system test base class to drive a remote browser
&lt;/h2&gt;

&lt;p&gt;The configuration so far makes sure that Capybara starts up the server-under-test in a way that external tools can find it and talk to it. But we still needed to tell Capybara to interact with use an external WebDriver, instead of trying to drive a browser inside the container.&lt;/p&gt;

&lt;p&gt;We updated our &lt;code&gt;test/application_system_test.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "test_helper"
​
class ApplicationSystemTestCase &amp;lt; ActionDispatch::SystemTestCase
  parallelize(workers: 1)
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400],
                       options: {
                         browser: :remote,
                         url: "http://host.docker.internal:9515",
                       }
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Figuring out the right options to pass into &lt;code&gt;driven_by&lt;/code&gt; was… nontrivial. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser: :remote&lt;/code&gt; are the magic options to tell Capybara &lt;em&gt;not&lt;/em&gt; to try to talk to a local WebDriver tool.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host.docker.internal&lt;/code&gt; is a special hostname that Docker sets up to refer to the host machine.&lt;/li&gt;
&lt;li&gt;Port &lt;code&gt;9515&lt;/code&gt; is the standard port that &lt;code&gt;chromedriver&lt;/code&gt; listens for commands on. In a future revision I think I’d like to capture this knowledge more explicitly in another environment variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Install ChromeDriver on the development host
&lt;/h2&gt;

&lt;p&gt;Our first development host happened to be a Windows machine so we used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco install -y chromedriver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instructions for installing ChromeDriver on various platforms can be found at &lt;a href="https://sites.google.com/a/chromium.org/chromedriver/"&gt;the ChromeDriver project home page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Start the chromedriver
&lt;/h2&gt;

&lt;p&gt;…and tell it to allow connections from anywhere. This is accomplished, unintuitively, by using the &lt;code&gt;--whitelisted-ips&lt;/code&gt; flag without any arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; chromedriver --whitelisted-ipsStarting ChromeDriver 81.0.4044.69 (6813546031a4bc83f717a2ef7cd4ac6ec1199132-refs/branch-heads/4044@{#776}) on port 9515All remote connections are allowed. Use a whitelist instead!Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Maybe later we’ll figure out how to provide a whitelist for just the dev container.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Run system tests inside the container
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ PARALLEL\_WORKERS=1 rails test:system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We found parallel system tests to be flakey, so we disable them with an environment variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Enjoy spooky automated browser action!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XoAcXY6J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i2.wp.com/avdi.codes/wp-content/uploads/2020/05/711E376C-280B-4D9F-B64B-A9DCBFAC90A0.gif%3Ffit%3D770%252C433%26ssl%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XoAcXY6J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i2.wp.com/avdi.codes/wp-content/uploads/2020/05/711E376C-280B-4D9F-B64B-A9DCBFAC90A0.gif%3Ffit%3D770%252C433%26ssl%3D1" alt="Animation of Rails system tests driving a browser"&gt;&lt;/a&gt; &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" alt="🍿"&gt;&lt;/a&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" alt="🍿"&gt;&lt;/a&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--khoQ-8b8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f37f.png" alt="🍿"&gt;&lt;/a&gt; &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>testing</category>
      <category>docker</category>
    </item>
    <item>
      <title>Rails 6 System Tests, From Top to Bottom</title>
      <dc:creator>Avdi Grimm</dc:creator>
      <pubDate>Mon, 11 May 2020 06:27:04 +0000</pubDate>
      <link>https://dev.to/avdi/rails-6-system-tests-from-top-to-bottom-28g7</link>
      <guid>https://dev.to/avdi/rails-6-system-tests-from-top-to-bottom-28g7</guid>
      <description>&lt;p&gt;A Rails 6 &lt;em&gt;&lt;a href="https://guides.rubyonrails.org/testing.html#system-testing"&gt;system test&lt;/a&gt;&lt;/em&gt; is a test that exercises your application in a way that, as much as possible, simulates a real user interacting with it via a browser. More than any other kind of tests, system tests verify that the whole app does what it’s supposed to do.&lt;/p&gt;

&lt;p&gt;As such, they are typically used for &lt;em&gt;acceptance testing&lt;/em&gt;: tests that verify that an app has fully implemented a specified feature. They are also useful for &lt;em&gt;smoke tests&lt;/em&gt;: high-level tests that exercise the code just enough to verify that nothing is badly broken. As well as &lt;em&gt;characterization tests&lt;/em&gt;: tests that capture an app’s current behavior in preparation for internal refactoring.&lt;/p&gt;

&lt;p&gt;Technically, a system test doesn’t have to interact with an &lt;em&gt;actual&lt;/em&gt; browser. They can be configured to use the &lt;a href="https://github.com/rack/rack-test"&gt;&lt;code&gt;rack_test&lt;/code&gt;&lt;/a&gt; backend, which simulates HTTP requests and parses the resulting HTML. While &lt;code&gt;rack_test&lt;/code&gt;-based system tests run faster and more reliably than frontend tests using a real browser, they are significantly limited in their ability to simulate a real user’s experience, because they can’t run JavaScript.&lt;/p&gt;

&lt;p&gt;For the rest of this article I’m going to be focusing on system tests that interact with your app via an actual browser.&lt;/p&gt;

&lt;p&gt;There are a lot of moving parts involved in running a browser-based system test. Until recently, I was pretty fuzzy on how all these parts fit together. Recently I set out to get browser-based system tests running for a Dockerized Rails app, and as a result I’ve learned a bit more about them.&lt;/p&gt;

&lt;p&gt;At a high level, a Rails system test putting an app through its paces using a browser breaks down as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;MiniTest&lt;/strong&gt; test case, augmented with…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capybara&lt;/strong&gt; testing helpers, which 

&lt;ul&gt;
&lt;li&gt;start and stop an instance of your app, and&lt;/li&gt;
&lt;li&gt;provide an English-like DSL on top of…&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;selenium-webdriver&lt;/strong&gt; gem, which provides a Ruby API for using the…&lt;/li&gt;
&lt;li&gt;… &lt;strong&gt;WebDriver protocol&lt;/strong&gt; in order to interact with…&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;WebDriver&lt;/strong&gt; tool such as &lt;strong&gt;chromedriver&lt;/strong&gt; or &lt;strong&gt;geckodriver&lt;/strong&gt; , which…&lt;/li&gt;
&lt;li&gt;Is automatically downloaded by the &lt;strong&gt;webdrivers&lt;/strong&gt; gem. The WebDriver tool automates…&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;browser&lt;/strong&gt; , such as Chrome.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The Geology of a System Test
&lt;/h1&gt;

&lt;p&gt;Let’s look at each of those in more detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  MiniTest
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/seattlerb/minitest"&gt;MiniTest&lt;/a&gt; is the unit testing library that Rails’ built-in testing framework is based on. It provides base classes for test cases, basic assertions like &lt;code&gt;assert_equal&lt;/code&gt;, and a runner to run tests and report on their success and failure.&lt;/p&gt;

&lt;p&gt;For Rails System Tests, Rails provides an &lt;code&gt;ApplicationSystemTestCase&lt;/code&gt; base class which is in turn based on &lt;code&gt;ActionDispatch::SystemTestCase&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "test_helper"
​
class ApplicationSystemTestCase &amp;lt; ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end
​
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This base class augments your system test cases with Capybara abilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capybara
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/teamcapybara/capybara"&gt;Capybara&lt;/a&gt; is a Ruby library for full-stack testing of web applications. It provides your tests with a few different capabilities.&lt;/p&gt;

&lt;p&gt;First, Capybara arranges for an instance of your app to be started before the tests start executing, and shuts it down once the tests have finished. In other words, for a Rails app it does the equivalent of running &lt;code&gt;rails server&lt;/code&gt; before executing the tests, and hitting Ctrl-C once they are done. To keep it separate and to ensure that the tests are run against the right test instance, Capybara starts your app on a different port than the one you’d normally use to manually interact it.&lt;/p&gt;

&lt;p&gt;Second, Capybara provides a high-level, English-style API for authoring system tests. Capybara’s DSL is what enables you to write your tests in terms similar to how you’d describe the interactions to a human:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;visit "/"
click_on "Log in"
fill_in "Username", with: "avdi"
fill_in "Password", with: "xyzzy"
click_on "Submit"
# ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In a sense, Capybara acts as a kind of stable pivot point for around which all the other aspects of testing a Ruby web application can vary. Capybara is neutral towards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The web app framework you’re building on&lt;/strong&gt;. Whether you’re building on Rails, Sinatra, Hanami, or raw Rack, you can test it with the help of Capybara.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The test tooling you prefer&lt;/strong&gt;. Whether your tests are written in MiniTest, RSpec, or Cucumber, they can all incorporate the Capybara helper methods for controlling a simulated browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The type of browser simulation/automation.&lt;/strong&gt; Capybara lets you switch out “drivers” to automate different kinds of browsers, real or simulated. The same Capybara tests can be run against bare Rack requests, actual Chrome, Safari, or Firefox browsers; “headless” versions of those browsers, or simulated browsers such as PhantomJS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When Capybara is automating a real browser, it does so by translating calls to its the high-level Capybara DSL (&lt;code&gt;click_on&lt;/code&gt;, &lt;code&gt;fill_in&lt;/code&gt;) into invocations of a library called &lt;code&gt;selenium-webdriver&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  selenium-webdriver
&lt;/h2&gt;

&lt;p&gt;Automatic control of a browser is typically accomplished using a &lt;em&gt;WebDriver tool&lt;/em&gt;, which is a standalone executable for remote-controlling a particular browser. We’ll talk more about WebDriver executables in a minute.&lt;/p&gt;

&lt;p&gt;WebDriver executables all speak the &lt;a href="https://www.w3.org/TR/webdriver2/"&gt;WebDriver protocol&lt;/a&gt;, an HTTP-based protocol for automating browsers. Capybara doesn’t speak WebDriver directly. Instead, it makes use of a library called &lt;a href="https://www.selenium.dev/selenium/docs/api/rb/index.html"&gt;&lt;code&gt;selenium-webdriver&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What’s with the name “selenium”? The &lt;a href="https://www.selenium.dev/"&gt;Selenium project&lt;/a&gt; is the umbrella under which the WebDriver protocol for controlling browsers was first developed. This protocol is now &lt;a href="https://www.w3.org/TR/webdriver2/"&gt;a W3C standards track spec&lt;/a&gt;. The selenium project still maintains various language bindings to the WebDriver protocol. The &lt;code&gt;selenium-webdriver&lt;/code&gt; gem contains the Ruby webdriver bindings.&lt;/p&gt;

&lt;p&gt;Here’s an example of using &lt;code&gt;selenium-webdriver&lt;/code&gt; directly, that I swiped from the project documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'selenium-webdriver'
​
driver = Selenium::WebDriver.for :firefox
wait = Selenium::WebDriver::Wait.new(timeout: 10)
​
begin
  driver.get 'https://google.com/ncr'
  driver.find_element(name: 'q').send_keys 'cheese', :return
  first_result = wait.until { driver.find_element(css: 'h3&amp;gt;div') }
  puts first_result.attribute('textContent')
ensure
  driver.quit
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see how it’s a bit lower-level than the Capybara example further up. The &lt;code&gt;selenium-webdriver&lt;/code&gt; library translates these calls into WebDriver Protocol, which it speaks to a webdriver executable.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebDriver Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/TR/webdriver2/"&gt;WebDriver&lt;/a&gt; is an HTTP-based protocol for telling browsers what to do. For instance, in order to create a Chrome browser window and tell it to navigate to google.com, you would first start up a &lt;code&gt;chromedriver&lt;/code&gt; process. Then, you’d send it a “new session” command with an HTTP post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST 'http://127.0.0.1:9515/session' -d '{"capabilities":{"firstMatch":[{"browserName":"chrome"}]}}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This returns a bunch of data, including a session ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ ... "sessionId":"98de922f9be6784957775f25618c60a3" ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which we follow up with a “navigate to” command in the form of POSTing a new &lt;code&gt;url&lt;/code&gt; to the created session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST 'http://127.0.0.1:9515/session/97039faaa396941517d800b75d0f6096/url' -d '{"url": "https://google.com"}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is the level of tedium that the &lt;code&gt;selenium-webdriver&lt;/code&gt; gem is abstracting over with its Ruby bindings.&lt;/p&gt;

&lt;p&gt;You can’t send these WebDriver commands directly to a browser, however. You have to send them to a WebDriver process.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebDriver Tool
&lt;/h2&gt;

&lt;p&gt;“WebDriver” is a protocol. &lt;em&gt;A&lt;/em&gt; WebDriver, on the other hand, refers to a tool that speaks that protocol and controls a browser. If you want to drive a browser with WebDriver protocol, you need to first download that browser’s WebDriver tool.&lt;/p&gt;

&lt;p&gt;WebDriver tools act as servers: when you execute them, they start a persistent process that listens for HTTP requests until it is terminated.&lt;/p&gt;

&lt;p&gt;For every major browser, there is an associated WebDriver. Chrome has &lt;a href="https://sites.google.com/a/chromium.org/chromedriver/home"&gt;&lt;code&gt;chromedriver&lt;/code&gt;&lt;/a&gt;. Firefox has &lt;a href="https://github.com/mozilla/geckodriver"&gt;&lt;code&gt;geckodriver&lt;/code&gt;&lt;/a&gt;. MS Edge has &lt;a href="https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/"&gt;&lt;code&gt;edgedriver&lt;/code&gt;&lt;/a&gt;. Safari has &lt;a href="https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari"&gt;&lt;code&gt;safaridriver&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While they speak a common language, WebDriver tools aren’t all developed by the same people. Each browser’s WebDriver is a separate project, usually associated with the browser’s development team. Each WebDriver’s team typically provides binaries for every major platform that browser is available on.&lt;/p&gt;

&lt;p&gt;Finding, downloading, and installing the appropriate WebDriver tool somewhere in your &lt;code&gt;PATH&lt;/code&gt; is a hassle. That’s why Rails 6 projects, by default, use the &lt;code&gt;webdrivers&lt;/code&gt; gem to automatically download an up-to-date WebDriver tool that corresponds to Capybara’s configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The webdrivers gem
&lt;/h2&gt;

&lt;p&gt;This part of the stack is really just a convenience. When the &lt;a href="https://github.com/titusfortner/webdrivers"&gt;webdrivers&lt;/a&gt; gem is required, it automatically augments the &lt;code&gt;selenium-webdriver&lt;/code&gt; gem. Now when you run &lt;code&gt;selenium-webdriver&lt;/code&gt; tests, it automatically determines &lt;em&gt;which&lt;/em&gt; WebDriver executable needs to be downloaded for your platform and selected browser, downloads it, and arranges for that executable to be used by &lt;code&gt;selenium-webdriver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the WebDriver tool downloaded, your tests can drive a browser!&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser
&lt;/h2&gt;

&lt;p&gt;Once you have a WebDriver tool installed, the parts farther up the stack can use it to automate browser interactions. As system tests are run, you’ll see new browser windows appear, navigate to pages in your app, fill in forms, follow links, etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;It takes a lot of layers to make a Rails 6 system test work! There are a lot of moving parts, and a lot of opportunities for things to go wrong. Personally, I’ve spent a quite a bit of time fiddling with system test configuration in order to get them running consistently. Once they are running though, it’s a particularly satisfying feeling to watch your app being put through its paces automatically.&lt;/p&gt;

&lt;p&gt;Once you’ve managed to get system tests running in real browser windows locally, you can move on to the next step of getting them to run “headless”. Then you can have a continuous integration server such as CircleCI automatically run those slow-but-important system tests for you after every commit.&lt;/p&gt;

&lt;p&gt;But that’s a topic for another article!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
