<?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>Forem: Jason Park</title>
    <description>The latest articles on Forem by Jason Park (@jjpark987).</description>
    <link>https://forem.com/jjpark987</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%2F941871%2Fad8e03a2-77ee-4d24-9a0d-d872e5f19905.jpg</url>
      <title>Forem: Jason Park</title>
      <link>https://forem.com/jjpark987</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jjpark987"/>
    <language>en</language>
    <item>
      <title>Building a Gym Logging App</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Thu, 20 Mar 2025 20:44:31 +0000</pubDate>
      <link>https://forem.com/jjpark987/building-a-gym-logging-app-3c9c</link>
      <guid>https://forem.com/jjpark987/building-a-gym-logging-app-3c9c</guid>
      <description>&lt;p&gt;GymLogger was built to replace the manual process of tracking workouts in Google Sheets with a more visual and interactive experience. Instead of relying on spreadsheets, the app provides an easy way to schedule weekly exercises, log workouts, track sets, and see progress over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Structure
&lt;/h2&gt;

&lt;p&gt;GymLogger is built with React Native and Expo Go, designed to run on iOS without extra setup. It uses SQLite for local data storage, ensuring workouts are logged and accessible even without an internet connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workout Tab
&lt;/h3&gt;

&lt;p&gt;GymLogger is structured around a weekly workout routine, with four exercises scheduled for each of the five weekdays. When the app starts, it automatically detects the current day and displays the scheduled exercises for that day.&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%2F2664ax3bmgzpah36uodi.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%2F2664ax3bmgzpah36uodi.PNG" alt="View today's exercises" width="800" height="1731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each exercise consists of four sets of 10 reps, and progress is tracked based on total volume.&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%2Fw2e2ouj2v2ry41kht36z.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%2Fw2e2ouj2v2ry41kht36z.PNG" alt="Log reps for exercise" width="800" height="1731"&gt;&lt;/a&gt;&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%2Fmyol87pmzyetwwrsc60m.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%2Fmyol87pmzyetwwrsc60m.PNG" alt="Log reps for one-armed exercise" width="800" height="1731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all four sets are completed with 10 reps, the app automatically increases the weight for the next session, helping ensure steady progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schedule Tab
&lt;/h3&gt;

&lt;p&gt;The Schedule tab allows exercises to be created and updated for each day of the week. When adding a new exercise, the initial weight and increment value are set, ensuring automatic progression over time. This tab provides full control over workout planning, making it easy to adjust exercises as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  History Tab
&lt;/h3&gt;

&lt;p&gt;The History tab organizes past workout logs by week, then day, and finally by exercise for that day. This makes it easy to review progress over time. Logs can be edited or deleted directly from this tab, making it easy to adjust past entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;I initially planned to implement a background task that would automatically save workout data to SQLite at the end of each day between 10:59 PM and 11:59 PM local time. However, since Expo Go doesn’t support background tasks for iOS, and workarounds add costs, this became a challenge. For now, data is saved manually through user interactions, but future updates may explore alternative solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repo Link
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jjpark987/gymlogger" rel="noopener noreferrer"&gt;GymLogger&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactnative.dev/docs/getting-started" rel="noopener noreferrer"&gt;React Native documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.expo.dev/" rel="noopener noreferrer"&gt;Expo Go documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>typescript</category>
      <category>reactnative</category>
      <category>ios</category>
    </item>
    <item>
      <title>Building a Code Problem Solving Assistant</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Fri, 07 Mar 2025 22:13:00 +0000</pubDate>
      <link>https://forem.com/jjpark987/building-a-code-problem-solving-assistant-4b71</link>
      <guid>https://forem.com/jjpark987/building-a-code-problem-solving-assistant-4b71</guid>
      <description>&lt;p&gt;One of the hardest parts of solving coding problems, especially for beginners, is figuring out the right approach or algorithm. Once you understand how to break down the problem and select an algorithm, translating it into code becomes much more manageable. &lt;/p&gt;

&lt;p&gt;CodeScript is designed to help with this exact challenge. It provides structured problem analysis, suggests potential solution approaches, and assists in refining code implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;The app works by pulling a random coding problem from a database of LeetCode problems and displaying it to the user. Once the user submits their approach, the UI renders a response from the LLM, which analyzes the approach and provides feedback. This helps users validate their thought process before writing code, making it easier to refine their solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Structure
&lt;/h2&gt;

&lt;p&gt;This is a full-stack app using MySQL, FastAPI, and React. Once all components are completed, we combine everything into Docker Compose.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLM
&lt;/h3&gt;

&lt;p&gt;The app uses Ollama to run a quantized DeepSeek-Coder:6.7B model locally. The LLM is the most resource-intensive part of the system, as generating high-quality coding problem analysis requires a large model. Running such a model on local machines—especially those with limited RAM—can be challenging, making performance optimizations a key focus.&lt;/p&gt;

&lt;p&gt;Once the model is set up, we construct a structured prompt containing the problem details, user submission, and additional context. This prompt is sent to the Ollama server, which returns a response that we send back to the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_deepseek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submission&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parse_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;The backend provides two main API routes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GET /problems/random&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fetches a random problem from the database.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;POST /generate_feedback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sends the user's submission to the LLM for feedback.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;generate_feeback&lt;/code&gt; route integrates the DeepSeek-Coder:6.7B model through the &lt;code&gt;run_deepseek&lt;/code&gt; function. When a user submits their approach, the backend processes the request, passes the problem data and user input to the LLM, and returns a structured response.&lt;/p&gt;

&lt;p&gt;To ensure performance and prevent excessive requests, we implement a rate limiter (processing_limiter) and use a lock (request_lock) to manage concurrent LLM calls. The API also includes CORS middleware to allow secure frontend access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# routers.py
&lt;/span&gt;
&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@router.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/problems/random&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_random_problem_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_random_problem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;add_cors_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/generate_feedback&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LLMResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processing_limiter&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_feedback_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LLMRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LLMResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_deepseek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;problem_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_submission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LLMResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;request_lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problems_router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;The frontend is built using Vite with React, providing a fast and lightweight interface for users to interact with the app. When the app loads, it fetches a random problem from the backend using useEffect, ensuring a new problem is displayed on each visit.&lt;/p&gt;

&lt;p&gt;When the user submits their approach, the submit button is temporarily disabled to prevent multiple requests from being sent at once. This helps avoid overwhelming the backend and ensures smoother performance. The UI is designed to be simple and responsive, focusing on delivering the problem and feedback efficiently without unnecessary complexity.&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%2F6q7az2rwf3og1s7hzmc1.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%2F6q7az2rwf3og1s7hzmc1.png" alt="Figma Wireframe" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purple button toggles between viewing the current problem and the model's response, the yellow shuffle button goes to the next random problem, and the blue play button generates a response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;

&lt;p&gt;To simplify deployment and keep the app self-contained, we dockerize both the backend and frontend and use Docker Compose to run them together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The backend is packaged with FastAPI, its dependencies, and the LLM model. A Dockerfile ensures all necessary packages and configurations are set up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The frontend is also containerized with Vite and React, making it easy to serve the UI without needing additional setup.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using Docker Compose, we define both services in a docker-compose.yml file, allowing them to start together with a single command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codescript-backend&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOCKER_DATABASE_URL=${DOCKER_DATABASE_URL:-}&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codescript_network&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8000/"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codescript-frontend&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../codescript-frontend&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VITE_RANDOM_PROBLEM_URL=${VITE_RANDOM_PROBLEM_URL}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VITE_GENERATE_FEEDBACK_URL=${VITE_GENERATE_FEEDBACK_URL}&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codescript_network&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:80"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;codescript_network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repo Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jjpark987/codescript-backend" rel="noopener noreferrer"&gt;Backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jjpark987/codescript-frontend" rel="noopener noreferrer"&gt;Frontend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jjpark987/codescript-llm" rel="noopener noreferrer"&gt;LLM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ollama/ollama/tree/main" rel="noopener noreferrer"&gt;Ollama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/library/deepseek-coder:6.7b" rel="noopener noreferrer"&gt;DeepSeek-Coder:6.7B&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/reference/" rel="noopener noreferrer"&gt;Docker documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>ai</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Building a Lightweight OCR-Powered Receipt Parser</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Thu, 27 Feb 2025 18:49:38 +0000</pubDate>
      <link>https://forem.com/jjpark987/building-a-lightweight-ocr-powered-receipt-parser-1k21</link>
      <guid>https://forem.com/jjpark987/building-a-lightweight-ocr-powered-receipt-parser-1k21</guid>
      <description>&lt;p&gt;When working with scanned receipts, extracting structured data like dates, items, and prices can be tricky due to varying fonts, layouts, and image quality. ReceiptLogger is a lightweight, local-first application that utilizes PaddleOCR to process scanned receipts efficiently. By integrating PaddleOCR with a Tkinter GUI, the app extracts text from images, parses relevant details, and logs the data into a structured format like a Google Sheet. The goal is to make receipt digitization efficient, even on low-resource machines, ensuring quick and accurate text recognition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;ReceiptLogger runs on macOS (tested on an M1 Mac) with Python 3.12.9. It uses PaddleOCR version 2.9.1 for text extraction and Tcl/Tk version 8 for the GUI. Make sure these dependencies are installed before running the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Structure
&lt;/h2&gt;

&lt;p&gt;The app has two main components, the main script containing the Tkinter class for the GUI (main.py) and the helper script that extracts the data from the OCR response (process_data.py).&lt;/p&gt;

&lt;h3&gt;
  
  
  main.py
&lt;/h3&gt;

&lt;p&gt;main.py handles the core workflow of the app, from receiving images to extracting and storing data. It initializes a folder in Desktop to store uploaded receipt images, processes each image with PaddleOCR to extract text and relevant details, and finally uploads the structured data to Google Sheets for storage. Below are the two main parts of the class, the initialization of key variables and the creation of the UI components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReceiptLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize_variables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_ui&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_variables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ocr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaddleOCR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_angle_cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReceiptLogger&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipts_folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;~&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Desktop&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;🧾 RECEIPTS HERE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_refs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipt_data_refs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PaddleOCR is an open-source Optical Character Recognition (OCR) tool built on PaddlePaddle, a deep learning framework. It is designed for extracting text from images, supporting multiple languages and text orientations. In ReceiptLogger, PaddleOCR processes receipt images to extract structured data like store names, dates, and item details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_receipts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scroll_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;winfo_children&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_refs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipts_folder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;❌ Receipts folder not found&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;image_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipts_folder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipts_folder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; receipts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;img_path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;image_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;ocr_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                &lt;span class="n"&gt;receipt_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocr_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;❌ Error processing receipts: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receipt_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app uploads extracted receipt data to Google Sheets using the Google Sheets API. It authenticates with a service account, formats the extracted data into rows, and appends them to a specified worksheet. This allows easy access and organization of receipt records in a structured format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipt_data_refs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;⚠️ Extract receipts before uploading&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;📤 Uploading to Google Sheets...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# authenticate and prepare data to append
&lt;/span&gt;        &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GOOGLE_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sheet_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPREADSHEET_ID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;worksheet_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WORKSHEET_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;❌ Google Service Key not found in .env&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_service_account_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://www.googleapis.com/auth/spreadsheets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gspread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;open_by_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheet_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;worksheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worksheet_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ Successfully connected to Google Sheet: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ Connected to Google Sheets &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;rows_to_append&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipt_data_refs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%m/%d/%Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%m/%d/%y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%m/%d/%y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;tax_rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tax_rate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;item_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sku&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;item_map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;item_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
                            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;taxed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;taxed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;item_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

                &lt;span class="n"&gt;rows_to_append&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="n"&gt;tax_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;taxed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;item_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="c1"&gt;# append to google sheets
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows_to_append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;next_empty_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all_values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows_to_append&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;next_empty_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value_input_option&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USER_ENTERED&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ Successfully added &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows_to_append&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows to Google Sheets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ Uploaded &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows_to_append&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows to Google Sheets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;⚠️ No data to upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;⚠️ No data to upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;❌ Google Sheets connection error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;❌ Google Sheets authentication failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  process_data.py
&lt;/h3&gt;

&lt;p&gt;process_data.py extracts and processes structured receipt data from the OCR output. It identifies the store, extracts relevant details, and calculates tax for each item before returning the final structured data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;🚨 No data to extract&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;stores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;homegoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HomeGoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;marshalls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Marshalls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;marshalls homegoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Marshalls-HomeGoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ross&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ross&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t.j.maxx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;T.J.Maxx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tjmaxx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;T.J.Maxx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;possible_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;rf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\b&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;possible_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
                    &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HomeGoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Marshalls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Marshalls-HomeGoods&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;T.J.Maxx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;receipt_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_tjx_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;receipt_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ross&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;receipt_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_ross_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;receipt_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; 
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;🚨 Not included in the list of stores&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt_data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helper functions parse_tjx_receipt and parse_ross_receipt handle store-specific receipt formats. These functions are tailored to specific store receipt formats, extracting structured data like store names, dates, items, and prices based on each store’s unique layout. This ensures accurate parsing for supported stores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the App via Automator
&lt;/h2&gt;

&lt;p&gt;To package ReceiptLogger into a macOS app using Automator, create a new Application in Automator and add a Run Shell Script action. Inside the script, add the following three lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/ReceiptLogger
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
python &lt;span class="nt"&gt;-m&lt;/span&gt; app.main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the Automator workflow as an application, then place it in your Applications folder or on your desktop. Clicking it will launch ReceiptLogger without needing to open a terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/library/tkinter.html" rel="noopener noreferrer"&gt;Tkinter Documentation&lt;/a&gt; – Official Python Tkinter library documentation.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://paddlepaddle.github.io/PaddleOCR/latest/en/quick_start.html" rel="noopener noreferrer"&gt;PaddleOCR Quick Start Guide&lt;/a&gt; – Getting started with PaddleOCR for text extraction.&lt;/li&gt;
&lt;li&gt;You can find the full source code on GitHub &lt;a href="https://github.com/jjpark987/receiptlogger" rel="noopener noreferrer"&gt;ReceiptLogger&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>A Beginner's Guide to Open Source Contributions on GitHub</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Tue, 02 Jul 2024 23:06:42 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-open-source-contributions-on-github-4c3p</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-open-source-contributions-on-github-4c3p</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Preparation&lt;/li&gt;
&lt;li&gt;
Ways to Contribute

&lt;ul&gt;
&lt;li&gt;Raise an issue or make a suggestion&lt;/li&gt;
&lt;li&gt;Reproduce a reported bug&lt;/li&gt;
&lt;li&gt;Test a pull request&lt;/li&gt;
&lt;li&gt;Solve a reported bug&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;General Guide&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Being a developer isn’t all about creating cool solo projects. Some of the best repositories out there are &lt;strong&gt;open source&lt;/strong&gt;, meaning they are maintained and updated by other developers over time. Maybe you have heard of some of these: VS Code, Git, React, TensorFlow, Django, etc. These repositories have reached their status because developers come together to fix common bugs and issues.&lt;/p&gt;

&lt;p&gt;As a developer, it is crucial to know how to contribute to open source projects on GitHub. Not only does it reinforce our problem-solving skills, but it also allows aspiring developers to be a part of something greater. This blog will introduce how to make contributions to most open source projects on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;Before diving into making changes to any codebase, we must first select the appropriate repo. Selecting a repo as a first-time contributor can be daunting. For those absolute beginners, we could start with the &lt;a href="https://github.com/firstcontributions/first-contributions#first-contributions" rel="noopener noreferrer"&gt;first contributions&lt;/a&gt; tutorial. This repo provides step-by-step instructions on how to make your first contribution.&lt;/p&gt;

&lt;p&gt;Once you feel a bit more comfortable, we can look for real repos. There are some resources to help you find that right repo. They are listed here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goodfirstissues.com/" rel="noopener noreferrer"&gt;goodfirstissues.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goodfirstissue.dev/" rel="noopener noreferrer"&gt;goodfirstissue.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://up-for-grabs.net/#/" rel="noopener noreferrer"&gt;up-for-grabs.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/code52?WT.mc_id=-blog-scottha" rel="noopener noreferrer"&gt;Code 52&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More advanced folks may want to try different types of projects. A good starting point could be in (GitHub’s Explore)[&lt;a href="https://github.com/explore" rel="noopener noreferrer"&gt;https://github.com/explore&lt;/a&gt;]. Once you have found a repo you are interested in, we can dive into different ways to contribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ways to Contribute
&lt;/h2&gt;

&lt;p&gt;There are many ways to contribute to an open source repo. I will cover the most common ways here, listed in increasing order of difficulty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raise an issue or make a suggestion
&lt;/h3&gt;

&lt;p&gt;One of the easiest ways to contribute to an open source project is by raising an issue or making a suggestion. By identifying and documenting a problem or improvement idea in the project’s issue tracker, you help maintainers prioritize and address issues efficiently. When raising an issue, make sure to provide clear and detailed steps to reproduce the problem, along with any relevant context or error messages encountered. Similarly, when making a suggestion for improvement, articulate your rationale and propose practical solutions or enhancements that align with the project’s goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reproduce a reported bug
&lt;/h3&gt;

&lt;p&gt;When encountering a reported bug in an open source project, your ability to replicate the issue is crucial for developers to understand and resolve it effectively. Begin by following any steps or instructions provided in the bug report to reproduce the issue on your own system. This process involves documenting the exact steps taken, any specific configurations or environments required, and noting any error messages or unexpected behaviors encountered. By accurately reproducing the bug and providing detailed feedback, you assist developers in pinpointing the root cause and implementing a targeted fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test a pull request
&lt;/h3&gt;

&lt;p&gt;As developers propose changes to an open source project, they submit pull requests for review and integration. Testing a pull request involves evaluating the proposed changes to ensure they function as intended and do not introduce new issues or regressions. Begin by understanding the scope and purpose of the pull request, reviewing the associated code changes, and considering potential edge cases or scenarios not covered in the initial implementation. Execute relevant test cases, perform integration testing if applicable, and validate the overall impact of the changes on the project’s functionality and performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solve a reported bug
&lt;/h3&gt;

&lt;p&gt;Addressing a reported bug in an open source project involves identifying the root cause of the issue, proposing a solution, and implementing the necessary code changes to resolve it. Begin by analyzing the bug report, reproducing the issue if necessary, and investigating related code areas to understand the underlying cause. Once the problem is identified, devise a strategy to fix it while adhering to project coding standards and practices, typically found in Contributions.md. Implement the solution by modifying the codebase, writing new tests if applicable, and ensuring compatibility with existing functionalities. Collaborate with project maintainers and contributors to review the proposed fix, incorporate feedback, and verify the resolution through thorough testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  General Guide
&lt;/h2&gt;

&lt;p&gt;For all types of contributions, it is best practice, especially as a beginner, to be able to isolate any changes before submitting a change. Here are the basic steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fork Repo&lt;/strong&gt;: Once you have found a repo to work on, fork to create a copy of the repo to your GitHub. This can be done by clicking the fork button on the top of the repo home page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clone Repo&lt;/strong&gt;: Clone a copy of this to your local computer. This is done by going to a command line interface (such as Terminal on Mac) and running &lt;code&gt;git clone &amp;lt;SSH&amp;gt;&lt;/code&gt;. The &lt;code&gt;&amp;lt;SSH&amp;gt;&lt;/code&gt; can be found by clicking on code on your forked repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create New Branch&lt;/strong&gt;: Create a new branch and switch to it for your specific contribution using &lt;code&gt;git switch -c &amp;lt;name-of-new-branch&amp;gt;&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Raise an issue or make a suggestion&lt;/strong&gt;: This step may not be necessary if you’re simply raising an issue or making a suggestion without code changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce a reported bug&lt;/strong&gt;: Use this branch to document your steps and any finding while reproducing the bug&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test a pull request&lt;/strong&gt;: Use this branch to fetch and test the changes from the pull request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solve a reported bug&lt;/strong&gt;: Use this branch to develop your bug fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make Changes and Test&lt;/strong&gt;: Depending on the type of contribution:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce a reported bug&lt;/strong&gt;: Follow steps provided in the bug report and document findings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test a pull request&lt;/strong&gt;: Run the relevant tests and verify the changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solve a reported bug&lt;/strong&gt;: Implement your fix and test it thoroughly to ensure it works as intended.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit Changes&lt;/strong&gt;: Add and commit the changes for submission. This is done by running &lt;code&gt;git add .&lt;/code&gt; followed by &lt;code&gt;git commit -m “enter commit message here”&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push Changes&lt;/strong&gt;: Run &lt;code&gt;git push&lt;/code&gt; to finalize the save into GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Pull Request&lt;/strong&gt;: Navigate to “Pull Requests” on your forked repo and click on “New pull request”. Provide a concise explanation of your changes and how they address the chosen issue. If needed, reference the issue number in the description.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once submitted, project maintainers will review the pull request.&lt;/p&gt;

&lt;p&gt;And that’s it. You have now completed your first open source contribution!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Contributing to open source projects is a rewarding way to improve your skills, collaborate with other developers, and make a meaningful impact on software used by people worldwide. Whether you start by raising an issue, testing a pull request, or solving a bug, every contribution helps strengthen the project and its community. By following the steps outlined in this guide, you’ll be well on your way to making your first open source contribution. Happy coding!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn how to make open source contributions. Please read the official documentation linked below for more information. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github" rel="noopener noreferrer"&gt;GitHub’s Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hanselman.com/blog/get-involved-in-open-source-today-how-to-contribute-a-patch-to-a-github-hosted-open-source-project-like-code-52" rel="noopener noreferrer"&gt;Blog: Get involved in Open Source today - How to contribute a patch to a GitHub hosted Open Source project like Code 52&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/firstcontributions/first-contributions#first-contributions" rel="noopener noreferrer"&gt;first contributions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goodfirstissues.com/" rel="noopener noreferrer"&gt;goodfirstissues.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goodfirstissue.dev/" rel="noopener noreferrer"&gt;goodfirstissue.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://up-for-grabs.net/#/" rel="noopener noreferrer"&gt;up-for-grabs.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/code52?WT.mc_id=-blog-scottha" rel="noopener noreferrer"&gt;Code 52&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A Beginner’s Guide to App Deployment via PaaS Provider</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Tue, 20 Feb 2024 03:03:34 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-app-deployment-via-paas-provider-4g2e</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-app-deployment-via-paas-provider-4g2e</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Basics&lt;/li&gt;
&lt;li&gt;
Deployment method

&lt;ul&gt;
&lt;li&gt;App preparation&lt;/li&gt;
&lt;li&gt;Deploying&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Backing and restoring the production database&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You’ve spent the last month finishing up your very cool application and now you want to show it off to the world. You try to remember the lecture video on the topic a few months ago but fail to remember the details. Then you look online and come across a blog with the title: A Beginner’s Guide to App Deployment via Paas Provider. Sounds strangely close to exactly what you are looking for.&lt;/p&gt;

&lt;p&gt;There are two types of cloud computing services, Platform as a Service (PaaS) and Infrastructure as a Service (IaaS). PaaS simplifies and abstracts away the infrastructure complexity, making it easier to deploy and manage applications. Because of this, it lets developers focus more on coding. On the other hand, IaaS offers more flexibility over the infrastructure components and allows for customization to meet specific requirements. This blog will go over the basics and step-by-step protocol on how to deploy a React-Rails-PostgreSQL app using a PaaS provider, &lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt;. Other popular PaaS providers are &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;, &lt;a href="https://cloud.google.com/appengine" rel="noopener noreferrer"&gt;Google App Engine&lt;/a&gt;, and &lt;a href="https://azure.microsoft.com/en-us/" rel="noopener noreferrer"&gt;Microsoft Azure App Service&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;When creating our app for production, we undergo several environments depending on the stage of development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;development&lt;/strong&gt; environment is when working on the app locally and running code on your personal computer. We are able to use debugging tools like byebug and code analysis tools like ESLint.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;test&lt;/strong&gt; environment can be utilized when running test runners like RSpec or Jest. The goal is for the code to run as quickly as possible so we can get rapid feedback. Like in the development environment, we are able to use debugging tools when needed.&lt;/p&gt;

&lt;p&gt;When we are ready for deployment, we undergo the &lt;strong&gt;staging&lt;/strong&gt; environment. This environment is a replica of the production environment and is used to perform final testing before deploying changes to the live app. We can catch any issues that may have been missed in the testing environment. &lt;/p&gt;

&lt;p&gt;Finally we have the &lt;strong&gt;production&lt;/strong&gt; environment. This is post-deployment where the app is accessed by end users. This live environment has to be stable, secure, and highly available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The staging environment is used after deployment to test the app in a more production-like environment and to serve to end users. Because the steps for staging are generally similar to those of production, we will skip staging for simplicity.&lt;/p&gt;

&lt;p&gt;Before diving into the method, we need to think about cost. Although there are free options for many PaaS providers, they are only suitable for smaller and less complex apps. Most free options limit the app in terms of usage so apps that require more resources would probably have to think about the paid plan. Some free plans may also spin-down an app that has been inactive for some time, meaning it will take a minute or so starting back up from this phase. Other things to think about are scalability and performance. This is asking yourself, “how large do I expect this app to be?” and “will this app be performing at the level I want it to for the traffic it will receive?”. Again, using the free plan will limit these things to a significant degree so apps that require more will have to upgrade to the paid plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment method
&lt;/h2&gt;

&lt;p&gt;Assuming we have an app ready for production, the first thing we need to do (if not done already), is to link to a remote GitHub repo for the app. Normally, we should already have this during the development stage, but if not, here is a quick rundown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a repo on GitHub&lt;/li&gt;
&lt;li&gt;Connect local repo to remote repo with &lt;code&gt;git remote add origin &amp;lt;SSH&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add, commit, and push code to GitHub with &lt;code&gt;git push -u origin main&lt;/code&gt; on the first commit&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  App preparation
&lt;/h3&gt;

&lt;p&gt;We need to update some of our configuration files. Let’s update database.yml to make sure that we properly set up our production database. If we are using Render, we can connect via &lt;code&gt;url: &amp;lt;%= ENV[‘DATABASE_URL’] %&amp;gt;&lt;/code&gt; using the external database url. For now, we can just set up the url key and leave it alone until we have our database ready.&lt;/p&gt;

&lt;p&gt;Next we need to update config/puma.rb and config/environments/production.rb.&lt;br&gt;
&lt;/p&gt;

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

workers ENV.fetch("WEB_CONCURRENCY") { 4 }
preload_app!
&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;# config/environments/production.rb

config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? || ENV['RENDER'].present?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everytime we deploy the app, we want a script to run that helps build our frontend and backend code. We will create this script file as bin/render-build.sh.&lt;br&gt;
&lt;/p&gt;

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

echo 'Running render-build.sh...'
# exit on error
set -e

# verbose mode
set -x

# builds the front end code
rm -rf public
npm install --prefix client &amp;amp;&amp;amp; npm run build --prefix client
cp -a client/build/. public/

# builds the back end code
bundle install
bundle exec rake db:migrate
bundle exec rake db:seed

echo '...finished running render-build.sh'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple things to note here. The first line &lt;code&gt;#!/usr/bin/env bash&lt;/code&gt; is the shebang line that instructs how this script file should be read. We set the exit on error and verbose mode to help us check the progress of the script as it runs. To build our frontend, we remove the public folder, install dependencies, build our production version, and save that build in our empty public folder. Our backend consists of installing the gems, running the migrations, and seeding the database if it is our first deployment. After our first deployment, we would comment out the &lt;code&gt;bundle exec rake db:seed&lt;/code&gt; to avoid duplicate records.&lt;/p&gt;

&lt;p&gt;Once we have our script file ready, we want to give it permission to make sure it is executable. On the terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ chmod a+x bin/render-build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pressing enter on this won’t show any response. To check if the file executable, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls -l bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are three x’s in the permission string, we are good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying
&lt;/h2&gt;

&lt;p&gt;Go to render.com and create a PostgreSQL instance as our production database. Make sure that the PostgreSQL version is the same as the version used in development. Create the database and wait for it to be available. In the meanwhile, start a new tab and go back to render.com. This time we want to create a web service. After filling out the requested information, go down to where it says Build Command. Here we want to run our script from above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./bin/render-build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Start Command should run our puma.rb file, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec puma -C config/puma.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to connect our web service to our local app and our newly created PostgreSQL production database. Remember, to set the Ruby version to that of the app.&lt;/p&gt;

&lt;p&gt;To connect to our local app, set the key to &lt;code&gt;RAILS_MASTER_KEY&lt;/code&gt; and the value to the secret file master.key in your app. If there is no master.key, we can create one using these steps.&lt;/p&gt;

&lt;p&gt;Delete config/credentials.yml.enc&lt;br&gt;
Run this code in the terminal to create a new master.key and credentials.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EDITOR="code --wait" bin/rails credentials:edit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect to our production database, set another key to &lt;code&gt;DATABASE_URL&lt;/code&gt; and the value to the internal database url. On our first tab with the Render database, go to connections to find this url.&lt;/p&gt;

&lt;p&gt;That’s it. You have now deployed your first web app along with its database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backing and restoring the production database
&lt;/h2&gt;

&lt;p&gt;If you end up using the free version, keep it in mind that on Render, these instances will only be available for a few months. When nearing the end of its life, we need to consider backing and restoring the databases if we want to keep our app alive. &lt;/p&gt;

&lt;p&gt;Backing up the database requires us to modify the PSQL connection string (PSQL Command) to run PSQL’s &lt;code&gt;pg_dump&lt;/code&gt;. This creates a .sql file of the database. Below is a template of what this string should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PGPASSWORD=your_password pg_dump -h your_host -U your_username --format=custom --no-acl --no-owner your_database &amp;gt; your_database_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace&lt;br&gt;
&lt;code&gt;your_password&lt;/code&gt; with the actual PostgreSQL password shown on Render&lt;br&gt;
&lt;code&gt;your_host&lt;/code&gt; with the actual PostgreSQL host shown on Render&lt;br&gt;
&lt;code&gt;your_username&lt;/code&gt; with the actual PostgreSQL username shown on Render&lt;br&gt;
&lt;code&gt;your_databse&lt;/code&gt; with the actual PostgreSQL database name&lt;br&gt;
&lt;code&gt;your_databse_backup.sql&lt;/code&gt; with whatever name you want for the backup file&lt;/p&gt;

&lt;p&gt;Copy and paste the PGPASSWORD into the terminal in a directory without a git repo, since we don't want this backup file to get pushed to GitHub.&lt;/p&gt;

&lt;p&gt;After creating our backup file, we can now delete the expiring PostgreSQL instance and create a new one, along with the new production database. We can create the database using psql or pgAdmin. Finally, we need to modify the PSQL connection string once more, but this time to run PSQL’s &lt;code&gt;pg_restore&lt;/code&gt;. Below is the template once more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PGPASSWORD=your_password pg_restore -h your_host -U your_username --verbose --clean --no-acl --no-owner -d your_database your_database_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace&lt;br&gt;
&lt;code&gt;your_password&lt;/code&gt; with the new PostgreSQL password shown on Render&lt;br&gt;
&lt;code&gt;your_host&lt;/code&gt; with the new PostgreSQL host shown on Render&lt;br&gt;
&lt;code&gt;your_username&lt;/code&gt; with the new PostgreSQL username shown on Render&lt;br&gt;
&lt;code&gt;your_databse&lt;/code&gt; with the new PostgreSQL database name&lt;br&gt;
&lt;code&gt;your_databse_backup.sql&lt;/code&gt; with whatever named your backup file in the previous step&lt;/p&gt;

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

&lt;p&gt;You now know how to deploy your app using a PaaS provider. To review, the steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect to a repo GitHub&lt;/li&gt;
&lt;li&gt;Update database.yml, puma.rb, and production.rb&lt;/li&gt;
&lt;li&gt;Create our build script, render-build.sh and make it executable&lt;/li&gt;
&lt;li&gt;Create the PostgreSQL server for our production database on Render&lt;/li&gt;
&lt;li&gt;Create the web service on Render&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Optional:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a backup file using a modified PSQL command to run pg_dump&lt;/li&gt;
&lt;li&gt;Restore a database through the backup file using a modified PSQL command to run pg_restore&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn how to deploy apps. Please read the official documentation linked below for more information. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.render.com/" rel="noopener noreferrer"&gt;Render Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Beginner's Guide to Redux</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Mon, 18 Dec 2023 20:09:48 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-redux-2cfg</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-redux-2cfg</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Basics&lt;/li&gt;
&lt;li&gt;Implementing with React&lt;/li&gt;
&lt;li&gt;File Organization&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Frontend state management is an unavoidable concept when creating larger web applications. While there are multiple ways to approach this, one tried and true method is by using a predictable, centralized, and flexible tool called Redux. In React, there are other methods such as the &lt;code&gt;useContext&lt;/code&gt; hook that may be easier to set up, yet many developers choose to work with Redux due to its ability to handle complex state interactions in larger applications more effectively. This guide will go over the basics of Redux and how it can be easily implemented in a typical React application using the Redux toolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;Developers use Redux to avoid prop drilling and work with a single source of truth for the application state. Before we go over how to use Redux, we must go over some of the key fundamentals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;store&lt;/code&gt; is the single source of truth that holds the entire state of the application. State represents the current data and the way the application behaves at a given moment. Components or other parts of the application can subscribe to the Redux store to receive notifications about state changes.&lt;/p&gt;

&lt;p&gt;All applications using Redux must have &lt;code&gt;reducer&lt;/code&gt; functions. These are pure functions that define how the state should change based on the current state and a particular action. There can be multiple &lt;code&gt;reducers&lt;/code&gt; for a particular state. When working with React, the &lt;code&gt;reducers&lt;/code&gt; are defined in a separate file called a &lt;code&gt;slice&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An action is an object describing how to change state. Developers often use action creator functions to generate these action objects. This becomes particularly useful when there are more complex actions or when we want to abstract away the details of action creation, especially in cases where we might have asynchronous operations in the action creation process.&lt;/p&gt;

&lt;p&gt;In order to call for these changes, &lt;code&gt;dispatch&lt;/code&gt; functions are used. A &lt;code&gt;dispatch&lt;/code&gt; function takes in an action as an argument that provides instructions on how to change the state.&lt;/p&gt;

&lt;p&gt;The state is typically immutable. This means that you don't directly modify the existing state; instead, you create a new state object when changes are needed. This helps in tracking and debugging state changes more effectively.&lt;/p&gt;

&lt;p&gt;Redux allows the use of middleware, which are functions that can intercept and augment the standard behavior of dispatch. Middleware is often used for tasks like logging actions, handling asynchronous operations, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simple restaurant analogy can help us understand the Redux system. In this analogy, the &lt;code&gt;store&lt;/code&gt; is the restaurant menu, the &lt;code&gt;dispatch&lt;/code&gt; function is the act of placing an order, the &lt;code&gt;reducer&lt;/code&gt; function is the kitchen, and the resulting state is the meal. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;store&lt;/code&gt; displays all the items available for ordering. Just as the menu is a central place for all the food options, the &lt;code&gt;store&lt;/code&gt; is a central palace for all the application’s state. When we want to order food (make changes to state), we tell the waiter what we want (dispatching an action) and the kitchen (reducer) processes the request to prepare the dish (update the state). The kitchen follows a recipe (reducer logic) to cook a new dish (updated state). Because the state is immutable, the kitchen doesn't modify the dish, but creates a new one.&lt;/p&gt;

&lt;p&gt;Here is a simple example of a Redux set-up in vanilla Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Action
const incrementAction = { type: 'INCREMENT' };

// Reducer
const counterReducer = (state = 0, action) =&amp;gt; {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

// Redux Store
const store = createStore(counterReducer);

// Dispatching an action to update the state
store.dispatch(incrementAction);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing with React
&lt;/h2&gt;

&lt;p&gt;Redux can be implemented in React applications easily through the help of some dependencies, namely &lt;code&gt;redux&lt;/code&gt;, &lt;code&gt;react-redux&lt;/code&gt;, and &lt;code&gt;@reduxjs/toolkit&lt;/code&gt;. Here is a step by step protocol on how we can do this.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;store.js&lt;/code&gt;. Here we import &lt;code&gt;configureStore&lt;/code&gt; from &lt;code&gt;@reduxjs/toolkit&lt;/code&gt; and any &lt;code&gt;reducers&lt;/code&gt; we want to use. &lt;code&gt;configureStore&lt;/code&gt; is a method that helps us consolidate all our &lt;code&gt;reducer&lt;/code&gt; functions. In the example below, we have our store defined and added one &lt;code&gt;reducer&lt;/code&gt; called &lt;code&gt;counterReducer&lt;/code&gt;, which is defined in a separate file &lt;code&gt;./features/counter/counterSlice&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;// store.js
import { configureStore } from "@reduxjs/toolkit"
import counterReducer from "./features/counter/counterSlice"

const store = configureStore({
    reducer: {
        counter: counterReducer
    }
})

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

&lt;/div&gt;



&lt;p&gt;To make this state available, we provide access by wrapping the application with a special context &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt; component in &lt;code&gt;index.js&lt;/code&gt;, similar to the &lt;code&gt;BrowserRouter&lt;/code&gt; component for React router. The &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt; component has an attribute of &lt;code&gt;store={store}&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;// index.js
import React from "react"
import ReactDOM from "react-dom"
import { Provider } from "react-redux"
import store from "./store"
import App from "./App"

ReactDOM.render(
    &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;App /&amp;gt;
    &amp;lt;/Provider&amp;gt;, 
    document.getElementById("root")
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we set up our &lt;code&gt;store&lt;/code&gt; and gave our application access to it, we need to define the initial state, reducers, and action creators, which are coded in a separate file. We extract &lt;code&gt;createSlice&lt;/code&gt; and pass an object with keys of &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;initialState&lt;/code&gt;, and &lt;code&gt;reducers&lt;/code&gt;. It is imperative that we export all the action creators and the reducer itself at the bottom of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// counterSlice.js
import { createSlice } from "@reduxjs/toolkit";

// slice
const slice = createSlice({
    name: 'counter',
    initialState: {
        count: 0,
        running: true
    },
    reducers: {
        increment: state =&amp;gt; {
            state.count += 1
        },
        decrement: state =&amp;gt; {
            state.count -= 1
        },
        toggleRunning: state =&amp;gt; {
            state.running = !state.running
        },
        incrementBy: (state, action) =&amp;gt; {
            state.count += action.payload
        }
    }
})


export const { increment, decrement, toggleRunning, incrementBy } = slice.actions
export default slice.reducer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, all we have left is to use the &lt;code&gt;dispatch&lt;/code&gt; function in the components that need it by importing &lt;code&gt;useDispatch&lt;/code&gt; from &lt;code&gt;react-redux&lt;/code&gt;. Remember to also import the action creators from our slice file as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Controls.js
import React from "react"
import { useDispatch, useSelector } from "react-redux"
import { decrement, incrementBy, toggleRunning } from "./counterSlice"

function Controls() {
    const dispatch = useDispatch()
    const running = useSelector(state =&amp;gt; state.running)

    function handleMinusClick() {
        dispatch(decrement())
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;button id="minus" onClick={handleMinusClick}&amp;gt;-&amp;lt;/button&amp;gt;
            &amp;lt;button id="plus" onClick={() =&amp;gt; dispatch(incrementBy(10))}&amp;gt;+&amp;lt;/button&amp;gt;
            &amp;lt;button id="play" onClick={() =&amp;gt; dispatch(toggleRunning())}&amp;gt;{running ? "⏸" : "▶️"}&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

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

&lt;/div&gt;



&lt;p&gt;In our example above, we have the &lt;code&gt;onClick&lt;/code&gt; event listeners defined for each button. Within the event listeners, we call our &lt;code&gt;dispatch&lt;/code&gt; function while passing our action creator as the argument.&lt;/p&gt;

&lt;p&gt;So now we know how to update state, but how can we access the state to use in our components? In the provided example, we utilize the useSelector hook from react-redux to access and utilize the state in our components. Specifically, we set the variable running using useSelector, and it corresponds to the running key in the initial state object defined in our counterSlice. As per the counterSlice example, the initialState object includes a key running set to true. This mechanism allows us to effortlessly access and utilize state variables throughout our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Organization
&lt;/h2&gt;

&lt;p&gt;Organizing files in a Redux and React application is crucial for maintainability and scalability. A common practice is to structure the project based on features or modules. Each feature typically has its own directory containing related components, actions, reducers, and selectors. For instance, a "counter" feature might have files such as Counter.js for the component, counterSlice.js for the Redux slice (including actions and reducer), and possibly Counter.css for styles. This modular organization promotes encapsulation and makes it easier to locate and update code associated with a specific feature. Additionally, there is often a centralized store directory that houses the Redux store configuration, combining all slices into the root reducer. To enhance readability, consider using subdirectories within features for further categorization. Overall, a well-organized file structure not only improves code maintainability but also facilitates collaboration and ensures a logical separation of concerns in your Redux and React application.&lt;/p&gt;

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

&lt;p&gt;This beginner's guide has provided the basics introducing Redux and its integration with React, offering a robust state management solution for larger web applications. By understanding the fundamental concepts of the Redux architecture, such as the store, reducers, actions, and the dispatch function, developers gain a powerful tool to manage and update application state effectively. By adopting Redux, developers can streamline state management, simplify complex interactions, and build more resilient and scalable React applications.&lt;/p&gt;

&lt;p&gt;If you're eager to delve deeper into Redux, refer to the official Redux documentation for comprehensive resources and guidance on advanced topics. Happy coding!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn Redux in React. Please read the official documentation linked below for more information. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>redux</category>
      <category>react</category>
    </item>
    <item>
      <title>A Beginner’s Guide to Authentication and Authorization in Rails</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Fri, 01 Sep 2023 18:56:08 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-authentication-and-authorization-in-rails-4n2l</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-authentication-and-authorization-in-rails-4n2l</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Authentication vs. Authorization&lt;/li&gt;
&lt;li&gt;Cookies and Sessions&lt;/li&gt;
&lt;li&gt;BCrypt&lt;/li&gt;
&lt;li&gt;Signing Up&lt;/li&gt;
&lt;li&gt;Logging In&lt;/li&gt;
&lt;li&gt;Auto Login&lt;/li&gt;
&lt;li&gt;Logging Out&lt;/li&gt;
&lt;li&gt;Authorizing&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As we expand our app development knowledge, let's dive into the world of authentication and authorization in Ruby on Rails. In this guide, we'll cover the essential concepts and tools you need to build secure web applications. Authentication and authorization are fundamental to ensuring the right users access the right resources. We'll explore topics like cookies and sessions, BCrypt for secure password handling, user sign-up and logIn processes, auto-login mechanisms, and user logout procedures with examples. Plus, we'll delve into the art of authorizing actions to secure the backend server from unwanted requests. Let's get started on this crucial aspect of web app development!&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication vs. Authorization
&lt;/h2&gt;

&lt;p&gt;First we need to define the differences between authentication and authorization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Authentication is the process of verifying the identity of the user. In other words, we are checking to make sure the users are who they say they are.&lt;/p&gt;

&lt;p&gt;Authorization is the process of allowing certain users to gain access to certain features in the app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, authentication answers the question of “who are you?” while authorization determines “what are you allowed to do?” The security of any web application lies in its ability to authenticate and authorize users effectively, safeguard sensitive data, prevent unauthorized access, and continuously adapt to emerging threats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cookies and Sessions
&lt;/h2&gt;

&lt;p&gt;While there are many ways to authenticate users, we will be exploring how to achieve this via cookies and sessions. Cookies and sessions are fundamental tools in web development for maintaining user state and enhancing security. They allow us to keep track of user data and interactions throughout their visit to our application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cookies are small pieces of data that a web server sends to a user’s browser (server to client). These data packages are domain-specific and stored on the user’s device after the user has visited a website or a web application so that on subsequent visits, the server quickly knows who the client is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cookies serve various purposes including storing information such as user authentication tokens, shopping cart contents, or user preferences. They are stored on the client-side in the browser and sent back to the server with each HTTP request, allowing the server to recognize and remember the user. In the context of authentication, cookies often store user session information to keep a user logged in across multiple interactions with a web application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sessions are a server-side mechanism for maintaining user state. Unlike cookies, which are stored on the user’s device, sessions are stored on the server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a user interacts with a web application, a unique session identifier is typically stored in a cookie on the user's device. This session identifier allows the server to associate subsequent requests from the same user with their session data stored on the server. In the context of authentication and authorization, user session data can include information like the user's ID, which are used to determine what actions the user is allowed to perform within the application.&lt;/p&gt;

&lt;p&gt;Here is how this works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User visits a website/application and logs in.&lt;/li&gt;
&lt;li&gt;Server generates a session on the server-side and sends a cookie containing a session identifier to the client's browser.&lt;/li&gt;
&lt;li&gt;The client's browser stores this cookie.&lt;/li&gt;
&lt;li&gt;On subsequent requests, the client's browser automatically sends the stored cookie with the session identifier back to the server.&lt;/li&gt;
&lt;li&gt;The server uses this session identifier to retrieve the user's data from its session storage and identifies the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to enable cookies and sessions in our Rails application, there are some things we need to set up.&lt;/p&gt;

&lt;p&gt;In our config/application.rb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module MyApp
    class Application &amp;lt; Rails::Application
        # Add cookies and session middleware
        config.middleware.use ActionDispatch::Cookies
        config.middleware.use ActionDispatch::Session::CookieStore

        # Use SameSite=Strict for all cookies to help protect against CSRF
        config.action_dispatch.cookies_same_site_protection = :strict

        # Initialize configuration defaults for originally generated Rails version.
        config.load_defaults 6.1

        # Only loads a smaller set of middleware suitable for API only apps.
        # Middleware like session, flash, cookies can be added back manually.
        # Skip views, helpers and assets when generating a new resource.
        config.api_only = true
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our app/controllers/application_controller.rb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ApplicationController &amp;lt; ActionController::API
    include ActionController::Cookies
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once these files are properly set up, we can use cookies and sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  BCrypt
&lt;/h2&gt;

&lt;p&gt;When a user first signs up, the server must store that information in their databases. However, one very important concept to keep in mind is that &lt;strong&gt;passwords are never saved as a plain text in any databases due to security risks&lt;/strong&gt;. A standard method of storing passwords is through encryption via salting &amp;amp; hashing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Salting is the process used to enhance the security of password storage. This process involves a random and unique piece of data called “salt”, which is generated and combined with every password before applying the hash function. This ensures that two users with the same password will have different values due to the unique salts.&lt;/p&gt;

&lt;p&gt;Hashing is a one-way function that transforms the password into a fixed-length string of characters called “hash”. After a password has been salted, it undergoes the hashing process. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The primary characteristic of a good cryptographic hash function is that it's irreversible, meaning you can't reverse the process to retrieve the original password. Even a small change in the input data should produce a significantly different hash. In the context of password security, the user's password is salted and hashed and then stored in the database. When a user attempts to log in, their entered password is hashed again, and the resulting hash is compared to the stored hash in the database. If they match, the login attempt is successful.&lt;/p&gt;

&lt;p&gt;Here is how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We have two users John and Jane with the same password, “Password123”.&lt;/li&gt;
&lt;li&gt;When they sign up, they are given two unique salts: John gets “sd9f6” and Jane gets “3x41p”.&lt;/li&gt;
&lt;li&gt;The salting process turns John’s password into “Password123sd9f6” and Jane’s into “Password1233x41p”.&lt;/li&gt;
&lt;li&gt;The hashing process via a secure hash function turns John’s salted password into “d3f4e5g6h7i8j9k0” and Jane’s into “m1n2o3p4q5r6s7t8”.&lt;/li&gt;
&lt;li&gt;The server stores John’s salt “sd9f6” and hashed password “d3f4e5g6h7i8j9k0” and Jane’s salt “3x41p” and hashed password “m1n2o3p4q5r6s7t8”.&lt;/li&gt;
&lt;li&gt;When John or Jane logs in, the salt and hash function converts their password input into the hashed version. If the hashed passwords match, they are successfully logged in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To simplify this process as web developers, we can use a Ruby gem, BCrypt. BCrypt provides the &lt;code&gt;has_secure_password&lt;/code&gt; method for the User model. This method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically salts and hashes user passwords&lt;/li&gt;
&lt;li&gt;Automatically adds a &lt;code&gt;password_confirmation&lt;/code&gt; attribute to the model (used for signing up and updating passwords).&lt;/li&gt;
&lt;li&gt;Automatically adds validations for password-related inputs, such as its presence and any other specified requirements (minimum length, required characters, etc)&lt;/li&gt;
&lt;li&gt;Provides the &lt;code&gt;authenticate&lt;/code&gt; method for the Sessions controller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these features can be utilized after including the &lt;code&gt;has_secure_password&lt;/code&gt; method in the User model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ApplicationRecord
    has_secure_password
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last thing we need to do before we move on is to add the &lt;code&gt;password_digest&lt;/code&gt; attribute as a string to our User model. This is where our salted and hashed passwords are stored. Once we include this attribute, BCrypt will automatically manage the passwords in a secure way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signing Up
&lt;/h2&gt;

&lt;p&gt;To set up the sign-up logic in Rails, we first need to create the appropriate route in config/routes.rb.&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
    # signing up
    post '/signup', to: 'users#create'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are creating a new User instance, we need to have the route point to the &lt;code&gt;users#create&lt;/code&gt; controller action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UsersController &amp;lt; ApplicationController
    # post '/signup'
    def create
        render json: User.create!(user_params), status: :created
    rescue ActiveRecord::RecordInvalid =&amp;gt; e
        render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
end

    private

    def user_params
        params.require(:user).permit(:username, :password, :password_confirmation)
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;users#create&lt;/code&gt; action, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new instance of the User model and save it to our database using the sister method &lt;code&gt;create!()&lt;/code&gt;. This allows for Rails to catch any exceptions raised by our &lt;code&gt;rescue&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;Utilize strong parameters, which are defined under the private methods. We make sure to include both the &lt;code&gt;:password&lt;/code&gt; and &lt;code&gt;:password_confirmation&lt;/code&gt; attributes in the permitted params.&lt;/li&gt;
&lt;li&gt;Make sure to include the &lt;code&gt;status: :created&lt;/code&gt; to let the browser know that the User was successfully created.&lt;/li&gt;
&lt;li&gt;Include the &lt;code&gt;rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;/code&gt; to catch any invalid records. We render this error message in a hash and set the &lt;code&gt;status: :unprocessable_entity&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we want to have the user automatically log in after creating a new account, we need to set up the login mechanism in our backend and create a new session after the frontend fetch post request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging In
&lt;/h2&gt;

&lt;p&gt;As with the sign up process, we first need to make a new route for this controller action.&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
    # signing up
    post '/signup', to: 'users#create'
    # logging in
    post '/login', to: 'sessions#create'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, we point to our sessions controller. Remember, that when users log in, they must request for the server to create a new session that they can store server-side and refer to when they receive the cookie with the session identifier. For this reason, our controller action will be &lt;code&gt;sessions#create&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;class SessionsController &amp;lt; ApplicationController
    # post '/login'
    def create
        user = User.find_by(username: params[:username])

        if user&amp;amp;.authenticate(params[:password])
            session[:user_id] = user.id
            render json: user, status: :created
        else
            render json: { error: 'Invalid username or password' }, status: :unauthorized
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;sessions#create&lt;/code&gt; action, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;code&gt;.find_by()&lt;/code&gt; method on our User model to find the appropriate user by the username.&lt;/li&gt;
&lt;li&gt;Set up a conditional using the &lt;code&gt;user&amp;amp;.authenticate(params[:password])&lt;/code&gt; method. The &lt;code&gt;&amp;amp;.&lt;/code&gt; is a shorthand way of saying “if the &lt;code&gt;user&lt;/code&gt; object exists, invoke the &lt;code&gt;authenticate(params[:password])&lt;/code&gt; method on it. Otherwise, return &lt;code&gt;nil&lt;/code&gt;.”&lt;/li&gt;
&lt;li&gt;Create a new session hash with the key :user_id and value set to the actual user id (user.id) if the conditional is true. This hash typically represents a session token or identifier, which is essential for subsequent authenticated requests.&lt;/li&gt;
&lt;li&gt;Render a response with the status: :created if the conditional is true. This response usually contains the session-related information.&lt;/li&gt;
&lt;li&gt;Render an error message “Invalid username or password” with &lt;code&gt;status: :unauthorized&lt;/code&gt; if the conditional is false.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once this controller action is set up, we can make a fetch post request from our frontend whenever we need to create a session (log in). If we have our sign-up logic set up, it is typical for the frontend to automatically log in, so the frontend can utilize this fetch request at the end of the sign-up process as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto Login
&lt;/h2&gt;

&lt;p&gt;When the user reloads a page or closes and reopens the browser, it typically starts a new browsing session. This means the session cookie from the previous session is no longer available, and the server can't associate the new request with the old session. Instead of having the user enter their credentials every time, this feature allows for the user to maintain their logged in status through page reloads.&lt;/p&gt;

&lt;p&gt;Like before, we start by creating a new route for a new controller action.&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
    # signing up
    post '/signup', to: 'users#create'
    # logging in
    post '/login', to: 'sessions#create'
    # auto login
    get '/me', to: 'users#show'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, we point to the &lt;code&gt;users#show&lt;/code&gt; action since we are looking for an existing user instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UsersController &amp;lt; ApplicationController
    # post '/signup'
    def create
        render json: User.create!(user_params), status: :created
    rescue ActiveRecord::RecordInvalid =&amp;gt; e
        render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
    end

    # get '/me'
    def show
        render json: User.find_by!(id: session[:user_id])
    rescue ActiveRecord::RecordNotFound
        render json: { error: 'Not authorized' }, status: :unauthorized
    end

    private

    def user_params
        params.require(:user).permit(:username, :password, :password_confirmation)
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;users#show&lt;/code&gt; action, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;code&gt;find_by!(id: session[:user_id])&lt;/code&gt; method to find the existing user by its id.&lt;/li&gt;
&lt;li&gt;Render the error message with &lt;code&gt;status: :unauthorized&lt;/code&gt; if the user is not authenticated (if the session has expired or the user is not logged in).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this set up, we make sure our frontend triggers a fetch get request to this route on start-up. On React, this can be done using &lt;code&gt;useEffect(() =&amp;gt; {}, [])&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once we are able to retrieve the user information from the backend, we can properly set up our frontend to save our user state. On React, this can be done using &lt;code&gt;useContext()&lt;/code&gt; to persist this data throughout our components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging Out
&lt;/h2&gt;

&lt;p&gt;Since we created a new session when logging in, we need to delete that session[:user_id] for logging out.&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
    # signing up
    post '/signup', to: 'users#create'
    # logging in
    post '/login', to: 'sessions#create'
    # auto login
    get '/me', to: 'users#show'
    # logging out
    delete '/logout', to: 'sessions#destroy'
end
&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 SessionsController &amp;lt; ApplicationController
    # post '/login'
    def create
        user = User.find_by(username: params[:username])

        if user&amp;amp;.authenticate(params[:password])
            session[:user_id] = user.id
            render json: user, status: :created
        else
            render json: { error: 'Invalid username or password' }, status: :unauthorized
        end
    end

    # delete '/logout'
    def destroy
        session.delete :user_id
        head :no_content
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;sessions#destroy&lt;/code&gt; action, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the .delete(:user_id) method to delete the session[:user_id].&lt;/li&gt;
&lt;li&gt;Send an empty &lt;code&gt;head&lt;/code&gt; with &lt;code&gt;:no_content&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Authorizing
&lt;/h2&gt;

&lt;p&gt;To add another layer of backend routing security, we want to restrict access to most of our controller actions to those who are authorized. To restrict all controller actions, we must create a custom method in the application controller using &lt;code&gt;before_action&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;class ApplicationController &amp;lt; ActionController::API
    include ActionController::Cookies
    before_action :authorize

    def authorize
        return render json: { error: "Not authorized" }, status: :unauthorized unless session.include? :user_id
    end
end

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

&lt;/div&gt;



&lt;p&gt;This custom method &lt;code&gt;authorize&lt;/code&gt; sends an error message “Not authorized” whenever the user tries to access a controller action before logging in. However, there are some actions that must be exempt from this.&lt;/p&gt;

&lt;p&gt;If we think about it, we need to allow the user to be able to sign up and log in without this method stopping them. So we need to create exemptions for these actions. Here is what our final users and sessions controllers should look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UsersController &amp;lt; ApplicationController
    skip_before_action :authorize, only: [:create]

    # post '/signup'
    def create
        render json: User.create!(user_params), status: :created
    rescue ActiveRecord::RecordInvalid =&amp;gt; e
        render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
    end

    # get '/me'
    def show
        render json: User.find_by!(id: session[:user_id])
    rescue ActiveRecord::RecordNotFound
        render json: { error: 'Not authorized' }, status: :unauthorized
    end

    private

    def user_params
        params.require(:user).permit(:username, :password, :password_confirmation)
    end
end
&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 SessionsController &amp;lt; ApplicationController
    skip_before_action :authorize, only: [:create]

    # post '/login'
    def create
        user = User.find_by(username: params[:username])

        if user&amp;amp;.authenticate(params[:password])
            session[:user_id] = user.id
            render json: user, status: :created
        else
            render json: { error: 'Invalid username or password' }, status: :unauthorized
        end
    end

    # delete '/logout'
    def destroy
        session.delete(:user_id)
        head(:no_content)
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;skip_before_action&lt;/code&gt; allows us to exempt the sign up and log in actions from our custom &lt;code&gt;authorize&lt;/code&gt; method.&lt;/p&gt;

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

&lt;p&gt;We’ve explored critical concepts of authentication and authorization in the context of web application security. We've established the importance of distinguishing between these two processes, ensuring that only authorized users can access protected resources while allowing for efficient authentication processes. Cookies and sessions were examined as tools for managing user sessions securely. Additionally, the use of BCrypt for password encryption was discussed, enhancing application security. By implementing these practices and understanding their roles, developers can build robust and secure web applications, safeguarding user data and privacy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn authentication and authorization mechanisms in Rails. Please read the official documentation linked below for more information. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/security.html" rel="noopener noreferrer"&gt;Rails Documentation on Sessions and Security&lt;/a&gt;&lt;br&gt;
&lt;a href="https://owasp.org/www-community/SameSite" rel="noopener noreferrer"&gt;SameSite Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/bcrypt" rel="noopener noreferrer"&gt;BCrypt Documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
    <item>
      <title>A Beginner's Guide to Active Record</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Wed, 07 Jun 2023 20:03:03 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-active-record-pnf</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-active-record-pnf</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Basics&lt;/li&gt;
&lt;li&gt;
Getting started with Active Record

&lt;ul&gt;
&lt;li&gt;Set up Gemfile&lt;/li&gt;
&lt;li&gt;Create models and associations&lt;/li&gt;
&lt;li&gt;Create new migrations&lt;/li&gt;
&lt;li&gt;Migrate changes and check status&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Next steps&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Active Record is an essential component of web development that simplifies the interaction between a Ruby application and a relational database. It is an Object-Relational Mapping framework that provides mapping database tables to Ruby classes and enables developers to perform common database operations using familiar object-oriented syntax. With Active Record, we can easily create, retrieve, update, and delete records in the database without having to write raw SQL queries. This guide will walk you through the basics of Active Record, including setting up the necessary gems, defining models and associations, creating database migrations, and performing database operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;A web application can typically be broken down into three parts: the model, the view, the controller. This is known as the &lt;strong&gt;Model View Controller&lt;/strong&gt; architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Model View Controller (MVC) is a pattern in software development that helps promote a clear separation of concerns and reusability of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model is responsible for data persistence and management. The view is how that information is presented to the user via HTML, CSS, and JavaScript. The controller acts as an intermediary between the model and view, handling the routing, requests, and coordination of data flow. For this guide, we will focus on the model in MVC.&lt;/p&gt;

&lt;p&gt;The model represents the database in our application. As developers, we want to be able to access these databases through code by implementing &lt;strong&gt;object-relational mapping&lt;/strong&gt; via &lt;strong&gt;Active Record&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Object-Relational Mapping (ORM) is the concept of using an object-oriented programming language (Ruby) to access relational databases (SQLite3).&lt;/p&gt;

&lt;p&gt;Active Record is an ORM framework represented as a Ruby gem. By abiding by its conventions, it allows us to simplify database configurations in Ruby-based applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A fundamental concept of ORM (and Active Record), is the mapping between models that represent object-oriented entities and database tables. We can view the model object as the database table and instances of that object as rows/records on that table. Following the logic, it makes sense to present the object’s attributes as table columns. In addition, every table has a column for the primary key ID that is unique to every instance. Depending on their association with other tables, they may include a foreign key ID as well. The foreign key is used to establish relationships between other tables.&lt;/p&gt;

&lt;p&gt;The two most common ways tables relate to another is via a &lt;strong&gt;one-to-many&lt;/strong&gt; or &lt;strong&gt;many-to-many&lt;/strong&gt; relationship.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One-to-many relationship is when one instance of a model connects to many instances of another model. A user can have many games so there is a one-to-many relationship between the User model and the Game model.&lt;/p&gt;

&lt;p&gt;Many-to-many relationship is when one instance of a model connects to many instances of another model and vice versa. A student can enroll in multiple courses and every course contains multiple students so this is a many-to-many relationship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if we want to create an app where users can browse a list of cities to see posts about that city, we can think about two models, City and Post. Each of these models can be represented as a table with each row representing a specific city or post. There is a one-to-many relationship between City and Post (a city has many posts and a post belongs to a city), which means that the posts table must have a foreign key that matches a primary key of a city.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Active Record
&lt;/h2&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up Gemfile&lt;/li&gt;
&lt;li&gt;Create models and associations&lt;/li&gt;
&lt;li&gt;Create new migrations&lt;/li&gt;
&lt;li&gt;Migrate changes and check status&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Set up Gemfile
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is to make sure we have the &lt;a href="https://github.com/ruby/rake" rel="noopener noreferrer"&gt;Rake&lt;/a&gt; and &lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;Active Record&lt;/a&gt; gems included in the Gemfile. For more information on these gems, please visit the main website for each library.&lt;/p&gt;

&lt;p&gt;Once the Gemfile is set up, run &lt;code&gt;bundle install&lt;/code&gt; on the CLI to create Gemfile.lock.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create models and associations
&lt;/h3&gt;

&lt;p&gt;When creating models, we need to have it inherit from ActiveRecord::Base. This gives our models access to commonly used methods for data manipulation. Within the class, we define its association with the other model. When using Active Record, it is crucial to use the correct conventions. When naming models, it must be singular and capitalized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class City &amp;lt; ActiveRecord::Base
    # a city has many posts
    has_many :posts
end
&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 Post &amp;lt; ActiveRecord::Base
    # a post belongs to a city
    belongs_to :city
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a more comprehensive list of ActiveRecord::Base methods, please visit this &lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create new migrations
&lt;/h3&gt;

&lt;p&gt;Once the models are created, we need to create the database using rake and migrations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rake is a tool that allows us to automate certain tasks. By writing these task shortcuts inside Rakefile.rb, we can execute them using a CLI with quick commands.&lt;/p&gt;

&lt;p&gt;Migrations are an Active Record feature that provides version control for database changes. It produces a blueprint for database creation and manipulation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To create a new migration, we run &lt;code&gt;rake db:create_migration NAME=&amp;lt;description_of_change&amp;gt;&lt;/code&gt;. This will create a new migration file and defines a class of   that inherits from ActiveRecord::Migration. It will automatically name the file with the datetime it was created.&lt;/p&gt;

&lt;p&gt;In the case of our example, we would run &lt;code&gt;rake db:create_migration Name=create_cities&lt;/code&gt; and then another for the Post model. Within the class, we write the code for the creation of our table. Here is how that looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateCities &amp;lt; ActiveRecord::Migration[6.1]
  def change
    create_table :cities do |t|
      t.string :name
      t.string :population
      t.string :image
      t.string :country
    end
  end
end
&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 CreatePosts &amp;lt; ActiveRecord::Migration[6.1]
  def change
    create_table :posts do |t|
      t.integer :city_id
      t.string :category
      t.string :title
      t.string :body
      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define a method &lt;code&gt;change&lt;/code&gt; by invoking &lt;code&gt;create_table&lt;/code&gt; (inherited from ActiveRecord::Migration) and passing in the table name. When naming tables, the word is in all lowercase and is pluralized. We map each attribute to a column by setting up its data type and name. We do not need to manually create the &lt;code&gt;id&lt;/code&gt; column since it is automatically generated when adding a new record.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrate changes and check status
&lt;/h3&gt;

&lt;p&gt;Once we have our initial migration files, we can run &lt;code&gt;rake db:migrate&lt;/code&gt; to execute the changes. Active Record will automatically run each migration file in order of creation and simultaneously create a schema.rb file. This is where we can check to make sure the tables are created properly with the appropriate columns.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;rake db:migrate:status&lt;/code&gt; allows us to check the status of our migrations. It provides information about which migrations have been applied to the database. When a migration is marked as "up," it means that it has been successfully executed and applied to the database whereas “down” would mean it has not been applied or has been rolled back.&lt;/p&gt;

&lt;p&gt;This way of version control allows developers to track the migration history and easily identify the current state of the database schema. By knowing which migrations have been successfully applied, developers can ensure that the database structure is up to date and consistent with the application's codebase. It provides a systematic approach to managing and applying changes to the database over time, making it easier to collaborate with other developers and maintain the integrity of the database schema across different environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next steps
&lt;/h3&gt;

&lt;p&gt;Once we have created our models and corresponding database tables with columns defined, our next steps would be to seed the database and initiate the server. We can create seeds.rb, whose sole purpose is to fill our tables with data. Although we won't go over these in detail, there are some ways to achieve this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using randomly generated data via &lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Grabbing data from a public API via &lt;a href="https://github.com/rest-client/rest-client" rel="noopener noreferrer"&gt;RestClient&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scraping data from HTML via &lt;a href="https://github.com/sparklemotion/nokogiri" rel="noopener noreferrer"&gt;Nokogiri&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, before we open our server, we must have our controller (in our MVC paradigm) set up. This middle layer dictates the communication between our client (view) and server (model). An easy way to do this is through &lt;a href="https://github.com/sinatra/sinatra" rel="noopener noreferrer"&gt;Sinatra&lt;/a&gt;, a web framework built on top of &lt;a href="https://github.com/rack/rack" rel="noopener noreferrer"&gt;Rack&lt;/a&gt;. Sinatra provides a simple way to set up our routes using a configuration file (config.ru) and the HTTP request handling file (application_controller.rb).&lt;/p&gt;

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

&lt;p&gt;Active Record, as an ORM framework, plays a crucial role in simplifying database interactions and promoting efficient development practices. Migrations provide a systematic approach to managing database changes, allowing developers to track the migration history and maintain the integrity of the database schema. With the ability to automate tasks using Rake and easily check the status of migrations, developers can ensure that their database structure is up to date and consistent. By mastering the fundamentals of Active Record and migrations, developers can enhance their productivity, collaborate effectively, and build robust web applications with ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn Object-Relational Mapping. Please read from the official Active Record website for further detail. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;Active Record&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rest-client/rest-client" rel="noopener noreferrer"&gt;RestClient&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sparklemotion/nokogiri" rel="noopener noreferrer"&gt;Nokogiri&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rack/rack" rel="noopener noreferrer"&gt;Rack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sinatra/sinatra" rel="noopener noreferrer"&gt;Sinatra&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>database</category>
    </item>
    <item>
      <title>A Beginner's Guide to React</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Wed, 29 Mar 2023 03:29:34 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-react-2h0f</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-react-2h0f</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Components&lt;/li&gt;
&lt;li&gt;JSX&lt;/li&gt;
&lt;li&gt;props&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As we dive further into web development, we begin to learn about React by Facebook. When beginners start coding, they do so &lt;strong&gt;imperatively&lt;/strong&gt; but React approaches coding &lt;strong&gt;declaratively&lt;/strong&gt;. These are different programming paradigms that change the way programmers code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Imperative programming&lt;/strong&gt; is when the programmer explicitly specifies each step. &lt;strong&gt;Declarative programming&lt;/strong&gt; focuses on the task that needs to be done and does not specify how it is achieved. In other words, imperative programming is described as “do this then do that,” whereas declarative programming is described as “tell me what you want and not how to get there.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many benefits to declarative programming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is easier to scale and incorporate into larger programs.&lt;/li&gt;
&lt;li&gt;It is easier to debug and maintain the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;In order to program declaratively, React takes advantage of &lt;strong&gt;components&lt;/strong&gt; that each have a specific role in the overall application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Components&lt;/strong&gt; are different groups of code that work together to create a functional React application. For example, one component can take care of the main menu, while another renders from a database API. By separating these “building blocks” of code, programmers can work on specific functions of the app more easily than they could for a monolithic block of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Components are structured in a hierarchy that can be illustrated in a wireframe. Here is an example of a component hierarchy:&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%2F515jj6y5ibl3kzsbhmz9.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%2F515jj6y5ibl3kzsbhmz9.png" alt="component hierarchy" width="310" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, the App.js component is at the top-level. Other common components are the NavBar.js (containing code to render the navigation menu) and Home.js components (containing code to display the Home page). The remaining components work together in tandem to create the functionality of the application. &lt;/p&gt;

&lt;p&gt;The lines between the components indicate how they are connected. For example, the NavBar.js and Home.js components are child components of App.js and sibling components to each other. RecipeItem.js is a child component of RecipesList.js, which is another child component of App.js. These relationships illustrate how information is transferred from component to component.&lt;/p&gt;

&lt;p&gt;React components require that we import some crucial modules from a library of &lt;strong&gt;dependencies&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dependencies&lt;/strong&gt; are a collection of pre-written code modules that the program needs to function properly. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To initialize React, we need to import the &lt;code&gt;React&lt;/code&gt; and &lt;code&gt;ReactDOM&lt;/code&gt; modules from the &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; dependencies, respectively. &lt;code&gt;ReactDOM&lt;/code&gt; is invoked with two arguments. The first is the React component being rendered and the second is the location to render it at. This can be done in index.js. We can think of this as our starting file.&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 from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(&amp;lt;App /&amp;gt;, document.getElementById(‘root’));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App.js is the top-level component from our example above. From there, all the other components are called in the order shown in the hierarchy.&lt;/p&gt;

&lt;p&gt;Components also require an export statement to be able to transfer data to either its parent or child component.&lt;/p&gt;

&lt;p&gt;A typical export statement in most components is placed at the bottom of the .js file.&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 App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of the &lt;code&gt;export default&lt;/code&gt; line as a &lt;code&gt;return&lt;/code&gt; statement for the component itself. Speaking of &lt;code&gt;return&lt;/code&gt; statements, what do these components &lt;code&gt;return&lt;/code&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  JSX
&lt;/h2&gt;

&lt;p&gt;When we code imperatively, we must make all the changes in the HTML file ourselves. Whenever we need to manipulate the DOM (Document Object Model), we must write specific code step by step to detail it out. For example, we may need to create a new element, update that new element then finally append it to the DOM to see changes in our browser. With React, we code declaratively meaning we do not need to write all that out. Instead, we can tell React to change the HTML by returning &lt;strong&gt;JSX&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;JSX (JavaScript XML)&lt;/strong&gt; is a JavaScript extension that allows us to write HTML-like code in our JavaScript file. This opens the door to directly manipulating our DOM, without having to tell it to create, update and append.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each component is essentially a function that returns some JSX. This JSX is rendered on a virtual DOM, through the &lt;code&gt;return&lt;/code&gt; and &lt;code&gt;export default&lt;/code&gt; statements, whenever the component is called. By going through a virtual DOM, React calculates the minimum number of changes needed to update the real DOM. This results in a faster and more efficient render. Here is a simple example:&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 from "react";

function Home() {
    const name = "John";

    return (
        &amp;lt;div id="home"&amp;gt;
            &amp;lt;h1&amp;gt;Welcome to Recipe Journal!&amp;lt;/h1&amp;gt;
            &amp;lt;h3&amp;gt;Click on "Recipes" to see a list of recipes.&amp;lt;/h3&amp;gt;
            &amp;lt;h3&amp;gt;Click on "Add Recipe" to add a new recipe to the list.&amp;lt;/h3&amp;gt;
            &amp;lt;h3&amp;gt;Let's start cooking, {name}!&amp;lt;/h3&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

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

&lt;/div&gt;



&lt;p&gt;It is convention to capitalize the names of functions that represent components. This helps to distinguish them from regular HTML elements and other functions in your code. It is important to remember to make sure that the function returns only one element. In our example, we return one &lt;code&gt;&amp;lt;div id=”home”&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; with multiple elements inside.&lt;/p&gt;

&lt;p&gt;Whenever we want to code JSX, we use parenthesis around it. If we need to go back to JavaScript inside our JSX, we need to use curly brackets. In our example, we need access to the variable &lt;code&gt;name&lt;/code&gt; inside our last &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; tag so we encase &lt;code&gt;{name}&lt;/code&gt; in curly brackets.&lt;/p&gt;

&lt;h2&gt;
  
  
  props
&lt;/h2&gt;

&lt;p&gt;If we want to make our code more dynamic, we introduce the use of props.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;props&lt;/code&gt; (short for properties) are a tool for passing information from a parent to child component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a parent component renders a child component, it can pass data to the child component by providing it as props. The child component can then access and use this data as needed.&lt;/p&gt;

&lt;p&gt;When we want to run a component, we go to its parent component and invoke it. To do this, we must import the child component, like we would with any necessary modules.&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 from "react";
import RecipeItem from "./RecipeItem";

function RecipesList() {
    const currentRecipe = {
        name: "Pizza",
        servings: 3
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;RecipeItem recipe={currentRecipe} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

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

&lt;/div&gt;



&lt;p&gt;We define the &lt;code&gt;currentRecipe&lt;/code&gt; object with two keys. In our &lt;code&gt;return&lt;/code&gt; statement, we call the RecipeItem.js component and pass down &lt;code&gt;currentRecipe&lt;/code&gt; as &lt;code&gt;recipe&lt;/code&gt;. Think of these as arguments for RecipeItem.js. Now let’s look at RecipeItem.&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 from "react";

function RecipeItem({ recipe }) {
    return (
        &amp;lt;div className="item"&amp;gt;
            &amp;lt;h3&amp;gt;{recipe.name}&amp;lt;/h3&amp;gt;
            &amp;lt;h3&amp;gt;{recipe.servings}&amp;lt;/h3&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

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

&lt;/div&gt;



&lt;p&gt;Inside the parameter, we pass the prop as &lt;code&gt;recipe&lt;/code&gt;, making sure to destructure it correctly by using curly brackets around it. This gives us access to that object, where we can grab the values of its keys as we would with any object.&lt;/p&gt;

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

&lt;p&gt;React is a popular JavaScript library for building user interfaces for all kinds of different web and mobile applications. It uses declarative programming that allows us, as programmers, to describe what we want in our code without explaining how to get there. It makes use of components and props to return JSX in a way that makes it easy to manipulate the DOM via virtual DOM. Because of its efficiency, it has garnered a large active community and ecosystem, making it very appealing for future software developers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn Javascript. Please read from the official React website for further detail. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn" rel="noopener noreferrer"&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/your-first-component" rel="noopener noreferrer"&gt;Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/importing-and-exporting-components" rel="noopener noreferrer"&gt;Importing and Exporting Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/writing-markup-with-jsx" rel="noopener noreferrer"&gt;JSX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/javascript-in-jsx-with-curly-braces" rel="noopener noreferrer"&gt;JSX Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/passing-props-to-a-component" rel="noopener noreferrer"&gt;props&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>A Beginner’s Guide to Javascript Operators and Conditional Statements</title>
      <dc:creator>Jason Park</dc:creator>
      <pubDate>Thu, 29 Dec 2022 22:38:36 +0000</pubDate>
      <link>https://forem.com/jjpark987/a-beginners-guide-to-javascript-operators-and-conditional-statements-3kn1</link>
      <guid>https://forem.com/jjpark987/a-beginners-guide-to-javascript-operators-and-conditional-statements-3kn1</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Relational operators&lt;/li&gt;
&lt;li&gt;Logical operators&lt;/li&gt;
&lt;li&gt;Conditional operators&lt;/li&gt;
&lt;li&gt;
Conditional statements

&lt;ul&gt;
&lt;li&gt;if, else if, else statements&lt;/li&gt;
&lt;li&gt;switch statements&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Whether you are a seasoned professional or just starting your journey as a software engineer, one of the first major concepts you will learn is conditional statements. Conditional statements are blocks of code that allow you to be able to control certain actions. Simply put, it helps your program make decisions based on certain information. Every conditional statement contains one or more tests, typically involving the use of relational and logical operators. Before diving into how conditional statements work, we must first understand the relevant relational and logical operators that compose these statements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is for those who are just getting started with Javascript. If you are still confused after reading this guide, please read more about these topics in detail at the MDN website. All links are listed in the Resources section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Relational operators
&lt;/h2&gt;

&lt;p&gt;Relational operators compare two values and return either &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; based on the operator. All of these require two variables, each on opposite sides of the operator. Below is a table of the relational operators.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater than&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if first value is greater than second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;1 &amp;gt; 3&lt;br&gt;// false&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less than&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if first value is less than second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;1 &amp;lt; 3&lt;br&gt;// true&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater than or equal to&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if first value is greater than or equal to second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;3 &amp;gt;= 3&lt;br&gt;// true&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less than or equal to&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if first value is less than or equal to second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;5 &amp;lt;= 3&lt;br&gt;// false&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;===&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strict equality&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if both values equal each other (no type conversion)&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;‘hello’ === ‘hello’&lt;br&gt;// true&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strict inequality&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if both values do not equal each other (no type conversion)&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;‘3’ !== ‘3’&lt;br&gt;// false&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loose equality&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if both values equal each other after type conversion&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;3 == ‘3’&lt;br&gt;// true&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loose inequality&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if both values do not equal each other after type conversion&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;3 != true&lt;br&gt;// false&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Table 1: Relational Operators&lt;/p&gt;

&lt;p&gt;The difference between the strict and loose operators is that the strict operators will only return &lt;code&gt;true&lt;/code&gt; if the two values are equal to each other without any type conversions. On the other hand, the loose operator attempts to convert the values into the same type before making the comparison. Since the behind-the-scenes conversion creates more room for errors and bugs, it is best practice to use the strict operators as default over the loose operators.&lt;/p&gt;

&lt;p&gt;If you get to a point where you need to check between two values of different types, I recommend converting them into the same type yourself before using the strict operator to compare. It is important to also note that comparing values of different data types using relational operators may yield unpredictable results. This is yet another reason to make sure that the two values being compared are of the same data type before comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logical operators
&lt;/h2&gt;

&lt;p&gt;Logical operators can be broken down into two types: unary (one variable) and binary (two variables). The unary operator has one variable to the right of &lt;code&gt;!&lt;/code&gt; and the binary operators have a variable on both sides of the operator. Below is a table of the logical operators.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;!&lt;/code&gt;&lt;br&gt;(unary)&lt;/td&gt;
&lt;td&gt;Not (or Bang)&lt;/td&gt;
&lt;td&gt;Returns the opposite boolean&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;!3&lt;br&gt;// false&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;br&gt;(binary)&lt;/td&gt;
&lt;td&gt;And&lt;/td&gt;
&lt;td&gt;Returns the first value if it is falsy; otherwise return the second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;0 &amp;amp;&amp;amp; false&lt;br&gt;// 0&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;||&lt;/code&gt;&lt;br&gt;(binary)&lt;/td&gt;
&lt;td&gt;Or&lt;/td&gt;
&lt;td&gt;Returns the first value if it is truthy; otherwise return the second value&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;undefined || ‘hello’&lt;br&gt;// ‘hello’&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Table 2: Logical Operators&lt;/p&gt;

&lt;p&gt;&lt;code&gt;!&lt;/code&gt; converts the value into a boolean (if it isn’t one already) and returns the opposite boolean value. Remember that all values are truthy except for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NaN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;“”&lt;/code&gt; or &lt;code&gt;‘’&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to its uses shown on the table, all three logical operators can be combined inside conditional statements to produce more elaborate tests. This will be shown in more detail in the following sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional operators
&lt;/h2&gt;

&lt;p&gt;A conditional operator returns one of two operands depending on the result of the conditional test. Another name for this is the ternary operator (or ternary expression).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;condition ? ifTrue : ifFalse &lt;/pre&gt;&lt;/td&gt;
&lt;td&gt;Ternary&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;ifTrue&lt;/code&gt; if &lt;code&gt;condition&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;; otherwise return &lt;code&gt;ifFalse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;pre&gt;let x = 1&lt;br&gt;x &amp;gt; 0 ? x + 3 : x&lt;br&gt;// 4&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Table 3: The Conditional Operator&lt;/p&gt;

&lt;p&gt;The ternary operator works the same as a simple &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; statement. So if we can just use &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; statements to accomplish what the ternary operator can do, why bother with it? The answer is conciseness. Traditional &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; statements will take up more lines than ternary expressions and for that reason, some may opt for this shorthand version in certain cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional statements
&lt;/h2&gt;

&lt;p&gt;Conditional statements combine the relational and logical operators to create a conditional test. Similar to the basic conditional operator, the conditional statement helps manage the flow of certain actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  if, else if, else statements
&lt;/h3&gt;

&lt;p&gt;As the heart of Javascript conditionals, the &lt;code&gt;if&lt;/code&gt; statement is a versatile tool that helps developers control a certain conditional behavior. When combined with &lt;code&gt;else if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt;, it can cover most, if not all, conditional situations. The set-up for these statements are specified below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(condition1) {
  // execute if condition1 is true
} else if(condition2) {
  // execute if condition1 is false and condition2 is true
} else {
  // execute if previous tests are false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code block above, we have one &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; statement. This suggests that there are three possible outcomes: &lt;code&gt;condition1&lt;/code&gt;, &lt;code&gt;condition2&lt;/code&gt; and none of the above. You can add as many &lt;code&gt;else if&lt;/code&gt; statements after the &lt;code&gt;if&lt;/code&gt; and before the &lt;code&gt;else&lt;/code&gt; if there are more outcomes. The &lt;code&gt;else if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; statements are optional but in order to use them, you need an &lt;code&gt;if&lt;/code&gt; statement in the beginning.&lt;/p&gt;

&lt;p&gt;I mentioned earlier that &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; can increase the complexity of conditional statements. This is done by combining one or more conditional tests to generate a single multi-step test. We will start with the and operator, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As its name implies, the and operator is used as a two part test. In order to pass the overall test, one must pass &lt;strong&gt;&lt;em&gt;both&lt;/em&gt;&lt;/strong&gt; individual conditions. If any one of the two conditions fail, the overall test will also fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const x = 5;

if(x &amp;gt; 0 &amp;amp;&amp;amp; x &amp;lt; 3) {
  // code1
} else if(x &amp;gt; 5 &amp;amp;&amp;amp; x &amp;lt;= 10) {
  // code2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first condition tests if &lt;code&gt;x&lt;/code&gt; is greater than 0 and if &lt;code&gt;x&lt;/code&gt; is less than 3. If these conditions are fulfilled, we execute &lt;code&gt;code1&lt;/code&gt; in the first &lt;code&gt;if&lt;/code&gt; statement. If not, we move on to the next conditional test. This one checks if &lt;code&gt;x&lt;/code&gt; is greater than 5 and less than or equal to 10. Since &lt;code&gt;x&lt;/code&gt; = 5, it doesn’t pass either conditions and neither &lt;code&gt;code1&lt;/code&gt; or &lt;code&gt;code2&lt;/code&gt; is executed.&lt;/p&gt;

&lt;p&gt;We can think of &lt;code&gt;||&lt;/code&gt; in a similar way but with the word “or” instead of “and.” There are two conditions and if one &lt;strong&gt;&lt;em&gt;or&lt;/em&gt;&lt;/strong&gt; the other passes, the entire condition passes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const x = 3;

if(!x || x !== ‘hello’) {
  // code1
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;!x&lt;/code&gt; equals true or  &lt;code&gt;x&lt;/code&gt; does not strictly equal &lt;code&gt;’hello’&lt;/code&gt;, we run &lt;code&gt;code1&lt;/code&gt;. In any other case, we run &lt;code&gt;code2&lt;/code&gt; inside the &lt;code&gt;else&lt;/code&gt; block. Since &lt;code&gt;x&lt;/code&gt; equals 3, &lt;code&gt;!x&lt;/code&gt; (or &lt;code&gt;!3&lt;/code&gt;) equals false (all numbers are truthy and &lt;code&gt;!&lt;/code&gt; switches the truthiness of the variable). Moreover, &lt;code&gt;x&lt;/code&gt; does not equal &lt;code&gt;’hello’&lt;/code&gt;, as it is assigned to 3. Because one of the two conditions results in &lt;code&gt;true&lt;/code&gt;, we pass our overall test and &lt;code&gt;code1&lt;/code&gt; is executed.&lt;/p&gt;

&lt;h3&gt;
  
  
  switch statements
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;switch&lt;/code&gt; statement contains an expression that is matched against each &lt;code&gt;case&lt;/code&gt; value via strict equality. One at a time, it goes down each case until it finds a match. Once it does, it starts the execution from that &lt;code&gt;case&lt;/code&gt;. If there isn’t a &lt;code&gt;break&lt;/code&gt; keyword, the execution falls through and runs the block of code inside the next &lt;code&gt;case&lt;/code&gt;, even if the &lt;code&gt;case&lt;/code&gt; value doesn’t match. This process repeats until it meets &lt;code&gt;break&lt;/code&gt; or checks every succeeding clause. The syntax is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch(expression) {
  case value1:
    // execute if expression matches value1
  case value2:
    // execute if expression matches value2
    break
  default:
    // execute if expression doesn’t match value1 or value2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;break&lt;/code&gt; and &lt;code&gt;default&lt;/code&gt; keywords are optional: the point of &lt;code&gt;break&lt;/code&gt; is to cease the automatic fall through and immediately stop; &lt;code&gt;default&lt;/code&gt; is there so it runs when there is no other match or when the execution reaches it from falling through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const x = 3

switch(x + 1) {
  case 3:
    // code1
  case 4:
    // code2
  case 5:
    // code3
    break
  default:
    // code4
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start with &lt;code&gt;x&lt;/code&gt; equals 3. The &lt;code&gt;switch&lt;/code&gt; expression is &lt;code&gt;x + 1&lt;/code&gt;, which equals 4. Starting from the top, there is no match until it reaches &lt;code&gt;case 4&lt;/code&gt;. It proceeds to run &lt;code&gt;code2&lt;/code&gt; then falls through to &lt;code&gt;case 5&lt;/code&gt;. Even though this isn’t a match, &lt;code&gt;code3&lt;/code&gt; is executed. The &lt;code&gt;break&lt;/code&gt; at the end of &lt;code&gt;case 5&lt;/code&gt; stops the execution from falling through to &lt;code&gt;default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;switch&lt;/code&gt; statements are more situational than &lt;code&gt;if&lt;/code&gt; statements. They are most commonly utilized when there are many &lt;code&gt;else if&lt;/code&gt; or &lt;code&gt;else&lt;/code&gt; statements that need to be matched through strict equality (&lt;code&gt;===&lt;/code&gt;).&lt;/p&gt;

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

&lt;p&gt;In the preceding sections, we discussed the role of relational and logical operators within the context of conditional statements. After reviewing all the pertinent operators, we explored how &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt; and &lt;code&gt;switch&lt;/code&gt; can cover almost every conditional test with the right combination of operators and statements.&lt;/p&gt;

&lt;p&gt;It is an understatement to make the claim that conditional statements are important in programming. Even beyond the scope of software development, it plays a crucial role in our everyday lives. “I will go on a walk if it doesn’t rain” or “if I plan on going out tomorrow night, I will do my homework tonight” are some real life examples of conditional statements that we set for ourselves, sometimes without even thinking about them. With this in mind, it is paramount that novice software developers have a solid grasp on this concept before trekking forward in their new and exciting career.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is meant to serve as a learning tool for people who are just starting to learn Javascript (and coding in general as these concepts apply to other languages beyond Javascript). Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at &lt;a href="mailto:jjpark987@gmail.com"&gt;jjpark987@gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators" rel="noopener noreferrer"&gt;MDN Expressions and operators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT" rel="noopener noreferrer"&gt;MDN Logical NOT (!)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND" rel="noopener noreferrer"&gt;MDN Logical AND (&amp;amp;&amp;amp;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR" rel="noopener noreferrer"&gt;MDN Logical OR (||)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else" rel="noopener noreferrer"&gt;MDN if…else&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch" rel="noopener noreferrer"&gt;MDN switch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break" rel="noopener noreferrer"&gt;MDN break&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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