<?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: Chris Power</title>
    <description>The latest articles on DEV Community by Chris Power (@cpow).</description>
    <link>https://dev.to/cpow</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%2F31596%2F5ca6512d-4dd1-4fda-bccd-4bf8020b53aa.jpeg</url>
      <title>DEV Community: Chris Power</title>
      <link>https://dev.to/cpow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cpow"/>
    <language>en</language>
    <item>
      <title>Why I am switching from Vim to IntelliJ</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Thu, 05 Nov 2020 14:10:27 +0000</pubDate>
      <link>https://dev.to/cpow/why-i-am-switching-from-vim-to-intellij-18al</link>
      <guid>https://dev.to/cpow/why-i-am-switching-from-vim-to-intellij-18al</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;From Vim&lt;/th&gt;
&lt;th&gt;To IntelliJ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2F8805418a02fe96f9f449cb5057c0e95e%2F1ac29%2Fvimlogo.png"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2Fd2f6c44d5d9d14c9e66176feeb648fbf%2F2bef9%2Fintellijlogo.png"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  In the beginning, a love affair
&lt;/h2&gt;

&lt;p&gt;For the last 10 years or so, I have been a die-hard Vim user. I was introduced to Vim on my first programming job. It was almost an instant connection for me. I fell in love with the minimalism of only needing a terminal to do work; and the power of stringing together commands, like words, to edit programs and text.&lt;/p&gt;

&lt;p&gt;The love affair I have with Vim stuck with me throughout my career and remains strong to this very day. When I was a junior-level release engineer, working on small scripts, Vim was amazing. Later, working as a Ruby on Rails Engineer, Vim was still an amazing editor. Thoughtbot, Hashrocket, and countless other agencies, companies, and individuals contributed to the community (and cult) of Vim. That community pushed my knowledge, comfort, and understanding further and further.&lt;/p&gt;

&lt;p&gt;Eventually, I was running tmux and Vim together in the terminal. With sufficient plugins and dotfiles, I was CRUISING through codebases. I could write tests, run them, refactor code, and run commands with absolute ease.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2Fc088aa0c91b04d0725177c7b04c624c0%2F9b29b%2Fvim-and-tmux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2Fc088aa0c91b04d0725177c7b04c624c0%2F9b29b%2Fvim-and-tmux.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Vim and tmux... were so amazing&lt;/center&gt;




&lt;h2&gt;
  
  
  Old feelings fade
&lt;/h2&gt;

&lt;p&gt;Lately, though, I have been feeling a little underwhelmed by Vim. Even with my sophisticated dotfiles, knowledge, and Plugin setup; something felt off. I started a new job working on a large typescript React project. Vim couldn't handle opening a TSX file larger than 50 lines without crashing.&lt;/p&gt;

&lt;p&gt;Within the last few years, I tried my hand at programming mobile applications. Flutter and Dart -- although supported by an LSP -- was not running very well on my Vim setup. After giving Android Studio a shot with Flutter, it felt like a whole new world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android Studio with Flutter -- The tipping point
&lt;/h2&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2F2dcc652b86c5e6e9506eaded15001bb0%2F0784f%2Fandroid-studio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbrowntreelabs.com%2Fstatic%2F2dcc652b86c5e6e9506eaded15001bb0%2F0784f%2Fandroid-studio.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Android Studio was a game changer&lt;/center&gt;




&lt;p&gt;All of a sudden, I had everything at my fingertips. Auto completion, Auto formatting, snippets, and documentation lookup was all set &lt;strong&gt;By Default&lt;/strong&gt;. I actually understood the Flutter framework much better because Android Studio made deep framework code easy to debug, inspect, and explore. Not to mention, the Vim integration was &lt;strong&gt;fantastic&lt;/strong&gt;. This was the best of both worlds. I had amazing editing, minimal configuration, and Vim keybindings!&lt;/p&gt;

&lt;p&gt;When picking up a new language or framework in Vim, I would spend hours configuring plugins, dealing with linting, and looking up documentation. With and IDE like Android Studio, all of this came pre-built without any configuration.&lt;/p&gt;

&lt;p&gt;Android Studio was something of a revelation for me. I began thinking to myself, "If this is so great for Flutter, wouldn't this be great for my other projects?".&lt;/p&gt;

&lt;h2&gt;
  
  
  Vim to VSCode to Webstorm to IntelliJ
&lt;/h2&gt;

&lt;p&gt;I am now working almost exclusively on IntelliJ IDEA. It is the JetBrains defacto editor that includes plugins for almost anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby on Rails&lt;/li&gt;
&lt;li&gt;Ember&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;The list literally goes on forever&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every project I work on -- like this Gatsby blog in React, my corporate Typescript React job, or my Ruby on Rails side projects -- All work within this one editor.&lt;/p&gt;

&lt;p&gt;I had a brief stint with VSCode but, to me, it appeared to have the same flaws I disliked with Vim at the time. It was slow with large files, and it felt sluggish with complicated programs and complicated programming languages (like typescript and tsserver).&lt;/p&gt;

&lt;p&gt;I also had a long affair with WebStorm, which was mostly great. I did; however, find it odd to be using an editor &lt;strong&gt;ONLY&lt;/strong&gt; made for a few programs. If I wanted to run a Ruby on Rails app in Webstorm, I was SOL. I thought it was a little silly to run RubyMine, Webstorm, and Pycharm on one machine. If I wrote code in all three languages, shouldn't one editor be able to handle it?&lt;/p&gt;

&lt;p&gt;Eventually I wound up running IntelliJ with plugins that handle any programming need I may have. And for now, I am very happy. There are, of course, many cons with IntelliJ. But I truly believe the Pros outweigh the Cons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vim and IntelliJ, a Pros and Cons list:
&lt;/h2&gt;

&lt;p&gt;I still think Vim is a great editor and I love to break it out every once in a while. But I believe the pros of IntelliJ outweigh the cons of Vim at this point in my career. Here is an overview of my thought process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and Cons of Vim
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;When picking up a new language or framework in Vim, I would spend hours configuring plugins, dealing with linting, and looking up documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Pros:&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimalist.&lt;/strong&gt; All you need is the terminal, and that is it. You don’t need to open multiple windows, Tmux and Vim together were &lt;strong&gt;awesome&lt;/strong&gt; for this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gets you into the Flow:&lt;/strong&gt; . Vim &lt;a href="https://en.wikipedia.org/wiki/Flow_(psychology)" rel="noopener noreferrer"&gt;gets me into "The Flow"&lt;/a&gt; better than anything. I just need to move my fingers a bit, and I’m in/out of windows, buffers, splits, etc… I specifically remember smiling when I was coding with Vim, because it just felt so fun sometimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible:&lt;/strong&gt; It's been around forever, Thoughtbot, Hashrocket, and many community members contributed greatly to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Cons:&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Very steep learning curve:&lt;/strong&gt; Vim takes a long time to learn, and you’ll never stop learning it. This is not a problem with more experienced users like myself, but I thought it was worth mentioning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent Plugins:&lt;/strong&gt; Quick, tell me the best plugin for auto-completion in Vim. ALE/Omnicomplete? YouCompleteMe? VimCompletesMe? DeoPlete? There is an insane number of plugins for any task you may have. And not all plugins play together. This has become slightly better with the emergence of LSPs; however, I constantly find myself playing with plugins to make things work better, only to dislike the setup and go for another mix of plugins later. Having dot files are nice, but when you switch computers/systems, you have to install pip3/python for some packages, node/npm/yarn for others, and for some, you’ll have to cd into the dir where its installed and run a makefile! (looking at you, YouCompleteMe)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doesn’t always play nice with everything else:&lt;/strong&gt; I have had countless configurations with Vim. Vim standalone, NeoVim, Vim with Tmux, and MacVim/GUIVim. They all have strengths and weaknesses, but there is always one thing in common, it doesn’t play very well with every computer system. Try to set up copy+paste on Vim on a Mac, done? Ok great! Now do it for Linux. There are so many workarounds and hacks to get everything to play well together. When you get it set up, its nice, but sometimes its rough. I find myself reading old blog posts to remember how I set up yank + paste on my system-wide clipboard over and over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isn’t very fun with complex codebases in complicated languages (like typescript):&lt;/strong&gt; I remember starting a new job that used typescript, and there were a lot of engineers working on some really cool problems. After getting Vim/ ENV vars/ etc… all set up, I go to start woking in Vim and &lt;strong&gt;BAM&lt;/strong&gt;, any file over 20 lines freezes and its incredibly slow to work with. Sure enough, my javascript parser/highlighting plugin (see my point above about plugins) was not efficient, and it was bogging down everything. So I had to settle for a lesser parser that performed better. A similar thing happens with prettier, eslint, and tsserver. If you need your code to be auto-fixed with these technologies, its blocks read/write. Even when your IO is blocked for only a short amount of time, it makes for a trudging, slow-as-molasses feeling when writing code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros and Cons of IntelliJ
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;All of a sudden, I had everything at my fingertips. Auto completion, Auto formatting, snippets, and documentation lookup was all set &lt;strong&gt;By Default&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;PROS:&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Everything is pre-configured:&lt;/strong&gt; You don’t have to worry about setting up tags, or YouCompleteMe, or NERDTree. I can’t tell you how many times I open a new project, perhaps in a different language, and everything works great out of the box. It's a total sigh of relief.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File watchers are powerful:&lt;/strong&gt; There is a thing in IntelliJ called file watchers. You can set arbitrary tasks to run after you edit/save/type in a file. This can be super powerful when you have more advanced tasks you need to run in your codebase like: sorting imports, running specific eslint rules, or running rubocop after saving a file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything plays nicely with your OS:&lt;/strong&gt;  I can’t tell you how many times I set up Vim and tmux on a new OS, like linux. I’ll run all my dot files, configure anything crazy I missed. Compile the plugins that need compilation. Fix any small issues that happen between Vim/tmux/linux (or Mac, whatever). Then I’m programming, and I have to copy+paste something, and BAM, I lose 4 hours configuring copy and paste correctly between Vim/tmux/&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complicated codebases and languages are handled easily:&lt;/strong&gt; After using IntelliJ (or Webstorm), all my problems with my large Typescript codebase went away. Everything gets indexed, parsed, highlighted, and rendered correctly. There are very few hangups when working with complicated codebases, and the exceptions are usually much easier to deal with than with Vim.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;CONS:&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complicated Interface:&lt;/strong&gt; The interface to IntelliJ is somewhat, complicated. There are a lot of tools, dropdown menus, and configurations. This is definitely a daunting thing at first, but once you get used to it, its not a big problem at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Usage:&lt;/strong&gt; IntelliJ does eat up a bit of RAM on my machines. I believe this memory usage is worth it though. You're paying an upfront price to index, tag, and cache programs, so your editing experience is smooth as butter. VSCode and Vim (with plugins) only do this lazily; therefore, your programming experience with complicated projects becomes very awkward and slow over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficult configuration for some tasks:&lt;/strong&gt; Again, this is something you get used to with time. Some configuration options with IntelliJ are less than ideal. For example, when setting up auto-formatting for eslint, you have to go through a couple hoops to set up a file watcher in IntelliJ. Once you understand this concept though, its fairly straightforward and easy to use with other tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, I have decided that the pros for IntelliJ greatly outweigh the cons. I have been really happy with the platform and look forward to using it in the future.&lt;/p&gt;

&lt;p&gt;If you've made it this far, congrats, you're really amazing. I wonder; though, are you feeling the same way I was? I am not sure I'm alone in this thinking or not. I'm sure in 6 months you can find a post about how I switched back to Vim.  But for now, I'm really enjoying this experience.&lt;/p&gt;

&lt;p&gt;Either way, thanks, signing off for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  :wq
&lt;/h2&gt;

</description>
      <category>vim</category>
      <category>intellij</category>
      <category>ruby</category>
      <category>programming</category>
    </item>
    <item>
      <title>Remembering that I once was a terrible programmer</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Thu, 03 Oct 2019 13:46:44 +0000</pubDate>
      <link>https://dev.to/cpow/remembering-that-i-once-was-a-terrible-programmer-4np6</link>
      <guid>https://dev.to/cpow/remembering-that-i-once-was-a-terrible-programmer-4np6</guid>
      <description>&lt;p&gt;I think it's pretty fair to say that as we grow older and wiser, we tend to forget where we came from. And I think this recency-bias shows up frequently in software engineering as well. In software engineering, you tend to build on layers of layers of skills that you accumulate over a long period of time. After being a programmer for, lets say 10 years, you may forget how much you struggled early in the process. As you learn more advanced techniques, you tend to forget how much you may have struggled with even the simplest of programming concepts. I even find myself losing patience with new engineers from time to time, wondering how they couldn't understand some basic techniques that I take for granted every day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking our Hubris
&lt;/h3&gt;

&lt;p&gt;I think this is a form of hubris -- excessive pride or self-confidence. As we grow more experienced in a given field, we gain more and more hubris. We tend to forget from where we started, and feel more confident in the skills we've acquired. Some of you may think &lt;em&gt;"But Chris! I struggle with imposter syndrome all the time!"&lt;/em&gt;, and I agree with you (I do as well). Software Engineering is a very humbling field where one cannot literally learn enough. However, if you had 10+ years of experience and I asked you to write out a simple "fizzbuzz" problem on a whiteboard, you'd scoff at it wouldn't you? (you know you would).&lt;/p&gt;

&lt;p&gt;The point I'm trying to make is that some people are just starting out in their programming careers. And they may struggle with basic algorithms, fizzbuzz-type challenges, and -- as we'll see in a moment -- writing a simple controller in Ruby on Rails. I think its easy to forget where you came from, because I know I have.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;if you had 10+ years of experience and I asked you to write out a simple "fizzbuzz" problem on a whiteboard, you'd scoff at it wouldn't you? (you know you would).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  On to REAPP -- a little diddy from 10 years ago
&lt;/h3&gt;

&lt;p&gt;Recently, I received a &lt;a href="https://help.github.com/en/articles/about-security-alerts-for-vulnerable-dependencies"&gt;security vulnerability alert&lt;/a&gt; for one of my private repositories in github. I recognized the name of the project, but I don't remember the last time I opened the codebase. The vulnerability alert came from a repository called "REAPP" (it stood for real estate application).&lt;/p&gt;

&lt;p&gt;This application was my first attempt to build a Property Management platform -- a SaaS product that allows landlords/property managers to accept rent, manage tenants, and more. I have since re-written this application many times over the last 10 years or so, but this was it's first iteration. Clicking around in the codebase, I remembered how hard I worked to get this code to do much of anything. Specifically, I can remember a "properties" controller I wrote, and the struggle I had with it. I am pretty sure this controller made me abandon the project, because it was just too hard of a problem to solve.&lt;/p&gt;

&lt;h3&gt;
  
  
  The controller that ended this project
&lt;/h3&gt;

&lt;p&gt;Let me just paste the code below, for you all to enjoy. Continue below the fold for my analysis and thoughts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="c1"&gt;# GET /properties/1&lt;/span&gt;
  &lt;span class="c1"&gt;# GET /properties/1.xml&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;#Handing tenants&lt;/span&gt;
    &lt;span class="vi"&gt;@tenants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tenants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@tenants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:success&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Weclome to LivingRoom!&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;
                             We noticed you don't have any tenants set up, please find the 'tenants' box on the right hand side of your screen
                             and be sure to add your first tenant before anything else.
                             Thanks!"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;#handling incomes, and the income chart series data&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:income_page&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@incomes_all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:income_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@income_chart_monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;incomes_chart_series_monthly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@incomes_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;#handinling expenses and the expenses chart series data&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses_all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expenses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expenses&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expenses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:expense_page&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@expenses_all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:expense_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@expense_chart_monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expenses_chart_series_monthly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@expenses_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@net_series&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;net_series_monthly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


    &lt;span class="c1"&gt;#zillow chart stuff&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_chart_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zillow_chart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="vi"&gt;@valuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zillow_value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zillow_chart&lt;/span&gt;
    &lt;span class="vi"&gt;@rillow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rillow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MY-ZILLOW-CREDS"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@rillow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_search_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                              &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_property_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@zillow_search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"zpid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_property_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@zillow_property_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;

    &lt;span class="vi"&gt;@z_chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@rillow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@zillow_property_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"percent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:width&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:height&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:chart_duration&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"5years"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                  
    &lt;span class="vi"&gt;@result_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@z_chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@result_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zillow_value&lt;/span&gt;
    &lt;span class="vi"&gt;@rillow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rillow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MY-ZILLOW-CREDS"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@rillow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_search_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                              &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@zillow_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@zillow_search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_hash&lt;/span&gt;
    &lt;span class="vi"&gt;@value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@zillow_search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"valuationRange"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@value&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# GET /properties/1&lt;/span&gt;
  &lt;span class="c1"&gt;# GET /properties/1.xml&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_expenses&lt;/span&gt;
    &lt;span class="vi"&gt;@property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@tenants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tenants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expenses&lt;/span&gt;
    &lt;span class="vi"&gt;@expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@expenses_full&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@expense_chart_monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expenses_chart_series_monthly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@expenses_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="c1"&gt;# GET /properties/1&lt;/span&gt;
  &lt;span class="c1"&gt;# GET /properties/1.xml&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_incomes&lt;/span&gt;
    &lt;span class="vi"&gt;@property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@tenants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tenants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@incomes_full&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@income_chart_monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;incomes_chart_series_monthly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@incomes_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# GET /properties/1&lt;/span&gt;
  &lt;span class="c1"&gt;# GET /properties/1.xml&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_tenants&lt;/span&gt;
    &lt;span class="vi"&gt;@property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="c1"&gt;# show.html.erb&lt;/span&gt;
      &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xml&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:xml&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Let's talk about instance variables
&lt;/h3&gt;

&lt;p&gt;I don't think I knew what the difference was between an instance variable and a regular variable. I definitely felt the need to make &lt;em&gt;almost everything&lt;/em&gt; an instance variable, regardless of my understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  How many variables are needed for income?
&lt;/h3&gt;

&lt;p&gt;I pasted the &lt;code&gt;show&lt;/code&gt; action in this controller for a reason -- its god&lt;br&gt;
awful. I'm not sure why I needed 5 different instance variables to represent incomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="vi"&gt;@incomes_all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:income_page&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;:per_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@incomes_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@incomes_all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:income_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@income_chart_monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;incomes_chart_series_monthly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@incomes_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I &lt;em&gt;most likely&lt;/em&gt; just wanted to use the paginated incomes for a property, not &lt;em&gt;all&lt;/em&gt; of the incomes. From there, I could have pulled the chart data, and had it update when a user changed the query param for page. That would have been nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a service object?
&lt;/h3&gt;

&lt;p&gt;I like this part of the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="vi"&gt;@zillow_chart_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zillow_chart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="vi"&gt;@valuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zillow_value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;First of all, these names make no sense. Why would I make a method named&lt;br&gt;
&lt;code&gt;zillow_chart&lt;/code&gt; that returns a url? These, and other methods, should have been placed in their own service objects to encapsulate different behaviors and concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion. We were all like this, once
&lt;/h3&gt;

&lt;p&gt;This was a fun trip down memory lane. But I think some lessons can be learned here. Whenever I find myself frustrated reviewing a junior developer's code, I should remember that I once wrote an insane controller with 40+ instance variables, no separation of concerns, and -- oh yeah -- INLINE API CREDENTIALS. I am also pretty sure this was completely untested. Actually let me check ... yup, no tests whatsoever. 😊&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
      <category>life</category>
      <category>inspiration</category>
    </item>
    <item>
      <title>Global Snackbars in React with Redux, and Material UI</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Thu, 12 Sep 2019 16:36:40 +0000</pubDate>
      <link>https://dev.to/cpow/global-snackbars-in-react-with-redux-and-material-ui-3pej</link>
      <guid>https://dev.to/cpow/global-snackbars-in-react-with-redux-and-material-ui-3pej</guid>
      <description>&lt;p&gt;I'm working on a side project in React with Redux and Material UI. I love how snackbars look, and I wanted to create an easy system for displaying them throughout my application. If you've ever worked in Rails, picture the way you display &lt;code&gt;flash&lt;/code&gt; messages. I wanted something simple like that amazing Rails feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  About Snackbars
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://material.io/components/snackbars/"&gt;Material Design Guidelines&lt;/a&gt;: "Snackbars provide brief messages about app processes at the bottom of the screen".&lt;/p&gt;

&lt;p&gt;The ☝ ️guidelines also specify the way you're supposed to implement snackbars: "Snackbars inform users of a process that an app has performed or will perform. They appear temporarily, towards the bottom of the screen. They shouldn’t interrupt the user experience, and they don’t require user input to disappear". This is the perfect use case to show a user when they completed a successful action on your application!&lt;/p&gt;

&lt;h3&gt;
  
  
  Snackbars in Material UI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://material-ui.com"&gt;Material UI&lt;/a&gt; is a component framework for React based on Material IO guidelines. They have an &lt;a href="https://material-ui.com/components/snackbars/"&gt;excellent snackbar component&lt;/a&gt; that we can leverage to show snackbar notifications to our users.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem in my application
&lt;/h3&gt;

&lt;p&gt;I wanted to use snackbars all over my application. There are many places where a user can take an action that results in a snackbar appearing. I didn't want to instantiate a new Snackbar component for every single component that requires one to be displayed. Here is a quick example of one snackbar in my app:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Redux!
&lt;/h3&gt;

&lt;p&gt;Thankfully I am using React, with Redux. And I love using Redux as often as possible to solve my state issues. There is a pattern that I love with redux that I like to call the "redux ui" pattern. Basically I use redux to store at least some of my UI state throughout the app. This makes global UI changes (think users specifying dark mode in their apps) easy to store and propagate throughout your app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lets start by creating our reducers, this will give us the shape of our state for displaying snackbars:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// reducers/uiReducer.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uiReducer&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;action&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;switch&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="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SNACKBAR_SUCCESS&lt;/span&gt;&lt;span class="dl"&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="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="na"&gt;successSnackbarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;successSnackbarMessage&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="nx"&gt;message&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SNACKBAR_CLEAR&lt;/span&gt;&lt;span class="dl"&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="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="na"&gt;successSnackbarOpen&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="na"&gt;errorSnackbarOpen&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="na"&gt;infoSnackbarOpen&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="nl"&gt;default&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;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;uiReducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;☝️ Will give us some structure for displaying a "success" snackbar, along with clearing out all possible snackbars we may want to create (including error and info... just in case).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lets make some action creators to run the reducers.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// actions/snackbarActions.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showSuccessSnackbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&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="nx"&gt;dispatch&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;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SNACKBAR_SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearSnackbar&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;return&lt;/span&gt; &lt;span class="nx"&gt;dispatch&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;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SNACKBAR_CLEAR&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;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a snackbar that uses the &lt;code&gt;clearSnackbar()&lt;/code&gt; function to clear itself&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/SuccessSnackbar.js or whatever you wanna call it&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;useDispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSelector&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="s2"&gt;react-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;Snackbar&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@material-ui/core/Snackbar&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;IconButton&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@material-ui/core/IconButton&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;Icon&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="s2"&gt;@material-ui/core&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;clearSnackbar&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="s2"&gt;../../store/actions/snackbarActions&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SuccessSnackbar&lt;/span&gt;&lt;span class="p"&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;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;successSnackbarMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;successSnackbarOpen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSelector&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;=&amp;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;ui&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleClose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clearSnackbar&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;Snackbar&lt;/span&gt;
      &lt;span class="nx"&gt;anchorOrigin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;horizontal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;successSnackbarOpen&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;autoHideDuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onClose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClose&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;describedby&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client-snackbar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;message&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client-snackbar&lt;/span&gt;&lt;span class="dl"&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;Icon&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;check_circle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Icon&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;successSnackbarMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;action&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;IconButton&lt;/span&gt;
          &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inherit&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;handleClose&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;Icon&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Icon&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;/IconButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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="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;in ☝️ note that we are using the &lt;code&gt;clearSnackbar()&lt;/code&gt; function when the snackbar calls &lt;code&gt;handleClose&lt;/code&gt;. Note, &lt;code&gt;handleClose&lt;/code&gt; is called after the timeout specified, so the snackbar will automatically call this function after a certain amount of time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add this new snackbar component to your &lt;code&gt;App.js&lt;/code&gt; file. This allows you to display a snackbar ANYWHERE in your app using redux:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;SuccessSnackbar&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;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;//App stuff goes in here&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Router&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dispatch the &lt;code&gt;showSuccessSnackbar()&lt;/code&gt; function with your message whenever you want to show a success snackbar.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showSuccessSnackbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Success!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Success! Now you can display a snackbar anywhere in your React app using Redux. You are such a smart developer, aren't you? 😄&lt;/p&gt;

&lt;p&gt;I found this to be a fun and interesting pattern to use when you want to globally display snackbars, or alerts, or anything in your React application. It's straightforward enough for any developer to understand, and its extensible to add anything: alerts, snackbars, messages, etc...&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>redux</category>
    </item>
    <item>
      <title>Scraping Reddit's API in NodeJS with Snoowrap</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Fri, 30 Aug 2019 16:26:14 +0000</pubDate>
      <link>https://dev.to/cpow/scraping-reddit-s-api-in-nodejs-with-snoowrap-2m9n</link>
      <guid>https://dev.to/cpow/scraping-reddit-s-api-in-nodejs-with-snoowrap-2m9n</guid>
      <description>&lt;p&gt;I'm still working on my side-project where I'm gathering information around the web. I'm eventually going to use this information in a weekly aggregate newsletter for Real Estate Investing and Property Management. If you're curious, &lt;a href="https://theweeklydigs.com"&gt;The Newsletter is Here&lt;/a&gt;. For this part of the project, I'm going to scrape some of Reddit's API to find interesting Real Estate and Landlord Posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tooling
&lt;/h3&gt;

&lt;p&gt;There is only one package you need to successfully scrape the reddit API in NodeJS: &lt;a href="https://github.com/not-an-aardvark/snoowrap"&gt;snoowrap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Snoowrap is a "fully featured javascript wrapper for the Reddit API" -- quote taken from the github repo's index page. Snoowrap is really great, and it allows you to query posts, comments, scores, etc...&lt;/p&gt;

&lt;p&gt;All of the responses are wrapped in their own little objects as well, and its all fairly well documented. Also, if you're using an IDE like Webstorm, you can easily auto-complete the functions and classes because of really great type definitions in the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing snoowrap
&lt;/h3&gt;

&lt;p&gt;Install Snoowrap just like any other npm package in NodeJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;snoowrap&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and require it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;snoowrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;snoowrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up Snoowrap
&lt;/h3&gt;

&lt;p&gt;Before making any calls to the Reddit API, you have to go through an initial setup for oauth2 to generate an app, and tokens. This is fairly straightforward, but requires a few steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;go to &lt;a href="https://not-an-aardvark.github.io/reddit-oauth-helper/"&gt;https://not-an-aardvark.github.io/reddit-oauth-helper/&lt;/a&gt; and note the redirect URL you must use when creating your reddit app (the thing you use to call the API). As of this writing, the URL is: &lt;code&gt;https://not-an-aardvark.github.io/reddit-oauth-helper/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;go to &lt;code&gt;https://www.reddit.com/prefs/apps/&lt;/code&gt; and create a new app. It should generally look like this:&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EgP6-km_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dlev7p49d56thtltlmjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EgP6-km_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dlev7p49d56thtltlmjj.png" alt="New Web App on Reddit"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;center&gt;&lt;i&gt;Note the redirect URI&lt;/i&gt;&lt;/center&gt;&lt;/small&gt;&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Next, go back to &lt;a href="https://not-an-aardvark.github.io/reddit-oauth-helper/"&gt;https://not-an-aardvark.github.io/reddit-oauth-helper/&lt;/a&gt;, select the permissions you want, and generate your tokens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, you can configure the snoowrap object in your script.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;snoowrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A random string.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client ID from oauth setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client Secret from oauth setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token from the oauth setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Script for querying RealEstate subreddit
&lt;/h3&gt;

&lt;p&gt;Now that you're all set up with snoowrap (great job, you smart developer you). You can query reddit's API in NodeJS with a script similar to the one below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;snoowrap&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;snoowrap&lt;/span&gt;&lt;span class="dl"&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;scrapeSubreddit&lt;/span&gt;&lt;span class="p"&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;snoowrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A random string.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client ID from oauth setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client Secret from oauth setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token from the oauth setup&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;subreddit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;realEstate&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;topPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;subreddit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;week&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;topPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The ☝️ script above outputs the top 3 posts from Reddit's RealEstate API. Pretty neat right? I thought this was a fun experience, and I really love how Snoowrap works. Now I can use this data to flesh out the newsletter I'm making, again, if your curious, &lt;a href="https://theweeklydigs.com"&gt;you can check it out here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you, have a nice day!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>es6</category>
      <category>reddit</category>
    </item>
    <item>
      <title>Scraping websites with NodeJS</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Wed, 21 Aug 2019 16:29:41 +0000</pubDate>
      <link>https://dev.to/cpow/scraping-websites-with-nodejs-11om</link>
      <guid>https://dev.to/cpow/scraping-websites-with-nodejs-11om</guid>
      <description>&lt;p&gt;I'm currently working on a side project where I want to scrape and store the blog posts on certain pages. For this project I chose to use NodeJS. I have been working more with javascript lately so I figured this would be a fun thing to do with Node instead of Ruby, Python, whatever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tooling
&lt;/h2&gt;

&lt;p&gt;There are two really great tools to use when scraping websites with NodeJs: &lt;a href="https://github.com/axios/axios"&gt;Axios&lt;/a&gt; and &lt;a href="https://github.com/cheeriojs/cheerio"&gt;Cheerio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using these two tools together, we can grab the HTML of a web page, load it into Cheerio (more on this later), and query the elements for the information we need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Axios
&lt;/h3&gt;

&lt;p&gt;Axios is a promise based HTTP client for both the browser, and for NodeJS. This is a well known package that is used in tons and tons of projects. Most of the React and Ember projects I work on use Axios to make API calls.&lt;/p&gt;

&lt;p&gt;We can use axios to get the HTML of a website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;☝️ will give us the HTML of the URL we request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cheerio
&lt;/h3&gt;

&lt;p&gt;Cheerio is the most amazing package I never heard of until now. Essentially, Cheerio gives you jQuery-like queries on the DOM structure of the HTML you load! Its amazing and allows you to do things like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cheerio&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;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h2 class="title"&amp;gt;Hello world&amp;lt;/h2&amp;gt;&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;titleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you're at all familiar with JS development, this should feel very familiar to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The final Script
&lt;/h3&gt;

&lt;p&gt;With Axios and Cheerio, making our NodeJS scraper is dead simple. We call a URL with axios, and load the output HTML into cheerio. Once our HTML is loaded into cheerio, we can query the DOM for whatever information we want!&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;axios&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;axios&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;cheerio&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;cheerio&lt;/span&gt;&lt;span class="dl"&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;scrapeRealtor&lt;/span&gt;&lt;span class="p"&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;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/&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;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.site-main article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;elem&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img.wp-post-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2.entry-title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p.hide_xxs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2.entry-title a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;h3&gt;
  
  
  The output
&lt;/h3&gt;

&lt;p&gt;We now have our scrapped information!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rdcnewsadvice.wpengine.com/wp-content/uploads/2019/08/iStock-172488314-832x468.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One-Third of Mortgage Borrowers Are Missing This Opportunity to Save $2,000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Consumer advocates have an important recommendation for first-time buyers to take advantage of an opportunity to save on housing costs.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/one-third-of-mortgage-borrowers-are-missing-this-opportunity-to-save-2000/&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;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rdcnewsadvice.wpengine.com/wp-content/uploads/2019/08/iStock-165493611-832x468.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trump Administration Reducing the Size of Loans People Can Get Through FHA Cash-Out Refinancing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cash-out refinances have grown in popularity in recent years in tandem with ballooning home values across much of the country.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/trump-administration-reducing-the-size-of-loans-people-can-get-through-fha-cash-out-refinancing/&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;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rdcnewsadvice.wpengine.com/wp-content/uploads/2019/08/GettyImages-450777069-832x468.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mortgage Rates Steady as Fed Weighs Further Cuts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mortgage rates stayed steady a day after the Federal Reserve made its first interest-rate reduction in a decade, and as it considers more.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/mortgage-rates-steady-as-fed-weighs-further-cuts/&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;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rdcnewsadvice.wpengine.com/wp-content/uploads/2019/07/GettyImages-474822391-832x468.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mortgage Rates Were Falling Before Fed Signaled Rate Cut&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The Federal Reserve is prepared to cut interest rates this week for the first time since 2008, but the biggest source of debt for U.S. consumers—mortgages—has been getting cheaper since late last year.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.realtor.com/news/real-estate-news/mortgage-rates-were-falling-before-fed-signaled-rate-cut/&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;



</description>
      <category>node</category>
      <category>javascript</category>
      <category>scraping</category>
    </item>
    <item>
      <title>Please don't forget your history (in Git)</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Wed, 07 Aug 2019 15:45:13 +0000</pubDate>
      <link>https://dev.to/cpow/please-don-t-forget-your-history-in-git-395g</link>
      <guid>https://dev.to/cpow/please-don-t-forget-your-history-in-git-395g</guid>
      <description>&lt;p&gt;When you work on a new feature, or bug fix, and your code is approved; what do you do before merging it into master? Do you squash all commits, write the most beautiful commit message, and merge everything in? Or do you leave the history -- warts and all -- in place when merging?&lt;/p&gt;

&lt;p&gt;I would hope you do the latter -- leaving every grisly commit for the world to see your process. A lot of people don't necessarily agree with this viewpoint; saying you must squash all commits, re-write your history to nice small chunks, and only merge the most beautiful git commits possible. I think that is kind of dumb for a few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You should never be breaking your build, so why re-write history?&lt;/li&gt;
&lt;li&gt;When someone looks at a commit you made, they need granular detail&lt;/li&gt;
&lt;li&gt;Managing groomed commits is a horrible experience.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets talk about these points in more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. You should never be breaking your build
&lt;/h3&gt;

&lt;p&gt;There was &lt;a href="http://blog.felipe.rs/2017/02/25/id-software-programming-principles/"&gt;a talk I remember seeing a while ago&lt;/a&gt; about ID software and how they developed games so quickly in their heyday. One of the points that stood out to me the best was &lt;em&gt;"Never break the build"&lt;/em&gt;. Whenever ID commits code for other engineers to test, the build always works, and the game is always playable. This kept the development team focused on only creating working features. They never made a whole bunch of WIP (Work In Progress) commits and told themselves they could fix it later; no, the build must always pass and the game must always be playable.&lt;/p&gt;

&lt;p&gt;I took this to heart within my own engineering practices. Whenever I commit something to a code-base, I make sure it always works, the tests pass, and the "build" never breaks. This is the easiest way to stay focused, keep commits light, and keep your features solid. When writing code this way, you should never feel the need to squash commits, re-write commit messages, or any of that crap. Just write solid commits that don't break the current build, and you're well on your way.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. People need granular detail when looking at commit history
&lt;/h3&gt;

&lt;p&gt;Stop me if you've experienced this scenario: You're debugging some very intricate piece of code, and its a very dense and hard-to-understand authentication logic. You check the commit history to get some context and it says something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create shopping cart feature for users to store items before checking out&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;WHAT?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of seeing a commit with some context around this small piece of code, you are hit with a &lt;em&gt;squashed&lt;/em&gt; commit message that summarizes a whole👏entire👏 feature👏. I see this happen way too often at many organizations. There is too much emphasis on squashing your commits to keep the history neat, but I think that is dumb. Instead, imagine seeing this series of messages&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Created new auth function to store information for shopping cart feature&lt;/p&gt;

&lt;p&gt;Fixed small bug with auth function&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we have some context! You can see the actual thought process behind each change in this function you're debugging, and now you have more information to move forward with. You can see that your colleague created a function in the auth module (or whatever) related to this big feature, then later you can see they already tried to fix one bug! Perhaps that created the bug you're currently debugging? It just adds so much to your thought process and you guess much less.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Managing groomed commits is not fun
&lt;/h3&gt;

&lt;p&gt;If your company or organization heavily practices re-writing history, you may have run into merge conflicts with your re-written commits. Lets walk through a scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;you add a new feature and make a commit&lt;/li&gt;
&lt;li&gt;you add a test for the feature, and make a second commit&lt;/li&gt;
&lt;li&gt;someone reviews your code and finds a small issues with the feature&lt;/li&gt;
&lt;li&gt;you create a new commit to fix the bug, interactively rebase against master, and fixup commit #1 with this new work.&lt;/li&gt;
&lt;li&gt;you realize you forgot something, rebase and fixup commit #2 with more tests&lt;/li&gt;
&lt;li&gt;you rebase against master to get up to date and &lt;em&gt;BOOM&lt;/em&gt; you hit a merge conflict.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It turns out that when you fixed up your commits and replayed the rewritten history of your branch, you have merge conflicts. This is a common problem I've seen many times. It's really hard to manager larger features and/or branches when you're continually fixing up commits with more work. Eventually you'll hit a merge conflict, and typically will create a whole new branch just to make everything better again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion: Don't forget your history
&lt;/h3&gt;

&lt;p&gt;I think in general, it should be best practice to leave your commit history in place when writing code. This will leave your colleagues with better documented commit messages, more context when dealing with bugs, and overall will be easier to work with.&lt;/p&gt;

&lt;p&gt;Now if you'll excuse me, I have to commit this post without squashing any history!&lt;/p&gt;

</description>
      <category>git</category>
      <category>development</category>
    </item>
    <item>
      <title>Creating a SaaS product quickly with Rails an React</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Tue, 30 Jul 2019 13:09:47 +0000</pubDate>
      <link>https://dev.to/cpow/creating-a-saas-product-quickly-with-rails-an-react-392k</link>
      <guid>https://dev.to/cpow/creating-a-saas-product-quickly-with-rails-an-react-392k</guid>
      <description>&lt;center&gt;&lt;h3&gt;LivingRoom up and running!&lt;/h3&gt;&lt;/center&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8paQ3cqt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mvajcn2phbagcxsncmqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8paQ3cqt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mvajcn2phbagcxsncmqf.png" alt="LivingRoom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Client"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://livingroomhq.com"&gt;Living Room&lt;/a&gt; Is a company (started by Browntree Labs) that creates software for property managers. LivingRoom wanted to build a beautiful application that empowers both tenants and property managers with tools to make managing apartments easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I started LivingRoom myself, as both a personal project and a professional endeavor. I love making products, and I love working on things that interest me. In a very limited amount of time, I wanted to build a product that allowed landlords to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collect rent&lt;/li&gt;
&lt;li&gt;handle issues reported by tenants&lt;/li&gt;
&lt;li&gt;manage leases&lt;/li&gt;
&lt;li&gt;manage their properties, apartments, and tenants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and allows tenants to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pay rent online&lt;/li&gt;
&lt;li&gt;chat with landlords/property managers&lt;/li&gt;
&lt;li&gt;allow &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Since we wanted to build a fully featured product within a very limited time-frame, we chose a set of very well known frameworks and libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby on Rails for rendering and API&lt;/li&gt;
&lt;li&gt;React for more user intensive sections&lt;/li&gt;
&lt;li&gt;Stripe for payment processing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ruby on Rails for rendering and basic pages
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt; is one of the most recognizable frameworks for building web applications. At Browntree Labs, most of our projects involve Ruby on Rails, so we're extremely familiar with the framework inside and out. Because the framework is so popular, every typical problem already has a really well thought out solution in the open source community.&lt;/p&gt;

&lt;p&gt;A great example of a typical problem with a solution is a gem called &lt;a href="https://github.com/plataformatec/devise"&gt;Devise&lt;/a&gt;. Devise is a Ruby on Rails gem that adds authentication, and authorization logic to your application. By using Devise, we literally had authentication up and running within minutes. If we used a different framework that didn't have this problem solved by the community, it would have taken hours to handle all the nuances of authentication, not to mention the requirements of authorization (only allowing certain users to access certain information).&lt;/p&gt;

&lt;p&gt;By utilizing Ruby on Rails, we were able to quickly iterate on large sections of this site. For example, pages like the Property Information page took no time at all to make, because there were no API calls, and no complicated set up. It was just a matter of fetching data from the database, and displaying information in HTML and ERB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tLLXi6JA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2rzgtwfn1dvxdeywh3c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tLLXi6JA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2rzgtwfn1dvxdeywh3c5.png" alt="Property Information Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;i&gt;&lt;small&gt;&lt;center&gt;The property information page&lt;/center&gt;&lt;/small&gt;&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using React for more user intensive sections
&lt;/h3&gt;

&lt;p&gt;Although Rails is great for displaying basic data, and creating basic CRUD (Create Read Update Destroy) interfaces, sometimes you need something with a more &lt;em&gt;rich&lt;/em&gt; user experience. For example, when displaying a sortable table, with fuzzy searching capabilities, you don't want to refresh the page whenever someone types in a letter, or sorts a column. For these sections of the site, we used a Javascript library called &lt;a href="https://reactjs.org"&gt;React&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;React, originally and currently being developed by &lt;a href="https://facebook.com"&gt;Facebook&lt;/a&gt;, has been the most popular javascript library and/or framework for years now. Bursting onto the scene in 2013, React quickly gained popularity due to its simple API, its easy to understand patterns, and its powerful approach to adopting reactive programming to javascript applications.&lt;/p&gt;

&lt;p&gt;By utilizing react, we were able to quickly create some amazing looking things in our application. Our data tables throughout the application; for example, were a feature we used react for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7_bZYSz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rrl212sthallktxtdz2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7_bZYSz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rrl212sthallktxtdz2p.png" alt="Unit Data Table"&gt;&lt;/a&gt;&lt;br&gt;
&lt;i&gt;&lt;small&gt;&lt;center&gt;The Units data table&lt;/center&gt;&lt;/small&gt;&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;We used React to render the above table in some HTML generated by Rails. When a user tries to search for a unit, React will make a call to rails to fetch any possible matches for that unit name. With this setup, a user has a really snappy interface to sort and search for information. There are plenty of other examples throughout the app where we used React. Such as: Unit-wide chat between tenant/landlord, Issue creation and management, Tenant and property data tables, and property-wide chat between tenants and landlords! Since this was all done with well known tools, we were able to create everything very quickly and painlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Stripe for payment processing
&lt;/h3&gt;

&lt;p&gt;The world of online payment processing is a bit complicated. There are many solutions to many different problems. But for us, we chose &lt;a href="https://stripe.com"&gt;Stripe&lt;/a&gt;. Stripe has some of the best API documentation around, and its the easiest payment processing technology to work with, because it has packages for any possible language/framework you can think of. By using Stripe, we had an almost plug-and-play solution for processing user payments immediately.&lt;/p&gt;

&lt;p&gt;Our setup for payments was a little odd. We needed to use the &lt;a href="https://stripe.com/connect"&gt;Stripe Connect&lt;/a&gt; platform to enable us to allow third parties to charge their customers (landlords charging tenants), and get paid through our application. There was some custom code involved in setting this up; however, the documentation was excellent, and in generally this was accomplished fairly easily.&lt;/p&gt;

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

&lt;p&gt;From start to finish, we only spent a couple of months on LivingRoom in our part time. But because we stuck to well-known tools, we accomplished a lot within a very limited time-frame. Using well-known tools and patterns are part of what we do at Browntree Labs, and this project was just another example of how well this strategy works.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>rails</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Re-writing DRAFTs Ember app in React</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Wed, 26 Jun 2019 20:43:39 +0000</pubDate>
      <link>https://dev.to/cpow/re-writing-drafts-ember-app-in-react-58e5</link>
      <guid>https://dev.to/cpow/re-writing-drafts-ember-app-in-react-58e5</guid>
      <description>&lt;p&gt;Using React best practices and tools, I helped re-write DRAFT's web application within 9 months. Here is the overview of how that was accomplished.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Client
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://draft.com/" rel="noopener noreferrer"&gt;DRAFT&lt;/a&gt; is a daily fantasy sports company that prides themselves&lt;br&gt;
on unique and fun DFS sports games and formats. They have multiple betting games for sports ranging from Football to Golf. In 2017 they were acquired&lt;br&gt;
by Paddy Power Betfair, and have been operating as DRAFT (by Fanduel) since 2018. DRAFT mostly focuses on their iOS and Android mobile apps, but they also have a very extensive Web Application that compliments the mobile apps, originally built in Ember.&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;In early 2018 due to the lack of interest in the engineering team, and a lack of available talent, and a new investment from PPB, DRAFT began the process of re-writing their Ember app in React. DRAFT has a robust Ruby on Rails API that serves the mobile applications,&lt;br&gt;
and the web application; however, adoption of new API calls was dwindling due to the lack of Ember work the team was putting out. The&lt;br&gt;
front-end client (the Ember web application) was missing out on new functionality and new game formats.&lt;/p&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;Browntree Labs was originally hired to work on the Ruby on Rails API in anticipation of the upcoming football season. After 3 months on the API team;&lt;br&gt;
however, it became clear that the new React team needed help pushing the re-write project over the finish line. In a very short amount of time,&lt;br&gt;
yours truly was working on a very large and complicated React application, implementing features as quickly as possible. the team and I used a few main&lt;br&gt;
concepts to build features quickly and maintain great code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Atomic Directory Structure&lt;/li&gt;
&lt;li&gt;Redux for State Management&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  React
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; is an amazing javascript component library written by facebook. React makes it very easy to write&lt;br&gt;
reusable components for your project.&lt;/p&gt;

&lt;p&gt;DRAFT has a lot of re-use in their web application. Imagine the concept of a&lt;br&gt;
'player card'. A 'Player' could be a professional athlete who plays in the NHL,&lt;br&gt;
the NBA, the MLB, or the PGA. In the re-write, we created the concept of a&lt;br&gt;
'player card' that encapsulates all of these scenarios, and we were able to&lt;br&gt;
share it throughout the whole application. By creating solid components in React&lt;br&gt;
and re-using them in different places, we quickly implemented many features of&lt;br&gt;
the previous web application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Atomic Structure
&lt;/h3&gt;

&lt;p&gt;For this project, the team and I used the &lt;a href="https://medium.com/@janelle.wg/atomic-design-pattern-how-to-structure-your-react-application-2bb4d9ca5f97" rel="noopener noreferrer"&gt;Atomic Design&lt;br&gt;
Pattern&lt;/a&gt;&lt;br&gt;
to structure the application. If you're unfamiliar with this pattern, it looks&lt;br&gt;
something like this:&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="nx"&gt;atoms&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;molecules&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;organisms&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;templates&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Atomic Structure pattern works very well when applied to a React Project. In&lt;br&gt;
React, you create everything in terms of components. And with an Atomic&lt;br&gt;
Structure, you can think of differently sized components fitting neatly into the&lt;br&gt;
various parts of the structure.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9qrnc8fg4v9isqevvkx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9qrnc8fg4v9isqevvkx1.png" alt="Player Card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;The player card -- an organism&lt;/small&gt;&lt;/center&gt;



&lt;p&gt;Imagine our Player Card from above. There are actually many parts to this&lt;br&gt;
component. We have some components that span multiple levels of our Atomic&lt;br&gt;
Structure in our Player Card alone! We have a player avatar, that stems from an&lt;br&gt;
&lt;code&gt;avatar&lt;/code&gt; &lt;em&gt;atom&lt;/em&gt; component. We have a table for stats, that comes from a&lt;br&gt;
&lt;code&gt;statList&lt;/code&gt; &lt;em&gt;molecule&lt;/em&gt; component. And we put everything together in the&lt;br&gt;
&lt;code&gt;playerCard&lt;/code&gt; which happens to be an &lt;em&gt;organism&lt;/em&gt; component -- a component that&lt;br&gt;
combines molecules and atoms to form a more complex piece of UI.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0y8eptlpovr7ou9qu2zj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0y8eptlpovr7ou9qu2zj.png" alt="Player List"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;the player list -- a template&lt;/small&gt;&lt;/center&gt;



&lt;p&gt;The &lt;code&gt;playerCard&lt;/code&gt; component may be grouped up into a list of players. This list&lt;br&gt;
could be considered a &lt;em&gt;template&lt;/em&gt;. And you can combine these templates together&lt;br&gt;
to form a page. In our image above, we can see the Page that displays the&lt;br&gt;
winnings and scores from past contests in DRAFT.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn8gbqb9jxedor1b068w8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn8gbqb9jxedor1b068w8.png" alt="Results Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;the results page -- a page&lt;/small&gt;&lt;/center&gt;



&lt;p&gt;Using a set structure helped us come up with UI components very quickly, as we&lt;br&gt;
had a good mental picture of how things should fit together through code. We&lt;br&gt;
also were able to quickly re-use large pieces of functionality, because we split&lt;br&gt;
up our code in neatly packaged components, like organisms and templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redux for State Management
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://redux.js.org" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; is an amazing tool for managing state throughout a Javascript application.  Taken from the Redux&lt;br&gt;
website: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Redux is a predictable state container for JavaScript apps. It helps you write&lt;br&gt;
applications that behave consistently, run in different environments (client, server, and native), and&lt;br&gt;
are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Redux works really nicely with React with the &lt;a href="https://react-redux.js.org/" rel="noopener noreferrer"&gt;React-Redux&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Within the DRAFT application, we wanted to maintain the concept of a page needing a specific set of data. This differs from&lt;br&gt;
some other javascript frameworks that use the MVC model -- which says a model holds data. We used reducers -- a core concept of redux -- &lt;br&gt;
to slice our data into relevant chunks to use in a given page on the application. Using data this way allowed us to easily manage&lt;br&gt;
a complex set of data through solid reducer design and re-use.&lt;/p&gt;

&lt;p&gt;A quick example would be the Results page shown above. When we make a request from the API, we get a whole bunch of data. Most of this&lt;br&gt;
data is not necessary for the Results page; however, we can use Redux's reducers to slice the data to only the relevant&lt;br&gt;
pieces we need. Slicing the data is a great way to keep each page lean.&lt;/p&gt;

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

&lt;p&gt;React is an amazing library, and when you couple it with the right tools and concepts, you can very quickly create very complicated&lt;br&gt;
applications. We (the front-end team at DRAFT and I) used a few principles and concepts to very quickly re-create a whole&lt;br&gt;
complex web application in React with a very tight deadline.&lt;/p&gt;

</description>
      <category>react</category>
      <category>ember</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What making a game in Vue.js taught me about Front-end development</title>
      <dc:creator>Chris Power</dc:creator>
      <pubDate>Tue, 04 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/cpow/what-making-a-game-in-vue-js-taught-me-about-front-end-development-54j1</link>
      <guid>https://dev.to/cpow/what-making-a-game-in-vue-js-taught-me-about-front-end-development-54j1</guid>
      <description>&lt;p&gt;This is the most useful thing I have ever done in my career. I have created my&lt;br&gt;
masterpiece, my magnus opus. Beethoven himself would be shaking in his little&lt;br&gt;
boots at the awe of this creation. Dear reader, I've done it, I have created:&lt;br&gt;
"Battleshit".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--frbqCIsV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/12an1wpg6xs6b7pz9ydp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--frbqCIsV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/12an1wpg6xs6b7pz9ydp.png" alt="BattleShit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/cpow/pen/JqVjRL"&gt;https://codepen.io/cpow/pen/JqVjRL&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A disclaimer
&lt;/h2&gt;

&lt;p&gt;I want to make it perfectly clear that I know the code here is terrible. It was&lt;br&gt;
hastily written, it probably has bugs, and it doesn't follow vue.js conventions&lt;br&gt;
all that well, at least, to my understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why write it?
&lt;/h2&gt;

&lt;p&gt;I wanted to learn just a little bit about Vue.js and to prove a little theory&lt;br&gt;
I've had for some time now. That theory goes like this: All javascript&lt;br&gt;
component frameworks operate in mostly the same way. They all have similar&lt;br&gt;
patterns, conventions, and best practices. And they're all very easy to pick up&lt;br&gt;
and start programming with. Our over-engineering of tools, tooling, and the lack&lt;br&gt;
of a large standard lib in Javascript is what makes front-end programming hard.&lt;br&gt;
Having to learn Vue.js, or React, or Ember, or Angular is easy; having to learn&lt;br&gt;
the respective ECOSYSTEM of these frameworks is difficult. In Front-end development,&lt;br&gt;
all the concepts are the same, we just find more and more clever ways to make them&lt;br&gt;
difficult to understand.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In Front-end development, all the concepts are the same, we just find more and more clever ways to make them difficult to&lt;br&gt;
understand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  So, onto BattleShit
&lt;/h2&gt;

&lt;p&gt;This is a really really silly game I made in about an hour, give or take. I just&lt;br&gt;
wanted to learn Vue.js a little bit, and through that, understand our ever evolving&lt;br&gt;
front-end ecosystem just a little bit more. I can safely say that after&lt;br&gt;
Battleshit, I have a greater understanding that we are just re-inventing the wheel&lt;br&gt;
over and over again.&lt;/p&gt;

&lt;p&gt;Anyway, enjoy Battleshit!&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
