<?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: Jessie Rohrer</title>
    <description>The latest articles on DEV Community by Jessie Rohrer (@jrrohrer).</description>
    <link>https://dev.to/jrrohrer</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%2F560041%2Ffe9d0812-b486-424c-a537-904879ad8fe9.jpeg</url>
      <title>DEV Community: Jessie Rohrer</title>
      <link>https://dev.to/jrrohrer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jrrohrer"/>
    <language>en</language>
    <item>
      <title>Ruby Reduce</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Mon, 24 Jan 2022 18:28:31 +0000</pubDate>
      <link>https://dev.to/jrrohrer/ruby-reduce-cob</link>
      <guid>https://dev.to/jrrohrer/ruby-reduce-cob</guid>
      <description>Photo by &lt;a href="https://unsplash.com/@shambam?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Sam 🐷&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/funnel?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
If you have bounced back and forth between Ruby and JavaScript like I have, you may have found yourself doing some mathematical operations in Ruby and thinking to yourself, "Wouldn't it be nice if Ruby had a reduce method?". If you followed a similar Ruby learning path to me, you might have learned about the &lt;code&gt;inject&lt;/code&gt; method, but not put two and two together (ha!). &lt;/p&gt;

&lt;p&gt;In Ruby, &lt;code&gt;inject&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt; are aliases for each other. They both behave the same way, and there is no performance benefit to one over the other. They both combine all elements to a single output. &lt;code&gt;reduce&lt;/code&gt; is a Ruby enumerable, which means that for each element in your argument, something is done. In the case of &lt;code&gt;reduce&lt;/code&gt;, you can specify an operation, a block, or pass in a symbol to tell the reduce method how you want your values, um, reduced.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this method, we are passing in an array of numbers to add up to a total. We pass in 0 as the initial argument to reduce, so it knows where to start counting. Then, inside the block, we tell the &lt;code&gt;reduce&lt;/code&gt; method what we want it to do. For this method, we want the reduce method to keep a running total, called &lt;code&gt;sum&lt;/code&gt;, and then add each number in the array to it. So if we pass in the array &lt;code&gt;[1, 2, 3, 4, 5]&lt;/code&gt;, our operation will look something like this as reduce iterates over the array:&lt;/p&gt;

&lt;p&gt;sum = 0&lt;br&gt;
sum = 0 + 1&lt;br&gt;
sum = 1 + 2 &lt;br&gt;
sum = 3 + 4&lt;br&gt;
sum = 7 + 5&lt;/p&gt;

&lt;p&gt;A shorthand way of writing the same method would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not tell &lt;code&gt;reduce&lt;/code&gt; what the initial value is, the first element of the array is used as the initial value. Here, we're passing in a symbol instead of a block which tells &lt;code&gt;reduce&lt;/code&gt; to add up all the elements in the array. &lt;/p&gt;

&lt;p&gt;You use reduce to do all sorts of mathematical operations on larger amounts of numbers where a final answer is the desired output. Here's an example with multiplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce_mult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have to tell the &lt;code&gt;reduce&lt;/code&gt; method to start at 1, since 0 * anything is 0, and the method will output 0 every time, no matter what you pass in. &lt;/p&gt;

&lt;p&gt;I hope this helps the next time you find yourself needing to do math with more than two numbers in Ruby!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-reduce" rel="noopener noreferrer"&gt;Ruby Docs on Reduce&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>enumerables</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why You Should Care About Web Accessibility, and What to Consider When Designing Your Next Project</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Sat, 15 Jan 2022 21:17:25 +0000</pubDate>
      <link>https://dev.to/jrrohrer/why-you-should-care-about-web-accessibility-and-what-to-consider-when-designing-your-next-project-1465</link>
      <guid>https://dev.to/jrrohrer/why-you-should-care-about-web-accessibility-and-what-to-consider-when-designing-your-next-project-1465</guid>
      <description>Photo of a laptop and braille display by &lt;a href="https://unsplash.com/@elizabeth_woolner?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Elizabeth Woolner&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/accessibility?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
One of the important topics that generally gets sort of glossed over in bootcamp courses and online learning resources used by many self-taught developers is web accessibility. If you're going to be building up your skills to become a successfully employed front-end developer, you should really spend some time getting familiar with web accessibility best practices. Starting with the WCAG guidelines is a good idea, but just reading them might not give you a good idea of the whole picture. &lt;br&gt;
If you're looking for more in-depth exposure to why web accessibility should be on your mind when you're designing a new project, I suggest checking out the WCAG's free course on edX, &lt;a href="https://learning.edx.org/course/course-v1:W3Cx+WAI0.1x+3T2019/home" rel="noopener noreferrer"&gt;Introduction to Web Accessibility&lt;/a&gt;, which will introduce you to the different tools those with disabilities use to access information online, as well as many difficulties they face in doing so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Is Web Accessibility Important?
&lt;/h2&gt;

&lt;p&gt;Even if people with disabilities aren't your primary audience, incorporating accessibility best practices into your development process will expand the audience your content is able to reach. Optimizing your site for use with screen readers or making it navigable by people who only use keyboard, button, or switch inputs opens your content up to a wider audience. &lt;br&gt;
Even accessibility features built in to modern technology that were meant for those with disabilities can be useful to those of us who aren't disabled. For example: using voice commands to interact with devices, setting dark mode to reduce eye strain, using zoom features to make text larger, or using closed captions on videos when we can't play the audio out loud. So, in essence, what makes things easier for those with disabilities, makes things easier for all of us. &lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Things to Keep In Mind When Designing Your Project
&lt;/h2&gt;

&lt;p&gt;When you're in the early stages of creating a new project, keep in mind these design elements that can improve your project's accessibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Color contrast

&lt;ul&gt;
&lt;li&gt;Make sure you use available tools (like Adobe Color) when you're selecting your project's color palette, and check the contrast between your colors to ensure your information is easily readable.&lt;/li&gt;
&lt;li&gt;You can also use tools like Coolors, which has options for viewing your chosen colors as those with different types of color blindness would see them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Navigation

&lt;ul&gt;
&lt;li&gt;Ensure that you use semantic HTML to denote your site's main navigation (wrap it in the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; tag). This will make things simpler for those using screen readers or keyboard-only inputs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Tab Order

&lt;ul&gt;
&lt;li&gt;Pay attention to the order in which your content is navigated when tabbing through your website. Does it make sense? Does it follow the reading order of your content (i.e. left to right, top to bottom)? Do you need a hidden skip to content link at the top of your page to help the user easily get to the main content on the page?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Font Size

&lt;ul&gt;
&lt;li&gt;Make sure you use an appropriate size for your chosen fonts. Also, don't use images of text. True text enlarges more easily, loads faster, and is easier to translate and customize. It's also easier for screen reader users to decipher. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Clearly indicated links

&lt;ul&gt;
&lt;li&gt;Links in text shouldn't be indicated solely by color. There should always be a fallback indicator for links, like an underline, for those who can't discern the difference in color. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Link messages make sense on their own

&lt;ul&gt;
&lt;li&gt;Link text should indicate what they link to. Try to avoid link text like 'Click Here', as it doesn't indicate what the link leads to. Someone using a screen reader to hop between links would have a hard time finding the information they're searching for if your page is full of 'Click Here' or 'Read more' links.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Heading Structure

&lt;ul&gt;
&lt;li&gt;Ideally, your page should have a single H1 element, and h2-6 should be used in a nested fashion to create a logical heading structure, which will make your content more easily readable by every user.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Keyboard focus indicators

&lt;ul&gt;
&lt;li&gt;Make sure your actionable elements (links, buttons, etc) are keyboard focusable. Most browsers will indicate the current focus location with a border of some kind, but you will need to add this ability to some elements, or pay attention not to accidentally remove it from others.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make sure information doesn't rely solely on color

&lt;ul&gt;
&lt;li&gt;Just like you shouldn't indicate links with just color, the same goes with other forms of important information. If you have elements that convey information using only color, those settings may get overridden by user settings or they may be indiscernible to the user due to colorblindness. Screen reader users would miss this information entirely.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Accessible form controls

&lt;ul&gt;
&lt;li&gt;Making sure form controls have descriptive labels, instructions, and error messages will help users easily 
navigate your forms. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some Good Places to Start with Accessibility
&lt;/h2&gt;

&lt;p&gt;1) Lay out your project using semantic HTML. Use appropriate tags for your elements (like the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; tag for the navbar), which will ensure that the browser/screen reader handles those elements correctly. Avoid &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; soup -- divs have no semantic meaning. &lt;br&gt;
2) Use alt text in your image tags. Providing alt text helps convey the meaning of an image to someone who can't view it, whether they're using a screen reader or they have a slow connection.&lt;br&gt;
3) Ensure that there is sufficient color contrast between text and background colors. Using tools like Adobe Color, which has built in settings for addressing color contrast, or tools like &lt;a href="http://a11yrocks.com/colorPalette/" rel="noopener noreferrer"&gt;A11y Rocks color palette&lt;/a&gt; should help you ensure appropriate contrast for your colors. &lt;br&gt;
4) Use descriptive links. This one is pretty easy: make sure your links tell the user what they lead to! 'Click Here' is not always helpful, especially to screen reader users. Visually impaired users can scan through links just like sighted users can, so using descriptive link text helps to put the link in context for all your users.&lt;br&gt;
5) Provide captions if you're embedding video. Captions provide content to users in real time when audio is not an option. Many youtube videos now have the option to turn captions on or off. Or, if you are creating the video content yourself, consider including open captions (which is an option available on TikTok, where users include captions that are part of the video and can't be turned off) or providing a transcript for users who can't make use of audio content.&lt;br&gt;
6) Test out your website on a mobile device. You can use the responsive design mode in the DevTools in both Chrome and Firefox to check out how your site works on mobile (after all, not everyone has access to a laptop or desktop computer). Are your links clickable? Are there spaces between your buttons that make it easy for the user to click what they want to click?&lt;/p&gt;

&lt;p&gt;Web accessibility is an important concept for new webdevs to grasp. Making the above considerations second nature early on will ensure that all of your projects are accessible to a wider audience. Accessibility principles make the web a better place for everyone!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Post-Bootcamp Retrospective</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Wed, 05 Jan 2022 14:23:01 +0000</pubDate>
      <link>https://dev.to/jrrohrer/post-bootcamp-retrospective-2da3</link>
      <guid>https://dev.to/jrrohrer/post-bootcamp-retrospective-2da3</guid>
      <description>&lt;p&gt;On November 9th, I passed my final project assessment and officially graduated from Flatiron School. The following weekend, I competed in my first hackathon, Technica, with two fellow alumnae, and we won both categories we submitted our project to. &lt;/p&gt;

&lt;p&gt;The past two months have been full of career prep -- transforming my LinkedIn page to reflect my new skills, building a &lt;a href="https://jessierohrer.info" rel="noopener noreferrer"&gt;portfolio site&lt;/a&gt; to showcase my projects, mock interviews, and working through Flatiron's postwork curriculum. &lt;/p&gt;

&lt;p&gt;Just before Christmas, I completed my mock technical interview, which went really well considering I didn't know much about data structures and am still stuck on the "functional components are presentational components" mindset. As part of the career prep curriculum, graduates are given a token for a free mock technical interview through SkilledInc. I chose the React interview, which lasted about an hour. I was asked several questions: Tell me about yourself, some JavaScript concept questions, and a few data structure questions. The problem I was given to solve was: Create two buttons containing the text "A" and "B". When A is clicked, the text should become bold. When B is clicked, B should become bold and A should be un-bolded. I answered this question with a lot of basic JavaScript, partly because I was given the problem inside of a functional component. When the interviewer asked why I didn't make use of state to solve the problem, I told her how I would have solved this inside of a class component: The clicked button would be tracked in state, the clicked button would be given a className attribute, and some CSS would be triggered to change the styling based on which button was clicked. But since my mindset for functional components is that they are presentational, I solved the problem without using state. She accepted my answer, and I passed. I had been really nervous about the mock technical going in, but the interviewer was very patient, calm, and friendly. She gave me a lot of great advice on where to go from here, what to practice, and what to learn next.&lt;/p&gt;

&lt;p&gt;Since then, I have been really enjoying finally being able to dig into the stuff I want to learn myself. I've found a course on edX on Web Accessibility, and it's my first step toward hopefully obtaining a certification from the &lt;a href="https://www.accessibilityassociation.org/s/certification" rel="noopener noreferrer"&gt;IAAP&lt;/a&gt;. I've also been focusing more on the data structures and algorithms problems provided as part of the post work. And I've started learning TypeScript, since it keeps coming up alongside JavaScript in a lot of job postings. &lt;/p&gt;

&lt;p&gt;Flatiron taught me a lot and gave me a good foundation for learning more in the future. Now that I have experience with a lot of the major programming concepts, I can pick up where Flatiron left off on my own and shape my knowledge to fit my personal goals. It would have been nice to have a more up-to-date education, especially when it comes to functional programming in React, or an introduction to Redux ToolKit, but I have the skills now to learn that stuff on my own. And I'm looking forward to doing it!&lt;/p&gt;

&lt;p&gt;Today, I am going to declare my job search start date. I have already started applying for different positions, and I am looking forward to rejoining the workforce this year. Wish me luck!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>Rendering a React Modal from Another Component</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Sun, 31 Oct 2021 03:43:50 +0000</pubDate>
      <link>https://dev.to/jrrohrer/rendering-a-react-modal-from-another-component-2omn</link>
      <guid>https://dev.to/jrrohrer/rendering-a-react-modal-from-another-component-2omn</guid>
      <description>&lt;p&gt;Y'all, I just ventured into the land of modals in React, and my head is spinning. I just wanted a little success message to display when someone posts a review to my &lt;a href="https://dev.to/jrrohrer/bookcritiq-a-react-project-1glj"&gt;BookCritiq app&lt;/a&gt;. I'm going to share my solution here (before I forget) and hopefully this will help anyone else slogging through learning the same thing.&lt;/p&gt;

&lt;p&gt;My app was built using Create-React-App and is styled with Bootstrap, so I used a Bootstrap modal component to create my modal.I placed my modal inside a functional component, since it's state will be handled by the parent component (the component that renders it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-bootstrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SuccessModal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;// passing in the isOpen prop from the container&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Modal.Title&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Modal.Header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Thanks&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;contribution&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Modal.Body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Footer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="c1"&gt;// passing in the toggle function so that clicking the OK button closes the modal&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Modal.Footer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Modal&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;SuccessModal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am going to handle the state for the modal component in my ReviewsContainer component. This container renders both my ReviewCreateForm and Reviews components. The trigger for the modal to appear will be the submit button on the ReviewCreateForm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ReviewsContainer.js&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReviewsContainer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// setting the initial state for the modal as hidden&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// creating a toggle function that will be passed down to any children of this container that need to toggle the modal on or off&lt;/span&gt;
  &lt;span class="nx"&gt;toggleModal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showModal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="c1"&gt;// pass the toggle function to the ReviewCreateForm&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/books/:id/reviews/new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;routeProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReviewCreateForm&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;routeProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleModal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt; } /&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Reviews&lt;/span&gt; &lt;span class="nx"&gt;reviews&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="c1"&gt;// Render the modal and pass down the state and toggle function as props.&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SuccessModal&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleModal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ReviewsContainer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I am going to add the toggle function to my handleOnSubmit event handler inside of my review form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ReviewCreateForm.js&lt;/span&gt;

  &lt;span class="nx"&gt;handleOnSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the time I spent banging my head on the wall over figuring this out was because I was trying to allow the modal component to oversee it's own state. I forgot one of the main characteristics of React: The data flows down.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover Photo by &lt;a href="https://unsplash.com/@mikeanywhere?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Mike Lewis HeadSmart Media&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/waterfall?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>BookCritiq: A React Project</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Fri, 22 Oct 2021 04:00:40 +0000</pubDate>
      <link>https://dev.to/jrrohrer/bookcritiq-a-react-project-1glj</link>
      <guid>https://dev.to/jrrohrer/bookcritiq-a-react-project-1glj</guid>
      <description>&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@ugur?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Ugur Akdemir&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/books?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have made it! This is my final project for the Flatiron School software engineering bootcamp! I was so excited to start this project, I flew through the last section of the course. Partially because I know from my experience building all of my previous projects that project mode is where I learn the most. And React is a handful - I did &lt;em&gt;so&lt;/em&gt; much googling and reading and researching.&lt;/p&gt;

&lt;p&gt;I landed on the idea of creating a book review app for this project after sifting through some other ideas that turned out to be a bit complicated for a first foray into building with React. The basic idea is to create a space where people can find or add a book in the database, then add their review to it. My MVP requires just the book and review models, but I built out my Rails API back end with the full app in mind -- I used the Rails scaffold generator to spin up four models: User, Book, Review, and Comment (no test framework, please). This was so quick that it felt like cheating. I added some books and a couple of reviews to the seed file, sorted out model associations, and set up my serializers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A note on serializers here. I used &lt;code&gt;fast-jsonapi&lt;/code&gt; for this project because I was familiar with it. I &lt;em&gt;highly&lt;/em&gt; recommend that you use a different serializer like ActiveModel Serializer, because fast-jsonapi nests your serialized data inside a data object, which makes your data more complicated to work with on the front end. I spent a lot of time console-logging and sorting out how to access the pieces of data I wanted, and using a less deeply nested set of data would have made things a lot easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After playing around with creating and relating objects and seeing what I could access at different endpoints, I moved on to the front end.&lt;/p&gt;

&lt;p&gt;I created a boilerplate react app using the &lt;code&gt;create-react-app&lt;/code&gt; command. I then spent a few minutes removing the bits I wasn't going to need. Then I tackled adding some dependencies. The project requirements state that I have to make use of Redux and Thunk, and I knew I wanted to tackle adding styling with bootstrap, as well as handling client-side routing with React Router, so I added those dependencies with npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install redux
npm install react-redux
npm install redux-thunk
npm install bootstrap
npm install react-bootstrap
npm install react-router-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step was to tackle setting up my App component, which I refactored from the boilerplate functional component to a class component. I then moved over to the Index component and set up the Redux store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;thunk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-thunk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;composeEnhancers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__REDUX_DEVTOOLS_EXTENSION_COMPOSE__&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// store is where data is stored globally, reducers tell us what to do with our store based on certain actions.&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;booksReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;composeEnhancers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thunk&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;


&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/React.StrictMode&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then create a reducer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;booksReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]},&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the app will process actions and modify the Redux store. &lt;/p&gt;

&lt;p&gt;With the store and reducer set up, I moved on to creating some book components. I decided that the main component will be the BookContainer, which will render BooksList, BookCreateForm, and BookShow components. BooksList contains links to all of the books, BookShow renders a single book, and BookCreateForm contains a controlled form for creating a new book. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I ran into some trouble getting the correct book to render on the BookShow component. I ended up using the &lt;code&gt;useParams()&lt;/code&gt; hook that comes built into React to access the id in the URL the user clicked in order to render the component, then used that id to filter out the correct book from state. Using &lt;code&gt;let book = props.books[props.match.params.id - 1]&lt;/code&gt; selected the book based on its position in the array, and I wanted the id of the book rendered to match the id in the URL, not a position in the props array. The final code I used to achieve this looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This pulls the id value directly from the URL.&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// This line filters out the book we're looking for based on the ID in the URL&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// check if the book was found. If not, return null (allow time for the fetch to run).&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bookData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// This is just to make it so I don't have to say 'book.attributes.property' every time I want to access the book data. Thanks, fast-jsonapi!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I followed a flow like this to complete the rest of the project: Create components -&amp;gt; import necessary files -&amp;gt; decide if the component should be a class or functional component -&amp;gt; build out the component -&amp;gt; connect to the store if necessary -&amp;gt; build a new action if necessary -&amp;gt; add a case to the reducer to handle the action if necessary -&amp;gt; connect to other components with React Router if needed -&amp;gt; test everything works. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmshr7g5ynh1tzrxquoym.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmshr7g5ynh1tzrxquoym.gif" alt="Spongebob Go with the flow" width="498" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the Book components set up, I moved on to the Review components. I created a Review container that will be rendered by the BookShow component. The only place reviews will be visible currently is on a book's individual show page. Inside the ReviewContainer component, there are CreateReviewForm and Reviews components. CreateReviewForm contains a controlled form for adding a review to a Book, and the Reviews component is responsible for rendering all of the reviews. &lt;/p&gt;

&lt;p&gt;With the main functionality in place, I moved on to UI design. The first thing I did with bootstrap is add the CDN script to the head of the index.html file. Aside from removing some standard create-react-app code, this was the only time I touched this file. I chose to use the CDN because users may have already come across it and their browsers will already be aware of Bootstrap's styles, which means that the styles for my app will load faster in their browser.&lt;/p&gt;

&lt;p&gt;With that set up, I created a Navbar component and added links to the Routes that have been created throughout my app. I then styled it responsively using bootstrap class tags. I'll be honest: I pieced this together based on Bootstrap's documentation examples after a couple of hours of googling and figuring out how to set my own color scheme. &lt;/p&gt;

&lt;p&gt;I found &lt;a href="https://medium.com/swlh/responsive-navbar-using-react-bootstrap-5e0e0bd33bd6" rel="noopener noreferrer"&gt;this article&lt;/a&gt; to be very helpful for setting up a responsive navbar.&lt;/p&gt;

&lt;p&gt;The final component added is a Home component, which serves as a landing page for the app. My final steps were to move through all of my components and apply appropriate styles using bootstrap components and class tags. While I did spend a lot of time learning, implementing the Bootstrap library turned out to be much faster at making the app look appealing than trying to write out the CSS all on my own. &lt;/p&gt;

&lt;p&gt;Overall, I am pleased with the outcome of this project. I am still a little shaky on some of the concepts, but I have a much better grasp on React and Redux (and Bootstrap!) than I did when I started. I am excited to come back to this project after graduation to sort out adding users, authentication, and comments.&lt;/p&gt;

&lt;p&gt;If you'd like to check out the repos, you can find the front end &lt;a href="https://github.com/jrrohrer/book-critiq-fe" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and the back end &lt;a href="https://github.com/jrrohrer/book-critiq-be" rel="noopener noreferrer"&gt;here&lt;/a&gt;. (And if you do check them out, shoot me a message! I'd love to talk about it, especially if you have suggestions.)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Rails/JS Project: Apple Expert Front End</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Mon, 23 Aug 2021 15:53:27 +0000</pubDate>
      <link>https://dev.to/jrrohrer/rails-js-project-apple-expert-front-end-1jm7</link>
      <guid>https://dev.to/jrrohrer/rails-js-project-apple-expert-front-end-1jm7</guid>
      <description>&lt;p&gt;&lt;em&gt;See my previous post about setting up the back end &lt;a href="https://dev.to/jrrohrer/rails-js-project-apple-expert-back-end-3bio"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I started the front end for this project, I sketched out a basic idea of what I wanted. A hero header image followed by a drop-down where a user can select the type of recipe they're making, then a submit button. Below that would be a div that is hidden until it is populated with cards displaying each apple that matches their query. At the bottom of the page would be a hidden form for adding a new apple to the database. When a button is clicked, the form would appear. When the form is submitted, the user will see a preview of their new apple card and a success message. &lt;/p&gt;

&lt;p&gt;I started with the bones of the app: the HTML. I hashed out the required elements with descriptive id's so that they can easily be found and manipulated with css and javascript later. I created the forms but left the drop down select elements empty so that they can be dynamically populated with javascript. I then created the needed javascript files and linked them at the bottom of the html body section. If I add a footer later, I will move the links down so that all of the html content is loaded before the JS files are run.&lt;/p&gt;

&lt;p&gt;With the HTML finished for now, I moved on to the index.js page. Here I made a mistake and decided to just start hashing out my functionality in vanilla js. The project requirements say that my JS has to be object oriented according to ES6 standards, and I should have written it that way from the start, because refactoring later was a headache.&lt;/p&gt;

&lt;p&gt;Once I got most of the behavior I wanted on the DOM, I made a new branch in git and started refactoring. I have 5 javascript files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index.js // handles all of the direct DOM manipulation/event listeners
apple.js // the apple class that handles instances of apple objects
category.js // the category class that handles instances of category objects
appleApi.js // handles api calls to the apples endpoint
categoryApi.js // handles api calls to the category endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refactoring involved me moving code from index.js into the appropriate class until all that remained in index.js is the javascript that specifically controls the DOM in a way that is not directly related to one of the classes. All of my global variables live here, along with some event listeners, and a form handler function that takes the user's input in the create apple form and turns it into data that can be passed back to the database in a post fetch. &lt;/p&gt;

&lt;p&gt;The order of operations goes like this:&lt;br&gt;
On page load, the drop down select elements are populated with the category objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// categoryApi.js
getCategories() {
  return fetch(this.baseUrl)
    .then(response =&amp;gt; response.json());
}

populateCategoryDropdown() {
    this.getCategories()
    .then(categories =&amp;gt; {
      const dropDown = document.getElementById('category'); // gets the first select element
      const categorySelection = document.getElementById('add-category'); // gets the select element in the new apple form
      categories.data.forEach(category =&amp;gt; { // for each category in the data object
        let c = new Category(category.id, category.attributes)
        let newOption = new Option(c.name, c.id) // create a new option with name key and id value
        dropDown.add(newOption, undefined) // add the new option to the bottom of the dropdown list
        let newCategoryOption = new Option(c.name, c.id)
        categorySelection.add(newCategoryOption, undefined) // does the same thing, but for the create new apple form at the bottom of the page
      })
    })
    .catch(err =&amp;gt; alert(err));
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when the user selects the category and clicks "Show me the apples!", a second get fetch is sent, this time to the apple endpoint, with a query param that contains the category id for the selected category. The back end sends back only the apples that match that category. The apple data is iterated over, and a new apple object is created for each apple in the data object. Then, each apple has a card created for it and displayed on the DOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// appleApi.js
  getApples() {
    let categoryId = parseInt(document.getElementById('category').value); // getting the category ID and turning it into an integer
    fetch(`${port}/apples?category_id=${categoryId}`)
    .then(response =&amp;gt; response.json())
    .then(apples =&amp;gt; {
      appleCardsContainer.innerHTML = ""; // clears any old search results before displaying new ones
      messageDiv.innerHTML = ""; // clears any messages before new search
      Apple.all = []; // clears the Apple.all array before handling the new search results
      apples.data.forEach(apple =&amp;gt; {
        let a = new Apple(apple.id, apple.attributes)
        a.displayApple()
      })
    })
    .catch(err =&amp;gt; alert(err));
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// apple.js

getCard() {
  const appleCard = `
    &amp;lt;div data-id=${this.id} class="apple-card"&amp;gt;
      &amp;lt;img src="${this.image_url}"&amp;gt;
      &amp;lt;h3&amp;gt;${this.variety}&amp;lt;/h3&amp;gt;
      &amp;lt;h4&amp;gt;Harvested in ${this.harvest}&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;${this.notes}&amp;lt;/p&amp;gt;
      &amp;lt;button&amp;gt;Delete&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;`;

  this.element.innerHTML = appleCard;
  return this.element;
}

displayApple = () =&amp;gt; {
  const appleCard = this.getCard();
  Apple.container.appendChild(appleCard);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user decides to create a new apple, they can click the button to show the form, then fill it out. When they click submit, we prevent the default post action and instead collect the user's inputs and pass them to a post fetch back to the apple endpoint.&lt;br&gt;
&lt;/p&gt;

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

function createFormHandler(e) {
  e.preventDefault();
  const varietyInput = document.querySelector("#input-variety").value;
  const harvestInput = document.querySelector("#input-harvest").value;
  const notesInput = document.querySelector("#input-notes").value;
  const imageInput = document.querySelector("#input-image-url").value;
  const categorySelections = document.getElementById('add-category').selectedOptions;
  // the following line collects all of the ids from the selected category objects
  const categoryIds = Array.from(categorySelections).map(x =&amp;gt; x.value);
  appleApi.postApple(varietyInput, harvestInput, notesInput, imageInput, categoryIds)
  e.target.reset()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The post fetch is then called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// appleApi.js

postApple(variety, harvest, notes, image_url, category_ids) {
  let bodyData = {variety, harvest, notes, image_url, category_ids}
  fetch(`${port}/apples`, {
    method: "POST",
    headers: {"Content-Type": "application/json"},
    body: JSON.stringify({apple: bodyData})
  })
  .then(response =&amp;gt; response.json())
  .then(apple =&amp;gt; {
    appleCardsContainer.innerHTML = ""; // clears previous results
    messageDiv.innerHTML = "&amp;lt;h3&amp;gt;Your apple has been saved to the database and will be included in future searches. Thanks for contributing to Apple Expert!&amp;lt;/h3&amp;gt;" // displays success message
    let a = new Apple(apple.data.id, apple.data.attributes) // creates new apple object
    a.displayApple() // displays new apple card on the DOM
  }) 
  .catch(err =&amp;gt; alert(err));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user searches again, the previous results are cleared, as well as the success message. The new apples will appear in any new searches. &lt;/p&gt;

&lt;p&gt;The final feature I added was the ability to delete the apples, both from the DOM and the database. I added a delete button to each apple card, created a function to clear the card from the DOM, and then destroy the apple object in the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// apple.js

// added these two lines to the apple constructor:
this.element = document.createElement('div');
this.element.addEventListener('click', this.handleDeleteClick)

handleDeleteClick = (e) =&amp;gt; {
  if(e.target.innerText === "Delete"){
    this.deleteApple(e)
  }
}

// appleApi.js

deleteApple = (id) =&amp;gt; {
  fetch(`${port}/apples/${id}`, {
      method: "DELETE",
      headers: {"Content-Type": "application/json"},
  })
    .then(response =&amp;gt; response.json())
    .then(json =&amp;gt; alert(json.message)) 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the apple is deleted, the user will see an alert modal notifying them that the delete was successful.&lt;/p&gt;

&lt;p&gt;The final thing I worked on was the CSS. I linked a stylesheet to the index.html file and hashed out a css grid for the apple cards, handled hiding empty divs, controlled image sizing so that the images on the cards are roughly the same size but retain their aspect ratio, dressed up the buttons and forms, and added some other decorative touches.&lt;/p&gt;

&lt;p&gt;This was my first project that incorporated using JavaScript and using Rails to set up an API. It took me longer than any of the other projects to do, and when I explained it to my mom, she said, "That doesn't sound so hard." Heh. Granted, it's no Facebook or anything, but this project was challenging for me, especially since trying to force JavaScript into my brain was like mixing oil and water. There are still lots of things I don't exactly understand, but practice and reviewing will get me there.&lt;/p&gt;

&lt;p&gt;Here's the repo: &lt;a href="https://github.com/jrrohrer/apple-expert-frontend" rel="noopener noreferrer"&gt;https://github.com/jrrohrer/apple-expert-frontend&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Rails/JS Project: Apple Expert Back End</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Mon, 23 Aug 2021 14:56:17 +0000</pubDate>
      <link>https://dev.to/jrrohrer/rails-js-project-apple-expert-back-end-3bio</link>
      <guid>https://dev.to/jrrohrer/rails-js-project-apple-expert-back-end-3bio</guid>
      <description>&lt;p&gt;For my 4th project for the Flatiron School software engineering boot camp, we have to build a single page application with a Rails back end and an HTML/CSS/object-oriented JavaScript front end. I came up with a couple of ideas, but settled on a little app that will give you apple variety suggestions based on what type of recipe you're trying to make. (This project gave me major fall vibes, and now I just want to hang out with some cider!)&lt;/p&gt;

&lt;p&gt;I have to admit that I found this section of the curriculum lacking...We were introduced to some pretty high level JS stuff without covering the basics, and there was a side-step into functional programming for 1 lesson and a pretty hefty lab. I spent a lot of time on my own reading and learning the JS basics elsewhere. I know there is a section of JS in the pre-work for the boot camp, but as a self-paced student, I was told to skip the pre-work and jump right into the curriculum, so I think I missed out, and now I no longer have access to the pre-work. &lt;/p&gt;

&lt;p&gt;I also had some difficulty with OOJS, mostly because JS has prototypal inheritance, whereas Ruby has class-based inheritance. Thanks to the syntactic sugar of ES6, writing a JS class looks an awful lot like writing a Ruby class does, with some exceptions, but it behaves differently. I stumbled a lot trying to figure out what should go into a JS class vs. living in the index.js file or in an API class, and how to get the class-level data to be accessible elsewhere. My app is working, but I am going to spend more time before my assessment researching so that I can fully understand what I've written.&lt;/p&gt;

&lt;p&gt;When I sat down to start this project, I shot myself in the foot a little bit by choosing a has-and-belongs-to-many relationship between my models in the back end. I couldn't think of a good third model to instead use the more familiar (and better-supported) has-many-through relationship. So, an apple has and belongs to many categories, and a category has and belongs to many apples. Each apple can have multiple categories and each category can have multiple apples.&lt;/p&gt;

&lt;p&gt;So the first thing I did was generate models for both apple and category, set up the migrations, and seed the database with the 6 categories (table apples, cooking/baking apples, sauce apples, pie apples, juice apples, and butter apples) and the 10 most common apple varieties. Once that was done, I spent some time in the Ruby console getting familiar with my data and how it needs to be called given this new-to-me relationship. It took some tweaking and a private helper method in the apple controller to call out apples based on their categories, but I was able to get the back end behaving correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_apples_by_category(category)
    Apple.joins(:apples_categories).where(apples_categories: { category_id: category })
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I gave my back end the necessary routes for the behaviors my front end needs to have. I namespaced the routes so that if I create any updates or overhauls, it will be easier to change the endpoints and link the front end to the new back end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace :api do
    namespace :v1 do
      resources :apples, only: [:index, :create, :destroy]
      resources :categories, only: [:index]
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step was to set up the needed controller actions. The apple controller has index, create, and destroy actions. While I was at it, I added the private strong params method. I include the empty array with &lt;code&gt;category_ids&lt;/code&gt; so that all of an apple's categories can be passed through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def apple_params
    params.require(:apple).permit(:variety, :harvest, :notes, :image_url, category_ids: [])
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did the same for the categories controller. This one has only an index action and a private strong params method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Api::V1::CategoriesController &amp;lt; ApplicationController

  def index
    categories = Category.all
    render json: CategorySerializer.new(categories)
  end

  private

  def category_params
    params.require(:category).permit(:name, :category_id)
  end

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

&lt;/div&gt;



&lt;p&gt;Next, I added the fast_jsonapi gem to my gemfile and uncommented rack-cors, then re-ran &lt;code&gt;bundle install&lt;/code&gt; to add them to my project. (Don't forget to replace the sample url in the cors.rb file with a "*" wildcard for development!) I set up serializers for both of my models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AppleSerializer
  include FastJsonapi::ObjectSerializer
  attributes :variety, :harvest, :notes, :image_url, :categories
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give me a data object that contains each of the apple objects. Each apple object will have an attributes object that contains attributes as key/value pairs. This includes the category information for each apple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"data": [
  {
    "id": "10",
    "type": "apple",
    "attributes": {
      "variety": "Ambrosia",
      "harvest": "Late September",
      "notes": "Store in the refrigerator. Does not brown when sliced. Sweet and honey-like flavor. Good for salads and snacks.",
      "image_url": "https://www.freshpoint.com/wp-content/uploads/commodity-ambrosia.jpg",
      "categories": [...]
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CategorySerializer
  include FastJsonapi::ObjectSerializer
  attributes :name, :apples
end

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

&lt;/div&gt;



&lt;p&gt;When I visit the &lt;code&gt;categories&lt;/code&gt; endpoint, I will see a similar data object that contains each category object, which contains it's own attributes object. When I am working with the data on the front end, I will need to keep this structure in mind. This structure comes from the fast_jsonapi serializer, and it allows me to choose what information gets sent to the front end when a request for an apple or category is made. &lt;/p&gt;

&lt;p&gt;So in my controller, my index methods look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
    # because I am using a get request to get only apples from the user's requested category, I am sending the category ID back to the controller as a query param. The category variable saves the query param and passes it to an activerecord query method that returns all the Apple instances that match the query. I am then alphabetizing the results and passing that collection to the serializer.
    category = params[:category_id].to_i
    @apples = get_apples_by_category(category)
    sorted_apples = @apples.order(:variety)
    render json: AppleSerializer.new(sorted_apples)
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I wanted to, I could specify here which attributes to include or exclude. &lt;/p&gt;

&lt;p&gt;With my database, models, controllers, routes, actions, and serializers set up, I can now go to my endpoints and view serialized data in the browser. My back end is working as expected, and now it's time to go set up the front end.&lt;/p&gt;

&lt;p&gt;Here's a link to the github repo for anyone interested in looking around:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jrrohrer/apple-expert-backend" rel="noopener noreferrer"&gt;https://github.com/jrrohrer/apple-expert-backend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the post about setting up the front-end: &lt;a href="https://dev.to/jrrohrer/rails-js-project-apple-expert-front-end-1jm7"&gt;https://dev.to/jrrohrer/rails-js-project-apple-expert-front-end-1jm7&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rails</category>
      <category>ruby</category>
      <category>api</category>
    </item>
    <item>
      <title>Sinatra Project: Yarn Stasher</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Thu, 17 Jun 2021 02:59:15 +0000</pubDate>
      <link>https://dev.to/jrrohrer/sinatra-project-yarn-stasher-2ddd</link>
      <guid>https://dev.to/jrrohrer/sinatra-project-yarn-stasher-2ddd</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on March 20, 202` as a project requirement for Flatiron School&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I have been a crafter for a couple of decades now. If you've met me over zoom, you will see a whole wall of yarn and related implements behind me in my home office. I can spin my own yarn from wool or alpaca fibers, I can knit, and I can crochet. The downside to having this particular hobby is the lack of digital support. There are little apps you can download to keep track of what pattern row you're on, but there aren't many for quick references when you're standing in the yarn aisle wondering if you already have a particular yarn in your stash at home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1554168396-aab725fa9d34%3Fixid%3DMXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%253D%26ixlib%3Drb-1.2.1%26auto%3Dformat%26fit%3Dcrop%26w%3D1352%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1554168396-aab725fa9d34%3Fixid%3DMXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%253D%26ixlib%3Drb-1.2.1%26auto%3Dformat%26fit%3Dcrop%26w%3D1352%26q%3D80" alt="Aesthetic photo of yarn for sale in a market" width="1352" height="899"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@mkvandergriff?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Maranda Vandergriff&lt;/a&gt; on &lt;a href="/s/photos/yarn-stash?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I made yarn stasher because it's something I would use, and it's something my friends could use. The basic idea is that a user can sign up, log in, and create yarn objects that will be stored in a table view for them to reference. I wanted it to be quick and easy to use. Users cannot view other users' stashes, but they can view individual yarns if another user sends them a link to that yarn's show page.&lt;/p&gt;

&lt;p&gt;I started my project by using the Corneal gem to build out my boilerplate code and filesystem. I added a couple of folders for views so that they could be separated by model. I added controller files (users_controller and yarns_controller) which inherit from the application_controller file. Next, I removed some gems from the gemfile that I don't need (like database cleaner) and ran bundle install. Now I'm ready to get coding.&lt;/p&gt;

&lt;p&gt;The next step was to create the models. This app currently has two models: User and Yarn. A User has_many yarns and a Yarn belongs_to a user. I added some ActiveRecord validations to my User controller to ensure that each user has a unique username. Then I made use of the bcrypt gem to protect the user's password by adding the has_secure_password macro to the User model. Both models inherit from ActiveRecord::Base.&lt;/p&gt;

&lt;p&gt;Next, I set up the UsersController and YarnsController files and mounted them in the config.ru file. While I was there, I added access to Rack middleware with the line "user Rack::MethodOverride" so that my app will have access to HTTP verbs other than GET and POST. This is the line that makes PATCH and DELETE requests possible. &lt;/p&gt;

&lt;p&gt;Back in the ApplicationController, I enabled sessions and set a session secret. Now that everything is connected, I can save and get to work on my migrations.&lt;/p&gt;

&lt;p&gt;I created two migrations with rake db:create_migration: one for the users table and one for the yarns table. My users table has four colums: username, email, password_digest (so that bcrypt will salt and hash the user's password), and timestamps (not necessary at this point, but could be useful later). The primary key is assigned automatically and I do not have to add a column for it. Thanks, ActiveRecord!&lt;/p&gt;

&lt;p&gt;The yarns table has 6 columns. Name, color, weight, fiber, user_id (where the yarns table joins the users table) and timestamps (again, not needed now, but could be useful in the long run). &lt;/p&gt;

&lt;p&gt;I then created a seed file to fill the table with data. This will be useful for anyone who is testing out the app: it gives the database some dummy data to play with and test the app's functionality. &lt;/p&gt;

&lt;p&gt;I then ran rake db:migrate, which went smoothly, and then rake db:seed. I now had a database with a couple of users who each have 1 or 2 yarns. Time to start working on controllers and views.&lt;/p&gt;

&lt;p&gt;The first thing I did was to set the index get route. Corneal initially sets this route to render a welcome.erb view. I deleted that view and replaced it with my own index.erb file, then populated it with a header so that I could test that my route worked correctly. I updated the get route for the index page to render my index.erb file, then moved on to the users controller. &lt;/p&gt;

&lt;p&gt;In order to create a RESTful MVC app, I hashed out the routes I wanted to create and filled them with comments saying what I want each route to do. The UsersController should have the routes related to the user actions: logging in, signing up, creating a new user, redirecting to a landing page, and logging out. So I hashed out a get '/login' , post '/login', get '/signup', post '/signup', and get '/logout' routes. I initially also had a get '/users/:id' route that rendered a user show page, but decided later that it was redundant and created a security problem where other people could see a user's show page, so I removed it and used the /yarns/stash.erb view as the read route for both users and yarns. &lt;/p&gt;

&lt;p&gt;With the UsersController created and hashed out, I began filling the routes with their required variables and logic. As I worked, I realized that I would need some helper methods because I was repeating some of the same lines of code over and over. So I went back to the ApplicationController and added some helper methods to set the current_user and determine if the user was logged in.&lt;/p&gt;

&lt;p&gt;I continued on, creating views with the necessary forms as I went. The views/users folder in my app has two files: signup.erb and login.erb. The index and layout erb files are contained in the parent views folder. Both the signup and login files now contained the appropriate HTML form for both functions. I later added a "back" button to both forms so that the user could easily return to the welcome page if they chose the wrong option.&lt;/p&gt;

&lt;p&gt;I have made the login page so that a user will only be logged in if they enter credentials that exist in the database. I also used the authenticate method from bcrypt If they enter unknown credentials or submit an empty form, they will be sent back to the login page again. I later added flash messages to tell the user why their login attempt failed.&lt;/p&gt;

&lt;p&gt;The signup page will only create a new account for a user that does not already exist in the database. I did this by checking the entered username against the usernames in the database. If a user with that name already exists, the user is redirected back to the signup page and shown a flash message that asks them to log in if they already have an account or choose a different username if they do not have an account. If a blank form is submitted, the signup page will reload and show the user a flash message telling them they need to fill in the form completely.&lt;/p&gt;

&lt;p&gt;Once a user is created and logged in, they will land on the yarns/stash.erb view. So this is the next view I set up. I made a simple header that greets the user, then went to the YarnsController and began hashing out the routes and logic needed. The YarnsController has a get '/yarns' route that renders the landing page which will display all of the yarns created by that user. It has a get '/yarns/new' route that renders a form for creating a new yarn, and a post 'yarns/new' that creates and persists the new Yarn object to the database, then loads the show page for that yarn, which is rendered by a get '/yarns/:id' route. This route sets the yarn so that the view has access to its attributes, then renders the yarn's individual show page. It also has a get '/yarns/:id/edit' route that renders a form to edit a given yarn, and a patch '/yarns/:id' route that updates the given yarn based on the user's input in the edit form. Finally, there is a delete '/yarns/:id' route that checks if a user owns the yarn they are trying to delete, then deletes the yarn and redirects the user back to their stash page.&lt;/p&gt;

&lt;p&gt;When I was writing these routes, I realized I was setting yarn variables repeatedly with the line: &lt;a class="mentioned-user" href="https://dev.to/yarn"&gt;@yarn&lt;/a&gt; = Yarn.find(params[:id]), so I contained this in a private helper method. &lt;/p&gt;

&lt;p&gt;I also realized I was checking if a user owned a yarn repeatedly, so I added a helper method to the ApplicationController to help me check if a user is authorized to change or delete something.&lt;/p&gt;

&lt;p&gt;Now that my application is behaving the way it should, I can get to some UX stuff. Mainly adding buttons, making navigation easier, and doing a little styling. &lt;/p&gt;

&lt;p&gt;I started by adding a nav bar, which allows the user to get around the app and contains a link for logging out. This nav bar is viewable only when a user is logged in. I added some CSS styling to my anchor tags to make them appear horizontally across the top of the viewscreen, give them a background color and a hover action.&lt;/p&gt;

&lt;p&gt;Next, I added flash messaging at the top of the wrapper div where all the content is displayed. This will make the flash message appear (if it exists) at the top of the content area where the user will see it. &lt;/p&gt;

&lt;p&gt;I then added some styling on my navigation links, gave the app a new background color, and added a favicon that will show up on the browser tab. &lt;/p&gt;

&lt;p&gt;Finally, I used CSS to style my forms and the table that displays all of the user's yarns. &lt;/p&gt;

&lt;p&gt;I feel pretty confident at this point that my project meets the criteria, but I have a couple of stretch goals: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;More attributes for the Yarn objects: yardage, number of skeins owned, perhaps a field where users can record where they bought the yarn.&lt;/li&gt;
&lt;li&gt;Some sort of sorting option would be good. Allowing the user to sort their list of yarn objects seems like a handy UX feature, especially if the user has a long list of yarns.&lt;/li&gt;
&lt;li&gt;I need to comb over my code to make sure I've taken care of redundancies or see if there is a better way to refactor some of the logic in my controllers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm going to take a few days to see what else I can make this app do, and then I will be satisfied to add it to my portfolio.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>sinatra</category>
    </item>
    <item>
      <title>CLI Data Gem Project Part 2: TakeoutFinder</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Thu, 17 Jun 2021 02:49:17 +0000</pubDate>
      <link>https://dev.to/jrrohrer/cli-data-gem-project-part-2-takeoutfinder-3g59</link>
      <guid>https://dev.to/jrrohrer/cli-data-gem-project-part-2-takeoutfinder-3g59</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted Nov 29, 2020 as part of a project requirement for Flatiron School&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's done! (For now...)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jrrohrer/takeout_finder" rel="noopener noreferrer"&gt;Here's the github repo if you want to check out the code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After getting all of the initial setup for the gem done, I was finally able to layout my gem's functionality. There were a few decisions that I had made prior to starting to write code that ended up being dictated by the data and the layout of the website I am scraping. The flow of my gem is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;    User starts app&lt;/li&gt;
&lt;li&gt;    User is presented with a greeting and asked for two inputs (state, city). This is necessary to get the correct URL for              the city they want to search.&lt;/li&gt;
&lt;li&gt;    New scraper is created with user input which collects data and creates category and restaurant objects&lt;/li&gt;
&lt;li&gt;    User is presented with a list of restaurant categories&lt;/li&gt;
&lt;li&gt;    User selects a category&lt;/li&gt;
&lt;li&gt;    User is given a list of restaurants in that category&lt;/li&gt;
&lt;li&gt;    User selects a restaurant&lt;/li&gt;
&lt;li&gt;    User is given details for that restaurant (name, location, phone number, description)&lt;/li&gt;
&lt;li&gt;    User is asked if they are done or if they want to start over. If done - exit, if start over - begin program again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1_1dVGuF77mXSKIldd9_1agTrj4FksGUq/view?usp=sharing" rel="noopener noreferrer"&gt;Click here for the flow chart.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At any point, if the user types "exit" as a response to a prompt, the program will end. If they misspell a city name or enter an invalid state code, they will get a custom error that asks them to check their spelling and exits the program so they can start again. &lt;/p&gt;

&lt;p&gt;Initially, I was going to scrape just one city for restaurant suggestions, but decided that would give my program kind of a narrow focus. There is a #query method in the CLI class that greets the user and asks for two inputs: city and state. These inputs are stored as local variables and then used as arguements for the call to the Scraper class, which is instantiated with state and city arguements that get concatenated into the URL to be scraped. This allows the user to search any city for takeout. The previously mentioned custom error is also part of this #query method. Once the user enters their information, a new scraper is instantiated. This scrapes the website, then creates the Category and Restaurant objects. &lt;/p&gt;

&lt;p&gt;I waffled a little bit over whether the category and restaurant data should be handled by one class or two, and decided on two because it allows for more abstraction. The Category class is responsible for tracking the available restaurant categories in a given city, and it knows about the restaurants in each category. The Restaurant class stores data re: individual restaurants and is responsible for returning details about individual restaurants. It seemed like too much responsibility for one class. &lt;/p&gt;

&lt;p&gt;The Scraper class is responsible for scraping the custom URL for data re: available categories in the given city, and the details for the individual restaurants in the city. It collects the appropriate data, then stores it in individual category and restaurant objects. I had the Scraper instance methods that scrape for the data immediately use that data to create the appropriate objects. &lt;/p&gt;

&lt;p&gt;Understanding the self keyword was essential to creating this gem. The Category class and Restaurant class both use the self keyword at the class level to keep track of all of their instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def self.all
        @@all
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These can be used by calling:  &lt;code&gt;TakeoutFinder::Category.all&lt;/code&gt; or  &lt;code&gt;TakeoutFinder::Restaurant.all&lt;/code&gt; The return value would be an array of Category or Restaurant objects&lt;/p&gt;

&lt;p&gt;The Category class uses a method just like one in an earlier lab to return a list of all the restaurants that belong to a category instance using the self keyword at the instance level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def restaurants
        # creates array of restaurant objects with a category that matches the category instance.
        TakeoutFinder::Restaurant.all.select {|restaurant| restaurant.category == self.name}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Self in this method refers to the individual instance of Category. Maybe someone wants pizza. This method would return all the restaurants which have a category of "pizza". So self.name in this instance is "pizza". &lt;/p&gt;

&lt;p&gt;Overall, completing this project was a great learning experience. I am much more comfortable with using git, creating a file system with bundler, and thinking through how I want a program to work and then implementing it. I am still not quite sure that I laid everything out correctly, or that the different responsibilities are in the correct places. so I am looking forward to the refactoring section of the project review. Wish me luck!&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>CLI Data Gem Project Part 1: Planning and Prepping</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Thu, 17 Jun 2021 02:47:13 +0000</pubDate>
      <link>https://dev.to/jrrohrer/cli-data-gem-portfolio-project-part-1-planning-and-prepping-22f8</link>
      <guid>https://dev.to/jrrohrer/cli-data-gem-portfolio-project-part-1-planning-and-prepping-22f8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on Nov 16, 2020 as a project requirement for Flatiron School&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Prepping&lt;/p&gt;

&lt;p&gt;It has taken me longer than I hoped to get to this point in the curriculum, but I am excited to finally be starting the first project! I am doing my best to balance overseeing online learning for my 1st and 3rd graders, bootcamp coursework, and the added stress of every day living during a pandemic. So here we go!&lt;/p&gt;

&lt;p&gt;Step number one was to come up with an idea and find a scrapable website to help implement it. I came up with the idea of an app that scrapes local restaurant data, returns to the user an option of food categories (such as "pizza", "fast food", "burgers', "pub food", "asian", etc), and then returns a list of restaurants that fit that category in your area. Choosing a restaurant name would return to the user the location, phone number, and a description for that restaurant. The website I chose was restaurantji.com, since it has a consistent html/css layout. I have experience with bulding websites with HTML and CSS, so I was pretty confident this site would work, but I ran it through the Scraper Checker tool listed in the project planning resources lesson just to be safe. &lt;/p&gt;

&lt;p&gt;The next step for me was to just sit and watch all of the recorded lectures and the Eden Project live build videos. I found them all to be immensely helpful. I will admit I was overwhelmed after reading the project requirements, and these resources were highly valuable. If you are a fellow student reading this blog post, I &lt;strong&gt;highly&lt;/strong&gt; recommend that you don't skip over reviewing these resources. &lt;/p&gt;

&lt;p&gt;I also took the time to figure out how to represent my app in the form of a wireframe flowchart using draw.io. This was also very helpful, as I was able to hash out the basics of how I want my app to behave, what classes I will need, and how the CLI should run. Here is the first iteration (I am sure I will update this later as I work on my project): &lt;a href="https://drive.google.com/file/d/1_1dVGuF77mXSKIldd9_1agTrj4FksGUq/view" rel="noopener noreferrer"&gt;TakeoutFinder Flowchart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, I was ready to get started. I used the recommended IDE link in the CLI Data Gem Portfolio Project lab. This is the IDE sandbox, so I cleared out the files I had previously worked on to start with a blank slate. I then used bundler to create my gem's file structure. I ran &lt;code&gt;bundle gem takeout_finder&lt;/code&gt;, answered no when asked if I wanted to generate tests, yes when asked if I wanted to generate a MIT license, and yes when asked if my gem should include a code of conduct. With the file structure created, my next step was to get the repo set up on GitHub so I didn't lose my work. &lt;/p&gt;

&lt;p&gt;Doing this step in a project always feels daunting to me for some reason. But here's how it went: &lt;br&gt;
I ran &lt;code&gt;cd takeout_finder&lt;/code&gt; to move into my gem's directory, then &lt;code&gt;git status&lt;/code&gt;. I could see the list of new files ready to be added and committed. I made a minor change to the generated README.md file, ran &lt;code&gt;git add -m README.md&lt;/code&gt;, followed by &lt;code&gt;git commit -m "initial commit"&lt;/code&gt;. Now that git is tracking my project, I can get the repo added to GitHub. In my GitHub profile, I chose "create new repo" and named it the same as my gem. On the next page, I followed the instructions for uploading existing files to the repo and (thankfully) ran into no errors. After those steps were complete, I was able to run the &lt;code&gt;git log&lt;/code&gt; command to see that my initial commit showed up in the commit history. &lt;/p&gt;

&lt;p&gt;The last step for today is to get the executable bin file working. I created a new file in the bin directory and named it takeout_finder. I ran my new file to find (unsuprisingly) that it did not have executable permissions. Running &lt;code&gt;cd bin&lt;/code&gt;, &lt;code&gt;ls -lah&lt;/code&gt;, and then &lt;code&gt;chmod +x takeout_finder&lt;/code&gt; gives executable permission to my new bin file. I ran the &lt;code&gt;ls -lah&lt;/code&gt; command again to verify. Changing directory back to takeout_finder, I am now able to add a puts line in my bin/takeout_finder file and run &lt;code&gt;./bin/takeout_finder&lt;/code&gt; in the console, and it sucessfully runs my puts line. &lt;/p&gt;

&lt;p&gt;I'm all set now to get my files talking to each other correctly so I can get down to coding the functionality for my app! The next step is to get my CLI class running with some fake data. Check back later for Part 2!&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Rails Project: Yarn Stasher</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Mon, 07 Jun 2021 21:00:45 +0000</pubDate>
      <link>https://dev.to/jrrohrer/rails-project-yarn-stasher-1dd5</link>
      <guid>https://dev.to/jrrohrer/rails-project-yarn-stasher-1dd5</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@mkvandergriff?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Maranda Vandergriff&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/yarn?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week I finished up my third project for the Software Engineering course at Flatiron School. The task was to create a Rails webapp with at least one join table/model, some scope methods, validations, vanilla login/signup, oauth login/signup, at least one nested resource, and display of validation errors. &lt;/p&gt;

&lt;p&gt;For the previous section of the course, I had created a basic CRUD app using Sinatra that allowed users to keep track of their yarn stash. My hobby is spinning and knitting, so this is something I would use, and also something I can't find an existing solution for. And having come up empty on new project ideas, I decided to just build on my initial idea by adding the ability to create projects, assign yarns to a project, and leave comments on your own or others' projects.&lt;/p&gt;

&lt;p&gt;The first thing I did was spend about a week stressing over how to set up my models and migrations so that my project would meet the requirements without too much re-migrating later. I made a wireframe (after watching several videos on how to make a wireframe with Chen notation), and that helped me visualize what I needed to do. I went a step further and hand-drew a bunch of 2 inch squares on printer paper (because I felt like I was wasting time trying to figure out Canva or Figma) for each view I would need and what links were needed on what pages for navigation, where I would need validations, and what I wanted the basic layout to look like. &lt;/p&gt;

&lt;p&gt;Finally confident that I understood what I needed to do, I used the resource generator to set up the framework for my app. I added some basic validations and generated some seed data to play with. This also allowed me to play with my models to understand how they would work together given the associations I assigned them. Since I am using Rails 6.0, I followed the documentation, since some commands are different from the ones I had been using during lessons and labs. The biggest difference is running &lt;code&gt;rails routes&lt;/code&gt; instead of &lt;code&gt;rake routes&lt;/code&gt;. I spent some time testing out the associations in the console to make sure I knew how they worked. &lt;/p&gt;

&lt;p&gt;The next thing I worked on was adding the vanilla login/signup/logout functionality. This was very similar to Sinatra, and didn't give me too much trouble. Hint: Make sure your bcrypt gem is un-commented in your gemfile before starting this part :) I also added a navigation bar to the application layout file to make it easier to get around while testing my app in the browser. I ended up changing this from my original plan because what made sense on paper did not necessarily make sense in the browser. I also added flash messages to display signup/login errors to the user in the views. &lt;/p&gt;

&lt;p&gt;With the login/logout functionality in place, I began working on create forms and views. I decided to nest the comments resources for index, new, create, and show actions under the projects resource. This made sense to me since all of the comments can't exist without a project. I did have to pause here and refresh my knowledge on nested routes, since I only half-understood it the first time I went through the lessons. The actions in the Comments controller took me awhile to figure out, because the new and create actions required the use of build methods, and I had to spend time trying to understand how they work and what they return. &lt;/p&gt;

&lt;p&gt;With the necessary CRUD actions created for all of the models, I moved on to some error handling. I made an error partial and then added a line of code to render it on each page if there was an error present. I added a nested form to the Create Yarn page to allow a user to also add a new project when they add a new yarn to their stash, and then I had to do a little googling to figure out how to make filling out that nested form optional. I also added an optional collection select to the form to allow the user to associate a new yarn with an existing project. &lt;/p&gt;

&lt;p&gt;One thing I had been dreading doing was figuring out scope methods. I don't think they were covered in the curriculum, but I had read about them in the Active Record documentation. So I re-read that section, and then found a lecture recording that covered the topic. I ended up with three scope methods, which display data about what's going on in the app on the post-login landing page. In the Project model, I have a scope method for finding the project with the highest number of comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scope :popular_project, -&amp;gt; { joins(:comments).group(:project_id).order('COUNT(comments.id) DESC').limit(1) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And another one for finding the 5 most recently created projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scope :recent_projects, -&amp;gt; { order(created_at: :desc).limit(5) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran into some trouble getting the scope methods to return what I wanted them to. I kept getting 'undefined method for ActiveRecord_Relation' errors. It took me a couple of hours to figure out that I needed to either iterate over the array that my scope methods were returning, or call an array method on them, like &lt;code&gt;.first&lt;/code&gt;, to get them to return the actual objects rather than an Active Record Relation object. &lt;/p&gt;

&lt;p&gt;More confident now, I added a bonus scope method to my Yarn model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scope :popular_yarn, -&amp;gt; { group(:brand_name).select(:brand_name).order("count(id) desc").limit(1) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one returns the brand_name attribute that occurs most often in the database. &lt;/p&gt;

&lt;p&gt;Scope methods conquered, I moved on to adding some before actions to require that only the users who made a thing are able to edit or delete it. So only the user who created a yarn can edit it or delete it, but any user can view it. I also made the data displayed on a user's profile page dependent on the id in the URL so that a user can view other users's profile pages to see their yarns or view their projects. I took the extra step of adding a tiny bit of logic to the views to hide the links to the edit and delete actions unless the session user_id matches the user_id associated to the object in question. &lt;/p&gt;

&lt;p&gt;Finally, I pulled some of the styling from by Sinatra project and applied it to the Rails version. I used CSS grid to arrange project links as cards on the post-login landing page and the user's profile page, added a footer, and styled the nav bar. &lt;/p&gt;

&lt;p&gt;To meet the final requirement I had remaining, I tackled adding OAuth login. I tried Facebook first, but got frustrated with it and switched over to Google. I thought I had set everything up correctly, and I was feeling confident because this was one part of the Rails curriculum that I hadn't struggled with :) But my hubris came back to bite me, because it didn't work and the reason was because I had misspelled GOOGLE in the .env file. It took me 2 hours to see that spelling error. BUT at least I remembered to add the .env file to my gitignore, so I had that going for me. &lt;/p&gt;

&lt;p&gt;I am almost ready to turn this in and move on to the JavaScript section of the course (finally!), but I have a couple of UX related changes I want to make before I'm totally happy with the state of the project. I am sure I will come back to this project in the future. I would love to be happy enough with it to put it out in the world, but it needs some JavaScript magic to make it as I envisioned it. &lt;/p&gt;

&lt;p&gt;Here's the link to the repo, if anyone is interested:&lt;br&gt;
&lt;a href="https://github.com/jrrohrer/yarn-stasher-rails" rel="noopener noreferrer"&gt;https://github.com/jrrohrer/yarn-stasher-rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! Wish me luck on my assessment!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Rails Generators</title>
      <dc:creator>Jessie Rohrer</dc:creator>
      <pubDate>Wed, 19 May 2021 21:47:07 +0000</pubDate>
      <link>https://dev.to/jrrohrer/rails-generators-1p44</link>
      <guid>https://dev.to/jrrohrer/rails-generators-1p44</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@robpumphrey?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Rob Pumphrey&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/red-blocks?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I started learning Rails a few weeks ago, the concept of using generators scared me. I am a visual learner, and I like to have information laid out neatly in front of me. I can't push to GitHub without refreshing my repo page to make sure the push actually...pushed. So imagine my anxiety when I ran a generator for the first time and about 500 files were added to my project. &lt;/p&gt;

&lt;p&gt;If you are a beginner who is just as weary of new command line actions as I am, I am here to tell you it's totally OK to embrace Rails' generators. They will make your life easier, I promise. I'm going to break down what the most commonly used generators do, what they add to your project, and how to decide which ones to use and when.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffold Generator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rails generate scaffold ModelName column_name:data_type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This one is basically cheating, but your app will be up and running (if not looking super bland) in just a couple of minutes. But it is unlikely that the code generated will be a perfect fit for your application, because it basically generates a complete app, minus the content. You will end up removing files and taking extra time to go through and figure out which of the generated files you actually need. &lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use It
&lt;/h3&gt;

&lt;p&gt;If you need an app up and running, like, yesterday, and you don't care about bloat files. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resource Generator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rails generate resource ModelName column_name:data_type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once you have your project planned out, this generator will save you a lot of time getting set up. It will create your model, controller, migration, helper file, scss file, and views folder. It will also add a full &lt;code&gt;resources&lt;/code&gt; call in your &lt;code&gt;routes.rb&lt;/code&gt; file.  You can specify attributes that will be added to your migration table, and you can specify a 'belongs_to' Active Record association that will automatically be filled in for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use It
&lt;/h3&gt;

&lt;p&gt;If you want to create your views on your own, you are using a front-end MVC framework, or you are building an API. &lt;/p&gt;

&lt;h2&gt;
  
  
  Model Generator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rails generate model ModelName column_name:data_type column_name2:data_type2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This generator is great for creating the core code needed to create a model and associated database table without adding a bunch of extra stuff to your program. &lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use It
&lt;/h3&gt;

&lt;p&gt;Primarily when you are adding a new model to your project, if you only want the model file and the migration file. &lt;/p&gt;

&lt;h2&gt;
  
  
  Controller Generator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rails generate controller ControllerName controller_action controller_action&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command will allow you to create a controller. You can append the names of controller actions you want to add, and Rails will create routes to each of those arguments. It will also create a  new view directory and a template file for each of the controller actions specified. Finally, it will also create a helper file and an scss file. &lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use It:
&lt;/h3&gt;

&lt;p&gt;This generator is ideal if you want to create a static views or non-CRUD related features. Like adding a Sessions controller for handling signups and logins. &lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Generator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rails generate migration MigrationName column_name:data_type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Like the name implies, this generator will create a new migration for you, and add it to your &lt;code&gt;db/migrate&lt;/code&gt; file. You can specify the name of the migration and any attributes you want to add. Rails is even kind enough to infer from the name of your migration what you want to accomplish. So if you name your migration &lt;code&gt;AddContentToComments&lt;/code&gt; it will create a migration that will add a column called content to your comments table. &lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Use It
&lt;/h3&gt;

&lt;p&gt;When all you need is a new migration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on Rails Generators
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can shorten the command by swapping the word &lt;code&gt;generate&lt;/code&gt; with the letter &lt;code&gt;g&lt;/code&gt;: &lt;code&gt;rails g model User username:string password:password_digest&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you don't want the included Rails test framework, append the flag &lt;code&gt;--no-test-framework&lt;/code&gt; to the end of your generate command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you ever want to know what generators are available to you, you can always type &lt;code&gt;rails generate&lt;/code&gt; into your console. You'll get an output that looks like this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: rails generate GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  application_record
  assets
  benchmark
  channel
  controller
  generator
  helper
  integration_test
  jbuilder
  job
  mailbox
  mailer
  migration
  model
  resource
  scaffold
  scaffold_controller
  system_test
  task

ActiveRecord:
  active_record:application_record

TestUnit:
  test_unit:channel
  test_unit:generator
  test_unit:install
  test_unit:mailbox
  test_unit:plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rails generators are your friend. They do a lot of grunt work for you so you can get started on the fun parts of your project. &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
