<?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: alliecaton</title>
    <description>The latest articles on DEV Community by alliecaton (@alliecaton).</description>
    <link>https://dev.to/alliecaton</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%2F536509%2Fed7ee60a-a1e8-4985-afb1-ac83813e35a8.png</url>
      <title>DEV Community: alliecaton</title>
      <link>https://dev.to/alliecaton</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alliecaton"/>
    <language>en</language>
    <item>
      <title>React-Redux Smoothie E-commerce Site</title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Fri, 30 Apr 2021 17:54:43 +0000</pubDate>
      <link>https://dev.to/alliecaton/empty-tbd-cl</link>
      <guid>https://dev.to/alliecaton/empty-tbd-cl</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XnjfJajN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccbk4ibpye0y2dj8znwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XnjfJajN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccbk4ibpye0y2dj8znwj.png" alt="Screen Shot 2021-05-07 at 10.41.43 AM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For my final React-Redux project for Flatiron's Software Engineering bootcamp, I created a demo e-commerce site for a fake smoothie shop. The main features are the Build A Smoothie and cart features. In this blog post, I'm going to demo the code that makes up my cart functionality. It's a very barebones shopping cart, which hopefully will be helpful for anyone trying to quickly throw together a cart feature.&lt;/p&gt;

&lt;p&gt;To start off, I broke down my shopping cart into 5 different components: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xpgWhreA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m3lq590drilpzkd4otb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xpgWhreA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m3lq590drilpzkd4otb.png" alt="Screen Shot 2021-05-07 at 2.46.21 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also created a redux cart reducer to handle my actions, and mapped my redux state and dispatch actions to a component called CartContainer. Within my CartContainer, I passed redux state/dispatch actions to the appropriate components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';
import CartList from '../components/cart/CartList'
import { connect } from 'react-redux'
import CartTotal from '../components/cart/CartTotal'
import CheckoutButton from '../components/cart/CheckoutButton'
import { Container } from 'react-bootstrap'
import { removeCartItem } from '../actions/cartActions.js'
class CartContainer extends Component {

    render() {
        return (
            &amp;lt;Container&amp;gt; 
                &amp;lt;div className="body-wrapper"&amp;gt;
                    &amp;lt;div className="inner-wrapper"&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;CartList removeCartItem={this.props.removeCartItem} items={this.props.items} /&amp;gt;
                            &amp;lt;CartTotal cartTotal={this.props.cartTotal} /&amp;gt;
                            &amp;lt;CheckoutButton items={this.props.items} totalPrice={this.props.cartTotal} /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/Container&amp;gt;
        );
    }
}

const mapStateToProps = (state) =&amp;gt; ({
    items: state.cartReducer.cartItems,
    ingredientIds: state.cartReducer.cartItems.ingredientIds,
    cartTotal: state.cartReducer.cartTotal
})

const mapDispatchToProps = dispatch =&amp;gt; ({
    removeCartItem: (id) =&amp;gt; dispatch(removeCartItem(id)),
})

export default connect(mapStateToProps, mapDispatchToProps)(CartContainer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My &lt;code&gt;CartList&lt;/code&gt;, &lt;code&gt;CartItem&lt;/code&gt;, &lt;code&gt;CartTotal&lt;/code&gt;, and &lt;code&gt;CheckoutButton&lt;/code&gt; simply renders buttons and data from my global store. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VzMFZdzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwm7ta9zdb7pda4jbgtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VzMFZdzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwm7ta9zdb7pda4jbgtu.png" alt="Screen Shot 2021-05-07 at 2.51.22 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within my &lt;code&gt;CheckoutContainer&lt;/code&gt;, I created a very standard HTML form to collect checkout information:&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;form className="checkout-form"&amp;gt;
                        &amp;lt;label&amp;gt;Please Enter Checkout Details&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                    &amp;lt;Row&amp;gt;
                        &amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                        &amp;lt;Col&amp;gt;
                            &amp;lt;label htmlFor="customerName"&amp;gt;* Name:&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;input id="customerName" type="text" name="customerName" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;

                            &amp;lt;label htmlFor="address"&amp;gt;* Address:&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;textarea id="address" type="textarea" name="address" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;

                            &amp;lt;label htmlFor="note"&amp;gt;Customer Note:&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;input id="note" type="text" name="note" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                        &amp;lt;/Col&amp;gt;
                        &amp;lt;Col&amp;gt;
                            &amp;lt;label htmlFor="cardnum"&amp;gt;* Card Number:&amp;lt;/label&amp;gt;
                            &amp;lt;input id="cardnum" type="text" name="cardNumber" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;label htmlFor="cardexp"&amp;gt;* Expiration Date (MM/YY):&amp;lt;/label&amp;gt;
                            &amp;lt;input id="cardexp" type="text" name="cardExp" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;

                            &amp;lt;label htmlFor="cardsec"&amp;gt;* Security Code:&amp;lt;/label&amp;gt;
                            &amp;lt;input id="cardsec" type="password" name="cardSecurityNum" onChange={this.handleChange} /&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;br&amp;gt;&amp;lt;/br&amp;gt;
                            &amp;lt;Button variant="success" onClick={(e) =&amp;gt; this.handleSubmit(e)} &amp;gt;Submit Order&amp;lt;/Button&amp;gt;
                        &amp;lt;/Col&amp;gt;

                    &amp;lt;/Row&amp;gt;

                    &amp;lt;/form &amp;gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CheckoutForm&lt;/code&gt; component keeps track of it's own state to pass along to the global redux store which is ultimately what gets POSTed to the database upon checkout finalization. Card information is commented out as I didn't actually want to collect any real card information since this is a fake shop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    state = {
        items: this.props.items, 
        totalPrice: this.props.totalPrice, 
        customerName: '', 
        address: '', 
        // cardNumber: '', 
        // cardExp: '',
        // cardSecurityNum: '',
        note: '', 
        message: ''
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mapped my checkout dispatch functions to my &lt;code&gt;CheckoutForm&lt;/code&gt; container and utilized them within my submit event handlers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mapDispatchToProps = dispatch =&amp;gt; ({
    checkout: (data) =&amp;gt; dispatch(checkout(data)),
    emptyCart: () =&amp;gt; dispatch(emptyCart())
})
&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;    handleChange = (e) =&amp;gt; {
        this.setState((prevState) =&amp;gt; ({
            ...prevState, 
            [e.target.name]: e.target.value, 
        }))
    }

    handleSubmit = (e) =&amp;gt; {
        e.preventDefault()
        if (this.state.items.length &amp;gt; 0 &amp;amp;&amp;amp; this.state.customerName !=='' &amp;amp;&amp;amp; this.setState.address !== '') {
            this.props.checkout(this.state)
            this.props.emptyCart()
            this.setState({message: "Your order is on the way!"})
        } else {
            this.setState({message: "Please fill out all required fields"})
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My checkout POST action utilizes Thunk to incorporate my dispatches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const checkout = (data) =&amp;gt; {

        return (dispatch) =&amp;gt; {
            dispatch({type: 'LOADING_POST'})

            return fetch('https://boiling-earth-59543.herokuapp.com/orders', {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(humps.decamelizeKeys({order: data}))
            })
            .then(resp =&amp;gt; resp.json())
            .then(json =&amp;gt; {
                console.log('this is the posted checkout obj', json)
                dispatch({type: 'CHECKOUT', payload: data})
            })
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And my reducer handles saving this checkout item to the global store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function cartReducer(state=  {
    order: []}, action) {

    switch (action.type) {

        case 'CHECKOUT': 

        const newOrder = {
            ingredientIds: action.payload.items,
            totalPrice: action.payload.totalPrice, 
            customerName: action.payload.customerName, 
            address: action.payload.address, 
            cardNumber: action.payload.cardNumber, 
            cardExp: action.payload.cardExp,
            cardSecurityNum: action.payload.cardSecurityNum,
            note: action.payload.note
        }

        return {
            order: [...state.order, newOrder]
        }


        default: 
            return state

    }

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

&lt;/div&gt;



&lt;p&gt;As far as my backend, things are pretty simple. My backend is a Rails api. My model relationships that pertain to the cart functionality are very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Order &amp;lt; ApplicationRecord
    has_many :products

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Product &amp;lt; ApplicationRecord
    has_many :product_ingredients
    has_many :ingredients, through: :product_ingredients
    belongs_to :order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I save Products to the database at the same time as Orders, as there was no real reason for my to be making extra fetches to the database when creating smoothie prodcuts on the frontend until they needed to be associated with an order. My create method looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def create
    @order = Order.create(order_params)
    Product.add_ingredients(params[:order][:items], @order)
    if @order.save
      render json: @order, status: :created, location: @order
    else
      render json: @order.errors, status: :unprocessable_entity
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created the class method &lt;code&gt;add_ingredients&lt;/code&gt; to help create Prodcuts alongside Orders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def self.add_ingredients(items, order)
        items.each do |item|
            product = Product.create
            item[:ingredient_ids].each do |id|
                ingredient = Ingredient.find_by(id: id)
                product.ingredients &amp;lt;&amp;lt; ingredient
                product.order_id = order.id
                product.save
            end
        end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Order's are persisted to the databases along with their Products (smoothies) and the ingredients that make them up. Thanks for reading!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>JS Music Recommendation Web App</title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Wed, 31 Mar 2021 15:42:53 +0000</pubDate>
      <link>https://dev.to/alliecaton/js-music-randomizer-262e</link>
      <guid>https://dev.to/alliecaton/js-music-randomizer-262e</guid>
      <description>&lt;p&gt;Music-R is available for use &lt;a href="http://plausible-nest.surge.sh/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Vf6A-zS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndhaiz1a15l7qd4y5w48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Vf6A-zS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndhaiz1a15l7qd4y5w48.png" alt="img1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the past week, I've dug into my first real vanilla Javascript application, and it's been a really enjoyable week (surprisingly? maybe so)! For the last three weeks I have moaned and groaned over the growing pains of switching from Ruby to learning JS, but putting the skills I've learned to use on a real application has really changed my entire mindset about Javascript! &lt;/p&gt;

&lt;p&gt;My application is a simple music recommender. You enter a song and its artist, and it spits out a related song. You have the option to make a username for yourself and keep track of recommended songs that you like. Pretty simple, but the whole thing ended up being about 300 lines of code! WHEW. &lt;/p&gt;

&lt;p&gt;I used three API's for this project, the last.fm API, the Youtube API, and a my Rails backend API where I stored user and song data.&lt;/p&gt;

&lt;p&gt;I had a good time playing around with CSS styling and DOM manipulation with this one-- it's probably the cutest looking application I've made so far. But more than that, I had a really good time building out the logic of my fetch requests. &lt;/p&gt;

&lt;p&gt;The last.fm API has a built in endpoint that returns 100 related tracks when given a single track-- it's the first place that the app hooks into when a user enters a song. But, it leaves a lot to be desired. Last.fm doesn't have related songs for everything-- in fact, most of the music that I listen to at least, didn't have related tracks. So instead of throwing an alert or an error, I decided to construct a fallback for when the related tracks endpoint comes up empty. I built out some functions that would grab the artist of the song, feed it into the related artists last.fm endpoint, grab that artist's most popular songs, and return a random song from that list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static getRelatedArtists(artistName) {
        return fetch(`https://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&amp;amp;artist=${artistName.toLowerCase().trim()}&amp;amp;api_key=efeaa32576655308d8b417be9812fc15&amp;amp;format=json`)
        .then(function(response) {
            return response.json();
        })
        .then(function(json) {
            console.log(json)
            let randomNum = Math.floor((Math.random() * 100))
            let artist = json.similarartists.artist[randomNum].name
            lastFmApi.getArtistTopSongs(artist)
        })
        .catch(function() {
            alert("Something went wrong. Reload and try again!")
        })
    }

    static getArtistTopSongs(artistName){
        return fetch(`https://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&amp;amp;artist=${artistName.toLowerCase().trim()}&amp;amp;api_key=efeaa32576655308d8b417be9812fc15&amp;amp;format=json`)
        .then(function(response) {
            return response.json();
        })
        .then(function(json) {
            console.log(json)
            let randomNum = Math.floor((Math.random() * 50))
            let song = json.toptracks.track[randomNum]
            lastFmApi.displayResults(song)
            console.log(song)
            Youtube.getVideo(song.name, song.artist.name)
        })
        .catch(function() {
            alert("Something went wrong. Reload and try again!")
        })
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I call it off the back of my fetch request to the related tracks fetch request in a simple if/else block that checks the length of the returned related tracks array. Once I implemented this, I almost never bumped up against a song without a recommendation. &lt;/p&gt;

&lt;p&gt;All in all, this was a really great experience-- probably my favorite thing to have built! It might not be the most interesting in terms of user interactivity, but I definitely felt like the process of building this project was smoother than others, and I learned a TON. Happy listening! &lt;/p&gt;

</description>
      <category>vanillajavascript</category>
      <category>javascript</category>
      <category>spa</category>
      <category>railsapi</category>
    </item>
    <item>
      <title>Book Tracking Rails App</title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Fri, 12 Mar 2021 20:37:57 +0000</pubDate>
      <link>https://dev.to/alliecaton/book-tracking-rails-app-5h2g</link>
      <guid>https://dev.to/alliecaton/book-tracking-rails-app-5h2g</guid>
      <description>&lt;p&gt;This app is available for use at: &lt;a href="https://shelvd-app.herokuapp.com/"&gt;https://shelvd-app.herokuapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For my rails application, I built a very simple version of a book tracking app like Goodreads or The Storygraph. I love to read, so I wanted to make something that I could actually use myself! I've been feeling demystified with Goodreads recently, so wanted to make something that suited my most baseline needs for a book tracking app. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TElIHZMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imhmaaanbzz4fjjvnkqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TElIHZMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imhmaaanbzz4fjjvnkqe.png" alt="homepage of Shelvd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My rails application has definitely been the most challenging project yet, but I've also learned the most during this process. The biggest thing I think I took away from this, aside from new hard programming skills, is the importance of project preparation. For our last two projects, we had a one week buffer off the back of our week-long project week. For both of those projects I filled that extra week to the brim expanding my application, changing things, and iterating. This time around, there was no time to iterate or change beyond the most essential refactoring. I didn't fully think through how having one week less to work on this project would affect the experience of it, so I planned about as much as I had with my CLI and Sinatra project(which, to say the least, was not sufficient for building something with such a tight scope). I've definitely come to understand the importance of structure and well thought out plans when tasked with a tight timeframe during this experience.&lt;/p&gt;

&lt;p&gt;Anyway, let's get into the app! &lt;/p&gt;

&lt;p&gt;My application has eight main models (two of which are purely join tables with no other function) &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Author &lt;/li&gt;
&lt;li&gt;Book &lt;/li&gt;
&lt;li&gt;BooksAuthor&lt;/li&gt;
&lt;li&gt;BooksShelf&lt;/li&gt;
&lt;li&gt;Post &lt;/li&gt;
&lt;li&gt;ReadingRoom &lt;/li&gt;
&lt;li&gt;Shelf&lt;/li&gt;
&lt;li&gt;User &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For gathering book information, I used the &lt;a href="https://github.com/zeantsoi/GoogleBooks"&gt;GoogleBooks gem&lt;/a&gt;. It packages up the Google Books API into an easy to use gem.&lt;/p&gt;

&lt;p&gt;The interactions between Users and Shelves is pretty straight forward-- shelves are lists the User can create to track books with. The books have authors, a description, and average rating information attached to them. In the future, I'd like to expand my application to allow users within my app to add a rating/review to books they've read. &lt;/p&gt;

&lt;p&gt;The other side of my application is the ReadingRoom model. I didn't have enough time to flesh out a Friend-to-Friend structure (I would like to add this in the future), but I still wanted to include a bit of a social aspect to the application. Reading Rooms are like mini forum rooms where users can create a room about a topic, and chat with other users about it. &lt;/p&gt;

&lt;p&gt;To share a bit of code, I'll share what my Book model looks like since it makes use of scope methods and the GoogleBooks API gem&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    has_many :books_shelves 
    has_many :shelves, through: :books_shelves 
    has_many :users, through: :shelves
    has_many :books_authors
    has_many :authors, through: :books_authors

    scope :highest_rated, -&amp;gt; {where('average_rating &amp;gt;= ?', 4).sort}

    validates :title, presence: true 
    validates :isbn, presence: :true 
    validates :isbn, uniqueness: true 


    def most_pop
        average_rating.to_i * ratings_count.to_i
    end

    def self.highest_rated_sorted
        highest_rated.sort_by {|book| book.most_pop }
    end

    def self.search(search)
        book = GoogleBooks.search("#{search}", {count: 3 })
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of using the GoogleBooks.search method all over my controllers, I decided to wrap it up into my own class method that would return exactly the amount of books I wanted. I also used a few different class methods and instance methods to sort books by rating and how many reviewers it has. &lt;/p&gt;

&lt;p&gt;All in all, I'm excited to keep working on this project as it's been a huge learning journey. There are so many areas I'm dying to refactor and slim up, so I am looking forward to continuing to develop this app in the future! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Illustrack: Sinatra Project</title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Fri, 12 Feb 2021 20:02:13 +0000</pubDate>
      <link>https://dev.to/alliecaton/illustrack-sinatra-project-3bg6</link>
      <guid>https://dev.to/alliecaton/illustrack-sinatra-project-3bg6</guid>
      <description>&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1jrPkc_LvThbxWZfbeVJJPHNFj8AOPm8m/view?usp=sharing"&gt;Demo Video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flatiron School's second project tasks us with making a Sinatra application that utilizes our knowledge of Active Record CRUD and ORM's. For my project, I decided to make a portfolio/e-commerce website for artists that I named Illustrack. I love to draw in my free time, so I wanted to make something that I myself would enjoy using.&lt;/p&gt;

&lt;p&gt;After getting my basic file structure set up using the gem Corneal, the first thing I did was set up my models and associations. &lt;/p&gt;

&lt;p&gt;My models utilized has_many, belongs_to, and has_many_through relationships, with tag_illustration handling the many to many relationship between illustrations and tags. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O9-vsuy6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/frjnjgvjj4c2w9nkb0kc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O9-vsuy6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/frjnjgvjj4c2w9nkb0kc.png" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After setting up my Application Controller and getting just a basic index page working, the next thing I did was get the image uploading functionality up and running. I used a gem called Carrierwave which helps handle file uploading and displaying. Once that was done, I moved on to stubbing out the rest of my controller requests. &lt;/p&gt;

&lt;p&gt;Rather than go through all of my many, many request blocks, I'll share the most challenging one to implement. When uploading a listing, users are able to upload a new image (or images), and/or choose from images they have already uploading previously. Additionally, I didn't want images that were ONLY uploaded to a listing to appear in the overall /illustrations index. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---wJdUIIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e6twrpssuut4jae0ydgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---wJdUIIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e6twrpssuut4jae0ydgl.png" alt="alt text"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I needed a way to separate out old illustrations that were now also used in a listing, and new illustrations that were only used in a listing. I ended up adding a new boolean attribute to my Illustration model called &lt;code&gt;used_in_listing&lt;/code&gt; which I used to flag illustrations that were only used in listings and should not be displayed on the portfolio side of the site. I set this in the same block where I created and assigned listing uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if params[:listing][:images]
            @imgs = params[:listing][:images].map do |image|
                Illustration.create(image: image, user_id: current_user.id, used_in_listing: "false")
            end

            @imgs.each do |img|
                img.used_in_listing = true 
            end

            @listing.illustrations &amp;lt;&amp;lt; @imgs
        end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If params included images, new images would be created, and if not, the application would move on to my next code block that handled the adding of old illustrations to a listing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if params[:listing][:image_ids]
            @old_imgs = params[:listing][:image_ids].map do |id|
                Illustration.find(id)
            end
            @listing.illustrations &amp;lt;&amp;lt; @old_imgs
        end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire post request ends up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    post '/listings' do 
        if !logged_in? 
            redirect '/login'
        else

        if !valid_listing?
            flash[:message] = "Please fill out all fields."
            redirect '/listings/new'
        end

        @listing = Listing.create(title: params[:listing][:title], description: params[:listing][:description])
        @listing.user_id = current_user.id
        @formatted_price = "#{params[:listing][:price]}00".to_i
        @listing.price = @formatted_price
        @listing.save!


        if params[:listing][:images]
            @imgs = params[:listing][:images].map do |image|
                Illustration.create(image: image, user_id: current_user.id, used_in_listing: "false")
            end

            @imgs.each do |img|
                img.used_in_listing = true 
            end

            @listing.illustrations &amp;lt;&amp;lt; @imgs
        end

        if params[:listing][:image_ids]
            @old_imgs = params[:listing][:image_ids].map do |id|
                Illustration.find(id)
            end
            @listing.illustrations &amp;lt;&amp;lt; @old_imgs
        end

        @listing.save!

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

&lt;/div&gt;



&lt;p&gt;It's quite long, but there were a lot of different components to handle. You'll notice at the start of my post request there are two lines for handling the price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@formatted_price = "#{params[:listing][:price]}00".to_i
        @listing.price = @formatted_price
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The functionality is disabled on my live site, but for purchasing items off of listings I used the Stripe API. The API called for certain attributes to be formatted a specific way, which is where these two lines of code come from. In hindsight, I would have gone with a different e-commerce service as Stripe legacied many of the services that would be necessary if this was a real, usable site (such as attaching shipping costs to items, etc). However, it was a HUGE learning experience integrating the API, and definitely the most difficult and time consuming part of building this application. Even though that functionality is not live, it was in no way a waste of time as I learned a &lt;em&gt;ton&lt;/em&gt; by going through the practice of integrating. &lt;/p&gt;

&lt;p&gt;Overall, the experience of building this application was a huge learning experience and a ton of fun. It's such an awesome feeling to be able to see and use something that you built from scratch. Looking forward to our next phase!&lt;/p&gt;

</description>
      <category>sinatra</category>
      <category>flatiron</category>
      <category>activerecord</category>
    </item>
    <item>
      <title>Mass Assignment in Ruby </title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Thu, 14 Jan 2021 22:15:29 +0000</pubDate>
      <link>https://dev.to/alliecaton/mass-assignment-in-ruby-51m7</link>
      <guid>https://dev.to/alliecaton/mass-assignment-in-ruby-51m7</guid>
      <description>&lt;p&gt;This morning, I had my first code review for my Ruby CLI project. During the review we went through my code and talked through the different files and methods that make up my project. The last portion of the review was dedicated to code refactoring where we focused on refactoring my initialize method for my &lt;code&gt;Book&lt;/code&gt; class. My original initialize method looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attr_accessor :title, :author, :rank, :description, :buy_links

    def initialize(hash)
        @title = title
        @author = author
        @description = description
        hash.each do |key,value| 
            if key == "title" 
                self.send(("#{key}="), value)
            elsif key == "rank"
                self.send(("#{key}="), value)
            elsif key == "author"
                self.send(("#{key}="), value)
            elsif key == "description"
                self.send(("#{key}="), value)
            elsif key == "buy_links"
                self.send(("#{key}="), value)
            end
        end
        save
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very smelly!!&lt;/p&gt;

&lt;p&gt;The first thing we did was comment out the &lt;code&gt;@title&lt;/code&gt;, &lt;code&gt;@author&lt;/code&gt;, and &lt;code&gt;@description&lt;/code&gt; variable assignments at the top, as they were leftover from earlier code where I was not using mass assignment to initialize (yet). After that, the instructor asked me to look into the &lt;code&gt;.respond_to?&lt;/code&gt; Ruby method. After looking through the documentation, and with prompting from the instructor, we whittled my code down to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attr_accessor :title, :author, :rank, :description, :buy_links

    def initialize(hash)
        hash.each do |key,value| 
            if self.respond_to?(key)
                self.send(("#{key}="), value)
            end
        end
        save
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call the &lt;code&gt;respond_to?&lt;/code&gt; method on an object, it will return true if the object responds to a given method. Another way I could think about my initialize with &lt;code&gt;respond_to?&lt;/code&gt; would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attr_accessor :title, :author, :rank, :description, :buy_links

    def initialize(hash)
        hash.each do |attr_key,attr_value| 
            if self.respond_to?(attr_key)
                self.send(("#{attr_key}="), attr_value)
            end
        end
        save
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thinking about it like this helps me conceptualize why I would be using the &lt;code&gt;respond_to?&lt;/code&gt; method here. Basically, I'm taking the hash passed into initialize, iterating over each key/value pair, and checking if my Book object (self) can respond to the writer/reader methods that attr_key corresponds to, AKA my &lt;code&gt;self&lt;/code&gt; object can be written/read through my attr_accessors. &lt;/p&gt;

&lt;p&gt;The other method that we briefly spoke about was the &lt;code&gt;send&lt;/code&gt; method in Ruby. I knew what this method did in context of my applications, but I didn't know how it could be used in ways not directly related to mass assignment in my initialize. The instructor explained that &lt;code&gt;send&lt;/code&gt; is a method that is used to call other methods. Theoretically, I could create a custom method and use &lt;code&gt;send&lt;/code&gt; to call that method and arguments on an object, for example &lt;code&gt;object.send :find_by_name, "Allie"&lt;/code&gt;. Or in this case, use send to call a writer method that we interpolate the key we want to write into, and associate it with the key's value. &lt;/p&gt;

&lt;p&gt;I definitely struggled with mass assignment conceptually in the last few weeks of the Ruby phase, so being forced to refactor my own code and digging into why certain methods are used in mass assignment (and how they can be used for other purposes) has definitely helped me gain a stronger understanding. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>New York Times Bestsellers List CLI Gem </title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Fri, 08 Jan 2021 18:17:58 +0000</pubDate>
      <link>https://dev.to/alliecaton/new-york-times-bestsellers-list-cli-gem-kfh</link>
      <guid>https://dev.to/alliecaton/new-york-times-bestsellers-list-cli-gem-kfh</guid>
      <description>&lt;p&gt;Over the last week, I've jumped head first into creating my very first ruby project from scratch. I created a gem that will give you information about the New York Times Fiction Bestsellers for a given date,________&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: API Class&lt;/strong&gt;&lt;br&gt;
The first thing I did when I got started (right after I ran bundler to create my gem's structure) was build out the API class. My class initializes with a date that the user inputs, and creates the call. I created two methods in this class: one that gets the API call and returns the hash that I am interested in using, and one that creates objects out of each book that the API call retrieves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    attr_accessor :date

    def initialize(date)
        @date = date
        create_book
    end

    ## Calls on the API with the date initialized
    def get
        url = "https://api.nytimes.com/svc/books/v3/lists/#{@date}/combined-print-and-e-book-fiction.json?api-key=#{ENV["API_KEY"]}"
        uri = URI.parse(url)
        response = Net::HTTP.get_response(uri)
        response_hash = JSON.parse(response.body)
        response_hash["results"]["books"]
    end


    ## Creates a book using a hash of relevant attributes
    def create_book
        get.each do |book|
            new_book = NytCli::Book.new(book)
        end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of now, when I call create_book, nothing will be created! Which leads us to...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Book Class&lt;/strong&gt;&lt;br&gt;
For my book class, I knew I wanted every book to require a title and an author, but I also wanted other attributes to be initialized if they existed in the API response hash. So I set up my book's initialize method to account for optional attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ## Initializes book with only specified keys of a hash-- possibly add where to buy later
    def initialize(hash)
        @title = title
        @author = author
        # @description = description
        hash.each do |key,value| 
            if key == "title" 
                self.send(("#{key}="), value)
            elsif key == "rank"
                self.send(("#{key}="), value)
            elsif key == "author"
                self.send(("#{key}="), value)
            elsif key == "description"
                self.send(("#{key}="), value)
            elsif key == "buy_links"
                self.send(("#{key}="), value)
            end
        end
        save
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not the DRY-est method, and I am interested in doing some research about how to condense this down (looking at you StackOverflow). The next thing I did was set up some class variables. I knew I wanted a class variable that collected all book objects in an array, one that collected the book objects that the user gets more information about (more on that later), and one that collects the book objects that a user saves to their session collection (also more on that later)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @@all = [] ## All book instances
    @@all_selected = [] ## All book instances that the user has viewed
    @@saved = [] ## Books user has saved to their session collection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that was out of the way, I went ahead and built out a bunch of standard methods that would come in handy later on when building my CLI class. There is a method for resetting the @@all array, one that returns a book object using an argument of a book title, a save method to shovel books into the @@all array, and a few others to assist some functions I wanted to build out in the CLI class. Now, onto the monster...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: CLI Class&lt;/strong&gt;&lt;br&gt;
Here is where the real meat of the application lies. The application has 5 main functions: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Print a list of books that are associated with a user input date &lt;/li&gt;
&lt;li&gt;Choose a book from that list to get more information about it &lt;/li&gt;
&lt;li&gt;Open a link to buy a selected book from the application &lt;/li&gt;
&lt;li&gt;Add a selected book to a user's saved books for a session &lt;/li&gt;
&lt;li&gt;Show the user's saved collection, and offer the same functions as a selected book from a NYT list&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll skip over showing some of the more basic methods in this class for brevity, and talk about the methods that gave me the most trouble, and the methods that I learned something new while creating. &lt;/p&gt;

&lt;p&gt;The biggest hurdle I had was creating methods to validate a user-input date. I needed to validate whether the date was actually a real date, whether the date was input in the correct format to interact with the API, and whether the date fell between the user's current date, and the date that the NYT API started at (when they started publishing the Bestsellers list online). Initially, I tried to wrap up these 3 validity checks into the same method, and things quickly got out of hand, so I split them into two. The first method &lt;code&gt;valid_date?&lt;/code&gt; checks to make sure the date is a real date and in the correct format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ## Checks if date is real and in the correct format
    def valid_date?(date)
        Date.valid_date? *"#{Date.strptime(date,"%Y-%m-%d")}".split('-').map(&amp;amp;:to_i) 
        rescue 
            puts "\n#{@@emojis[0]} Oops! That input is invalid. Please input a valid date using YYYY-MM-DD #{@@emojis[0]}\n".red
            sleep(1)
            get_date
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had no idea about the build in Date and Time classes in ruby until I this project. It made life soooo much easier to be able to use those classes. Basically, this method defines what an acceptable format is, parses out the input date into a format that the Date class can read, and then calls the built in Date class method &lt;code&gt;valid_date?&lt;/code&gt; to check whether or not the date is a real date. The second validity method I created was my &lt;code&gt;valid_timeframe?&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def valid_timeframe?(date)
        new_date = Time.new(date.split("-")[0].to_i, date.split("-")[1].to_i, date.split("-")[2].to_i)
        nyt_start_date = Time.new(2011, 2, 13)
        if new_date &amp;lt;= Time.now &amp;amp;&amp;amp; new_date &amp;gt;= nyt_start_date
            true
        else
            puts "\n#{@@emojis[0]} Oops! That input is invalid. Make sure your date falls between now and 2011-02-13 #{@@emojis[0]}\n".red
            sleep(1)
            get_date
        end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method simply checks to make sure that the date falls within a valid timeframe for the API call. If both of those validity methods, check out, a new API object is called and initialized, which also means we have a bunch of new book objects created and ready to access! Woohoo! &lt;/p&gt;

&lt;p&gt;The next methods I built out were fairly simply display methods for listing out the books that were created along with the API call in a numbered list, along with a method to retrieve a user input number associated with one of the listed books, and display the title, author, rank, and description of that selected book, along with some new menu options. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b7qcZtiX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a28lhyl89ul449sejzje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b7qcZtiX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a28lhyl89ul449sejzje.png" alt="Screen Shot 2021-01-08 at 1.01.33 PM"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--irARYUoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxjd9i5itpeewzs2hklm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--irARYUoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxjd9i5itpeewzs2hklm.png" alt="Screen Shot 2021-01-08 at 1.02.08 PM"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I had a lot of fun building out the method to buy a book because it was the first method I built in this application that used an external gem. I used &lt;a href="https://github.com/copiousfreetime/launchy"&gt;Launchy&lt;/a&gt; for this method which basically allows you to launch links from inside a CLI. Very cool!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ## Links out to a B&amp;amp;N link for selected book 
    def buy_book(title)
        current_book = NytCli::Book.find_by_title (title)
        current_book.buy_links.each do |shop|
            if shop["url"].include? "barnes"
                Launchy.open(shop["url"])
                self.ask
            end 
        end 
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API gives links to several different sites to buy books from including Amazon, Indiebound, B&amp;amp;N, Google Books, etc. &lt;br&gt;
Initially I wanted to only launch Bookshop (support your local book stores!!!!) links, but for some reason many of the links from NYT for Bookshop and Indiebound were broken! Not cool!! So I decided to use Barnes &amp;amp; Noble as a last resort and seemed more reliable within this API. I utilized my Book class &lt;code&gt;self.find_by_title&lt;/code&gt; method to retrieve the book instance, and then iterated over the hash of links associated with that object to retrieve just the Barnes &amp;amp; Noble one. Then we pass that link into Launchy, and we're golden! &lt;/p&gt;

&lt;p&gt;The next bit of functionality I had a good time with was the save books functionality. I really liked the idea of a place where you could collect books from different dated lists you view within the application, in case you see something you like and want to remember for later. &lt;/p&gt;

&lt;p&gt;The basic functionality of this is very similar to the option to get a list of Bestseller books for a date, just pulling from a different place. From an individual book screen, you are able to save a book to your collection. Under the hood this means shoveling the book into a Book class variable array &lt;code&gt;@@saved&lt;/code&gt;. To display a user's saved list, I iterate over this array and output a list. From here, things work and look pretty much the same as the functionality to list books from a Bestseller list. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LXTRe_Zm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u0wlz9vp14tmvfzba4q4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LXTRe_Zm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u0wlz9vp14tmvfzba4q4.png" alt="Screen Shot 2021-01-08 at 1.14.43 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's pretty much it! I learned a lot of new things while creating this CLI, namely how to set up an environment and where and when to require what. Up until now, I've been writing methods to pass tests provided by Flatiron School, so there isn't much setup involved. It was very satisfying (and frustrating at times) to take the ruby skills I've learned over the last 3 weeks, and actually put them into something that works. I'm still working out how to make this a real, install-able gem, but I'm getting close! I had a lot of fun doing this, and I will definitely be making more ruby gems in the future! &lt;/p&gt;

</description>
      <category>cli</category>
      <category>ruby</category>
      <category>gem</category>
    </item>
    <item>
      <title>Why I wanted to study Software Engineering</title>
      <dc:creator>alliecaton</dc:creator>
      <pubDate>Sun, 13 Dec 2020 22:02:44 +0000</pubDate>
      <link>https://dev.to/alliecaton/why-i-wanted-to-study-software-engineering-c54</link>
      <guid>https://dev.to/alliecaton/why-i-wanted-to-study-software-engineering-c54</guid>
      <description>&lt;p&gt;About a year ago, I was out to dinner with a friend of mine and we were discussing how (against our better judgement, perhaps) we really missed being in school. Not for the exams, or homework, or GPA-induced pressures, but because we missed &lt;em&gt;learning.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this same time, I was working as an Associate Producer at a small video game studio and getting ramped up on a project that would require me to learn how to use Unity and write some simple Lua. This task was not exactly within my job description as a project manager, but it was through this project that I really began to understand that coding is a skill that requires constant learning. The ups and downs of learning something new, getting stuck, and then learning something new to get unstuck was something that spoke directly to my desire to grow and gain new skills that school and college had once satiated. &lt;/p&gt;

&lt;p&gt;I started to do some self-directed learning outside of work, mostly focused on HTML and CSS. I found myself losing huge chunks of time totally engrossed in making cute mini websites for myself. One of my favorite things to do is acquire new hobbies (another perk of being a developer is the financial stability it provides that allows people to explore their personal tech and non-tech hobbies), and this felt like the ultimate hobby to take up. &lt;/p&gt;

&lt;p&gt;At that same job, I had wonderful colleagues who saw my passing interesting in coding, and encouraged me in it. At a certain point, the interest became less of a passing one, but rather one I wanted to pursue full time. So, here I am at the start of that journey-- I can't wait to see where it takes me! &lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>coding</category>
      <category>programming</category>
      <category>codingbootcamp</category>
    </item>
  </channel>
</rss>
