<?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: Liz Laffitte</title>
    <description>The latest articles on DEV Community by Liz Laffitte (@lizlaffitte).</description>
    <link>https://dev.to/lizlaffitte</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%2F258135%2F8c74246f-d49d-4bf8-ac3f-a3764d274e22.jpg</url>
      <title>DEV Community: Liz Laffitte</title>
      <link>https://dev.to/lizlaffitte</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lizlaffitte"/>
    <language>en</language>
    <item>
      <title>WP2AT Part 4: Comparing Datasets</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 14 Apr 2021 01:38:22 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/wp2at-part-4-comparing-datasets-126k</link>
      <guid>https://dev.to/lizlaffitte/wp2at-part-4-comparing-datasets-126k</guid>
      <description>&lt;p&gt;I'm working on a Ruby CLI gem that will automate adding WordPress blog data to AirTable. It uses the WordPress API to collect a blog's title, date published, ID and URL, formats the data, and then uses the AirTable API to create new rows in a specified table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/lizlaffitte/wp2at-part-3-retrieving-all-table-rows-from-airtable-3pd9"&gt;Last time&lt;/a&gt; I discussed my new workflow. Instead of adding all the data received from the WordPress to AirTable every time the sync method was called, the gem will compare the AirTable's ID column to the ids of the WordPress posts, and only add new posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  collect_row_data()
&lt;/h3&gt;

&lt;p&gt;I shared my collect_row_data() method in my last post. This method calls call_at(), and collects the HTTP response data in a usable format for the rest of the gem. &lt;/p&gt;

&lt;p&gt;I've revised it so that it isn't requesting data it doesn't need, speeding up the process. This is accomplished by passing in the fields I want (ID and Last Modified) as query parameters. The responses are also collected into a hash, instead of an array. I made this change so that later, when comparing our two datasets, I can easily get the ID column data by calling .keys on row_data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;collect_row_data&lt;/span&gt;
        &lt;span class="n"&gt;row_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;at_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"fields%5B%5D=ID&amp;amp;fields%5B%5D=Last+Modified"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"records"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;row_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"Last Modified"&lt;/span&gt;&lt;span class="p"&gt;]]}&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;row_data&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sync()
&lt;/h3&gt;

&lt;p&gt;When a user wants to update the WordPress data in AirTable, they call the sync method.&lt;/p&gt;

&lt;p&gt;In its latest form, sync pings WordPress to get the total number of result pages and ensure the user has entered the blog post URL correctly. &lt;/p&gt;

&lt;p&gt;It then gathers the Wordpress data (collect_post_data) and AirTable data (collect_row_data) into two hashes. The datasets are compared by passing arrays of their ids into compare_datasets() and a hash is returned. The all_data hash will always have a key :current, and sometimes have a of :new. If the hash has a key of :new, we filter the WordPress data, keeping only the posts whose ids appear in all_data[:new]. That data is added to AirTable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sync&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ping_wp&lt;/span&gt;
            &lt;span class="n"&gt;post_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collect_post_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collect_row_data&lt;/span&gt;
            &lt;span class="n"&gt;all_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare_datasets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:ids&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;all_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prep_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;keep_if&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;all_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
                &lt;span class="n"&gt;add_to_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vc"&gt;@@at_api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"All data up-to-date"&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"There was an issue. Try correcting your blog's URL"&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;
  
  
  compare_datasets()
&lt;/h3&gt;

&lt;p&gt;This method takes our two arrays of ids as arguments. We get all the new posts by subtracting the AirTable id array from the WordPress id array. We are left with any values that appear &lt;em&gt;only&lt;/em&gt; on the WordPress array. These are the ids of our new posts to be added to AirTable!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compare_datasets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at_arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wp_arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;new_posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wp_arr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;at_arr&lt;/span&gt;
        &lt;span class="n"&gt;post_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:current&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at_arr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
            &lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_posts&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;post_data&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I now have all of the post data in data structures that make them easy to compare and change. The next step will be altering the WordPress API call so that we get the last modified date for each post. We'll compare these to the last modified date of each AirTable row. If WordPress has been modified later than AirTable, we'll update AirTable with the Wordpress info.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>gem</category>
      <category>cli</category>
    </item>
    <item>
      <title>WP2AT Part 3: Retrieving All Table Rows from AirTable</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 07 Apr 2021 02:09:37 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/wp2at-part-3-retrieving-all-table-rows-from-airtable-3pd9</link>
      <guid>https://dev.to/lizlaffitte/wp2at-part-3-retrieving-all-table-rows-from-airtable-3pd9</guid>
      <description>&lt;p&gt;If you've been &lt;a href="https://dev.to/lizlaffitte/wp2at-part-2-working-with-wordpress-airtable-apis-4hj5"&gt;following along&lt;/a&gt; you know I've been working on a Ruby CLI gem that will automate adding WordPress blog data to AirTable. It uses the WordPress API to collect a blog's title, date published, ID and URL, formats the data, and then uses the AirTable API to create new rows in a specified table.&lt;/p&gt;

&lt;p&gt;Last week, I talked about how the gem can only add all the blog post data wholesale to AirTable. It doesn't take into account the rows of data already in the table, meaning we could have duplicate content.&lt;/p&gt;

&lt;p&gt;At first, I considered having the gem's sync() method accept flags, so that the user could specify &lt;em&gt;how&lt;/em&gt; the data was synced: just adding new rows or deleting all the table data first before adding all the WordPress data wholesale. After some deliberation, I decided to scrap that idea. &lt;/p&gt;

&lt;h2&gt;
  
  
  New Plan
&lt;/h2&gt;

&lt;p&gt;Instead of accepting flags for the sync method, the gem should get all the data from WordPress, all the data from AirTable and compare those two sets of data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; just check and see what the highest ID was in AirTable and retrieve any WordPress posts with an ID greater than that ID. Unfortunately, we wouldn't be able to tailor our request for conditional IDs (meaning, we can't ask the WordPress API for only posts with IDs great than 772). So we would have to request all of the posts anyway.&lt;/p&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; just request the WordPress API to only send us the posts published after a certain date. Unfortunately, WordPress authors can change their publish date, meaning we might end up with holes in our data. &lt;/p&gt;

&lt;p&gt;Instead, I want to compare the WordPress post IDs from two whole sets of data. Any IDs that exist in both sets will be updated in AirTable (because maybe we changed a blog post's name to improve the SEO). Any IDs that exist only in the WordPress dataset will be added to AirTable as new rows. Any IDs that only exist in the AirTable dataset will have their rows removed from AirTable (maybe we decided to delete a post that didn't make sense anymore).&lt;/p&gt;

&lt;h2&gt;
  
  
  First Steps
&lt;/h2&gt;

&lt;p&gt;The first step in accomplishing this new workflow is making sure all the data is retrieved from AirTable. AirTable (like WordPress) sends a max of 100 records per request. If there are more records available, the response will have a top level key of offset. To get the next set of data, you send the offset with the new request. &lt;/p&gt;

&lt;h2&gt;
  
  
  How I accomplished it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;collect_row_data&lt;/span&gt;
  &lt;span class="n"&gt;row_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;at_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      
 &lt;span class="n"&gt;row_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"records"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;]}})&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;at_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;row_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided to use a do while loop to call the AirTable API as long as there is an offset value in the response. Every time we get a response back, we create a new hash, with the key being the AirTable row's id and the value being the WP post id. If we don't get a response with a key of offset, we break the loop. This way, we know we will always call the AirTable API at least once. Finally, we flatten our array of arrays and return it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Next, I'll work on a method for comparing our two datasets. Then we'll send each array of IDs ([exist in both], [only in AT], [only in WP]) to methods that will handle updating, deleting and adding AirTable rows.&lt;/p&gt;

</description>
      <category>api</category>
      <category>ruby</category>
      <category>cli</category>
    </item>
    <item>
      <title>WP2AT Part 2: Working with WordPress &amp; AirTable APIs</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 31 Mar 2021 02:13:25 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/wp2at-part-2-working-with-wordpress-airtable-apis-4hj5</link>
      <guid>https://dev.to/lizlaffitte/wp2at-part-2-working-with-wordpress-airtable-apis-4hj5</guid>
      <description>&lt;h3&gt;
  
  
  Correction
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/lizlaffitte/what-i-m-working-on-wp2at-a-ruby-gem-3kh9"&gt;part 1&lt;/a&gt; I implied that wp2at isn't a CLI. That is incorrect. Just because it doesn't continuously run via a while loop until the user exits the app (like my very entertaining &lt;a href="https://github.com/LizLaffitte/hogwarts"&gt;Hogwarts CLI gem&lt;/a&gt;), doesn't mean it's not a CLI. Part of learning and growing in development is sharpening your industry vocab and I work on that every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating execute()
&lt;/h2&gt;

&lt;p&gt;This week I worked on integrating the WordPress and AirTable APIs into wp2at  (aka: the fun stuff).&lt;/p&gt;

&lt;p&gt;This is where we left off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@current_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"userconfig"&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;add_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;username&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"blog"&lt;/span&gt; 
            &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_blog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_blogs&lt;/span&gt; 
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"sync"&lt;/span&gt;
            &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;API&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="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
                &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_blogs&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"That's not an option"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The "sync" when condition is where we are telling the gem to get the WordPress blog data and add it to AirTable. This is what that section of the execute method looks like right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@current_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
       &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"sync"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blog_count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Add a blog by running blog with an argument of the blog name you'd like to add."&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_api&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
                    &lt;span class="n"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blogs&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;API&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="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Add an AirTable API Key by running the command api-key and passing an API key."&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Add a blog to be synced"&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"That's not an option"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;I'm doing a little error handling here with some if statements. First, the method checks to see if there is a blog to retrieve data for, then it checks to see if there is an AirTable API key available. Without those two pieces, the gem can't do the one thing I want it to do. If the gem has all the necessary pieces, it finds the blog object the user wants to grab post data from, instantiates a new API object and passes it that blog object and the current settings. It then calls the API instance method ping. (Normally I would advise against this, but ignore the flags for now...)&lt;/p&gt;

&lt;h2&gt;
  
  
  API Class
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the initialize method in the API class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="vc"&gt;@@wp_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/wp-json/wp/v2/posts?_fields=id,title,date,link&amp;amp;per_page=100&amp;amp;page="&lt;/span&gt;
    &lt;span class="vc"&gt;@@at_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.airtable.com/v0/"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@current_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
        &lt;span class="vi"&gt;@blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;
        &lt;span class="vc"&gt;@@wp_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vc"&gt;@@at_api&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="vi"&gt;@blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base_id&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="n"&gt;replace_space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;First, there are two class variable assignments. The base or domain of the AirTable API route will always be the same, while the subdirectories and query parameters will depend on the base and table values of the Blog object. On the other hand, the subdirectories and query params are constant for the WordPress API endpoint, while the domain will depend on the Blog object's url attribute.&lt;/p&gt;

&lt;p&gt;When the class instantiates the new API object, it uses the Blog object it was passed  to concatenate and prepend to the @@at_api and @@wp_api class variables, respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ping!
&lt;/h2&gt;

&lt;p&gt;Let's look at the method our options class is calling: ping()&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collect_post_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;prep_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;add_to_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vc"&gt;@@at_api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method calls collect_post_data, which gets the WordPress blog post data using the WordPress API. Then it calls prep_data to clean that data before passing it to add_to_at which adds it to AirTable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;collect_post_data&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;resp_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
            &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTParty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vc"&gt;@@wp_api&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;resp_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"x-wp-totalpages"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;resp_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method sends a GET request to the WordPress API and adding that parsed response to resp_array. The WordPress API response will include ["x-wp-totalpages"] in the header. I'm using this to make sure I'm are getting all of the post data. Otherwise we would only get the first page of results, which WordPress limits to a max of 100 results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prep_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rendered"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Date Published"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;post&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"link"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:fields&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;records&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prep_data iterates over the post data it was passed, changing key names to match the table header names in AirTable. It also adds a top-level key of :fields for each post, because that is what AirTable will expect in a post request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_to_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at_route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
            &lt;span class="n"&gt;at_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTParty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at_route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="ss"&gt;:body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:records&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;:headers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Authorization"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Bearer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_api&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;at_response&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating new records, AirTable limits you to creating 10 records in each new request. That's why I use each_slice() here to send a post request with the cleaned post data in slices of 10. &lt;/p&gt;

&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;Right now, the gem can add blog data for any WordPress blog saved in the YAML file. However, multiple calls to sync the data don't account for the rows already in AirTable. Meaning that duplicate blog data is being added. The table header names are also very rigid at the moment. They must match what is hardcoded in the API class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Remember the flags? The next step will be allowing users to indicate whether they want to delete all the current post data and add all the post data again as new rows, or just update the table with missing data. I'd also like to add an option for retitling the AirTable table headers, saving the values alongside the other data in the YAML file. &lt;/p&gt;

</description>
      <category>ruby</category>
      <category>wordpress</category>
      <category>airtable</category>
      <category>api</category>
    </item>
    <item>
      <title>What I’m Working On: WP2AT – A Ruby Gem</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 24 Mar 2021 01:38:46 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/what-i-m-working-on-wp2at-a-ruby-gem-3kh9</link>
      <guid>https://dev.to/lizlaffitte/what-i-m-working-on-wp2at-a-ruby-gem-3kh9</guid>
      <description>&lt;h2&gt;
  
  
  The Scenario: I work for a marketing company and our niche – the thing we’re really, really good at – is telling stories. One of the primary ways we tell our clients’ stories each month are blog posts.
&lt;/h2&gt;

&lt;p&gt;For new clients, it’s easy to remember all of the topics we’ve written about. But some of our clients have been with us for more than 8 years, and others have changed hands from client manager to client manager as the company evolves.&lt;/p&gt;

&lt;p&gt;To help keep track of what topics we’ve written about, we started tracking blogs in AirTable. We keep track of the blog title, date posted, category, URL and Yoast SEO data as well. This makes it really easy for our writers to ensure we haven’t written about a topic, and see what SEO keywords we’ve focused on in the past. &lt;/p&gt;

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

&lt;p&gt;My coworkers and our interns have been inputting this data by hand. By hand. This is an issue for me because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The potential for human error during the data entry process&lt;/li&gt;
&lt;li&gt;The tables are never up to date because we always forget to update them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BY HAND?!&lt;/strong&gt; This simple task takes up too much time, which is to say, it takes more than a minute. We’ve all got better things to do!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Hard Way:
&lt;/h2&gt;

&lt;p&gt;My initial solution was to export all the WordPress data monthly from phpMyAdmin and then paste it all into AirTable. This took less time, but it still require that I access one client’s DB at a time, export the data and then find the correct table in AirTable and import it. This was still taking too much time, and the blogs were never posted at the same time, so I ended up doing this task the hard way several times a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Better Way:
&lt;/h2&gt;

&lt;p&gt;I decided that I would build a program that uses the AirTable and WordPress APIs to gather the blog data and import it into AirTable, with a simple command in the terminal. &lt;/p&gt;

&lt;h2&gt;
  
  
  Done is Better than Perfect
&lt;/h2&gt;

&lt;p&gt;Because I am on a React kick, initially I thought I would build this project as a Rails API backend with a React frontend. After some more planning, I realized that I didn’t need a GUI, I didn’t want to deal with creating users and storing passwords, and I wouldn’t want to worry about the security implications of storing AirTable API keys (which are required to make this work) or sending them in HTTP requests.  &lt;/p&gt;

&lt;p&gt;I decided that the program should store data on the user’s computer in a YAML file. &lt;/p&gt;

&lt;p&gt;My next thought was to build a full-fledged CLI application. I very quickly got bogged down in all the puts statements and walking the user through the program loops. &lt;/p&gt;

&lt;p&gt;I decided that I didn’t need a full app that continued to run until the user exited the program. This project needs to go from WIP to portfolio project, so I shifted gears.&lt;/p&gt;

&lt;p&gt;I want the user (i.e. me) to be able to run specific commands in the terminal that accomplished different tasks. Think using Git in the command line. &lt;code&gt;$ git config --global user.name "John Doe"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I want to run a command like &lt;code&gt;wp2at sync&lt;/code&gt; and have all the AirTable tables be updated with the latest WordPress blog info. I want to run &lt;code&gt;wp2at blogs&lt;/code&gt; and get a list of all the blogs I’ve configured. Ruby gem with executables it is!&lt;/p&gt;

&lt;p&gt;I started with a simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle gem wp2at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my bin directory, that was created for me with the previous command, I created a file wp2at. In this file, I use the shebang line to tell the computer that this code is in Ruby, require a Ruby file that will serve as the entry point to all the other code in the gem, and include an executable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env ruby

require_relative '../lib/wp2at'
Wp2at::Option.new.execute(ARGV)

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

&lt;/div&gt;



&lt;p&gt;Now, when I run &lt;code&gt;./bin/wp2at&lt;/code&gt; in the terminal, &lt;code&gt;Wp2at::Option.new.exevute(ARGV)&lt;/code&gt; is called. &lt;/p&gt;

&lt;p&gt;In my Option class, I have an instance method &lt;em&gt;execute&lt;/em&gt; that takes the command line arguments. In &lt;em&gt;execute&lt;/em&gt;, I use a (case statement)[ &lt;a href="https://www.rubyguides.com/2015/10/ruby-case/"&gt;https://www.rubyguides.com/2015/10/ruby-case/&lt;/a&gt;] to run different methods based on the arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@current_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"userconfig"&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;add_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;username&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"blog"&lt;/span&gt; 
            &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_blog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_blogs&lt;/span&gt; 
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"sync"&lt;/span&gt;
            &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;API&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="vi"&gt;@current_settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
                &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_blogs&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"That's not an option"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;Right now, the program loads or creates a Settings instance, based on the presence or absence of a YAML file. It can save a username and blogs to said YAML file. I’ve started to incorporate the WordPress API. The program can now GET blog data based on the blog info added to the Settings instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Next, I’ll concentrate on formatting the returned blog data so that it can be easily sent to AirTable. &lt;br&gt;
I hope you’ll stick around and check back to see how this one progresses!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>cli</category>
      <category>wip</category>
    </item>
    <item>
      <title>“It used to work!” Reviewing this Week's Web Dev Problem Solved</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 17 Mar 2021 02:03:30 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/it-used-to-work-reviewing-this-week-s-web-dev-problem-solved-2be4</link>
      <guid>https://dev.to/lizlaffitte/it-used-to-work-reviewing-this-week-s-web-dev-problem-solved-2be4</guid>
      <description>&lt;p&gt;One thing I hear often when working on web development projects is: “It used to work!” This past week I had the opportunity to tackle an “It used to work” project, and this is a review of that project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;p&gt;The Pretty Puppy Association (PPA)* has a website for their members and other puppy enthusiasts. It is built in Expression Engine and they are using the Low Reorder add-on (among others) and jQuery.&lt;/p&gt;

&lt;p&gt;One of their channels is State Puppies. It has 51 entries, each one featuring an official state puppy for each state in the U.S. (and one for Puerto Rico). Each entry has a photo, a breed name, and basic information about the puppy’s breed.&lt;/p&gt;

&lt;p&gt;*This is entirely made up, but ultimately the subject matter of the website doesn’t matter for this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Used to Work Feature
&lt;/h2&gt;

&lt;p&gt;On the PPA website homepage, there is a Spotlight on Spot section where the image and breed name of the featured puppy is displayed. Here users can select their state’s official puppy from a dropdown menu. When they make a selection, the photo and breed name in the spotlight section change to match their selection. When users visit the website in the future, their last-selected puppy should be seen in the spotlight feature. &lt;/p&gt;

&lt;h3&gt;
  
  
  The code
&lt;/h3&gt;

&lt;p&gt;In the homepage template, we have the spotlight section itself, including a form with only a select element. You'll notice everywhere there would normally be puppy-specific info (breed, image URL) there is...nothing. That information is being retrieved and placed in the DOM via JavaScript.&lt;/p&gt;

&lt;p&gt;The select element itself is being populated via Expression Engine, with the help of the Low Reorder add-on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-4 puppysection"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-12 spotlighthdr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fa fa-chevron-circle-right"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; Spotlight On Spot&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"homefeatured"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"puppylink"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"URL"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"divFeaturedPuppyBreed"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"featuredbreed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Breed&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"aFeaturedPuppy"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"URL"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imgFeaturedPuppy"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"IMG"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"img-responsive puppy-photo"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"BREED"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"326"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"336"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                {exp:channel:entries channel="new_home_page" dynamic="off" limit="1"}
                    {spotlight_text}
                {/exp:channel:entries}
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"connectform"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-8 nopadding"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Pick a state to see that state's official state puppy and get a link to read more."&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"drpPuppies"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hiddenLabel2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pick a state to see that state's official state puppy and get a link to read more.&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"drpPuppies"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"puppylist"&lt;/span&gt; &lt;span class="na"&gt;form=&lt;/span&gt;&lt;span class="s"&gt;"puppyform"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                {exp:low_reorder:entries  set="puppies" dynamic="no" disable="member_data|category_fields"}
                                    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{entry_id}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{state}&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                                {/exp:low_reorder:entries}
                            &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"clear:both;padding-top:20px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"aFeaturedPuppyLink"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"URL"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Read more about the &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sbreed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have some inline JavaScript in the homepage template. There is a change event listener set on the state select. When a change event is triggered, JSON data is retrieved from "/site/puppy-info/VALUE_OF_SELECTION". If we have a successful GET request, that information is used to populate our spotlight section with the selected puppy's info.&lt;/p&gt;

&lt;p&gt;At the very bottom of the script, we see &lt;code&gt;$('#drpPuppies').change();&lt;/code&gt;. This line of code is triggering the change event, without a user actually making a selection. This will become important later one.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;jqExt&lt;/span&gt;&lt;span class="p"&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;#drpPuppies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&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="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/site/puppy-info/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&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;status&lt;/span&gt; &lt;span class="o"&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;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;#divFeaturedPuppyName&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;.sname&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;.puppylink&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#aFeaturedPuppy&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#aFeaturedPuppyLink&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#imgFeaturedPuppy&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="s2"&gt;alt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;#imgFeaturedPuppy&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="s2"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_width&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;#imgFeaturedPuppy&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="s2"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_height&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;#imgFeaturedPuppy&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="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&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;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#drpPuppies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;change&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;Let's look at '/site/puppy-info/'&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;            {exp:low_reorder:entries set="puppies" entry_id="{segment_3}" }
    {exp:ce_img:pair src="{puppy_picture}" width="300" height="340" crop="yes|center,top|0,0|yes" allow_scale_larger="yes"}
    &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
        &lt;span class="nb"&gt;setcookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"exp_custom_default-puppy-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;365&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{entry_id}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'breed'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{breed}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'image_url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{made}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'image_width'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;'image_height'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;'puppy_url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{url_title_path="puppies/bio"}'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
    {/exp:ce_img:pair}
{/exp:low_reorder:entries}

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

&lt;/div&gt;



&lt;p&gt;If you blink, you might miss the setcookie() function. When users change that select dropdown, a GET request is sent to '/site/puppy-info/VALUE_SELECTED'. That value selected, or entry_id is set as a cookie. &lt;/p&gt;

&lt;p&gt;Conceivably, when the user leaves the homepage and comes back, their last-selected puppy will be showing in the spotlight section, right? That wasn't happening. &lt;/p&gt;

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

&lt;p&gt;Instead of showing a user’s last-selected puppy, the Spotlight on Spot section is defaulting to the first state on the dropdown menu: Alabama – Blackhound. &lt;em&gt;(I also made this up for example purposes. There is no official dog breed of Alabama.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Why? First, the cookie was &lt;em&gt;set&lt;/em&gt;, but that was it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/gIfgb4RSmErc07m4n9/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="100%" src="https://i.giphy.com/media/gIfgb4RSmErc07m4n9/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cookie was never read and nothing was ever done with the value of that cookie. &lt;/p&gt;

&lt;p&gt;Also, triggering the change event led to the featured pup &lt;em&gt;always&lt;/em&gt; being Alabama, because of the way the select element works. The first option in the select element is always &lt;em&gt;selected&lt;/em&gt; be default. So the page loads, the change event is triggered, this.value defaults to Alabama and the cookie value is set to Alabama. And over and over....&lt;/p&gt;

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

&lt;p&gt;Solving the problem involved four main parts: reading the cookie when the window loads; using that value in our GET request; and not triggering the change event automatically.&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;keyValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(^|;) ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&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="s1"&gt;=([^;]*)(;|$)&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="nx"&gt;keyValue&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;keyValue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&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;setPuppy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&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;#divFeaturedPuppyName&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;.sname&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;.puppylink&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#aFeaturedPuppy&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#aFeaturedPuppyLink&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="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bio_url&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;#imgFeaturedPuppy&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="s2"&gt;alt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;#imgFeaturedPuppy&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="s2"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_width&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;#imgFeaturedPuppy&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="s2"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_height&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;#imgFeaturedPuppy&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="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;puppySelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drpPuppies&lt;/span&gt;&lt;span class="dl"&gt;'&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;myPuppy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exp_custom_default-puppy-id&lt;/span&gt;&lt;span class="dl"&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;myPuppy&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;myPuppy&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="nx"&gt;myPuppy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;27&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="nx"&gt;puppySelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myPuppy&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;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/site/puppy-info/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;myPuppy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&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;status&lt;/span&gt; &lt;span class="o"&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;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;setPuppy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;jqExt&lt;/span&gt;&lt;span class="p"&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;#drpPuppies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&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="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/site/puppy-info/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&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;status&lt;/span&gt; &lt;span class="o"&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;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="nx"&gt;setPuppy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we have &lt;code&gt;getCookie()&lt;/code&gt;, a function that will retrieve any cookie, given its name. &lt;/p&gt;

&lt;p&gt;Next I pulled all of the code around setting the puppy-specific info in the spotlight section into a new function &lt;code&gt;setPuppy()&lt;/code&gt; so I can call it in two other functions without repeating myself. It takes the results of the GET request as an argument.&lt;/p&gt;

&lt;p&gt;Up next is a window.onload event where we are checking for the "exp_custom_default-puppy-id" cookie and saving it into the variable myPuppy. If it is not set, we are returning a default value (27 - for Florida!). &lt;/p&gt;

&lt;p&gt;The myPuppy value is used in our GET request. This way, we are sure that every user is greeted by an adorable puppy, whether or not they've visited before or their cookie has expired. We're also using &lt;code&gt;setPuppy()&lt;/code&gt; to set the values of the elements we're referencing using the results of our GET request.&lt;/p&gt;

&lt;p&gt;Last is our trusty &lt;code&gt;jqExt()&lt;/code&gt; function, still with its change event listener intact. This takes care of rendering the correct pup when the user makes a selection. It also calls our setPuppy() function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;It turns out this “It used to work” never worked. It was requested when the website was updated, but this feature never worked in the way it was requested. On a former iteration of the website, the spotlight section would be randomly populated every time the homepage loaded.&lt;/p&gt;

&lt;p&gt;Have you worked on any "it used to work" projects lately?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Converting CSS to SASS in Rails Project</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 10 Mar 2021 03:20:41 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/converting-css-to-sass-in-rails-project-446o</link>
      <guid>https://dev.to/lizlaffitte/converting-css-to-sass-in-rails-project-446o</guid>
      <description>&lt;p&gt;As an exercise to flex and practice my SASS skills, I recently converted the CSS in one of my projects to SASS. Did this project need the conversion? &lt;em&gt;Ehh, probably not.&lt;/em&gt; I'm only using one stylesheet and it's less than 300 lines, but converting a smaller stylsheet is great practice for a larger project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;My YellowBitRoad application is running Rails 6, so the sass-rails gem was shipped with Rails.&lt;/p&gt;

&lt;p&gt;I had dumped all of my CSS into application.css. To start the conversion to SASS, I created a new stylesheet &lt;strong&gt;/app/assets/stylesheets/style.scss&lt;/strong&gt;. All stylesheets included in &lt;strong&gt;app/assets/stylesheets&lt;/strong&gt; will be compiled and used in your application. &lt;/p&gt;

&lt;p&gt;In your Rails application, you'll see comments at the top of application.css explaining how stylesheets are incorporated. You'll also see a suggestion to split up your CSS, and see .scss files created for each model generated with the &lt;code&gt;rails g model&lt;/code&gt; command. For my purposes, it doesn't make sense to split up such a small file, so I'm going to put everything into one stylesheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Color Palette
&lt;/h2&gt;

&lt;p&gt;One of the most convenient things about SASS, to me, is not having to remember, look up, copy and/or paste HEX codes. Also, do you know what color #01BAEF is? Maybe you do, but I have no clue. I love picking out a color palette for my application, I don't like having to remember HEX or RGB codes or what colors they represent.&lt;/p&gt;

&lt;p&gt;My first step in the conversion was saving all my theme colors as variables, and them implementing those variables throughout my stylesheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS: Before&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#04724D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#04724D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bold&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;&lt;strong&gt;SASS: After&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="nv"&gt;$dark_green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#04724D&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt; 
&lt;span class="nv"&gt;$light_green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mh"&gt;#16DB65&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$yellow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#FFD23F&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$red&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#B80c09&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$light_blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mh"&gt;#01BAEF&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

 &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="err"&gt; {&lt;/span&gt;&lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="err"&gt;;}&lt;/span&gt;
 &lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$dark_green&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt; 
   &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
   &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;}&lt;/span&gt;
 &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$dark_green&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt; 
  &lt;span class="nt"&gt;font-weight&lt;/span&gt;&lt;span class="nd"&gt;:bold&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray! That was exceedingly easy, and now I can decide to change my entire color palette if I would like. I can also remember color names a lot easier than HEX codes, so I don't have to keep referencing previous style rules when writing more CSS. &lt;/p&gt;

&lt;p&gt;If there is a good chance you'll change your theme colors entirely, you may want to choose more generic variables names ($main_color, $secondary_color, etc.) so you can change the HEX values and still have variables that make sense for your fellow devs and your future self.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nesting
&lt;/h2&gt;

&lt;p&gt;Next I tackled nesting my separate style rules where I could. For my project, this made a lot of sense for my nav and footer elements.&lt;/p&gt;

&lt;p&gt;Anywhere I had a rule for a child element of nav or footer (really apparent in my CSS as I referenced those parent elements), I converted the separate rules into my two new nested rules. I also used the opportunity to nest some rules where I didn't reference the parent element, but used an ID for their children. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before: CSS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#04724D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;85%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Roboto Slab'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;#branding&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;#nav-items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;85%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After: SASS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;dark_green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;ul&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;85%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="err"&gt;li&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
            &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
            &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Roboto Slab'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;#nav-items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;85%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;#branding&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="err"&gt;}&lt;/span&gt;
 &lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;dark_green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
     &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="err"&gt;input&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Roboto Slab'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="err"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, we've had to repeat some styles here, in the outer footer and nav nested rules, but I'm okay with that. It's minor, and now I can visually see all the style rules that affect these two parent elements and their children.&lt;/p&gt;

&lt;p&gt;Converting the a smaller CSS file to SASS is great practice. Do you use SASS, CSS or both? Have you ever converted a large project from one CSS to SASS? Let me know!&lt;/p&gt;

</description>
      <category>css</category>
      <category>sass</category>
      <category>rails</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Eggasaurus: JavaScript SPA Browser Game</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 03 Mar 2021 02:32:32 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/eggasaurus-javascript-spa-browser-game-2noo</link>
      <guid>https://dev.to/lizlaffitte/eggasaurus-javascript-spa-browser-game-2noo</guid>
      <description>&lt;p&gt;&lt;em&gt;This post will review how I handled gameplay visualization in a JavaScript SPA browser game I built.&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;When working through the &lt;a href="https://flatironschool.com/career-courses/coding-bootcamp" rel="noopener noreferrer"&gt;Software Engineering bootcamp&lt;/a&gt; at Flatiron, I built a JavaScript SPA as a portfolio project. &lt;/p&gt;

&lt;p&gt;I decided to build a game that was simple enough for my (then) three-year-old to play. (&lt;strong&gt;Spoiler alert: he was not impressed.&lt;/strong&gt; ) The resulting game, Eggasaurus, came from a combination of my love of browser games and my son’s fascination with dinosaurs. Users can create dinosaur pets and interact with them. &lt;/p&gt;

&lt;p&gt;Over time, the dinosaurs get hungry, tired and bored. This is shown visually by what I call mood meters. Moods decrease constantly, over time. The player can click the care buttons to "fill" the corresponding mood meter back up. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fva6jxln8msp67pmt8k7k.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fva6jxln8msp67pmt8k7k.PNG" alt="Screenshot of Eggasaurus game"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I accomplished this with a combination of HTML (divs), CSS (background colors, width) and JavaScript (setInterval, event listeners, HTML DOM style property). &lt;/p&gt;
&lt;h2&gt;
  
  
  Mood Meter HTML &amp;amp; CSS
&lt;/h2&gt;

&lt;p&gt;The mood meters are built with divs. Each mood meter (there are three total: hunger, happiness and tiredness) starts with a div with the class &lt;code&gt;meters&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This parent div has a border-radius, border and height to form the actual meter. If this was an old-school thermometer, it would be the outer, glass piece of the thermometer. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73dxkklodo7m2cfob4rr.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73dxkklodo7m2cfob4rr.PNG" alt="Screenshot of abovementioned mood meters"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;div.meters {
  height: 20px;
  border: 4px solid gray;
  border-radius: 30px ;
  margin-bottom:5px !important;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then there is a child div with a class &lt;code&gt;green&lt;/code&gt; and and id according to what mood it represents. It also has a width value that we're retrieving via string interpolation. This div represents the value or level of the mood. Continuing our thermometer example, it is analogous to the mercury level in the thermometer (if that mercury was green). &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lqpyl6sfr7fsjya9e0p.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lqpyl6sfr7fsjya9e0p.PNG" alt="Screenshot of green div mood levels"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="meters"&amp;gt;
  &amp;lt;div id="hunger-meter" class="green" style="width:${this.hungerP}"&amp;gt;
    ...
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSS gives the mood level div the same height as its parent; a left float so it appears to grow and shrink from the left; a background color to visually display the level value; and a border-radius to match our illusion of the meter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  div.meters div.green{
    height: 20px;
    float: left;
    border-radius: 30px; 
    background: green;
  }

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

&lt;/div&gt;



&lt;p&gt;Finally, there is a grandchild and a great-grandchid div. These hold the sprite that matched the mood the meter represents. So, chicken leg for hunger, in this case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="meters"&amp;gt;
  &amp;lt;div id="hunger-meter" class="green" style="width:${this.hungerP}"&amp;gt;
    &amp;lt;div class="sprite-holder"&amp;gt;
     &amp;lt;div class="sprite"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSS gives the sprite holder a background color, keeping the mood level from showing through. It also places it perfectly in our top-level mood meter div; and sets the background position depending on the meter, so the correct icon is displayed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;div.sprite{
    background: url('images/meter_icons.png');
    width: 20px;
    height: 20px;
    background-size: 58px auto;
}
div.sprite-holder{
    width: 25px;
    background-color: #fff;
    float: left;
    position: relative;
    left: -5px;
    top: -5px;
    padding: 5px;
    border-radius: 50%;
}
div#hunger-meter div.sprite{
    background-position: 0px 0px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To handle the interactions with these mood meters, there are corresponding care buttons. Each button has a background image and id that corresponds with the mood meter it will affect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcxdotgfrnvggsms2x9f.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcxdotgfrnvggsms2x9f.PNG" alt="Screenshot of care buttons"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="care-btns"&amp;gt;
  &amp;lt;button id="feed" data-id="${this.id}"&amp;gt;&amp;lt;/button&amp;gt; 
  &amp;lt;button id="play" data-id="${this.id}"&amp;gt;&amp;lt;/button&amp;gt; 
  &amp;lt;button id="nap" data-id="${this.id}"&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mood Meter JS
&lt;/h2&gt;

&lt;p&gt;The JavaScript takes care of changing the width of the mood levels with the use of setInterval, the HTML DOM style property and event listeners.&lt;/p&gt;

&lt;h3&gt;
  
  
  setInterval
&lt;/h3&gt;

&lt;p&gt;First, when the game loads or a dinosaur is selected, &lt;code&gt;moodTimer&lt;/code&gt; is called. This function accepts a Dino object id and uses setInterval to repeatedly call a function &lt;code&gt;decreaseMoods&lt;/code&gt; on the Dino instance with the passed id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function moodTimer(dinoId){
    newMoodAdjust = window.setInterval(() =&amp;gt; {Dino.findDino(dinoId).decreaseMoods()}, 1000)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In newMoodAdjust, decreaseMoods will be called on the Dino instance, every 1000 milliseconds.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.&lt;br&gt;
-- &lt;a href="https://www.w3schools.com/jsref/met_win_setinterval.asp" rel="noopener noreferrer"&gt;W3Schools&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a user logs out or selects a different dinosaur to play with, clearInterval is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clearInterval(newMoodAdjust)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a user just selected a new dino, &lt;code&gt;moodTimer()&lt;/code&gt; is called again, and it is passed the new dino id.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decreasing Moods (HTML DOM style)
&lt;/h2&gt;

&lt;p&gt;The newMoodAdjust calls the function decreaseMoods every second. This function is a Dino class method. It checks to see if a Dino instance's hunger, happiness and tiredness properties are above 0. If so, it calls the corresponding class methods hungry(); bored(); or tired(). &lt;em&gt;These&lt;/em&gt; methods adjust the value of the Dino instance's properties and call another class method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    decreaseMoods(){
        if(this.hunger &amp;gt; 0){
            this.hungry()
        }
        if(this.happiness &amp;gt; 0){
            this.bored()
        }
        if(this.tiredness &amp;gt; 0){
            this.tired()
        }
    }

    hungry(){
            this.hunger -= 0.5
            this.adjustHungerMeter()
    }

    bored() { 
            this.happiness -= 0.5
            this.adjustHappinessMeter()
    }

    tired() { 
            this.tiredness -= 0.5
            this.adjustTirednessMeter()
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that we are displaying the Dino's hunger, happiness and tiredness property values to the player visually by adjusting the width of the green div. Zero is our floor. Once those properties and the width of the green div reaches zero, we don't need to make any more adjustments.&lt;/p&gt;

&lt;p&gt;Our adjust class methods retrieve the appropriate green div and set its width equal to the Dino's hunger, happiness or tiredness property value, using the HTML DOM style property.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;this.hungerP&lt;/code&gt; is a getter that translates the hunger property to a percent string. So if &lt;code&gt;this.hunger // 50&lt;/code&gt; then &lt;code&gt;this.hungerP // "50%"&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    adjustHungerMeter(){
        const hungerMeter = document.getElementById("hunger-meter")
        hungerMeter.style.width = this.hungerP
    }

    adjustHappinessMeter(){
        const happinessMeter = document.getElementById("happiness-meter")
        happinessMeter.style.width = this.happinessP
    }

    adjustTirednessMeter(){
        const napMeter = document.getElementById("tiredness-meter")
        napMeter.style.width = this.tiredP
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Increasing Moods (Event listeners &amp;amp; style)
&lt;/h3&gt;

&lt;p&gt;When the game is loaded and a dino is selected, the SPA calls &lt;code&gt;moodListeners()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This function adds click event listeners to each of our care buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function moodListeners(){
    document.getElementById("care-btns").childNodes.forEach(btn =&amp;gt; {
        btn.addEventListener("click", (e) =&amp;gt; {
            const thisDino = Dino.findDino(e.target.dataset.id)
            if(e.target.id == "play"){
                thisDino.play()
            } else if(e.target.id == "feed"){
                thisDino.feed()
            } else if(e.target.id == "nap"){
                thisDino.nap()
            }
        })
    })

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

&lt;/div&gt;



&lt;p&gt;When the buttons are clicked, different Dino class methods are called: play(); feed(); and nap().&lt;/p&gt;

&lt;p&gt;Unsurprisingly, these methods increase the Dino instance's mood property values (setting them to 100), and call the adjust class methods discussed above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    feed(){
        this.hunger = 100
        this.adjustHungerMeter()
    }
    play(){
        this.happiness = 100
        this.adjustHappinessMeter()
    }
    nap(){    
        this.tiredness = 100
        this.adjustTirednessMeter()
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided to increase the moods to their max level, instead of increasing them by increments, because my son was getting frustrated with all the clicking.&lt;/p&gt;

&lt;p&gt;Have you built any games just for fun? Are you a professional game developer or engineer? What are your favorite browser games? (Mine are CookieClicker and NGU Idle.)&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Web Development Bad Habits to Kick</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 24 Feb 2021 02:11:34 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/web-development-bad-habits-to-kick-1f0n</link>
      <guid>https://dev.to/lizlaffitte/web-development-bad-habits-to-kick-1f0n</guid>
      <description>&lt;p&gt;&lt;a href="https://www.pexels.com/photo/toddler-with-red-adidas-sweat-shirt-783941/"&gt;&lt;em&gt;Cover photo by mohamed abdelghaffar from Pexels&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Not Designing for Mobile First
&lt;/h2&gt;

&lt;p&gt;You know what’s difficult? Taking a complex, intricate design that looks pixel-perfect on a 24 inch monitor and trying to shove it on a 300-pixel-wide screen.&lt;/p&gt;

&lt;p&gt;How do you decide what to cut? How do you ensure the most important info is still above the fold? You go back in time and design the mobile view first. &lt;/p&gt;

&lt;p&gt;If you’re not optimizing your design for mobile, you’re likely serving a subpar experience for a majority of your users. &lt;/p&gt;

&lt;p&gt;Start with a simple prototype that gives users easy access to the most important information on the page. Add design elements as you scale for bigger devices.&lt;/p&gt;

&lt;p&gt;If you’re not a designer, take advantage of tools like &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Not Being Consistent
&lt;/h2&gt;

&lt;p&gt;Consistency helps users know what to expect from your website and helps teach them how to use it. Being inconsistent can lead to frustration, despair and high bounce rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inconsistent Links
&lt;/h3&gt;

&lt;p&gt;You should treat links of the same type the same. If one of your external links opens in a new window, they should all open in a new window. (&lt;strong&gt;Don’t forget to think about users using screen readers.&lt;/strong&gt; Check out these suggestions on making &lt;a href="https://dev.to/cerchie/3-ways-to-make-target-blank-accessible-3lan"&gt;target=”_blank” more accessible&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This includes links to media! Don't indicate a link opens a PDF in one place and neglect to add that info everywhere. &lt;/p&gt;

&lt;h3&gt;
  
  
  Inconsistent Style / Design
&lt;/h3&gt;

&lt;p&gt;While it's more than likely that you'll have multiple page templates and designs, users shouldn't think they've navigated to a new website or caught you in the middle of a theme update when the open a new page. &lt;/p&gt;

&lt;p&gt;You should have your basic theme colors and include a header, navigation and footer where appropriate. (E.g. If you're building a JavaScript SPA browser game, you probably don't need a footer.)&lt;/p&gt;

&lt;p&gt;It can be tempting to use an entire palette of colors and six different Google fonts, but less is...way less confusing. Define your CSS styles for font families, colors and sizes and &lt;strong&gt;use them&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Hardcoding Full Paths
&lt;/h2&gt;

&lt;p&gt;Stop. It. Do you enjoy doing things multiple times? Does it bring you joy to install a plugin that literally only does one thing that you could’ve done yourself? Stop hardcoding full paths!&lt;/p&gt;

&lt;p&gt;I cannot express to you how many times I’ve had someone ask me to troubleshoot a website issue that turned out to be a hardcoded full path for an image source attribute or external stylesheet. &lt;/p&gt;

&lt;p&gt;Clients change their minds about domain names, production and development domains differ. Using partial paths gives you the freedom to make changes and push to production without having to go back and look for references to a previous domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Not Using Image Alt Tags
&lt;/h2&gt;

&lt;p&gt;Seriously, it’s 2021. Adding alt tags to images is literally the most basic thing we can do to make a website or app more accessible. This is especially true for any images that contain text. The goal is to express the function, meaning or information portrayed by the image to all users.&lt;/p&gt;

&lt;p&gt;Check out this &lt;a href="https://www.w3.org/WAI/tutorials/images/decision-tree/"&gt;alt decision tree&lt;/a&gt; for help deciding how to make images in specific contexts more accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Being Div-Happy
&lt;/h2&gt;

&lt;p&gt;Listen, we all love divs. They're fun little boxes that you can use for almost anything. But just because you &lt;strong&gt;can&lt;/strong&gt; doesn't mean you should.&lt;/p&gt;

&lt;p&gt;We should try to use divs only when there are no more appropriate - read &lt;em&gt;semantic&lt;/em&gt; - elements for the job. You could use a div for your menu and everything would work fine. But using a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element helps anyone using a screen-reader and the non-humans (i.e. search engines) understand the meaning behind your code. This isn't just a box with links in it, it's a navigation elements that holds a menu that will guide me through the rest of the site.&lt;/p&gt;

&lt;p&gt;Instead of using a div to...&lt;br&gt;
Place text under an image ... use &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt;&lt;br&gt;
Make a link that performs an action (e.g. uses JavaScript to open a modal or page through a multi-page form) look like a button ... use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; &lt;br&gt;
Group the main part of your page ... use &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What are your web development bad habits? Which ones do you come across most often?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Creating Accessible Accordions with HTML, CSS &amp; JavaScript </title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 17 Feb 2021 00:37:14 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/creating-an-accordion-with-html-css-javascript-3gmn</link>
      <guid>https://dev.to/lizlaffitte/creating-an-accordion-with-html-css-javascript-3gmn</guid>
      <description>&lt;p&gt;An accordion, in development and design, is a graphical control element that consists of vertically stacked headers and hidden internal content. When clicked, a header's previously collapsed/hidden content box will expand to show its content; often text, images, or other grouped information.&lt;/p&gt;

&lt;p&gt;You've probably seen (or used) an accordion on a FAQ page, with the questions shown in the headers, and the answers to those questions hidden in the content boxes.  &lt;/p&gt;

&lt;p&gt;Accordions can help increase the user experience on web and application pages with lots of information. They allow developers to group all that information on one page, but only display the higher level headers/titles. Users can then glance over all the titles without being overwhelmed by the details. They can more easily find, and click on, the headers/titles that they are interested in, and access the greater detail of the content.&lt;/p&gt;

&lt;p&gt;There are countless widgets, plugins and other code snippets that will auto-magically add an accordion to your website or app. &lt;em&gt;But&lt;/em&gt; you can also build a simple accordion with only HTML, CSS and JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/jOVBzbY?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Accordion HTML
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul id="accordion"&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;button aria-controls="content-1" aria-expanded="false" id="accordion-control-1"&amp;gt;FAQ 1&amp;lt;/button&amp;gt;
    &amp;lt;div class="acc-item-content" aria-hidden="true" id="content-1"&amp;gt;
      &amp;lt;p&amp;gt;Answer 1!&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;button aria-controls="content-2" aria-expanded="false" id="accordion-control-2"&amp;gt;FAQ 2&amp;lt;/button&amp;gt;
    &amp;lt;div class="acc-item-content" aria-hidden="true" id="content-2"&amp;gt;
      &amp;lt;p&amp;gt;Answer 2&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;button aria-controls="content-3" aria-expanded="false" id="accordion-control-3"&amp;gt;FAQ 3&amp;lt;/button&amp;gt;
    &amp;lt;div class="acc-item-content" aria-hidden="true" id="content-3"&amp;gt;
      &amp;lt;p&amp;gt;Answer 3&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;button aria-controls="content-4" aria-expanded="false" id="accordion-control-4"&amp;gt;FAQ 4 &amp;lt;/button&amp;gt;
    &amp;lt;div class="acc-item-content" aria-hidden="true" id="content-4"&amp;gt;
      &amp;lt;p&amp;gt;Answer 4&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;button aria-controls="content-5" aria-expanded="false" id="accordion-control-5"&amp;gt;FAQ 5&amp;lt;/button&amp;gt;
    &amp;lt;div class="acc-item-content" aria-hidden="true" id="content-5"&amp;gt;
      &amp;lt;p&amp;gt;Answer 5&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the HTML, our entire accordion is housed in an unordered list. Each list item holds a div with the inner content and a button that will toggle the div's visibility. In an effort to make the accordion more accessible, we have &lt;code&gt;aria-expanded&lt;/code&gt; and &lt;code&gt;aria-hidden&lt;/code&gt; attributes, as well as &lt;code&gt;aria-controls&lt;/code&gt; attributes on the buttons that correspond with the ids of the &lt;code&gt;acc-item-content&lt;/code&gt; divs. These attributes will help users using screen readers understand our accordion, and what is and is not visible when the buttons are clicked on.&lt;/p&gt;

&lt;p&gt;I've also got my text in paragraph tags, which will be helpful if you have more than a few sentences in the content divs.&lt;/p&gt;

&lt;p&gt;Hopefully you're using a loop somewhere to dynamically create each list item and its child elements. &lt;/p&gt;

&lt;h2&gt;
  
  
  Accordion CSS
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ul {
  list-style: none;
}

#accordion button:focus {
  border-radius: 0px;
  outline: none;
}
#accordion button {
  outline: none;
  background-color: DarkSeaGreen;
  padding: 10px;
  border: none;
  border-bottom: 1px solid darkslategrey;
  color: white;
  width: 100%;
  text-align: left;
  font-size: 16px;
  border-radius: 0px;
}
#accordion li {
  border: 1px solid DarkSlateGray;
  border-bottom: none;
}
.acc-item:last-child {
  border-bottom: 1px solid DarkSlateGray;
}
#accordion button::after {
  content: "\002B";
  font-weight: 900;
  font-size: 22px;
  float: right;
}

#accordion {
  width: 80%;
  max-width: 800px;
  min-width: 275px;
  margin: auto;
}

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

&lt;/div&gt;



&lt;p&gt;Most of the CSS is for...style. We add background colors, borders and pseudo content to visually indicate that this is an accordion, and that you should click if you want to see more.&lt;/p&gt;

&lt;p&gt;Technically, the only rule set you need is this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.acc-item-content {
  padding: 0px 10px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sets the height of the content divs to 0 (hiding them from view); and gives the max-height a transition style and speed. This will come in handy when we get to the JavaScript, where we'll change the max-height values for our divs when the buttons are clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accordion JavaScript
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.addEventListener("DOMContentLoaded", (event) =&amp;gt; {
  let buttons = document.querySelectorAll("#accordion button");
  buttons.forEach((button) =&amp;gt; {
    let content = button.nextElementSibling;
    button.addEventListener("click", (event) =&amp;gt; {
      if (button.classList.contains("active")) {
        button.classList.remove("active");
        button.setAttribute("aria-expanded", false);
        content.style.maxHeight = null;
        content.setAttribute("aria-hidden", true);
      } else {
        button.classList.add("active");
        button.setAttribute("aria-expanded", true);
        content.style.maxHeight = content.scrollHeight + "px";
        content.setAttribute("aria-hidden", false);
      }
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In pseudo code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When all the DOM content is loaded...

  Collect all the buttons that are child elements of the element 
  with the id #accordion...

  Loop through each of these buttons...
     Grab the button's sibling element and save it in a variable 
     called content AND

     Add an event listener to each button, so that when the 
     button is clicked...

       If the button has the class active...
           Remove "active" from its class list AND

           Set its aria-expanded attribute to false AND

           Set the content variable's max-height value to null AND

           Set the content variable's aria-hidden attribute to true.

       Otherwise, if the button doesn't have the class active...
            Add "active" to its class list AND

           Set its aria-expanded attribute to true AND

           Set the content variable's max-height value even 
           to the value of the content variable's scroll height 
           (the height of an element's content) AND

           Set the content variable's aria-hidden attribute to false.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it: an accessible, simple accordion made with only HTML, CSS and vanilla JavaScript! &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Conquering Twitter OAuth Authorization in a Rails/React App</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 10 Feb 2021 00:49:13 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/conquering-twitter-oauth-authorization-in-a-rails-react-app-49di</link>
      <guid>https://dev.to/lizlaffitte/conquering-twitter-oauth-authorization-in-a-rails-react-app-49di</guid>
      <description>&lt;p&gt;&lt;a href="https://www.pexels.com/photo/red-blue-and-yellow-textile-5417837/"&gt;Cover Photo by Brett Jordan from Pexels&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a quick walkthrough of making Twitter API requests that require OAuth authorization, in a Rails/React app. You will need a Twitter developer account, an app with read and write permissions registered within your account, and your application’s API key and API key secret. You’ll also need to have added a callback URL for your application in your developer portal. I suggest app.com/auth/twitter/callback.&lt;/p&gt;

&lt;p&gt;Please note that Twitter calls their client credentials several different things, but I’ll be using API key and API secret to keep it simple.&lt;/p&gt;

&lt;p&gt;If you’ve ever built a React application with a separate Rails API backend, you’ve likely used the omniauth and omniauth-twitter gems to handle authorization for various social media platforms. However, if your application, like my social media assistant app, does not have separate front and backends (i.e. you’re not using views at all), you won’t be able to (easily) use the omniauth gem. &lt;/p&gt;

&lt;p&gt;Instead, we’re going to use oauth and httparty. &lt;/p&gt;

&lt;p&gt;The Twitter 3-legged OAuth flow is made up of three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;POST oauth/request_token 

&lt;ul&gt;
&lt;li&gt;Your application will send a POST request to &lt;a href="https://api.twitter.com/oauth/request_token"&gt;https://api.twitter.com/oauth/request_token&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The request will include your API key and API key secret&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If successful, the request response will return a token and a token secret&lt;/li&gt;
&lt;li&gt;GET oauth/authorize&lt;/li&gt;
&lt;li&gt;Your application will send a GET request to &lt;a href="https://api.twitter.com/oauth/authorize"&gt;https://api.twitter.com/oauth/authorize&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The request will include the previously returned token as a param.&lt;/li&gt;
&lt;li&gt;Your app’s user will be asked to authorize your app&lt;/li&gt;
&lt;li&gt;If successful, your user will be redirected to the callback URL specified in your developer portal&lt;/li&gt;
&lt;li&gt;The request response will include a oauth_token and a oauth_verifier&lt;/li&gt;
&lt;li&gt;POST oauth/access_token&lt;/li&gt;
&lt;li&gt;Your app will send a POST request to &lt;a href="https://api.twitter.com/oauth/access_token"&gt;https://api.twitter.com/oauth/access_token&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The request will include the previously returned oauth_token and oauth_verifier as params.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, in our application’s Gemfile, we’ll add oauth and httparty as dependencies.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Gemfile&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'httparty'
gem 'oauth'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to run &lt;code&gt;$bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we’ll save our API key and API key secret as environmental variables in a &lt;strong&gt;untracked&lt;/strong&gt; .env file.&lt;/p&gt;

&lt;p&gt;Never, ever, save API keys or other sensitive information into a file that’s being tracked by a version control system; or upload it somewhere public where it can be accessed (e.g. GitHub).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;.env&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEY=THISISOBVIOUSLYNOTMYAPIKEYREPLACEWITHYOURS
SECRET=AGAINTHISISJUSTANEXAMPLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is connecting your callback URL to the sessions controller create action.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;/config/routes.rb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  get '/auth/:provider/callback', to: "sessions#create"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’re set up and ready to start the 3-legged OAuth flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: POST oauth/request_token
&lt;/h2&gt;

&lt;p&gt;We’re going to be making our API calls from a controller that exclusively handle any social media API calls.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app/controllers/social_controller.rb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'pry'
require 'oauth'
class SocialController &amp;lt; ApplicationController
    def create
        ckey = ENV['KEY']
        csecret = ENV['SECRET']
        consumer = OAuth::Consumer.new(ckey,csecret,
            :site =&amp;gt; 'https://api.twitter.com',
            :authorize_path =&amp;gt; '/oauth/authenticate',
            :debug_output =&amp;gt; false)
        callback_url = "http://127.0.0.1:3000/auth/twitter/callback"
        request_token = consumer.get_request_token(:oauth_callback =&amp;gt; callback_url)
        token = request_token.token
        token_secret = request_token.secret
        confirmed = request_token.params["oauth_callback_confirmed"]
        if confirmed === "true"
            redirect_to "https://api.twitter.com/oauth/authorize?oauth_token=#{token}"
        else
            redirect_to "/"
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the method, first we’re saving our API key and API key secret from our .env file to local variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def req_token
  ckey = ENV['KEY']
   csecret = ENV['SECRET']
…
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we are creating a new consumer instance, passing it the API key and secret, the site we’re making the API request to, the authorize path and setting debug_output to false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def req_token
…
        consumer = OAuth::Consumer.new(ckey,csecret,
            :site =&amp;gt; 'https://api.twitter.com',
            :authorize_path =&amp;gt; '/oauth/authenticate',
            :debug_output =&amp;gt; false)
…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we save the callback URL into a local variable. We then make the POST request, by calling  get_request_token on our consumer instance, passing in the callback variable and saving the response as request_token (step 1 a-b). We then use that response to save the returned token and token secret (step 1 c).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: GET oauth/authorize
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def req_token
…
callback_url = "http://127.0.0.1:3000/auth/twitter/callback"
request_token = consumer.get_request_token(:oauth_callback =&amp;gt; callback_url)
token = request_token.token
 token_secret = request_token.secret
…
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure our request was successful, we’re checking to see if request_token contains oauth_callback_confirmed. If so, we’re redirecting to &lt;a href="https://api.twitter.com/oauth/authorize"&gt;https://api.twitter.com/oauth/authorize&lt;/a&gt; (step 2 a), adding the token we just saved to the URL as a param (step 2 b).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def req_token
…
confirmed = request_token.params["oauth_callback_confirmed"]
        if confirmed === "true"
            redirect_to "https://api.twitter.com/oauth/authorize?oauth_token=#{token}"
        else
            redirect_to "/"
        end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the point in the OAuth flow when the users will be redirected and asked to authorize your application. If they do, they’ll be redirected to yourapp.com/auth/twitter/callback which we connected to sessions#create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: POST oauth/access_token
&lt;/h2&gt;

&lt;p&gt;For example purposes, I’ve dumped all the rest of the flow into sessions#create.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
def create
        if params[:oauth_token] &amp;amp;&amp;amp; params[:oauth_verifier]
            oauth_token = params["oauth_token"]
            oauth_verifier = params["oauth_verifier"]
            baseUrl = 'https://api.twitter.com/oauth/access_token'
            response = HTTParty.post(baseUrl + "?oauth_token=#{oauth_token}&amp;amp;oauth_verifier=#{oauth_verifier}" )
            @access_token = response.split("&amp;amp;")[0].split("=")[1]
            @access_secret = response.split("&amp;amp;")[1].split("=")[1]
            @user_id = response.split("&amp;amp;")[2].split("=")[1]
            @user_sn = response.split("&amp;amp;")[3].split("=")[1]
            user = User.find_by(username: @user_sn)
            if user
                session[:user_id] = user.id
                render json: UserSerializer.new(user)
            else
                new_user_info = get_user(@user_sn)
                 new_user = User.new(username: new_user_info[“name”], password: SecureRandom.hex, uid: @user_id )
                if @user.save
                   log_in(@user)
                  else
                  render :new
                end
                render json: new_user_info
…
            end
        end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the params promised in step 2e were returned, we’ll use HTTParty to make a post request, passing those params to the base URL.&lt;/p&gt;

&lt;p&gt;That’s it! After you get the basics down, you’ll be able to connect the API calls to actions on your frontend.&lt;/p&gt;

</description>
      <category>oauth</category>
      <category>twitter</category>
      <category>rails</category>
      <category>react</category>
    </item>
    <item>
      <title>CSS but with Bridgerton Characters</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 03 Feb 2021 02:58:23 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/css-but-with-bridgerton-characters-1kpi</link>
      <guid>https://dev.to/lizlaffitte/css-but-with-bridgerton-characters-1kpi</guid>
      <description>&lt;p&gt;A few CSS selectors explained, but with Bridgerton characters. Possible spoilers!&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything Selector
&lt;/h3&gt;

&lt;p&gt;Selects every element.&lt;br&gt;
Syntax: &lt;code&gt;*&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* {
  color:white;
  font-size:48px;
   -webkit-text-stroke-width: 1px;
  -webkit-text-stroke-color: black;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/MWbwMdP?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Element Selector
&lt;/h3&gt;

&lt;p&gt;Selects every one of the specified elements.&lt;br&gt;
Syntax: &lt;code&gt;div&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;div{
 border: 1px solid red;
  border-radius:5px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/XWNbLvQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Class Selector
&lt;/h3&gt;

&lt;p&gt;Select every element with the specified class.&lt;br&gt;
Syntax: &lt;code&gt;.class&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.the-ton {
  color:red;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/wvoaLPg?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Double Class Selector
&lt;/h3&gt;

&lt;p&gt;Select every element with both specified classes&lt;br&gt;
Syntax: &lt;code&gt;.first-class.second-class&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.the-ton.bridge-brothers {
  color: blue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/NWbqZEa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Class Selector
&lt;/h3&gt;

&lt;p&gt;Selects every element with the second specified class, that is a child of an element with the first specified class.&lt;br&gt;
Syntax: &lt;code&gt;.first-class .second-class&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.the-ton .secret {
  font-size:30px; color:green;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/zYoGVeb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  ID Selector
&lt;/h3&gt;

&lt;p&gt;Selects the one element with the specified id. There should be only one.&lt;br&gt;
Syntax: &lt;code&gt;#id&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#queen{
  color: purple;font-size:68px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/xxRGoBv?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  First Child
&lt;/h3&gt;

&lt;p&gt;Selects the element that is the first child of its parent element.&lt;br&gt;
Syntax: &lt;code&gt;element:first-child&lt;/code&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#characters div:first-child{
  color:green;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/LizLaffitte/embed/MWbwNWZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The more you use these, and other CSS selectors, the more they will become second-hand. Do try not to bungle it up.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>PHP Cheatsheet for Rubyists</title>
      <dc:creator>Liz Laffitte</dc:creator>
      <pubDate>Wed, 27 Jan 2021 03:11:40 +0000</pubDate>
      <link>https://dev.to/lizlaffitte/php-cheatsheet-for-rubyists-198p</link>
      <guid>https://dev.to/lizlaffitte/php-cheatsheet-for-rubyists-198p</guid>
      <description>&lt;p&gt;This is a quick cheatsheet for every Rubyist struggling to remember PHP syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;br&gt;
Variable names can begin with an alphanumeric character or an underscore, but not a number. Local variables don't require any keywords. Variables are case sensitive (name and Name are two different variables). Conventionally, variables begin with a lowercase letter, and snakecase is used for multi-word variable names. You must assign a value to a variable when it is initialized, even if that value is 0, an empty string, or nil.&lt;/p&gt;

&lt;p&gt;Instance variables are initialized with the @ symbol, class variables with two @@, and global variables with a $.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name = "Ginny"
Name = "Jennifer" 
p name # "Ginny"
p Name # "Jennifer"
your_name = "Ted"
_energy = nil
@@name = "Liz"
$tater = "Tot"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt;&lt;br&gt;
PHP variables &lt;strong&gt;always&lt;/strong&gt; start with a dollar sign ($) and all variable declarations (all lines in PHP actually) must end with a semicolon (;).  Other than that, PHP and Ruby variables have very similar rules. Variables names can start only with an alphanumeric character or an underscore, never a number. Variables are case sensitive.&lt;/p&gt;

&lt;p&gt;However, you can declare a PHP variable without assigning it a value. Also, multi-word variables names can be camel- or snakecase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
$tatertots;
$name = "Liz";
$hours_of_sleep = 0;
$tatertots = 3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comments
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;br&gt;
Single line comments begin with a hash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p "Pay attention to this." #ignore this
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiline comments begin with &lt;code&gt;=begin&lt;/code&gt; and end with &lt;code&gt;=end&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p "Print this."
=begin
Don't print this.
Also, don't look at this.
It's not pretty enough to be part of Ruby.
=end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's very common to see hashes across multiple lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p "This is common"
#Even though
#they are
#for single lines
#technically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt;&lt;br&gt;
Single line comments start with two forward slashes ( // ). Multi-line PHP comments start with a forward slash and a star (/&lt;em&gt;) and end with a star and a forward slash (&lt;/em&gt;/), similar to multiline CSS comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
echo "This will print to the screen." //But this will be ignored.
/*
This will also be ignored
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Arrays
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;br&gt;
There are two ways to create an array in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_array = Array.new
your_array = [1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt;&lt;br&gt;
There is a grand total of one way to create an array in PHP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
$this_array = array(1, 2, 3);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>php</category>
      <category>cheatsheet</category>
      <category>ruby</category>
      <category>syntax</category>
    </item>
  </channel>
</rss>
