<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kayla Reopelle</title>
    <description>The latest articles on DEV Community by Kayla Reopelle (@kreopelle).</description>
    <link>https://dev.to/kreopelle</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F103098%2F4f9802ef-8bd5-48a1-bc66-ac18e90fa479.jpeg</url>
      <title>DEV Community: Kayla Reopelle</title>
      <link>https://dev.to/kreopelle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kreopelle"/>
    <language>en</language>
    <item>
      <title>What's a Dual Boot?</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Sat, 28 Mar 2020 17:53:31 +0000</pubDate>
      <link>https://dev.to/kreopelle/what-s-a-dual-boot-569n</link>
      <guid>https://dev.to/kreopelle/what-s-a-dual-boot-569n</guid>
      <description>&lt;p&gt;I was recently asked about using a dual boot strategy for Rails upgrades and thought I'd share my response as a blog post in case others may be interested in a high-level overview of the approach.&lt;/p&gt;

&lt;p&gt;I'd highly recommend this strategy for an application upgrade if the entire development can't stop feature development and shift their focus to the upgrade. &lt;/p&gt;

&lt;p&gt;The idea behind a dual boot is to have two Gemfiles, one the contains the current, production application's dependencies. The other contains the dependencies required for an upgrade, often referred to as &lt;code&gt;next&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The code that differs between the old and next versions of Rails is activated by a conditional that uses a method, normally named &lt;code&gt;next?&lt;/code&gt; to determine which part should be running. &lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if next?   
  # code compatible with the next version of Rails
else  
  # existing code that works for the app's current version of Rails
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After the dual Gemfiles are set up, run the test suite in the &lt;code&gt;next&lt;/code&gt; version and start fixing specs, adding conditionals where necessary to get the code to run on the next version. Check your changes by running the test suite on the current version to make sure that something that was fixed didn't inadvertently break something that was working on the live app.&lt;/p&gt;

&lt;p&gt;Everyone's work is done branched off of master, so there's no messy rebasing or git pulling practices that need to be in place to balance the upgrade team's work with the feature team's work.&lt;/p&gt;

&lt;p&gt;Here are some resources I found helpful while implementing a dual boot for a Rails 2 to Rails 4 upgrade:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/clio/ten_years_rails"&gt;Ten Years Rails Gem&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Creates a symlinked Gemfile.next to have two separate bundles, one for the current version of Rails and the other for the version you're upgrading to&lt;/li&gt;
&lt;li&gt;To separate code activated on the old version vs. the upgraded version, wrap the code in a conditional statement &lt;code&gt;if next?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;next?&lt;/code&gt; changes the BUNDLE_GEMFILE to use the Gemfile.next versions&lt;/li&gt;
&lt;li&gt;There's a talk linked on the repo I'd recommend watching&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.blog/2018-09-28-upgrading-github-from-rails-3-2-to-5-2/"&gt;Upgrading Github from Rails 3.2 to Rails 5.2&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Written by Eileen Uchitelle who led the upgrade effort. They used a dual boot and now run the app off of Rails master&lt;/li&gt;
&lt;li&gt;She's given a lot of podcast interviews as well, Google her name for more resources&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://engineering.shopify.com/blogs/engineering/upgrading-shopify-to-rails-5-0"&gt;Upgrading Shopify to Rails 5&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Shopify also used this strategy to upgrade, their journey is similar to Github's but a little different&lt;/li&gt;
&lt;li&gt;They also have their own dual boot gem, &lt;a href="https://github.com/Shopify/bootboot"&gt;boot boot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Also great from Shopify is &lt;a href="https://www.youtube.com/watch?v=I-2Xy3RS1ns"&gt;this talk by Rafael França&lt;/a&gt; about their upgrade&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/amplifr/how-to-migrate-monolith-to-the-scary-new-version-of-rails-3o52"&gt;How to Migrate Monotlith to the Scary New Version of Rails&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Excellent breakdown of how the dual boot works, with more awesome links at the end&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do you have any questions about dual booting? What are your favorite Rails upgrade tips and strategies? Post them below! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>upgrades</category>
      <category>dualboot</category>
    </item>
    <item>
      <title>Are vulnerabilities for testing dependencies a threat to my app in production? </title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Mon, 06 Jan 2020 14:44:22 +0000</pubDate>
      <link>https://dev.to/kreopelle/are-vulnerabilities-for-testing-dependencies-a-threat-to-my-app-in-production-4017</link>
      <guid>https://dev.to/kreopelle/are-vulnerabilities-for-testing-dependencies-a-threat-to-my-app-in-production-4017</guid>
      <description>&lt;p&gt;&lt;strong&gt;Some background&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;I'm working on a React application my agency recently picked up. The application has existed for ~2 years. I ran &lt;code&gt;npm audit&lt;/code&gt; last week and discovered the app has quite a few high-level security vulnerabilities. &lt;/p&gt;

&lt;p&gt;Most of them are coming from &lt;code&gt;jest&lt;/code&gt;, a testing framework for React. We'll need to upgrade the package by a few major versions to resolve the vulnerabilities. &lt;/p&gt;

&lt;p&gt;This could be a lot of work to take on right now, but I don't know if it should be a high priority. I'm not sure if this dependency has any impact on my app's security in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My question:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Are vulnerabilities from my testing framework a threat to my app in production?&lt;/p&gt;

&lt;p&gt;Any advice would be appreciated! Thank you!&lt;/p&gt;

</description>
      <category>help</category>
    </item>
    <item>
      <title>Polyglot programming</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Mon, 30 Dec 2019 00:16:25 +0000</pubDate>
      <link>https://dev.to/kreopelle/polyglot-programming-414</link>
      <guid>https://dev.to/kreopelle/polyglot-programming-414</guid>
      <description>&lt;p&gt;One of my favorite things about programming so far has been comparing different languages. My introduction to coding was through a Codecademy course on Python, then I studied JavaScript for Boot Camp Prep and am working my way through lessons in Ruby. I was thrilled to find the Cartoon Collections lesson in the Ruby curriculum, as I had completed the same exercises in JavaScript a few months before. What were the different tools and approaches I would access to tackle the languages? How had my programming skills developed? Is there some similarity between the way the mind adapts after it learns a second spoken language and how it functions after learning a second programming language? &lt;/p&gt;

&lt;p&gt;As someone who still considers herself very much a beginner in both languages, here are my assessments while comparing the code I wrote in both exercises:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RUBY&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terser&lt;/li&gt;
&lt;li&gt;More linguistic/English Focused&lt;/li&gt;
&lt;li&gt;Easier for non-programmers to follow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;JAVASCRIPT&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Potentially unnecessary loops included&lt;/li&gt;
&lt;li&gt;More algebraic&lt;/li&gt;
&lt;li&gt;Shorthand/abbreviation &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far, the basic elements of Ruby feel quite similar to JavaScript, square brackets are connected to Arrays, quotation marks to Strings, parentheses to arguments (&lt;a href="http://www.gavilan.edu/csis/languages/parentheses.html"&gt;which, from this article, seems to derive from their connection to the C language pattern&lt;/a&gt;) .  &lt;/p&gt;

&lt;p&gt;Though there has been some mental translation while transitioning to Ruby, such as remembering: &lt;br&gt;
&lt;em&gt;methods&lt;/em&gt; are &lt;em&gt;functions&lt;/em&gt;,&lt;br&gt;
&lt;em&gt;for&lt;/em&gt; is the same as &lt;em&gt;each&lt;/em&gt;,&lt;br&gt;
&lt;em&gt;else if&lt;/em&gt; becomes &lt;em&gt;elsif&lt;/em&gt;,&lt;br&gt;
&lt;em&gt;objects&lt;/em&gt; are &lt;em&gt;hashes&lt;/em&gt;,&lt;/p&gt;

&lt;p&gt;More practice brings more familiarity and comfort. The process has reminded me very much of trying to learn German. The clunky recitation of phrases during common conversations with cashiers or passersby is beginning to morph into a gray area. I am both &lt;em&gt;bereit&lt;/em&gt; and &lt;em&gt;ready&lt;/em&gt; because I now feel them as the same concept. &lt;/p&gt;

&lt;p&gt;Is there some similarity between the way the mind adapts after it learns a second spoken language and how it functions after learning a second programming language? I’m learning to perceive concepts more abstractly. With the relative youth of programming, I can’t help but wonder — will programming languages become more unified as time goes on? The shift to including &lt;em&gt;colons :&lt;/em&gt; when defining hashes in Ruby instead of just the &lt;em&gt;hash rocket =&amp;gt;&lt;/em&gt; feels like a narrowing may be in progress. Will programming dialects morph into more homogenized practices across languages? Does preserving the culture of these languages share similarities with preserving rapidly vanishing languages around the world?&lt;/p&gt;

&lt;p&gt;Like most things with programming, I still have more questions than answers.&lt;/p&gt;

&lt;p&gt;I’m looking forward to deepening my understanding of the vibrant world of programming languages; from the powerhouse languages taught in the Flatiron School’s Online Web Developer curriculum to earlier languages such as BASIC or even more obscure languages like &lt;a href="https://esolangs.org/wiki/Omgrofl"&gt;Omgrofl&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>beginners</category>
    </item>
    <item>
      <title>DIY - Developing a better life</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Mon, 30 Dec 2019 00:14:42 +0000</pubDate>
      <link>https://dev.to/kreopelle/diy-developing-a-better-life-1hm2</link>
      <guid>https://dev.to/kreopelle/diy-developing-a-better-life-1hm2</guid>
      <description>&lt;h1&gt;
  
  
  DIY - Developing a better life
&lt;/h1&gt;

&lt;p&gt;Left with triple figures of student debt, an itch to settle down, and uncertainty about my next freelance gig — I knew something needed to change. &lt;/p&gt;

&lt;p&gt;I noticed my hobbies revolve around experiencing how things work:&lt;/p&gt;

&lt;p&gt;Twisting the hair of a sheep into a sweater.&lt;br&gt;
Coaxing water, flour, salt, yeast and time into steaming loaves.&lt;br&gt;
Observing bees, legs heavy with pollen, turning dust into honey.&lt;/p&gt;

&lt;p&gt;My interest in understanding how the web worked, how computers worked, was daunted by the vast resources online (and my dial-up internet connection), to create something on my own. During college, I was introduced to new media — combining technology, interactivity, and information to craft new experiences and ways of learning. As a documentary film student, it got my mind buzzing about apps and sites to create, but I didn’t have the skills to create them. I moved into nonprofit communications and film producing after graduation and my ideas faded away until a few of my friends got into tech.&lt;/p&gt;

&lt;p&gt;I started hanging out with app developers, people who helped developers connect with companies, and software programmers. As they spoke about their work, it piqued my interest. Maybe I could learn about this too?&lt;/p&gt;

&lt;p&gt;I decided to learn software development first to get under the hood of the apps, websites, and technology I use every day, and second, to try to develop a life that better supports my lifelong love of learning.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>bootcamp</category>
      <category>career</category>
    </item>
    <item>
      <title>Building the CLI Data Gem Project</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Mon, 30 Dec 2019 00:10:43 +0000</pubDate>
      <link>https://dev.to/kreopelle/building-the-cli-data-gem-project-5d69</link>
      <guid>https://dev.to/kreopelle/building-the-cli-data-gem-project-5d69</guid>
      <description>&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;For my CLI Data Gem Project, I wanted to build an app that would utilize data I wanted to learn more about. I've practiced yoga for years, and when I came across &lt;a href="https://www.yogajournal.com/poses"&gt;Yoga Journal’s pose library&lt;/a&gt; and ran some preliminary scrapes in pry, I knew I found my idea. &lt;/p&gt;

&lt;h3&gt;
  
  
  Development
&lt;/h3&gt;

&lt;p&gt;Originally I wanted to use the entire &lt;a href="https://www.yogajournal.com/poses/types"&gt;Poses by Type&lt;/a&gt; page to print a list of each pose type, print all of the poses within that type, and then allow a user to receive detailed information about a selected pose. After I discovered the project required scraping only two levels of data, I decided to focus on &lt;a href="https://www.yogajournal.com/poses/types/strength"&gt;Strengthening Yoga Poses&lt;/a&gt; and consider building an extension to include all pose types at another time. &lt;/p&gt;

&lt;p&gt;I took to heart that the aim of the project to write good object-oriented ruby code and not show off scraping prowess. I wanted to knock out the scraping selectors before starting on the architecture to let the program's design take center stage. &lt;/p&gt;

&lt;p&gt;Grabbing the 'Sanskrit Name' and 'Beginner's Tip' stumped me. I decided to table finding the selectors for those two attributes and focus on getting the rest of the app up and running. &lt;/p&gt;

&lt;p&gt;I was grateful for the support of the CLI Data Gem Study Group session to get my project up and running. Using Bundler to create a gem made sense during the README, but I didn’t really understand the process until I set it up for my app. Advice to change the repository's name to snake case made a huge difference — storing my app in a single directory instead of three nested directories. I followed Kenlyn’s advice to &lt;a href="https://chris.beams.io/posts/git-commit/"&gt;obey the seven rules of a great Git commit message&lt;/a&gt; and eventually got into a flow of adding, committing and pushing my changes to GitHub.&lt;/p&gt;

&lt;p&gt;I followed Avi's process as demonstrated in the CLI Data Gem Walkthrough. I created a working version of the user interface with hard-coded data, transitioned to developing a hard-coded version of Asana objects before scraping the real data. It took a while to get used to the patterns and standard placements for &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;require_relative&lt;/code&gt; statements. &lt;/p&gt;

&lt;p&gt;When it was time to return to find the selectors for ‘Sanskrit Name’ and ‘Beginner’s Tip’, I spent a few hours trying to figure it out on my own. Realizing I ran out of ideas and was wasting time, I asked my peers for support. We worked together to determine a selector that could grab both pieces of data. I learned about CSS combinators and utilized the adjacent sibling combinator. My favorite Eureka moment of the project came after struggling to create an iterator that would access the specific &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag I was looking for based on the text of the &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; tag above it. I tried conditional statements and #detect to no avail. Finally, I realized I could store &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;  and their adjacent &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; siblings as key/value pairs in a hash!  Iterating through the &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; tags in this way allows for the extension of the program in future versions to give the user access to other pieces of data. The only problem with my solution was that it grabbed only &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags, which worked for my purposes, but later I would also like it to grab the adjacent sibling &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tags in the same div.&lt;/p&gt;

&lt;p&gt;I must admit, after the first successful run of the app I was so excited that I ran upstairs, shook my sleeping spouse awake, and bounced around the room, babbling about my good news.&lt;/p&gt;

&lt;p&gt;I made a deal with myself to hold off on watching the anti-pattern and refactoring videos until I had a functioning project. I felt pretty confident about my code but was encouraged by the walkthroughs to reorganize my Scraper class to decrease the load time of the project. I set aside my pride, commented out my code, and tinkered until I designed new methods that first scraped just the Strengthening Yoga Poses page for the list view, and then scraped the individual yoga pose pages upon user input. It felt scary, I knew there was a chance I wouldn’t be able to figure it out and would be stuck with code that functioned, but didn’t have that Ruby elegance. Successfully refactoring those methods brought another burst of joy, proving to myself, yet again, I underestimate my own abilities. &lt;/p&gt;

&lt;h3&gt;
  
  
  What I got out of this project
&lt;/h3&gt;

&lt;p&gt;This project gave me new confidence in web development. I can see how this simple CLI app lays a foundation for much more complex projects down the road. Applying patterns and concepts from the curriculum felt like having my first conversation in a new language. I recognized just how much support the LearnIDE and lab setup provides to create a space to practice concepts, as opposed to the additional distractions of creating and maintaining your own repository. Presenting creative work requires vulnerability. I have a bad habit of waiting I feel absolutely certain something is without critique before sharing. Web development is teaching me that not only do I need to become comfortable with failure, I need to embrace it. If I don’t try, the code won’t get written, the bug won’t be fixed, and the functionality won’t be improved. There’s a balance between accessing my own knowledge and humbly asking for support. I recognize that I am still very much a beginner, but feel fortunate to be supported by a community of learners who strive to succeed together. &lt;/p&gt;

</description>
      <category>cli</category>
      <category>ruby</category>
      <category>bootcamp</category>
    </item>
    <item>
      <title>React/Redux Final Project</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Mon, 30 Dec 2019 00:08:01 +0000</pubDate>
      <link>https://dev.to/kreopelle/react-redux-final-project-2llb</link>
      <guid>https://dev.to/kreopelle/react-redux-final-project-2llb</guid>
      <description>&lt;p&gt;&lt;strong&gt;ACTIVE STORAGE + REDUX —&amp;gt; IT IS POSSIBLE.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is my attempt to piece together advice across the Internet* to write a tutorial for using ActiveStorage with React. Follow these steps to upload any file, be it mp3, jpeg or pdf, from a form on a React/Redux application to Active Storage on a Rails API. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post would not have been possible without the amazing help of Jessie Huff, Dakota Martinez, and the gracious souls who responded to numerous Github Issues and StackOverflow Questions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This tutorial uses a local storage system and does not use direct uploads. I hope to write addendums to this post to accommodate those processes as I understand them better.&lt;/p&gt;

&lt;p&gt;There are tons of awesome tutorials that go in-depth on setting up a React App with a Rails API on the backend. I got started using the guides by &lt;a href="https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/"&gt;Full Stack React&lt;/a&gt; and &lt;a href="https://medium.com/@nick.hartunian/rails-5-api-create-react-app-full-stack-heaven-2b2160b5ce6b"&gt;Nick Hartunian&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;After starting your basic React App with a Rails API, we’ll:&lt;br&gt;
Install ActiveStorage&lt;br&gt;
Set up a model, controller, and serializer to handle file attachments&lt;br&gt;
Create stateful React components connected to the Redux store to upload and display your content&lt;br&gt;
Generate reducers and actions to make asynchronous requests to your Rails API&lt;/p&gt;

&lt;p&gt;Here’s a guide to fast-forward through the setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails new app_name --api  
$ cd app_name 
$ create-react-app client 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To create a rake task that will start both servers at once:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add Foreman to your Gemfile and run bundle install
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile 
gem ‘foreman’
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a Procfile
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch Procfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add to the file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Procfile

web: sh -c ‘cd client &amp;amp;&amp;amp; npm start’
api: bundle exec rails s -p 3001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a new rake task to run that command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch lib/tasks/start.rake 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And in that file, paste:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/tasks/start.rake

task :start do
  exec 'foreman start -p 3000'
end 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you have the basic skeleton for your app as well as a command to start both your Rails API (located at localhost:3001) and your React app (located at localhost:3000) simultaneously. Just type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rake start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Beautiful! You should see the spinning React logo open in a browser window. If you navigate to localhost:3001, you should be greeted by our Rails cartoon friends.&lt;/p&gt;

&lt;p&gt;Now for the fun stuff:&lt;/p&gt;

&lt;h3&gt;
  
  
  Install active_model_serializers gem
&lt;/h3&gt;

&lt;p&gt;This gem prepares Model attributes to be rendered into JSON. Down the line, we’ll use it to include the url for our attached file in the JSON representation of our model. Add it to your Gemfile and run bundle install.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

gem ‘active_model_serializers’
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;NOTE: Active Model Serializers is, at the time of writing, undergoing renovations. Rails may have other approved methods/processes in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the model you’d like to attach a file to
&lt;/h3&gt;

&lt;p&gt;For the sake of this tutorial, we’ll run a scaffold generator for our model. This will create a controller with actions ready to render JSON (thank you API mode!), a model, a serializer with attributes pre-filled, and a migration ready to run for our DB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$rails g scaffold posts title:string body:string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After the generator is finished, check out your files to make sure they’re what you hope they’d be. If all’s well, migrate the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Active Storage
&lt;/h3&gt;

&lt;p&gt;If you’re new to Active Storage, a tool that facilitates attaching files to Active Record models, I highly recommend you check out the &lt;a href="https://edgeguides.rubyonrails.org/active_storage_overview.html"&gt;Active Storage Overview on Rails Guides&lt;/a&gt;. Previously gems like Paperclip facilitated attached files, but as of Rails 5.2, Active Storage comes ready to install with any Rails app.&lt;/p&gt;

&lt;p&gt;To install, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails active_storage:install 
$ rails db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will generate two tables in your application’s database, &lt;code&gt;active_storage_blobs&lt;/code&gt; and &lt;code&gt;active_storage_attachments&lt;/code&gt;. Previous solutions required columns to be added to existing models to accommodate attachments. &lt;/p&gt;

&lt;p&gt;Instead, Attachment is a join model that connects Blobs (which stands for Binary Large OBject) to your models. &lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://evilmartians.com/chronicles/rails-5-2-active-storage-and-beyond"&gt;Evil Martians&lt;/a&gt;, &lt;code&gt;active_storage_blobs&lt;/code&gt; don’t put the binary into your database, but tracks the location of the binary file and its associated metadata. &lt;/p&gt;

&lt;h3&gt;
  
  
  Associate Model, Controller, and Serializer with File
&lt;/h3&gt;

&lt;p&gt;Model: &lt;/p&gt;

&lt;p&gt;To associate a file with your model, you just need to add &lt;code&gt;has_one_attached&lt;/code&gt; and then the attribute name for that file to your model. The attribute name can be anything you’d like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/post.rb

class Post &amp;lt; ApplicationRecord

has_one_attached :file

end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you’d like to associate multiple files with an Active Record Model, you can use &lt;code&gt;has_many_attached&lt;/code&gt; instead. I haven’t tested the rest of this tutorial using the &lt;code&gt;has_many_attached&lt;/code&gt; association.&lt;/p&gt;

&lt;p&gt;Controller: &lt;br&gt;
Add the attribute assigned to has_one_attached from your model to the private params method at the bottom of your controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#app/controllers/posts_controller.rb 

… 

private 

def post_params
  params.require(:post).permit(:title, :body, :file)
end 

… 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Serializer: &lt;/p&gt;

&lt;p&gt;Right now your file will exist as a blob, but to use it in your React app, we need to serialize the URL that points to where this blob lives in your database (remember, to your program it is just a large binary object). To make this happen we need to include Rails’ url_helpers and write a method that will return the associated blob URL. &lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Routing/UrlFor.html"&gt;Rails API&lt;/a&gt;, url_helpers enable, among other things, access to those handy prefix methods like &lt;code&gt;posts_path&lt;/code&gt;. In our case, we’d like to get access to the blob URL associated with our file. &lt;/p&gt;

&lt;p&gt;Those route methods are automatically included in controllers, views, and mailers. To access them in other directories, they’ll need to be explicitly included. Just below the class definition for your serializer, write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/serializers/post_serializer.rb 

class PostSerializer &amp;lt; ActiveModel::Serializer

# enable access to the url helpers in the serializer 
  include Rails.application.routes.url_helpers 

  attributes :id, :title, :body

end

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



&lt;p&gt;Next, write a method that creates an attribute pointing to the URL related to your blob file. Use the rails_blob_url helper method to generate a permanent link to the resource, and add the method’s name to the list of attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#app/serializers/post_serializer.rb 

class PostSerializer &amp;lt; ActiveModel::Serializer 
   include Rails.application.routes.url_helpers

  attributes :id, :title, :body, :file_url

  def file_url
    return rails_blob_url(object.file)
  end 

end 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This won’t work out of the box, as you need to provide a &lt;code&gt;default_url&lt;/code&gt; option to tell your Rails app what the prefix for the &lt;code&gt;blob_url&lt;/code&gt; should be. &lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your application
&lt;/h3&gt;

&lt;p&gt;Navigate to config/environments/development.rb. This file holds the configuration for your application in development mode. When you transfer the application to production mode, you’ll need to repeat a similar process in the config/environments/production.rb file. &lt;/p&gt;

&lt;p&gt;After the closing &lt;code&gt;end&lt;/code&gt; statement for &lt;code&gt;Rails.application.configure&lt;/code&gt;, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/environments/development.rb 

Rails.application.routes.default_url_options[:host] = “localhost:3001” 

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



&lt;p&gt;This line sets the default host for your &lt;code&gt;url_helpers&lt;/code&gt;. The &lt;code&gt;url_helpers&lt;/code&gt; generate the end of the path for your application, not the hosting information. By default, Rails sets the host as &lt;code&gt;localhost:3000&lt;/code&gt;, but that won’t work because we’re running our React app on that port. Instead, we need to explicitly set this to &lt;code&gt;localhost:3001&lt;/code&gt; to generate the correct host information in the URL for our attached file. &lt;/p&gt;

&lt;p&gt;While we’re configuring things, let’s enable rack-cors. This gem allows our Rails app to accept Cross-Origin-Resource-Sharing requests (cors) from our React app, so we can make asynchronous javascript requests (also known as AJAX) to our Rails API. &lt;/p&gt;

&lt;p&gt;Hop over to your Gemfile, uncomment and install the rack-cors gem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile 

gem ‘rack-cors’

# and in your terminal, run 

$ bundle install

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



&lt;p&gt;Then head to &lt;code&gt;config/application.rb&lt;/code&gt;. We’ll need to configure Rack::Cors to accept requests from the React app’s origin. Within the class definition for the Rails application, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/application.rb 

… 

module YourApp
  Class Application &amp;lt; Rails::Application 
    …

    config.api_only = true 

    #=&amp;gt; Middleware to enable cross-origin requests 
    config.middleware.insert_before 0, Rack:Cors do
      allow do
        origins ‘http://localhost:3000' #=&amp;gt; or whatever host your React app points to
        resource ‘*’, :headers =&amp;gt; :any, :methods, =&amp;gt; [:get, :post, :options]
      end 
    end 

  end 
end 

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



&lt;p&gt;This middleware explicitly allows any requests from &lt;code&gt;localhost:3000&lt;/code&gt; to be accepted by our Rails API. &lt;/p&gt;

&lt;h3&gt;
  
  
  YOUR RAILS API IS OFFICIALLY READY FOR LIFTOFF ###
&lt;/h3&gt;

&lt;p&gt;Take a brief intermission before we dive into the React portion. Perhaps, by watching this lovely video: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://vimeo.com/27315673"&gt;Trim&lt;/a&gt; from &lt;a href="https://vimeo.com/peteyboy"&gt;Peter Simon (Petey Boy)&lt;/a&gt; on &lt;a href="%E2%80%9Chttps://vimeo.com%22"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Okay, now we’re back. It’s React time.&lt;/p&gt;

&lt;p&gt;For brevity’s sake, we’re going to just use the pre-made App component for our own devices. To truly follow React’s presentational/container pattern and take advantage of the beauty of components for a single purpose, I would recommend creating separate components for: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the form that holds the upload field&lt;/li&gt;
&lt;li&gt;the container that displays the content from the API&lt;/li&gt;
&lt;li&gt;the individual records retrieved from the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t already, check out &lt;a href="https://reactjs.org/docs/thinking-in-react.html"&gt;Thinking in React&lt;/a&gt; to get up to speed on the process. Long story short (but hopefully not made longer by this intermission), this tutorial is skipping best practices and encouraged design patterns to get to what’s necessary to make Active Storage uploads happen. &lt;/p&gt;

&lt;p&gt;At this point, you’ve gone through the hard and cryptic stuff. The rest is just building a React application with a Redux store that uses Thunk middleware to make POST and GET requests to your Rails API. &lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare your React application to use Redux and Redux-Thunk
&lt;/h3&gt;

&lt;p&gt;Redux is a state management tool that works with React to have one consistent state object, known as the store, accessible to all connected components. This makes the process of accessing passing props between components without direct relationships a lot easier. &lt;/p&gt;

&lt;p&gt;The store operates as a single source of truth for the Redux application, allowing data to be accessed more quickly. &lt;/p&gt;

&lt;p&gt;Instead of making database calls every time a component is rendered, the store holds data related to the current state of your application and passes that data to the components that need it. &lt;/p&gt;

&lt;p&gt;The store updates through actions (Javascript Objects with a key of “type”) and reducers (switch/case statements that alter the state based on the actions dispatched to them). &lt;/p&gt;

&lt;p&gt;Thunk is a middleware for Redux that makes life a lot easier to make asynchronous requests. &lt;/p&gt;

&lt;p&gt;Redux has a built-in function called dispatch that passes actions (which are just plain ol’ JavaScript objects with a key of “type”) down to reducers. According to &lt;a href="https://github.com/reduxjs/redux-thunk"&gt;the docs&lt;/a&gt;, “a thunk is a function that wraps an expression to delay its evaluation.” Calls to external sources are asynchronous. Because &lt;/p&gt;

&lt;p&gt;To break it down: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redux is not automatically part of React, it needs to be installed&lt;/li&gt;
&lt;li&gt;React passes props down from parent components to child components, making it difficult for cousins to get access to that data &lt;/li&gt;
&lt;li&gt;Redux creates a store that is a single source of truth for the application’s current state. &lt;/li&gt;
&lt;li&gt;The store can be accessed by any component connected to it&lt;/li&gt;
&lt;li&gt;Redux uses actions and reducers to dispatch changes to the store &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gaining these powers is as simple as running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd client
$ npm install --save redux
$ npm install --save react-redux
$ npm install —save redux-thunk
$ touch src/reducer.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your React app now has the ability to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hold a store that functions as a single source of truth for the state of the application (&lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;) &lt;/li&gt;
&lt;li&gt;Dispatch actions from components to alter the store and read data from the store (&lt;a href="https://react-redux.js.org/docs/introduction/quick-start"&gt;React-Redux&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Write action creators that return functions instead of actions allowing asynchronous requests (&lt;a href="https://github.com/reduxjs/redux-thunk"&gt;Thunk&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final command created a file to store our future reducer in, the place where dispatch will send its commands.&lt;/p&gt;

&lt;p&gt;There’s one more thing to add before we get started. Our Rails API is ready to accept asynchronous requests from our React application, but our React application does not know where to find our Rails API. Head over to &lt;code&gt;client/package.json&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Add the following key/value pair to the first object, right above the key of &lt;code&gt;“dependencies"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“proxy”: “http://localhost:3001",

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



&lt;p&gt;Instead of writing the entire API URL every time we make a fetch request, now our React app will automatically prefix the path to include the proxy. &lt;/p&gt;

&lt;p&gt;Great! Let’s put these new powers to use! &lt;/p&gt;

&lt;h3&gt;
  
  
  Set up index.js to handle middleware and provide the store
&lt;/h3&gt;

&lt;p&gt;Add the following into your index.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/index.js 

import { Provider } from ‘react-redux’;

import { createStore, applyMiddleware, compose } from ‘redux’;

import thunk from ‘redux-thunk’; 

import reducer from ‘./reducer 

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



&lt;p&gt;Provider is a component that connects the Redux store to the React app. It passes down the store as a prop. Provider is the parent component to App — the top-level component for our React application. As a child, App also receives access to the store.&lt;/p&gt;

&lt;p&gt;Next, we import three key Redux functions: &lt;code&gt;createStore&lt;/code&gt; initializes the store based on a reducer and has a second argument containing middleware, which is created by calling &lt;code&gt;applyMiddleware&lt;/code&gt;. For our purposes, &lt;code&gt;applyMiddleware&lt;/code&gt;’s argument will be &lt;code&gt;thunk&lt;/code&gt;. If you’d like to use the Redux DevTools Extension, &lt;code&gt;compose&lt;/code&gt; allows multiple pieces of middleware to be added to the store upon initialization. &lt;/p&gt;

&lt;p&gt;We put these into action after the import statements with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/index.js

…

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

let store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));

ReactDOM.render(
  &amp;lt;Provider store={store}&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  document.getElementById('root'),
);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first part, &lt;code&gt;composeEnhancers&lt;/code&gt; connects our application to Redux DevTools, providing a view of dispatched actions and the store’s current state in the browser’s console.&lt;/p&gt;

&lt;p&gt;Next, the store is created by calling the &lt;code&gt;createStore&lt;/code&gt; function with two arguments: the &lt;code&gt;rootReducer&lt;/code&gt;, which we’ll create in a moment, that contains all of the case/switch statements that will manipulate the store, and middleware connections. Since we’d like to access both the Redux DevTools and Thunk, we use &lt;code&gt;composeEnhancers&lt;/code&gt; with &lt;code&gt;applyMiddleware(thunk)&lt;/code&gt;as its argument. If you don’t want to use DevTools, you can also just pass &lt;code&gt;applyMiddleware(thunk)&lt;/code&gt; as the second argument.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build a stateful component with a file upload field
&lt;/h3&gt;

&lt;p&gt;Let’s create a component to hold our upload form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch client/src/FormContainer.js 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a React component called FormContainer, and connect it to the Redux store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/FormContainer.js 

import React, { Component } from ‘react’
import { connect } from ‘react-redux’

class FormContainer extends Component {

  render(){
    return(
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Upload File&amp;lt;/h2&amp;gt;
        &amp;lt;form&amp;gt;
          &amp;lt;input type=“text” name=“title” id=“title” placeholder=“title” /&amp;gt;
          &amp;lt;input type=“text” name=“body” id=“body” placeholder=“body” /&amp;gt;
          &amp;lt;input type=“file” name=“file” id=“file” /&amp;gt;
          &amp;lt;input type=“submit” /&amp;gt;
        &amp;lt;/form&amp;gt;
     &amp;lt;/div&amp;gt;
    )
  }
}

export default connect()(FormContainer)

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



&lt;p&gt;And while we’re at it, let’s import the FormContainer to our App component, our topmost component, to check our work as we go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/App.js



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



&lt;p&gt;Right now, our &lt;code&gt;FormContainer&lt;/code&gt; component will render HTML to create a form with a title, body and file upload field. The final line connects the component to the store but doesn’t yet have access to any props or actions from the store. If you submit the form at this point, the information would go nowhere. We need to hijack the &lt;code&gt;onSubmit&lt;/code&gt; action for the form and the &lt;code&gt;onChange&lt;/code&gt; actions for the input fields to prepare our data to send to an action. &lt;/p&gt;

&lt;p&gt;To do this we’ll: &lt;br&gt;
Give the form a local state object that contains keys for each of the file fields&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/FormContainer.js 

import React, { Component } from ‘react'
import { connect } from ‘react-redux’

class FormContainer extends Component {
  constructor(props){
    super(props)
    this.state = {
      title: '',
      body: '',
      file: null
    }
… 

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



&lt;p&gt;Bind handleOnChange and handleOnSubmit functions to &lt;code&gt;this&lt;/code&gt;, giving the functions access to the component’s state&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/FormContainer.js 

import React, { Component } from ‘react'
import { connect } from ‘react-redux’

class FormContainer extends Component {
  constructor(props){
    super(props)
    this.state = {
      title: '',
      body: '',
      file: null
    }
    this.handleOnChange = this.handleOnChange.bind(this)
    this.handleOnSubmit = this.handleOnSubmit.bind(this)
  }

… 

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



&lt;p&gt;Add onChange and onSubmit listeners for each of the fields&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/FormContainer.js 

import React, { Component } from ‘react'
import { connect } from ‘react-redux’

class FormContainer extends Component {
  constructor(props){
    super(props)
    this.state={
      title: '',
      body: '',
      file: null
    }
    this.handleOnChange = this.handleOnChange.bind(this)
    this.handleOnSubmit = this.handleOnSubmit.bind(this)
  }

  render(){
    return(
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Upload File&amp;lt;/h2&amp;gt;
        &amp;lt;form onSubmit={this.handleOnSubmit}&amp;gt;
          &amp;lt;input type="text" name="title" id="title" placeholder="title" onChange={this.handleOnChange} /&amp;gt;
          &amp;lt;input type="text" name="body" id="body" placeholder="body" onChange={this.handleOnChange} /&amp;gt;
          &amp;lt;input type="file" name="file" id="file" onChange={this.handleUpload} /&amp;gt;
          &amp;lt;input type="submit" /&amp;gt;
        &amp;lt;/form&amp;gt;
     &amp;lt;/div&amp;gt;
    )
  }
}

export default connect()(FormContainer)
… 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice the file field is pointing to a different event handler for its onChange property. For text fields, such as title and body, we can use a common handleOnChange pattern, setting the state based on the event target’s name and value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  handleOnChange = event =&amp;gt; {
    this.setState({
      [event.target.name]: event.target.value
    })
  }

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



&lt;p&gt;To have the state always reflect the current value of the input field, let’s set the value in each text input field to the name of the field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="text" name="title" id="title" placeholder="title" onChange={this.handleOnChange} value={this.state.title} /&amp;gt;

&amp;lt;input type="text" name="body" id="body" placeholder="body" onChange={this.handleOnChange} value={this.state.body} /&amp;gt;

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



&lt;p&gt;For the file field, instead of setting the state to the value of the event target, we need to set it based on the first item in the files property array&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  handleUpload = event =&amp;gt; {
    this.setState({
      file: event.target.files[0]
    })
  }

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



&lt;p&gt;This puts all of the file’s important information and metadata in the component’s state, ready to pass to the onSubmit function, and furthermore our dispatched action.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;handleOnSubmit&lt;/code&gt;, the function starts out as most submit functions do for regular text inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handleOnSubmit = event =&amp;gt; {
    event.preventDefault()
    const title = this.state.title
    const body = this.state.body
    const file = this.state.file
    const post = {post: {title: title, body: body, file: file}}

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



&lt;p&gt;This prevents the form from submitting in its standard fashion, pulls the current state of each input field (set through the &lt;code&gt;handleOnChange&lt;/code&gt; and &lt;code&gt;handleUpload&lt;/code&gt; functions), and combines those values into a nested object that reflects the format our Posts controller expects, with the name of the model on the outer level, and the attributes on the inner level.&lt;/p&gt;

&lt;p&gt;And finally, we close by resetting the form to its empty state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.setState({
  title: ‘’,
  body: ‘’,
  file: null
})
document.getElementById(“file”).value = null

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



&lt;p&gt;Though &lt;code&gt;setState&lt;/code&gt; makes the state version of the file null, we also need to use &lt;code&gt;document.getElementById&lt;/code&gt; to reset the value of the file field so the previous file’s name is no longer present next to the upload button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an action to make a post request to your API
&lt;/h3&gt;

&lt;p&gt;Currently, &lt;code&gt;handleOnSubmit&lt;/code&gt; function isn’t sending our data anywhere. Ultimately we want to make a &lt;code&gt;fetch()&lt;/code&gt; request that POSTs the data to our Rails API. To do this, we need to create an action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch src/actions.js 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;actions.js&lt;/code&gt; file, we’ll use thunk to make our post request. &lt;/p&gt;

&lt;p&gt;Dispatch an action letting the store know that we’re taking an asynchronous action&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function addPost(post)
  return (dispatch) =&amp;gt; {
    dispatch({ type: ‘START_ADD_POST_REQUEST’ })

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



&lt;p&gt;Convert the data from our component into a format that’s friendly to both JSON and our Rails API using the built-in JavaScript FormData object and appending our data to it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      const postData = new FormData()
      postData.append("post[title]", post.post.title)
      postData.append("post[body]", post.post.body)
      postData.append("post[file]", post.post.file)

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



&lt;p&gt;Make a fetch request to POST to the Rails API with our &lt;code&gt;postData&lt;/code&gt; as the body and convert the response to JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      return fetch('/api/sounds', {
        method: 'POST',
        body: soundData,
        contentType: false,
      })
      .then(resp =&amp;gt; resp.json())

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



&lt;p&gt;Dispatch the JSON version of the response to your reducer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.then(post =&amp;gt; dispatch({ type: ‘ADD_POST’, post }))

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



&lt;p&gt;The whole function should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/actions.js

export function addPost(post){
  return (dispatch) =&amp;gt; {
    dispatch({ type: 'START_ADD_POST_REQUEST' })
    const postData = new FormData()
    postData.append("post[title]", post.post.title)
    postData.append("post[body]", post.post.body)
    postData.append("post[file]", post.post.file)
    return fetch('/posts', {
      method: 'POST',
      body: postData,
      contentType: false,
    })
    .then(resp =&amp;gt; resp.json())
    .then(post =&amp;gt; dispatch({ type: 'ADD_POST', post }))
  }
}


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



&lt;p&gt;Our reducer will receive the final action, &lt;code&gt;{type: ‘ADD_POST’, post}&lt;/code&gt;. We need to create a reducer that holds an initial state for our Post model, responds to the &lt;code&gt;ADD_POST&lt;/code&gt; action type and adds our post to the store. &lt;/p&gt;

&lt;p&gt;Create and export your reducer function. The first argument should be what your initial state will look like, in this case, an object with an array of posts. The second argument is action, which will be passed with whatever action object dispatch sends to the reducer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/reducer.js 

export default function reducer(state = {posts: []}, action){

}


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



&lt;p&gt;Write a switch statement with an argument of action.type, and add the case for our ‘ADD_POST’ action and a default response that returns the current state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// client/src/reducer.js 

export default function reducer(state = {posts: []}, action){
  switch(action.type){
    case 'ADD_POST':
      return [...state, action.post]

    default:
      return state;
  }
}

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



&lt;p&gt;The &lt;code&gt;ADD_POST&lt;/code&gt; case statement’s return value will concatenate the information from the fetch request to the application’s store.&lt;/p&gt;

&lt;p&gt;Now that our action exists, include it in our connect function within the &lt;code&gt;FormContainer&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;First, import the action into the document&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { addPost } from './actions.js'

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



&lt;p&gt;Within the call to the connect function in the export statement for &lt;code&gt;FormContainer&lt;/code&gt;, add two arguments&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default connect(null, { addPost })(FormContainer)

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



&lt;p&gt;null is the place reserved for mapStateToProps, which passes down information in the store for components to use. &lt;code&gt;{ addPost }&lt;/code&gt; is in the place of mapDispatchToProps. The curly braces in this case take place of explicitly calling the dispatch function (&lt;code&gt;dispatch{ addPost }&lt;/code&gt;). By adding this action to the connect function, we can now call it in &lt;code&gt;handleOnSubmit&lt;/code&gt; and pass our &lt;code&gt;post&lt;/code&gt; object to it as an argument. &lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;handleOnSubmit&lt;/code&gt;, between the &lt;code&gt;const post&lt;/code&gt; declaration and call to &lt;code&gt;this.setState&lt;/code&gt;, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.props.addPost(post)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The entire function should now look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  handleOnSubmit = event =&amp;gt; {
    event.preventDefault()
    const title = this.state.title
    const body = this.state.body
    const file = this.state.file
    const post = {post: {title: title, body: body, file: file}}
    this.props.addPost(post)
    this.setState({
      title: '',
      body: '',
      file: null
    })
    document.getElementById("file").value = null
  }

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



&lt;p&gt;Now all the functionality is present to render a form to upload a file and send the submitted form’s data as a POST request to your Rails API! Fire up the server, open your Redux DevTools, and let’s make a post! &lt;/p&gt;

&lt;p&gt;Click on the “State” button on the right side of your Redux DevTools Console (“Diff” is automatically selected upon launch).&lt;/p&gt;

&lt;p&gt;On the right side of your Redux DevTools, you’ll see a list of all the actions dispatched. First, our &lt;code&gt;START_ADD_POST_REQUEST&lt;/code&gt; was sent, which told the store what was happening in the application. Then, the promise from the fetch request, attached to our &lt;code&gt;ADD_POST&lt;/code&gt; action, was returned and a new object was added to the posts object in the state.&lt;/p&gt;

&lt;p&gt;Let’s peek at our API. Navigate to &lt;a href="https://dev.tolocalhost:3001/sounds"&gt;localhost:3001/sounds&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should see the JSON version of the object we just posted with the title, body, and file_url attributes. Click on the file_url link, and see your file in your browser! &lt;/p&gt;

&lt;p&gt;It’s all well and good to know our POST requests are working, but what if we want to render this file in the browser? &lt;/p&gt;

&lt;p&gt;All it takes is creating a component to render the items stored in your Rails API, writing an action to submit a GET request, and calling that action in your topmost component (in this case, our App component) during the &lt;code&gt;componentDidMount&lt;/code&gt; lifecycle method to push the API’s data to the store. &lt;/p&gt;

&lt;p&gt;Phew! Let’s break that down: &lt;/p&gt;

&lt;h3&gt;
  
  
  Create a component to render items stored in your Rails API
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch client/src/Posts.js 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And in that file, write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'

const Posts = (props) =&amp;gt; {
  return(
    &amp;lt;div className="posts"&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Posts

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



&lt;h3&gt;
  
  
  Write an action to handle a GET request for the API content
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#client/src/actions.js

export function getPosts(post){
  return (dispatch) =&amp;gt; {
    dispatch({ type: ‘START_GET_POSTS_REQUEST’ })
     return fetch('/posts')
    .then(resp =&amp;gt; resp.json())
    .then(posts =&amp;gt; dispatch({ type: 'GET_POSTS', posts }))
  }
}

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



&lt;h3&gt;
  
  
  Write a case statement to handle that action in the reducer
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# client/src/reducer.js 

… 

    case 'GET_POSTS':
      return {...state, posts: action.posts }

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



&lt;h3&gt;
  
  
  Import the &lt;code&gt;getPosts&lt;/code&gt; action, the &lt;code&gt;connect&lt;/code&gt; function, and the Posts component into the App component
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { getPosts } from './actions.js'
import { connect } from 'react-redux'
import Posts from './Posts.js'

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



&lt;h3&gt;
  
  
  Pass &lt;code&gt;getPosts&lt;/code&gt; to the &lt;code&gt;connect&lt;/code&gt; function as the argument for &lt;code&gt;mapDispatchToProps&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default connect(null, { getPosts })(App)

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



&lt;h3&gt;
  
  
  Write a mapStateToProps function to access the posts object from the store, outside of the component and pass the function as the first argument of the connect() function
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function mapStateToProps(state){
  return {
    posts: state.posts
  }
}

export default connect(mapStateToProps, { getPosts })(App)

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



&lt;h3&gt;
  
  
  Call getPosts within the componentDidMount() lifecycle method within the App component
&lt;/h3&gt;

&lt;p&gt;By calling the getPosts method during componentDidMount lifecycle method of the App component, the information will be fetched from the database only when the entire Application is reloaded. Any new posts added without the App reload, will be pushed to the store through the ADD_POST action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class App extends Component {

  componentDidMount(){
    this.props.getPosts()
  }

  render() {
    return (
      &amp;lt;div className="App"&amp;gt;
        &amp;lt;FormContainer /&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}


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



&lt;h3&gt;
  
  
  Add the Posts component return statement below the &lt;code&gt;FormContainer&lt;/code&gt;, and pass down the posts returned from mapStateToProps as a prop.
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class App extends Component {

  componentDidMount(){
    this.props.getPosts()
  }

  render() {
    return (
      &amp;lt;div className="App"&amp;gt;
        &amp;lt;FormContainer /&amp;gt;
        &amp;lt;Posts posts={this.props.posts} /&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}


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



&lt;h3&gt;
  
  
  Use the posts props to render individual posts on the page
&lt;/h3&gt;

&lt;p&gt;Returning to our Posts.js file, iterate through the post objects passed down from the App component, and render each object as an &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'

const Posts = (props) =&amp;gt; {

  const renderPosts = this.props.posts.map(post =&amp;gt; {
    &amp;lt;li key={post.id}&amp;gt;&amp;lt;strong&amp;gt;{post.title}&amp;lt;/strong&amp;gt; - {post.body} - {post.file_url}&amp;lt;/li&amp;gt;
  })

  return(
    &amp;lt;div className="posts"&amp;gt;
    {this.renderPosts}
    &amp;lt;/div&amp;gt;
  )
}

export default Posts


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



&lt;p&gt;There you have it! Thanks for reading! &lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>rails</category>
      <category>activestorage</category>
    </item>
    <item>
      <title>Film Budget Helper - Rails Project</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Sun, 29 Dec 2019 23:53:58 +0000</pubDate>
      <link>https://dev.to/kreopelle/film-budget-helper-rails-project-1foc</link>
      <guid>https://dev.to/kreopelle/film-budget-helper-rails-project-1foc</guid>
      <description>&lt;h1&gt;
  
  
  Film Budget Helper - Rails Project
&lt;/h1&gt;

&lt;p&gt;The Film Budget Helper is a Rails app designed to help producers track and organize eligible expenses for applications to the New York State Empire State Development Film Production Tax Credit (the tax credit). For more information about the tax credit, visit &lt;a href="https://esd.ny.gov/industries/tv-and-film"&gt;their website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tracking hundreds of expenses, reimbursing cast and crew members for purchases made with their personal funds, updating cash on hand in real-time, and organizing expenses into reports can be tedious and time-consuming.&lt;/p&gt;

&lt;p&gt;This app is designed to streamline the process for producers and crew members by providing an efficient tool to process reimbursements and organize expenses.&lt;/p&gt;

&lt;p&gt;The tax credit requires the following information to qualify an expense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vendor&lt;/li&gt;
&lt;li&gt;Date of purchase&lt;/li&gt;
&lt;li&gt;Department related to the purchase (e.g. craft services, camera, wardrobe)&lt;/li&gt;
&lt;li&gt;Purchase total&lt;/li&gt;
&lt;li&gt;Receipt image&lt;/li&gt;
&lt;li&gt;Location code (the region where the purchase was used)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a proof of concept. Additional features still under development include running reports, generating independent budget fields, and reimbursing users.&lt;/p&gt;

&lt;p&gt;The main function of this application is to create expenses related to production with the information required by the tax credit and allow an administrator to approve those expenses.&lt;/p&gt;

&lt;p&gt;There are two tiers of users in the app — regular users and administrators. Regular user accounts align with the responsibilities of a crew member on a production. Administrator accounts align with the responsibilities of a producer. Users may create an account using the site’s authentication system or through a Google account. Users can identify themselves as administrators only if they sign up using the site’s authentication system.&lt;/p&gt;

&lt;p&gt;On a regular user’s homepage, the user will see a table of the expenses he or she has created with information about the vendor, date, department, total and status. When an expense is submitted, its status is set to pending. This allows an administrator to review the expense and change the status to approved or not approved. If the status is approved, the expense may no longer be edited. If the status is marked as not approved, the user must edit the expense to meet approval guidelines. The regular user's homepage contains links to view the details of the expense, create a new expense, and log out. &lt;/p&gt;

&lt;p&gt;If the user clicks the link to create a new expense, a form to create an expense will be rendered. In this version of the application, vendor, date, department, total, production, and receipt image are all required. There is also an optional field for descriptions. The departments listed come from the tax credit’s guidelines and are set using seed data. Regular users may view and edit their own expenses.&lt;/p&gt;

&lt;p&gt;Productions are built by administrators and may not be deleted.&lt;br&gt;
A user becomes an administrator by checking the Admin box upon registration. An administrator’s homepage displays a table of all productions and a page to view the individual expenses related to a production that serves as a general ledger. The administrator also has his or her own table of personally submitted expenses at the bottom of his or her home page.&lt;/p&gt;

&lt;p&gt;Administrators may edit or delete any expense except those with an approved status. Once approved, an expense will appear on the show page of the production it is related to. Administrators may also view all pending, approved, and not approved expenses using links present on the homepage. Statuses will be connected to reimbursement in future versions of this program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;This project was difficult to start — I felt daunted by the material and unsure of what I knew. As I dove in deeper, I got hooked on the building. Eventually, I needed to rein in my excitement to build new features, and move on. I reminded myself that there’s more waiting for me in JS, React, and Redux! Working on this project built my confidence in refactoring logic into partials, helpers, and model methods. Working with the Rails, Pundit, and ActiveStorage documentation (with the guidance of my Technical Coach Lead) opened my eyes to the process of learning to use programming tools based on web documentation and blogs.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>bootcamp</category>
    </item>
    <item>
      <title>Embrace the Process - My Sinatra Project</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Sun, 29 Dec 2019 23:49:51 +0000</pubDate>
      <link>https://dev.to/kreopelle/embrace-the-process-my-sinatra-project-f0g</link>
      <guid>https://dev.to/kreopelle/embrace-the-process-my-sinatra-project-f0g</guid>
      <description>&lt;h1&gt;
  
  
  Embrace the Process – My Sinatra Project
&lt;/h1&gt;

&lt;h3&gt;
  
  
  The Project
&lt;/h3&gt;

&lt;p&gt;My Sinatra project grew from two pieces of advice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From my TCL — think of it as a glorified spreadsheet&lt;/li&gt;
&lt;li&gt;From my mother-in-law — make something you would want to use&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My previous job was as a film producer. My main project over the past few years was a micro-budget feature film–my first feature and my first fiction film. &lt;/p&gt;

&lt;p&gt;During production, the crew scrambled to catch up at every moment, and eventually, we just had to let some things go. One of the things that got postponed until after production was the general ledger — a record of every expense from the production. After we wrapped, I spent weeks manually entering 1000+ expenses into a format required by a state-run tax credit.&lt;/p&gt;

&lt;p&gt;As I bounced between paper receipts, company transaction histories, and numerous spreadsheets, I wished there was an easier way to integrate expenses from the production into the proper format and a way to easily track the expenses in real-time.&lt;/p&gt;

&lt;p&gt;Cue this project: I designed the &lt;a href="https://github.com/kreopelle/sinatra_expense_tracker"&gt;Sinatra-Expense-Tracker&lt;/a&gt; as a tool to record expenses for reimbursement.&lt;/p&gt;

&lt;p&gt;This is just the first step for a larger application I hope to build during my Rails project that will automate reimbursements, create a general ledger, offer real-time budget data for each department, and output the spreadsheets required for film production tax credits.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Process
&lt;/h2&gt;

&lt;p&gt;While building this project, I tried to follow the steps laid out during the Pirates lab walkthrough video: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build DB&lt;/li&gt;
&lt;li&gt;Build Models&lt;/li&gt;
&lt;li&gt;Alternate between creating controllers and views for each model in this order: &lt;/li&gt;
&lt;li&gt;index&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;show&lt;/li&gt;
&lt;li&gt;edit&lt;/li&gt;
&lt;li&gt;destroy. 
Begin with the has_many object controllers and then work on the belongs_to object controllers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Some takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sketch out your MVC and object relationships ahead of time. That roadmap will guide you through the build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure the foundation of your project (structure, gems, environment, requirements) is laid out before you begin. It’s no fun to chase an error that could have been resolved before you started!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow good CRUD principles and create RESTful Routes. These two concepts take the guesswork out of design and make the project easier to debug while also checking off the boxes in the guidelines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Work methodically and test consistently. Anytime I tried to jump ahead to the next route or forgot to test something after changing it, I ended up with at least an extra 20 minutes of debugging down the line.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Know when something works, but don’t be afraid to break it to make your program as a whole function better. This was something I learned in my group Fwitter project that served me well during the Sinatra project. Your methods may depend on each other to work, but that can also blind you to how their relationship might be breaking something else. Question everything and you’ll find the right answer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sinatra</category>
      <category>ruby</category>
      <category>bootcamp</category>
    </item>
    <item>
      <title>How to Deal with 1,000 Failing Specs in a Rails App</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Thu, 17 Oct 2019 22:22:59 +0000</pubDate>
      <link>https://dev.to/kreopelle/how-to-deal-with-1-000-failing-specs-in-a-rails-app-pke</link>
      <guid>https://dev.to/kreopelle/how-to-deal-with-1-000-failing-specs-in-a-rails-app-pke</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qPD3DWzL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://planetargon-blog.s3-us-west-2.amazonaws.com/images/2019/1019/how-to-deal-with-1000-failing-specs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qPD3DWzL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://planetargon-blog.s3-us-west-2.amazonaws.com/images/2019/1019/how-to-deal-with-1000-failing-specs.png" alt="Person on Computer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few months ago, myself and the team at &lt;a href="https://www.planetargon.com/"&gt;Planet Argon&lt;/a&gt; kicked off a Rails 2.1 to Rails 3.0 / Ruby 1.8.7 to Ruby 1.9.3 upgrade for one of our clients. The monolithic Rails app contained over 1 million lines of code and accumulated over 9,000 RSpec tests throughout its decade of development. Once the test suite was running on the new version, 8,000 of these specs failed. &lt;/p&gt;

&lt;p&gt;This blog post is a collection of my lessons learned while tackling this upgrade in hopes you may be better prepared to &lt;em&gt;fearlessly&lt;/em&gt; face 1000 failing specs. We’ll cover what to do when facing 1000 failing specs, and then move on to learning what to do to prevent 1000 failing specs in the future. &lt;/p&gt;

&lt;h2&gt;
  
  
  When the 1000 failing specs are already there
&lt;/h2&gt;

&lt;p&gt;Ok, so you’ve upgraded your Gemfile, finished your first successfully failing RSpec run and see 1,000 or so failed specs. Now what? &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Strategize where you start
&lt;/h3&gt;

&lt;p&gt;Breaking the work into manageable chunks will keep your process calm, organized, and productive. I took different approaches based on the kinds of failures left. I preferred to focus first on fixing errors with similar messages, then fixing all errors related to a functional part of the app. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy A: Fix Errors with Similar Messages&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Once the test suite runs all the way through, you’ll likely see a lot of similar messages or error types. &lt;code&gt;ActionController::RoutingError&lt;/code&gt;, &lt;code&gt;ActiveRecord::RecordNotSaved&lt;/code&gt;, &lt;code&gt;undefined local variable or method&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d7I-ecWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://planetargon-blog.s3-us-west-2.amazonaws.com/images/2019/1019/failing-specs-rails-example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d7I-ecWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://planetargon-blog.s3-us-west-2.amazonaws.com/images/2019/1019/failing-specs-rails-example.png" alt="Code example of similar error types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the failure output and begin to familiarize yourself with common error messages in your test run. Outputting the test run into a file using RSpec's built-in &lt;code&gt;--output&lt;/code&gt; method is quite helpful here.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# outputs the run of "example_spec" to a file called rspec.txt on your computer $ rspec example_spec.rb --format documentation --out rspec.txt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Since there are so many failures, it’s not important to pick the one occurring at the highest frequency. Just pick one you see a lot. Or one that you feel confident you could fix. Focusing on fixing that one kind of error in as many places as you can will start whittling the errors down. &lt;/p&gt;

&lt;p&gt;When you feel confident it's resolved, conduct another full suite run. Search for any references to that error message. When there's zero results, move onto the next one. &lt;/p&gt;

&lt;p&gt;This strategy will become less useful as the total errors diminish. At that point, I recommend focusing on fixing specs that share Objects. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy B: Fix all specs related to the same Object&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Let's say you have a User model. Locate all the spec files that relate to Users (ex. User models, User requests, User controllers, User features, User reports, etc.). Fix those spec files. Perhaps even clean up those files as you go by removing old TODOs or updating organization to reflect current best practices. Laying to rest a particular part of the app clears precious mental space to narrow down where the stickiest problems remain. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Specs aren’t resolved 1:1
&lt;/h3&gt;

&lt;p&gt;Fixing that &lt;code&gt;AbstractController::ActionNotFound&lt;/code&gt; error doesn’t always mean that the number of passing specs increased. &lt;/p&gt;

&lt;p&gt;Resolving some specs may take a few extra rounds. Your hours of hard work allowed RSpec to evaluate the next line in the stack, that could have an issue with it too. Especially early on, keep the mantra of “get to the next error message”, close to your heart. Eventually, you’ll weed through the stack and start seeing green, but finding success in the meantime will be paramount to maintaining morale. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Separate WIPs into a child-branch and compare the pass rate with a parent-branch
&lt;/h3&gt;

&lt;p&gt;Just because your code change made a miraculous difference in one spec file, doesn’t mean that 10 errors weren't reopened in another part of your app. &lt;/p&gt;

&lt;p&gt;To prevent this tail-chasing conundrum, work on your manageable chunks in child-branches that get merged into a parent-branch. Using the parent-branch test run as the standard, you can see if child-branch changes break specs that were already fixed. &lt;/p&gt;

&lt;h3&gt;
  
  
  4. Be flexible in your focus (or take mini-project breaks)
&lt;/h3&gt;

&lt;p&gt;In the upgrade I mentioned at the beginning, I ran into a big problem -- Locales completely changed between the versions of Rails. At first, I commented out the code, wrote a TODO, and moved onto a different error. Eventually, I acknowledged this patch-method was too big of a blocker to continue making meaningful progress in other parts of the app. &lt;/p&gt;

&lt;p&gt;You could think of it like a vacation. Immerse yourself in totally upgrading this entire part of the app. Understand how it changed and why. Test it in the browser. Minimize your attention on the specs. The other failures will be waiting for you once you’re finished (though some may happily resolve themselves in the process.) &lt;/p&gt;

&lt;h3&gt;
  
  
  5. Document what works
&lt;/h3&gt;

&lt;p&gt;That feeling of, “I think I saw something like this last week…” can be satiated with good notes. &lt;/p&gt;

&lt;p&gt;Keep a running log of successful syntax changes. Help your future self find the answers. You can also use this log to quell feelings of doubt during the upgrade journey by reviewing all you've learned along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;: Share notes with the team post-upgrade to help them get up to speed on new syntax and patterns required by the version change. &lt;/p&gt;

&lt;h3&gt;
  
  
  6. Keep an open mind of what might be causing the failure you face
&lt;/h3&gt;

&lt;p&gt;Failed Specs arise from a number of sources: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby version changes &lt;/li&gt;
&lt;li&gt;Rails version changes &lt;/li&gt;
&lt;li&gt;Gem version changes (especially RSpec) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each version change is usually accompanied by a well-documented explanation of the shake-ups between old and new. They also usually have APIs with detailed explanations of which methods exist in different versions of the framework. Especially if you take the bump-everything-up-at-once route, the error could come from any of these sources. &lt;/p&gt;

&lt;p&gt;If you're feeling stuck, ask yourself: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the method this spec relies upon still exist in the new version? &lt;/li&gt;
&lt;li&gt;Does the method do something drastically different than it used to? &lt;/li&gt;
&lt;li&gt;Is there a different process for performing this, or a similar, action in the new versions? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have the Changelog, Upgrade Guide, API, and READMEs close at hand to evaluate whether this is something that should be expected based on the code, or something that broke because of the tests evaluating the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevent seeing 1000 failing specs in the first place
&lt;/h2&gt;

&lt;p&gt;Preparing your Rails app for an upgrade will reduce the number of failing specs you have to deal with. You may prevent seeing 1000 failing specs in the first place by following these tips. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upgrade dependencies as close to the version you'll use for the Rails upgrade as you can
&lt;/h3&gt;

&lt;p&gt;If a version change is required to keep your gems compatible with the upgraded version of Rails, you may run into spec failures due to syntax changes between the versions. Even if you can't upgrade to the same version you'll use once you've upgraded, pushing the version higher in your current not-yet-upgraded app could introduce helpful deprecation warnings to adjust code before it breaks. It’s also not always possible, &lt;em&gt;and that’s okay too&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Test out one change at a time. Change the gem version, run the test suite, fix what fails, and move on to the next gem. &lt;/p&gt;

&lt;p&gt;The main gems I’d recommend updating before upgrading would be anything related to testing, such as RSpec, FactoryBot, Capybara, etc. You’ll save yourself the hassle of wondering if a failure is due to something that changed in Rails or something that changed in RSpec. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fix deprecation warnings
&lt;/h3&gt;

&lt;p&gt;We hate them. We ignore them. We become blind to them. Now is not the time – pay attention before you make that version switch to stave off unnecessary errors that would’ve been resolved by taking the advice of the maintainers. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make sure your test coverage is robust
&lt;/h3&gt;

&lt;p&gt;If you’re reading this post, your app probably has 1,000+ specs. However, I wanted to share this: &lt;/p&gt;

&lt;p&gt;Test coverage is a great way to evaluate whether your application is functioning as expected during and after an upgrade. However, having a lot of specs doesn’t necessarily mean they’re evaluating the most important stuff in your app. &lt;/p&gt;

&lt;p&gt;Use a test coverage evaluation tool, such as &lt;a href="https://github.com/colszowka/simplecov"&gt;SimpleCov&lt;/a&gt;, to determine if the app you’re about to upgrade has a high percentage of coverage in key areas. If the coverage isn’t there, you may want to write some new specs before starting the upgrade. &lt;/p&gt;

&lt;h3&gt;
  
  
  4. Follow the Changelog and Upgrade Guides
&lt;/h3&gt;

&lt;p&gt;Those failures before you get to the failures; the RSpec errors preventing the suite from even running? They're probably related to fixing your config files to accommodate the new changes in Rails. Follow the official &lt;a href="https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"&gt;Rails upgrade guides&lt;/a&gt; to move on to the good stuff. &lt;/p&gt;


 

&lt;p&gt;Maybe there isn’t time for you to spend on fixing all of these specific items. Often, upgrades are done under a time crunch. That’s totally fine. These are hindsight tips I wished I had done as I was working through the last big upgrade I worked on. &lt;/p&gt;

&lt;p&gt;Our process involved bumping up the whole Gemfile, and once we got a successful bundle, diving into the failed specs. We were able to get a ~10,000 spec project back to green without the mindful adjustment of dependencies ahead of time. We even simultaneously upgraded Ruby and Rails. It still worked out. I don't know how morale or efficiency could have transformed had we applied these ideas. &lt;/p&gt;

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

&lt;p&gt;Dealing with 1,000+ failing specs on a Rails app is no easy task, and it's just one part of your larger upgrade goal. &lt;/p&gt;

&lt;p&gt;An upgrade is like a marathon. You’re not going to finish by beating yourself up or burning yourself out. You need to take care of yourself to achieve your goal. &lt;/p&gt;

&lt;p&gt;Like any endurance activity, your quick successes will slow down. Where one change might fix 100 specs, as the upgrade goes on, you may need to spend a few days fixing a single spec. Don’t get down on yourself. &lt;em&gt;That’s just how this works&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;It took 27 seconds for the test suite to start running on the upgrade project that sparked this article. During those seconds, I started closing my eyes, taking three deep breaths and then watching the tests run. Find moments to recharge while you work.&lt;/p&gt;

&lt;p&gt;Sticking to small goals and healthy habits will keep you sane. Bargaining with yourself to solve one more spec before taking a break probably won’t. &lt;/p&gt;

&lt;p&gt;Green suites will come, don't lose hope. Persevere! Take care of yourself. Good luck! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>upgrade</category>
      <category>specs</category>
      <category>rspec</category>
    </item>
    <item>
      <title>Tools to sort RSpec Error Output by Error Type</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Wed, 03 Apr 2019 04:50:34 +0000</pubDate>
      <link>https://dev.to/kreopelle/tools-to-sort-rspec-error-output-by-error-type-1i25</link>
      <guid>https://dev.to/kreopelle/tools-to-sort-rspec-error-output-by-error-type-1i25</guid>
      <description>&lt;p&gt;I'm working on a Rails upgrade for a large codebase, and would love a way to sort all of my errors by type (e.g. all of the ActionView::MissingTemplate errors in a group). &lt;/p&gt;

&lt;p&gt;I've been doing this manually, but feel like there must be another way! &lt;/p&gt;

&lt;p&gt;Does anyone know of tools that they use to sort RSpec output, or have ideas to approach this challenge? &lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>How to Successfully Onboard a Junior Developer</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Tue, 12 Feb 2019 00:18:39 +0000</pubDate>
      <link>https://dev.to/kreopelle/how-to-successfully-onboard-a-junior-developer-1a53</link>
      <guid>https://dev.to/kreopelle/how-to-successfully-onboard-a-junior-developer-1a53</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9nteyhoxd0lbou4fn8dl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9nteyhoxd0lbou4fn8dl.png" title="How to Successfully Onboard a Junior Developer" alt="How to Successfully Onboard a Junior Developer - Planet Argon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Landing your first job as a developer is an exciting moment.&lt;/p&gt;

&lt;p&gt;Whether you’re coming from college, a boot camp, or teaching yourself, the job offer may feel like the moment you’ve been waiting for — proof that the time and effort you put into learning to program has paid off.&lt;/p&gt;

&lt;p&gt;However, learning to code and working as a developer are slightly different things — you have a company to write code for, co-workers to collaborate with who have more and/or different experience than your own, and new tools to grow accustomed to.&lt;/p&gt;

&lt;p&gt;I was excited for my first day on the job, but also fought back feelings of intimidation, uncertainty, and isolation. Here are some strategies Planet Argon implemented to help me grow into being a Junior Developer on their team.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Build confidence
&lt;/h2&gt;

&lt;p&gt;A huge element of my onboarding process was building confidence by shattering my imposter syndrome fears. During my first month, I often asked myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Am I going to be able to do this job?&lt;/li&gt;
&lt;li&gt;What if I break everything?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confidence as a junior developer comes in two ways: feeling trusted enough to perform tasks independently, and having a comfortable environment to ask questions and work with other developers. Including a balance of activities in your onboarding schedule will help your new junior developer feel comfortable in their role a lot faster.&lt;/p&gt;

&lt;p&gt;Planet Argon’s first-week plan includes DevOps time for junior developers to set up their machine and get familiar with our client projects and shadowing time with more senior developers to support our new team members in both ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Establish expectations
&lt;/h2&gt;

&lt;p&gt;A junior developer’s first week at Planet Argon includes meetings with leadership and project managers. It’s a time to share company values and the day-to-day process of how we get things done.&lt;/p&gt;

&lt;p&gt;Help your new hires avoid the feeling of, “I’m not sure what I’m supposed to be doing,” by dedicating time to let them know what success looks like during their onboarding period.&lt;/p&gt;

&lt;p&gt;Contributing to a codebase can be scary for the uninitiated. As confident as your junior developer may have become in their at-home development environment, working on someone else’s code, for money, can be a big intimidation factor.&lt;/p&gt;

&lt;p&gt;Share high-level goals and milestones with them, as well as detailed procedures for writing commits and responding to JIRA tickets. A concrete example from my onboarding schedule included incrementally decreasing the amount of time spent on “absorbed development”. This is a term we use for non-coding related time spent learning how to make the required changes. It’s time we “absorb” because we don’t bill clients for it.&lt;/p&gt;

&lt;p&gt;I also appreciated learning about Planet Argon’s “no surprises” rule. When things get overwhelming or off-track, tell someone. We don’t want to surprise our clients or our teammates.&lt;/p&gt;

&lt;p&gt;Overall, remind your junior developer that they were brought into the team with the knowledge that other developers would spend time with them to get them up to speed. This company was interested in investing in you, junior developer, and we want you here.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Share knowledge
&lt;/h2&gt;

&lt;p&gt;No matter how many times coworkers said, “feel free to ask me any questions you have,” (which does help), I didn’t comfortably take advantage of it until I spent some time getting to know them. After shadowing, I also went back to my personal tasks with new skills to apply, and more comfort when I ran into an issue that I couldn’t solve.&lt;/p&gt;

&lt;p&gt;Be patient and make time to plan the tricks of the trade. Reflect on what you wish you would have known when you get started. Keyboard shortcuts, customizing &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;oh my zsh&lt;/a&gt;, and discussing version control stunned me at first, as I watched other developers glide through the command line. Using the shortcuts at my workstation felt a bit like practicing scales on the piano, but now I’m getting the hang of playing small concertos.&lt;/p&gt;

&lt;p&gt;After watching how it’s done and getting more familiar with your company’s tools, have a manageably-sized ticket ready for them to work on. It’s a great introduction to how your task management system works and it will feel great for them to finish something and mark it “done”.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Review and update documentation
&lt;/h2&gt;

&lt;p&gt;A common todo to find at the bottom of many lists is to update documentation. As a new hire, this documentation can be crucial — it’s your map to understanding the company and what you should be doing. Be aware of what’s included. Be confident that what you’re giving them is helpful.&lt;/p&gt;

&lt;p&gt;Review and update the materials you’ll give your junior dev before handing it off to them. No longer relevant setup guides may stop them up. You wouldn’t want someone to give you a ReadMe that wasn’t accurate, don’t do the same for your juniors.&lt;/p&gt;

&lt;p&gt;In addition, ask your junior devs to contribute to your company materials to help the next person who comes along. They’re the most recent ones to touch the documents, they probably know what’s missing and outdated. Giving them this opportunity to contribute provides a chance to reflect on the giant learning curve they’ve embarked on.&lt;/p&gt;

&lt;p&gt;Habits form quickly — what felt terrifying and new should (hopefully) become second nature relatively quickly. If you don’t leave notes for the next developer as the onboarding process occurs, chances are the junior dev will assimilate them (just as they assimilate into your team), and forget what stopped them up by the next time a new hire comes around.&lt;/p&gt;

&lt;p&gt;Onboarding junior developers and being a junior developer acclimating to a new role are both huge and delicate responsibilities. Combat intimidation with knowledge and support. Quash uncertainty by providing specific instructions. Remind your junior developer that you trust them and that you’re excited to have them on your team.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>career</category>
      <category>culture</category>
    </item>
    <item>
      <title>Iterating through Hashes</title>
      <dc:creator>Kayla Reopelle</dc:creator>
      <pubDate>Tue, 13 Nov 2018 18:32:03 +0000</pubDate>
      <link>https://dev.to/kreopelle/iterating-through-hashes-15jb</link>
      <guid>https://dev.to/kreopelle/iterating-through-hashes-15jb</guid>
      <description>&lt;p&gt;As a baker by day and programming student by night, I find a lot of connections between my two worlds. Variables remind me of ingredients (type_of_flour = bread flour). Instructions remind me of loops (knead dough 10 times). Knowing when and how to proceed reminds me of conditional statements (if the dough is sticky, add flour, else if the dough is dry, add water, else begin rise).&lt;/p&gt;

&lt;p&gt;One of my earliest lessons with baking bread was to measure my ingredients as precisely as possible. When baking a loaf or two at home, an extra tablespoon of flour can easily be compensated for, but when baking 200+ loaves before the Easter rush, you better get it right. There are enough other factors (mixing, proofing, shaping, etc.) that require attention and discretion that I found beginning with a groundedness in my ingredients being measured properly made a huge difference. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SyfnVkMj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.seriouseats.com/images/2014/11/bread-infographic2-forweb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SyfnVkMj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.seriouseats.com/images/2014/11/bread-infographic2-forweb.jpg" alt="With all of these potential issues, why worry about measuring if you don't have to? Photo from Serious Eats"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.seriouseats.com/2014/11/troubleshoot-bad-bread-messed-up-loaf.html"&gt;With all these potential issues, why worry about measuring if you don't have to? Photo from Serious Eats by Vicki Wasik.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately, the United States is stubborn about their independent units of measurement and still leans more toward cups than the precision and universality of grams. Cups can be tricky – they're imprecise. &lt;em&gt;My&lt;/em&gt; one cup may not hold quite the same volume as &lt;em&gt;your&lt;/em&gt; one cup. With the potential for flour to pack down into the cup, measuring ingredients on a scale helps eliminate some of the guesswork (and you can just dump everything into a bowl to check the amount). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TN9Fknpy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.food52.com/bVlnUPAnkvV_c97GkJiViXD1Vxc%3D/fit-in/800x0/f4605c1a-be2e-4671-b687-b99cb589311a--IMG_6657.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TN9Fknpy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.food52.com/bVlnUPAnkvV_c97GkJiViXD1Vxc%3D/fit-in/800x0/f4605c1a-be2e-4671-b687-b99cb589311a--IMG_6657.JPG" alt="Sarah Jampel of Food52 found that there could be as much as a 37-gram difference in the volume of sugar in a measuring cup based on which cup you use."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://food52.com/blog/16497-the-truth-about-your-measuring-cup-dun-dun-dun"&gt;Sarah Jampel of Food52 found that there could be as much as a 37-gram difference in the volume of sugar in a quarter-cup measure based on your set. Photo from Food52.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unwilling to kick my favorite American recipes to the curb, but believing in an easier way than researching, converting, and writing each ingredient down when I want to use my kitchen scale instead of measuring cups -- why not create a method to do the job for me?! &lt;/p&gt;

&lt;p&gt;For this experiment, I decided to use &lt;a href="https://www.foodnetwork.com/recipes/bobby-flay/pizza-dough-recipe-1921714"&gt;Bobby Flay's Pizza Dough&lt;/a&gt;, my go-to for a quick, foolproof dinner.&lt;/p&gt;

&lt;p&gt;I decided that the best way to store the ingredients was in a hash. Hashes are dictionary-like collections of key-value pairs. In terms of a recipe, keys could be thought of as ingredients and values could be thought of as the quantity of the ingredient to use in the recipe. &lt;/p&gt;

&lt;p&gt;I transformed this: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingredients&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 1/2 to 4 cups bread flour, plus more for rolling (Chef's Note: Using bread flour will give you a much crisper crust. If you can't find bread flour, you can substitute it with all-purpose flour which will give you a chewier crust.)&lt;/li&gt;
&lt;li&gt;1 teaspoon sugar&lt;/li&gt;
&lt;li&gt;1 envelope instant dry yeast&lt;/li&gt;
&lt;li&gt;2 teaspoons kosher salt&lt;/li&gt;
&lt;li&gt;1 1/2 cups water, 110 degrees F&lt;/li&gt;
&lt;li&gt;2 tablespoons olive oil, plus 2 teaspoons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pizza_dough_ingredients = {
  bread_flour: "3.5 cups",
    sugar: "1 teaspoon",
    instant_dry_yeast: "1 envelope",
    kosher_salt: "2 teaspoons",
    water: "1.5 cups",
  olive_oil: "2 tablespoons"  
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Originally I tested my method using a list of variables, with each ingredient set equal to its quantity, however, it made it difficult to see the two together after running them through a method. I wanted the two values to stick together as I converted the quantity from cups to grams, so key-value pairs seemed more reasonable.&lt;/p&gt;

&lt;p&gt;Next, I worked on my method. Fortunately, the values of the ingredients all shared a similar pattern that I could use to my advantage. They started with a number and were followed by the unit of measurement. I wrote out some pseudocode to map out what I wanted to accomplish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# go through the entire collection of ingredients 
# separate the amount from the unit of measurement
# perform math to change the value to its equivalent in grams
# put all data back together into a readable list 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then I tested my process with just a single ingredient. I wanted to make sure that the code I'd be using to iterate over the hash did what I wanted it to do before running it over the entire list. According to &lt;a href="https://www.convertunits.com/from/teaspoons/to/grams"&gt;Convert Units&lt;/a&gt;, one teaspoon should be equal to five grams.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
sugar = "1 teaspoon"

def gram_converter(ingredient)
  ingredient_array = ingredient.split 

# separate the quantity from the unit and save them into a new variable. #split returns an array.

  if ingredient_array[1] == "teaspoon"

# the second value of the array should be the unit of measurement. 
# this checks to determine if it is a teaspoon

    ingredient = ingredient_array[0].to_i * 5

# the first value in the ingredient array should be the quantity.
# if the value is a teaspoon, then convert it to an integer and multiply it by 5 
# if it is not converted to an integer, the quantity will just appear five times

    puts ingredient

# reveals the new value of the ingredient 

  else 
    puts "error"

# if it didn't work, lets me know so that I can go back to check my work

  end

end

gram_converter(sugar)

# =&amp;gt; 5

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



&lt;p&gt;Once I got this part of the method working, I moved over to the hash iteration. Hash iteration works like iterating over any other form of data, except it accepts two parameters––the key and the value. For this method, I set the key equal to an ingredient, and the value equal to its quantity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
def gram_converter(ingredient_hash)
  ingredient_hash.each do |ingredient, quantity|
    # code to perform on each key-value pair
    end 
end

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



&lt;p&gt;Now, I needed to account for the different units of measurement commonly found in American recipes -- teaspoons, tablespoons, and cups. I created a series of conditional statements that would operate on the quantity if a given unit word was included in the string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def gram_converter(ingredient_hash)

  ingredient_hash.each do |ingredient, quantity|

    quantity_array = quantity.split 

    if quantity_array[1].include?("teaspoon")
      quantity = quantity_array[0].to_i * 5

    elsif quantity_array[1].include?("cup")
      quantity = quantity_array[0].to_i * 236.58

    elsif quantity_array[1].include?("tablespoon")
      quantity = quantity_array[0].to_i * 15

    else 
      puts "error, unable to convert '#{ingredient}: #{quantity}'"

     end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The format was similar to my test code: split the value and save it in a new variable, check to see which unit of measurement is being used for that ingredient, transform the quantity into an integer and multiply it by the proper adjustment in grams. By setting the #include? method to "cup", it allowed both the writing of "cup" and "cups" to be evaluated for the operation.&lt;/p&gt;

&lt;p&gt;Though this solution worked well in some respects, it had a few huge errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It didn't account for the possibility that some ingredients may not be integers, but floats. With flour, for example, the program evaluated 3 cups of flour instead of 3.5, resulting in 709 grams of flour instead of the accurate 828 grams. &lt;/li&gt;
&lt;li&gt;The return value did not print the adjusted ingredients, only the error message, and the original hash&lt;/li&gt;
&lt;li&gt;The program didn't adjust the value of instant_dry_yeast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To fix the bugs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I converted

&lt;code&gt;quantity_array[0]&lt;/code&gt;

to a float instead of an integer, and then later returned it to the more pleasing-to-the-eye integer value.
* I didn't want to lose the original recipe's values, so instead I added a puts line that printed the ingredient and its quantity in a more readable way
* I looked up and added a line for "envelope" to convert the value of instant_dry_yeast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, my code read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def gram_converter(ingredient_hash)

  puts "Ingredients converted to grams"

ingredient_hash.each do |ingredient, quantity|

quantity_array = quantity.split 

        if quantity_array[1].include?("teaspoon")
      quantity = quantity_array[0].to_f * 5
      quantity = quantity.to_i 
      puts "#{ingredient}: #{quantity} grams"

        elsif quantity_array[1].include?("cup")
      quantity = quantity_array[0].to_f * 236.58
      quantity = quantity.to_i 
      puts "#{ingredient}: #{quantity} grams"

        elsif quantity_array[1].include?("tablespoon")
      quantity = quantity_array[0].to_f * 15
      quantity = quantity.to_i 
      puts "#{ingredient}: #{quantity} grams"

        elsif quantity_array[1].include?("envelope")
      quantity = quantity_array[0].to_f * 7
      quantity = quantity.to_i 
      puts "#{ingredient}: #{quantity} grams"

        else 
      puts "error, unable to convert '#{ingredient}: #{quantity}'"

        end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I couldn't help but feel like there was far too much repetition in this code and remembered that I had learned about a way to re-write conditional statements to perform relatively similar operations on code with different values. In came the case statement! &lt;/p&gt;

&lt;p&gt;I didn't remember the format well, so I hopped on Google and found &lt;a href="https://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/"&gt;a great article on Skorks&lt;/a&gt; that walked me through it. Now, my code performs the same task, but looks a lot cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def gram_converter(ingredient_hash)

  puts "INGREDIENTS CONVERTED TO GRAMS"

  ingredient_hash.each do |ingredient, quantity|

    quantity_array = quantity.split

     case quantity_array[1]

    when "teaspoon", "teaspoons" 
      quantity = quantity_array[0].to_f * 5

     when "cup", "cups"
      quantity = quantity_array[0].to_f * 236.58

        when "tablespoon", "tablespoons"
      quantity = quantity_array[0].to_f * 15

        when "envelope", "envelopes", "packet", "packets"
      quantity = quantity_array[0].to_f * 7

     else 
      puts "error, unable to convert '#{ingredient}: #{quantity}'"
    end

        puts "#{ingredient}: #{quantity.to_i}"
        end

    puts "ORIGINAL: #{ingredient_hash}"
end

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



&lt;p&gt;So when I call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 I get:



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

&lt;p&gt;INGREDIENTS CONVERTED TO GRAMS&lt;br&gt;
sugar: 5&lt;br&gt;
bread_flour: 828&lt;br&gt;
olive_oil: 30&lt;br&gt;
instant_dry_yeast: 7&lt;br&gt;
water: 354&lt;br&gt;
ORIGINAL: {:sugar=&amp;gt;"1 teaspoon", :bread_flour=&amp;gt;"3.5 cups", :olive_oil=&amp;gt;"2 tablespoons", :instant_dry_yeast=&amp;gt;"1 envelope", :water=&amp;gt;"1.5 cups"}&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This method just scrapes the surface of useful ingredient conversion. Could I re-write it to pull the conversions from a database to include even more measurement types (such as the information for the grams in an envelope of yeast instead of adding it as another elsif statement)? How could I account for ingredients that are used multiple times (such as olive oil in the original recipe first being used in the quantity of two tablespoons and later on being used in the quantity of two teaspoons)? Is there a way to preserve ranges of quantities, such as 3.5 to 4 cups of bread flour, and have both of the values converted? How might I adjust the program to simply copy and paste the ingredient list into a user input field, and have the program itself format it into a hash before converting the data to grams?

As I learn more about programming, I hope to come back and update this method to incorporate the functionality mentioned above. For now, I'm just thrilled to have a way to easily get a list of my ingredients without spending too much time doing the math. 

*Do you have any ideas to make my code terser? Do you have any answers to the questions above? What are some of your favorite applications of programming in your daily life?*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

</description>
      <category>ruby</category>
      <category>beginners</category>
      <category>hashes</category>
    </item>
  </channel>
</rss>
