<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jess Alejo</title>
    <description>The latest articles on DEV Community by Jess Alejo (@jessalejo).</description>
    <link>https://dev.to/jessalejo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F830370%2F3b5afd6f-0b94-44ae-8aa6-cb62217c61f2.jpg</url>
      <title>DEV Community: Jess Alejo</title>
      <link>https://dev.to/jessalejo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jessalejo"/>
    <language>en</language>
    <item>
      <title>Connecting GitLab MCP to Claude Code Without Putting Your Token in the Config</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Tue, 08 Sep 2026 04:40:56 +0000</pubDate>
      <link>https://dev.to/jessalejo/connecting-gitlab-mcp-to-claude-code-without-putting-your-token-in-the-config-3fp5</link>
      <guid>https://dev.to/jessalejo/connecting-gitlab-mcp-to-claude-code-without-putting-your-token-in-the-config-3fp5</guid>
      <description>&lt;p&gt;If you want your AI coding assistant to read GitLab issues, review merge requests, or check pipeline status, it needs API access.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/zereight/gitlab-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;zereight/gitlab-mcp&lt;/code&gt;&lt;/a&gt; server provides exactly that over MCP.&lt;/p&gt;

&lt;p&gt;The problem is that most setup examples put your personal access token directly into a JSON config file.&lt;/p&gt;

&lt;p&gt;I wanted to avoid that.&lt;/p&gt;

&lt;p&gt;Instead, this setup keeps the token in your shell environment and lets the MCP configuration reference it.&lt;/p&gt;

&lt;p&gt;Same goal, but the secret does not have to be manually pasted into the configuration. It also makes rotating the token much easier.&lt;/p&gt;

&lt;p&gt;This guide walks through the entire setup, including two problems that commonly cause this configuration to fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18.17 or newer&lt;/li&gt;
&lt;li&gt;A GitLab account on gitlab.com or a self-hosted GitLab instance&lt;/li&gt;
&lt;li&gt;An MCP client. I am using Claude Code here, but the general configuration approach is portable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Personal Access Token
&lt;/h2&gt;

&lt;p&gt;In GitLab, go to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Settings &amp;gt; Access Tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a new personal access token.&lt;/p&gt;

&lt;p&gt;Choose the scope based on what you want the assistant to be able to do:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Gets you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read_api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read-only access. Enough to read issues, merge requests, and pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full read and write access. Needed to post comments or update issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Set an expiry date.&lt;/p&gt;

&lt;p&gt;Make sure you copy the token when GitLab shows it. You will not be able to view the token again later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Work Out Your API URL
&lt;/h2&gt;

&lt;p&gt;The MCP server needs the GitLab API endpoint, not your normal GitLab web URL.&lt;/p&gt;

&lt;p&gt;The API URL ends with &lt;code&gt;/api/v4&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gitlab.com/api/v4
https://gitlab.example.com/api/v4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a self-hosted instance, if your GitLab URL is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gitlab.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then your API URL is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gitlab.example.com/api/v4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an easy place to make a mistake.&lt;/p&gt;

&lt;p&gt;Do not use the bare hostname and do not use the project URL. The server needs the API endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Export the Variables
&lt;/h2&gt;

&lt;p&gt;Add both variables to your shell profile.&lt;/p&gt;

&lt;p&gt;For Bash:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For Zsh:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITLAB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;glpat-xxxxxxxxxxxxxxxxxxxx
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITLAB_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://gitlab.example.com/api/v4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The &lt;code&gt;export&lt;/code&gt; Is Important
&lt;/h3&gt;

&lt;p&gt;This is probably the single most common way this setup fails.&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;export&lt;/code&gt;, you create a shell variable that exists only inside the shell where you defined it.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;export&lt;/code&gt;, you create an environment variable that is inherited by processes launched from that shell.&lt;/p&gt;

&lt;p&gt;That distinction matters because MCP servers run as child processes.&lt;/p&gt;

&lt;p&gt;So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FOO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bar
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$FOO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But a child process will not see &lt;code&gt;FOO&lt;/code&gt; unless it was exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FOO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bar
bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo $FOO'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is empty.&lt;/p&gt;

&lt;p&gt;You can verify the environment from a child process without printing the actual token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &lt;span class="nt"&gt;-lic&lt;/span&gt; &lt;span class="s1"&gt;'echo "token set: ${GITLAB_PERSONAL_ACCESS_TOKEN:+yes}"; echo "url: $GITLAB_API_URL"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token set: yes
url: https://gitlab.example.com/api/v4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the token line is blank, either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You forgot &lt;code&gt;export&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You edited a profile file your shell does not actually read&lt;/li&gt;
&lt;li&gt;You have not started a new shell after changing the profile&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Confirm the Token Works Before Involving MCP
&lt;/h2&gt;

&lt;p&gt;Before troubleshooting MCP, make sure the GitLab credentials themselves work.&lt;/p&gt;

&lt;p&gt;This takes about ten seconds and can save a lot of confusion.&lt;/p&gt;

&lt;p&gt;If this request fails, the problem is your token or API URL. No amount of MCP configuration will fix it.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"PRIVATE-TOKEN: &lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_API_URL&lt;/span&gt;&lt;span class="s2"&gt;/user"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get your GitLab user object back.&lt;/p&gt;

&lt;p&gt;Then check that you can see the projects you expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"PRIVATE-TOKEN: &lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_API_URL&lt;/span&gt;&lt;span class="s2"&gt;/projects?membership=true&amp;amp;per_page=20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of the numeric &lt;code&gt;id&lt;/code&gt; of a project you care about.&lt;/p&gt;

&lt;p&gt;It turns out to be more reliable than using the project path in some situations, which we will cover in the troubleshooting section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Install the Server
&lt;/h2&gt;

&lt;p&gt;There are several ways to install the server.&lt;/p&gt;

&lt;p&gt;Using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @zereight/mcp-gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or using Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp
brew &lt;span class="nb"&gt;install &lt;/span&gt;zereight/gitlab-mcp/zereight-mcp-gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both approaches give you the &lt;code&gt;zereight-mcp-gitlab&lt;/code&gt; binary.&lt;/p&gt;

&lt;p&gt;You can also skip installing it globally and let &lt;code&gt;npx&lt;/code&gt; fetch it when it launches. That trades a little startup time for always getting the current package.&lt;/p&gt;

&lt;p&gt;Check what you have installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; zereight-mcp-gitlab
npm &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 | &lt;span class="nb"&gt;grep &lt;/span&gt;gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Register the MCP Server
&lt;/h2&gt;

&lt;p&gt;Now for the interesting part.&lt;/p&gt;

&lt;p&gt;The goal is to keep the token out of the MCP configuration itself.&lt;/p&gt;

&lt;p&gt;With Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;-s&lt;/span&gt; user gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'GITLAB_PERSONAL_ACCESS_TOKEN=${GITLAB_PERSONAL_ACCESS_TOKEN}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'GITLAB_API_URL=${GITLAB_API_URL}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'GITLAB_PERMISSION_MODE=modify'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; zereight-mcp-gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three things worth paying attention to here.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Single Quotes
&lt;/h3&gt;

&lt;p&gt;Notice the single quotes around the &lt;code&gt;${...}&lt;/code&gt; values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'GITLAB_PERSONAL_ACCESS_TOKEN=${GITLAB_PERSONAL_ACCESS_TOKEN}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quotes prevent your shell from expanding the variable when you run the command.&lt;/p&gt;

&lt;p&gt;The idea is for the literal &lt;code&gt;${GITLAB_PERSONAL_ACCESS_TOKEN}&lt;/code&gt; reference to be stored in the configuration, with the client resolving it when the server launches.&lt;/p&gt;

&lt;p&gt;If you use double quotes instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"GITLAB_PERSONAL_ACCESS_TOKEN=&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;your shell expands the variable immediately.&lt;/p&gt;

&lt;p&gt;That means today's token gets written into the configuration, which defeats the purpose of keeping the secret out of the config file.&lt;/p&gt;

&lt;p&gt;There is an important caveat here, which we will revisit in Troubleshooting: variable expansion behavior can depend on how the MCP server is configured and launched.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use User Scope
&lt;/h3&gt;

&lt;p&gt;I am explicitly using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-s user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than the default local scope.&lt;/p&gt;

&lt;p&gt;A local-scope server is associated with the directory where you registered it.&lt;/p&gt;

&lt;p&gt;That can become annoying when you open another repository and discover that the server is no longer available.&lt;/p&gt;

&lt;p&gt;You may then register it again, potentially ending up with multiple copies of the same server configuration.&lt;/p&gt;

&lt;p&gt;User scope registers the server once so it is available across your directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Set the Permission Mode
&lt;/h3&gt;

&lt;p&gt;The command also includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITLAB_PERMISSION_MODE=modify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will look at the available modes in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Equivalent JSON Configuration
&lt;/h2&gt;

&lt;p&gt;If you prefer editing the MCP configuration directly, the equivalent entry looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gitlab"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zereight-mcp-gitlab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITLAB_PERSONAL_ACCESS_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${GITLAB_PERSONAL_ACCESS_TOKEN}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITLAB_API_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${GITLAB_API_URL}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITLAB_PERMISSION_MODE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"modify"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the token value is a variable reference rather than the actual token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Choose a Permission Mode
&lt;/h2&gt;

&lt;p&gt;The server defaults to &lt;code&gt;full&lt;/code&gt;, which exposes every destructive tool it has.&lt;/p&gt;

&lt;p&gt;I prefer setting the permission mode explicitly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Read&lt;/th&gt;
&lt;th&gt;Create and update&lt;/th&gt;
&lt;th&gt;Delete&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;readonly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For most workflows, &lt;code&gt;modify&lt;/code&gt; is a good middle ground.&lt;/p&gt;

&lt;p&gt;It lets the assistant read issues, post review comments, and update descriptions while preventing delete operations.&lt;/p&gt;

&lt;p&gt;If the assistant only needs to look things up, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;readonly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would avoid &lt;code&gt;full&lt;/code&gt; unless you genuinely need the destructive operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Restart and Verify
&lt;/h2&gt;

&lt;p&gt;MCP servers are read by the client at startup, and a running client keeps the environment it was launched with.&lt;/p&gt;

&lt;p&gt;This matters if you edited your shell profile in Step 3.&lt;/p&gt;

&lt;p&gt;Your current Claude Code session may still have the old environment.&lt;/p&gt;

&lt;p&gt;Start a fresh terminal and launch Claude Code again.&lt;/p&gt;

&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp list
claude mcp get gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want to see the GitLab server connected without any missing-variable warning.&lt;/p&gt;

&lt;p&gt;Finally, exercise the server.&lt;/p&gt;

&lt;p&gt;Ask your assistant to fetch a real GitLab issue and verify that it can access the repository.&lt;/p&gt;

&lt;p&gt;If you are using &lt;code&gt;modify&lt;/code&gt; mode, also confirm that the delete tools are not available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trimming the Toolset
&lt;/h2&gt;

&lt;p&gt;The server exposes 261+ tools.&lt;/p&gt;

&lt;p&gt;That is a lot of surface area.&lt;/p&gt;

&lt;p&gt;Depending on your context budget and workflow, exposing hundreds of tools can consume context that could otherwise be used for the actual task.&lt;/p&gt;

&lt;p&gt;You can narrow the toolset if needed.&lt;/p&gt;

&lt;p&gt;For example, only enable specific groups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GITLAB_TOOLSETS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;issues,merge_requests,pipelines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or explicitly allow individual tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GITLAB_TOOLS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;get_issue,list_issues,create_note
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use a deny-list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GITLAB_DENIED_TOOLS_REGEX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'^delete_'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My recommendation is to start broad and narrow the toolset only if it becomes a problem.&lt;/p&gt;

&lt;p&gt;There is no need to optimize this upfront.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Server connects, but every call returns 401
&lt;/h3&gt;

&lt;p&gt;Your token is probably not reaching the MCP process.&lt;/p&gt;

&lt;p&gt;The most common cause is the missing &lt;code&gt;export&lt;/code&gt; from Step 3.&lt;/p&gt;

&lt;p&gt;Check it from a child process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &lt;span class="nt"&gt;-lic&lt;/span&gt; &lt;span class="s1"&gt;'echo ${GITLAB_PERSONAL_ACCESS_TOKEN:+set}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not, check your shell profile and make sure the variable is exported.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;claude mcp list&lt;/code&gt; warns about a missing variable
&lt;/h3&gt;

&lt;p&gt;This means the &lt;code&gt;${VAR}&lt;/code&gt; reference did not resolve as expected.&lt;/p&gt;

&lt;p&gt;Variable expansion is documented for &lt;code&gt;.mcp.json&lt;/code&gt; and project-scoped servers, but whether the same behavior applies to user-scoped entries is not stated outright.&lt;/p&gt;

&lt;p&gt;So this is something worth verifying rather than assuming.&lt;/p&gt;

&lt;p&gt;If expansion fails, the configuration can still load while the literal string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;${GITLAB_PERSONAL_ACCESS_TOKEN}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gets passed to the server as the token.&lt;/p&gt;

&lt;p&gt;The server can therefore appear healthy while every authenticated request fails with a 401.&lt;/p&gt;

&lt;p&gt;If you run into this, you can interpolate the environment variable when adding the server instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp remove &lt;span class="nt"&gt;-s&lt;/span&gt; user gitlab

claude mcp add &lt;span class="nt"&gt;-s&lt;/span&gt; user gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"GITLAB_PERSONAL_ACCESS_TOKEN=&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"GITLAB_API_URL=&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'GITLAB_PERMISSION_MODE=modify'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; zereight-mcp-gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tradeoff is that the actual secret is now written into the configuration.&lt;/p&gt;

&lt;p&gt;You still keep a single user-scoped server entry, but you lose the benefit of keeping the secret out of the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;404 Project Not Found&lt;/code&gt; for a project you can definitely see
&lt;/h3&gt;

&lt;p&gt;GitLab's API accepts a URL-encoded project path such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group%2Fproject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, some HTTP clients normalize &lt;code&gt;%2F&lt;/code&gt; back into &lt;code&gt;/&lt;/code&gt; before sending the request.&lt;/p&gt;

&lt;p&gt;That can turn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/projects/group%2Fproject/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/projects/group/project/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which GitLab does not recognize as the intended project path.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl --path-as-is&lt;/code&gt; can force the encoded form through, but the more durable solution is to use the numeric project ID from Step 4.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;123456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the numeric ID avoids the path-encoding problem entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  The server is missing in one repo but present in another
&lt;/h3&gt;

&lt;p&gt;You probably registered it with local scope.&lt;/p&gt;

&lt;p&gt;Re-register it at user scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;-s&lt;/span&gt; user gitlab ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then clean up the old local entries from the directories where they were created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/code/some-project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; claude mcp remove &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nb"&gt;local &lt;/span&gt;gitlab&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nothing changed after editing my shell profile
&lt;/h3&gt;

&lt;p&gt;Start a new terminal and restart Claude Code.&lt;/p&gt;

&lt;p&gt;The running client will not magically inherit environment changes made after it started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The whole setup boils down to four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A GitLab personal access token with the right scope&lt;/li&gt;
&lt;li&gt;An API URL ending in &lt;code&gt;/api/v4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Both values exported into your environment&lt;/li&gt;
&lt;li&gt;A single user-scoped MCP server entry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most important detail is the environment variable.&lt;/p&gt;

&lt;p&gt;If it is not exported, the MCP process cannot see it.&lt;/p&gt;

&lt;p&gt;If the variable reference is not expanded by the client, the server may appear connected while authentication fails.&lt;/p&gt;

&lt;p&gt;That is why I recommend verifying the environment from a child process and testing the GitLab API directly before involving MCP.&lt;/p&gt;

&lt;p&gt;Once those pieces are correct, the rest tends to fall into place.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>gitlab</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>String#squish Quietly Changed My SQL</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Sun, 12 Jul 2026 13:28:07 +0000</pubDate>
      <link>https://dev.to/jessalejo/stringsquish-quietly-changed-my-sql-gc3</link>
      <guid>https://dev.to/jessalejo/stringsquish-quietly-changed-my-sql-gc3</guid>
      <description>&lt;p&gt;I use &lt;code&gt;String#squish&lt;/code&gt; almost without thinking.&lt;/p&gt;

&lt;p&gt;It's one of those little ActiveSupport helpers that keeps heredoc SQL readable in code and tidy in logs. You write a nicely indented query, call &lt;code&gt;.squish&lt;/code&gt;, and end up with a clean one-liner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;squish&lt;/span&gt;&lt;span class="sh"&gt;
  SELECT *
  FROM orders
  WHERE status = 'active'
&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt; "SELECT * FROM orders WHERE status = 'active'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still love it.&lt;/p&gt;

&lt;p&gt;Last week, though, it introduced a bug so subtle that I chased it through three layers of the stack before realizing the culprit had been sitting in plain sight the whole time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug report
&lt;/h2&gt;

&lt;p&gt;A reporting dashboard had an "Average Days to Ship" metric filtered by sales representative.&lt;/p&gt;

&lt;p&gt;It worked perfectly for every rep except one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mary  Jones&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Notice the double space between her first and last name.&lt;/p&gt;

&lt;p&gt;The report wasn't wrong. Her name really was stored with two spaces, and it displayed correctly everywhere else. Only this one metric always returned &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The production query has been simplified and anonymized to protect sensitive data. The bug and the relevant code path are exactly the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The investigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Suspect #1: The data
&lt;/h3&gt;

&lt;p&gt;My first instinct was bad data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;sales_rep_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;sales_rep_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'Mary%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- "Mary  Jones"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database contained the double space exactly as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Suspect #2: The params
&lt;/h3&gt;

&lt;p&gt;Maybe the browser or Rails params had collapsed the whitespace.&lt;/p&gt;

&lt;p&gt;I traced the value from the form, through the controller, into the query builder.&lt;/p&gt;

&lt;p&gt;Still two spaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Suspect #3: The browser
&lt;/h3&gt;

&lt;p&gt;HTML has its own whitespace rules, so I convinced myself the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element had to be responsible.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;The option was rendered with an explicit &lt;code&gt;value="Mary  Jones"&lt;/code&gt; attribute, and attribute values preserve internal whitespace.&lt;/p&gt;

&lt;p&gt;Another dead end.&lt;/p&gt;

&lt;p&gt;At that point, I finally did what I should have done from the beginning.&lt;/p&gt;

&lt;p&gt;Instead of reasoning about where the value &lt;em&gt;might&lt;/em&gt; have changed, I logged the final SQL string immediately before it was executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"sales_rep_name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Mary Jones'&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One space.&lt;/p&gt;

&lt;p&gt;The database had two.&lt;/p&gt;

&lt;p&gt;The params had two.&lt;/p&gt;

&lt;p&gt;The Arel-generated SQL fragment had two.&lt;/p&gt;

&lt;p&gt;Somewhere during the final query construction, one of those spaces disappeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reveal
&lt;/h2&gt;

&lt;p&gt;Here's a simplified version of the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sub_query&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;squish&lt;/span&gt;&lt;span class="sh"&gt;
    SELECT AVG(days) AS average_count
    FROM (
      SELECT ...
      FROM orders
      WHERE (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;apply_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WHERE'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)
    ) sub
&lt;/span&gt;&lt;span class="no"&gt;  SQL&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the order of operations.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apply_filter(...).to_sql&lt;/code&gt; produces a properly quoted SQL fragment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="nv"&gt;"sales_rep_name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Mary  Jones'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That fragment is interpolated into the heredoc.&lt;/p&gt;

&lt;p&gt;At that point, the SQL string already contains the data value.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;.squish&lt;/code&gt; runs.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;String#squish&lt;/code&gt; does exactly what it's designed to do: collapse every run of whitespace into a single space.&lt;/p&gt;

&lt;p&gt;It doesn't know that some whitespace belongs to SQL formatting while other whitespace is part of a string literal.&lt;/p&gt;

&lt;p&gt;To &lt;code&gt;squish&lt;/code&gt;, they're all just whitespace.&lt;/p&gt;

&lt;p&gt;You can reproduce it with a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"WHERE name = 'Mary  Jones'"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;squish&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "WHERE name = 'Mary Jones'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query now searches for a person who doesn't exist.&lt;/p&gt;

&lt;p&gt;No rows match.&lt;/p&gt;

&lt;p&gt;The metric ends up reporting &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is such a good trap
&lt;/h2&gt;

&lt;p&gt;Every individual piece of code is perfectly reasonable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building SQL with a heredoc.&lt;/li&gt;
&lt;li&gt;Interpolating a properly quoted Arel fragment.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;.squish&lt;/code&gt; to make the SQL compact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bug only appears when those three things are combined.&lt;/p&gt;

&lt;p&gt;Even worse, it only appears when the data happens to contain adjacent whitespace.&lt;/p&gt;

&lt;p&gt;Most names don't.&lt;/p&gt;

&lt;p&gt;Most free-text values don't.&lt;/p&gt;

&lt;p&gt;So the code can happily run for years, pass every test, and then one day someone named &lt;strong&gt;Mary  Jones&lt;/strong&gt; appears, or a product category is saved as &lt;strong&gt;Office  Supplies&lt;/strong&gt;, and one dashboard quietly starts reporting incorrect numbers.&lt;/p&gt;

&lt;p&gt;Nothing crashes.&lt;/p&gt;

&lt;p&gt;Nothing logs an error.&lt;/p&gt;

&lt;p&gt;The result just looks... believable.&lt;/p&gt;

&lt;p&gt;And that's the dangerous kind of bug.&lt;/p&gt;

&lt;p&gt;A crash gets fixed.&lt;/p&gt;

&lt;p&gt;A believable but incorrect number can live in production for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The fix turned out to be almost anticlimactic.&lt;/p&gt;

&lt;p&gt;Don't apply a whitespace-normalizing transformation after you've already mixed data into the string.&lt;/p&gt;

&lt;p&gt;Removing &lt;code&gt;.squish&lt;/code&gt; solved the problem.&lt;/p&gt;

&lt;p&gt;PostgreSQL doesn't care whether the query spans one line or twenty, so the SQL executes exactly the same.&lt;/p&gt;

&lt;p&gt;The only difference is how it looks in the logs.&lt;/p&gt;

&lt;p&gt;More importantly, avoid interpolating values into raw SQL whenever possible.&lt;/p&gt;

&lt;p&gt;Let ActiveRecord and the database driver handle quoting and parameter binding instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The broader lesson
&lt;/h2&gt;

&lt;p&gt;This isn't really about &lt;code&gt;String#squish&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's about applying text transformations &lt;strong&gt;after&lt;/strong&gt; you've mixed structure and data into the same string.&lt;/p&gt;

&lt;p&gt;Whether it's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;String#squish&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gsub(/\s+/, " ")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;split.join(" ")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;an HTML minifier&lt;/li&gt;
&lt;li&gt;or any other "cleanup" step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;they all operate on plain text.&lt;/p&gt;

&lt;p&gt;They can't distinguish between formatting and content.&lt;/p&gt;

&lt;p&gt;Once your data has been interpolated into the string, a transformation intended to clean up the structure can just as easily modify the data.&lt;/p&gt;

&lt;p&gt;Normalize your templates before interpolation.&lt;/p&gt;

&lt;p&gt;Normalize your data at the boundaries of your system.&lt;/p&gt;

&lt;p&gt;But once structure and data have been combined into the same string, treat that string as read-only.&lt;/p&gt;

&lt;p&gt;I still use &lt;code&gt;String#squish&lt;/code&gt; almost every day.&lt;/p&gt;

&lt;p&gt;I just make sure it's the last thing that touches my SQL template, not the first thing that touches my data.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>sql</category>
    </item>
    <item>
      <title>TIL: How to Fix a PostgreSQL Collation Version Warning in a Kamal-Deployed Rails App</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Fri, 16 Jan 2026 03:24:48 +0000</pubDate>
      <link>https://dev.to/jessalejo/til-how-to-fix-a-postgresql-collation-version-warning-in-a-kamal-deployed-rails-app-5no</link>
      <guid>https://dev.to/jessalejo/til-how-to-fix-a-postgresql-collation-version-warning-in-a-kamal-deployed-rails-app-5no</guid>
      <description>&lt;p&gt;&lt;strong&gt;Today I learned&lt;/strong&gt; how to properly fix this PostgreSQL warning in production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARNING: database "myapp_production" has a collation version mismatch
DETAIL: The database was created using collation version 2.36,
but the operating system provides version 2.41.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This typically appears after an OS or &lt;code&gt;glibc&lt;/code&gt; upgrade. Your Rails app will continue to work, but PostgreSQL warns that &lt;strong&gt;text sorting and indexes may be inconsistent&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;p&gt;Collation affects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ORDER BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LIKE / ILIKE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;indexes on &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;varchar&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignoring the warning can lead to &lt;strong&gt;subtle bugs&lt;/strong&gt;, not crashes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix (safe &amp;amp; recommended)
&lt;/h2&gt;

&lt;p&gt;Since the app is deployed with &lt;strong&gt;Kamal&lt;/strong&gt;, I ran the maintenance commands from inside the app container.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Exec into the running container and open the database console
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kamal app &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"bin/rails dbconsole"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rails handles the correct connection details and avoids Unix socket issues.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Refresh the collation version
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp_production&lt;/span&gt; &lt;span class="n"&gt;REFRESH&lt;/span&gt; &lt;span class="k"&gt;COLLATION&lt;/span&gt; &lt;span class="k"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates PostgreSQL's metadata to acknowledge the new OS collation rules.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Rebuild indexes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REINDEX&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp_production&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ This briefly locks tables, so run it during low-traffic hours.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/rails console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The warning should no longer appear.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This is a &lt;strong&gt;PostgreSQL maintenance task&lt;/strong&gt;, not a Rails bug&lt;/li&gt;
&lt;li&gt;OS upgrades can require manual DB follow-up&lt;/li&gt;
&lt;li&gt;Kamal is perfectly fine for running one-off production maintenance&lt;/li&gt;
&lt;li&gt;Always rebuild indexes after refreshing collation&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Short, practical, and now future-me won’t panic the next time I see that warning 😄&lt;/p&gt;

</description>
      <category>rails</category>
      <category>kamal</category>
    </item>
    <item>
      <title>Pagy 9.1 to 43.0? What have Changed?</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Sun, 16 Nov 2025 07:31:23 +0000</pubDate>
      <link>https://dev.to/jessalejo/pagy-91-to-430-what-have-changed-2933</link>
      <guid>https://dev.to/jessalejo/pagy-91-to-430-what-have-changed-2933</guid>
      <description>&lt;p&gt;If you've noticed a massive version bump for the Pagy gem, you're not seeing things! The Pagy team skipped straight to &lt;strong&gt;Version 43.0.0&lt;/strong&gt;, calling it a "leap version" to signal that this isn't just a new major release—it’s a &lt;strong&gt;complete overhaul and redesign&lt;/strong&gt; of the best Ruby pagination gem.&lt;/p&gt;

&lt;p&gt;Here is a look at the critical changes and exciting improvements that make Pagy v43 simpler, faster, and smarter.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Critical Change: A Complete Redesign for Simplicity 🛠️
&lt;/h2&gt;

&lt;p&gt;The core philosophy behind v43 is "more... with less."&lt;/p&gt;

&lt;p&gt;This release represents a fundamental refactoring and redesign of the code, API, and usage. While this is a &lt;strong&gt;breaking change&lt;/strong&gt; (you must check the upgrade guide!), the result is a massively streamlined experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dramatically Simpler API:&lt;/strong&gt; The mental load is gone. You now primarily need just the &lt;strong&gt;&lt;code&gt;pagy&lt;/code&gt; method&lt;/strong&gt; and the resulting &lt;strong&gt;&lt;code&gt;@pagy&lt;/code&gt; instance&lt;/strong&gt; to handle pagination for &lt;em&gt;any&lt;/em&gt; collection type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;99% Reduction in Configuration:&lt;/strong&gt; Pagy is now smarter out of the box, requiring far less manual setup than ever before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Autoloading:&lt;/strong&gt; Say goodbye to unnecessary overhead. Methods are now &lt;strong&gt;autoloaded only if you use them&lt;/strong&gt;, ensuring near-zero memory consumption for unused features.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. 🚀 Performance and New Techniques
&lt;/h2&gt;

&lt;p&gt;Pagy v43 pushes the boundaries of fast pagination techniques, offering options for every scenario.&lt;/p&gt;

&lt;p&gt;The star of the show is the introduction of &lt;strong&gt;Keynav Pagination&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Keynav:&lt;/strong&gt; This exclusive Pagy technique is the next generation of keyset (cursor-based) pagination. It combines the extreme performance of keyset—which avoids slow &lt;code&gt;OFFSET&lt;/code&gt; and &lt;code&gt;COUNT&lt;/code&gt; queries—with all of Pagy's powerful frontend helpers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technique Versatility:&lt;/strong&gt; Pagy now fully supports a comprehensive suite of pagination techniques, letting you choose the fastest method for your data:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;KEYNAV&lt;/strong&gt; (The new fastest choice)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KEYSET&lt;/strong&gt; (Cursor-based)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OFFSET&lt;/strong&gt; (Traditional)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COUNTLESS&lt;/strong&gt; (For huge collections)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CALENDAR&lt;/strong&gt; (For time-based data)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. 🪄 Better DX with New Interactive Dev-Tools
&lt;/h2&gt;

&lt;p&gt;The new version includes features specifically designed to improve the developer experience (DX):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New PagyWand:&lt;/strong&gt; This interactive tool helps you &lt;strong&gt;integrate Pagy’s CSS with your app's themes&lt;/strong&gt; in real-time. It’s a huge win for styling and customization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagy AI:&lt;/strong&gt; A unique interactive AI is now available directly within the docs and your application to help you with specific Pagy usage questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic I18n Loading:&lt;/strong&gt; Internationalization is now handled automatically, saving you setup time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified JavaScript Setup:&lt;/strong&gt; The process of setting up client-side rendering is now much easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ready to Upgrade?
&lt;/h3&gt;

&lt;p&gt;Pagy v43 is a powerful step forward. While the redesign is significant, the commitment to simplicity means your day-to-day coding will be cleaner and faster.&lt;/p&gt;

&lt;p&gt;Be sure to check out the &lt;strong&gt;&lt;a href="https://ddnexus.github.io/pagy/guides/upgrade-guide/" rel="noopener noreferrer"&gt;Upgrade Guide&lt;/a&gt;&lt;/strong&gt; on the Pagy documentation to make your transition smooth!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rubygems</category>
    </item>
    <item>
      <title>Neat trick to Stop Retyping Arguments</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Sun, 14 Sep 2025 01:31:38 +0000</pubDate>
      <link>https://dev.to/jessalejo/neat-trick-to-stop-retyping-arguments-9gm</link>
      <guid>https://dev.to/jessalejo/neat-trick-to-stop-retyping-arguments-9gm</guid>
      <description>&lt;p&gt;It’s a quiet Sunday morning, and I’m tinkering with my side project. As I look at the service object I’m writing, I catch myself seeing the same pattern… again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is what I keep doing.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;permissions: &lt;/span&gt;&lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
    &lt;span class="vi"&gt;@permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Saving user &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See that &lt;code&gt;self.create&lt;/code&gt; method? All I’m really doing is grabbing arguments and passing them straight to &lt;code&gt;new&lt;/code&gt;. It works, but it feels clunky. Every time I add a new argument to &lt;code&gt;initialize&lt;/code&gt;, say an &lt;code&gt;age&lt;/code&gt; parameter, I have to update it in &lt;em&gt;two places&lt;/em&gt;. It’s a small hassle today, but tomorrow it’s a classic recipe for bugs.&lt;/p&gt;

&lt;p&gt;Somewhere along the way I read about Ruby’s argument forwarding syntax (&lt;code&gt;...&lt;/code&gt;). I need to burn this into my brain because it makes life so much easier.&lt;/p&gt;

&lt;p&gt;Here’s the rewrite for my future self:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is how I should be doing it.&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- So much better&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
    &lt;span class="vi"&gt;@permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Saving user &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;self.create(...)&lt;/code&gt; says, “Grab whatever comes in,” and &lt;code&gt;new(...)&lt;/code&gt; replies, “Pass it all along.” Clean. Simple. Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this is better
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It’s DRY.&lt;/strong&gt; No more repeating &lt;code&gt;name:, email:, permissions:&lt;/code&gt; twice. Less typing, less clutter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It’s resilient.&lt;/strong&gt; If I add &lt;code&gt;age:&lt;/code&gt; to &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;self.create&lt;/code&gt; doesn’t need a single change. Argument forwarding takes care of it automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It covers everything.&lt;/strong&gt; Positional args, keyword args, even blocks. The &lt;code&gt;...&lt;/code&gt; operator forwards it all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And if I ever need to intercept just one argument while passing along the rest, say for logging, that’s easy too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# grab `name`, forward the rest&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"About to create a user named &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
  &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memo to self
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;...&lt;/code&gt; for forwarding arguments. It keeps code cleaner, avoids duplication, and saves future-me from unnecessary headaches.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
    </item>
    <item>
      <title>What If Ruby Didn’t Have Syntactic Sugar?</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Thu, 17 Jul 2025 03:09:40 +0000</pubDate>
      <link>https://dev.to/jessalejo/what-if-ruby-didnt-have-syntactic-sugar-4fm</link>
      <guid>https://dev.to/jessalejo/what-if-ruby-didnt-have-syntactic-sugar-4fm</guid>
      <description>&lt;p&gt;If Ruby didn’t implement &lt;strong&gt;syntactic sugar&lt;/strong&gt;, the language would still work just fine, but it wouldn’t be the Ruby we love. It would be &lt;strong&gt;less elegant, less expressive,&lt;/strong&gt; and frankly, &lt;strong&gt;less enjoyable&lt;/strong&gt; to use.&lt;/p&gt;

&lt;p&gt;So what exactly would we be missing? Let’s take a closer look.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🍬 What Is Syntactic Sugar?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Syntactic sugar refers to language features that don’t add new functionality but make the code more concise, readable, and pleasant to write.&lt;/p&gt;

&lt;p&gt;It’s like a shortcut or a smoother path to express something that’s otherwise more verbose or awkward.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧱 Without Syntactic Sugar: More Verbose, Less Joy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s explore how Ruby would look with and without some of its syntactic sweetness:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Looping
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;With syntactic sugar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Without:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Example 2: Safe navigation (&amp;amp;.)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;With syntactic sugar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Without:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Example 3: Conditional execution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;With syntactic sugar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Welcome!"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;logged_in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Without:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;logged_in&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Welcome!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;🧠 Even Operators Are Methods in Ruby&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's something that surprises many beginners: operators like &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, and &lt;code&gt;[]&lt;/code&gt; are just methods under the hood.&lt;/p&gt;

&lt;p&gt;So when you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby actually sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, even the &lt;code&gt;+&lt;/code&gt; is a method being called on the 1 object. Here's a side-by-side of other common operators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;              &lt;span class="c1"&gt;# 10.-(5)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;              &lt;span class="c1"&gt;# 10./(5)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;              &lt;span class="c1"&gt;# 10.*(5)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;              &lt;span class="c1"&gt;# 10.%(5)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;             &lt;span class="c1"&gt;# 10.**(5)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Arrays too!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;     &lt;span class="c1"&gt;# array = [1, 2, 3, 4, 5]&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;          &lt;span class="c1"&gt;# array.&amp;lt;&amp;lt;(6)&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;            &lt;span class="c1"&gt;# array.[](3)&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"five"&lt;/span&gt;   &lt;span class="c1"&gt;# array.[]=(4, "five")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby makes these look natural, but under the hood, they’re all method calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Bottom Line:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Without syntactic sugar, Ruby would still be capable—but it would &lt;strong&gt;feel&lt;/strong&gt; a lot more like Java or C: more boilerplate, less expression, and definitely less joy.&lt;/p&gt;

&lt;p&gt;What sets Ruby apart isn’t just what it can do, but &lt;strong&gt;how naturally you can express your intent&lt;/strong&gt;. That’s by design.&lt;/p&gt;

&lt;p&gt;As Ruby’s creator Yukihiro “Matz” Matsumoto once said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I want to make Ruby natural, not simple... I want to minimize the human effort, not the computer's."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That vision is brought to life through syntactic sugar—making code feel like poetry, not machinery.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
    </item>
    <item>
      <title>Fixing the “Could not find a valid mapping” Error in Rails 8 Tests</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Tue, 24 Jun 2025 10:06:10 +0000</pubDate>
      <link>https://dev.to/jessalejo/fixing-the-could-not-find-a-valid-mapping-error-in-rails-8-tests-1neh</link>
      <guid>https://dev.to/jessalejo/fixing-the-could-not-find-a-valid-mapping-error-in-rails-8-tests-1neh</guid>
      <description>&lt;p&gt;While writing RSpec tests for a model that references User, I encountered this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;RuntimeError:
  Could not find a valid mapping &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="c"&gt;#&amp;lt;User ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, it seemed like a Devise misconfiguration. But after digging deeper, I found the root cause: &lt;strong&gt;Rails 8 now lazy loads routes by default&lt;/strong&gt;, and this behavior breaks Devise's internal logic during test runs if the routes haven't been loaded yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;To solve it, simply add this snippet in your &lt;code&gt;spec/rails_helper.rb:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:action_mailer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload_routes_unless_loaded&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures routes are loaded before tests relying on Devise or User mappings run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Happens
&lt;/h3&gt;

&lt;p&gt;Devise needs access to routes to resolve model mappings (like for authentication or email delivery). Since Rails 8 defers route loading for performance, Devise can’t find the route it needs during test boot, unless you force it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;p&gt;Big thanks to &lt;a href="https://alvincrespo.hashnode.dev/rails-8s-lazy-route-loading-devise" rel="noopener noreferrer"&gt;Alvin Crespo &lt;/a&gt;for documenting this!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>devise</category>
      <category>rspec</category>
    </item>
    <item>
      <title>Comparing params.expect to params.require and params.fetch</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Sat, 24 May 2025 16:31:31 +0000</pubDate>
      <link>https://dev.to/jessalejo/comparing-paramsexpect-to-paramsrequire-and-paramsfetch-5a69</link>
      <guid>https://dev.to/jessalejo/comparing-paramsexpect-to-paramsrequire-and-paramsfetch-5a69</guid>
      <description>&lt;p&gt;I recently started using &lt;code&gt;params.expect&lt;/code&gt; in my new Rails 8 project. It looked cleaner and more convenient than the usual &lt;code&gt;require(...).permit(...)&lt;/code&gt; approach I’d been using. While it worked well in my code, I wanted to take a step back and understand how it actually compares to the older methods like &lt;code&gt;params.require&lt;/code&gt; and &lt;code&gt;params.fetch&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;&lt;code&gt;params.require&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the standard approach in Rails versions before 8. It explicitly requires that the &lt;code&gt;:user&lt;/code&gt; key exists in the parameters. If it doesn't, Rails raises an &lt;code&gt;ActionController::ParameterMissing&lt;/code&gt; error. After that, it permits only the specified attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fails loudly if the expected key is missing.&lt;/li&gt;
&lt;li&gt;Helps protect against mass-assignment vulnerabilities.&lt;/li&gt;
&lt;li&gt;Well-documented and widely used in the Rails community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slightly verbose, especially for nested structures.&lt;/li&gt;
&lt;li&gt;Requires chaining &lt;code&gt;requires&lt;/code&gt; and &lt;code&gt;permit&lt;/code&gt;, which can feel repetitive.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;&lt;code&gt;params.fetch&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach attempts to fetch the &lt;code&gt;:user&lt;/code&gt; key. If it's not found, it returns and empty hash instead of raising an error. It's more forgiving method, but it has trade-offs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids exceptions when the key is missing.&lt;/li&gt;
&lt;li&gt;Useful in cases where the key is optional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Silently fails back to an empty hash, which can hide bugs.&lt;/li&gt;
&lt;li&gt;Not recommended when strict validation is needed.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;&lt;code&gt;params.expect&lt;/code&gt;&lt;/strong&gt; (Rails 8+)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method was introduced in Rails 8 as a more concise and strict way to handle parameters. It combines both the presence checking and attribute whitelisting in a single method call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks that &lt;code&gt;:user&lt;/code&gt; exists in the parameters.&lt;/li&gt;
&lt;li&gt;Ensures that only the listed attributes are allowed.&lt;/li&gt;
&lt;li&gt;Raises an error if the structure does not match expectations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner syntax with less chaining.&lt;/li&gt;
&lt;li&gt;Strict validation by default.&lt;/li&gt;
&lt;li&gt;Easier to read, especially for nested or deeply structured parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only available in Rails 8 and later.&lt;/li&gt;
&lt;li&gt;May be unfamiliar to developers who haven't used the latest Rails versions.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;If you are working on a Rails 8+ project, &lt;code&gt;params.expect&lt;/code&gt; offers a more concise and expressive way to handle strong parameters. It replaces the need to separately call &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;permit&lt;/code&gt;, while also enforcing strict structure validation.&lt;/p&gt;

&lt;p&gt;For older version of Rails, or for developers who prefer explicit method chaining, &lt;code&gt;params.require(...).permit(...)&lt;/code&gt; remains the reliable and well-understood approach.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;params.fetch&lt;/code&gt; only when you have a clear reason to allow missing keys without raising an error; and be cautious about the risks it introduces.&lt;/p&gt;

&lt;p&gt;This comparison helped me clarify when and why to use each method. I hope it helps others make more informed choices when writing controller logic in Rails.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>readonly vs disabled in Rails Forms</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Wed, 21 May 2025 22:54:44 +0000</pubDate>
      <link>https://dev.to/jessalejo/readonly-vs-disabled-in-rails-forms-47cj</link>
      <guid>https://dev.to/jessalejo/readonly-vs-disabled-in-rails-forms-47cj</guid>
      <description>&lt;p&gt;Today I found out that disabling a form field in Rails means it won't be submitted at all. I used &lt;code&gt;disabled: true&lt;/code&gt; thinking I could show a value that users can’t change — and I could — but the downside is that the value doesn’t get sent to the controller.&lt;/p&gt;

&lt;p&gt;I learned that if I want the value to be shown but not editable and still be passed in the form data, the correct attribute to use is &lt;code&gt;readonly: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This small difference between &lt;code&gt;disabled&lt;/code&gt; and &lt;code&gt;readonly&lt;/code&gt; matters when you're depending on that data in your controller.&lt;/p&gt;

&lt;p&gt;It’s not a Rails issue; it’s how forms work in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form_with&lt;/span&gt; &lt;span class="ss"&gt;model: &lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- This won't send the value --&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;disabled: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- This will send the value --&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;readonly: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a helpful thing to learn today. Hopefully it saves someone else a bit of time too.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Mastering Ruby's String Concatenation with the Backslash (`\`)</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Fri, 04 Apr 2025 06:36:10 +0000</pubDate>
      <link>https://dev.to/jessalejo/mastering-rubys-string-concatenation-with-the-backslash--5bd6</link>
      <guid>https://dev.to/jessalejo/mastering-rubys-string-concatenation-with-the-backslash--5bd6</guid>
      <description>&lt;p&gt;Have you ever struggled with long strings in Ruby that stretch way beyond the recommended 80-character line limit? Fear not—Ruby has a clever trick up its sleeve: the &lt;strong&gt;backslash (&lt;code&gt;\&lt;/code&gt;)&lt;/strong&gt;. This handy tool lets you split long strings across multiple lines, keeping your code clean and readable without sacrificing functionality.&lt;/p&gt;

&lt;p&gt;Let’s dive into how this works and why it’s such a game-changer for developers!&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;What Exactly Does the Backslash Do?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In simple terms, placing a backslash (&lt;code&gt;\&lt;/code&gt;) at the end of a line tells Ruby, “Hey, this string isn’t done yet—it continues on the next line!” Ruby then stitches those lines together into one seamless string. &lt;/p&gt;

&lt;p&gt;This is especially useful when dealing with long blocks of text, like SQL queries or error messages, where readability matters just as much as functionality.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;How It Works in Practice&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s an example to make things crystal clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;long_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This is a very long string that exceeds the 80 character limit "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
              &lt;span class="s2"&gt;"and we want to split it across multiple lines for better readability."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this snippet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The backslash (&lt;code&gt;\&lt;/code&gt;) at the end of the first line signals that the string continues on the next line.&lt;/li&gt;
&lt;li&gt;Ruby combines the two parts into one continuous string.&lt;/li&gt;
&lt;li&gt;Notice that no extra space is added between the lines unless you explicitly include one (like the space after &lt;code&gt;limit&lt;/code&gt; in this example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? A single, easy-to-read string that doesn’t clutter your codebase.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;When Should You Use This Technique?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The backslash method shines in scenarios where you’re working with lengthy text or complex queries. For instance, consider this SQL query inside a Rails scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:with_name_or_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
    &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"LOWER(last_name) LIKE :name OR LOWER(first_name) LIKE :name OR "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
      &lt;span class="s2"&gt;"email LIKE :name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking the query into multiple lines makes it easier to read and debug. Without the backslash, you’d either have to cram everything into one long line (ugh!) or use awkward concatenation methods like &lt;code&gt;+&lt;/code&gt;. The backslash keeps things neat and tidy.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Why You’ll Love Using the Backslash&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here are the top reasons to embrace this technique:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Readable Code&lt;/strong&gt;: Long lines can be overwhelming and hard to maintain. Splitting them up improves clarity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Formatting&lt;/strong&gt;: Whether you’re writing SQL queries, error messages, or user prompts, breaking strings into logical chunks makes your code more professional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Extra Syntax&lt;/strong&gt;: Unlike other languages where you might need to use &lt;code&gt;+&lt;/code&gt; or other operators, Ruby handles it all automatically. Just add the backslash, and you’re good to go!&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Key Things to Keep in Mind&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While the backslash is incredibly useful, there are a few rules to remember:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No Automatic Spaces&lt;/strong&gt;: Ruby won’t insert spaces between the lines unless you explicitly add them. For example:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;   &lt;span class="s2"&gt;"Hello "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
   &lt;span class="s2"&gt;"world!"&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt; "Hello world!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the space after &lt;code&gt;"Hello "&lt;/code&gt; ensures the final output reads correctly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Backslash Placement Matters&lt;/strong&gt;: The backslash must always appear at the &lt;strong&gt;end of the line&lt;/strong&gt;. If there’s anything after it—even a space—you’ll get a syntax error.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Wrapping Up&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Using the backslash for string concatenation is a small but mighty tool in your Ruby toolkit. It helps you write cleaner, more maintainable code while adhering to best practices for line length. Whether you’re crafting SQL queries, formatting error messages, or simply managing long strings, this technique will save you time and headaches.&lt;/p&gt;

&lt;p&gt;So next time you find yourself wrestling with a long string, remember: let the backslash do the heavy lifting. Your future self—and your teammates—will thank you! &lt;/p&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Why 0.1 + 0.2 Doesn’t Equal 0.3?</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Fri, 28 Mar 2025 18:31:31 +0000</pubDate>
      <link>https://dev.to/jessalejo/why-01-02-doesnt-equal-03-2m08</link>
      <guid>https://dev.to/jessalejo/why-01-02-doesnt-equal-03-2m08</guid>
      <description>&lt;p&gt;As developers, we often assume that basic arithmetic will always work as expected. For example, adding &lt;code&gt;0.1&lt;/code&gt; and &lt;code&gt;0.2&lt;/code&gt; should give us &lt;code&gt;0.3&lt;/code&gt;, right? But when you run this calculation in Ruby (or many other programming languages), you might be surprised by the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
&lt;span class="c1"&gt;# Output: 0.30000000000000004&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why doesn’t &lt;code&gt;0.1 + 0.2&lt;/code&gt; equal exactly &lt;code&gt;0.3&lt;/code&gt;? Let’s break it down in simple terms.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Computers Use Binary
&lt;/h2&gt;

&lt;p&gt;Computers represent numbers in binary (base 2), but some decimal numbers can’t be perfectly represented in binary. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The decimal number &lt;code&gt;0.1&lt;/code&gt; is like &lt;code&gt;1/3&lt;/code&gt; in base 10—it becomes a repeating fraction in binary.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;0.2&lt;/code&gt; also has a repeating binary representation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you add these imprecise binary representations together, tiny rounding errors occur. That’s why the result of &lt;code&gt;0.1 + 0.2&lt;/code&gt; ends up being something like &lt;code&gt;0.30000000000000004&lt;/code&gt; instead of exactly &lt;code&gt;0.3&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does This Matter?
&lt;/h2&gt;

&lt;p&gt;While the difference between &lt;code&gt;0.3&lt;/code&gt; and &lt;code&gt;0.30000000000000004&lt;/code&gt; might seem small, it can cause problems in situations where precision is important. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Equal"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Not Equal"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# Output: Not Equal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens because the tiny error makes the numbers not exactly equal.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Fix It in Ruby
&lt;/h2&gt;

&lt;p&gt;If you need precise results, here are two simple solutions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Use BigDecimal for Exact Arithmetic
&lt;/h3&gt;

&lt;p&gt;Ruby provides the &lt;code&gt;BigDecimal&lt;/code&gt; class, which allows you to perform precise decimal calculations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'bigdecimal'&lt;/span&gt;

&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="c1"&gt;# Output: "0.3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using strings to initialize &lt;code&gt;BigDecimal&lt;/code&gt;, you avoid the inaccuracies of floating-point numbers.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Round the Result
&lt;/h3&gt;

&lt;p&gt;If you don’t need extreme precision, you can round the result to a reasonable number of decimal places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
&lt;span class="n"&gt;rounded_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;rounded_result&lt;/span&gt; &lt;span class="c1"&gt;# Output: 0.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach works well for most everyday cases.&lt;/p&gt;




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

&lt;p&gt;The reason &lt;code&gt;0.1 + 0.2&lt;/code&gt; doesn’t equal &lt;code&gt;0.3&lt;/code&gt; is due to how computers represent decimal numbers in binary. While this can lead to unexpected results, tools like &lt;code&gt;BigDecimal&lt;/code&gt; or rounding can help you get the answers you expect.&lt;/p&gt;

&lt;p&gt;So next time you see &lt;code&gt;0.30000000000000004&lt;/code&gt;, remember—it’s just a quirk of how computers handle numbers!&lt;/p&gt;




&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Simulating GCash's Lucky Load Game with Minimal Ruby Code</title>
      <dc:creator>Jess Alejo</dc:creator>
      <pubDate>Wed, 26 Mar 2025 04:07:28 +0000</pubDate>
      <link>https://dev.to/jessalejo/simulating-gcashs-luck-load-game-with-minimal-ruby-code-4edl</link>
      <guid>https://dev.to/jessalejo/simulating-gcashs-luck-load-game-with-minimal-ruby-code-4edl</guid>
      <description>&lt;p&gt;If you've ever participated in a lottery, raffle, or lucky draw, you might have wondered how many possible outcomes exist. In Ruby, three powerful methods—&lt;code&gt;product&lt;/code&gt;, &lt;code&gt;combination&lt;/code&gt;, and &lt;code&gt;sample&lt;/code&gt;—make calculating and generating these possibilities simple. In this post, we'll explore how to use these methods to simulate games like &lt;strong&gt;GCash Lucky Load&lt;/strong&gt; and the &lt;strong&gt;Super Lotto 6/49 lottery&lt;/strong&gt;. By the end, you'll understand how to efficiently model scenarios like selecting &lt;strong&gt;6 unique pairs&lt;/strong&gt; from a set of options or picking &lt;strong&gt;6 numbers out of 49&lt;/strong&gt; for a lottery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding &lt;code&gt;product&lt;/code&gt;, &lt;code&gt;combination&lt;/code&gt;, and &lt;code&gt;sample&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;code&gt;product&lt;/code&gt;: Generating All Possible Combinations&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;product&lt;/code&gt; method in Ruby creates all possible pairings (or combinations) of elements from multiple arrays. This is particularly useful when combining two distinct sets of options, such as colors and icons.&lt;/p&gt;

&lt;p&gt;For example, given an array of colors and an array of icons, &lt;code&gt;product&lt;/code&gt; generates every possible color-icon pair:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[Green Orange Red Purple Teal]&lt;/span&gt;
&lt;span class="n"&gt;icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[Crown Diamond Gift Thumb Ticket Trophy]&lt;/span&gt;

&lt;span class="n"&gt;combinations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [["Green", "Crown"], ["Green", "Diamond"], ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This results in &lt;strong&gt;30 unique color-icon pairs&lt;/strong&gt; (5 × 6).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;code&gt;combination&lt;/code&gt;: Selecting Unique Groups&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once you've generated all possible pairs, &lt;code&gt;combination(n)&lt;/code&gt; allows you to create unique groups of &lt;code&gt;n&lt;/code&gt; items where the order doesn’t matter. This is perfect for simulating draws where only a subset of items is chosen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;draws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Total possible 6-draw combinations: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;draws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Total possible 6-draw combinations: 593,775&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;code&gt;sample&lt;/code&gt;: Simulating Random Draws&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;sample&lt;/code&gt; method is useful for randomly selecting a &lt;strong&gt;single winning draw&lt;/strong&gt;, simulating a real-world lottery or prize selection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;winning_draw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Winning Draw: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;winning_draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [["Orange", "Gift"], ["Red", "Crown"], ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we understand these methods, let’s apply them to simulate a &lt;strong&gt;GCash Lucky Load game&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Simulating GCash's Lucky Load Game
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Define the Available Choices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We set up two arrays: one for colors and another for icons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[Green Orange Red Purple Teal]&lt;/span&gt;
&lt;span class="n"&gt;icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[Crown Diamond Gift Thumb Ticket Trophy]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Generate All Possible Color-Icon Combinations&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;product&lt;/code&gt;, we generate every possible pairing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;all_pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Total unique color-icon pairs: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;all_pairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Total unique color-icon pairs: 30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Simulate a 6-Pair Lucky Draw&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To simulate a game round, we randomly select &lt;strong&gt;6 unique pairs&lt;/strong&gt; from the 30 available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;draws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;all_pairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="n"&gt;winning_draw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Winning combination: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;winning_draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Display the Winning Draw in a Grid&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For better visualization, we format the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;col_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;col_width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;

&lt;span class="n"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ljust&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;winning_draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'x'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'o'&lt;/span&gt;
    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Winning selection:"&lt;/span&gt;
&lt;span class="n"&gt;winning_draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"x &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This outputs a &lt;strong&gt;clear representation&lt;/strong&gt; of the winning draw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         Green   Orange   Red    Purple   Teal  
Crown      o       o       o       o       o    
Diamond    o       x       o       o       x    
Gift       o       o       o       x       o    
Thumb      o       x       o       o       o    
Ticket     o       o       x       x       o    
Trophy     o       o       o       o       o    

Winning selection:
x Orange-Diamond
x Orange-Thumb
x Red-Ticket
x Purple-Gift
x Purple-Ticket
x Teal-Diamond
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Applying This to Other Games
&lt;/h2&gt;

&lt;p&gt;These methods extend beyond just color-icon games. Here are some additional applications:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Lotto (Pick 6 out of 49 Numbers)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ever wondered about your odds in a standard &lt;strong&gt;Super Lotto 6/49&lt;/strong&gt; game? With &lt;strong&gt;13,983,816 possible combinations&lt;/strong&gt;, here’s how to generate a winning draw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="n"&gt;winning_draw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Winning Lotto Draw: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;winning_draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Fantasy Sports Drafts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;combination&lt;/code&gt; to simulate drafting teams of players from a pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Card Games&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Generate random hands by combining cards from a deck.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prize Draws&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ensure fairness by randomly selecting winners from a list of participants.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use &lt;code&gt;product&lt;/code&gt;, &lt;code&gt;combination&lt;/code&gt;, and &lt;code&gt;sample&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Ruby’s &lt;code&gt;product&lt;/code&gt;, &lt;code&gt;combination&lt;/code&gt;, and &lt;code&gt;sample&lt;/code&gt; methods offer efficient ways to generate, analyze, and randomly select from large sets of possibilities. Whether you're designing a game, running a raffle, or calculating probabilities, these methods allow you to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quickly calculate total possible outcomes.
&lt;/li&gt;
&lt;li&gt;Simulate random draws or selections.
&lt;/li&gt;
&lt;li&gt;Test different scenarios without manually listing all possibilities.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Ruby’s &lt;code&gt;product&lt;/code&gt; and &lt;code&gt;combination&lt;/code&gt; methods are powerful tools for simulating real-world games and scenarios. By combining them with &lt;code&gt;sample&lt;/code&gt;, you can efficiently generate all possible outcomes and calculate probabilities. Whether you're designing a &lt;strong&gt;lucky draw&lt;/strong&gt;, organizing a &lt;strong&gt;raffle&lt;/strong&gt;, or building a &lt;strong&gt;card game&lt;/strong&gt;, these methods make the process straightforward and fun.&lt;/p&gt;

&lt;p&gt;Try experimenting with different sets and numbers to see how the odds change—and let us know what creative applications you come up with!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Final Notes:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The example above uses a &lt;strong&gt;6-pair draw&lt;/strong&gt;, but you can adjust this based on your game’s rules.&lt;/li&gt;
&lt;li&gt;For larger datasets, consider using lazy evaluation (&lt;code&gt;lazy&lt;/code&gt;) to optimize memory usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Happy coding!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
