<?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: Kevin Sassé</title>
    <description>The latest articles on DEV Community by Kevin Sassé (@sassek70).</description>
    <link>https://dev.to/sassek70</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%2F938506%2F5f95423b-7b97-48d4-8017-833e833180f4.jpg</url>
      <title>DEV Community: Kevin Sassé</title>
      <link>https://dev.to/sassek70</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sassek70"/>
    <language>en</language>
    <item>
      <title>Using Rails Action Cable with a React Client</title>
      <dc:creator>Kevin Sassé</dc:creator>
      <pubDate>Thu, 05 Jan 2023 19:40:11 +0000</pubDate>
      <link>https://dev.to/sassek70/using-rails-action-cable-with-a-react-client-3o8j</link>
      <guid>https://dev.to/sassek70/using-rails-action-cable-with-a-react-client-3o8j</guid>
      <description>&lt;p&gt;For my final bootcamp project, I decided to make use of websockets to create a 1v1 card game using React &amp;amp; Rails. In my research, I had difficulty finding sources that went beyond the functionality of a simple chat application. As a result, the guide below worked for my purposes but there may be more efficient ways to handle this type of connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic terminology
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;consumer&lt;/code&gt; is the client connecting to a websocket and is created in a javascript client. A &lt;code&gt;consumer&lt;/code&gt; can be subscribed to multiple channels at a time.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;channel&lt;/code&gt; "Each channel encapsulates a logical unit of work, similar to what a controller does in a typical MVC setup" - rubyonrails.org&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;subscriber&lt;/code&gt; is a &lt;code&gt;consumer&lt;/code&gt; that has successfully connected to a &lt;code&gt;channel&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;Starting with Rails, in the Gemfile, uncomment &lt;code&gt;gem "rack-cors"&lt;/code&gt; and run &lt;code&gt;bundle install&lt;/code&gt; in your terminal. Open the cors.rb file (app/config/initializers/cors.rb) and uncomment this section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "*"

    resource "*",
      headers: :any,
      methods: [:get, :post, :patch, :delete, :options, :head]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the purposes of this guide, I am setting origins to "*". That is fine if you are working locally but be sure to update it to the URL of your front-end application if you deploy your project.&lt;/p&gt;

&lt;p&gt;Next we need to create a route in the routes.rb file and mount the action cable server, your routes file should 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;Rails.application.routes.draw do
  # rest of your routes
  mount ActionCable.server =&amp;gt; '/cable'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the cable.yml file, it should 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;development:
  adapter: async

test:
  adapter: test

production:
  adapter: redis
  url: &amp;lt;%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %&amp;gt;
  channel_prefix: &amp;lt;your_app_name&amp;gt;_production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guide only focuses on the development environment.The async adapter will suffice however you will most likely need to use redis for production, please refer to the documentation your hosting service for configuration.&lt;/p&gt;

&lt;p&gt;Now let's set up the React app to work with our server. First let's install an npm package that helps connect React with rails. There are many available but I found &lt;code&gt;@rails/actioncable&lt;/code&gt; to be the most up-to-date and most used.&lt;/p&gt;

&lt;p&gt;In your console, run &lt;code&gt;npm i @rails/actioncable&lt;/code&gt;. Once installation is complete, create a new file &lt;code&gt;cable.js&lt;/code&gt; in the &lt;code&gt;src&lt;/code&gt; folder and add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createConsumer } from "@rails/actioncable";

const consumer = createConsumer(`&amp;lt;your_backend_url&amp;gt;/cable`)

export default consumer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a connection
&lt;/h2&gt;

&lt;p&gt;Now that both applications are configured to use ActionCable we can create the websocket connection.&lt;/p&gt;

&lt;p&gt;In the Rails app, create a new file in the channels folder &lt;code&gt;app/channels&lt;/code&gt;. The name of this file will be the name of the channel itself. If you used a rails generator you will see two methods added by default, &lt;code&gt;subscribed&lt;/code&gt; and &lt;code&gt;unsubscribed&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For my game, when a new game is started, a random key is generated. I am using that key to identify channel instances, my channel file 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;class GameSessionChannel &amp;lt; ApplicationCable::Channel
  def subscribed
    # stream_from "some_channel"
    game = Game.find_by(game_key: params[:game_key])
    stream_for game
  end

  def unsubscribe
    # Any cleanup needed when channel is unsubscribed
    puts "unsubscribed"
    stop_all_streams

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

&lt;/div&gt;



&lt;p&gt;In React, the game creates the websocket connection when a user joins a game as part of a useEffect when the Game component first loads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import consumer from "../cable"

useEffect(() =&amp;gt; {
        consumer.subscriptions.create({
                channel: "GameSessionChannel",
                game_key: `${gameSession.game_key}`,
            },{
                connected: () =&amp;gt; {
                    console.log("connected")
                    setIsConnected(true)
                },
                disconnected: () =&amp;gt; {
                    console.log("disconnected")
                    setIsConnected(false)
                },
                received: (data) =&amp;gt; {
                    switch(data.action) {
                        case "attack-declared":
                          // any logic you want to run
                          break;
                        case "defense-declared":
                          // any logic you want to run
                          break;
                        // any other cases needed
                    }
                }
            })
            return () =&amp;gt; {
                consumer.disconnect()
            }
    },[])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down. First, &lt;code&gt;consumer.subscriptions.create&lt;/code&gt; will create the websocket connection. It needs a &lt;code&gt;channel&lt;/code&gt; and &lt;code&gt;identifier&lt;/code&gt;. The &lt;code&gt;channel&lt;/code&gt; must match the class of the Rails channel that will handle this connection, in this case it is "GameSessionChannel". The &lt;code&gt;identifier&lt;/code&gt; is the &lt;code&gt;game_key&lt;/code&gt;. In the Rails application, I use this game_key to find the Game in the database and set it as the broadcast target for this channel in the subscribe method.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;connected&lt;/code&gt;, &lt;code&gt;disconnected&lt;/code&gt; and &lt;code&gt;received&lt;/code&gt; are functions that will run when those conditions are met, i.e when the connection is created/terminated and when the client receives data from the server.&lt;/p&gt;

&lt;p&gt;I also include a &lt;code&gt;return&lt;/code&gt; callback function to disconnect the websocket when the user navigates away from the page or closes the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broadcasting data
&lt;/h2&gt;

&lt;p&gt;Now that we have the connections set up correctly, we can broadcast data from anywhere within our Rails application.&lt;br&gt;
Let's send a message when a player joins the game.&lt;/p&gt;

&lt;p&gt;In our React app, we will send a fetch request to the server with the User information and the Game Key as parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const handleSubmit = (e) =&amp;gt; {
        e.preventDefault()


          fetch(`&amp;lt;your_backend_url&amp;gt;/joingame/${formData.gameKey}`,{
            method: "PATCH",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({opponent_id: currentUser.id}),
          })
          .then(res =&amp;gt; {if (res.ok) {
            res.json()
          .then(existingGame =&amp;gt; {
            setGameSession(existingGame)
            navigate(`/game/${existingGame.game_key}`)
          })
          } else {
              res.json().then(errors =&amp;gt; setErrors(errors))
          }})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Rails application will then look up the Game in the database, associate the user as the opponent(or host if this user is creating a game) then broadcast to all subscribers that a user has joined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def join_game
        game = Game.find_by!(game_key: params[:game_key])
        user = User.find(params[:opponent_id])
        game.update!(opponent_id: params[:opponent_id])
        user.update!(gamesPlayed: user.gamesPlayed + 1)
        GameSessionChannel.broadcast_to game, {action: "user-joined", game: game, message: "Opponent has joined the game"}
             render json: game, status: :accepted
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line is the websocket broadcast from the &lt;code&gt;game&lt;/code&gt; channel as defined by the &lt;code&gt;game_key&lt;/code&gt; and is sent to all subscribers of that particular game:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GameSessionChannel.broadcast_to game, {action: "user-joined", game: game, message: "Opponent has joined the game"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;action&lt;/code&gt; key matches a case in the React app, which then triggers the state of the gameSession to be updated with the new player's information as well as adding a &lt;code&gt;message&lt;/code&gt; to the game log for all players to see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        case "user-joined":
                            setGameSession(data.game)
                            setGameLog(gameLog =&amp;gt; ([ ...gameLog, data.message]))
                            break;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I was first setting up broadcasts, I had set all of my cases to console.log(data.message) as an easy test to check if data was coming from the server correctly.&lt;/p&gt;

&lt;p&gt;Additional reading:&lt;br&gt;
&lt;a href="https://guides.rubyonrails.org/action_cable_overview.html" rel="noopener noreferrer"&gt;Rails docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/@rails/actioncable" rel="noopener noreferrer"&gt;@rails/actioncable npm package&lt;/a&gt;&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Ruby on Rails Serialization: The Basics</title>
      <dc:creator>Kevin Sassé</dc:creator>
      <pubDate>Thu, 08 Dec 2022 08:10:43 +0000</pubDate>
      <link>https://dev.to/sassek70/ruby-on-rails-serialization-16f5</link>
      <guid>https://dev.to/sassek70/ruby-on-rails-serialization-16f5</guid>
      <description>&lt;p&gt;Rails is a powerful Ruby framework that allows developers to focus on developing while handling most of the tedium work, i.e. database querying, in the background as long as you, the developer, follow the "convention-over-configuration" format. Rails' generators make following that format even easier.&lt;/p&gt;

&lt;p&gt;For my current project, we want a User to be able to see all of their upcoming events. There a few ways to accomplish this and luckily Rails offers a couple simple solutions to us.&lt;/p&gt;

&lt;p&gt;Note: Rails' conventions rely on the resource associations, In my &lt;a href="https://dev.to/sassek70/a-beginners-guide-to-active-record-associations-2boi"&gt;previous blog&lt;/a&gt; I cover Active Record associations and some tips to help set them up correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  USING INCLUDE: &amp;amp; EXCEPT:
&lt;/h3&gt;

&lt;p&gt;When we query the database to find a User, we want to receive the User's information along with the events associated with that User. By default, the &lt;code&gt;show&lt;/code&gt; action in the Users Controller will only return the User data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#users_controller.rb
    def show
        user = User.find(params[:id])
        render json: user
    end

---

#server response
{
    "id": 2,
    "user": "kevin"
    "created_at": "2022-12-08T07:03:38.943Z",
    "updated_at": "2022-12-08T07:03:38.943Z",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we add &lt;code&gt;include: :events&lt;/code&gt; as part of the &lt;code&gt;show&lt;/code&gt; action's render we will receive all events associated with that user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#users_controller.rb

    def show
        user = User.find(params[:id])
        render json: user, include: :events
    end

---

#server response
{
    "id": 2,
    "user": "kevin",
    "created_at": "2022-12-08T07:03:38.943Z",
    "updated_at": "2022-12-08T07:03:38.943Z",
    "events": [
         {
            "id": 9,
            "name": "Muse",
            "venue": "Houston Center",
            "event_type": "concert",
            "created_at": "2022-12-08T06:09:39.617Z",
            "updated_at": "2022-12-08T06:09:39.617Z"
        },
        {
            "id": 5,
            "name": "Radiohead",
            "venue": "Atlanta-Center",
            "event_type": "concert",
            "created_at": "2022-12-08T06:09:39.596Z",
            "updated_at": "2022-12-08T06:09:39.596Z"
        }
    ]
}

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

&lt;/div&gt;



&lt;p&gt;Rails generators will, by default, add &lt;code&gt;t.timestamps&lt;/code&gt; columns to your tables. We can use &lt;code&gt;except:&lt;/code&gt; to prevent them from being returned by the server, however this will only exclude those keys from the top level resource, our events will still show those key/value pairs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#users_controller.rb

    def show
        user = User.find(params[:id])
        render json: user, except: [:created_at, :updated_at], include: :events
    end

---

#server response
{
    "id": 2,
    "user": "kevin",
    "events": [
         {
            "id": 9,
            "name": "Muse",
            "venue": "Houston Center",
            "event_type": "concert",
            "created_at": "2022-12-08T06:09:39.617Z",
            "updated_at": "2022-12-08T06:09:39.617Z"
        },
        {
            "id": 5,
            "name": "Radiohead",
            "venue": "Atlanta-Center",
            "event_type": "concert",
            "created_at": "2022-12-08T06:09:39.596Z",
            "updated_at": "2022-12-08T06:09:39.596Z"
        }
    ]
}

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

&lt;/div&gt;



&lt;p&gt;We could work around that by nesting &lt;code&gt;include:&lt;/code&gt; and &lt;code&gt;except:&lt;/code&gt; statements, however, even with this simple example, that becomes very messy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def show
        user = User.find(params[:id])
        render json: user.to_json(except: [:created_at, :updated_at], include: [:events =&amp;gt; {except:[:created_at, :updated_at]}])
    end

---

#server response
{
    "id": 2,
    "user": "kevin",
    "events": [
         {
            "id": 9,
            "name": "Muse",
            "venue": "Houston Center",
            "event_type": "concert",
        },
        {
            "id": 5,
            "name": "Radiohead",
            "venue": "Atlanta-Center",
            "event_type": "concert",
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you really wanted to, you can keep chaining &lt;code&gt;include:&lt;/code&gt; and &lt;code&gt;except:&lt;/code&gt; as much as needed but Rails provides a better way.&lt;/p&gt;

&lt;h3&gt;
  
  
  SERIALIZERS
&lt;/h3&gt;

&lt;p&gt;Using Rails serializers allows us to clean up the Users Controller and control what data is being returned by the server.  You will have to make sure the gem is added to your Gemfile and installed. This gem is &lt;u&gt;not&lt;/u&gt; included when running &lt;code&gt;rails new&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem 'active_model_serializers'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once installed, you can use &lt;code&gt;rails g resource&lt;/code&gt; to create a model, controller and serializer at the same time, or you can also use &lt;code&gt;rails g serializer &amp;lt;name(singular)&amp;gt;&lt;/code&gt; if you already have a model &amp;amp; controller.&lt;/p&gt;

&lt;p&gt;Serialzers user &lt;code&gt;attributes&lt;/code&gt; to define what keys are returned instead of &lt;code&gt;only:&lt;/code&gt; or &lt;code&gt;except:&lt;/code&gt;. They also make use of Active Record associations to include data from other tables. &lt;/p&gt;

&lt;p&gt;Returning to our User events example, we can simplify the Users Controller and add &lt;code&gt;attributes&lt;/code&gt; and the necessary relationships to our serializers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#users_controller.rb
    def show
        user = User.find(params[:id])
        render json: user
    end

---

#user_serializer.rb
  class UserSerializer &amp;lt; ActiveModel::Serializer
    attributes :id, :username
    has_many :events
  end

---

#event_serializer.rb
  class EventSerializer &amp;lt; ActiveModel::Serializer
    attributes :id, :name, :venue, :event_type
  end

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

&lt;/div&gt;



&lt;p&gt;After updating all the files, our server response will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#server response
{
    "id": 2,
    "user": "kevin",
    "events": [
         {
            "id": 9,
            "name": "Muse",
            "venue": "Houston Center",
            "event_type": "concert",
        },
        {
            "id": 5,
            "name": "Radiohead",
            "venue": "Atlanta-Center",
            "event_type": "concert",
        }
    ]
}

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

&lt;/div&gt;



&lt;p&gt;Using serializers we can make our code cleaner and more efficient by following "separation of concerns" and Rails "convention over configuration". This is a simple example showing the basics of Rails Active Model Serializers&lt;/p&gt;

&lt;p&gt;Further reading:&lt;br&gt;
&lt;a href="https://github.com/rails-api/active_model_serializers/tree/0-10-stable"&gt;Active Model Serializers Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>tutorial</category>
      <category>flatironschool</category>
      <category>activemodel</category>
    </item>
    <item>
      <title>A Beginner's Guide to Active Record Associations</title>
      <dc:creator>Kevin Sassé</dc:creator>
      <pubDate>Thu, 17 Nov 2022 07:13:36 +0000</pubDate>
      <link>https://dev.to/sassek70/a-beginners-guide-to-active-record-associations-2boi</link>
      <guid>https://dev.to/sassek70/a-beginners-guide-to-active-record-associations-2boi</guid>
      <description>&lt;p&gt;Active Record is an Object-Relational Mapping (ORM) that allows you to map database tables to Ruby classes. A Ruby class will be its own table and instances of that class will be the rows of that table. &lt;/p&gt;

&lt;p&gt;Let's imagine we are creating a simple cookbook application. You will have a class called Recipes, each row in the corresponding Recipes table will be an instance of the Recipes class and the database table would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj8i2lq5bg3so0fqzgqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj8i2lq5bg3so0fqzgqa.png" alt="Recipes table" width="621" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a friend expresses interest in the app, you decide that the app should allow for multiple users and those users can add recipes from the Recipes table to their individual accounts. How do you make that possible?  First, you will need a new User class &amp;amp; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9egfsh2nzyy6jd41to33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9egfsh2nzyy6jd41to33.png" alt="Users table" width="127" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next you will need away for those two tables to connect, this is where a JOIN table is needed, let's call it "user_recipes" and it will use the user_id &amp;amp; recipe_id to link recipes to the users. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqryqy4m1phha9l9m5vu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqryqy4m1phha9l9m5vu0.png" alt="UserRecipes table" width="185" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, you know have all of the tables you need to be able to associate Users and Recipes together. But you still need to define the relationships between them to allow Active Record to work its magic.&lt;/p&gt;

&lt;p&gt;Active Record has &lt;a href="https://guides.rubyonrails.org/association_basics.html#the-types-of-associations" rel="noopener noreferrer"&gt;built-in macros to make these associations&lt;/a&gt;, or table relationships: &lt;code&gt;belongs_to&lt;/code&gt;, &lt;code&gt;has_one&lt;/code&gt;, &lt;code&gt;has_many&lt;/code&gt;, &lt;code&gt;has_many through&lt;/code&gt;, &lt;code&gt;has_one through&lt;/code&gt;, &lt;code&gt;has_and_belongs_to_many&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my experience, &lt;code&gt;belongs_to&lt;/code&gt;, &lt;code&gt;has_many&lt;/code&gt;, &lt;code&gt;has_many through&lt;/code&gt; are most common and are the only ones needed for our simple cookbook app.&lt;/p&gt;

&lt;p&gt;When creating the table relationships, it helps to break down each class individually, for example:&lt;/p&gt;

&lt;p&gt;"A User can have many Recipes"&lt;/p&gt;

&lt;p&gt;"A Recipe can have many Users"&lt;/p&gt;

&lt;p&gt;We know now that the Users and Recipes tables need to have a many-to-many relationship. Now let's look at the JOIN table, the place where the user and recipe get associated together by their individual IDs. User 2 has Recipe 5 and 4, as shown on rows 2 &amp;amp; 3. Each row, or instance, of the UserRecipes table, class, is a one-to-one relationship&lt;/p&gt;

&lt;p&gt;To create these relationships, we will use these macros: &lt;code&gt;belongs_to&lt;/code&gt;, &lt;code&gt;has_many&lt;/code&gt;, &lt;code&gt;has_many through&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;"A User &lt;code&gt;has_many&lt;/code&gt; UserRecipes"&lt;br&gt;
"A UserRecipe &lt;code&gt;belongs_to&lt;/code&gt; a User"&lt;br&gt;
"A UserRecipe &lt;code&gt;belongs_to&lt;/code&gt; a Recipe"&lt;br&gt;
"A User &lt;code&gt;has_many&lt;/code&gt; Recipes &lt;code&gt;through&lt;/code&gt; UserRecipes"&lt;br&gt;
"A Recipe &lt;code&gt;has_many&lt;/code&gt; Users &lt;code&gt;through&lt;/code&gt; UserRecipes"&lt;/p&gt;

&lt;p&gt;These associations will allow a User to have access to their own specific Recipes as well as be able to get the corresponding data from the Recipes table. Otherwise, the return would just be the IDs from the JOIN table and NOT the recipe itself.&lt;/p&gt;

&lt;p&gt;This doesn't seem too complicated in the scope of our simple cookbook but can get very complicated very quickly as the application grows.  Thankfully there are tools to help you create diagrams and visualize all of these connections such as: &lt;a href="https://dbdiagram.io/home" rel="noopener noreferrer"&gt;dbdiagram&lt;/a&gt; and &lt;a href="https://www.figma.com/" rel="noopener noreferrer"&gt;Figma&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our app example, mapped out in dbdiagram would look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63ohtpwbstrzh0pyfb30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63ohtpwbstrzh0pyfb30.png" alt="dbdiagram example" width="664" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No matter what diagram tool you use, there is no question that they will help to make your database development easier.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Using a nested NavLink to conditionally render JSX</title>
      <dc:creator>Kevin Sassé</dc:creator>
      <pubDate>Fri, 28 Oct 2022 05:33:32 +0000</pubDate>
      <link>https://dev.to/sassek70/using-a-nested-navlink-to-conditionally-render-jsx-3e5p</link>
      <guid>https://dev.to/sassek70/using-a-nested-navlink-to-conditionally-render-jsx-3e5p</guid>
      <description>&lt;p&gt;I've been learning React.js for the past three weeks at Flatiron School and have spent this entire week working on my end-of-phase project. One of the core requirements for the project is that it uses React Router.&lt;/p&gt;

&lt;p&gt;Our single-page application displays a list of all the currently active NFL quarterbacks, as of 10/24/2022. The user can click on the player cards show/hide a player's stats as well as edit the stats and add players to a favorite list. We also included the ability to sort by different stats and a search feature.&lt;/p&gt;

&lt;p&gt;One of the issues we encountered was how to only show the Search and Sort features on specific endpoints. Initially we tried to set state equal to the endpoint of the link that was clicked and use a ternary to determine which JSX elements should be returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return (
        &amp;lt;&amp;gt;
            &amp;lt;nav className="link-container"&amp;gt;
                &amp;lt;NavLink className="linkButtons" to="/" onClick={handleLinkClick}&amp;gt;Home&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" to="/form" onClick={handleLinkClick}&amp;gt;New Player Form&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" to="/favorites" onClick={handleLinkClick}&amp;gt;Favorites List&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" to="/activelist" onClick={handleLinkClick}&amp;gt;Active List&amp;lt;/NavLink&amp;gt;
            &amp;lt;/nav&amp;gt;
            {pathName === "/form" || pathName === "/player/:id/EditForm" ? 
            null
            :
            &amp;lt;div className="search-sort"&amp;gt;
                &amp;lt;Search changeSearch ={changeSearch}                              
                changeSearchValue = {changeSearchValue}
                searchValue = {searchValue} /&amp;gt;
                &amp;lt;Sort changeSortBy = {changeSortBy}/&amp;gt;
            &amp;lt;/div&amp;gt;
            }
        &amp;lt;/&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This had limited success and worked on the form to add a new player but not the one to edit a player. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JXTu3iYN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1xcxzquxroq47enrg52.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JXTu3iYN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1xcxzquxroq47enrg52.PNG" alt="Image description" width="704" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The console showed the correct path names for most of the endpoints.  If state was being updated correctly, the final "/" should have been "/player/:id/EditForm". The link is still routing to the edit player form but appears as the home endpoint in state. We believe the cause of this issue was that the link to the edit form is nested within the player card component itself instead of the Header component like the new player form.&lt;/p&gt;

&lt;p&gt;We tried a few different ways to try and force the correct endpoint into state by passing callback functions through the various components, and did successfully get state to display the correct endpoint. Unfortunately this had issues with the ternary as the ":id" value wasn't getting passed into the Header component. This was overly complex and would only get more involved to get the ternary working properly. &lt;/p&gt;

&lt;p&gt;Sometimes you need to take a step back, and rethink your approach to a problem. We were able to solve this issue by using the &lt;code&gt;onClick&lt;/code&gt; event to set state to either "true" or "false" based on the name of the &lt;code&gt;&amp;lt;NavLink&amp;gt;&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;const handleLinkClick =(e) =&amp;gt; {
    e.preventDefault()
    const { name, pathname } = e.target
    if(name === "home" || name === "Favorite Players" || name === "Active Players") {
        setDisplayStatus(true)
    } else {
        setDisplayStatus(false)
    }
    changePageUrl(pathname)        
}

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

&lt;/div&gt;



&lt;p&gt;We also had to pass the &lt;code&gt;handleLinkClick&lt;/code&gt; function as a prop to the player card component to make it available to the edit form link on each card. We then were able to refactor our ternary to render the Search and Sort elements based on the state value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    return (
        &amp;lt;&amp;gt;
            &amp;lt;nav className="link-container"&amp;gt;
                &amp;lt;NavLink className="linkButtons" name="home" to="/" onClick={handleLinkClick}&amp;gt;Home&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" name="New Player Form" to="/form" onClick={handleLinkClick}&amp;gt;New Player Form&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" name="Favorite Players" to="/favorites" onClick={handleLinkClick}&amp;gt;Favorite Players&amp;lt;/NavLink&amp;gt;
                &amp;lt;NavLink className="linkButtons" name="Active Players" to="/activelist" onClick={handleLinkClick}&amp;gt;Active Players&amp;lt;/NavLink&amp;gt;
            &amp;lt;/nav&amp;gt;
            {displayStatus === false  ? 
            null
            :
            &amp;lt;div className="search-sort"&amp;gt;
                &amp;lt;Search changeSearch ={changeSearch}
                changeSearchValue = {changeSearchValue}
                searchValue = {searchValue} /&amp;gt;
                &amp;lt;Sort changeSortBy = {changeSortBy}/&amp;gt;
            &amp;lt;/div&amp;gt;
            }
        &amp;lt;/&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of the falsey endpoints is clicked, the &lt;code&gt;&amp;lt;Search/&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Sort/&amp;gt;&lt;/code&gt; components will not be displayed. This approach is much simpler than trying to conditionally render components using a nested endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sassek70/phase-2-project"&gt;Github link for our project&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>tutorial</category>
      <category>flatironschool</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A beginner's attempt at fetch() and a crash-course on browser cache</title>
      <dc:creator>Kevin Sassé</dc:creator>
      <pubDate>Fri, 07 Oct 2022 00:55:32 +0000</pubDate>
      <link>https://dev.to/sassek70/a-beginners-attempt-at-fetch-and-a-crash-course-on-browser-cache-4k08</link>
      <guid>https://dev.to/sassek70/a-beginners-attempt-at-fetch-and-a-crash-course-on-browser-cache-4k08</guid>
      <description>&lt;p&gt;Backstory: You changed your employment status to part-time so you can attend a full-time coding bootcamp. You spent a few weeks prior to the first day of class slowly going through the pre-course material just learning the basics like; "what even is a function?". All the while doing your best to make sure you don't overlook a topic. &lt;/p&gt;

&lt;p&gt;Three weeks ago, the bootcamp started. On day one, you're suddenly thrown into content overload. Gone is the slow methodical pace you had during the pre-course work. Someone just strapped a brick on the gas pedal, the bootcamp is moving along at full speed. You stumbled a bit on the first day trying to keep up but quickly found a rhythm.&lt;/p&gt;

&lt;p&gt;Today: Now here you are, three weeks later working on the end of phase project. A month ago you didn't even know what the DOM was and in a few days you will have completed your first single page application that uses an API and next week will be learning React.&lt;/p&gt;

&lt;p&gt;You could've kept things easier for self and created a db.json and used a json server to host it, but you wanted a challenge. You did the local server and db.json thing, and used an external public API for source data. You are feeling confident that you can handle two different APIs within this project and start coding. &lt;/p&gt;

&lt;p&gt;You make your html, css &amp;amp; js files, link them up and everything seems to be working locally. You remembered a tip from your instructor: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Set your endpoints to variables so you only have to change it one place if it ever changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With that in mind, you write out the fetch request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch(externalUrl)
.then(res =&amp;gt; res.json())
.then(data =&amp;gt; console.log(data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint is supposed to pull a random object from the database. Satisfied that it looks like what you saw in the lectures and labs, you open up the html file in a browser and hope you see some data... &lt;/p&gt;

&lt;p&gt;SUCCESS! You did it! An object appeared in your console from an external source! Three weeks ago those three lines of code meant absolutely nothing to you and now you can write code to communicate with a server!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ooLS8JP0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zylp0fu49z4q1512b5rv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ooLS8JP0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zylp0fu49z4q1512b5rv.png" alt="Image description" width="880" height="1100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You quickly add a button to your webpage to call this API and get a new object every time it is clicked and refresh the web page. You start clicking, seeing a new entry in the console log every time. It works!&lt;/p&gt;

&lt;p&gt;And then you see it. You actually &lt;em&gt;read&lt;/em&gt; the logged information. Its the same object. Every. Single. Time... Why? How? You scratch your head for a while trying to figure out what is happening. The API docs provided a demo of this endpoint and it returned a different object every time, why isn't that happening in your code?&lt;/p&gt;

&lt;p&gt;You try different browsers, you ask other students to try making a request to that endpoint on their machines, but the end result is always the same. Enter:&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Cache
&lt;/h2&gt;

&lt;p&gt;All browsers have a cache, most apps on your smart devices have one as well. Your device's CPU even has built-in layers of cache. What is it?  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cache is a high-speed read/write storage layer used to hold commonly accessed data. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is commonly used to help make a smoother user experience by reducing loading times. Instead of having to constantly make new requests to the same endpoint, the browser will cache the initial response, then reference the cache if the request is going to the same endpoint with the same parameters. It makes the assumption it will receive the same response in that scenario and will return the cached information instead.&lt;/p&gt;

&lt;p&gt;In your project, since the button is sending a request to the same endpoint without changing any parameters to get a new random object, the browser's network tab will show that the request targeted the cache instead because nothing about the request changed, however, it should have been a different response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5H2zOZoV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7v3g4udv4symhaqxcxi.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5H2zOZoV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7v3g4udv4symhaqxcxi.PNG" alt="Image description" width="682" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, when a request is made using the javascript fetch API, the cache header used is &lt;code&gt;"default"&lt;/code&gt;. Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;"default"&lt;/code&gt;: Fetch will inspect the HTTP cache on the way to the network. If there is a fresh response it will be used. If there is a stale response a conditional request will be created, and a normal request otherwise. It then updates the HTTP cache with the response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why every time you click that button to pull a random object, your browser is instead, retrieving the cached object from your initial request.&lt;/p&gt;

&lt;p&gt;While caching does make using the internet and many programs smoother and provide a better user experience, there may be cases in which you want to disable it. Say, for instance, your end of phase project where you want a different random object on every time. Fortunately for you, there is a simple solution to force a request to skip the cache and get a new a new response instead.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cache: "no-cache"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Setting the HTTP header &lt;code&gt;cache:&lt;/code&gt; to the value &lt;code&gt;"no-cache"&lt;/code&gt; in your fetch request will tell the browser to store the response in the cache after it is re-validated. This means you should get a fresh response each time you click the button and get a new random object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch(externalUrl,{
      cache: "no-cache"
})
.then(res =&amp;gt; res.json())
.then(data =&amp;gt; console.log(data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You then reload the webpage and click the button a few times to test it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wCRZ-fy8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlnkoiuwcbj1a21vw31a.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wCRZ-fy8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlnkoiuwcbj1a21vw31a.PNG" alt="Image description" width="698" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You did it! The request is getting a server response every time! Your first hurdle of the project is resolved. You know it won't be long before the next one shows up, but that doesn't slow you down. You signed up for this bootcamp to learn, and hurdles a good learning experiences.&lt;/p&gt;

&lt;p&gt;For further reading:&lt;br&gt;
&lt;a href="https://javascript.info/fetch-api"&gt;https://javascript.info/fetch-api&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.openbrewerydb.org/documentation"&gt;https://www.openbrewerydb.org/documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/31601167/fetch-api-cache-mode"&gt;https://stackoverflow.com/questions/31601167/fetch-api-cache-mode&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
  </channel>
</rss>
