DEV Community: Chuck The latest articles on DEV Community by Chuck (@eclecticcoding). https://dev.to/eclecticcoding 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%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg DEV Community: Chuck https://dev.to/eclecticcoding en Using each_slice in Rails Chuck Sat, 23 Oct 2021 15:48:53 +0000 https://dev.to/eclecticcoding/using-eachslice-in-rails-51cj https://dev.to/eclecticcoding/using-eachslice-in-rails-51cj <p>I wanted to take a moment to share about an approach to a problem I encounter with our application at work. I was working on a multi-step controller pattern which returned data from an external API to move through a registration process. The data did not persist into a database, but instead, was presented from JSON.</p> <p>The design called for a grid of 12 cards that could be paginated, searched, and selected. For instance if we have 32 cards, on multiple pages, we needed to be able to select all the cards, and on submit, persist 32 cards to the next controller action with an array of data. After reviewing the specification, we decided to use <a href="proxy.php?url=https://www.datatables.net/" rel="noopener noreferrer">DataTables</a>, which we have already used throughout this Rails 5 application.</p> <p><strong>The problem</strong>: How do you populate a card view into a HTML table? DataTables will read a table, add pagination and search automatically but does not work with a CSS Grid Card view. </p> <p><strong>TLTR</strong>: Just go grab the <a href="proxy.php?url=https://github.com/eclectic-coding/article_rails_each_slice" rel="noopener noreferrer">source code</a> if you prefer. </p> <h2> Base application </h2> <p>So, I have created a base Rails 6 application to start with set up with Webpacker, Bootstrap5, and I have added jQuery for DataTables. I have created a single resource called Player, with the attribute of <code>name</code>, used faker to populate the database with thirty instances, and displayed in a card view on the index action. </p> <p><a href="proxy.php?url=https://media2.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%2Fdmxmmxgvcfa1scodea0d.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.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%2Fdmxmmxgvcfa1scodea0d.png" alt="Players card view" width="800" height="274"></a></p> <h2> Set up Datatables </h2> <p>We are going to set up DataTables just so we can see the results. You can go to the DataTables <a href="proxy.php?url=https://datatables.net/download/" rel="noopener noreferrer">Download</a> page to confirm which package you will need. In my case, I am using the package which uses Bootstrap5. </p> <p><code>yarn add datatables.net-bs5</code></p> <p>Next set up a file to configure. I just placed in my packs directory: <code>app/javascript/packs/player-datatables.js</code>, and do not forget to import from <code>application.js</code>: <code>import "./player-datatables.js"</code>.</p> <p>To set up, call like so in <code>player-datatables.js</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="nf">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">datatables.net-bs5</span><span class="dl">'</span><span class="p">);</span> <span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nf">ready</span><span class="p">(</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#players</span><span class="dl">'</span><span class="p">).</span><span class="nc">DataTable</span><span class="p">();</span> <span class="c1">// players ID for our table</span> <span class="p">}</span> <span class="p">);</span> </code></pre> </div> <p>However, I want to make a few configuration changes. </p> <ul> <li>Set the pagination parameter of 4 rows</li> <li>Do not show the rows filter select and label</li> <li>Hide the search box label</li> <li>Add a placeholder into the search box</li> <li>Inject some CSS classes for the search box styling </li> </ul> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="nf">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">datatables.net-bs5</span><span class="dl">'</span><span class="p">);</span> <span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nf">ready</span><span class="p">(</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#players</span><span class="dl">'</span><span class="p">).</span><span class="nc">DataTable</span><span class="p">({</span> <span class="dl">"</span><span class="s2">pageLength</span><span class="dl">"</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="c1">// set rows for pagination</span> <span class="dl">"</span><span class="s2">bInfo</span><span class="dl">"</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// Hide show columns select</span> <span class="dl">"</span><span class="s2">bLengthChange</span><span class="dl">"</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// Hide bInfo 1 of n shown</span> <span class="dl">"</span><span class="s2">oLanguage</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">sSearch</span><span class="dl">"</span><span class="p">:</span> <span class="dl">""</span><span class="p">,</span> <span class="dl">"</span><span class="s2">sSearchPlaceholder</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Search players...</span><span class="dl">"</span> <span class="p">}</span> <span class="p">});</span> <span class="c1">// Add classes to search box</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#players_filter</span><span class="dl">'</span><span class="p">).</span><span class="nf">addClass</span><span class="p">(</span><span class="dl">'</span><span class="s1">d-flex justify-content-end me-3</span><span class="dl">'</span><span class="p">)</span> <span class="p">}</span> <span class="p">);</span> </code></pre> </div> <p>DataTables will look for a table with the ID of <code>players</code>, use the table rows to populate the data, and do its magic. The table <strong>must</strong> have column headings for each column, but we can kind of fake it like I have done here:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;table</span> <span class="na">id=</span><span class="s">"players"</span><span class="nt">&gt;</span> <span class="nt">&lt;thead</span> <span class="na">class=</span><span class="s">"d-none"</span><span class="nt">&gt;</span> <span class="nt">&lt;tr&gt;</span> <span class="nt">&lt;td&gt;</span>Player<span class="nt">&lt;/td&gt;</span> <span class="nt">&lt;td&gt;</span>Player<span class="nt">&lt;/td&gt;</span> <span class="nt">&lt;td&gt;</span>Player<span class="nt">&lt;/td&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="nt">&lt;/thead&gt;</span> <span class="nt">&lt;tbody&gt;&lt;/tbody&gt;</span> <span class="nt">&lt;/table&gt;</span> </code></pre> </div> <p>Now we have an empty table populating on the index view. <br> <a href="proxy.php?url=https://media2.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%2Fivlz5vcu6jttxmi7yw5s.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.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%2Fivlz5vcu6jttxmi7yw5s.png" alt="Empty Data Table" width="800" height="154"></a></p> <h2> Logic of iteration </h2> <p>Let's look at the logic of iterating over a collection to populate a view. Iterating normally would look something like this using <code>each</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight erb"><code> <span class="nt">&lt;tbody&gt;</span> <span class="cp">&lt;%</span> <span class="vi">@players</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">player</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="nt">&lt;tr&gt;</span> <span class="nt">&lt;td&gt;</span><span class="cp">&lt;%=</span> <span class="n">player</span><span class="p">.</span><span class="nf">title</span> <span class="cp">%&gt;</span><span class="nt">&lt;/td&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tbody&gt;</span> <span class="nt">&lt;/table&gt;</span> </code></pre> </div> <p>However, we are trying to replicate a card grid into a table, which means that we want to force only three columns, then move to the next row, using the same data in each table cell. The above loop will place our entire collection into one column. </p> <p>There might be other solutions, maybe using <code>each_with_index</code> and evaluating the index with <code>modulus</code>, although this did not work for me. </p> <p>First let me say, I love Ruby, which had the perfect method for this use case: <code>each_slice</code>, which will iterate the given block for each slice of a number of specified elements. If no block is given, it will return an enumerator.</p> <p>Notice this example:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>irb<span class="o">(</span>main<span class="o">)</span>:009:0&gt; <span class="o">(</span>1..10<span class="o">)</span>.each_slice<span class="o">(</span>3<span class="o">)</span> <span class="o">{</span> |a| p a <span class="o">}</span> <span class="o">[</span>1, 2, 3] <span class="o">[</span>4, 5, 6] <span class="o">[</span>7, 8, 9] <span class="o">[</span>10] <span class="o">=&gt;</span> nil </code></pre> </div> <p><code>each_slice</code> return arrays with the number of elements specified (3), and then an array of the remainder elements. </p> <h2> Solution </h2> <p>So, to start building our view with <code>each_slice</code>, the <code>&lt;tbody&gt;</code> section will start as so:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight erb"><code><span class="nt">&lt;tbody&gt;</span> <span class="cp">&lt;%</span> <span class="vi">@players</span><span class="p">.</span><span class="nf">each_slice</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">player</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="nt">&lt;tr</span> <span class="na">role=</span><span class="s">"row"</span><span class="nt">&gt;</span> ... <span class="nt">&lt;/tr&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tbody&gt;</span> </code></pre> </div> <p>Now, remember, <code>each_slice</code> returns an array, so <code>player</code> is an array, not an instance, in which will will need to iterate:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight erb"><code><span class="nt">&lt;tbody&gt;</span> <span class="cp">&lt;%</span> <span class="vi">@players</span><span class="p">.</span><span class="nf">each_slice</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">player</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="nt">&lt;tr</span> <span class="na">role=</span><span class="s">"row"</span><span class="nt">&gt;</span> <span class="cp">&lt;%</span> <span class="n">player</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">p</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%=</span> <span class="n">render</span> <span class="ss">partial: </span><span class="s2">"players/player"</span><span class="p">,</span> <span class="ss">locals: </span><span class="p">{</span> <span class="ss">p: </span><span class="nb">p</span> <span class="p">}</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tbody&gt;</span> </code></pre> </div> <p>This seems like we are finished, but remember if we do have a remainder array returned from <code>each_slice</code>, we have not addressed these. In fact, DataTable will crash. See console output:<br> <a href="proxy.php?url=https://media2.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%2F0r8ul48ymiix2xz7fa76.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.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%2F0r8ul48ymiix2xz7fa76.png" alt="Data Tables console errors" width="549" height="202"></a><br> <strong>The reason</strong>: DataTables expect correctly formatted table markup. The browser is more forgiving, and will display the table. However, all the DataTables features will not be present (i.e. search, pagination). If there are remainders, there is no <code>&lt;td&gt;&lt;/td&gt;</code> tags for those cells. Luckily, Ruby can help us real easily to check if there are any remainder in <code>player</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight erb"><code><span class="nt">&lt;tbody&gt;</span> <span class="cp">&lt;%</span> <span class="vi">@players</span><span class="p">.</span><span class="nf">each_slice</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">player</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="nt">&lt;tr</span> <span class="na">role=</span><span class="s">"row"</span><span class="nt">&gt;</span> <span class="cp">&lt;%</span> <span class="n">player</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">p</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%=</span> <span class="n">render</span> <span class="ss">partial: </span><span class="s2">"players/player"</span><span class="p">,</span> <span class="ss">locals: </span><span class="p">{</span> <span class="ss">p: </span><span class="nb">p</span> <span class="p">}</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="p">(</span><span class="mi">3</span> <span class="o">-</span> <span class="n">player</span><span class="p">.</span><span class="nf">length</span><span class="p">).</span><span class="nf">times</span> <span class="k">do</span> <span class="cp">%&gt;</span> // calc remainders <span class="nt">&lt;td&gt;&lt;/td&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="nt">&lt;/tbody&gt;</span> </code></pre> </div> <h2> Conclusion </h2> <p>So, what have we learned? First, Ruby is beautiful, but beyond the obvious, we have learned about <code>each_slice</code>. This is a method you may not use everyday, but with this example you have seen a least one use case. Be sure to leave a comment or hit me up on <a href="proxy.php?url=https://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>, and I hope you have enjoyed. </p> rails ruby webdev tutorial Learning Rails Chuck Thu, 11 Mar 2021 22:07:50 +0000 https://dev.to/eclecticcoding/learning-rails-1k0l https://dev.to/eclecticcoding/learning-rails-1k0l <p>So, I decided to write this article to share a few resources I have used over the last twenty months of learning Ruby and Ruby on Rails in the hopes it may help others. Also, I am writing to share a little of my story. </p> <p>I have always loved web technologies and am a self-taught developer, which started as a hobby. Initially, I build my first website with HTML 3.2 because my business needed a website. For fun, I eventually, I supported many area non-profits by building WordPress sites for many years. However, two years ago, something changes, I had a desire to learn more and transition to a professional career. </p> <p>In August 2019, I started at <a href="proxy.php?url=https://flatironschool.com/" rel="noopener noreferrer">Flatiron School</a> and fell in love with the entire Ruby eco-system: Ruby, Sinatra, and especially <a href="proxy.php?url=https://rubyonrails.org/" rel="noopener noreferrer">Ruby on Rails</a>. I have a thirst and drive to <strong>be better tomorrow than I am today</strong>, so these resources reflect that desire.</p> <h2> Free Resources </h2> <p><a href="proxy.php?url=https://gorails.com/start" rel="noopener noreferrer">Gorails</a> - this course is by Chris Oliver of GoRails, creator of <a href="proxy.php?url=https://jumpstartrails.com/?utm_source=gorails" rel="noopener noreferrer">JumpstartPro</a> and <a href="proxy.php?url=https://hatchbox.io" rel="noopener noreferrer">Hatchbox</a>. This is a new free course for beginners , and is a great place to begin, watch, and code along.</p> <p><a href="proxy.php?url=https://www.railscodealong.com/" rel="noopener noreferrer">Rails Code Along</a> - Steve Polito's course is a little different. This course bridges the gap between building side-projects, to a full production application. It follows full Test Driven Development with a Continuous Integration workflow. If you have been coding Rails for a little while, this will really way to step up your game.</p> <p><a href="proxy.php?url=https://guides.rubyonrails.org/" rel="noopener noreferrer">Rails Guides</a> - Yes, the Rails Guides. Rails documentation is actually quite good. Any sharp developer needs to learn to read the documentation. </p> <p><strong>Other I have not used:</strong><br><br> <a href="proxy.php?url=https://www.theodinproject.com/courses/ruby-on-rails" rel="noopener noreferrer">OdinProject</a> - a lot of Rails developers started with Odin Project, I just never have.</p> <p><a href="proxy.php?url=https://web-crunch.com/" rel="noopener noreferrer">Web-Crunch</a> - Andy Leverenz has a multi-part Rails series that I understand is quite good.</p> <h2> Commercial Resources </h2> <p><a href="proxy.php?url=https://www.learnenough.com/" rel="noopener noreferrer">Rails Tutorial</a> - the Rails Tutorial, by <a href="proxy.php?url=https://twitter.com/mhartl" rel="noopener noreferrer">Michael Hartl</a>, has been the definitive standard for learning Rails for years. It is on longer free, so it is listed in the Commercial section, but it is worth the cost of admission. Even if you only purchase the Book, it is a great resource. I finished this tutorial while learning Rails at Flatiron, and I am continually referencing the book, or the finished application. It covers User accounts (not Devise), relationship models, and all with TDD using minitest.</p> <p><a href="proxy.php?url=https://www.udemy.com/share/101EFgB0UTdV9TRnw=/" rel="noopener noreferrer">Professional Rails Code Along</a> - this Udemy course takes a unique approach of mimicking a Professional Production application, designed to meet the clients' expectations. It includes an Administration Dashboard to manager users and resources, and full Test Driven Development. Now, the tutorials are dated as they are built with Rails 4.2.6. So, I decided to build with the latest Rails and Ruby versions. When I hit a roadblock, I stop, worked through the problems, and documented the results. </p> <p><a href="proxy.php?url=https://www.udemy.com/course/ruby-rails-5-bdd-rspec-capybara/" rel="noopener noreferrer">Ruby on Rails 5 - BDD, RSpec and Capybara</a> - a full TDD course by Mashrur Hossain and Emmanuel Asante. Again, a little dated (Rails 5.1), so as before I decided to build with the latest Rails and Ruby versions.</p> <p><a href="proxy.php?url=https://store.afomera.dev/" rel="noopener noreferrer">Andrea Fomera</a> - Andrea's courses are top notch. She has a Rails course that touches on User accounts, relationships, and Javascript reactive rendering, just to name a few topics. There is also a new pre-released course <strong>Learn Hotwire by Building a Forum</strong> that I am doing right now that covers reactivity of Hotwire and is excellent.</p> <p><a href="proxy.php?url=https://courses.jasoncharnes.com/" rel="noopener noreferrer">Jason Charnes</a> - offers a great course called <strong>Interactive Rails with StimulusReflex</strong> which I highly recommend, which teaches Stimulus Reflex. </p> <h2> Books </h2> <p>Books I have read and/or reading that I have found beneficial </p> <ul> <li> <strong>Confident Ruby</strong> by Avdi Grimm</li> <li> <strong>Metaprogramming Ruby</strong> by Paola Perrotta</li> <li> <strong>Practical Object-Oriented Design</strong> by Sandi Metz</li> </ul> <h2> Communities </h2> <p>Being in a community of developers has been vital to my personal growth:</p> <ul> <li> <a href="proxy.php?url=https://virtualcoffee.io/" rel="noopener noreferrer">VirtualCoffee</a> - Virtual Coffee is a laid-back conversation with developers twice a week. It's the conversation that keeps going in slack. It's the online events that support developers at all stages of the journey. It's the place you go to make friends.</li> <li> <a href="proxy.php?url=https://gorails.com/" rel="noopener noreferrer">GoRails</a> - a community of Rails developers, learning Ruby on Rails to build their ideas, products, and businesses.</li> <li>Local and virtual Ruby Meetups</li> <li>Rails and StimulusReflex Discord</li> </ul> <h2> Workflow </h2> <p>When I first graduated from Flatiron, in February 2020, I was pulled in<br> a lot of directions, mostly influenced by the amount of job posting I was<br> reading. I spent time learning more about Redux, a lot of time learning Vue,<br> which I loved, and time learning the basics of Python, which I have a<br> desire to fully learn. Eventually I spent some time considering exactly what I wanted to do with my development career - I LOVE RAILS, so I redoubled my efforts to just Rails.</p> <p>It was important for me to develop a routine, a routine that was missing since Bootcamp. </p> <p>My daily routine is the same each day:</p> <ul> <li>In my office at 6am everyday</li> <li>Catch up on emails, and follow up on potential Ruby/Rails job openings</li> <li>Network on Twitter, LinkedIn, and the Slack communities I am a member</li> <li>Then, pay attention to this part, <strong>I code eight hours a day</strong>, committing and pushing code to a repository, running continuous integration, and sometimes deploying. </li> </ul> <p>That's right I work as if I already had the job I am searching. It is important to develop skills and the muscle memory of developing. </p> <p>The second routine I have developed is to make notes. If I have worked through a problem, in a tutorial or side project, I document in a Notions workbook. There is no reason to do the same task of discovery all over again. </p> <p>It is fine to study, learn, listen to others, follow tutorials, but you just have to build stuff. It is the task of build were you learn to work through problems, and solve tasks. </p> <h2> So, What now. </h2> <p>I have been searching for my first full-time Rails position for thirteen months. During this time I have met a lot of wonderful developers, I have sharpened my skills, and with a strong passion and desire to <strong>be better tomorrow than I am today</strong>, I continue to code. Why?</p> <p>I am a Rails Developer. Even though I am still searching for that first position, I am a Rails developer and therefore I code. </p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, and strives to be <em>better tomorrow than I am today</em>, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> <p>Credit: <a href="proxy.php?url=https://github.com/codica2/rails-puma-ssl" rel="noopener noreferrer">Localhost SSL</a></p> ruby rails webdev Set Up Rbenv Revisited Chuck Thu, 04 Mar 2021 14:49:26 +0000 https://dev.to/eclecticcoding/set-up-rbenv-revisited-4ngo https://dev.to/eclecticcoding/set-up-rbenv-revisited-4ngo <p>I have been working on setting up a new operating system distribution. I took some time to test out a different package manager, but ended up going back to my familiar toolset. This article will talk about package manager to manage Ruby version. </p> <h2> Package Managers </h2> <p>So what is a package manager? </p> <blockquote> <p>A package manager or package-management system is a collection of software tools that automates the process of installing, upgrading, configuring, and removing computer programs for a computer's operating system consistently.</p> </blockquote> <p>They give you the ability to manage multiple versions of the same packages (i.e. Node.js, Ruby, etc.). </p> <p>When it comes to <strong>Ruby</strong>, there are three major package managers to consider.</p> <p><a href="proxy.php?url=https://rvm.io/" rel="noopener noreferrer">RVM</a> - When I first started learning Ruby almost two years ago, and set up my MacBook Pro, I started with RVM. It worked fine, and I had no real issues. However, it <em>seemed</em> heavy, computer resource wise.</p> <p><a href="proxy.php?url=https://github.com/asdf-vm/asdf" rel="noopener noreferrer">ASDF</a> - this is a different package manager as it is language agnostic. You can install a plugin for the respect language (i.e. Ruby, nodejs, etc.). I recently tried this one, but have returned to <code>rbenv</code>, just because I prefer it and the Node package manger I use.</p> <p><a href="proxy.php?url=https://github.com/rbenv/rbenv" rel="noopener noreferrer">RBENV</a> - this is my preference, and the focus of this article. A major pull of <code>rbenv</code> for me is that it's <em>lighter</em>, and by that I mean that it doesn't have to throw as many hooks into your computer system as <code>rvm</code>, although there is some load to the terminal. This is the exact same reason I prefer <a href="proxy.php?url=https://github.com/tj/n" rel="noopener noreferrer">N</a> to manage Node versus <a href="proxy.php?url=https://github.com/nvm-sh/nvm" rel="noopener noreferrer">NVM</a>, because there is ZERO terminal load. This may be the source of another article.</p> <h2> Enter the Clones </h2> <p>So, Homebrew offers a <code>rbenv</code> package install, and Ubuntu does as well. I have used both, but I prefer to have more control, so I just clone the repositories. <br> To set up <strong>RBENV</strong> there is the default way: </p> <ul> <li>Clone rbenv to <code>.rbenv</code> </li> <li>Clone rbenv-build to <code>.rbenv/plugins</code> </li> <li>Set up <code>.rbenv/bin</code> in your <code>$PATH</code> </li> <li>Set up <code>.rbenv/bin/rbenv init</code> in your shell</li> <li>Restart shell</li> <li>Run <a href="proxy.php?url=https://github.com/rbenv/rbenv-installer/blob/master/bin/rbenv-doctor" rel="noopener noreferrer">rbenv-doctor</a> script to verify installation</li> </ul> <p>There is thankfully an easier install <a href="proxy.php?url=https://github.com/rbenv/rbenv-installer" rel="noopener noreferrer">script</a>. This script installs or updates <code>rbenv</code> on your system. If Homebrew is detected, installation will proceed using <code>brew install/upgrade</code>. Otherwise, rbenv is installed under <code>~/.rbenv</code>. Additionally, ruby-build is also installed if rbenv install is not already available. After the installation, a separate <code>rbenv-doctor</code> script is run to verify the success of the installation and to detect common issues.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># with curl</span> curl <span class="nt">-fsSL</span> https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash <span class="c"># alternatively, with wget</span> wget <span class="nt">-q</span> https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer <span class="nt">-O-</span> | bash </code></pre> </div> <h2> Default Gems </h2> <p>There in one <code>rbenv</code> plugin which I prefer to set up and that is <code>rbenv-default-gems</code>. This plugin will manage installing a select set of gems whenever you update your version of <code>ruby</code>. Install the plugin:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>git clone https://github.com/rbenv/rbenv-default-gems.git <span class="si">$(</span>rbenv root<span class="si">)</span>/plugins/rbenv-default-gems </code></pre> </div> <p>Create a simple text file: <code>touch ~/.rbenv/default-gems</code>. In this file create your link of preferred gems to install/update:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>bundler rails </code></pre> </div> <p>Now install your ruby version: <code>rbenv install 3.0.0</code></p> <h2> Updating </h2> <p>When new versions of Ruby are releases it is important to update rbenv. There are two ways to do this. Since, you have literally cloned the repository, you can <code>git pull</code>, but there are two directories to do that in:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">cd</span> ~/.rbenv git pull </code></pre> </div> <p>And for <code>ruby-build</code><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">cd</span> ~/.rbenv/plugins/ruby-build git pull </code></pre> </div> <p>Check on Ruby versions to install:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># list latest stable versions:</span> <span class="nv">$ </span>rbenv <span class="nb">install</span> <span class="nt">-l</span> <span class="c"># list all local versions:</span> <span class="nv">$ </span>rbenv <span class="nb">install</span> <span class="nt">-L</span> <span class="c"># install a Ruby version:</span> <span class="nv">$ </span>rbenv <span class="nb">install </span>2.7.2 </code></pre> </div> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby tutorial webdev Devise Profile Usernames Chuck Wed, 03 Feb 2021 13:10:04 +0000 https://dev.to/eclecticcoding/devise-profile-usernames-30j4 https://dev.to/eclecticcoding/devise-profile-usernames-30j4 <p>I have worked on several projects recently with user accounts managed by Devise, and I have been working at changing the way user profile URL's are set up and presented to the user. In this article I will address this task and use a few of my recent favorite gems. </p> <p><strong>TLTR</strong>: If you just want the <a href="proxy.php?url=https://github.com/eclectic-coding/article_devise_usernames" rel="noopener noreferrer">code</a> go grab it, and post questions or responses if you like.</p> <h2> Goals </h2> <p>Here are the project parameters:</p> <ul> <li>Set up user accounts with <a href="proxy.php?url=https://github.com/heartcombo/devise" rel="noopener noreferrer">Devise</a> </li> <li>Set up an URL for a User's profile page as: <code>/users/:username</code> </li> <li>Generate a unique <code>username</code> which is not requested on the sign-up form</li> </ul> <p><strong>name_of_person</strong> - We will use the <a href="proxy.php?url=https://github.com/basecamp/name_of_person" rel="noopener noreferrer">name_of_person</a> gem by Basecamp. This gem creates a pseudo-field for full name (requires <code>first_name</code> and <code>last_name</code> in the <code>User</code> table). It has many other abstractions, but this is the only feature we will use.</p> <p><strong>friendly_id</strong> - We will use the <a href="proxy.php?url=https://github.com/norman/friendly_id" rel="noopener noreferrer">friendly_id</a> gem, which created slugs that we can map to a predetermined route. This is a method you can use throughout an application, not just with the User models. </p> <h2> Basic set up </h2> <p>We will start by setting up a basic Rails app: <code>rails new awesome_app</code>, and set up a static controller for a home route: <code>rails g controller static home</code>.</p> <p>Configure the routes to load the basic <code>home.html.erb</code>, in <code>config/routes.rb</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span> <span class="n">root</span> <span class="s2">"static#home"</span> <span class="k">end</span> </code></pre> </div> <p>Add to follow to the <code>application.html.erb</code>, to add a rudimentary navbar we can use later:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="cp">&lt;!DOCTYPE html&gt;</span> <span class="nt">&lt;html&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;title&gt;</span>ArticleDeviseUsernames<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width,initial-scale=1"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">csrf_meta_tags</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">csp_meta_tag</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">stylesheet_link_tag</span> <span class="err">'</span><span class="na">application</span><span class="err">',</span> <span class="na">media:</span> <span class="err">'</span><span class="na">all</span><span class="err">',</span> <span class="err">'</span><span class="na">data-turbolinks-track</span><span class="err">'</span><span class="na">:</span> <span class="err">'</span><span class="na">reload</span><span class="err">'</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">javascript_pack_tag</span> <span class="err">'</span><span class="na">application</span><span class="err">',</span> <span class="err">'</span><span class="na">data-turbolinks-track</span><span class="err">'</span><span class="na">:</span> <span class="err">'</span><span class="na">reload</span><span class="err">'</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/head&gt;</span> <span class="nt">&lt;body&gt;</span> <span class="nt">&lt;div</span> <span class="na">style=</span><span class="s">"margin-top: 20px"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">user_signed_in</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">User</span> <span class="na">Profile</span><span class="err">",</span> <span class="na">user_path</span><span class="err">(</span><span class="na">current_user.slug</span><span class="err">)</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Logout</span><span class="err">",</span> <span class="na">destroy_user_session_path</span><span class="err">(</span><span class="na">current_user.slug</span><span class="err">),</span> <span class="na">method:</span> <span class="na">:delete</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">else</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Log</span> <span class="na">in</span><span class="err">",</span> <span class="na">new_user_session_path</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">yield</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/body&gt;</span> <span class="nt">&lt;/html&gt;</span> </code></pre> </div> <h2> Set up Devise </h2> <p>Add the Devise gem to the <code>Gemfile</code> and <code>bundle install</code>, then install Devise: <code>rails generate devise:install</code>. You will need to add to <code>config/environments/development.rb</code> the following line:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } </code></pre> </div> <p>We are going to generate a devise <code>User</code> model: <code>rails generate devise User</code>.</p> <p>To use <code>name_of_person</code> gem, we need to add two columns to the created Devise migration. Add <code>first_name</code> and <code>last_name</code> somewhere within the database migration, then migrate with <code>rails db:migrate</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">DeviseCreateUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">6.1</span><span class="p">]</span> <span class="k">def</span> <span class="nf">change</span> <span class="n">create_table</span> <span class="ss">:users</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span> <span class="c1">## Database authenticatable</span> <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">default: </span><span class="s2">""</span> <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:encrypted_password</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">default: </span><span class="s2">""</span> <span class="o">...</span> <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:first_name</span> <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:last_name</span> <span class="n">t</span><span class="p">.</span><span class="nf">timestamps</span> <span class="ss">null: </span><span class="kp">false</span> <span class="k">end</span> <span class="n">add_index</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">unique: </span><span class="kp">true</span> <span class="n">add_index</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:reset_password_token</span><span class="p">,</span> <span class="ss">unique: </span><span class="kp">true</span> <span class="c1"># add_index :users, :confirmation_token, unique: true</span> <span class="c1"># add_index :users, :unlock_token, unique: true</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>Devise will automatically add the appropriate routes, but it always a good idea to check, so make sure that in <code>config/routes.rb</code> the route is found: <code>devise_for: users</code>. </p> <p>Add to the <code>User</code> model to use the <code>name_of_person</code> gem, by adding <code>has_person_name</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span> <span class="c1"># Include default devise modules. Others available are:</span> <span class="c1"># :confirmable, :lockable, :timeoutable, :trackable and :omniauthable</span> <span class="n">devise</span> <span class="ss">:database_authenticatable</span><span class="p">,</span> <span class="ss">:registerable</span><span class="p">,</span> <span class="ss">:recoverable</span><span class="p">,</span> <span class="ss">:rememberable</span><span class="p">,</span> <span class="ss">:validatable</span> <span class="n">has_person_name</span> <span class="k">end</span> </code></pre> </div> <p>This is the basic configure for the gem, but we are going to have to tell Devise to allow the new <code>name</code> field. So, in <code>app/controllers/application_controller.rb</code> add the following:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span> <span class="n">before_action</span> <span class="ss">:configure_permitted_parameters</span><span class="p">,</span> <span class="ss">if: :devise_controller?</span> <span class="kp">protected</span> <span class="k">def</span> <span class="nf">configure_permitted_parameters</span> <span class="n">devise_parameter_sanitizer</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:sign_up</span><span class="p">,</span> <span class="ss">keys: </span><span class="p">[</span><span class="ss">:name</span><span class="p">])</span> <span class="n">devise_parameter_sanitizer</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:account_update</span><span class="p">,</span> <span class="ss">keys: </span><span class="p">[</span><span class="ss">:name</span><span class="p">])</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>We will need to add the <code>name</code> field to the sign-in and sign-up forms, so we need to generate the views: <code>rails generate devise:views</code>. In <code>app/views/devise/registrations/new.html.erb</code>, <code>.../registrations/edit.html.erb</code>, and <code>.../sessions/new.html.erb</code> add the following for the name field:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;h2&gt;</span>Sign up<span class="nt">&lt;/h2&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">form_for</span><span class="err">(</span><span class="na">resource</span><span class="err">,</span> <span class="na">as:</span> <span class="na">resource_name</span><span class="err">,</span> <span class="na">url:</span> <span class="na">registration_path</span><span class="err">(</span><span class="na">resource_name</span><span class="err">))</span> <span class="na">do</span> <span class="err">|</span><span class="na">f</span><span class="err">|</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">render</span> <span class="err">"</span><span class="na">devise</span><span class="err">/</span><span class="na">shared</span><span class="err">/</span><span class="na">error_messages</span><span class="err">",</span> <span class="na">resource:</span> <span class="na">resource</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"field"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.label</span> <span class="na">:name</span> <span class="err">%</span><span class="nt">&gt;&lt;br</span> <span class="nt">/&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.text_field</span> <span class="na">:name</span><span class="err">,</span> <span class="na">autofocus:</span> <span class="na">true</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"field"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.label</span> <span class="na">:email</span> <span class="err">%</span><span class="nt">&gt;&lt;br</span> <span class="nt">/&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.email_field</span> <span class="na">:email</span><span class="err">,</span> <span class="na">autofocus:</span> <span class="na">false</span><span class="err">,</span> <span class="na">autocomplete:</span> <span class="err">"</span><span class="na">email</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> ... </code></pre> </div> <p>Almost there ... We need to create a page to contain the User Profile, so we need to create a <code>User</code> controller:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>rails g controller User show </code></pre> </div> <p>Edit controller:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="k">def</span> <span class="nf">show</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>Lastly, update the routes, so for the route to the profile page:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span> <span class="n">devise_for</span> <span class="ss">:users</span> <span class="n">resources</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span><span class="ss">:show</span><span class="p">]</span> <span class="n">root</span> <span class="s2">"static#home"</span> <span class="k">end</span> </code></pre> </div> <p>All done with Devise. You can restart your <code>rails server</code>, open the development site, and create a User Account. When you are redirected to the <code>root_path</code>, click the "User Profile" link in the navbar. You will be redirected to a path, something like <code>http://localhost:3000/users/1</code>. This is not the goal, so lets move on.</p> <h2> Friendly ID </h2> <p>Add the <code>friendly_id</code> gem to the <code>Gemfile</code> and <code>bundle install</code>, then create a migration:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>rails g migration AddSlugToUsers slug:uniq </code></pre> </div> <p>This will create a unique slug. In our case we are going to use the first and last name fields, and it will create a unique username. So in my case <code>/users/chuck-smith</code>. If this is not unique, maybe there is another "Chuck Smith" in the user table, it will make it unique: <code>/users/chuck-s</code>. </p> <p>Next we need to generate friendly_id: <code>rails generate friendly_id</code>, and migrate the database: <code>rails db:migrate</code>. </p> <p>We use Friendly_Id by extending the User model, and define the <code>:name</code> column, from <code>name_of_person</code> as the slug field:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span> <span class="c1"># Include default devise modules. Others available are:</span> <span class="c1"># :confirmable, :lockable, :timeoutable, :trackable and :omniauthable</span> <span class="n">devise</span> <span class="ss">:database_authenticatable</span><span class="p">,</span> <span class="ss">:registerable</span><span class="p">,</span> <span class="ss">:recoverable</span><span class="p">,</span> <span class="ss">:rememberable</span><span class="p">,</span> <span class="ss">:validatable</span> <span class="n">has_person_name</span> <span class="kp">extend</span> <span class="no">FriendlyId</span> <span class="n">friendly_id</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">use: :slugged</span> <span class="k">end</span> </code></pre> </div> <p>Now, edit the show action in the UsersController to use the <code>:slug</code> param from Friendly:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="k">def</span> <span class="nf">show</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">friendly</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:slug</span><span class="p">])</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>Lastly, update the routes, for the new <code>param</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span> <span class="n">devise_for</span> <span class="ss">:users</span> <span class="n">resources</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span><span class="ss">:show</span><span class="p">],</span> <span class="ss">param: :slug</span> <span class="n">root</span> <span class="s2">"static#home"</span> <span class="k">end</span> </code></pre> </div> <p>If you already have <code>Users</code> created, from the <code>rails console</code> execute: <code>User.find_each(&amp;:save)</code>, which will update the new slug column. </p> <p>Now, where you log in and browse to your User Profile the URL is friendlier (forgive the pun): <code>/users/chuck-smith</code>.</p> <p>One additional optional configuration. If you want to change the user profile path you can edit the routes file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span> <span class="n">devise_for</span> <span class="ss">:users</span> <span class="n">resources</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span><span class="ss">:show</span><span class="p">],</span> <span class="ss">param: :slug</span><span class="p">,</span> <span class="ss">path: </span><span class="s2">""</span> <span class="n">root</span> <span class="s2">"static#home"</span> <span class="k">end</span> </code></pre> </div> <p>Notice I have added a path key to the <code>:users</code> resource which is empty. So, now if you browse to the user profile page the path will be (in this example): <code>/chuck-smith</code>.</p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby tutorial webdev Dev Environment SSL Chuck Tue, 02 Feb 2021 16:56:02 +0000 https://dev.to/eclecticcoding/dev-environment-ssl-4fg8 https://dev.to/eclecticcoding/dev-environment-ssl-4fg8 <p>Have you ever wanted to set up SSL for localhost development on your computer? No? Honestly, as hard as this can be at times, me neither. What changed? Recently, I have been working on a Rails contract project, and the project was set up using SSL. I needed to set up SSL on my Linux computer, so I could collaborate on the project. </p> <p>Why you may need SSL in development? Check this <a href="proxy.php?url=https://twitter.com/getify/status/1023202051902373888" rel="noopener noreferrer">tweet</a> to find the answer.</p> <p>So there is one solution <a href="proxy.php?url=https://github.com/puma/puma-dev#linux-support" rel="noopener noreferrer">Puma-dev</a>, however, even though there is Linux support, the solution and set up never worked for me. </p> <h2> Solution </h2> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 365 -keyout localhost.key -out localhost.crt </code></pre> </div> <p>You will be provided with some information fields to fill in about country key, email, etc. However, you can skip this step. This command will create two new files localhost.key and localhost.crt in the current directory. You can move these files anywhere.</p> <h3> Rails Server and Webpack </h3> <p>Open in your browser: <code>ssl://localhost:3000?key=/path/to/localhost.key&amp;cert=/path/to/localhost.crt</code>. Make sure the certificate and key location is correct in the URL string.</p> <p>You cannot use the standard <code>rails server</code> as this defaults to <code>http://localhost:3000</code>. Instead, use the following command:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>rails s -b 'ssl://localhost:3000?key=/path/to/localhost.key&amp;cert=/path/to/localhost.crt' </code></pre> </div> <p>Make sure the certificate and key location is correct in the URL string.</p> <p>FYI: you will have to accept the certificate in your browser the first time you open the URL.</p> <p>I have included in a ZSH alias, which is much easier to remember:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>alias rss="rails s -b 'ssl://localhost:3000?key=/path/to/localhost.key&amp;cert=/path/to/localhost.crt'" </code></pre> </div> <p>What about Webpack, which I like to start in a separate terminal window? It is actually not that hard and is documented on the repository for webpack: <a href="proxy.php?url=https://github.com/rails/webpacker/blob/9bbc51f333137f51cdd676e2cf4abc3583fa5462/docs/webpack-dev-server.md#webpack-dev-server" rel="noopener noreferrer">Webpack SSL</a>. Basiclly, we have to enable <code>https</code> in <code>config/webpacker.yml</code>, start webpacker like normal and accept the certificate. </p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, and strives to be <em>better tomorrow than I am today</em>, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> <p>Credit: <a href="proxy.php?url=https://github.com/codica2/rails-puma-ssl" rel="noopener noreferrer">Localhost SSL</a></p> rails webdev tutorial Rails Basic Template Chuck Fri, 15 Jan 2021 18:57:00 +0000 https://dev.to/eclecticcoding/rails-basic-template-3opg https://dev.to/eclecticcoding/rails-basic-template-3opg <p>There is no need for developers to walking the same path, performing the same tasks at the beginning of any project, over and over again. That is what this series of articles has been about, streamlining our workflow. </p> <p>I have writing about create templates for Rails before, but quite frankly, I have learned so much about Rails over the last six months, as I sharpen my skills while searching for a position, it bears touching this topic again. </p> <div class="ltag__link"> <a href="proxy.php?url=/eclecticcoding" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg" alt="eclecticcoding"> </div> </a> <a href="proxy.php?url=/eclecticcoding/rails-boilerplate-4hgf" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Rails Boilerplate</h2> <h3>Chuck ・ Jul 8 '20</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#rails</span> <span class="ltag__link__tag">#ruby</span> <span class="ltag__link__tag">#webdev</span> <span class="ltag__link__tag">#tutorials</span> </div> </div> </a> </div> <p>This is a default/basic template:</p> <ul> <li>RSpec</li> <li>Code Quality with Rubocop</li> <li>Code Coverage</li> <li>Configure Rails generators</li> <li>Static routes </li> <li>No styling (this varies per project)</li> <li>No user accounts</li> </ul> <p><strong>TLTR</strong>: The Rails template is hosted in this <a href="proxy.php?url=https://github.com/eclectic-coding/rails_default_template.git" rel="noopener noreferrer">Repository</a></p> <p>The template design uses a modular design, so it is easy to maintain and turn off features if I desire. I am going to step through a few of the methods to explain my set up:</p> <h2> Setup Additional Gems </h2> <p>The first method adds new gem requirements to the 'Gemfile'. This does not install them, it only prepares for the installation. Most of these gems are to set up my testing, code quality, and code coverage norms I prefer.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">add_gems</span> <span class="c1"># Rexml is required for Ruby 3</span> <span class="n">gem</span> <span class="s2">"rexml"</span> <span class="n">gem_group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span> <span class="n">gem</span> <span class="s2">"capybara"</span><span class="p">,</span> <span class="s2">"&gt;= 2.15"</span> <span class="n">gem</span> <span class="s2">"database_cleaner"</span> <span class="n">gem</span> <span class="s2">"factory_bot_rails"</span><span class="p">,</span> <span class="ss">git: </span><span class="s2">"http://github.com/thoughtbot/factory_bot_rails"</span> <span class="n">gem</span> <span class="s2">"rspec-rails"</span> <span class="k">end</span> <span class="n">gem_group</span> <span class="ss">:development</span> <span class="k">do</span> <span class="n">gem</span> <span class="s2">"fuubar"</span> <span class="n">gem</span> <span class="s2">"guard"</span> <span class="n">gem</span> <span class="s2">"guard-rspec"</span> <span class="n">gem</span> <span class="s2">"rubocop"</span> <span class="n">gem</span> <span class="s2">"rubocop-rails"</span><span class="p">,</span> <span class="ss">require: </span><span class="kp">false</span> <span class="n">gem</span> <span class="s2">"rubocop-rspec"</span> <span class="k">end</span> <span class="n">gem_group</span> <span class="ss">:test</span> <span class="k">do</span> <span class="n">gem</span> <span class="s2">"simplecov"</span><span class="p">,</span> <span class="ss">require: </span><span class="kp">false</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>I have written on this topic before, and you can read more in my previous article: <a href="proxy.php?url=https://dev.to/rails-testing-setup">Rails testing Setup</a></p> <div class="ltag__link"> <a href="proxy.php?url=/eclecticcoding" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg" alt="eclecticcoding"> </div> </a> <a href="proxy.php?url=/eclecticcoding/rails-testing-setup-nla" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Rails Testing Setup</h2> <h3>Chuck ・ Aug 27 '20</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#rails</span> <span class="ltag__link__tag">#testing</span> <span class="ltag__link__tag">#ruby</span> </div> </div> </a> </div> <h2> Static Routes </h2> <p>This method sets up a static route controller with a view for a home route. This is a mounting point for any static routes the project may have:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">add_static</span> <span class="n">generate</span> <span class="s2">"controller static home"</span> <span class="n">route</span> <span class="s2">"root to: 'static#home'"</span> <span class="k">end</span> </code></pre> </div> <h2> Copy Additional Files </h2> <p>We can stop here with one configuration file. However, we can also setup some files to copy for a more complete configuration:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">copy_templates</span> <span class="n">remove_dir</span> <span class="s2">"spec"</span> <span class="n">copy_file</span> <span class="s2">"Guardfile"</span> <span class="n">copy_file</span> <span class="s2">".rspec"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">copy_file</span> <span class="s2">".rubocop.yml"</span> <span class="n">copy_file</span> <span class="s2">".simplecov"</span> <span class="n">directory</span> <span class="s2">"config"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">directory</span> <span class="s2">"lib"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">directory</span> <span class="s2">"spec"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="k">end</span> </code></pre> </div> <p>We are copying four files which are specifically for testing, code quality, and code coverage. </p> <p>Whenever I use rails generators, I prefer to not create a lot of extra files I am not going to use, like stylesheets, helpers, and spec files. If I choose to use any of these resources, I would rather manually create them. So, I configure the generators in <code>config/application.rb</code>, and copy the <code>config</code> directory:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="n">config</span><span class="p">.</span><span class="nf">generators</span> <span class="k">do</span> <span class="o">|</span><span class="n">g</span><span class="o">|</span> <span class="n">g</span><span class="p">.</span><span class="nf">stylesheets</span> <span class="kp">false</span> <span class="n">g</span><span class="p">.</span><span class="nf">helper</span> <span class="kp">nil</span> <span class="n">g</span><span class="p">.</span><span class="nf">test_framework</span> <span class="kp">nil</span> <span class="k">end</span> </code></pre> </div> <p>Lastly, I remove the generated <code>spec</code> directory and copy a new one that includes the configuration I need, and one <code>feature</code> spec to test the static route. </p> <h2> Config File </h2> <p>To use the new <code>template</code>, you need to set up a <code>.railsrc</code> dotfile, in the users' path, in your <code>$HOME</code> directory. When you use the <code>rails new</code> command will inject commands to the command line silently. So my simple <code>.railsrc</code> file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>--database=postgresql --database=postgresql -T -m /path/to/template.rb </code></pre> </div> <p>I can start a new project: <code>rails new cool_app</code> and silently the postgresql flag is added, no default testing framework is installed, and the custom setup installation process begins.</p> <p>Remember to check out the complete Rail's template hosted in the <a href="proxy.php?url=https://github.com/eclectic-coding/rails_default_template.git" rel="noopener noreferrer">Repository</a></p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby tutorial webdev Rails Image Helper Chuck Tue, 12 Jan 2021 16:27:18 +0000 https://dev.to/eclecticcoding/rails-image-helper-22mc https://dev.to/eclecticcoding/rails-image-helper-22mc <p>Often times when developing application, the developer may need to include logic to conditionally render UI elements. For instance, maybe an active CSS class in the navbar menu-item for the current page. Sometimes the required logic is more complex. In my case I needed to provide a fallback image when an image was not available from the API. In this article I will show the methodology I used to create a Rails Helper to meet this goal. </p> <h2> Structure </h2> <p>On my side project <a href="proxy.php?url=https://yourcongress.co/senators" rel="noopener noreferrer">Your Congress</a>, I am using a free GitHub repository, <a href="proxy.php?url=https://github.com/unitedstates/images/tree/gh-pages/congress" rel="noopener noreferrer">UnitedStates/Images</a> which stores all the photographs of serving Congress members. The images are accessible through an API served from GitHub pages, by images size and member ID. So, the image URL will be configured like so: <code>https://theunitedstates.io/images/congress/[size]/[member_id].jpg</code>. </p> <p>The image is rendered in the ERB partial:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="n">image_tag</span> <span class="s2">"https://theunitedstates.io/images/congress/225x275/</span><span class="si">#{</span><span class="n">senator</span><span class="p">.</span><span class="nf">member_id</span><span class="si">}</span><span class="s2">.jpg"</span> </code></pre> </div> <h2> Problem </h2> <p>The existing code works fine, but as new members have been added to the US Congress, the available images are not currently available for the new members. When the image is not available, the browser developer console logs a 404 status code, and the UI displays a broken image, thereby breaking the card layout. So, there needs to be a better way to access these images, and check for availability. </p> <p><strong>The goals</strong>:</p> <ul> <li>Check for a success code when accessing the image, and render this image. </li> <li>Check for unsuccessful access of an image and render a fallback image to preserve the UI layout. </li> </ul> <p>This is too much complexity for the ERB partial, so to DRY out this process we will create a Rails Helper. </p> <h2> Solution </h2> <blockquote> <p>What are helpers in Rails? A helper is a method that is (mostly) used in your Rails views to share reusable code. <br> <a href="proxy.php?url=https://www.rubyguides.com/2020/01/rails-helpers/" rel="noopener noreferrer">RubyGuides</a></p> </blockquote> <p>We are creating a new helper in <code>app/helpers</code> called <code>bio_image_helper.rb</code>. The basic structure of the helper is to create a module and then within the module, a method we will call within our view. We are going to define the URI string in a variable and pass in the <code>member_id</code> in an argument called <code>member</code>. So the basic structure:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">BioImageHelper</span> <span class="k">def</span> <span class="nf">bioimage_helper</span><span class="p">(</span><span class="n">member</span><span class="p">)</span> <span class="n">img_url</span> <span class="o">=</span> <span class="s2">"https://theunitedstates.io/images/congress/225x275/</span><span class="si">#{</span><span class="n">member</span><span class="si">}</span><span class="s2">.jpg"</span> <span class="n">res</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">get_response</span><span class="p">(</span><span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">img_url</span><span class="p">.</span><span class="nf">to_s</span><span class="p">))</span> <span class="n">res</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Net</span><span class="o">::</span><span class="no">HTTPSuccess</span><span class="p">)</span> <span class="p">?</span> <span class="n">img_url</span> <span class="p">:</span> <span class="s1">'backup.png'</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>Remember the second goal was to validate the success of the image response, and offer a fallback image. We will use two libraries to meet these objectives: <code>net/http</code> and <code>uri</code>. With these, we will store to a variable (<code>res</code>) the response object and parse the <code>img_url</code> string. Then use a Ruby ternary to check the response object for an <code>HTTPSuccess</code>, and use the image, or our fallback image.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'net/http'</span> <span class="nb">require</span> <span class="s1">'uri'</span> <span class="k">module</span> <span class="nn">BioImageHelper</span> <span class="k">def</span> <span class="nf">bioimage_helper</span><span class="p">(</span><span class="n">member</span><span class="p">)</span> <span class="n">img_url</span> <span class="o">=</span> <span class="s2">"https://theunitedstates.io/images/congress/225x275/</span><span class="si">#{</span><span class="n">member</span><span class="si">}</span><span class="s2">.jpg"</span> <span class="n">res</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">get_response</span><span class="p">(</span><span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">img_url</span><span class="p">.</span><span class="nf">to_s</span><span class="p">))</span> <span class="n">res</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Net</span><span class="o">::</span><span class="no">HTTPSuccess</span><span class="p">)</span> <span class="p">?</span> <span class="n">img_url</span> <span class="p">:</span> <span class="s1">'backup.png'</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>So, the <code>image_tag</code> in the partial now is abstracted to:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>image_tag bioimage_helper(senator.member_id) </code></pre> </div> <h2> Thanks </h2> <p>Special thanks to the users on the <a href="proxy.php?url=https://discord.gg/uX2sCxxX" rel="noopener noreferrer">Ruby on Rails Discord Server</a> who pointed me in the right direction, with feedback, and code review. </p> <p>Also, the <a href="proxy.php?url=https://stackoverflow.com/questions/7205950/how-to-check-if-an-image-was-found-on-a-web-site" rel="noopener noreferrer">Stack Overflow</a> discussion which helped the most.</p> <h2> Footer </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> ruby rails tutorial webdev Rails Search Bar Chuck Sun, 18 Oct 2020 21:56:01 +0000 https://dev.to/eclecticcoding/rails-search-bar-j77 https://dev.to/eclecticcoding/rails-search-bar-j77 <p>A search bar is an especial feature as a web application grows. There are several ways to implement this feature in a Rails application. This article will explore one of these, by searching a Postgres database with the <code>pg_search</code> gem.</p> <h2> Install pg_search </h2> <p>In a smaller application you can query the database using ActiveRecord, a simple way to prototype search and filtering. It allows you to quickly find related records for databases of fewer than 1000 records, but, as your database grows, ActiveRecord queries can get overly complex and make your app lag.</p> <p>Install the gem as usual. Add <code>gem pg_search</code> to your <code>Gemfile</code> and <code>bundle install</code>.</p> <p>There are two basic search configurations with <code>pg_search</code>, a Single Model search scope or a multi Model configuration. In my case I am only using the Single Model configuration, but you can read more about <a href="proxy.php?url=https://github.com/Casecommons/pg_search#multisearchable" rel="noopener noreferrer">multi-search</a> in the documentation.</p> <p>I am using my <a href="proxy.php?url=https://github.com/eclectic-coding/rails_your_congress" rel="noopener noreferrer">Rails Your Congress</a> as an example, and I am setting up a search bar on the Senator's search page.</p> <h2> Model </h2> <p>To start using <code>pg_search</code>, you need to include <code>PgSearch</code> in your model and set up the <code>pg_search_scope</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Senator</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span> <span class="kp">include</span> <span class="no">PgSearch</span> <span class="n">scope</span> <span class="ss">:sorted</span><span class="p">,</span> <span class="o">-&gt;</span><span class="p">{</span> <span class="n">order</span><span class="p">(</span><span class="ss">last_name: :asc</span><span class="p">)</span> <span class="p">}</span> <span class="n">pg_search_scope</span> <span class="ss">:global_search</span><span class="p">,</span> <span class="ss">against: </span><span class="p">[</span><span class="ss">:first_name</span><span class="p">,</span> <span class="ss">:last_name</span><span class="p">,</span> <span class="ss">:state</span><span class="p">],</span> <span class="ss">using: </span><span class="p">{</span> <span class="ss">tsearch: </span><span class="p">{</span> <span class="ss">prefix: </span><span class="kp">true</span> <span class="p">}</span> <span class="p">}</span> <span class="k">end</span> </code></pre> </div> <p>In this example, I am searching from the Senator resource, by <code>first_name</code>, <code>last_name</code>, and <code>state</code>.</p> <h2> Controller </h2> <p>Since, I am providing a search on the index page, the index method in the Senators controller:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">SenatorsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="k">def</span> <span class="nf">index</span> <span class="k">if</span> <span class="n">params</span><span class="p">[</span><span class="ss">:query</span><span class="p">].</span><span class="nf">present?</span> <span class="vi">@senator_search</span> <span class="o">=</span> <span class="no">Senator</span><span class="p">.</span><span class="nf">global_search</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:query</span><span class="p">])</span> <span class="vi">@senators</span> <span class="o">=</span> <span class="vi">@senator_search</span><span class="p">.</span><span class="nf">paginate</span><span class="p">(</span><span class="ss">page: </span><span class="n">params</span><span class="p">[</span><span class="ss">:page</span><span class="p">],</span> <span class="ss">per_page: </span><span class="mi">20</span><span class="p">)</span> <span class="k">else</span> <span class="vi">@senators</span> <span class="o">=</span> <span class="no">Senator</span><span class="p">.</span><span class="nf">paginate</span><span class="p">(</span><span class="ss">page: </span><span class="n">params</span><span class="p">[</span><span class="ss">:page</span><span class="p">],</span> <span class="ss">per_page: </span><span class="mi">20</span><span class="p">)</span> <span class="k">end</span> <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span> <span class="nb">format</span><span class="p">.</span><span class="nf">html</span> <span class="nb">format</span><span class="p">.</span><span class="nf">json</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">json: </span><span class="p">{</span> <span class="ss">senators: </span><span class="vi">@senators</span> <span class="p">}</span> <span class="p">}</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>In this method, we set a conditional to determine if we are searching or not. If we are searching we are setting two instance variables. The variable <code>@senators</code> is used in the views, and I had to set up two variables to get <code>will_paginate</code> to play nicely with the <code>pg_search</code> results.</p> <h2> Improvements </h2> <p>With each search the entire page re-renders. So next week we will look at how I set up StimulusJS to make the Senator container reactive.</p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails postgres webdev tutorial Rails Multiple Seed Files Chuck Mon, 12 Oct 2020 12:32:19 +0000 https://dev.to/eclecticcoding/rails-multiple-seed-files-3422 https://dev.to/eclecticcoding/rails-multiple-seed-files-3422 <p>If you have a larger Rails project, or a project with many resources, managing one database seed file can get out of hand. In the name of DRY-ing out code, this article walks you through how to abstract your seed data into multiple files.</p> <p>So, the approach is to create a new directory: <code>db/seeds</code>, and had multiple seed files in this location. I think the best approach would be to separate your concerns in your seed files (i.e. books, posts, recipe, users etc). Create multiple seed files to manage easier each resource and place them in this new directory.</p> <p>When you execute <code>rails db:migrate</code>, Rails will only run the file: <code>db/seeds.rb</code>. To fix this, we will use Ruby to parse the new directory for files. So, in the default <code>db/seeds.rb</code> file add the following:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="no">Dir</span><span class="p">[</span><span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">,</span> <span class="s2">"db"</span><span class="p">,</span> <span class="s2">"seeds"</span><span class="p">,</span> <span class="s2">"*.rb"</span><span class="p">)].</span><span class="nf">sort</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">seed</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"seeding - </span><span class="si">#{</span><span class="n">seed</span><span class="si">}</span><span class="s2">. loading seeds, for real!"</span> <span class="nb">load</span> <span class="n">seed</span> <span class="k">end</span> </code></pre> </div> <p>Now, when you run the seed command, it will parse through all of your files and print to the command line exactly which file is loading.</p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails database webdev New Rails Template Chuck Mon, 28 Sep 2020 12:40:01 +0000 https://dev.to/eclecticcoding/new-rails-template-1dp https://dev.to/eclecticcoding/new-rails-template-1dp <p>In this article we take a look at a more complete Rails template, to start a new Rails project. Why? With a custom template, you can start developing your project immediately already preconfigured with the gems, packages, and settings you like to use.</p> <h2> Credit </h2> <p>The inspiration for my custom template starts with Andy Leverenz's <a href="proxy.php?url=https://github.com/justalever/kickoff_tailwind" rel="noopener noreferrer">Kickoff template</a>. There are a few other great resources as well.</p> <p><strong>Jumpstart</strong> Chris Oliver has put together a template called <a href="proxy.php?url=https://github.com/excid3/jumpstart" rel="noopener noreferrer">Jumpstart</a>. This template creates a Boilerplate app that includes user accounts, admin interface, and Bootstrap styling.</p> <p><strong>Jumpstart Pro</strong> Chris also has a commercial product called <a href="proxy.php?url=https://jumpstartrails.com/" rel="noopener noreferrer">Jumpstart Pro</a> that bootstraps a full featured e-commerce application setup.</p> <h2> Template Features </h2> <ul> <li>User accounts with Devise, friendly_id, and name_of_Person</li> <li>Sidekiq</li> <li>Testing environment with RSpec, Guard, Simplecov, and Rubocop</li> <li>User Avatars using Active Storage, with a fallback image using Gravatar</li> <li>Tailwind for styling</li> <li>Fontawesome</li> <li>Stimulus JS and <code>tailwindcss-stimulus-components</code> for the dropdown menu action</li> </ul> <p>TL;TR: The completed repository to the finished custom rails template if you would like to jump straight to the <a href="proxy.php?url=https://github.com/eclectic-coding/eclectic_template" rel="noopener noreferrer">code</a>.</p> <h2> Using a Template </h2> <p>First let's cover the basics of using a template. The <code>rails new</code> command offers many command option flags, and provides a flag to use a custom template:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>rails new myapp -m /path/to/my/template.rb </code></pre> </div> <div class="ltag__link"> <a href="proxy.php?url=/eclecticcoding" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg" alt="eclecticcoding"> </div> </a> <a href="proxy.php?url=/eclecticcoding/rails-boilerplate-4hgf" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Rails Boilerplate</h2> <h3>Chuck ・ Jul 8 '20</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#rails</span> <span class="ltag__link__tag">#ruby</span> <span class="ltag__link__tag">#webdev</span> <span class="ltag__link__tag">#tutorials</span> </div> </div> </a> </div> <p>When this command is executed, Rails will run the script, install and create the new app.</p> <p>Now, to streamline this process you can create a dotfile in the root of your file system: <code>.railsrc</code>. Rails will look for this configuration file first. In our case this is a good start:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>--database=postgresql -T -m /path/to/my/template.rb </code></pre> </div> <p>So, we can start the new project: <code>rails new myapp</code>. Rails will use Postgres database, will not install a testing framework (more on this later), and use our template, without having to type the commands on the command line.</p> <p>If at some time you wan to create a default Rails app, instead of your custom setup: <code>rails new myapp --no-rc</code>.</p> <h2> A Custom Template </h2> <p>If you start to look at the main <code>template.rb</code> in the <a href="proxy.php?url=https://github.com/eclectic-coding/eclectic_template/blob/main/template.rb" rel="noopener noreferrer">codebase</a> you will notice at the top of the file is a series of methods, and the business logic of calling these methods:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="c1"># Main setup</span> <span class="n">source_paths</span> <span class="n">add_gems</span> <span class="n">after_bundle</span> <span class="k">do</span> <span class="n">stop_spring</span> <span class="n">add_testing</span> <span class="n">add_active_storage</span> <span class="n">add_users</span> <span class="n">remove_app_css</span> <span class="n">add_sidekiq</span> <span class="n">add_foreman</span> <span class="n">copy_templates</span> <span class="n">add_tailwind</span> <span class="n">add_fontawesome</span> <span class="n">add_stimulus</span> <span class="n">add_friendly_id</span> <span class="n">copy_postcss_config</span> <span class="n">add_stimulus_navbar</span> <span class="c1"># Migrate</span> <span class="n">rails_command</span> <span class="s2">"db:create"</span> <span class="n">rails_command</span> <span class="s2">"db:migrate"</span> <span class="n">git</span> <span class="ss">:init</span> <span class="n">git</span> <span class="ss">add: </span><span class="s2">"."</span> <span class="n">git</span> <span class="ss">commit: </span><span class="sx">%Q{ -m "Initial commit" }</span> </code></pre> </div> <p>Basically, after the <code>bundle install</code> in completed the methods are executed, it creates the database, a migration is run, and then the first <code>git commit</code> is saved.</p> <p>So, let's go over a few of the methods to show you the power of the API.</p> <h2> Add gems </h2> <p>The <code>add_gem</code> method does not install these gems, it only adds the gems to the Gemfile. If you examine the method you will notice we are setting up gems associated with users, setting up a testing environment, and Rubocop.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">add_gems</span> <span class="n">gem</span> <span class="s2">"devise"</span><span class="p">,</span> <span class="s2">"~&gt; 4.7"</span><span class="p">,</span> <span class="s2">"&gt;= 4.7.2"</span> <span class="n">gem</span> <span class="s2">"friendly_id"</span><span class="p">,</span> <span class="s2">"~&gt; 5.3"</span> <span class="n">gem</span> <span class="s2">"image_processing"</span> <span class="n">gem</span> <span class="s2">"sidekiq"</span><span class="p">,</span> <span class="s2">"~&gt; 6.1"</span><span class="p">,</span> <span class="s2">"&gt;= 6.1.1"</span> <span class="n">gem</span> <span class="s2">"name_of_person"</span><span class="p">,</span> <span class="s2">"~&gt; 1.1"</span><span class="p">,</span> <span class="s2">"&gt;= 1.1.1"</span> <span class="n">gem_group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span> <span class="n">gem</span> <span class="s2">"database_cleaner"</span> <span class="n">gem</span> <span class="s2">"factory_bot_rails"</span><span class="p">,</span> <span class="ss">git: </span><span class="s2">"http://github.com/thoughtbot/factory_bot_rails"</span> <span class="n">gem</span> <span class="s2">"rspec-rails"</span> <span class="k">end</span> <span class="n">gem_group</span> <span class="ss">:development</span> <span class="k">do</span> <span class="n">gem</span> <span class="s2">"fuubar"</span> <span class="n">gem</span> <span class="s2">"guard"</span> <span class="n">gem</span> <span class="s2">"guard-rspec"</span> <span class="n">gem</span> <span class="s2">"rubocop"</span> <span class="n">gem</span> <span class="s2">"rubocop-rails"</span><span class="p">,</span> <span class="ss">require: </span><span class="kp">false</span> <span class="n">gem</span> <span class="s2">"rubocop-rspec"</span> <span class="k">end</span> <span class="n">gem_group</span> <span class="ss">:test</span> <span class="k">do</span> <span class="n">gem</span> <span class="s1">'simplecov'</span><span class="p">,</span> <span class="ss">require: </span><span class="kp">false</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>NOTE: After your app is set up you will probably want to clean up your Gemfile. All the above gems and groups are added to the bottom of the Gemfile.</p> <p>If you have been following my articles you know I have written about this Rails Testing Setup before:</p> <div class="ltag__link"> <a href="proxy.php?url=/eclecticcoding" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg" alt="eclecticcoding"> </div> </a> <a href="proxy.php?url=/eclecticcoding/rails-testing-setup-nla" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Rails Testing Setup</h2> <h3>Chuck ・ Aug 27 '20</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#rails</span> <span class="ltag__link__tag">#testing</span> <span class="ltag__link__tag">#ruby</span> </div> </div> </a> </div> <h2> Testing </h2> <p>I noted previously, in the <code>.railsrc</code> file, we did not install the default Rails testing environment. In the <code>add_testing</code> method we are setting up Rspec.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">add_testing</span> <span class="n">generate</span> <span class="s2">"rspec:install"</span> <span class="n">directory</span> <span class="s2">"spec"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">run</span> <span class="s2">"rm -r test"</span> <span class="k">if</span> <span class="no">Dir</span><span class="p">.</span><span class="nf">exist?</span><span class="p">(</span><span class="s2">"test"</span><span class="p">)</span> <span class="n">copy_file</span> <span class="s2">"config/webpacker.yml"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">copy_file</span> <span class="s2">".rspec"</span><span class="p">,</span> <span class="ss">force: </span><span class="kp">true</span> <span class="n">copy_file</span> <span class="s2">".rubocop.yml"</span> <span class="n">copy_file</span> <span class="s2">".simplecov"</span> <span class="n">copy_file</span> <span class="s2">"Guardfile"</span> <span class="k">end</span> </code></pre> </div> <p>Remember, this method will run after <code>bundle install</code>, so we are only configuring the gems here. Since we are using a repository, instead of just a simple template file, we can copy preconfigured files and directories:</p> <ul> <li>We are copying the <code>spec</code> directory and force overwriting the directory in the new app.</li> <li>In case the user does not have -T in the <code>.railsrc</code> file, we are deleting the <code>test</code> directory if it exists.</li> </ul> <p>You can read more about the Template API at <a href="proxy.php?url=https://guides.rubyonrails.org/rails_application_templates.html" rel="noopener noreferrer">Railsguides</a>.</p> <h2> Modifying Files </h2> <p>In the creating process we can also modify files. For instance, in the <code>add_tailwind</code> method:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="n">append_to_file</span><span class="p">(</span><span class="s2">"app/javascript/packs/application.js"</span><span class="p">,</span> <span class="s1">'import "stylesheets/application"'</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span> <span class="n">inject_into_file</span><span class="p">(</span><span class="s2">"./postcss.config.js"</span><span class="p">,</span> <span class="s2">"let tailwindcss = require('tailwindcss');</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="ss">before: </span><span class="s2">"module.exports"</span><span class="p">)</span> <span class="n">inject_into_file</span><span class="p">(</span><span class="s2">"./postcss.config.js"</span><span class="p">,</span> <span class="s2">"</span><span class="se">\n</span><span class="s2"> tailwindcss('./app/javascript/stylesheets/tailwind.config.js'),"</span><span class="p">,</span> <span class="ss">after: </span><span class="s2">"plugins: ["</span><span class="p">)</span> </code></pre> </div> <p>In this case we can <code>append_to_file</code> an import. Also, we are injecting into a file at a specific point (before of after some text).</p> <p>We can also inject at the end of a file a block of inserts:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="n">inject_into_file</span> <span class="s1">'app/javascript/controllers/index.js'</span> <span class="k">do</span> <span class="o">&lt;&lt;~</span><span class="no">EOF</span><span class="sh"> // Import and register all TailwindCSS Components import { Dropdown, Modal, Tabs, Popover, Toggle } from "tailwindcss-stimulus-components" application.register('dropdown', Dropdown) application.register('modal', Modal) application.register('tabs', Tabs) application.register('popover', Popover) application.register('toggle', Toggle) </span><span class="no">EOF</span> <span class="k">end</span> </code></pre> </div> <p>The Template API is fairly robust and offers a way to streamline the startup time of a new project. This is my personal setup. Clone the repository and give it a try, tweak it for your own, and if something doesn't work, open a PR.</p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby webdev tutorial Part 2: Rails Gravatar Chuck Wed, 23 Sep 2020 12:19:35 +0000 https://dev.to/eclecticcoding/testing-426g https://dev.to/eclecticcoding/testing-426g <p>In the previous article in this series, we learned about Active Storage. We set up an interface to allow a user to add an Avatar to their user profile. </p> <div class="ltag__link"> <a href="proxy.php?url=/eclecticcoding" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69818%2F4ac54d27-6960-4a16-9615-bfbff7176484.jpg" alt="eclecticcoding"> </div> </a> <a href="proxy.php?url=/eclecticcoding/part-1-rails-active-storage-1ikh" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Part 1: Rails Active Storage</h2> <h3>Chuck ・ Sep 22 '20</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#rails</span> <span class="ltag__link__tag">#ruby</span> <span class="ltag__link__tag">#webdev</span> </div> </div> </a> </div> <p>Part Two of this two part series, we extend this feature to use, as a fallback image, the Gravatar service.</p> <p>The goal is to check if the user has an attached image. If they do not, then fall back to the image from the <a href="proxy.php?url=https://en.gravatar.com/" rel="noopener noreferrer">Gravatar</a> service. According to the Gravatar website:</p> <blockquote> <p>Your Gravatar is an image that follows you from site to site appearing beside your name when you do things like comment or post on a blog</p> </blockquote> <p>What is interesting is that if the user has not provided an Avatar image for their profile at Gravatar, then it defaults to a standard image. So, essentially it creates redundancy in our case.</p> <p>TL;TR: The completed repository if you would like to jump straight to the <a href="proxy.php?url=https://github.com/eclectic-coding/article_active_storage" rel="noopener noreferrer">code</a>.</p> <h2> Helper method </h2> <p>So, we need to create a helper that we will locate in <code>app/helpers/application_helpers.rb</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">avatar_url_for</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span> <span class="n">size</span> <span class="o">=</span> <span class="n">opts</span><span class="p">[</span><span class="ss">:size</span> <span class="o">||</span> <span class="mi">32</span><span class="p">]</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">avatar</span><span class="p">.</span><span class="nf">attached?</span> <span class="n">user</span><span class="p">.</span><span class="nf">avatar</span><span class="p">.</span><span class="nf">variant</span><span class="p">(</span> <span class="ss">resize: </span><span class="s2">"</span><span class="si">#{</span><span class="n">size</span><span class="si">}</span><span class="s2">x</span><span class="si">#{</span><span class="n">size</span><span class="si">}</span><span class="s2">!"</span> <span class="p">)</span> <span class="k">else</span> <span class="nb">hash</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">MD5</span><span class="p">.</span><span class="nf">hexdigest</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">email</span><span class="p">.</span><span class="nf">downcase</span><span class="p">)</span> <span class="s2">"https://secure.gravatar.com/avatar/</span><span class="si">#{</span><span class="nb">hash</span><span class="si">}</span><span class="s2">.png?s=</span><span class="si">#{</span><span class="n">size</span><span class="si">}</span><span class="s2">"</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <p>the method accepts two arguments, the user and an optional option hash. This hash is where we control the image size, which defaults to 32px. The conditional logic checks for the attached avatar, uses a user selected avatar or reverts to a Gravatar, and resizes based on the options array.</p> <h2> Navbar </h2> <p>In the previous article we checked for the user's avatar like so:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="o">&lt;</span><span class="n">li</span> <span class="k">class</span><span class="o">=</span><span class="s2">"nav-item"</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="sx">%= link_to user_path(current_user.username), class: "nav-link" do %&gt; &lt;% if current_user.avatar.nil? %&gt; &lt;%=</span> <span class="n">image_tag</span> <span class="n">current_user</span><span class="p">.</span><span class="nf">avatar</span><span class="p">.</span><span class="nf">variant</span><span class="p">(</span><span class="ss">resize: </span><span class="s2">"24x24!"</span><span class="p">),</span> <span class="ss">class: </span><span class="s2">"mr-1"</span> <span class="o">%&gt;</span> <span class="o">&lt;</span><span class="sx">% end </span><span class="o">%&gt;</span> <span class="o">&lt;</span><span class="sx">%= current_user.username %&gt; &lt;% end %&gt; &lt;/li&gt; </span></code></pre> </div> <p>We can edit this code to use our helper, which includes the logic to check for an avatar. This DRY's out our view file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="o">&lt;</span><span class="n">li</span> <span class="k">class</span><span class="o">=</span><span class="s2">"nav-item"</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="sx">%= link_to user_path(current_user.username), class: "nav-link" do %&gt; &lt;%=</span> <span class="n">image_tag</span> <span class="n">avatar_url_for</span><span class="p">(</span><span class="n">current_user</span><span class="p">,</span> <span class="ss">size: </span><span class="mi">24</span><span class="p">),</span> <span class="ss">class: </span><span class="s2">"rounded-circle mr-1"</span> <span class="o">%&gt;</span> <span class="o">&lt;</span><span class="sx">%= current_user.username %&gt; &lt;% end %&gt; &lt;/li&gt; </span></code></pre> </div> <p>So, if the user hasn't setup an avatar on Gravatar or their user profile the fallback image is used:<br> <a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faz8b86x4m18laretdy5y.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faz8b86x4m18laretdy5y.png" alt="" width="401" height="181"></a></p> <h2> Edit profile </h2> <p>We need to update the <code>edit.html.erb</code> file to use the helper logic. Take a look at the complete <a href="proxy.php?url=https://github.com/eclectic-coding/article_rails_gravatar/blob/main/app/views/devise/registrations/edit.html.erb" rel="noopener noreferrer">source</a> for this file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"row"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"col-sm-2"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">image_tag</span> <span class="na">avatar_url_for</span><span class="err">(</span><span class="na">current_user</span><span class="err">,</span> <span class="na">size:</span> <span class="err">128),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">rounded-circle</span> <span class="na">m-4</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"col-sm-10"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.label</span> <span class="na">:avatar</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.file_field</span> <span class="na">:avatar</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <h2> Show profile </h2> <p>Finally, we need to update the <code>edit.html.erb</code> file to use the helper logic:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex align-items-center justify-content-center mt-5"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media mr-5 align-self-start"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">image_tag</span> <span class="na">avatar_url_for</span><span class="err">(</span><span class="na">current_user</span><span class="err">,</span> <span class="na">size:</span> <span class="err">128),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">rounded-circle</span> <span class="na">mr-4</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media-body"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex flex-row align-items-center justify-content-between"</span><span class="nt">&gt;</span> <span class="nt">&lt;h1&gt;&lt;</span><span class="err">%=</span> <span class="err">@</span><span class="na">user.username</span> <span class="err">%</span><span class="nt">&gt;&lt;/h1&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Edit</span><span class="err">",</span> <span class="na">edit_user_registration_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">ml-3</span> <span class="na">btn</span> <span class="na">btn-secondary</span> <span class="na">btn-sm</span><span class="err">"</span> <span class="na">if</span> <span class="na">current_user.id =</span><span class="s">=</span> <span class="err">@</span><span class="na">user.id</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <h2> Footnote </h2> <p>This has been fun. I hope you have enjoyed this short article series and it has been in some way helpful. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby webdev Part 1: Rails Active Storage Chuck Tue, 22 Sep 2020 14:06:37 +0000 https://dev.to/eclecticcoding/part-1-rails-active-storage-1ikh https://dev.to/eclecticcoding/part-1-rails-active-storage-1ikh <p>On any web application, the ability to use images are tantamount. In a Ruby on Rails project, using Active Storage increases the flexibility to use external storage services and to seamlessly create user interaction.</p> <p>In this article we will use Active Storage to allow a user to add an Avatar to their user profile. This Avatar will display on their Profile page and in the User Profile link in the Navbar.</p> <p>TL;TR: The completed repository if you would like to jump straight to the <a href="proxy.php?url=https://github.com/eclectic-coding/article_active_storage" rel="noopener noreferrer">code</a>.</p> <h2> Setup </h2> <p>This is not a complete tutorial on the setup of our Rails project. Here are the basic features below. I do suggest you look at the repository.</p> <ul> <li>Basic Rails project with Postgres</li> <li>A controller called <code>static</code> for the home page, and the root route specified as <code>static#home</code>.</li> <li>Add Bootstrap 4 using Webpacker</li> <li>Add Devise with the additional registration field of username and name added to the migration</li> <li>Use the gem <code>devise-bootstrapped</code> to preconfigure the Devise views with bootstrap styling.</li> </ul> <p>Add the following Bootstrap navbar:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;nav</span> <span class="na">class=</span><span class="s">"navbar navbar-expand-lg navbar-dark bg-dark mb-3"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Active</span> <span class="na">Storage</span><span class="err">",</span> <span class="na">root_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">navbar-brand</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">"navbar-toggler"</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">data-toggle=</span><span class="s">"collapse"</span> <span class="na">data-target=</span><span class="s">"#navbarNav"</span> <span class="na">aria-controls=</span><span class="s">"navbarNav"</span> <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">aria-label=</span><span class="s">"Toggle navigation"</span><span class="nt">&gt;</span> <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"navbar-toggler-icon"</span><span class="nt">&gt;&lt;/span&gt;</span> <span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"collapse navbar-collapse"</span> <span class="na">id=</span><span class="s">"navbarNav"</span><span class="nt">&gt;</span> <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"navbar-nav ml-auto"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">user_signed_in</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="na">current_user.username</span><span class="err">,</span> <span class="na">user_path</span><span class="err">(</span><span class="na">current_user.username</span><span class="err">),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Sign</span> <span class="na">out</span><span class="err">",</span> <span class="na">root_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">else</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item active"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Log</span> <span class="na">in</span><span class="err">",</span> <span class="na">root_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Sign</span> <span class="na">up</span><span class="err">",</span> <span class="na">root_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/ul&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/nav&gt;</span> </code></pre> </div> <p>Create a profile page for the user:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex align-items-center justify-content-center mt-5"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media mr-5 align-self-start"</span><span class="nt">&gt;</span> Avatar <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media-body"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex flex-row align-items-center justify-content-between"</span><span class="nt">&gt;</span> <span class="nt">&lt;h1&gt;&lt;</span><span class="err">%=</span> <span class="err">@</span><span class="na">user.username</span> <span class="err">%</span><span class="nt">&gt;&lt;/h1&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Edit</span><span class="err">",</span> <span class="na">edit_user_registration_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">ml-3</span> <span class="na">btn</span> <span class="na">btn-secondary</span> <span class="na">btn-sm</span><span class="err">"</span> <span class="na">if</span> <span class="na">current_user.id =</span><span class="s">=</span> <span class="err">@</span><span class="na">user.id</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <p>Add the route for the profile page: <code>resources :users, only: [:show], param: :username, path: ""</code></p> <p>Again, this is an overview of the setup, just look at this <a href="proxy.php?url=https://github.com/eclectic-coding/article_active_storage/tree/dfb6af23e8234c65f0d818a7b66d04ddf0f7fd8e" rel="noopener noreferrer">commit</a> for the complete <a href="proxy.php?url=https://github.com/eclectic-coding/article_active_storage/tree/dfb6af23e8234c65f0d818a7b66d04ddf0f7fd8e" rel="noopener noreferrer">beginning source</a>.</p> <h2> Active Storage </h2> <p>Active Storage gives us the option to use different storage services. We start by configuring the development environment, by adding the following to <code>config/environments/development.rb</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="c1"># Store files locally.</span> <span class="n">config</span><span class="p">.</span><span class="nf">active_storage</span><span class="p">.</span><span class="nf">service</span> <span class="o">=</span> <span class="ss">:local</span> </code></pre> </div> <p>If you want to use Amazon S3 service in production, you add the following to <code>config/environments/production.rb</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="c1"># Store files on Amazon S3.</span> <span class="n">config</span><span class="p">.</span><span class="nf">active_storage</span><span class="p">.</span><span class="nf">service</span> <span class="o">=</span> <span class="ss">:amazon</span> </code></pre> </div> <p>Refer to the <a href="proxy.php?url=https://edgeguides.rubyonrails.org/active_storage_overview.html#setup" rel="noopener noreferrer">documentation</a> for more configuration options in production.</p> <p>Remember Active Storage is part of Rails, so you just need to install to configure: <code>rails active_storage:install</code>, then run the migration: <code>rails db:migrate</code>.</p> <p>Active Storage uses two tables in your application’s database named <code>active_storage_blobs</code> and <code>active_storage_attachments</code>. The <code>active_storage_attachments</code> is a polymorphic join table that stores your model's class name.</p> <p>In the <code>Gemfile</code>, uncomment and <code>bundle</code> install <code>image_processing</code>. This gem allows us to resize our images. Make sure to restart your rails server.</p> <h2> Avatar </h2> <p>Add to the User model: <code>has_one_attached :avatar</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span> <span class="c1"># Include default devise modules. Others available are:</span> <span class="c1"># :confirmable, :lockable, :timeoutable, :trackable and :omniauthable</span> <span class="n">devise</span> <span class="ss">:database_authenticatable</span><span class="p">,</span> <span class="ss">:registerable</span><span class="p">,</span> <span class="ss">:recoverable</span><span class="p">,</span> <span class="ss">:rememberable</span><span class="p">,</span> <span class="ss">:validatable</span> <span class="n">has_one_attached</span> <span class="ss">:avatar</span> <span class="k">end</span> </code></pre> </div> <p>We need to update the permitted parameter's method in the application controller for the <code>avatar</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span> <span class="n">before_action</span> <span class="ss">:configure_permitted_parameters</span><span class="p">,</span> <span class="ss">if: :devise_controller?</span> <span class="kp">private</span> <span class="k">def</span> <span class="nf">configure_permitted_parameters</span> <span class="n">devise_parameter_sanitizer</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:sign_up</span><span class="p">,</span> <span class="ss">keys: </span><span class="p">[</span><span class="ss">:username</span><span class="p">])</span> <span class="n">devise_parameter_sanitizer</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:account_update</span><span class="p">,</span> <span class="ss">keys: </span><span class="sx">%i[avatar name username]</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> </code></pre> </div> <h2> Edit Profile </h2> <p>First, let's update the 'Edit Profile' form, so we can select an Avatar. You will find the template in <code>app/views/devise/registrations/edit.html.erb</code>.</p> <p>We need to add <code>name</code>, and <code>username</code> to the edit form:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.label</span> <span class="na">:username</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.text_field</span> <span class="na">:username</span><span class="err">,</span> <span class="na">autofocus:</span> <span class="na">true</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.text_field</span> <span class="na">:name</span><span class="err">,</span> <span class="na">autofocus:</span> <span class="na">true</span><span class="err">,</span> <span class="na">placeholder:</span> <span class="err">"</span><span class="na">Name</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <p>We need to add a place to display the Avatar, and a form picker to select the file. This will be placed at the top of the file. Take a look at the complete <a href="proxy.php?url=https://github.com/eclectic-coding/article_active_storage/blob/main/app/views/devise/registrations/edit.html.erb" rel="noopener noreferrer">source</a> for this file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"row"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"col-sm-2"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">current_user.avatar.nil</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">image_tag</span> <span class="na">f.object.avatar.variant</span><span class="err">(</span><span class="na">resize:</span> <span class="err">"128</span><span class="na">x128</span><span class="err">!"),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">rounded-circle</span> <span class="na">m-4</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"col-sm-10"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.label</span> <span class="na">:avatar</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">f.file_field</span> <span class="na">:avatar</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <p>We are testing if there is an Avatar that is associated with the current user, then displaying the image by using an <code>image_tag</code>, resized to 128px, a feature of the <code>image_processing</code> gem. We are using the Bootstrap classes to display the avatar as a rounded circle.</p> <h2> User Profile Page </h2> <p>So, we need to revisit our User <code>show.html.erb</code> template file to include the newly selected avatar.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex align-items-center justify-content-center mt-5"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media mr-5 align-self-start"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">current_user.avatar.nil</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">image_tag</span> <span class="na">f.object.avatar.variant</span><span class="err">(</span><span class="na">resize:</span> <span class="err">"128</span><span class="na">x128</span><span class="err">!"),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">rounded-circle</span> <span class="na">m-4</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"media-body"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"d-flex flex-row align-items-center justify-content-between"</span><span class="nt">&gt;</span> <span class="nt">&lt;h1&gt;&lt;</span><span class="err">%=</span> <span class="err">@</span><span class="na">user.username</span> <span class="err">%</span><span class="nt">&gt;&lt;/h1&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Edit</span><span class="err">",</span> <span class="na">edit_user_registration_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">ml-3</span> <span class="na">btn</span> <span class="na">btn-secondary</span> <span class="na">btn-sm</span><span class="err">"</span> <span class="na">if</span> <span class="na">current_user.id =</span><span class="s">=</span> <span class="err">@</span><span class="na">user.id</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <p>This is the same code we used for the edit template. This is the result:</p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq6me3ptx22c6h5mnek6l.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq6me3ptx22c6h5mnek6l.png" alt="'Screenshot of User Profile with new Avatar'" width="507" height="162"></a></p> <h2> Navbar </h2> <p>Originally, I had placeholder paths in the Navbar, so we revisit to make the links workable. Also, we will include the avatar, and a check if one exist.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;nav</span> <span class="na">class=</span><span class="s">"navbar navbar-expand-lg navbar-dark bg-dark mb-3"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Active</span> <span class="na">Storage</span><span class="err">",</span> <span class="na">root_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">navbar-brand</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">"navbar-toggler"</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">data-toggle=</span><span class="s">"collapse"</span> <span class="na">data-target=</span><span class="s">"#navbarNav"</span> <span class="na">aria-controls=</span><span class="s">"navbarNav"</span> <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">aria-label=</span><span class="s">"Toggle navigation"</span><span class="nt">&gt;</span> <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"navbar-toggler-icon"</span><span class="nt">&gt;&lt;/span&gt;</span> <span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"collapse navbar-collapse"</span> <span class="na">id=</span><span class="s">"navbarNav"</span><span class="nt">&gt;</span> <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"navbar-nav ml-auto"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">user_signed_in</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="na">user_path</span><span class="err">(</span><span class="na">current_user.username</span><span class="err">),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="na">do</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">if</span> <span class="na">current_user.avatar.nil</span><span class="err">?</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">image_tag</span> <span class="na">current_user.avatar.variant</span><span class="err">(</span><span class="na">resize:</span> <span class="err">"24</span><span class="na">x24</span><span class="err">!"),</span> <span class="na">class:</span> <span class="err">"</span><span class="na">mr-1</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">current_user.username</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Sign</span> <span class="na">out</span><span class="err">",</span> <span class="na">destroy_user_session_path</span><span class="err">,</span> <span class="na">method:</span> <span class="na">:delete</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">else</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item active"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Log</span> <span class="na">in</span><span class="err">",</span> <span class="na">new_user_session_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">link_to</span> <span class="err">"</span><span class="na">Sign</span> <span class="na">up</span><span class="err">",</span> <span class="na">new_user_registration_path</span><span class="err">,</span> <span class="na">class:</span> <span class="err">"</span><span class="na">nav-link</span><span class="err">"</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;</span><span class="err">%</span> <span class="na">end</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;/ul&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/nav&gt;</span> </code></pre> </div> <h2> Not Perfect </h2> <p>So, this solution is not entirely practical. I do not like the approach where we are displaying a blank space if the avatar does not exist. In the next article we will build on this feature by using the users <a href="proxy.php?url=https://en.gravatar.com/" rel="noopener noreferrer">Gravatar</a> as an optional or fallback image.</p> <h2> Footnote </h2> <p>This has been fun. Leave a comment or send me a DM on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a>.</p> <p>Shameless Plug: If you work at a great company and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on <a href="proxy.php?url=http://twitter.com/EclecticCoding" rel="noopener noreferrer">Twitter</a> and check out my <a href="proxy.php?url=http://www.linkedin.com/in/dev-chuck-smith" rel="noopener noreferrer">LinkedIn</a>.</p> rails ruby webdev