<?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: Joe McCann</title>
    <description>The latest articles on DEV Community by Joe McCann (@joemccanndev).</description>
    <link>https://dev.to/joemccanndev</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%2F782939%2F74d7f091-b48c-485a-b881-2840163750d3.jpeg</url>
      <title>DEV Community: Joe McCann</title>
      <link>https://dev.to/joemccanndev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joemccanndev"/>
    <language>en</language>
    <item>
      <title>Using Ruby to Translate Roman Numerals to Integers</title>
      <dc:creator>Joe McCann</dc:creator>
      <pubDate>Tue, 06 Sep 2022 20:43:38 +0000</pubDate>
      <link>https://dev.to/joemccanndev/using-ruby-to-translate-roman-numerals-to-integers-3go6</link>
      <guid>https://dev.to/joemccanndev/using-ruby-to-translate-roman-numerals-to-integers-3go6</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@climatechangevi?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Karl Callwood&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/roman-numeral?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Spoiler Alert&lt;/em&gt;&lt;/strong&gt;: This post reveals a solution to a common coding problem. Here is a link to the problem on &lt;a href="https://leetcode.com/problems/roman-to-integer/"&gt;leetcode&lt;/a&gt; with instructions if you'd like to solve it your own way first before reading on. Otherwise, join me below!&lt;/p&gt;

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

&lt;p&gt;Recently, I have been trying to maintain my coding chops by regularly submitting leetcode and codewars solutions. I thought I'd start with the 'easy' ones first to warm up. This one was categorized as easy but only has a 58% acceptance rate. &lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the problem
&lt;/h3&gt;

&lt;p&gt;Basically, the goal is to write a function, that, given a Roman numeral string as an input, outputs the equivalent integer, e.g., &lt;code&gt;"MCMXCIV" =&amp;gt; 1994&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thinking about data structures.
&lt;/h3&gt;

&lt;p&gt;My first thought was that I should map the fundamental Roman numerals to their equivalent values using a hash map, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;numerals&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'I'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'V'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'X'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'L'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'C'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'D'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'M'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I noted I am also dealing with a string and likely an array.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manipulating strings and arrays
&lt;/h3&gt;

&lt;p&gt;I realized I will also need to evaluate the characters of the string. Ruby has a handy &lt;code&gt;string#each_char&lt;/code&gt; method that iterates over each individual character of a string, so &lt;code&gt;'VIII'&lt;/code&gt; would be split into an array of characters as &lt;code&gt;['V', 'I', 'I', 'I' ]&lt;/code&gt;. Now that I am working with an array, I have access to the &lt;code&gt;array#map&lt;/code&gt; method, which can be used to map each numeral character to its value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [5, 1, 1, 1]&lt;/span&gt;

&lt;span class="c1"&gt;# using built-in array#sum method&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt; [5, 1, 1, 1].sum&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 8&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;So we're done right? We've basically covered the process we already do in our head (5 + 1 + 1 + 1 = 8)&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;p&gt;However, the tricky part comes when Roman numerals signify a number that is one factor less than a multiple of 5 or 10, e.g. IV is one less than V and means subtract one from five and return four. From the exercise instructions linked above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can be placed before V (5) and X (10) to make 4 and 9. &lt;/li&gt;
&lt;li&gt;X can be placed before L (50) and C (100) to make 40 and 90. &lt;/li&gt;
&lt;li&gt;C can be placed before D (500) and M (1000) to make 400 and 900.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This throws a wrench in our plans to simply sum the mapped characters of the string, because the above method does not "group" the numerals in anyway. Perhaps there is a way to extract those special cases, e.g. IX and CM.&lt;/p&gt;

&lt;p&gt;First, let's also define them in a hash like the one above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;special_numerals&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'IV'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'IX'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'XL'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'XC'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'CD'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'CM'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The scan method
&lt;/h3&gt;

&lt;p&gt;Now we need a way to scan the string and check for these special numerals. Thankfully, Ruby makes it easy with the &lt;code&gt;string#scan&lt;/code&gt; method which returns an array of matches found in the string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MCMXCIV"&lt;/span&gt; &lt;span class="c1"&gt;# 1994&lt;/span&gt;
  &lt;span class="n"&gt;special_matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/IV|IX|XL|XC|CD|CM/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; ["CM", "XC", "IV"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to use the same "map and sum" logic used above. But what to do with the remaining "non-special" characters. This will potentially result in counting some numerals twice. This can be prevented with a simple check.&lt;/p&gt;

&lt;p&gt;First, check if there are any special numerals used in the string. If so, map them to their values and sum them. Record this sum in a variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The gsub method
&lt;/h3&gt;

&lt;p&gt;Second, remove those "special" numerals from the string so that the remaining numerals can be summed accurately. This is where the handy &lt;code&gt;string#gsub&lt;/code&gt; method comes in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MCMXCIV"&lt;/span&gt; &lt;span class="c1"&gt;# 1994&lt;/span&gt;
  &lt;span class="n"&gt;special_matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/IV|IX|XL|XC|CD|CM/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; ["CM", "XC", "IV"]&lt;/span&gt;
  &lt;span class="n"&gt;special_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;special_matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;special_numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; 900 + 90 + 4 == 994&lt;/span&gt;
  &lt;span class="n"&gt;special_matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# s is modified in place with its special numerals removed.&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; s == "M"&lt;/span&gt;
  &lt;span class="c1"&gt;# we still need to add 1000 to our result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so, the input string is now "M" and can be summed using the same logic. In this case, there is only one character, but the logic is the same for multiple characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;normal_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;  
  &lt;span class="c1"&gt;# =&amp;gt; numerals['M'] == 1000&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; [1000].sum&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; 1000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;Putting it all together we return the "special" numeral sum plus the "normal" numeral sum if there are any "special" numerals in the string. Otherwise, we return the "normal" sum. In code, I translated this logic to the following solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roman_to_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;special_matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/IV|IX|XL|XC|CD|CM/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;special_matches&lt;/span&gt;
    &lt;span class="n"&gt;special_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;special_matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;special_numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;
    &lt;span class="n"&gt;special_matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;normal_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numerals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;special_sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;normal_sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;special_sum&lt;/span&gt;

  &lt;span class="n"&gt;normal_sum&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;numerals&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'I'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'V'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'X'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'L'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'C'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'D'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'M'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;special_numerals&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'IV'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'IX'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'XL'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'XC'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'CD'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'CM'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;roman_to_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MCMXCIV"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 1994&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I do not think this is the fastest solution on leetcode, but I personally find it readable. This is a testament to the power of the hash data structure, which made it straighforward to map a numeral to its integer value. &lt;/p&gt;

&lt;p&gt;How did you solve this problem or what could I do differently? Let me know in the comments below!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>leetcode</category>
      <category>programming</category>
    </item>
    <item>
      <title>Adding a Users Search to a Rails Facebook Clone</title>
      <dc:creator>Joe McCann</dc:creator>
      <pubDate>Sun, 16 Jan 2022 06:38:40 +0000</pubDate>
      <link>https://dev.to/joemccanndev/adding-a-users-search-to-a-rails-facebook-clone-237a</link>
      <guid>https://dev.to/joemccanndev/adding-a-users-search-to-a-rails-facebook-clone-237a</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@waguluz_?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Andreas Wagner&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/1298282/epic-search?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I recently completed the core functionality of my Rails Facebook clone project, &lt;a href="https://github.com/joe-mccann-dev/gembook"&gt;Gembook&lt;/a&gt;. I recently decided to add a rudimentary search form to the &lt;code&gt;Users#index&lt;/code&gt; page, allowing users to search for other users by name. I knew this would involve playing around with &lt;code&gt;routes.rb&lt;/code&gt;, and that I would need a form to submit a get request to the route created in &lt;code&gt;routes.rb&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does this post cover?
&lt;/h3&gt;

&lt;p&gt;We will cover the steps necessary to implement a basic search feature. This will make it easier for users to find their friends, but the approach used here can be applied to any application where a user might want to search a collection.&lt;/p&gt;

&lt;p&gt;This post will cover the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adding a route to handle search requests&lt;/li&gt;
&lt;li&gt;Creating the &lt;code&gt;User.search&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;Handling the search request in the &lt;code&gt;UsersController&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Submitting the  Search Form and Displaying Results&lt;/li&gt;
&lt;li&gt;Testing this behavior with RSpec&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1 | Adding a Route to Handle Search Requests
&lt;/h2&gt;

&lt;p&gt;I am using resourceful routes for this application. Since we are searching for users it makes sense to nest the &lt;code&gt;search&lt;/code&gt; route within &lt;code&gt;resources :users&lt;/code&gt;. For an excellent guide to routing in Rails, see the &lt;a href="https://guides.rubyonrails.org/routing.html"&gt;official guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's break down &lt;code&gt;get 'search', to: 'users#index', on: :collection&lt;/code&gt;. The snippet below allows get requests to &lt;code&gt;/users/search&lt;/code&gt; and dispatches those requests to the index action of the &lt;code&gt;UsersController&lt;/code&gt;. It will also create the &lt;code&gt;search_users_url&lt;/code&gt; and &lt;code&gt;search_users_path&lt;/code&gt; route helpers, which we will use later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'users#index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :collection&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ss"&gt;:profile&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before submitting search requests, we need a way for requests to be processed. We are searching for users, so it makes sense that we involve the User model, as that is the model that knows everything about a given user, including their name, which is how a user will search for other users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 | Creating the Search method
&lt;/h2&gt;

&lt;p&gt;My User model includes both a &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; column. The &lt;code&gt;User.search&lt;/code&gt; method will perform an Active Record query using the &lt;code&gt;query&lt;/code&gt; argument. Unnecessary database calls are prevented by the guard clause &lt;code&gt;return unless query&lt;/code&gt;. The &lt;code&gt;query&lt;/code&gt; argument is assigned to &lt;code&gt;name&lt;/code&gt;, stripped away of any whitespace, converted to lower-case, and split into an array. Users can then search by first or last name and the method will return matches for either or both. The &lt;code&gt;lower&lt;/code&gt; function is an &lt;a href="https://www.w3schools.com/sql/func_sqlserver_lower.asp"&gt;SQL function&lt;/a&gt; that converts a string to lower-case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# app/models/user.rb&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;

    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;
    &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lower(first_name) = ? OR lower(last_name) = ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 | Handling the search request in the UsersController
&lt;/h2&gt;

&lt;p&gt;Because Rails' controllers handle all incoming requests from the router, when a get request is submitted to &lt;code&gt;/users/search&lt;/code&gt;, the request will be dispatched to the &lt;code&gt;users#index&lt;/code&gt; action as shown above. The controller also can create an instance variable, in this case &lt;code&gt;@results&lt;/code&gt;, that is accessible to the View.&lt;/p&gt;

&lt;p&gt;Below, the controller sends a message to the &lt;code&gt;User&lt;/code&gt; class (&lt;code&gt;:search&lt;/code&gt;) with an argument from the params hash, &lt;code&gt;params[:query]&lt;/code&gt; and assigns the results to the &lt;code&gt;@results&lt;/code&gt; instance variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# app/controllers/users_controller.rb&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="c1"&gt;# other instance variables etc...&lt;/span&gt;
    &lt;span class="vi"&gt;@results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 | Submitting the Search Form and Displaying Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Submitting the Search Form
&lt;/h3&gt;

&lt;p&gt;Here is where all the above setup pays off. Using the &lt;code&gt;form_with&lt;/code&gt; helper, we can submit a get request to the &lt;code&gt;search_users_path&lt;/code&gt;, triggering the index action of the &lt;code&gt;UsersController&lt;/code&gt; (remember our route: &lt;code&gt;get 'search', to: 'users#index', on: :collection&lt;/code&gt;). As seen in Step 3, the index action includes a call to the &lt;code&gt;User.search&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;search_users_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: :get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;placeholder: &lt;/span&gt;&lt;span class="s1"&gt;'Search for users by name.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s1"&gt;'Search'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon submission of the form, the &lt;code&gt;UsersController&lt;/code&gt; will have access to the params hash, specifically &lt;code&gt;params[:query]&lt;/code&gt;. For example, consider a user named John Hancock. His friend, Thomas Jefferson, wants to search for him using Gembook. Mr. Jefferson enters 'John' into the text field. The query of 'John' gets passed to the &lt;code&gt;UsersController&lt;/code&gt; as &lt;code&gt;{"query"=&amp;gt;"John"}&lt;/code&gt; and an Active Record query is performed in the User model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Displaying Results
&lt;/h3&gt;

&lt;p&gt;To display the search results, we can create a partial: &lt;code&gt;app/views/users/_result.html.erb&lt;/code&gt;. This allows utilization of the &lt;code&gt;:collection&lt;/code&gt; option. For more on this, see the &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-collections"&gt;official guide&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/users/index.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Search Results&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;collection: &lt;/span&gt;&lt;span class="vi"&gt;@results&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:em&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"No users found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;none?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you display in your result partial will depend on your application.&lt;/p&gt;

&lt;p&gt;Mine looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt; &lt;span class="c"&gt;&amp;lt;!-- app/views/users/_result.html.erb --&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'friend'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;friend: &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result object is sent to a different partial depending on whether or not the current user is friends with the search result. If they are friends, an "unfriend" button will appear. If they are not friends, an "Add Friend" button will appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 | Testing with RSpec
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unit testing User.search
&lt;/h3&gt;

&lt;p&gt;Before writing a system spec, let's make sure this method behaves as expected with a unit test in &lt;code&gt;user_spec.rb&lt;/code&gt;. A variable called &lt;code&gt;expected results&lt;/code&gt; holds an Active Record relation returned from the test database. A variable called &lt;code&gt;search_results&lt;/code&gt; holds the ActiveRecord relation returned by the &lt;code&gt;User.search&lt;/code&gt; method. We then assert that &lt;code&gt;user&lt;/code&gt;, who is a member of &lt;code&gt;expected_results&lt;/code&gt;, is included in &lt;code&gt;search_results&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="c1"&gt;# spec/models/user_spec.rb&lt;/span&gt;
  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :model&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_seed&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;let!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# several other specs etc...&lt;/span&gt;

    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'.search'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'accepts a query string and returns user results'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expected_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;search_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_results&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing a System Spec
&lt;/h3&gt;

&lt;p&gt;For the system spec, we want to simulate user interaction with the application. To achieve this, we can create two test users, Thomas and John, and have Thomas search for John. We will then assert our expectation that a valid search returns a matching user and that a search for a non-existent user comes up empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/system/search_users_spec.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"SearchUsers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;driven_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rack_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;let!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Thomas'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Jefferson'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'thomas@jefferson.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'foobar'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:other_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Hancock'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'john@hancock.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'foobar'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'searching for a user'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'a user is logged in at users#index'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;login_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: :user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;users_path&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'allows them to enter a query and shows them results'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;other_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;
        &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;
        &lt;span class="n"&gt;click_on&lt;/span&gt; &lt;span class="s1"&gt;'Search'&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Search Results'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"Shows 'No users found' if there are no matches"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Joe'&lt;/span&gt;
        &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;
        &lt;span class="n"&gt;click_on&lt;/span&gt; &lt;span class="s1"&gt;'Search'&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Search Results'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'No users found'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;In this post, I attempted to show the process of adding a user search feature. In retrospect, I think a feature like this would be an excellent candidate for TDD. If I were to use TDD to implement this feature, I would probably start with the unit test and then write the method until the test passed, refactoring where appropriate. Next, I would write the system specs, and finally, write the form and view code to make them pass.&lt;/p&gt;

&lt;p&gt;Writing your own search method is a good way to grapple with core Rails functionality. This feature utilizes the  Rails router, Model, View, and Controller, all of which are working hard to display search results to your users.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post. If there is anything I did incorrectly or that could be improved, please let me know in the comments! I love to see different and better approaches to the same problem. Thanks and happy coding!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>testing</category>
      <category>webdev</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Extracting a Database Query from a Rails View</title>
      <dc:creator>Joe McCann</dc:creator>
      <pubDate>Thu, 30 Dec 2021 06:47:55 +0000</pubDate>
      <link>https://dev.to/joemccanndev/extracting-a-database-query-from-a-rails-view-gge</link>
      <guid>https://dev.to/joemccanndev/extracting-a-database-query-from-a-rails-view-gge</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;While some logic in Rails views is inevitable, recently I have been trying to move any unnecessary logic in the view to the model or controller--especially if it's a database query. This problem came into &lt;em&gt;view&lt;/em&gt; while I was coding a Friendship "Accept" button in a Rails Facebook clone.&lt;/p&gt;

&lt;p&gt;I had it setup so that users could accept or decline friend requests from the Notifications#index page. The problem was I had to locate the respective friendship object from within Notifications#index. This required a database query. Once I wrote the correct database query in the view to find the Friendship object associated with a notification, I started thinking of ways to remove it from the view. Below is how I approached the problem.&lt;/p&gt;

&lt;p&gt;Every notification references a &lt;code&gt;sender&lt;/code&gt; and a &lt;code&gt;receiver&lt;/code&gt;, both belonging to the User class. Similarly, a Friendship object is initiated with a &lt;code&gt;sender&lt;/code&gt; and a &lt;code&gt;receiver&lt;/code&gt;. If a user has many friend requests on their notifications page, it is necessary to determine &lt;em&gt;which specific&lt;/em&gt; friend request is being accepted or declined when the receiver clicks "Accept" or "Decline", respectively.&lt;/p&gt;

&lt;p&gt;Therefore, when rendering the notifications collection, we can find the soon-to-be-accepted or soon-to-be-declined Friendship with &lt;code&gt;notification.sender&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;notification.receiver&lt;/code&gt;. The "Accept" button is as follows:&lt;/p&gt;
&lt;h3&gt;
  
  
  Initial "Accept" Button
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;friendship&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Friendship&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sender_id: &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                     &lt;span class="ss"&gt;receiver_id: &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;button_to&lt;/span&gt; &lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;friendship_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;friendship&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="ss"&gt;method: :put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;friendship: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s1"&gt;'accepted'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can eliminate the need for the local variable by creating a hash of all friend requests sent to the current user, mapped to the &lt;code&gt;sender_id&lt;/code&gt;. The current user will always be the receiver in this situation and to find the Friendship request sent to the current user, we only need the &lt;code&gt;sender_id&lt;/code&gt;. I thought it made sense to make this method an instance method on User but there are probably better ways to do this. Let me know in the comments! Here are the relevant models and associations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Relevant Models
&lt;/h2&gt;
&lt;h3&gt;
  
  
  User, Friendship, Notification
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:sent_notifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Notification'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="s1"&gt;'sender_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:received_notifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Notification'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="s1"&gt;'receiver_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:sent_pending_requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;friendship_pending&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Friendship'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="s1"&gt;'sender_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:received_pending_requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;friendship_pending&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Friendship'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="s1"&gt;'receiver_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Friendship&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="sx"&gt;%i[pending accepted declined]&lt;/span&gt;

  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:friendship_pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: :pending&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And now the method that maps a sender id to a Friendship object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="c1"&gt;#...associations, etc.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;requests_via_sender_id&lt;/span&gt;
      &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;received_pending_requests&lt;/span&gt;
      &lt;span class="n"&gt;sender_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sender_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;sender_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sender_id: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say &lt;code&gt;received_pending_requests.count == 1&lt;/code&gt; at the moment. A user with an id of 3 has sent &lt;code&gt;current_user&lt;/code&gt; a request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requests_via_sender_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;Friendship:0x000055f71022ec20&lt;/span&gt;
   &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;sender_id: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;receiver_id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The good thing about doing it this way is that the controller can now request this information from the User model prior to rendering the Notifications#index view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
      &lt;span class="vi"&gt;@notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;received_notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;%i[sender receiver]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@friendships&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requests_via_sender_id&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; returns a hash&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can return to our "Accept" button which has access to the current notification being rendered.&lt;br&gt;
Note the absence of the &lt;code&gt;friendship&lt;/code&gt; local variable:&lt;/p&gt;

&lt;h3&gt;
  
  
  Final "Accept" Button
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;button_to&lt;/span&gt; &lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;friendship_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@friendships&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
              &lt;span class="ss"&gt;method: :put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;friendship: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s1"&gt;'accepted'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Another added benefit of this approach is improved readability. Which friendship are we updating? The one the sender of a notification requested --&amp;gt;&lt;br&gt;
&lt;code&gt;friendship_path(@friendships[notification.sender.id])&lt;/code&gt;. This is arguably more expressive than &lt;code&gt;friendship_path(friendship)&lt;/code&gt;, because it provides the context in which we are searching for a Friendship object.&lt;/p&gt;

&lt;p&gt;Balancing responsibilities between the Model, View, and Controller is sometimes challenging, but making your way towards a solution piece by piece can be very educational and rewarding.&lt;/p&gt;

&lt;p&gt;This was my first blog post. Thank you so much for reading. Having finished it I realize the benefit it has in regards to understanding. I hope to write more posts in the near future.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@hookie1001?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Lawrence Hookham&lt;/a&gt; on&lt;br&gt;
&lt;a href="https://unsplash.com/s/photos/rails-view?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>database</category>
      <category>programming</category>
    </item>
    <item>
      <title>Setting Up OmniAuth Authentication in Development</title>
      <dc:creator>Joe McCann</dc:creator>
      <pubDate>Thu, 30 Dec 2021 06:08:41 +0000</pubDate>
      <link>https://dev.to/joemccanndev/setting-up-omniauth-authentication-in-development-5d89</link>
      <guid>https://dev.to/joemccanndev/setting-up-omniauth-authentication-in-development-5d89</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;"Who are you?" may be the line sung by Roger Daltrey of The Who in their 1978 hit &lt;em&gt;Who Are You&lt;/em&gt;, but it is also the fundamental question that Authentication answers.&lt;/p&gt;

&lt;p&gt;Authentication is the process of determining whether or not a user is who they claim to be. You are likely familiar with the basic process. You sign up on a website with an email and password. The next time you log in you will be prompted for your email and password. If both your email and password are correct, the application verifies or &lt;em&gt;authenticates&lt;/em&gt; your identity.&lt;/p&gt;

&lt;p&gt;But what if we've already gone through this process on another site? Wouldn't it be nice if we could somehow communicate with that site's authentication system and use it to authenticate the users on our site? This is precisely what &lt;a href="https://github.com/omniauth/omniauth" rel="noopener noreferrer"&gt;OmniAuth&lt;/a&gt; allows us to do, specifically through the use of community-built strategies, or code that allows one to authenticate to a given provider, e.g. Facebook or GitHub. You know those "Login with Facebook" buttons? Yep, that's what we're talking about here.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does this post cover?
&lt;/h3&gt;

&lt;p&gt;In this post, I will go over the steps I took to authenticate to GitHub in a Rails development environment using the &lt;a href="https://github.com/omniauth/omniauth-github" rel="noopener noreferrer"&gt;omniauth-github&lt;/a&gt; gem, "the official OmniAuth strategy for authenticating to GitHub", along with &lt;a href="https://github.com/heartcombo/devise" rel="noopener noreferrer"&gt;Devise&lt;/a&gt;, and the &lt;a href="https://github.com/laserlemon/figaro" rel="noopener noreferrer"&gt;figaro&lt;/a&gt; gem.&lt;/p&gt;

&lt;p&gt;For some OAuth providers, you will need to install something like &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a nifty tool that exposes your local WebHost to the internet.&lt;/p&gt;

&lt;p&gt;This guide will assume you already have Devise authentication setup for your app. See the link above for installation instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;We will go over the following steps in more detail below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing the &lt;a href="https://github.com/laserlemon/figaro" rel="noopener noreferrer"&gt;figaro&lt;/a&gt; gem to securely store our OAuth Client ID and Client Secret.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/settings/developers" rel="noopener noreferrer"&gt;Registering a new OAuth&lt;/a&gt; app with GitHub to use the GitHub API.&lt;/li&gt;
&lt;li&gt;Configuring Omniauth GitHub and Devise.&lt;/li&gt;
&lt;li&gt;(Optional) Installing &lt;a href="https://dashboard.ngrok.com/get-started/setup" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; to expose localhost to the internet and obtain a functional callback URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: setting up an ngrok tunnel is only necessary if your OAuth provider does not allow local callback URLs.&lt;/em&gt;&lt;br&gt;
GitHub &lt;em&gt;does&lt;/em&gt; allow localhost callback URLs, so I will include this step as a courtesy if your OAuth provider disallows localhost callback URLs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 | Installing Figaro
&lt;/h2&gt;

&lt;p&gt;The first step is fairly straightforward. We need a method for securely storing the keys we will obtain in Step 2. Figaro does this by creating a &lt;code&gt;config/application.yml&lt;/code&gt; file and adding it to &lt;code&gt;.gitignore&lt;/code&gt;. This way your secret key will never be pushed to GitHub and will stay on your local machine.&lt;/p&gt;

&lt;p&gt;To install Figaro:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add it your Gemfile: &lt;code&gt;gem 'figaro'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;bundle install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install figaro: &lt;code&gt;bundle exec figaro install&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the gem &lt;a href="https://github.com/laserlemon/figaro" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more details. We will return to the &lt;code&gt;config/application.yml&lt;/code&gt; file generated by &lt;code&gt;bundle exec figaro install&lt;/code&gt; after we register a new OAuth app on GitHub (Step 2).&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 | Registering an OAuth App with GitHub
&lt;/h2&gt;

&lt;p&gt;To authenticate to GitHub, we must tell GitHub who we are and that we will be requesting information from its API. To do this, we must register our application as a new OAuth app. Navigate to &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Login and under your profile picture, click on Settings. Scroll down a bit and click on Developer Settings. Click on OAuth Apps, and finally "New OAuth App".&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name your application&lt;/li&gt;
&lt;li&gt;Under "Homepage URL", enter &lt;code&gt;http://localhost:3000&lt;/code&gt; as this is the homepage of your application in development.&lt;/li&gt;
&lt;li&gt;For "Authorization callback URL", enter &lt;code&gt;http://localhost:3000/users/auth/github/callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click "Register Application".&lt;/li&gt;
&lt;li&gt;Take note of your client id and client secret.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Saving client id and secret with Figaro
&lt;/h3&gt;

&lt;p&gt;In your Rails application, navigate to &lt;code&gt;config/application.yml&lt;/code&gt; and save your client id and secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;GITHUB_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt; client id &amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;GITHUB_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt; client secret &amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 | Configuring Omniauth GitHub and Devise
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Follow the directions in the &lt;a href="https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview" rel="noopener noreferrer"&gt;Devise Wiki&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Some notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The examples use omniauth-facebook, but you can essentially replace any occurrence of 'facebook' with 'github', or 'twitter', etc.&lt;/li&gt;
&lt;li&gt;scope entails the permissions you request of the authenticated user. "Access to email, profile info, etc." &lt;code&gt;scope: 'user,public_repo'&lt;/code&gt; below grants access to basic profile information and all public repo information. GitHub provides these scopes in their &lt;a href="https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps" rel="noopener noreferrer"&gt;docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Don't forget to add a "Sign in with GitHub" link.&lt;/li&gt;
&lt;li&gt;for the &lt;code&gt;config/initializers/devise.rb&lt;/code&gt; part, your entry is accessing the environment variables set by Figaro:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;omniauth&lt;/span&gt; &lt;span class="ss"&gt;:github&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GITHUB_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GITHUB_SECRET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'user,public_repo'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4 (Optional) | Installing Ngrok
&lt;/h2&gt;

&lt;h3&gt;
  
  
  why install ngrok?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ngrok is a tool that exposes a local web server to the internet. The colloquial term for this is a &lt;em&gt;tunnel&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;ngrok allows us to use a callback URL in development when an OAuth provider does not allow local callback URLs.&lt;/li&gt;
&lt;li&gt;The callback URL is essentially where a user is redirected after authenticating to a third party service.&lt;/li&gt;
&lt;li&gt;Some OAuth providers require that this URL be public, thus the need for a tool like ngrok.&lt;/li&gt;
&lt;li&gt;After an application, located at &lt;code&gt;localhost:3000&lt;/code&gt; sends a request for authentication to the OAuth provider's API, the OAuth provider "calls back" the publicly exposed WebHost's URL and the application now has an authenticated user. &lt;/li&gt;
&lt;li&gt;For GitHub, we did not need to publicly expose our localhost server. We simply used &lt;code&gt;http://localhost:3000/users/auth/github/callback&lt;/code&gt; in Step 2 above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install ngrok follow the &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;snap install ngrok&lt;/code&gt; (Ubuntu) or &lt;code&gt;brew install ngrok/ngrok/ngrok&lt;/code&gt; (Mac)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dashboard.ngrok.com/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for an account to get your authtoken.&lt;/li&gt;
&lt;li&gt;From your home directory: &lt;code&gt;ngrok authtoken &amp;lt;token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start a tunnel. For a Rails app, use port 3000. &lt;code&gt;ngrok http 3000&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final command, &lt;code&gt;ngrok http 3000&lt;/code&gt;, will bring up the ngrok UI:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8idiikc8t1k9ezrdh28.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8idiikc8t1k9ezrdh28.png" alt="the ngrok UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note the forwarding URL.&lt;/li&gt;
&lt;li&gt;Return to your application's settings and add &lt;code&gt;/users/auth/&amp;lt; insert_provider_name &amp;gt;/callback&lt;/code&gt; to the end of the forwarding URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to localhost:3000, or, if you're using ngrok, your ngrok forwarding URL (Ctrl+click from terminal), and click the "Sign in with GitHub" link you made earlier.&lt;/li&gt;
&lt;li&gt;You should now be able to authenticate a GitHub user in your application in Development. Try it out with your own GitHub account!&lt;/li&gt;
&lt;li&gt;If you're using ngrok for your Authorization callback URL, it may be necessary to add your ngrok domain to your list of allowed hosts in &lt;code&gt;config/environments/development&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"0e30-73-119-170-171.ngrok.io"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This will need to be changed if you close the terminal session that holds your ngrok session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  To move this setup to Production
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Return to Step 2:

&lt;ul&gt;
&lt;li&gt;On GitHub application page, update your "Homepage URL" to
&lt;code&gt;&amp;lt; your app's production domain name &amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update "Authorization callback URL" to &lt;code&gt;&amp;lt; your app's production domain name &amp;gt;/users/auth/github/callback&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Update your &lt;code&gt;config/application.yml&lt;/code&gt; file to include a production environment.&lt;/li&gt;
&lt;li&gt;Set Heroku config variables so Heroku can access your application keys

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;heroku config:set GITHUB_ID=github_id_key_here&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heroku config:set GITHUB_SECRET=github_secret_key_here&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Deploy to Heroku&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Reflection
&lt;/h4&gt;

&lt;p&gt;I was quite happy when I saw the "Successfully authenticated from Github account." flash message. Getting this to work was an exercise in patience and reading documentation slowly and carefully. Authentication is a deep topic and I feel I have only scratched the surface. I hope this post was a helpful guide on how to setup OmniAuth in a development environment. Before we walk, we must crawl. Before we deploy to production, we must struggle in development.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@brett_jordan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Brett Jordan&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/authenticity?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>omniauth</category>
      <category>authentication</category>
      <category>ngrok</category>
    </item>
  </channel>
</rss>
