<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Ginger Tech Blog]]></title><description><![CDATA[Software Development, Coding, and Tech Tutorials]]></description><link>https://www.gingertechblog.com/</link><image><url>https://www.gingertechblog.com/favicon.png</url><title>Ginger Tech Blog</title><link>https://www.gingertechblog.com/</link></image><generator>Ghost 5.82</generator><lastBuildDate>Tue, 05 May 2026 08:46:59 GMT</lastBuildDate><atom:link href="https://www.gingertechblog.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Automate Everything with n8n: What You Can Do With n8n as a Developer]]></title><description><![CDATA[<h1 id></h1><p>n8n is an <strong>open-source workflow automation tool</strong> that allows you to connect APIs, services, and databases together &#x2014; all without writing endless boilerplate code. It&#x2019;s like having Zapier, but with full flexibility and control over your data, custom logic, and hosting.</p><p>For developers, n8n isn&#x2019;t just</p>]]></description><link>https://www.gingertechblog.com/automate-everything-with-n8n/</link><guid isPermaLink="false">68ace9b3c447c104b30cfba2</guid><category><![CDATA[n8n]]></category><category><![CDATA[automation]]></category><category><![CDATA[dev-tools]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Mon, 25 Aug 2025 23:22:48 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2025/08/ChatGPT-Image-Aug-25--2025--07_17_21-PM.webp" medium="image"/><content:encoded><![CDATA[<h1 id></h1><img src="https://www.gingertechblog.com/content/images/2025/08/ChatGPT-Image-Aug-25--2025--07_17_21-PM.webp" alt="Automate Everything with n8n: What You Can Do With n8n as a Developer"><p>n8n is an <strong>open-source workflow automation tool</strong> that allows you to connect APIs, services, and databases together &#x2014; all without writing endless boilerplate code. It&#x2019;s like having Zapier, but with full flexibility and control over your data, custom logic, and hosting.</p><p>For developers, n8n isn&#x2019;t just a convenience tool &#x2014; it&#x2019;s a <strong>powerful integration and automation engine</strong> that can save hours of repetitive work and enable scalable solutions.</p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2025/08/image.png" class="kg-image" alt="Automate Everything with n8n: What You Can Do With n8n as a Developer" loading="lazy" width="2000" height="1081" srcset="https://www.gingertechblog.com/content/images/size/w600/2025/08/image.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2025/08/image.png 1000w, https://www.gingertechblog.com/content/images/size/w1600/2025/08/image.png 1600w, https://www.gingertechblog.com/content/images/size/w2400/2025/08/image.png 2400w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="%F0%9F%9A%80-why-n8n-is-great-for-developers">&#x1F680; Why n8n is Great for Developers</h2><ul><li><strong>Self-hostable</strong> &#x2192; Run it on your own server for privacy and control.</li><li><strong>Extensible</strong> &#x2192; Build your own custom nodes if something doesn&#x2019;t exist yet.</li><li><strong>Code-friendly</strong> &#x2192; Insert JavaScript or Python functions directly inside workflows.</li><li><strong>Event-driven</strong> &#x2192; Trigger automations with webhooks, schedules, or external events.</li></ul><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2025/08/image-1.png" class="kg-image" alt="Automate Everything with n8n: What You Can Do With n8n as a Developer" loading="lazy" width="2000" height="816" srcset="https://www.gingertechblog.com/content/images/size/w600/2025/08/image-1.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2025/08/image-1.png 1000w, https://www.gingertechblog.com/content/images/size/w1600/2025/08/image-1.png 1600w, https://www.gingertechblog.com/content/images/2025/08/image-1.png 2374w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="%F0%9F%94%A7-developer-use-cases-for-n8n">&#x1F527; Developer Use Cases for n8n</h2><h3 id="1-workflow-automation">1. Workflow Automation</h3><ul><li>Automate repetitive tasks by integrating data between different systems.</li><li>Sync user data across databases, CRMs, or email services.</li><li>Trigger notifications when code is deployed, or when an error occurs.</li></ul><p><strong>Example Workflow: </strong></p><ul><li>Webhook &#x2192; GitHub Node &#x2192; Slack Node</li></ul><p><strong>Result:</strong> Whenever a PR is merged on GitHub, a Slack message is automatically posted in your team channel.</p><hr><h3 id="2-api-integration">2. API Integration</h3><ul><li>Connect to external APIs to send and retrieve data.</li><li>Expose your workflows as custom API endpoints.</li><li>Handle OAuth2, tokens, and authentication seamlessly.</li></ul><p><strong>Example Workflow:</strong></p><ul><li>HTTP Request Node &#x2192; Function Node &#x2192; PostgreSQL Node</li></ul><p><strong>Result:</strong> Fetch data from a third-party API, process it with custom logic, and store it in your database.</p><hr><h3 id="3-data-processing-transformation">3. Data Processing &amp; Transformation</h3><ul><li>Extract and clean data from CSV/JSON files.</li><li>Transform datasets into the required format.</li><li>Push processed data into BI tools or dashboards.</li></ul><p><strong>Example Workflow:</strong></p><ul><li>Google Sheets &#x2192; Function Node &#x2192; Grafana</li></ul><p><strong>Result:</strong> Collect raw sales data from Google Sheets, process it with JavaScript, and send it to Grafana for visualization.</p><hr><h3 id="4-notifications-alerts">4. Notifications &amp; Alerts</h3><ul><li>Send alerts via Slack, Discord, or email when thresholds are met.</li><li>Monitor logs, APIs, or databases for anomalies.</li><li>Integrate with monitoring tools like Prometheus or Grafana.</li></ul><hr><h3 id="5-internal-developer-tools">5. Internal Developer Tools</h3><ul><li>Build quick internal APIs without writing a full backend.</li><li>Automate onboarding of new developers (create GitHub repos, send Slack invites, etc.).</li><li>Use n8n as a low-code backend for prototypes.</li></ul><p><strong>Example Workflow:</strong></p><ul><li>Cron Node (every 5 min) &#x2192; HTTP Request (API check) &#x2192; IF Node &#x2192; Slack Node</li></ul><p><strong>Result:</strong></p><ul><li>Every 5 minutes, check if your API is responding.</li><li>If the API returns an error, instantly send an alert to Slack.</li><li>No need for a complex monitoring setup!</li></ul><hr><h2 id="%F0%9F%92%A1-final-thoughts">&#x1F4A1; Final Thoughts</h2><p>For developers, n8n is not just about automation &#x2014; it&#x2019;s about <strong>building flexible, self-hosted integrations</strong> that you fully control. Whether you&#x2019;re handling APIs, automating dev workflows, or processing data, n8n can be your go-to tool for scaling productivity.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Self-Host the Outline Note App with Docker – A Powerful Collaborative Note Setup]]></title><description><![CDATA[<p>If you&apos;re looking for a self-hosted, secure, and modern knowledge management tool, <a href="https://docs.getoutline.com/s/guide?ref=gingertechblog.com" rel="noopener noreferrer nofollow">Outline</a> is a fantastic choice. It&#x2019;s like Notion for teams, but open-source and under your full control.</p><p>In this tutorial, we&#x2019;ll walk through setting up Outline with Docker Compose&#x2014;including PostgreSQL,</p>]]></description><link>https://www.gingertechblog.com/self-host-the-outline-note-app-with-docker-a-powerful-collaborative-note-setup/</link><guid isPermaLink="false">67fdbdf78f372d050a75fd34</guid><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Tue, 15 Apr 2025 02:19:48 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2025/04/DALL-E-2025-04-14-22.17.12---A-visually-striking-tech-blog-illustration-representing-the-top-features-of-the-Outline-note-taking-app.-Show-a-digital-workspace-with-floating-docume.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2025/04/DALL-E-2025-04-14-22.17.12---A-visually-striking-tech-blog-illustration-representing-the-top-features-of-the-Outline-note-taking-app.-Show-a-digital-workspace-with-floating-docume.webp" alt="Self-Host the Outline Note App with Docker &#x2013; A Powerful Collaborative Note Setup"><p>If you&apos;re looking for a self-hosted, secure, and modern knowledge management tool, <a href="https://docs.getoutline.com/s/guide?ref=gingertechblog.com" rel="noopener noreferrer nofollow">Outline</a> is a fantastic choice. It&#x2019;s like Notion for teams, but open-source and under your full control.</p><p>In this tutorial, we&#x2019;ll walk through setting up Outline with Docker Compose&#x2014;including PostgreSQL, and Redis &#x2014;for a complete, powerful deployment.</p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2025/04/image.png" class="kg-image" alt="Self-Host the Outline Note App with Docker &#x2013; A Powerful Collaborative Note Setup" loading="lazy" width="1466" height="1382" srcset="https://www.gingertechblog.com/content/images/size/w600/2025/04/image.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2025/04/image.png 1000w, https://www.gingertechblog.com/content/images/2025/04/image.png 1466w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="%F0%9F%A7%A0-why-outline">&#x1F9E0; Why Outline?</h2><p>Outline is a beautiful, collaborative knowledge base built for teams. It features:</p><h3 id="%F0%9F%93%81-drag-and-drop-file-uploads">&#x1F4C1; <strong>Drag-and-Drop File Uploads</strong></h3><p>Attach PDFs, images, and other docs directly inside your notes. Great for:</p><ul><li>Product spec sheets</li><li>Design mockups</li><li>Client documentation</li></ul><h3 id="%F0%9F%9A%80-build-a-private-wiki-for-your-team">&#x1F680; <strong>Build a Private Wiki for Your Team</strong></h3><p>Create a structured knowledge base with nested documents, organized by collections. Perfect for:</p><ul><li>Onboarding new team members</li><li>Documenting SOPs (standard operating procedures)</li><li>Engineering documentation</li><li>Company handbooks</li></ul><h3 id="%F0%9F%94%90-role-based-access-permissions">&#x1F510; <strong>Role-Based Access &amp; Permissions</strong></h3><p>You can control exactly who sees what:</p><ul><li><strong>View-only</strong> access for guests or contractors</li><li><strong>Full edit</strong> permissions for team members</li><li>Restrict certain collections to specific groups or individuals</li></ul><h3 id="%F0%9F%92%AC-live-collaborative-editing-like-google-docs">&#x1F4AC;  <strong>Live Collaborative Editing (Like Google Docs)</strong></h3><p>Outline supports live collaboration&#x2014;multiple people can edit a doc at once, and you&#x2019;ll see changes in real-time.</p><h3 id="%F0%9F%A7%A0-powerful-markdown-editor">&#x1F9E0; <strong>Powerful Markdown Editor</strong></h3><p>Built-in rich markdown editor with:</p><ul><li>Tables</li><li>Code blocks</li><li>Checklists</li><li>Links</li><li>Image embeds</li></ul><p>You get the simplicity of markdown with the polish of a modern editor.</p><h3 id="%F0%9F%8C%90-integrations-auth-providers">&#x1F310; <strong>Integrations &amp; Auth Providers</strong></h3><p>Out of the box, Outline supports:</p><ul><li>Google Login</li><li>Slack Login</li><li>OpenID Connect (for self-hosted SSO)</li><li>Webhooks for automation</li></ul><p>You can also build <strong>custom integrations</strong> with the Outline API.</p><h3 id="%F0%9F%A7%A9-embed-anything-charts-videos-etc">&#x1F9E9; <strong>Embed Anything (Charts, Videos, etc.)</strong></h3><p>Use Markdown embeds to drop in:</p><ul><li>YouTube videos</li><li>Figma designs</li><li>Loom recordings</li><li>Google Maps</li><li>GitHub Gists</li></ul><p></p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2025/04/image-1.png" class="kg-image" alt="Self-Host the Outline Note App with Docker &#x2013; A Powerful Collaborative Note Setup" loading="lazy" width="2000" height="1447" srcset="https://www.gingertechblog.com/content/images/size/w600/2025/04/image-1.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2025/04/image-1.png 1000w, https://www.gingertechblog.com/content/images/size/w1600/2025/04/image-1.png 1600w, https://www.gingertechblog.com/content/images/2025/04/image-1.png 2000w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="%F0%9F%90%B3-prerequisites">&#x1F433; Prerequisites</h2><p>Before you begin:</p><ul><li>Docker and Docker Compose installed</li><li>A Linux server or NAS (Synology, Unraid, Ubuntu, etc.)</li><li>A domain name (for HTTPS setup)</li><li>Basic knowledge of terminal commands</li></ul><hr><h2 id="%F0%9F%9B%A0-docker-compose-setup">&#x1F6E0; Docker Compose Setup</h2><p>Here&#x2019;s a full <code>docker-compose.yml</code> tailored to run Outline, PostgreSQL, and Redis. This version uses <code>host</code> networking, perfect for local NAS setups.</p><pre><code>
version: &quot;3&quot;

services:
  outline:
    image: docker.getoutline.com/outlinewiki/outline:latest
    user: &quot;999:100&quot;
    network_mode: &quot;host&quot;
    volumes:
      - /YOUR_SAVE_PATH_DIRECTORY:/var/lib/outline/data
    depends_on:
      - postgres
      - redis
    environment:
      SECRET_KEY: &quot;secret_key_here&quot;
      UTILS_SECRET: &quot;utils_secret_here&quot;
      DATABASE_URL: &quot;postgres://user:pass@localhost:5432/outline?sslmode=disable&quot;
      REDIS_URL: &quot;redis://localhost:6379&quot;
      URL: &quot;https://yourdomain.com&quot;
      FORCE_HTTPS: &quot;true&quot;
      PORT: &quot;3333&quot;
      FILE_STORAGE: &quot;local&quot;
      FILE_STORAGE_UPLOAD_MAX_SIZE: &quot;1073741824&quot; #Set Max file upload size
      GOOGLE_CLIENT_ID: &quot;your_google_client_id&quot;  #Needed for google auth
      GOOGLE_CLIENT_SECRET: &quot;your_google_secret&quot; #Needed for google auth

  redis:
    image: redis
    ports:
      - &quot;6379:6379&quot;
    volumes:
      - ./redis.conf:/redis.conf
    command: [&quot;redis-server&quot;, &quot;/redis.conf&quot;]
    healthcheck:
      test: [&quot;CMD&quot;, &quot;redis-cli&quot;, &quot;ping&quot;]
      interval: 10s
      timeout: 30s
      retries: 3
    networks:
      - outline_stack_net

  postgres:
    image: postgres
    user: &quot;999:100&quot;
    ports:
      - &quot;5432:5432&quot;
    volumes:
      - /YOUR_SAVE_PATH_DIRECTORY:/var/lib/postgresql/data
    healthcheck:
      test: [&quot;CMD&quot;, &quot;pg_isready&quot;, &quot;-d&quot;, &quot;outline&quot;, &quot;-U&quot;, &quot;user&quot;]
      interval: 30s
      timeout: 20s
      retries: 3
    environment:
      POSTGRES_USER: &quot;user_name&quot;
      POSTGRES_PASSWORD: &quot;password&quot;
      POSTGRES_DB: &quot;outline_db&quot;
    networks:
      - outline_stack_net

networks:
  outline_stack_net:
    external: true</code></pre><hr><h2 id="%E2%9A%99%EF%B8%8F-system-user-permissions">&#x2699;&#xFE0F; System User Permissions</h2><p>Make sure to update:</p><pre><code>
user: &quot;999:100&quot; #use your user and group id to match your system&apos;s</code></pre><p>This maps to your system&apos;s user and group ID. You can find it with:</p><pre><code>
id yourusername</code></pre><hr><h2 id="%F0%9F%94%90-enable-https-optional-but-recommended">&#x1F510; Enable HTTPS (Optional but Recommended)</h2><p>Set up a reverse proxy (like <strong>NGINX</strong> or <strong>Caddy</strong>) and use <a href="https://letsencrypt.org/?ref=gingertechblog.com" rel="noopener noreferrer nofollow">Let&apos;s Encrypt</a> for a free SSL certificate. Make sure your domain points to your server IP.</p><hr><h2 id="%F0%9F%A7%AA-test-everything">&#x1F9EA; Test Everything</h2><pre><code>
docker compose up -d</code></pre><p>Visit: <code>https://yourdomain.com:3333</code></p><hr><h2 id="%F0%9F%A7%A0-tips-best-practices">&#x1F9E0; Tips &amp; Best Practices</h2><ul><li>Backup your PostgreSQL volume regularly.</li><li>Use Docker secrets for sensitive info in production.</li><li>Run behind a firewall and use fail2ban for added security.</li><li>Monitor container logs with <code>docker logs &lt;container_name&gt;</code></li></ul><hr><h2 id="final-thoughts">Final Thoughts</h2><p>With Outline running in Docker, you&apos;ve got a secure, scalable, and self-hosted alternative to cloud note apps like Notion or Confluence.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Mastering Ruby on Rails Command Line Commands]]></title><description><![CDATA[<p>Ruby on Rails is a powerful web development framework that simplifies the development process. One of the key components of Rails is its command-line interface (CLI), which provides a wide range of commands to streamline development tasks. This guide will walk you through the most essential Rails commands, providing examples</p>]]></description><link>https://www.gingertechblog.com/mastering-ruby-on-rails-command-line-commands/</link><guid isPermaLink="false">66d5de656534601a6e416e9c</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[CLI]]></category><category><![CDATA[Terminal Commands]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Mon, 02 Sep 2024 15:53:42 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/09/rails_commands_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/09/rails_commands_blog.webp" alt="Mastering Ruby on Rails Command Line Commands"><p>Ruby on Rails is a powerful web development framework that simplifies the development process. One of the key components of Rails is its command-line interface (CLI), which provides a wide range of commands to streamline development tasks. This guide will walk you through the most essential Rails commands, providing examples to help you understand their usage.</p><h2 id="table-of-contents"><strong>Table of Contents</strong></h2><ol><li><a>Introduction to Rails Command Line</a></li><li><a>Commonly Used Rails Commands</a><ul><li><a>1. <code>rails new</code></a></li><li><a>2. <code>rails server</code></a></li><li><a>3. <code>rails generate</code></a></li><li><a>4. <code>rails console</code></a></li><li><a>5. <code>rails db</code></a></li><li><a>6. <code>rails routes</code></a></li><li><a>7. <code>rails test</code></a></li><li><a>8. <code>rails runner</code></a></li><li><a>9. <code>rails destroy</code></a></li></ul></li><li><a>Other Useful Rails Commands</a></li><li><a>Conclusion</a></li></ol><p></p><h2 id="1-introduction-to-rails-command-line"><strong>1. Introduction to Rails Command Line</strong></h2><p>The Rails command line tool is a powerful utility that helps developers perform a variety of tasks, from creating a new application to running tests and generating models. Understanding these commands is crucial for efficient Rails development.</p><p></p><h2 id="2-commonly-used-rails-commands"><strong>2. Commonly Used Rails Commands</strong></h2><p>Let&apos;s dive into the most commonly used Rails commands and see how they work.</p><p></p><h3 id="1-rails-new"><strong>1. <code>rails new</code></strong></h3><p>The <code>rails new</code> command is used to create a new Rails application. It sets up the directory structure, configuration files, and initial files needed to get started with a Rails app.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails new myapp
</code></pre><p>This command will create a new Rails application named <code>myapp</code> in the current directory.</p><p>You can also specify options such as database type:</p><pre><code class="language-bash">rails new myapp --database=postgresql
</code></pre><p>This command creates a new Rails application configured to use PostgreSQL as the database.</p><p></p><h3 id="2-rails-server"><strong>2. <code>rails server</code></strong></h3><p>The <code>rails server</code> command (often abbreviated as <code>rails s</code>) starts a local development server.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails server
</code></pre><p>By default, the server runs on <code>http://localhost:3000</code>. You can specify a different port if needed:</p><pre><code class="language-bash">rails server -p 4000
</code></pre><p>This command starts the Rails server on port 4000.</p><p></p><h3 id="3-rails-generate"><strong>3. <code>rails generate</code></strong></h3><p>The <code>rails generate</code> (or <code>rails g</code>) command is used to create various components of a Rails application, such as models, controllers, migrations, and more.</p><p><strong>Examples:</strong></p><p>To generate a model:</p><pre><code class="language-bash">rails generate model User name:string email:string
</code></pre><p>To generate a controller:</p><pre><code class="language-bash">rails generate controller Users index show
</code></pre><p>You can also generate a migration:</p><pre><code class="language-bash">rails generate migration AddAgeToUsers age:integer
</code></pre><p></p><h3 id="4-rails-console"><strong>4. <code>rails console</code></strong></h3><p>The <code>rails console</code> (or <code>rails c</code>) command opens an interactive Ruby session with the Rails environment loaded. This is useful for testing out code or querying the database.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails console
</code></pre><p>Once inside the console, you can interact with your Rails app:</p><pre><code class="language-ruby">User.first
</code></pre><p></p><h3 id="5-rails-db"><strong>5. <code>rails db</code></strong></h3><p>The <code>rails db</code> commands are a set of commands used to manage the database.</p><p><strong>Examples:</strong></p><p>To migrate the database:</p><pre><code class="language-bash">rails db:migrate
</code></pre><p>To create the database:</p><pre><code class="language-bash">rails db:create
</code></pre><p>To drop the database:</p><pre><code class="language-bash">rails db:drop
</code></pre><p>To seed the database:</p><pre><code class="language-bash">rails db:seed
</code></pre><p></p><h3 id="6-rails-routes"><strong>6. <code>rails routes</code></strong></h3><p>The <code>rails routes</code> command displays all the routes defined in your application. This is helpful to understand the endpoints and paths available in your Rails app.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails routes
</code></pre><p>To display routes in a specific format, such as JSON:</p><pre><code class="language-bash">rails routes -j
</code></pre><p></p><h3 id="7-rails-test"><strong>7. <code>rails test</code></strong></h3><p>The <code>rails test</code> command runs the tests in your Rails application.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails test
</code></pre><p>You can also run a specific test file:</p><pre><code class="language-bash">rails test test/models/user_test.rb
</code></pre><p></p><h3 id="8-rails-runner"><strong>8. <code>rails runner</code></strong></h3><p>The <code>rails runner</code> command allows you to run Ruby code in the context of your Rails application.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails runner &quot;User.all.each { |u| puts u.name }&quot;
</code></pre><p>This command runs the given Ruby code and outputs the names of all users.</p><p></p><h3 id="9-rails-destroy"><strong>9. <code>rails destroy</code></strong></h3><p>The <code>rails destroy</code> command is the opposite of <code>rails generate</code>. It removes the files and code generated by <code>rails generate</code>.</p><p><strong>Example:</strong></p><pre><code class="language-bash">rails destroy model User
</code></pre><p>This command will remove the model file, migration file, and any test files associated with the <code>User</code> model.</p><p></p><h2 id="3-other-useful-rails-commands"><strong>3. Other Useful Rails Commands</strong></h2><p>While the above commands are the most commonly used, there are several other commands that can be helpful:</p><ul><li><strong><code>rails about</code></strong>: Displays information about the Rails environment.</li><li><strong><code>rails notes</code></strong>: Lists all TODO and FIXME comments in the application code.</li><li><strong><code>rails stats</code></strong>: Provides a breakdown of the lines of code in your application.</li><li><strong><code>rails credentials:edit</code></strong>: Opens the encrypted credentials file for editing.</li><li><strong><code>rails middleware</code></strong>: Displays the middleware stack used by the Rails application.</li></ul><p></p><h2 id="4-conclusion"><strong>4. Conclusion</strong></h2><p>The Rails command line interface is a powerful tool that can significantly enhance your productivity as a developer. By mastering these commands, you&apos;ll be able to navigate the Rails ecosystem more effectively and build robust applications with ease.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Understanding Ruby on Rails Active Job: A Comprehensive Guide]]></title><description><![CDATA[<p>Ruby on Rails (RoR) is a powerful framework for building web applications, and Active Job is a part of the Rails ecosystem that provides a framework for declaring jobs and making them run on a variety of queueing backends. In this blog post, we&apos;ll explore what Active Job</p>]]></description><link>https://www.gingertechblog.com/understanding-ruby-on-rails-active-job-a-comprehensive-guide/</link><guid isPermaLink="false">66d3b81b6534601a6e416e5d</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Sun, 01 Sep 2024 00:49:16 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/09/active_job_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/09/active_job_blog.webp" alt="Understanding Ruby on Rails Active Job: A Comprehensive Guide"><p>Ruby on Rails (RoR) is a powerful framework for building web applications, and Active Job is a part of the Rails ecosystem that provides a framework for declaring jobs and making them run on a variety of queueing backends. In this blog post, we&apos;ll explore what Active Job is, why it&apos;s useful, and how you can implement it in your Rails applications to handle background jobs efficiently.</p><h2 id="what-is-active-job">What is Active Job?</h2><p>Active Job is a framework for declaring jobs in Ruby on Rails, making it easier to run background processes like sending emails, updating data, or generating reports asynchronously. It provides a standardized interface for job processing, which means you can switch between different backend processing libraries (like Sidekiq, Resque, Delayed Job, etc.) without changing the job&apos;s code.</p><h2 id="why-use-active-job">Why Use Active Job?</h2><h3 id="1-simplified-background-processing">1. Simplified Background Processing</h3><p>Active Job abstracts the complexity of managing background jobs across different systems. This abstraction allows developers to write jobs that are backend agnostic, meaning you can start with a simple backend like the built-in <code>Async</code> adapter and scale up to more complex systems like Sidekiq or Resque as your application grows.</p><h3 id="2-unified-interface">2. Unified Interface</h3><p>With Active Job, you can define jobs in a single way regardless of the backend you choose. This unified interface helps maintain cleaner, more maintainable code.</p><h3 id="3-easy-error-handling-and-retry-logic">3. Easy Error Handling and Retry Logic</h3><p>Active Job allows you to easily handle job errors and implement retry logic, ensuring robust job processing in your Rails application.</p><h2 id="how-to-set-up-active-job">How to Set Up Active Job</h2><p>Let&apos;s walk through a basic setup of Active Job in a Ruby on Rails application.</p><h3 id="step-1-generate-a-job">Step 1: Generate a Job</h3><p>To create a new job, use the Rails generator:</p><pre><code class="language-bash">rails generate job Example
</code></pre><p>This command creates a new job file in the <code>app/jobs</code> directory:</p><pre><code class="language-ruby"># app/jobs/example_job.rb
class ExampleJob &lt; ApplicationJob
  queue_as :default

  def perform(*args)
    # Do something later
  end
end
</code></pre><h3 id="step-2-enqueue-the-job">Step 2: Enqueue the Job</h3><p>To enqueue the job, use the <code>perform_later</code> method. This method will push the job into the queue to be processed asynchronously:</p><pre><code class="language-ruby">ExampleJob.perform_later(&apos;Hello, World!&apos;)
</code></pre><p>You can enqueue jobs from controllers, models, or any part of your Rails application.</p><h3 id="step-3-configure-the-job-queue-adapter">Step 3: Configure the Job Queue Adapter</h3><p>Rails comes with a built-in asynchronous adapter, but for production environments, you&apos;ll likely want to use a more robust solution like Sidekiq or Resque. To configure the queue adapter, open the <code>config/application.rb</code> file and set the desired adapter:</p><pre><code class="language-ruby"># config/application.rb
config.active_job.queue_adapter = :sidekiq
</code></pre><p>Make sure you have the appropriate gem installed for your chosen adapter. For example, if you&apos;re using Sidekiq, add it to your <code>Gemfile</code>:</p><pre><code class="language-ruby">gem &apos;sidekiq&apos;
</code></pre><p>Then, run <code>bundle install</code> to install the gem.</p><h3 id="step-4-handling-job-errors-and-retries">Step 4: Handling Job Errors and Retries</h3><p>Active Job makes it easy to handle errors and implement retries. You can specify the number of retry attempts and the delay between retries by using the <code>retry_on</code> method:</p><pre><code class="language-ruby">class ExampleJob &lt; ApplicationJob
  queue_as :default

  retry_on StandardError, wait: 5.seconds, attempts: 3

  def perform(*args)
    # Potentially dangerous operation
  end
end
</code></pre><h3 id="step-5-scheduling-jobs">Step 5: Scheduling Jobs</h3><p>Active Job also supports scheduling jobs to run in the future. You can use the <code>set</code> method to specify a delay:</p><pre><code class="language-ruby">ExampleJob.set(wait: 1.minute).perform_later(&apos;Hello, delayed World!&apos;)
</code></pre><p>This will schedule the job to run one minute from the time it is enqueued.</p><h2 id="real-world-use-cases-for-active-job">Real-World Use Cases for Active Job</h2><p>Here are some common scenarios where Active Job shines:</p><ul><li><strong>Sending Emails:</strong> Use Active Job to send emails in the background without blocking the main thread, improving your application&apos;s response time.</li><li><strong>Data Processing:</strong> Perform heavy data processing tasks asynchronously, such as generating reports or processing user uploads.</li><li><strong>Integrations with Third-Party Services:</strong> Interact with third-party services without making the user wait for the operation to complete.</li></ul><h2 id="conclusion">Conclusion</h2><p>Active Job in Ruby on Rails provides a powerful yet simple interface for handling background jobs. By abstracting away the complexities of different backend systems, it allows developers to focus on writing business logic rather than managing job queues. Whether you&apos;re sending emails, processing data, or interacting with external services, Active Job has you covered.</p><p>By following the steps in this guide, you can easily implement background job processing in your Rails applications, improving performance and enhancing the user experience.</p><p>For more detailed information, refer to the official <a href="https://guides.rubyonrails.org/active_job_basics.html?ref=gingertechblog.com">Ruby on Rails Active Job documentation</a>.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Ruby on Rails: Utilizing Hotwire (Turbo and Stimulus) to Create Dynamic and Seamless Web Experiences]]></title><description><![CDATA[<p>Creating fast, dynamic, and seamless web experiences has become the standard in modern web development. Users expect real-time interactions and smooth page transitions. Fortunately, <a href="https://hotwired.dev/?ref=gingertechblog.com">Hotwire</a>&#x2014;a framework created by the Basecamp team&#x2014;provides a solution that enables developers to build such experiences without the heavy use of JavaScript.</p>]]></description><link>https://www.gingertechblog.com/ruby-on-rails-utilizing-hotwire-turbo-and-stimulus-to-create-dynamic-and-seamless-web-experiences/</link><guid isPermaLink="false">66c661c46534601a6e416e0f</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Hotwire]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Turbo]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Wed, 21 Aug 2024 22:20:34 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/08/hotwire.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/08/hotwire.webp" alt="Ruby on Rails: Utilizing Hotwire (Turbo and Stimulus) to Create Dynamic and Seamless Web Experiences"><p>Creating fast, dynamic, and seamless web experiences has become the standard in modern web development. Users expect real-time interactions and smooth page transitions. Fortunately, <a href="https://hotwired.dev/?ref=gingertechblog.com">Hotwire</a>&#x2014;a framework created by the Basecamp team&#x2014;provides a solution that enables developers to build such experiences without the heavy use of JavaScript. Hotwire combines <strong>Turbo</strong> and <strong>Stimulus</strong> to provide a streamlined approach to creating reactive interfaces while keeping your code maintainable.</p><p>In this blog post, we&apos;ll explore how to use Turbo and Stimulus within the Hotwire framework to enhance your web applications, specifically focusing on creating dynamic and seamless user experiences.</p><h2 id="what-is-hotwire">What is Hotwire?</h2><p>Hotwire is an umbrella for several technologies that work together to create fast, modern web applications. The core components of Hotwire are:</p><ol><li><strong>Turbo</strong>: Turbo speeds up web applications by making navigation instantaneous without the need for a full-page reload. It replaces the need for most of your custom JavaScript code by handling the client-side transitions and updating the necessary parts of the DOM.</li><li><strong>Stimulus</strong>: Stimulus is a modest JavaScript framework that enhances the dynamic aspects of your web pages. It allows you to connect JavaScript behavior to HTML elements with minimal code, making it easier to manage front-end interactions.</li></ol><p>Together, Turbo and Stimulus offer a powerful yet straightforward approach to building reactive web applications.</p><h2 id="setting-up-hotwire-in-your-project">Setting Up Hotwire in Your Project</h2><p>To start using Hotwire, you&apos;ll need to add it to your project. If you&apos;re working with a Rails application, adding Hotwire is as simple as including the <code>hotwire-rails</code> gem.</p><pre><code class="language-bash"># Gemfile
gem &apos;hotwire-rails&apos;
</code></pre><p>After adding the gem, run <code>bundle install</code> and then:</p><pre><code class="language-bash">rails hotwire:install
</code></pre><p>For non-Rails projects, you can include Turbo and Stimulus via npm or yarn:</p><pre><code class="language-bash">npm install @hotwired/turbo @hotwired/stimulus
</code></pre><p>Or with yarn:</p><pre><code class="language-bash">yarn add @hotwired/turbo @hotwired/stimulus
</code></pre><h2 id="using-turbo-to-enhance-page-navigation">Using Turbo to Enhance Page Navigation</h2><p>Turbo drives page navigation by intercepting links and forms to update the page dynamically. For example, instead of rendering a new page on form submission, Turbo will update only the part of the page that has changed, creating a seamless experience.</p><p>Here&apos;s a basic example of how to use Turbo in a Rails app:</p><pre><code class="language-erb">&lt;!-- app/views/posts/index.html.erb --&gt;
&lt;%= link_to &apos;New Post&apos;, new_post_path, data: { turbo_frame: &apos;modal&apos; } %&gt;

&lt;div id=&quot;modal&quot;&gt;
  &lt;%= turbo_frame_tag &apos;modal&apos; do %&gt;
    &lt;!-- This will be replaced with the new content --&gt;
  &lt;% end %&gt;
&lt;/div&gt;
</code></pre><p>In this example, clicking the &quot;New Post&quot; link will update the content inside the <code>#modal</code> div without reloading the entire page. Turbo ensures that the user experience remains smooth and uninterrupted.</p><h2 id="stimulus-for-dynamic-interactions">Stimulus for Dynamic Interactions</h2><p>Stimulus complements Turbo by handling more complex JavaScript behaviors. It allows you to attach controllers to HTML elements, which then manage the interaction with those elements.</p><p>Here&#x2019;s a simple example of a Stimulus controller that toggles the visibility of an element:</p><pre><code class="language-javascript">// app/javascript/controllers/toggle_controller.js
import { Controller } from &quot;@hotwired/stimulus&quot;

export default class extends Controller {
  static targets = [ &quot;content&quot; ]

  toggle() {
    this.contentTarget.classList.toggle(&quot;hidden&quot;)
  }
}
</code></pre><p>And in your HTML:</p><pre><code class="language-erb">&lt;!-- app/views/posts/index.html.erb --&gt;
&lt;button data-controller=&quot;toggle&quot; data-action=&quot;click-&gt;toggle#toggle&quot;&gt;
  Toggle Content
&lt;/button&gt;

&lt;div data-toggle-target=&quot;content&quot; class=&quot;hidden&quot;&gt;
  This content is toggled.
&lt;/div&gt;
</code></pre><p>With this setup, clicking the button will toggle the visibility of the content, all handled by the Stimulus controller.</p><h2 id="combining-turbo-and-stimulus-for-seamless-experiences">Combining Turbo and Stimulus for Seamless Experiences</h2><p>The real power of Hotwire is realized when Turbo and Stimulus are used together. For example, you can use Turbo to load new content dynamically and Stimulus to add interactivity to that content.</p><p>Imagine a scenario where you load new items into a list using Turbo and then allow users to interact with those items using Stimulus controllers:</p><pre><code class="language-erb">&lt;!-- app/views/items/index.html.erb --&gt;
&lt;div id=&quot;item-list&quot;&gt;
  &lt;%= turbo_frame_tag &apos;item_list&apos; do %&gt;
    &lt;%= render @items %&gt;
  &lt;% end %&gt;
&lt;/div&gt;

&lt;%= link_to &apos;Load More&apos;, items_path(page: next_page), data: { turbo_frame: &apos;item_list&apos; } %&gt;
</code></pre><pre><code class="language-erb">&lt;!-- app/views/items/_item.html.erb --&gt;
&lt;div data-controller=&quot;item&quot; data-item-id=&quot;&lt;%= item.id %&gt;&quot;&gt;
  &lt;%= item.name %&gt;
  &lt;button data-action=&quot;click-&gt;item#remove&quot;&gt;Remove&lt;/button&gt;
&lt;/div&gt;
</code></pre><pre><code class="language-javascript">// app/javascript/controllers/item_controller.js
import { Controller } from &quot;@hotwired/stimulus&quot;

export default class extends Controller {
  remove() {
    this.element.remove()
  }
}
</code></pre><p>Here, Turbo handles loading more items without a full page reload, and Stimulus allows users to remove items from the list with a click.</p><h2 id="conclusion">Conclusion</h2><p>Hotwire&apos;s Turbo and Stimulus offer a powerful combination for building modern, dynamic web applications. By reducing the need for custom JavaScript and relying on these lightweight tools, you can create seamless user experiences that are easier to maintain.</p><p>Whether you&apos;re building a new project or enhancing an existing one, consider using Hotwire to streamline your development process and deliver fast, reactive web applications.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[30 Essential JavaScript Methods You Should Know]]></title><description><![CDATA[<p>JavaScript, the powerful language that powers the web, is packed with useful methods that can make your coding life easier. Here, we&#x2019;ll explore 30 essential JavaScript methods, complete with examples and explanations to help you understand how to use them in your projects.</p><h2 id="table-of-contents">Table of Contents</h2><ul><li><a>Array Methods</a></li></ul>]]></description><link>https://www.gingertechblog.com/30-essential-javascript-methods-you-should-know/</link><guid isPermaLink="false">66bf35836534601a6e416dc0</guid><category><![CDATA[Methods]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Training]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Fri, 16 Aug 2024 12:28:44 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/08/javascript_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/08/javascript_blog.webp" alt="30 Essential JavaScript Methods You Should Know"><p>JavaScript, the powerful language that powers the web, is packed with useful methods that can make your coding life easier. Here, we&#x2019;ll explore 30 essential JavaScript methods, complete with examples and explanations to help you understand how to use them in your projects.</p><h2 id="table-of-contents">Table of Contents</h2><ul><li><a>Array Methods</a><ul><li><a>push()</a></li><li><a>pop()</a></li><li><a>shift()</a></li><li><a>unshift()</a></li><li><a>map()</a></li><li><a>filter()</a></li><li><a>reduce()</a></li><li><a>forEach()</a></li><li><a>find()</a></li><li><a>findIndex()</a></li><li><a>includes()</a></li><li><a>indexOf()</a></li><li><a>concat()</a></li><li><a>slice()</a></li><li><a>splice()</a></li></ul></li><li><a>String Methods</a><ul><li><a>split()</a></li><li><a>trim()</a></li><li><a>replace()</a></li><li><a>toUpperCase()</a></li><li><a>toLowerCase()</a></li></ul></li><li><a>Object Methods</a><ul><li><a>keys()</a></li><li><a>values()</a></li><li><a>entries()</a></li><li><a>assign()</a></li><li><a>freeze()</a></li><li><a>seal()</a></li></ul></li><li><a>Math Methods</a><ul><li><a>random()</a></li><li><a>floor()</a></li><li><a>ceil()</a></li><li><a>max()</a></li></ul></li></ul><hr><h2 id="array-methods">Array Methods</h2><h3 id="1-arrayprototypepush">1. <code>Array.prototype.push()</code></h3><p><strong>Purpose</strong>: Adds one or more elements to the end of an array.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;];
fruits.push(&apos;orange&apos;);
console.log(fruits); // [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;]
</code></pre><h3 id="2-arrayprototypepop">2. <code>Array.prototype.pop()</code></h3><p><strong>Purpose</strong>: Removes the last element from an array and returns that element.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
let lastFruit = fruits.pop();
console.log(lastFruit); // &apos;orange&apos;
console.log(fruits); // [&apos;apple&apos;, &apos;banana&apos;]
</code></pre><h3 id="3-arrayprototypeshift">3. <code>Array.prototype.shift()</code></h3><p><strong>Purpose</strong>: Removes the first element from an array and returns that element.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
let firstFruit = fruits.shift();
console.log(firstFruit); // &apos;apple&apos;
console.log(fruits); // [&apos;banana&apos;, &apos;orange&apos;]
</code></pre><h3 id="4-arrayprototypeunshift">4. <code>Array.prototype.unshift()</code></h3><p><strong>Purpose</strong>: Adds one or more elements to the beginning of an array.</p><pre><code class="language-javascript">let fruits = [&apos;banana&apos;, &apos;orange&apos;];
fruits.unshift(&apos;apple&apos;);
console.log(fruits); // [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;]
</code></pre><h3 id="5-arrayprototypemap">5. <code>Array.prototype.map()</code></h3><p><strong>Purpose</strong>: Creates a new array with the results of calling a function on every element in the original array.</p><p><strong>Example with Arrays:</strong></p><p>Let&apos;s say you have an array of numbers, and you want to create a new array where each number is doubled.</p><pre><code class="language-javascript">let numbers = [1, 2, 3, 4];
let doubled = numbers.map(n =&gt; n * 2);
console.log(doubled); // [2, 4, 6, 8]
</code></pre><p>In this example, map() takes each number in the numbers array, multiplies it by 2, and returns a new array with the doubled values.</p><p><strong>Example with Objects:</strong></p><p>Now, let&apos;s see how map() can be used with an object. Suppose you have an object representing the prices of different products, and you want to apply a discount to each price.</p><pre><code class="language-javascript">let prices = {
  apple: 1.0,
  banana: 0.5,
  cherry: 2.0
};

let discountedPrices = Object.fromEntries(
  Object.entries(prices).map(([key, value]) =&gt; [key, value * 0.8])
);

console.log(discountedPrices); 
// { apple: 0.8, banana: 0.4, cherry: 1.6 }
</code></pre><p>Here, Object.entries(prices) converts the object into an array of key-value pairs. map() then applies a 20% discount to each price, and Object.fromEntries() converts the result back into an object.</p><h3 id="6-arrayprototypefilter">6. <code>Array.prototype.filter()</code></h3><p><strong>Purpose</strong>: Creates a new array with all elements that pass the test implemented by the provided function.</p><p><strong>Example with Arrays:</strong></p><p>Suppose you have an array of numbers and want to filter out the odd numbers, keeping only the even numbers.</p><pre><code class="language-javascript">let numbers = [1, 2, 3, 4];
let even = numbers.filter(n =&gt; n % 2 === 0);
console.log(even); // [2, 4]
</code></pre><p>In this example, filter() checks each number in the array and only includes the numbers that are even in the new array.</p><p><strong>Example with Objects:</strong></p><p>Consider an object where the keys are product names and the values are the quantities available. You want to filter out products that are out of stock (quantity of 0).</p><pre><code class="language-javascript">let inventory = {
  apple: 10,
  banana: 0,
  cherry: 5,
  mango: 0
};

let inStock = Object.fromEntries(
  Object.entries(inventory).filter(([key, value]) =&gt; value &gt; 0)
);

console.log(inStock); 
// { apple: 10, cherry: 5 }
</code></pre><p>In this case, Object.entries(inventory) converts the object into an array of key-value pairs, filter() removes the pairs where the quantity is 0, and Object.fromEntries() converts the filtered array back into an object.</p><h3 id="7-arrayprototypereduce">7. <code>Array.prototype.reduce()</code></h3><p><strong>Purpose</strong>: Executes a reducer function on each element of the array, resulting in a single output value.</p><p><strong>Example with Arrays:</strong></p><p>Let&apos;s say you want to sum all the numbers in an array.</p><pre><code class="language-javascript">let numbers = [1, 2, 3, 4];
let sum = numbers.reduce((acc, n) =&gt; acc + n, 0);
console.log(sum); // 10
</code></pre><p>In this example, reduce() iterates through each number in the array, adding it to the accumulator (acc), which starts at 0. The final result is the sum of all the numbers.</p><p><strong>Example with Objects:</strong></p><p>Now, consider you have an object representing the scores of players in a game, and you want to calculate the total score.</p><pre><code class="language-javascript">let scores = {
  player1: 10,
  player2: 15,
  player3: 7
};

let totalScore = Object.entries(scores).reduce((acc, [key, value]) =&gt; acc + value, 0);

console.log(totalScore); // 32
</code></pre><p>Here, Object.entries(scores) converts the object into an array of key-value pairs, and reduce() sums up all the values (scores) to produce a total score.</p><h3 id="8-arrayprototypeforeach">8. <code>Array.prototype.forEach()</code></h3><p><strong>Purpose</strong>: Executes a provided function once for each array element.</p><pre><code class="language-javascript">let numbers = [1, 2, 3];
numbers.forEach(n =&gt; console.log(n));
// 1
// 2
// 3
</code></pre><h3 id="9-arrayprototypefind">9. <code>Array.prototype.find()</code></h3><p><strong>Purpose</strong>: Returns the first element that passes the test provided by the function.</p><pre><code class="language-javascript">let numbers = [1, 2, 3, 4];
let found = numbers.find(n =&gt; n &gt; 2);
console.log(found); // 3
</code></pre><h3 id="10-arrayprototypefindindex">10. <code>Array.prototype.findIndex()</code></h3><p><strong>Purpose</strong>: Returns the index of the first element that passes the test provided by the function.</p><pre><code class="language-javascript">let numbers = [1, 2, 3, 4];
let index = numbers.findIndex(n =&gt; n &gt; 2);
console.log(index); // 2
</code></pre><h3 id="11-arrayprototypeincludes">11. <code>Array.prototype.includes()</code></h3><p><strong>Purpose</strong>: Determines whether an array includes a certain value among its entries.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
console.log(fruits.includes(&apos;banana&apos;)); // true
console.log(fruits.includes(&apos;grape&apos;)); // false
</code></pre><h3 id="12-arrayprototypeindexof">12. <code>Array.prototype.indexOf()</code></h3><p><strong>Purpose</strong>: Returns the first index at which a given element can be found in the array.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
console.log(fruits.indexOf(&apos;banana&apos;)); // 1
</code></pre><h3 id="13-arrayprototypeconcat">13. <code>Array.prototype.concat()</code></h3><p><strong>Purpose</strong>: Merges two or more arrays and returns a new array.</p><pre><code class="language-javascript">let array1 = [1, 2];
let array2 = [3, 4];
let combined = array1.concat(array2);
console.log(combined); // [1, 2, 3, 4]
</code></pre><h3 id="14-arrayprototypeslice">14. <code>Array.prototype.slice()</code></h3><p><strong>Purpose</strong>: Returns a shallow copy of a portion of an array into a new array.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;, &apos;grape&apos;];
let citrus = fruits.slice(1, 3);
console.log(citrus); // [&apos;banana&apos;, &apos;orange&apos;]
</code></pre><h3 id="15-arrayprototypesplice">15. <code>Array.prototype.splice()</code></h3><p><strong>Purpose</strong>: Adds/removes items to/from an array and returns the removed items.</p><pre><code class="language-javascript">let fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
fruits.splice(1, 1, &apos;grape&apos;);
console.log(fruits); // [&apos;apple&apos;, &apos;grape&apos;, &apos;orange&apos;]
</code></pre><hr><h2 id="string-methods">String Methods</h2><h3 id="16-stringprototypesplit">16. <code>String.prototype.split()</code></h3><p><strong>Purpose</strong>: Splits a string into an array of substrings.</p><pre><code class="language-javascript">let str = &apos;hello world&apos;;
let words = str.split(&apos; &apos;);
console.log(words); // [&apos;hello&apos;, &apos;world&apos;]
</code></pre><h3 id="17-stringprototypetrim">17. <code>String.prototype.trim()</code></h3><p><strong>Purpose</strong>: Removes whitespace from both ends of a string.</p><pre><code class="language-javascript">let str = &apos;   hello world   &apos;;
let trimmed = str.trim();
console.log(trimmed); // &apos;hello world&apos;
</code></pre><h3 id="18-stringprototypereplace">18. <code>String.prototype.replace()</code></h3><p><strong>Purpose</strong>: Replaces a substring with a new substring in a string.</p><pre><code class="language-javascript">let str = &apos;hello world&apos;;
let newStr = str.replace(&apos;world&apos;, &apos;JavaScript&apos;);
console.log(newStr); // &apos;hello JavaScript&apos;
</code></pre><h3 id="19-stringprototypetouppercase">19. <code>String.prototype.toUpperCase()</code></h3><p><strong>Purpose</strong>: Converts a string to uppercase letters.</p><pre><code class="language-javascript">let str = &apos;hello&apos;;
let upperStr = str.toUpperCase();
console.log(upperStr); // &apos;HELLO&apos;
</code></pre><h3 id="20-stringprototypetolowercase">20. <code>String.prototype.toLowerCase()</code></h3><p><strong>Purpose</strong>: Converts a string to lowercase letters.</p><pre><code class="language-javascript">let str = &apos;HELLO&apos;;
let lowerStr = str.toLowerCase();
console.log(lowerStr); // &apos;hello&apos;
</code></pre><hr><h2 id="object-methods">Object Methods</h2><h3 id="21-objectkeys">21. <code>Object.keys()</code></h3><p><strong>Purpose</strong>: Returns an array of a given object&apos;s property names.</p><pre><code class="language-javascript">let obj = {a: 1, b: 2, c: 3};
let keys = Object.keys(obj);
console.log(keys); // [&apos;a&apos;, &apos;b&apos;, &apos;c&apos;]
</code></pre><h3 id="22-objectvalues">22. <code>Object.values()</code></h3><p><strong>Purpose</strong>: Returns an array of a given object&apos;s property values.</p><pre><code class="language-javascript">let obj = {a: 1, b: 2, c: 3};
let values = Object.values(obj);
console.log(values); // [1, 2, 3]
</code></pre><h3 id="23-objectentries">23. <code>Object.entries()</code></h3><p><strong>Purpose</strong>: Returns an array of a given object&apos;s own enumerable string-keyed property <code>[key, value]</code> pairs.</p><pre><code class="language-javascript">let obj = {a: 1, b: 2, c: 3};
let entries = Object.entries(obj);
console.log(entries); // [[&apos;a&apos;, 1], [&apos;b&apos;, 2], [&apos;c&apos;, 3]]
</code></pre><h3 id="24-objectassign">24. <code>Object.assign()</code></h3><p><strong>Purpose</strong>: Copies the values of all enumerable own properties from one or more source objects to a target object.</p><pre><code class="language-javascript">let target = {a: 1};
let source = {b: 2, c: 3};
Object.assign(target, source);
console.log(target); // {a: 1, b: 2, c: 3}
</code></pre><h3 id="25-objectfreeze">25. <code>Object.freeze()</code></h3><p><strong>Purpose</strong>: Freezes an object, preventing new properties from being added and existing properties from being removed or changed.</p><pre><code class="language-javascript">let obj = {a: 1};
Object.freeze(obj);
obj.a = 2; // This will have no effect
console.log(obj.a); // 1
</code></pre><h3 id="26-objectseal">26. <code>Object.seal()</code></h3><p><strong>Purpose</strong>: Seals an object, preventing new properties from being added but allowing existing properties to be modified.</p><pre><code class="language-javascript">let obj = {a: 1};
Object.seal(obj);
obj.a = 2;
console.log(obj.a); // 2
</code></pre><hr><h2 id="math-methods">Math Methods</h2><h3 id="27-mathrandom">27. <code>Math.random()</code></h3><p><strong>Purpose</strong>: Returns a random floating-point number between 0 (inclusive) and 1 (exclusive).</p><pre><code class="language-javascript">let randomNum = Math.random();
console.log(randomNum); // A random number between 0 and 1
</code></pre><h3 id="28-mathfloor">28. <code>Math.floor()</code></h3><p><strong>Purpose</strong>: Returns the largest integer less than or equal to a given number.</p><pre><code class="language-javascript">let num = 5.9;
let floored = Math.floor(num);
console.log(floored); // 5
</code></pre><h3 id="29-mathceil">29. <code>Math.ceil()</code></h3><p><strong>Purpose</strong>: Returns the smallest integer greater than or equal to a given number.</p><pre><code class="language-javascript">let num = 5.1;
let ceiled = Math.ceil(num);
console.log(ceiled); // 6
</code></pre><h3 id="30-mathmax">30. <code>Math.max()</code></h3><p><strong>Purpose</strong>: Returns the largest of zero or more numbers.</p><pre><code class="language-javascript">let maxNum = Math.max(1, 3, 2);
console.log(maxNum); // 3
</code></pre><hr><h2 id="conclusion">Conclusion</h2><p>JavaScript offers a vast array of methods that can greatly simplify and enhance your development process. Understanding and effectively utilizing these methods&#x2014;whether for arrays, strings, objects, or mathematical operations&#x2014;allows you to write cleaner, more efficient, and more readable code.</p><p>Array methods like map(), filter(), and reduce() provide powerful ways to manipulate data collections, enabling transformations, filtering, and aggregation with minimal code. Similarly, string methods help you handle and format text effortlessly, while object methods give you the tools to manage and transform complex data structures. Math methods, though often overlooked, are essential for performing common numerical operations, from rounding to generating random values.</p><p>By mastering these 30 essential methods, you&apos;ll be better equipped to tackle everyday coding challenges, streamline your codebase, and write JavaScript that is both concise and powerful. As you continue to develop your skills, experimenting with these methods in different contexts will deepen your understanding and allow you to discover even more creative ways to solve problems with JavaScript.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Introduction to Scala: The Scalable Language for Modern Developers]]></title><description><![CDATA[<p>Scala, short for &quot;scalable language,&quot; is a powerful and flexible programming language that combines the best features of both object-oriented and functional programming paradigms. Since its inception in 2003 by Martin Odersky, Scala has been steadily gaining popularity, especially in industries that require highly concurrent and scalable applications,</p>]]></description><link>https://www.gingertechblog.com/introduction-to-scala-the-scalable-language-for-modern-developers/</link><guid isPermaLink="false">66b605c06534601a6e416d80</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Scala]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Fri, 09 Aug 2024 12:55:12 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/08/scala_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/08/scala_blog.webp" alt="Introduction to Scala: The Scalable Language for Modern Developers"><p>Scala, short for &quot;scalable language,&quot; is a powerful and flexible programming language that combines the best features of both object-oriented and functional programming paradigms. Since its inception in 2003 by Martin Odersky, Scala has been steadily gaining popularity, especially in industries that require highly concurrent and scalable applications, such as finance, data engineering, and telecommunications.</p><p>In this blog post, we will explore what makes Scala unique, its key features, and why it might be the right choice for your next project.</p><h2 id="why-scala">Why Scala?</h2><h2 id="1-combination-of-object-oriented-and-functional-programming">1. <strong>Combination of Object-Oriented and Functional Programming</strong></h2><p>Scala seamlessly integrates object-oriented and functional programming into one concise, high-level language. This dual nature allows developers to leverage the strengths of both paradigms:</p><h3 id="object-oriented">Object-Oriented</h3><p>Scala fully supports object-oriented programming, allowing you to create classes, objects, and traits (Scala&apos;s version of interfaces).</p><p>Here&apos;s an example of how you might use these features:</p><pre><code class="language-scala">// Define a class with properties and methods
class Animal(val name: String, val sound: String) {
  def makeSound(): Unit = {
    println(s&quot;The $name goes &apos;$sound&apos;&quot;)
  }
}

// Define a trait (similar to an interface in Java)
trait Pet {
  def ownerName: String
}

// Extend the Animal class and mix in the Pet trait
class Dog(name: String, owner: String) extends Animal(name, &quot;bark&quot;) with Pet {
  def ownerName: String = owner
}

// Create an instance of Dog and use it
val myDog = new Dog(&quot;Buddy&quot;, &quot;John&quot;)
myDog.makeSound() // Output: The Buddy goes &apos;bark&apos;
println(s&quot;${myDog.name} is owned by ${myDog.ownerName}&quot;) // Output: Buddy is owned by John

</code></pre><p><strong>Explanation:</strong></p><ul><li>Class Definition: <code>Animal</code> is a class with two properties, <code>name</code> and <code>sound</code>, and a method <code>makeSound</code>.</li><li>Trait: <code>Pet</code> is a trait that defines a contract for any class that mixes it in.</li><li>Inheritance and Composition: <code>Dog</code> extends the <code>Animal</code> class and mixes in the <code>Pet</code> trait, demonstrating both inheritance and composition.</li></ul><h3 id="functional">Functional</h3><p>Scala treats functions as first-class citizens, meaning you can pass them as arguments, return them from other functions, and assign them to variables.</p><p>Here&apos;s an example that highlights the functional programming approach:</p><pre><code class="language-scala">// Define a function that applies a given function to a list of numbers
def applyOperation(numbers: List[Int], operation: Int =&gt; Int): List[Int] = {
  numbers.map(operation)
}

// Define a function that doubles a number
val double: Int =&gt; Int = _ * 2

// Use the applyOperation function with the double function
val result = applyOperation(List(1, 2, 3, 4, 5), double)
println(result) // Output: List(2, 4, 6, 8, 10)

</code></pre><p><strong>Explanation:</strong></p><ul><li>Higher-Order Functions: <code>applyOperation</code> is a higher-order function that takes another function (<code>operation</code>) as a parameter and applies it to each element in the numbers list.</li><li>Anonymous Functions: The function <code>double</code> is defined using a concise lambda expression <code>_ * 2</code>.</li><li>Immutability: The list <code>result</code> is immutable, which is a common practice in functional programming to avoid side effects.</li></ul><h2 id="2-statically-typed-yet-concise">2. <strong>Statically Typed Yet Concise</strong></h2><p>Scala&apos;s type system is powerful, but the language also provides mechanisms to keep your code concise.</p><p><strong>Here&#x2019;s an example demonstrating concise statically-typed code in Scala.</strong></p><pre><code class="language-scala">// Define a list of integers with type inference
val numbers = List(1, 2, 3, 4, 5)

// Define a function with type inference
val sum = numbers.reduce(_ + _)
println(sum) // Output: 15

// Explicit types
val explicitNumbers: List[Int] = List(1, 2, 3, 4, 5)
val explicitSum: Int = explicitNumbers.reduce((a: Int, b: Int) =&gt; a + b)
println(explicitSum) // Output: 15

</code></pre><p><strong>Explanation:</strong></p><ul><li>Type Inference: In Scala, you don&apos;t need to specify types explicitly; the compiler infers them. This makes the code more concise.</li><li>Concise Syntax: The function <code>_ + _</code> is a shorthand for a lambda function that takes two arguments and returns their sum.<br><br><br></li></ul><p><strong>Java Example (Verbose Alternative):</strong></p><pre><code class="language-java">import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Verbose list definition
        List&lt;Integer&gt; numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // Verbose sum calculation
        int sum = 0;
        for (int number : numbers) {
            sum += number;
        }
        System.out.println(sum); // Output: 15
    }
}

</code></pre><p><strong>Explanation:</strong></p><ul><li>Explicit Typing: Java requires explicit types, which can make the code more verbose.</li><li>Imperative Loop: The sum is calculated using a for-loop, which is more verbose compared to Scala&#x2019;s functional approach.<br><br><br></li></ul><p><strong>Contrasting Example of Non-Concise, Verbose, and Dynamic Typing (in Python)</strong></p><p>Here&#x2019;s a similar example in Python to show how dynamic typing can lead to different practices:</p><pre><code class="language-python"># Dynamic typing allows for implicit type assignment
numbers = [1, 2, 3, 4, 5]

# Using a loop for summation (less concise than Scala&apos;s reduce)
sum = 0
for number in numbers:
    sum += number

print(sum)  # Output: 15

</code></pre><p><strong>Explanation:</strong></p><ul><li>Dynamic Typing: Python doesn&#x2019;t require explicit type declarations, which can make the code concise but potentially error-prone if types are not what you expect.</li><li>Less Functional: The imperative style with a loop is less concise compared to Scala&apos;s functional approach using reduce.</li></ul><p></p><p>These examples highlight how Scala allows you to write powerful, concise, and maintainable code using both object-oriented and functional paradigms while maintaining the safety and performance benefits of static typing.</p><h2 id="3-interoperability-with-java">3. <strong>Interoperability with Java</strong></h2><p>One of Scala&apos;s most significant advantages is its seamless interoperability with Java. Scala runs on the Java Virtual Machine (JVM), which means you can use all existing Java libraries and frameworks. This feature makes Scala an attractive option for teams transitioning from Java to a more modern language.</p><h2 id="4-concurrency-support">4. <strong>Concurrency Support</strong></h2><p>In modern computing, efficiently handling multiple tasks at once (concurrency) is crucial, especially in applications like web servers, real-time data processing, and complex simulations. Scala excels in this area by providing high-level abstractions for concurrency, allowing developers to write code that is both expressive and safe from common concurrency pitfalls like race conditions.</p><h3 id="futures">Futures</h3><p>A <code>Future</code> in Scala represents a computation that may complete at some point in the future, either successfully or with an error. It allows you to perform asynchronous, non-blocking operations and easily manage the result once it&apos;s available.</p><p><strong>Basic Example of a Future:</strong></p><pre><code class="language-scala">import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

// Define a Future that performs an asynchronous computation
val future = Future {
  Thread.sleep(1000)  // Simulate a long-running task
  42                  // Return the result
}

// Handle the result once the future is completed
future.onComplete {
  case scala.util.Success(value) =&gt; println(s&quot;Success: $value&quot;)
  case scala.util.Failure(exception) =&gt; println(s&quot;Failed with exception: $exception&quot;)
}

// Output (after 1 second): Success: 42

</code></pre><p><strong>Explanation:</strong></p><ul><li>Asynchronous Computation: The <code>Future</code> wraps a block of code that will be executed asynchronously.</li><li>Non-blocking: The main program continues to execute without waiting for the <code>Future</code> to complete.</li><li>Completion Handling: The <code>onComplete</code> method is used to handle the result of the <code>Future</code> once it&apos;s done, allowing you to react to both success and failure.</li></ul><h3 id="composing-futures">Composing Futures</h3><p>Futures can be easily composed, allowing you to chain multiple asynchronous operations together.</p><pre><code class="language-scala">import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val future1 = Future { Thread.sleep(500); 10 }
val future2 = Future { Thread.sleep(500); 32 }

// Combine the results of two futures
val combinedFuture = for {
  result1 &lt;- future1
  result2 &lt;- future2
} yield result1 + result2

combinedFuture.onComplete {
  case scala.util.Success(value) =&gt; println(s&quot;Combined Result: $value&quot;)
  case scala.util.Failure(exception) =&gt; println(s&quot;Failed with exception: $exception&quot;)
}

// Output (after 1 second): Combined Result: 42

</code></pre><p><strong>Explanation:</strong></p><ul><li>Future Composition: The <code>for-comprehension</code> is used to compose multiple futures, allowing you to wait for both to complete and then combine their results.</li><li>Non-blocking: Even though the computation waits for both futures, it remains non-blocking.</li></ul><h3 id="the-actor-model-with-akka">The Actor Model (with Akka)</h3><p>The Actor model is another powerful abstraction for managing concurrency in Scala. It&#x2019;s a design pattern that helps you manage state and behavior in concurrent systems without worrying about low-level thread management.</p><h3 id="introduction-to-akka-actors">Introduction to Akka Actors:</h3><p>Akka is a popular toolkit in Scala for building concurrent, distributed, and resilient message-driven applications using the Actor model. An Actor in Akka is an independent entity that encapsulates state and behavior. Actors communicate by exchanging messages asynchronously, ensuring that no two actors share state directly, thus avoiding race conditions.</p><pre><code class="language-scala">import akka.actor.{Actor, ActorSystem, Props}

// Define an Actor class
class Counter extends Actor {
  var count = 0

  def receive: Receive = {
    case &quot;increment&quot; =&gt; count += 1
    case &quot;get&quot; =&gt; sender() ! count
  }
}

// Create an ActorSystem and an instance of the Counter actor
val system = ActorSystem(&quot;CounterSystem&quot;)
val counter = system.actorOf(Props[Counter], &quot;counter&quot;)

// Send messages to the counter actor
counter ! &quot;increment&quot;
counter ! &quot;increment&quot;
counter ! &quot;get&quot;

// Shutdown the actor system
system.terminate()

</code></pre><p><strong>Explanation:</strong></p><ul><li>Actor Definition: The <code>Counter</code> actor maintains an internal state (<code>count</code>). It can receive messages and modify its state or reply to the sender.</li><li>Message Passing: Actors communicate exclusively through messages, which helps in achieving concurrency without the risks associated with shared mutable state.</li><li>Actor System: <code>ActorSystem</code> is the environment in which actors live. It manages the lifecycle and execution of actors.</li></ul><h3 id="scaling-with-akka-actors">Scaling with Akka Actors:</h3><p>Actors can easily be distributed across multiple nodes, making it possible to build highly scalable systems. Akka takes care of the complexities of networking, ensuring that messages are reliably delivered even in distributed environments.</p><pre><code class="language-scala">import akka.actor.{Actor, ActorSystem, Props}

// Define a message protocol
case class Increment(value: Int)
case object GetCount

// Define an Actor with more complex behavior
class AdvancedCounter extends Actor {
  var count = 0

  def receive: Receive = {
    case Increment(value) =&gt; count += value
    case GetCount =&gt; sender() ! count
  }
}

// Create an ActorSystem and the AdvancedCounter actor
val system = ActorSystem(&quot;AdvancedCounterSystem&quot;)
val counter = system.actorOf(Props[AdvancedCounter], &quot;advancedCounter&quot;)

// Interact with the AdvancedCounter actor
counter ! Increment(10)
counter ! Increment(5)
counter ! GetCount

// Shutdown the actor system
system.terminate()

</code></pre><p><strong>Explanation:</strong></p><ul><li>Custom Messages: The <code>AdvancedCounter</code> actor handles custom messages (<code>Increment</code> and <code>GetCount</code>), allowing more flexible interaction.</li><li>Concurrency Management: Akka&#x2019;s message-passing model ensures that all operations are handled sequentially within an actor, avoiding race conditions.</li></ul><h3 id="when-to-use-futures-vs-actors">When to Use Futures vs. Actors</h3><ul><li>Futures: Ideal for simple, stateless asynchronous computations where you need to perform a task in the background and eventually retrieve a result.</li><li>Actors: Best suited for scenarios where you need to manage state across multiple concurrent tasks or when building complex, distributed systems that require fault tolerance and resilience.</li></ul><h2 id="5-advanced-type-system">5. <strong>Advanced Type System</strong></h2><p>Scala&#x2019;s type system goes beyond the basics of static typing. It includes features like:</p><ul><li><strong>Case Classes:</strong> For immutable data structures.</li><li><strong>Pattern Matching:</strong> For concise and readable code.</li><li><strong>Traits:</strong> To create modular and reusable code components.</li></ul><p>These features empower developers to write robust, maintainable, and error-free code.</p><h2 id="use-cases-for-scala">Use Cases for Scala</h2><p>Scala shines in several areas, making it a go-to language for various types of projects:</p><ol><li><strong>Big Data Processing:</strong> Scala is the language of choice for Apache Spark, a leading framework for big data processing.</li><li><strong>Web Development:</strong> Frameworks like Play provide a solid foundation for building scalable web applications.</li><li><strong>Data Science:</strong> With libraries like Breeze and Spire, Scala is becoming increasingly popular in data science and machine learning circles.</li><li><strong>Reactive Systems:</strong> The Akka framework, built in Scala, is ideal for creating highly concurrent, distributed, and fault-tolerant systems.</li></ol><h2 id="getting-started-with-scala">Getting Started with Scala</h2><p>If you&apos;re interested in diving into Scala, here are a few steps to get started:</p><ol><li><strong>Install Scala:</strong> You can install Scala through the official website or use a build tool like sbt (Scala Build Tool).</li><li><strong>Set Up Your Environment:</strong> Consider using an IDE like IntelliJ IDEA, which has excellent support for Scala.</li><li><strong>Learn the Basics:</strong> There are plenty of free resources, including the official <a href="https://docs.scala-lang.org/?ref=gingertechblog.com">Scala documentation</a>, to help you get started.</li><li><strong>Build Projects:</strong> Start with simple projects and gradually move on to more complex ones, leveraging both object-oriented and functional programming.</li></ol><h2 id="conclusion">Conclusion</h2><p>Scala is a powerful, versatile language that caters to modern development needs, especially in areas requiring scalability and concurrency. Whether you&apos;re a Java developer looking to explore functional programming or a data engineer working with big data, Scala offers the tools and flexibility you need to succeed.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Introduction to Ruby on Rails Action Cable]]></title><description><![CDATA[<p>Ruby on Rails has long been a popular framework for web development due to its simplicity and robustness. One of the features that enhance its capability is Action Cable, a framework that integrates WebSockets with Rails. This allows developers to create real-time applications seamlessly. In this blog post, we&apos;</p>]]></description><link>https://www.gingertechblog.com/introduction-to-ruby-on-rails-action-cable/</link><guid isPermaLink="false">66b4ad386534601a6e416d3e</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Action Cable]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[WebSockets]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Thu, 08 Aug 2024 11:53:21 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/08/action_cable_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/08/action_cable_blog.webp" alt="Introduction to Ruby on Rails Action Cable"><p>Ruby on Rails has long been a popular framework for web development due to its simplicity and robustness. One of the features that enhance its capability is Action Cable, a framework that integrates WebSockets with Rails. This allows developers to create real-time applications seamlessly. In this blog post, we&apos;ll dive into what Action Cable is, how it works, and how you can start using it in your Rails applications.</p><h2 id="what-is-action-cable">What is Action Cable?</h2><p>Action Cable is a WebSocket framework that comes with Ruby on Rails. It allows you to handle real-time features in your Rails applications, such as live notifications, chat applications, and live updates. By integrating WebSockets with Rails, Action Cable enables full-duplex communication channels over a single TCP connection, providing a more interactive experience for users.</p><h3 id="key-features-of-action-cable">Key Features of Action Cable:</h3><ul><li><strong>Seamless Integration:</strong> Works directly with Rails, making it easy to add real-time features without extensive configuration.</li><li><strong>Scalability:</strong> Can handle multiple connections efficiently and integrates with background job processing for offloading tasks.</li><li><strong>Security:</strong> Comes with built-in authentication mechanisms to ensure secure connections.</li></ul><h2 id="setting-up-action-cable">Setting Up Action Cable</h2><p>To get started with Action Cable, you need to follow these steps:</p><h3 id="1-create-a-new-rails-application">1. Create a New Rails Application</h3><p>If you don&#x2019;t already have a Rails application, you can create one by running:</p><pre><code class="language-bash">rails new myapp
cd myapp
</code></pre><h3 id="2-generate-a-channel">2. Generate a Channel</h3><p>Channels are the cornerstone of Action Cable. They act like controllers in your typical Rails MVC structure. Generate a new channel using the Rails generator:</p><pre><code class="language-bash">rails generate channel Chat
</code></pre><p>This command will create a new channel file in <code>app/channels/chat_channel.rb</code> and a corresponding CoffeeScript file in <code>app/assets/javascripts/channels/chat.coffee</code>.</p><h3 id="3-define-the-channel-logic">3. Define the Channel Logic</h3><p>Edit the <code>ChatChannel</code> to define what happens when a user subscribes or performs actions. For example:</p><pre><code class="language-ruby"># app/channels/chat_channel.rb
class ChatChannel &lt; ApplicationCable::Channel
  def subscribed
    stream_from &quot;chat_#{params[:room]}&quot;
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    ActionCable.server.broadcast &quot;chat_#{params[:room]}&quot;, message: data[&apos;message&apos;]
  end
end
</code></pre><h3 id="4-client-side-subscription">4. Client-Side Subscription</h3><p>Edit the <code>chat.coffee</code> file to handle the client-side subscription:</p><pre><code class="language-coffeescript"># app/assets/javascripts/channels/chat.coffee
App.chat = App.cable.subscriptions.create { channel: &quot;ChatChannel&quot;, room: &quot;BestRoom&quot; },
  connected: -&gt;
    # Called when the subscription is ready for use on the server

  disconnected: -&gt;
    # Called when the subscription has been terminated by the server

  received: (data) -&gt;
    # Called when there&apos;s incoming data on the websocket for this channel
    $(&apos;#messages&apos;).append &quot;&lt;p&gt;#{data.message}&lt;/p&gt;&quot;

  speak: (message) -&gt;
    @perform &apos;speak&apos;, message: message
</code></pre><h3 id="5-broadcasting-messages">5. Broadcasting Messages</h3><p>To broadcast messages to the channel, you can use the following command in a Rails controller:</p><pre><code class="language-ruby"># app/controllers/messages_controller.rb
class MessagesController &lt; ApplicationController
  def create
    @message = Message.create!(message_params)
    ActionCable.server.broadcast &quot;chat_BestRoom&quot;, message: @message.content
  end

  private

  def message_params
    params.require(:message).permit(:content)
  end
end
</code></pre><h3 id="6-add-a-front-end-form">6. Add a Front-End Form</h3><p>Create a form in your view to send messages:</p><pre><code class="language-erb">&lt;!-- app/views/messages/index.html.erb --&gt;
&lt;h1&gt;Chat Room&lt;/h1&gt;
&lt;div id=&quot;messages&quot;&gt;
  &lt;!-- Messages will appear here --&gt;
&lt;/div&gt;
&lt;form id=&quot;new_message&quot; action=&quot;/messages&quot; method=&quot;post&quot;&gt;
  &lt;input type=&quot;text&quot; id=&quot;message_content&quot; name=&quot;message[content]&quot;&gt;
  &lt;input type=&quot;submit&quot; value=&quot;Send&quot;&gt;
&lt;/form&gt;

&lt;%= javascript_include_tag &quot;application&quot; %&gt;
</code></pre><h3 id="7-enabling-redis">7. Enabling Redis</h3><p>For production environments, it&#x2019;s recommended to use Redis as the Action Cable adapter. You can set it up by adding <code>redis</code> gem to your Gemfile:</p><pre><code class="language-ruby">gem &apos;redis&apos;, &apos;~&gt; 4.0&apos;
</code></pre><p>And configure it in <code>cable.yml</code>:</p><pre><code class="language-yaml"># config/cable.yml
production:
  adapter: redis
  url: redis://localhost:6379/1
  channel_prefix: myapp_production
</code></pre><h2 id="conclusion">Conclusion</h2><p>Action Cable is a powerful feature of Ruby on Rails that allows developers to add real-time capabilities to their applications with ease. By following the steps outlined in this post, you can start integrating WebSockets into your Rails projects and create dynamic, interactive web applications.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Understanding GraphQL: The Basics and Its Usefulness]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2><p>GraphQL is a query language for APIs and a runtime for executing those queries by using a type system you define for your data. Developed by Facebook in 2012 and released as an open-source project in 2015, GraphQL provides a more efficient, powerful, and flexible alternative to REST.</p><h2 id="what-is-graphql">What</h2>]]></description><link>https://www.gingertechblog.com/understanding-graphql-the-basics-and-its-usefulness/</link><guid isPermaLink="false">66b365976534601a6e416cd5</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[API]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Wed, 07 Aug 2024 12:39:07 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/08/graphql_blog.webp" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://www.gingertechblog.com/content/images/2024/08/graphql_blog.webp" alt="Understanding GraphQL: The Basics and Its Usefulness"><p>GraphQL is a query language for APIs and a runtime for executing those queries by using a type system you define for your data. Developed by Facebook in 2012 and released as an open-source project in 2015, GraphQL provides a more efficient, powerful, and flexible alternative to REST.</p><h2 id="what-is-graphql">What is GraphQL?</h2><p>GraphQL stands for Graph Query Language. It&apos;s a syntax that describes how to ask for data and is generally used to load data from a server to a client. It allows clients to request exactly what they need and nothing more, making it possible to get all required data in a single request.</p><h3 id="key-features-of-graphql">Key Features of GraphQL</h3><ol><li><strong>Hierarchical</strong>: A GraphQL query is hierarchical. The shape of the query matches the shape of the result. This makes it easy to predict what the response will look like.</li><li><strong>Strongly Typed</strong>: Every GraphQL server defines an application-specific schema. Clients can only query with what&#x2019;s allowed by the schema.</li><li><strong>Client-Specified Queries</strong>: Clients have the power to specify not only the structure but also the granularity of the response.</li><li><strong>Introspection</strong>: GraphQL APIs are self-documenting. The type system can be queried to understand the schema, making it easier to work with APIs.</li></ol><h2 id="basics-of-graphql">Basics of GraphQL</h2><h3 id="schema-definition">Schema Definition</h3><p>A GraphQL schema is a model of the data that can be queried through your API. It defines what queries are allowed and what types of objects can be returned.</p><pre><code class="language-graphql">type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String
  age: Int
  friends: [User]
}
</code></pre><h3 id="query-example">Query Example</h3><p>A basic GraphQL query to fetch a user&apos;s name and age might look like this:</p><pre><code class="language-graphql">{
  user(id: &quot;1&quot;) {
    name
    age
  }
}
</code></pre><p>The server&#x2019;s response will match the shape of the query:</p><pre><code class="language-json">{
  &quot;data&quot;: {
    &quot;user&quot;: {
      &quot;name&quot;: &quot;John Doe&quot;,
      &quot;age&quot;: 28
    }
  }
}
</code></pre><h3 id="mutations">Mutations</h3><p>Besides querying for data, GraphQL also supports writing data with mutations. Here&apos;s an example mutation to update a user&apos;s name:</p><pre><code class="language-graphql">mutation {
  updateUser(id: &quot;1&quot;, name: &quot;Jane Doe&quot;) {
    id
    name
  }
}
</code></pre><h2 id="why-graphql-is-useful">Why GraphQL is Useful</h2><ol><li><strong>Efficiency</strong>: Avoids over-fetching or under-fetching data, allowing clients to request exactly what they need.</li><li><strong>Flexibility</strong>: Clients can specify precisely what they require and in what format, reducing the need for multiple endpoints.</li><li><strong>Strongly Typed Schema</strong>: Helps in validating queries against the schema before executing them, reducing runtime errors.</li><li><strong>Versionless API</strong>: With GraphQL, there is no need for versioning. The schema can evolve by deprecating old fields and adding new ones.</li><li><strong>Better Developer Experience</strong>: Built-in tools like GraphiQL make it easier to explore and interact with the API.</li></ol><hr><h2 id="step-by-step-guide-to-setting-up-graphql">Step-by-Step Guide to Setting Up GraphQL</h2><p>Setting up GraphQL involves several steps, including setting up the server, defining the schema, and creating resolvers. This will guide you through setting up a basic GraphQL server using Node.js and Express with the <code>graphql</code> and <code>express-graphql</code> libraries.</p><h4 id="1-install-nodejs-and-npm">1. Install Node.js and npm</h4><p>First, make sure you have Node.js and npm installed. You can download them from the <a href="https://nodejs.org/?ref=gingertechblog.com" rel="noreferrer">official Node.js website</a>.</p><h4 id="2-set-up-a-new-nodejs-project">2. Set Up a New Node.js Project</h4><p>Create a new directory for your project and navigate into it:</p><pre><code class="language-bash">mkdir graphql-server
cd graphql-server
npm init -y</code></pre><h4 id="3-install-required-dependencies">3. Install Required Dependencies</h4><p>Install the necessary packages:</p><pre><code class="language-bash">npm install express express-graphql graphql
</code></pre><h4 id="4-create-the-server">4. Create the Server</h4><p>Create a new file <code>server.js</code> and set up a basic Express server with GraphQL.</p><pre><code class="language-javascript">const express = require(&apos;express&apos;);
const { graphqlHTTP } = require(&apos;express-graphql&apos;);
const { buildSchema } = require(&apos;graphql&apos;);

// Define the schema
const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// Define the root resolver
const root = {
  hello: () =&gt; {
    return &apos;Hello, world!&apos;;
  },
};

const app = express();
app.use(&apos;/graphql&apos;, graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));

app.listen(4000, () =&gt; {
  console.log(&apos;Running a GraphQL API server at http://localhost:4000/graphql&apos;);
});
</code></pre><h4 id="5-run-the-server">5. Run the Server</h4><p>Run the server with the following command:</p><pre><code class="language-bash">node server.js</code></pre><p>You should see a message indicating that the server is running:</p><pre><code class="language-bash">Running a GraphQL API server at http://localhost:4000/graphql</code></pre><h4 id="6-test-your-graphql-server">6. Test Your GraphQL Server</h4><p>Open your browser and navigate to <code>http://localhost:4000/graphql</code>. You should see the GraphiQL interface, which you can use to interact with your GraphQL API.</p><p>In the GraphiQL interface, enter the following query and click the &quot;Execute&quot; button:</p><pre><code class="language-graphql">{
  hello
}
</code></pre><p>You should see the following response:</p><pre><code class="language-json">{
  &quot;data&quot;: {
    &quot;hello&quot;: &quot;Hello, world!&quot;
  }
}
</code></pre><h3 id="adding-more-complexity">Adding More Complexity</h3><p>To demonstrate a more complex example, let&apos;s add a user type with fields and queries.</p><h4 id="update-schema-and-resolvers">Update Schema and Resolvers</h4><p>Modify your <code>server.js</code> to include the new schema and resolvers:</p><pre><code class="language-javascript">const express = require(&apos;express&apos;);
const { graphqlHTTP } = require(&apos;express-graphql&apos;);
const { buildSchema } = require(&apos;graphql&apos;);

// Define the schema
const schema = buildSchema(`
  type User {
    id: ID!
    name: String
    age: Int
  }

  type Query {
    user(id: ID!): User
    users: [User]
  }
`);

// Sample data
const users = [
  { id: 1, name: &apos;John Doe&apos;, age: 28 },
  { id: 2, name: &apos;Jane Doe&apos;, age: 25 },
];

// Define the root resolver
const root = {
  user: ({ id }) =&gt; users.find(user =&gt; user.id == id),
  users: () =&gt; users,
};

const app = express();
app.use(&apos;/graphql&apos;, graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));

app.listen(4000, () =&gt; {
  console.log(&apos;Running a GraphQL API server at http://localhost:4000/graphql&apos;);
});
</code></pre><h4 id="test-the-new-queries">Test the New Queries</h4><p>In the GraphiQL interface, enter the following query to fetch a specific user and all users:</p><pre><code class="language-graphql">{
  user(id: 1) {
    name
    age
  }
  users {
    id
    name
    age
  }
}
</code></pre><p>You should see a response with the user data:</p><pre><code class="language-json">{
  &quot;data&quot;: {
    &quot;user&quot;: {
      &quot;name&quot;: &quot;John Doe&quot;,
      &quot;age&quot;: 28
    },
    &quot;users&quot;: [
      {
        &quot;id&quot;: &quot;1&quot;,
        &quot;name&quot;: &quot;John Doe&quot;,
        &quot;age&quot;: 28
      },
      {
        &quot;id&quot;: &quot;2&quot;,
        &quot;name&quot;: &quot;Jane Doe&quot;,
        &quot;age&quot;: 25
      }
    ]
  }
}
</code></pre><hr><h2 id="conclusion">Conclusion</h2><p>GraphQL offers a modern way to work with APIs by providing a flexible and efficient alternative to REST. Its ability to allow clients to request specific data, its strongly typed schema, and its introspective nature make it a powerful tool for API development. Whether you&apos;re building a new API or working with an existing one, GraphQL can help streamline data fetching and improve overall performance.</p><p>This guide has walked you through the basics of setting up a GraphQL server using Node.js and Express. By following these steps, you can start building more complex GraphQL APIs tailored to your specific needs. GraphQL&apos;s flexibility and efficiency can significantly enhance your API development experience, providing powerful tools for managing data and building robust applications.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Creating a CRUD API with Ruby on Rails and Making API Calls]]></title><description><![CDATA[<p>In this tutorial, we will create a simple API using Ruby on Rails that supports CRUD (Create, Read, Update, Delete) operations. We will also demonstrate how to make API calls to this Rails API and handle the data.</p><h2 id="prerequisites">Prerequisites</h2><p>Before we start, ensure you have the following installed:</p><ul><li>Ruby</li><li>Rails</li></ul>]]></description><link>https://www.gingertechblog.com/creating-a-crud-api-with-ruby-on-rails-and-making-api-calls/</link><guid isPermaLink="false">668ef74f6534601a6e416c74</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[CRUD]]></category><category><![CDATA[API]]></category><category><![CDATA[Postman]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Wed, 10 Jul 2024 21:29:37 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/07/CRUD-api.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/07/CRUD-api.webp" alt="Creating a CRUD API with Ruby on Rails and Making API Calls"><p>In this tutorial, we will create a simple API using Ruby on Rails that supports CRUD (Create, Read, Update, Delete) operations. We will also demonstrate how to make API calls to this Rails API and handle the data.</p><h2 id="prerequisites">Prerequisites</h2><p>Before we start, ensure you have the following installed:</p><ul><li>Ruby</li><li>Rails</li><li>Postman (for testing API calls)</li></ul><h2 id="step-1-setting-up-the-rails-project">Step 1: Setting Up the Rails Project</h2><p>First, create a new Rails project:</p><pre><code class="language-bash">rails new blog_api
cd blog_api
</code></pre><p>Generate a scaffold for a simple resource, <code>Post</code>:</p><pre><code class="language-bash">rails generate scaffold Post title:string content:text
</code></pre><h3 id="setting-up-database">Setting up database</h3><ol><li>Ensure your database and credentials are configured in config/database.yml</li><li>Create the database</li></ol><pre><code class="language-bash">rails db:create
</code></pre><ol start="3"><li>Run the migrations to create the database tables:</li></ol><pre><code class="language-bash">rails db:migrate
</code></pre><h2 id="step-2-configuring-routes">Step 2: Configuring Routes</h2><p>In <code>config/routes.rb</code>, ensure the routes are set up for the posts resource:</p><pre><code class="language-ruby">Rails.application.routes.draw do
  resources :posts
end
</code></pre><h2 id="step-3-modify-the-postscontroller">Step 3: Modify the <code>PostsController</code></h2><p>Update your <code>PostsController</code> to skip CSRF verification for JSON format requests.</p><p>app/controllers/posts_controller.rb</p><pre><code class="language-ruby">class PostsController &lt; ApplicationController
  # Add to controller
  protect_from_forgery unless: -&gt; { request.format.json? }

end
</code></pre><h2 id="step-4-testing-the-api">Step 4: Testing the API</h2><p>Start the Rails server:</p><pre><code class="language-bash">rails s
</code></pre><h3 id="creating-a-post">Creating a Post</h3><p>To create a post, make a POST request to <code>http://localhost:3000/posts</code> with the following JSON body:</p><pre><code class="language-json">{
  &quot;post&quot;: {
    &quot;title&quot;: &quot;My First Post&quot;,
    &quot;content&quot;: &quot;This is the content of my first post.&quot;
  }
}
</code></pre><h3 id="reading-posts">Reading Posts</h3><p>To get a list of posts, make a GET request to <code>http://localhost:3000/posts</code>.</p><h3 id="updating-a-post">Updating a Post</h3><p>To update a post, make a PUT request to <code>http://localhost:3000/posts/1</code> with the following JSON body:</p><pre><code class="language-json">{
  &quot;post&quot;: {
    &quot;title&quot;: &quot;Updated Post&quot;,
    &quot;content&quot;: &quot;This is the updated content.&quot;
  }
}
</code></pre><h3 id="deleting-a-post">Deleting a Post</h3><p>To delete a post, make a DELETE request to <code>http://localhost:3000/posts/1</code>.</p><h2 id="step-5-making-api-calls-with-postman">Step 5: Making API Calls with Postman</h2><h3 id="sending-a-get-request">Sending a GET Request</h3><ol><li>Open Postman and create a new GET request.</li><li>Enter the URL: <code>http://localhost:3000/posts</code></li><li>Click <code>Send</code> and you should see a list of posts.</li></ol><h3 id="sending-a-post-request">Sending a POST Request</h3><ol><li>Create a new POST request in Postman.</li><li>Enter the URL: <code>http://localhost:3000/posts</code></li><li>Go to the <code>Body</code> tab and select <code>raw</code> and <code>JSON</code>.</li><li>Click <code>Send</code> to create a new post.</li></ol><p>Enter the JSON body:</p><pre><code class="language-json">{
  &quot;post&quot;: {
    &quot;title&quot;: &quot;Another Post&quot;,
    &quot;content&quot;: &quot;This is another post.&quot;
  }
}
</code></pre><h3 id="sending-a-put-request">Sending a PUT Request</h3><ol><li>Create a new PUT request in Postman.</li><li>Enter the URL: <code>http://localhost:3000/posts/1</code></li><li>Go to the <code>Body</code> tab and select <code>raw</code> and <code>JSON</code>.</li><li>Click <code>Send</code> to update the post.</li></ol><p>Enter the JSON body:</p><pre><code class="language-json">{
  &quot;post&quot;: {
    &quot;title&quot;: &quot;Updated Post Title&quot;,
    &quot;content&quot;: &quot;This is the updated content of the post.&quot;
  }
}
</code></pre><h3 id="sending-a-delete-request">Sending a DELETE Request</h3><ol><li>Create a new DELETE request in Postman.</li><li>Enter the URL: <code>http://localhost:3000/posts/1</code></li><li>Click <code>Send</code> to delete the post.</li></ol><h2 id="conclusion">Conclusion</h2><p>In this tutorial, we created a simple CRUD API using Ruby on Rails and demonstrated how to make API calls using Postman. This setup provides a foundation for building more complex APIs and interacting with them programmatically.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Building a Ruby GUI Text Messaging App with Twilio and GTK]]></title><description><![CDATA[<p>In this tutorial, we will dive deep into the code of a Ruby GUI text messaging app that uses GTK for the GUI and Twilio for sending and receiving messages. We&apos;ll break down each part of the code to understand its functionality.</p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2024/07/text-app.png" class="kg-image" alt loading="lazy" width="402" height="426"></figure><h2 id="overview">Overview</h2><p>This app is designed to</p>]]></description><link>https://www.gingertechblog.com/building-a-ruby-gui-text-messaging-app-with-twilio-and-gtk/</link><guid isPermaLink="false">668ad4b66534601a6e416c07</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[GUI]]></category><category><![CDATA[Twilio]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Sun, 07 Jul 2024 18:22:04 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/07/Ruby-GUI-text-app.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/07/Ruby-GUI-text-app.webp" alt="Building a Ruby GUI Text Messaging App with Twilio and GTK"><p>In this tutorial, we will dive deep into the code of a Ruby GUI text messaging app that uses GTK for the GUI and Twilio for sending and receiving messages. We&apos;ll break down each part of the code to understand its functionality.</p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2024/07/text-app.png" class="kg-image" alt="Building a Ruby GUI Text Messaging App with Twilio and GTK" loading="lazy" width="402" height="426"></figure><h2 id="overview">Overview</h2><p>This app is designed to send and receive text messages using Twilio&apos;s API. It leverages GTK for a user-friendly graphical interface.</p><hr><h2 id="setting-up-the-project">Setting Up the Project</h2><p>First, let&apos;s look at the project structure:</p><pre><code>Ruby-GUI-Text-App/
  &#x251C;&#x2500;&#x2500; .env
  &#x251C;&#x2500;&#x2500; Gemfile
  &#x251C;&#x2500;&#x2500; Gemfile.lock
  &#x251C;&#x2500;&#x2500; main.rb
  &#x251C;&#x2500;&#x2500; message_handler.rb
  &#x251C;&#x2500;&#x2500; twilio_client.rb
  &#x251C;&#x2500;&#x2500; interface.glade
  &#x251C;&#x2500;&#x2500; run_text_app.sh


</code></pre><hr><h2 id="gemfile">Gemfile</h2><p>The <code>Gemfile</code> includes the necessary gems for the project:</p><pre><code class="language-ruby">source &apos;https://rubygems.org&apos;

gem &apos;rack&apos;, &apos;~&gt; 2.2.9&apos;
gem &apos;thin&apos;
gem &apos;gtk3&apos;
gem &apos;twilio-ruby&apos;
gem &apos;dotenv&apos;
gem &apos;sinatra&apos;
</code></pre><hr><h2 id="env-file">.env File</h2><p>Create a <code>.env</code> file in the project root and add your Twilio and ngrok configuration:</p><pre><code class="language-env">TWILIO_ACCOUNT_SID=&apos;your_twilio_account_sid&apos;
TWILIO_AUTH_TOKEN=&apos;your_twilio_auth_token&apos;
TWILIO_PHONE_NUMBER=&apos;your_twilio_phone_number&apos;
NGROK_DOMAIN=&apos;your_ngrok_domain&apos;
NGROK_PORT=4567
</code></pre><ul><li><code>TWILIO_ACCOUNT_SID</code> and <code>TWILIO_AUTH_TOKEN</code>: Your Twilio account credentials.</li><li><code>TWILIO_PHONE_NUMBER</code>: The phone number you have set up in Twilio to send messages.</li><li><code>NGROK_DOMAIN</code> and <code>NGROK_PORT</code>: Your ngrok domain and port configuration.</li></ul><hr><h2 id="mainrb">main.rb</h2><p>The <code>main.rb</code> file is the core of the Ruby GUI text messaging app, orchestrating the integration of GTK for the graphical interface, Twilio for messaging, and Sinatra for handling web requests. Let&apos;s break down its key components and functionality.</p><h2 id="required-libraries">Required Libraries</h2><pre><code class="language-ruby">require &apos;gtk3&apos;
require &apos;pathname&apos;
require &apos;twilio-ruby&apos;
require &apos;dotenv&apos;
require &apos;sinatra/base&apos;
require &apos;thin&apos;
require &apos;open3&apos;
require &apos;json&apos;
require &apos;net/http&apos;
require &apos;uri&apos;
</code></pre><ul><li><code>gtk3</code>: Provides the graphical interface.</li><li><code>pathname</code>: For handling file paths.</li><li><code>twilio-ruby</code>: For interacting with the Twilio API.</li><li><code>dotenv</code>: For loading environment variables from a .env file.</li><li><code>sinatra/base</code>: A lightweight web framework for handling incoming messages.</li><li><code>thin</code>: A fast and lightweight web server.</li><li><code>open3</code>, <code>json</code>, <code>net/http</code>, <code>uri</code>: Various utilities for process management, JSON handling, and HTTP requests.</li></ul><h2 id="loading-environment-variables">Loading Environment Variables</h2><pre><code class="language-ruby">Dotenv.load
</code></pre><p>Loads environment variables from the .env file.</p><h2 id="textapp-class">TextApp Class</h2><p>The <code>TextApp</code> class encapsulates the GTK application logic.</p><h3 id="initialization">Initialization</h3><pre><code class="language-ruby">class TextApp
  @instance = nil

  def self.instance
    @instance
  end

  def self.instance=(instance)
    @instance = instance
  end

  def initialize
    puts &quot;Initializing GTK application...&quot;
    Gtk.init

    glade_file = Pathname.new(__FILE__).dirname + &apos;interface.glade&apos;
    puts &quot;Loading Glade file from #{glade_file.to_s}...&quot;

    unless File.exist?(glade_file.to_s)
      raise &quot;Glade file not found at #{glade_file.to_s}&quot;
    end

    builder = Gtk::Builder.new
    begin
      builder.add_from_file(glade_file.to_s)
    rescue =&gt; e
      raise &quot;Error loading Glade file: #{e.message}&quot;
    end

    @window = builder.get_object(&quot;window1&quot;)
    if @window.nil?
      raise &quot;Could not find object &apos;window1&apos; in Glade file&quot;
    end
    @window.signal_connect(&quot;destroy&quot;) { Gtk.main_quit }

    @phone_number_entry = builder.get_object(&quot;phone_number_entry&quot;)
    if @phone_number_entry.nil?
      raise &quot;Could not find object &apos;phone_number_entry&apos; in Glade file&quot;
    end

    @message_entry = builder.get_object(&quot;message_entry&quot;)
    if @message_entry.nil?
      raise &quot;Could not find object &apos;message_entry&apos; in Glade file&quot;
    end

    @send_button = builder.get_object(&quot;send_button&quot;)
    if @send_button.nil?
      raise &quot;Could not find object &apos;send_button&apos; in Glade file&quot;
    end
    @send_button.signal_connect(&quot;clicked&quot;) { on_send_button_clicked }

    @messages_text_view = builder.get_object(&quot;messages_text_view&quot;)
    if @messages_text_view.nil?
      raise &quot;Could not find object &apos;messages_text_view&apos; in Glade file&quot;
    end

    puts &quot;Showing all components...&quot;
    @window.show_all
    puts &quot;Window should now be visible.&quot;

    start_server
    set_twilio_webhook(&apos;https://gull-shining-peacock.ngrok-free.app&apos;)
  end
</code></pre><ul><li>Initializes the GTK application.</li><li>Loads the Glade file which defines the UI.</li><li>Connects UI components to their respective variables.</li><li>Sets up signals for button clicks and window destroy events.</li><li>Starts the Sinatra server and sets the Twilio webhook.</li></ul><h3 id="button-click-handler">Button Click Handler</h3><pre><code class="language-ruby">  def on_send_button_clicked
    phone_number = @phone_number_entry.text
    message_body = @message_entry.text

    if phone_number.empty? || message_body.empty?
      puts &quot;Phone number or message body cannot be empty&quot;
      return
    end

    send_message(phone_number, message_body)
    @message_entry.text = &quot;&quot;
  end
</code></pre><ul><li>Retrieves the phone number and message from the UI.</li><li>Sends the message using <code>send_message</code> method.</li><li>Clears the message entry after sending.</li></ul><h3 id="sending-messages">Sending Messages</h3><pre><code class="language-ruby">  def send_message(phone_number, message_body)
    account_sid = ENV[&apos;TWILIO_ACCOUNT_SID&apos;]
    auth_token = ENV[&apos;TWILIO_AUTH_TOKEN&apos;]
    twilio_phone_number = ENV[&apos;TWILIO_PHONE_NUMBER&apos;]

    client = Twilio::REST::Client.new(account_sid, auth_token)

    message = client.messages.create(
      body: message_body,
      to: phone_number,
      from: twilio_phone_number
    )

    puts &quot; &quot;
    puts &quot;######################################################################&quot;
    puts message.inspect
    puts &quot;######################################################################&quot;
    puts &quot; &quot;
    append_message(&quot;Sent to #{phone_number}: #{message_body}&quot;)
  end
</code></pre><ul><li>Uses Twilio&apos;s API to send a message.</li><li>Logs the message response.</li><li>Appends the sent message to the text view.</li></ul><h3 id="appending-messages">Appending Messages</h3><pre><code class="language-ruby">  def append_message(text)
    buffer = @messages_text_view.buffer
    end_iter = buffer.end_iter
    buffer.insert(end_iter, text + &quot;\n&quot;)
  end
</code></pre><p>Appends a text message to the <code>messages_text_view</code> buffer.</p><h3 id="starting-the-server">Starting the Server</h3><pre><code class="language-ruby">  def start_server
    Thread.new do
      MySinatraApp.run!
    end
  end
</code></pre><p>Starts the Sinatra server in a new thread.</p><h3 id="setting-twilio-webhook">Setting Twilio Webhook</h3><pre><code class="language-ruby">  def set_twilio_webhook(ngrok_url)
    account_sid = ENV[&apos;TWILIO_ACCOUNT_SID&apos;]
    auth_token = ENV[&apos;TWILIO_AUTH_TOKEN&apos;]
    client = Twilio::REST::Client.new(account_sid, auth_token)
    phone_number_sid = client.incoming_phone_numbers.list.first.sid
    client.incoming_phone_numbers(phone_number_sid).update(
      sms_url: &quot;#{ngrok_url}/incoming&quot;,
      sms_method: &apos;POST&apos;
    )
    puts &quot;Twilio webhook set to #{ngrok_url}/incoming&quot;
  end
</code></pre><p>Sets the webhook URL for Twilio to forward incoming messages.</p><h3 id="running-the-gtk-main-loop">Running the GTK Main Loop</h3><pre><code class="language-ruby">  def run
    puts &quot;Running GTK main loop...&quot;
    Gtk.main
  end
end
</code></pre><p>Runs the GTK main loop.</p><hr><h2 id="mysinatraapp-class">MySinatraApp Class</h2><p>The <code>MySinatraApp</code> class handles incoming HTTP requests.</p><pre><code class="language-ruby">class MySinatraApp &lt; Sinatra::Base
  post &apos;/incoming&apos; do
    body = params[&apos;Body&apos;]
    from = params[&apos;From&apos;]
    puts &quot;Incoming message from #{from}: #{body}&quot; # Debug statement
    TextApp.instance.append_message(&quot;Received from #{from}: #{body}&quot;)

    twiml = Twilio::TwiML::MessagingResponse.new do |r|
      # Uncomment to use reply message upon receipt of texts.
      # r.message body: &apos;We got your message, thank you!&apos;
    end

    content_type &apos;text/xml&apos;
    twiml.to_s
  end

  def self.run!
    Rack::Handler::Thin.run(self, Host: &apos;127.0.0.1&apos;, Port: 4567)
  end
end
</code></pre><ul><li>Sets up Sinatra to listen for incoming messages on the <code>/incoming</code> endpoint.</li><li>Appends incoming messages to the GTK app&apos;s text view.</li><li>Sends a TwiML response if needed.</li></ul><hr><h2 id="main-execution">Main Execution</h2><pre><code class="language-ruby">begin
  app = TextApp.new
  TextApp.instance = app
  app.run
rescue StandardError =&gt; e
  puts &quot;An error occurred: #{e.message}&quot;
end
</code></pre><ul><li>Initializes the <code>TextApp</code> and starts the GTK main loop.</li><li>Catches and logs any errors that occur during execution.</li></ul><p>This detailed breakdown provides an understanding of how the <code>main.rb</code> file integrates GTK, Twilio, and Sinatra to create a functional GUI text messaging app.</p><figure class="kg-card kg-image-card"><img src="https://www.gingertechblog.com/content/images/2024/07/sms-log.webp" class="kg-image" alt="Building a Ruby GUI Text Messaging App with Twilio and GTK" loading="lazy" width="1189" height="430" srcset="https://www.gingertechblog.com/content/images/size/w600/2024/07/sms-log.webp 600w, https://www.gingertechblog.com/content/images/size/w1000/2024/07/sms-log.webp 1000w, https://www.gingertechblog.com/content/images/2024/07/sms-log.webp 1189w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="interfaceglade">interface.glade</h2><p>Defines the UI layout using Glade:</p><pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!-- Generated with glade 3.40.0 --&gt;
&lt;interface&gt;
  &lt;requires lib=&quot;gtk+&quot; version=&quot;3.24&quot;/&gt;
  &lt;object class=&quot;GtkWindow&quot; id=&quot;window1&quot;&gt;
    &lt;property name=&quot;title&quot;&gt;Texting App&lt;/property&gt;
    &lt;property name=&quot;default-width&quot;&gt;400&lt;/property&gt;
    &lt;property name=&quot;default-height&quot;&gt;400&lt;/property&gt;
    &lt;child&gt;
      &lt;object class=&quot;GtkBox&quot;&gt;
        &lt;property name=&quot;orientation&quot;&gt;vertical&lt;/property&gt;
        &lt;property name=&quot;spacing&quot;&gt;10&lt;/property&gt;
        &lt;property name=&quot;margin-top&quot;&gt;10&lt;/property&gt;
        &lt;property name=&quot;margin-bottom&quot;&gt;10&lt;/property&gt;
        &lt;property name=&quot;margin-start&quot;&gt;10&lt;/property&gt;
        &lt;property name=&quot;margin-end&quot;&gt;10&lt;/property&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkLabel&quot;&gt;
            &lt;property name=&quot;label&quot; translatable=&quot;yes&quot;&gt;Message Log&lt;/property&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkScrolledWindow&quot;&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;expand&quot;&gt;True&lt;/property&gt;
            &lt;child&gt;
              &lt;object class=&quot;GtkTextView&quot; id=&quot;messages_text_view&quot;&gt;
                &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
                &lt;property name=&quot;can-focus&quot;&gt;True&lt;/property&gt;
                &lt;property name=&quot;editable&quot;&gt;False&lt;/property&gt;
                &lt;property name=&quot;wrap-mode&quot;&gt;word&lt;/property&gt;
              &lt;/object&gt;
            &lt;/child&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkLabel&quot;&gt;
            &lt;property name=&quot;label&quot; translatable=&quot;yes&quot;&gt;Phone Number&lt;/property&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkEntry&quot; id=&quot;phone_number_entry&quot;&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;can-focus&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;placeholder-text&quot; translatable=&quot;yes&quot;&gt;123-456-7890&lt;/property&gt;
            &lt;property name=&quot;input-purpose&quot;&gt;phone&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkLabel&quot;&gt;
            &lt;property name=&quot;label&quot; translatable=&quot;yes&quot;&gt;Message&lt;/property&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkEntry&quot; id=&quot;message_entry&quot;&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;can-focus&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;placeholder-text&quot; translatable=&quot;yes&quot;&gt;Add text message here&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class=&quot;GtkButton&quot; id=&quot;send_button&quot;&gt;
            &lt;property name=&quot;label&quot; translatable=&quot;yes&quot;&gt;Send&lt;/property&gt;
            &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;can-focus&quot;&gt;True&lt;/property&gt;
            &lt;property name=&quot;halign&quot;&gt;center&lt;/property&gt;
          &lt;/object&gt;
        &lt;/child&gt;
      &lt;/object&gt;
    &lt;/child&gt;
  &lt;/object&gt;
&lt;/interface&gt;
</code></pre><ul><li>Defines the main window with entries for the phone number and message, a send button, and a text view for received messages.</li></ul><hr><h2 id="runtextappsh">run_text_app.sh</h2><p>This script starts the ngrok and Ruby application:</p><pre><code class="language-bash">#!/bin/bash

# Load environment variables from .env file
if [ -f .env ]; then
  export $(grep -v &apos;^#&apos; .env | xargs)
fi

# Start ngrok in the background
echo &quot;Starting NGROK on port $NGROK_PORT&quot;
ngrok http $NGROK_PORT --domain=$NGROK_DOMAIN &amp;

# Give ngrok a moment to start
sleep 2

echo &quot;Starting SMS APP&quot;
# Start the Ruby application
bundle exec ruby main.rb
</code></pre><ul><li>Starts ngrok to forward requests to the local server.</li><li>Waits for ngrok to initialize before starting the Ruby application.</li></ul><hr><h2 id="conclusion">Conclusion</h2><p>This tutorial covered the key components of the Ruby GUI text app, including the script and <code>.env</code> file needed for configuration. By understanding each part, you can customize and extend the app according to your needs.</p><p>For more details, visit my <a href="https://github.com/JeremyDuncan/Ruby-GUI-Text-App?ref=gingertechblog.com">GitHub repository</a>.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Rails SMS Notification App Video Walkthrough]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2><p>This is a Ruby on Rails app I created which sends SMS messages to phone numbers based on certain triggers and user actions. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.gingertechblog.com/content/images/2024/07/image.png" class="kg-image" alt="App Home Page" loading="lazy" width="2000" height="1517" srcset="https://www.gingertechblog.com/content/images/size/w600/2024/07/image.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2024/07/image.png 1000w, https://www.gingertechblog.com/content/images/size/w1600/2024/07/image.png 1600w, https://www.gingertechblog.com/content/images/2024/07/image.png 2268w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">App Home Page</span></figcaption></figure><h3 id="the-application-tech-stack-involves">The application tech stack involves:</h3><ul><li>Devise user authentication</li><li>PostgreSQL database</li><li>Twilio API</li><li>Slim templating engine</li><li>Ruby</li><li>Javascript</li><li>HTML</li><li>CSS</li></ul><h2 id="application-and-source-code">Application and Source Code</h2>]]></description><link>https://www.gingertechblog.com/rails-sms-notification-app-video-walkthrough/</link><guid isPermaLink="false">66889eb16534601a6e416b69</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Slim]]></category><category><![CDATA[Twilio]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Sat, 06 Jul 2024 15:50:59 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/07/twilio_sms_app.webp" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://www.gingertechblog.com/content/images/2024/07/twilio_sms_app.webp" alt="Rails SMS Notification App Video Walkthrough"><p>This is a Ruby on Rails app I created which sends SMS messages to phone numbers based on certain triggers and user actions. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.gingertechblog.com/content/images/2024/07/image.png" class="kg-image" alt="Rails SMS Notification App Video Walkthrough" loading="lazy" width="2000" height="1517" srcset="https://www.gingertechblog.com/content/images/size/w600/2024/07/image.png 600w, https://www.gingertechblog.com/content/images/size/w1000/2024/07/image.png 1000w, https://www.gingertechblog.com/content/images/size/w1600/2024/07/image.png 1600w, https://www.gingertechblog.com/content/images/2024/07/image.png 2268w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">App Home Page</span></figcaption></figure><h3 id="the-application-tech-stack-involves">The application tech stack involves:</h3><ul><li>Devise user authentication</li><li>PostgreSQL database</li><li>Twilio API</li><li>Slim templating engine</li><li>Ruby</li><li>Javascript</li><li>HTML</li><li>CSS</li></ul><h2 id="application-and-source-code">Application and Source Code</h2><ul><li><a href="https://sms-app.jeremyd.net/?ref=gingertechblog.com">SMS App Demo</a></li><li><a href="https://github.com/JeremyDuncan/sms_app?ref=gingertechblog.com">Source Code</a></li></ul><h2 id="video-tutorial-of-app-and-code"><strong>Video Tutorial of App and Code</strong></h2><figure class="kg-card kg-video-card kg-width-wide" data-kg-thumbnail="https://www.gingertechblog.com/content/media/2024/07/sms_app_overview_thumb.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://www.gingertechblog.com/content/media/2024/07/sms_app_overview.mp4" poster="https://img.spacergif.org/v1/1920x1080/0a/spacer.png" width="1920" height="1080" playsinline preload="metadata" style="background: transparent url(&apos;https://www.gingertechblog.com/content/media/2024/07/sms_app_overview_thumb.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon" aria-label="Play video">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon" aria-label="Play video">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide" aria-label="Pause video">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">27:59</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate" aria-label="Adjust playback speed">1&#xD7;</button>
                        <button class="kg-video-unmute-icon" aria-label="Unmute">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide" aria-label="Mute">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            
        </figure><hr><h2 id="source-code-readme"><strong>Source Code README</strong></h2><h1 id="realtime-sms-notification-system">Realtime SMS Notification System</h1>
<p>This app provides a system for sending SMS notifications using the Twilio API.<br>
Users can send predefined triggered SMS notifications to themselves or custom SMS messages to any number.<br>
The application tracks the messages sent, including timestamps, user IDs, and message details.</p>
<h2 id="features">Features</h2>
<ul>
<li>User authentication with Devise</li>
<li>Send default triggered SMS notifications to user</li>
<li>Send custom SMS messages to any number</li>
<li>Send SMS message to user on registration</li>
<li>Track SMS message logs</li>
<li>Rails asynchronous job processing</li>
<li>Real-time job queue display using Action Cable</li>
</ul>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Ruby 3.3.2</li>
<li>Rails 7.1.3.4</li>
<li>PostgreSQL</li>
<li>Twilio account (for sending SMS messages)</li>
</ul>
<h2 id="installation">Installation</h2>
<ol>
<li>Clone the repository:</li>
</ol>
<pre><code class="language-sh">git clone https://github.com/JeremyDuncan/sms_app.git
cd sms_app
</code></pre>
<ol start="2">
<li>Install the required gems:</li>
</ol>
<pre><code class="language-sh">bundle install
</code></pre>
<ol start="3">
<li>Set up the database:</li>
</ol>
<pre><code class="language-sh">rails db:create
rails db:migrate
</code></pre>
<ol start="4">
<li>Set credentials:</li>
</ol>
<p>Add your Twilio and PostgreSQL credentials to <code>config/credentials.yml.enc</code>. Use <code>rails credentials:edit</code> to open the credentials file and add the following:</p>
<pre><code class="language-yaml">secret_key_base: YOUR_SECRET_BASE_KEY

twilio:
  development:
    account_sid: YOUR_ACCOUNT_SID
    auth_token: YOUR_ACCOUNT_AUTH_TOKEN
    phone_number: YOUR_PHONE_NUMBER
  production:
    account_sid: YOUR_ACCOUNT_SID
    auth_token: YOUR_ACCOUNT_AUTH_TOKEN
    phone_number: YOUR_PHONE_NUMBER

database:
  username: YOUR_USERNAME
  password: YOUR_PASSWORD

</code></pre>
<h2 id="running-the-server">Running the Server</h2>
<ol>
<li>Start the Rails server:</li>
</ol>
<pre><code class="language-sh">rails s
</code></pre>
<ol start="2">
<li>Navigate to <code>http://localhost:3000</code> in your web browser.</li>
</ol>
<h2 id="usage">Usage</h2>
<h3 id="home-page">Home Page</h3>
<p>The home page provides the following functionality:</p>
<ul>
<li><strong>Send Default SMS</strong>: Click the button to send a default SMS notification to the current user&apos;s phone number.</li>
<li><strong>Send Custom SMS</strong>: Enter a phone number and a message to send a custom SMS message.</li>
</ul>
<h3 id="real-time-job-queue">Real-time Job Queue</h3>
<p>The job queue section displays the status of SMS jobs in real-time using Action Cable.</p>
<hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Getting Started with Next.js: A Comprehensive Guide for Modern Web Development]]></title><description><![CDATA[<p>Next.js is a powerful React framework for building server-side rendering (SSR) and static web applications. It combines the best features of React with advanced performance optimizations and a robust development experience. In this blog post, we&apos;ll explore the key features of Next.js, its benefits, and how</p>]]></description><link>https://www.gingertechblog.com/getting-started-with-next-js-a-comprehensive-guide-for-modern-web-development/</link><guid isPermaLink="false">6682007f6534601a6e416b28</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Mon, 01 Jul 2024 12:15:23 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/07/nextjs.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/07/nextjs.webp" alt="Getting Started with Next.js: A Comprehensive Guide for Modern Web Development"><p>Next.js is a powerful React framework for building server-side rendering (SSR) and static web applications. It combines the best features of React with advanced performance optimizations and a robust development experience. In this blog post, we&apos;ll explore the key features of Next.js, its benefits, and how to get started with a simple example.</p><h2 id="why-choose-nextjs">Why Choose Next.js?</h2><p>Next.js offers several compelling features that make it a preferred choice for modern web development:</p><ol><li><strong>Server-Side Rendering (SSR) and Static Site Generation (SSG)</strong>: Next.js allows you to pre-render pages at build time or request time, providing faster initial load times and improved SEO.</li><li><strong>API Routes</strong>: You can create API endpoints within your Next.js application, eliminating the need for a separate backend.</li><li><strong>Automatic Code Splitting</strong>: Next.js automatically splits your code into smaller chunks, reducing the initial load time and improving performance.</li><li><strong>Built-In CSS and Sass Support</strong>: You can import CSS and Sass files directly into your components, simplifying the styling process.</li><li><strong>Image Optimization</strong>: Next.js includes an Image component that automatically optimizes images for better performance.</li><li><strong>TypeScript Support</strong>: Next.js has built-in TypeScript support, making it easy to build type-safe applications.</li></ol><h2 id="getting-started-with-nextjs">Getting Started with Next.js</h2><p>Let&apos;s walk through creating a simple Next.js application.</p><h3 id="step-1-setup">Step 1: Setup</h3><p>First, make sure you have Node.js installed. Then, create a new Next.js project using the following command:</p><h4 id="using-node-option">Using Node option</h4><pre><code class="language-bash">npx create-next-app my-next-app
cd my-next-app
</code></pre><h4 id="using-yarn-option">Using Yarn option</h4><p>First, make sure you have Node.js and Yarn installed. Then, create a new Next.js project using the following command:</p><pre><code class="language-bash">yarn create next-app my-next-app
cd my-next-app
</code></pre><h3 id="step-2-create-a-page">Step 2: Create a Page</h3><p>Next.js uses a file-based routing system. Create a new file <code>pages/index.js</code> with the following content:</p><pre><code class="language-javascript">import Head from &apos;next/head&apos;;

export default function Home() {
  return (
    &lt;div&gt;
      &lt;Head&gt;
        &lt;title&gt;My Next.js App&lt;/title&gt;
      &lt;/Head&gt;
      &lt;h1&gt;Welcome to Next.js!&lt;/h1&gt;
      &lt;p&gt;This is a simple Next.js application.&lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre><h3 id="step-3-run-the-development-server">Step 3: Run the Development Server</h3><h4 id="with-npm">With NPM</h4><p>Start the development server using the following command for :</p><pre><code class="language-bash">npm run dev
</code></pre><h4 id="with-yarn">With Yarn</h4><p>Start the development server using the following command:</p><pre><code class="language-bash">yarn dev
</code></pre><p>Navigate to <code>http://localhost:3000</code> in your browser to see your Next.js application in action.</p><h2 id="advanced-features">Advanced Features</h2><h3 id="server-side-rendering-ssr">Server-Side Rendering (SSR)</h3><p>To create a page with server-side rendering, use the <code>getServerSideProps</code> function:</p><pre><code class="language-javascript">// pages/ssr.js
export async function getServerSideProps() {
  // Fetch data from an API
  const res = await fetch(&apos;https://api.example.com/data&apos;);
  const data = await res.json();

  return { props: { data } };
}

export default function SSR({ data }) {
  return (
    &lt;div&gt;
      &lt;h1&gt;Server-Side Rendered Page&lt;/h1&gt;
      &lt;pre&gt;{JSON.stringify(data, null, 2)}&lt;/pre&gt;
    &lt;/div&gt;
  );
}
</code></pre><h3 id="static-site-generation-ssg">Static Site Generation (SSG)</h3><p>To create a page with static site generation, use the <code>getStaticProps</code> function:</p><pre><code class="language-javascript">// pages/ssg.js
export async function getStaticProps() {
  // Fetch data from an API
  const res = await fetch(&apos;https://api.example.com/data&apos;);
  const data = await res.json();

  return { props: { data } };
}

export default function SSG({ data }) {
  return (
    &lt;div&gt;
      &lt;h1&gt;Static Site Generated Page&lt;/h1&gt;
      &lt;pre&gt;{JSON.stringify(data, null, 2)}&lt;/pre&gt;
    &lt;/div&gt;
  );
}
</code></pre><h2 id="conclusion">Conclusion</h2><p>Next.js is a versatile framework that simplifies the development of fast, SEO-friendly web applications. With features like server-side rendering, static site generation, and built-in CSS support, it offers a comprehensive solution for modern web development.</p><p>Whether you&apos;re building a simple website or a complex application, Next.js provides the tools and flexibility you need to succeed.</p><p>To get started with Next.js, check out the <a href="https://nextjs.org/docs?ref=gingertechblog.com">official documentation</a> and explore its rich ecosystem and community support.</p><hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[React vs. Next.js: A Comprehensive Comparison for Modern Web Development]]></title><description><![CDATA[<h1 id></h1>
<p>Both React and Next.js are popular tools in the JavaScript ecosystem, but they serve different purposes and are used in different scenarios. Here&apos;s a detailed comparison:</p>
<h2 id="react">React</h2>
<h3 id="what-is-react">What is React?</h3>
<p>React is a JavaScript library for building user interfaces. Developed and maintained by Facebook, React allows developers</p>]]></description><link>https://www.gingertechblog.com/react-vs-next-js-a-comprehensive-comparison-for-modern-web-development/</link><guid isPermaLink="false">6681ff1e6534601a6e416ae7</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Mon, 01 Jul 2024 01:02:55 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/07/react-vs-nextjs.webp" medium="image"/><content:encoded><![CDATA[<h1 id></h1>
<img src="https://www.gingertechblog.com/content/images/2024/07/react-vs-nextjs.webp" alt="React vs. Next.js: A Comprehensive Comparison for Modern Web Development"><p>Both React and Next.js are popular tools in the JavaScript ecosystem, but they serve different purposes and are used in different scenarios. Here&apos;s a detailed comparison:</p>
<h2 id="react">React</h2>
<h3 id="what-is-react">What is React?</h3>
<p>React is a JavaScript library for building user interfaces. Developed and maintained by Facebook, React allows developers to create reusable UI components, manage the state efficiently, and build complex UIs from small, isolated pieces of code called components.</p>
<h3 id="key-features-of-react">Key Features of React</h3>
<ol>
<li><strong>Component-Based Architecture</strong>: React encourages the development of reusable components that manage their own state.</li>
<li><strong>Virtual DOM</strong>: React uses a virtual DOM to efficiently update and render components.</li>
<li><strong>Unidirectional Data Flow</strong>: Data flows in one direction, making it easier to understand and debug applications.</li>
<li><strong>JSX Syntax</strong>: React uses JSX, a syntax extension that allows you to write HTML-like code within JavaScript.</li>
<li><strong>Community and Ecosystem</strong>: A vast ecosystem of libraries and tools to extend React&apos;s functionality.</li>
</ol>
<h3 id="use-cases-for-react">Use Cases for React</h3>
<ul>
<li>Single-page applications (SPAs)</li>
<li>Mobile applications using React Native</li>
<li>Complex web applications with dynamic user interfaces</li>
</ul>
<h2 id="nextjs">Next.js</h2>
<h3 id="what-is-nextjs">What is Next.js?</h3>
<p>Next.js is a React framework that provides additional features and optimizations for building server-side rendering (SSR) and static web applications. Developed and maintained by Vercel, Next.js builds on top of React to offer a comprehensive solution for building modern web applications.</p>
<h3 id="key-features-of-nextjs">Key Features of Next.js</h3>
<ol>
<li><strong>Server-Side Rendering (SSR)</strong>: Automatically renders React components on the server, improving initial load times and SEO.</li>
<li><strong>Static Site Generation (SSG)</strong>: Pre-renders pages at build time, resulting in fast, static HTML pages.</li>
<li><strong>API Routes</strong>: Allows you to create API endpoints within the Next.js application, eliminating the need for a separate backend.</li>
<li><strong>Automatic Code Splitting</strong>: Automatically splits the code into smaller chunks, reducing the initial load time and improving performance.</li>
<li><strong>Built-In CSS and Sass Support</strong>: Supports importing CSS and Sass files directly into components.</li>
<li><strong>Image Optimization</strong>: Includes an Image component that automatically optimizes images for better performance.</li>
<li><strong>File-Based Routing</strong>: Pages are defined by the file structure within the <code>pages</code> directory, simplifying routing.</li>
</ol>
<h3 id="use-cases-for-nextjs">Use Cases for Next.js</h3>
<ul>
<li>Server-side rendered (SSR) applications</li>
<li>Static site generation (SSG) projects</li>
<li>Applications requiring both client-side and server-side rendering</li>
<li>SEO-friendly websites and blogs</li>
</ul>
<h2 id="comparison">Comparison</h2>
<h3 id="development-paradigm">Development Paradigm</h3>
<ul>
<li><strong>React</strong>: Focuses solely on the frontend and client-side rendering. Developers need to handle routing, SSR, and other functionalities with additional libraries.</li>
<li><strong>Next.js</strong>: Provides a full-stack framework with built-in SSR, SSG, routing, and API routes, making it easier to build complex web applications without configuring additional tools.</li>
</ul>
<h3 id="performance">Performance</h3>
<ul>
<li><strong>React</strong>: Client-side rendering can lead to longer initial load times, especially for large applications.</li>
<li><strong>Next.js</strong>: Optimizes performance with SSR and SSG, providing faster initial load times and better SEO.</li>
</ul>
<h3 id="seo">SEO</h3>
<ul>
<li><strong>React</strong>: Requires additional configurations and tools to achieve server-side rendering for SEO benefits.</li>
<li><strong>Next.js</strong>: Built-in SSR and SSG provide out-of-the-box SEO optimizations.</li>
</ul>
<h3 id="ease-of-use">Ease of Use</h3>
<ul>
<li><strong>React</strong>: Offers more flexibility but requires more configuration and setup for features like SSR and routing.</li>
<li><strong>Next.js</strong>: Offers a more opinionated and streamlined development experience with many features ready to use out of the box.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>React and Next.js serve different purposes and complement each other well. React is ideal for building dynamic user interfaces and single-page applications with client-side rendering. Next.js builds on top of React, providing additional features for server-side rendering, static site generation, and full-stack development, making it a comprehensive solution for building modern web applications.</p>
<p>To get started with React and Next.js, check out the <a href="https://reactjs.org/docs/getting-started.html?ref=gingertechblog.com">React documentation</a> and the <a href="https://nextjs.org/docs?ref=gingertechblog.com">Next.js documentation</a> for more information.</p>
<hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item><item><title><![CDATA[Getting Started with Express.js: A Simple and Efficient Node.js Framework]]></title><description><![CDATA[<p>Express.js is a fast, unopinionated, minimalist web framework for Node.js, designed to build web applications and APIs with ease. In this blog post, we&apos;ll cover the basics of setting up an Express.js project, creating a simple API, and explore some real-world use cases and examples.</p>]]></description><link>https://www.gingertechblog.com/getting-started-with-express-js-a-simple-and-efficient-node-js-framework/</link><guid isPermaLink="false">667389726534601a6e416a8c</guid><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Jeremy Duncan]]></dc:creator><pubDate>Thu, 20 Jun 2024 01:55:13 GMT</pubDate><media:content url="https://www.gingertechblog.com/content/images/2024/06/express_blog.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.gingertechblog.com/content/images/2024/06/express_blog.webp" alt="Getting Started with Express.js: A Simple and Efficient Node.js Framework"><p>Express.js is a fast, unopinionated, minimalist web framework for Node.js, designed to build web applications and APIs with ease. In this blog post, we&apos;ll cover the basics of setting up an Express.js project, creating a simple API, and explore some real-world use cases and examples.</p>
<h2 id="what-is-expressjs">What is Express.js?</h2>
<p>Express.js is a lightweight web framework that provides robust features for building web and mobile applications. It simplifies the development process by providing a thin layer of fundamental web application features without obscuring Node.js features.</p>
<h2 id="why-use-expressjs">Why Use Express.js?</h2>
<ul>
<li><strong>Minimal and Flexible</strong>: It provides a thin layer of fundamental web application features, leaving the core Node.js features intact.</li>
<li><strong>Robust Routing</strong>: Includes a powerful routing API that makes handling different HTTP requests easy.</li>
<li><strong>Middleware Support</strong>: Allows the use of middleware to handle various tasks like parsing request bodies, managing sessions, etc.</li>
<li><strong>Performance</strong>: Being a lightweight framework, it offers high performance for web applications and APIs.</li>
</ul>
<h2 id="setting-up-expressjs">Setting Up Express.js</h2>
<p>Let&apos;s walk through setting up a basic Express.js application.</p>
<h3 id="step-1-install-nodejs-and-npm">Step 1: Install Node.js and npm</h3>
<p>Before getting started, ensure you have Node.js and npm (Node Package Manager) installed. You can download them from <a href="https://nodejs.org/?ref=gingertechblog.com">Node.js official website</a>.</p>
<h3 id="step-2-initialize-a-new-nodejs-project">Step 2: Initialize a New Node.js Project</h3>
<p>Create a new directory for your project and navigate into it using the terminal. Then, initialize a new Node.js project with the following command:</p>
<pre><code class="language-bash">npm init -y
</code></pre>
<p>This command creates a <code>package.json</code> file with default settings.</p>
<h3 id="step-3-install-expressjs">Step 3: Install Express.js</h3>
<p>Next, install Express.js using npm:</p>
<pre><code class="language-bash">npm install express
</code></pre>
<h3 id="step-4-create-your-first-express-application">Step 4: Create Your First Express Application</h3>
<p>Create a new file named <code>app.js</code> and add the following code:</p>
<pre><code class="language-javascript">const express = require(&apos;express&apos;);
const app = express();
const port = 3000;

app.get(&apos;/&apos;, (req, res) =&gt; {
  res.send(&apos;Hello World!&apos;);
});

app.listen(port, () =&gt; {
  console.log(`Example app listening at http://localhost:${port}`);
});
</code></pre>
<p>This code sets up a basic Express server that responds with &quot;Hello World!&quot; when accessed at the root URL.</p>
<h3 id="step-5-run-your-application">Step 5: Run Your Application</h3>
<p>Start your Express application by running the following command in your terminal:</p>
<pre><code class="language-bash">node app.js
</code></pre>
<p>You should see the message <code>Example app listening at http://localhost:3000</code> in your terminal. Open your web browser and navigate to <code>http://localhost:3000</code> to see the &quot;Hello World!&quot; message.</p>
<h2 id="adding-routes">Adding Routes</h2>
<p>Express makes it easy to define routes for your application. Let&apos;s add a few more routes to demonstrate this:</p>
<pre><code class="language-javascript">app.get(&apos;/about&apos;, (req, res) =&gt; {
  res.send(&apos;About Page&apos;);
});

app.get(&apos;/contact&apos;, (req, res) =&gt; {
  res.send(&apos;Contact Page&apos;);
});
</code></pre>
<p>With these routes added, you can visit <code>http://localhost:3000/about</code> and <code>http://localhost:3000/contact</code> to see the respective messages.</p>
<h2 id="middleware-in-expressjs">Middleware in Express.js</h2>
<p>Middleware functions are functions that have access to the request object (<code>req</code>), the response object (<code>res</code>), and the next middleware function in the application&#x2019;s request-response cycle. Middleware can execute code, modify the request and response objects, end the request-response cycle, and call the next middleware function.</p>
<h3 id="example-of-middleware">Example of Middleware</h3>
<p>Here&apos;s a simple example of middleware that logs the current timestamp for each request:</p>
<pre><code class="language-javascript">app.use((req, res, next) =&gt; {
  console.log(&apos;Time:&apos;, Date.now());
  next();
});
</code></pre>
<h2 id="real-world-use-cases-and-examples">Real-World Use Cases and Examples</h2>
<h3 id="1-building-a-restful-api">1. Building a RESTful API</h3>
<p>Express.js is commonly used to build RESTful APIs. Let&apos;s create a simple API for managing a list of books.</p>
<pre><code class="language-javascript">const express = require(&apos;express&apos;);
const app = express();
const port = 3000;

app.use(express.json());

let books = [
  { id: 1, title: &apos;1984&apos;, author: &apos;George Orwell&apos; },
  { id: 2, title: &apos;To Kill a Mockingbird&apos;, author: &apos;Harper Lee&apos; }
];

// Get all books
app.get(&apos;/books&apos;, (req, res) =&gt; {
  res.json(books);
});

// Get a single book by ID
app.get(&apos;/books/:id&apos;, (req, res) =&gt; {
  const book = books.find(b =&gt; b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send(&apos;Book not found&apos;);
  res.json(book);
});

// Add a new book
app.post(&apos;/books&apos;, (req, res) =&gt; {
  const book = {
    id: books.length + 1,
    title: req.body.title,
    author: req.body.author
  };
  books.push(book);
  res.status(201).json(book);
});

// Update a book
app.put(&apos;/books/:id&apos;, (req, res) =&gt; {
  const book = books.find(b =&gt; b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send(&apos;Book not found&apos;);

  book.title = req.body.title;
  book.author = req.body.author;
  res.json(book);
});

// Delete a book
app.delete(&apos;/books/:id&apos;, (req, res) =&gt; {
  const bookIndex = books.findIndex(b =&gt; b.id === parseInt(req.params.id));
  if (bookIndex === -1) return res.status(404).send(&apos;Book not found&apos;);

  const deletedBook = books.splice(bookIndex, 1);
  res.json(deletedBook);
});

app.listen(port, () =&gt; {
  console.log(`Book API listening at http://localhost:${port}`);
});
</code></pre>
<h3 id="2-creating-a-static-file-server">2. Creating a Static File Server</h3>
<p>Express.js can serve static files such as HTML, CSS, and JavaScript files.</p>
<pre><code class="language-javascript">const express = require(&apos;express&apos;);
const app = express();
const port = 3000;

app.use(express.static(&apos;public&apos;));

app.listen(port, () =&gt; {
  console.log(`Static file server running at http://localhost:${port}`);
});
</code></pre>
<p>Place your static files (e.g., <code>index.html</code>, <code>style.css</code>, <code>script.js</code>) in the <code>public</code> directory, and they will be accessible at <code>http://localhost:3000</code>.</p>
<h3 id="3-building-a-simple-authentication-system">3. Building a Simple Authentication System</h3>
<p>Express.js can be used to build a simple authentication system.</p>
<pre><code class="language-javascript">const express = require(&apos;express&apos;);
const app = express();
const port = 3000;

app.use(express.json());

const users = [
  { id: 1, username: &apos;user1&apos;, password: &apos;password1&apos; },
  { id: 2, username: &apos;user2&apos;, password: &apos;password2&apos; }
];

// Login route
app.post(&apos;/login&apos;, (req, res) =&gt; {
  const user = users.find(u =&gt; u.username === req.body.username &amp;&amp; u.password === req.body.password);
  if (!user) return res.status(401).send(&apos;Invalid credentials&apos;);

  res.send(&apos;Login successful&apos;);
});

app.listen(port, () =&gt; {
  console.log(`Authentication system running at http://localhost:${port}`);
});
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Express.js is a powerful yet simple framework that makes building web applications and APIs with Node.js straightforward. With its minimalistic approach and robust features, you can quickly set up and expand your applications.</p>
<hr>
<!--kg-card-begin: html-->
<style>
.social-share {
  margin-top: 20px;
  text-align: center; /* Center align buttons if desired */
}
  body > div.gh-viewport > main > article > section > div > a.share-button{
  color: #fff;
  background-color: #555;
  text-decoration: none;
  }
.share-button {
  padding: 8px 15px;
  margin: 0 5px;
  color: #fff;
  background-color: #555;
  text-decoration: none;
  display: inline-flex;
  align-items: center; /* Aligns the icon and text */
  border-radius: 4px; /* Optional: rounds the corners */
}
.share-button i {
  margin-right: 5px; /* Space between icon and text */
}
.share-button:hover {
  opacity: 0.8;
}

/* Media query for mobile devices */
@media (max-width: 600px) {
  .share-button {
    padding: 12px 20px; /* Larger padding for easier tapping */
    margin: 10px; /* More space between buttons */
    font-size: 16px; /* Larger font size */
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 60%;
  }
  .social-share {
    padding: 20px 0; /* More vertical padding */
    display: flex;
    flex-direction: column;
    align-content: center;
    justify-content: center;
    align-items: center;
  }
}
</style>

<div class="social-share">
  <a href="#" class="share-button twitter" data-js="share-twitter">
    <i class="fab fa-twitter"></i> Tweet
  </a>
  <a href="#" class="share-button facebook" data-js="share-facebook">
    <i class="fab fa-facebook-f"></i> Share on Facebook
  </a>
  <a href="#" class="share-button linkedin" data-js="share-linkedin">
    <i class="fab fa-linkedin-in"></i> Share on LinkedIn
  </a>
</div>

<script>
  
document.addEventListener('click', function (e) {
  const target = e.target.closest('[data-js]');
  if (target) {
    e.preventDefault();
    var network = target.getAttribute('data-js').split('-')[1];

    var postUrl = encodeURI(document.location.href);
    var postTitleElement = document.querySelector('.gh-article-title');

    // Check if the post title element exists
    if (postTitleElement) {
      var postTitle = encodeURI(postTitleElement.innerText);

      console.log("Clicked:", network);  // Check which button was clicked

      switch (network) {
        case "twitter":
          window.open(`https://twitter.com/share?url=${postUrl}&text=${postTitle}`, 'twitter-share-dialog', 'width=800,height=600');
          break;
        case "facebook":
          window.open(`https://www.facebook.com/sharer/sharer.php?u=${postUrl}`, 'facebook-share-dialog', 'width=800,height=600');
          break;
        case "linkedin":
          window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`, 'linkedin-share-dialog', 'width=800,height=600');
          break;
      }
    } else {
      console.error('Post title element not found. Make sure your HTML includes an element with the class "post-title".');
    }
  }
});


</script>

<!--kg-card-end: html-->
]]></content:encoded></item></channel></rss>