<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://geezercode.notifysvc.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://geezercode.notifysvc.com/" rel="alternate" type="text/html" /><updated>2024-10-07T16:02:11-06:00</updated><id>https://geezercode.notifysvc.com/feed.xml</id><title type="html">Geezercode</title><subtitle>Ruby, Postgres, C, C++, Javascript, Ansible coding insights and examples.</subtitle><entry><title type="html">Crafting Neural Networks in Ruby: A Beginner’s Journey</title><link href="https://geezercode.notifysvc.com/blog/crafting-neural-networks-in-ruby-a-beginner's-journey" rel="alternate" type="text/html" title="Crafting Neural Networks in Ruby: A Beginner’s Journey" /><published>2024-10-07T15:07:15-06:00</published><updated>2024-10-07T15:07:15-06:00</updated><id>https://geezercode.notifysvc.com/blog/crafting-neural-networks-in-ruby:-a-beginner&apos;s-journey</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/crafting-neural-networks-in-ruby-a-beginner&apos;s-journey"><![CDATA[<h1 id="crafting-neural-networks-in-ruby-a-beginners-journey">Crafting Neural Networks in Ruby: A Beginner’s Journey</h1>

<h2 id="introduction">Introduction</h2>

<p>In the ever-evolving landscape of programming, Ruby stands out with its elegant syntax and powerful libraries, making it an intriguing choice for machine learning and artificial neural networks. While Python might dominate the deep learning scene with libraries like TensorFlow and PyTorch, Ruby’s ecosystem has been quietly growing, offering unique opportunities for developers who prefer or are already invested in Ruby. Here’s how you can start crafting your first neural network with Ruby.</p>

<h2 id="why-ruby-for-neural-networks">Why Ruby for Neural Networks?</h2>

<p>Ruby might not be the first language you think of when diving into neural networks, but its ease of use, combined with gems like numo-narray for numerical computations and ruby-fann for neural network creation, makes it a viable option. Ruby’s expressive nature allows for concise yet readable code, which is beneficial when experimenting with complex structures like neural networks.</p>

<h2 id="step-by-step-guide-to-build-your-first-neural-network-with-ruby">Step-by-Step Guide to Build Your First Neural Network with Ruby</h2>

<h3 id="setup-your-environment">Setup Your Environment:</h3>

<ul>
  <li>Ensure you have Ruby installed. If you’re using a version manager like RVM or rbenv, set up your Ruby version.</li>
  <li>Install necessary gems:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>ruby-fann numo-narray
</code></pre></div></div>

<h3 id="understanding-the-basics">Understanding the Basics:</h3>

<p>Before coding, grasp the basic architecture of neural networks: inputs, weights, biases, layers (input, hidden, output), and how they interact through activation functions.</p>

<h3 id="creating-a-simple-neural-network">Creating a Simple Neural Network:</h3>

<p>Let’s create a basic feedforward neural network for binary classification, inspired by the examples found in Ruby communities.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'ruby-fann'</span>

<span class="c1"># Create a neural network with 2 inputs, 6 neurons in a single hidden layer, and 1 output neuron</span>
<span class="n">network</span> <span class="o">=</span> <span class="no">RubyFann</span><span class="o">::</span><span class="no">Standard</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">num_inputs: </span><span class="mi">2</span><span class="p">,</span> <span class="ss">hidden_neurons: </span><span class="p">[</span><span class="mi">6</span><span class="p">],</span> <span class="ss">num_outputs: </span><span class="mi">1</span><span class="p">)</span>

<span class="c1"># Training data: Here, we're using hypothetical data for alcohol consumption vs. academic performance</span>
<span class="n">x_train</span> <span class="o">=</span> <span class="p">[[</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">],</span> <span class="p">[</span><span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.4</span><span class="p">],</span> <span class="p">[</span><span class="mf">0.5</span><span class="p">,</span> <span class="mf">0.6</span><span class="p">],</span> <span class="p">[</span><span class="mf">0.7</span><span class="p">,</span> <span class="mf">0.8</span><span class="p">]]</span>
<span class="n">y_train</span> <span class="o">=</span> <span class="p">[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]</span>

<span class="c1"># Train the network</span>
<span class="n">train_data</span> <span class="o">=</span> <span class="no">RubyFann</span><span class="o">::</span><span class="no">TrainData</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">x_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>
<span class="n">network</span><span class="p">.</span><span class="nf">train_on_data</span><span class="p">(</span><span class="n">train_data</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">)</span>

<span class="c1"># Test the network</span>
<span class="nb">puts</span> <span class="n">network</span><span class="p">.</span><span class="nf">run</span><span class="p">([</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">])</span> <span class="c1"># Should predict close to 0</span>
<span class="nb">puts</span> <span class="n">network</span><span class="p">.</span><span class="nf">run</span><span class="p">([</span><span class="mf">0.7</span><span class="p">,</span> <span class="mf">0.8</span><span class="p">])</span> <span class="c1"># Should predict close to 1</span>
</code></pre></div></div>

<h3 id="explanation">Explanation:</h3>

<ul>
  <li>Inputs: We’re using two inputs which could represent, for example, workday and weekend alcohol consumption.</li>
  <li>Training: The network learns from our provided data through backpropagation, adjusting weights to minimize error.</li>
  <li>Testing: We test the network with unseen data to see how well it generalizes.</li>
</ul>

<h2 id="advanced-tips">Advanced Tips</h2>

<ul>
  <li>Experiment with Architectures: Try different numbers of hidden layers or neurons to see how they affect learning.</li>
  <li>Custom Activation Functions: While ruby-fann provides common activation functions, understanding how to implement or change these can deepen your knowledge.</li>
  <li>Real-World Data: Apply your network to real datasets. Libraries like daru for data manipulation in Ruby can be useful here.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Creating a neural network in Ruby might not be the mainstream choice, but it’s a refreshing approach for Rubyists looking to expand into AI. By leveraging Ruby’s ecosystem, you can build, train, and deploy neural networks that perform tasks like classification or prediction. Remember, the key to mastering neural networks isn’t just in the code but in understanding the underlying concepts of machine learning, which Ruby, with its community-driven development, makes quite accessible.</p>

<p>For those intrigued by this intersection of Ruby and AI, dive deeper into gems, explore more complex architectures, and contribute to the evolving landscape of Ruby in AI. Your journey in crafting neural networks with Ruby could lead to innovative solutions, tailored precisely for the Ruby community’s needs.</p>

<p>Happy Coding!</p>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Crafting Neural Networks in Ruby: A Beginner’s Journey]]></summary></entry><entry><title type="html">Conway’s Game of Life - The Ruby Version</title><link href="https://geezercode.notifysvc.com/blog/conway's-game-of-life-the-ruby-version" rel="alternate" type="text/html" title="Conway’s Game of Life - The Ruby Version" /><published>2024-09-05T11:00:56-06:00</published><updated>2024-09-05T11:00:56-06:00</updated><id>https://geezercode.notifysvc.com/blog/conway&apos;s-game-of-life---the-ruby-version</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/conway&apos;s-game-of-life-the-ruby-version"><![CDATA[<h1 id="building-conways-game-of-life-in-ruby-a-step-by-step-guide">Building Conway’s Game of Life in Ruby: A Step-by-Step Guide</h1>

<p>Conway’s Game of Life, a cellular automaton devised by the British mathematician John Horton Conway, is not just a game but a fascinating simulation of life, death, and evolution based on simple rules. In this blog, we’ll explore how to implement this classic simulation using Ruby, a dynamic, object-oriented programming language known for its simplicity and readability.</p>

<h2 id="what-is-conways-game-of-life">What is Conway’s Game of Life?</h2>

<p>Before diving into code, let’s briefly understand the game:</p>

<ul>
  <li>
    <p><strong>Grid</strong>: The game is played on an infinite grid of square cells, each of which is in one of two states, alive or dead.</p>
  </li>
  <li>
    <p><strong>Rules</strong>:</p>
    <ul>
      <li><strong>Any live cell with fewer than two live neighbors dies, as if by underpopulation.</strong></li>
      <li><strong>Any live cell with two or three live neighbors lives on to the next generation.</strong></li>
      <li><strong>Any live cell with more than three live neighbors dies, as if by overpopulation.</strong></li>
      <li><strong>Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.</strong></li>
    </ul>
  </li>
</ul>

<h2 id="setting-up-your-ruby-environment">Setting Up Your Ruby Environment</h2>

<p>Ensure you have Ruby installed on your system. You can check by running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby <span class="nt">-v</span>
</code></pre></div></div>

<p>If you don’t have Ruby, you can download it from ruby-lang.org or use a version manager like RVM or rbenv.</p>

<h2 id="step-by-step-implementation">Step-by-Step Implementation</h2>

<p>Create a file called <code class="language-plaintext highlighter-rouge">game_of_life.rb</code> using your favorite editor. I use <a href="https://astronvim.com/">AstroNvim</a></p>

<h3 id="1-creating-the-grid">1. <strong>Creating the Grid</strong></h3>

<p>First, we’ll create a class to represent our grid:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># game-of-life.rb</span>
<span class="k">class</span> <span class="nc">Grid</span>
  <span class="nb">attr_accessor</span> <span class="ss">:cells</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">)</span>
    <span class="vi">@cells</span> <span class="o">=</span> <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">height</span><span class="p">)</span> <span class="p">{</span> <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">[]</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
    <span class="vi">@cells</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">[]=</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
    <span class="vi">@cells</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="2-initializing-the-grid-with-random-cells">2. <strong>Initializing the Grid with Random Cells</strong></h3>

<p>We’ll use Ruby’s <code class="language-plaintext highlighter-rouge">rand</code> method to randomly initialize cells:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">initialize_random</span><span class="p">(</span><span class="n">grid</span><span class="p">,</span> <span class="n">density</span> <span class="o">=</span> <span class="mf">0.5</span><span class="p">)</span>
  <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="p">,</span> <span class="n">y</span><span class="o">|</span>
    <span class="n">row</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">x</span><span class="o">|</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="o">=</span> <span class="nb">rand</span> <span class="o">&lt;</span> <span class="n">density</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="3-counting-neighbors">3. <strong>Counting Neighbors</strong></h3>

<p>A crucial function to determine the next state of a cell:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">count_neighbors</span><span class="p">(</span><span class="n">grid</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
  <span class="n">directions</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span>
    <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">],</span>          <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span>
    <span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
  <span class="p">]</span>

  <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">directions</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="o">|</span>
    <span class="n">nx</span><span class="p">,</span> <span class="n">ny</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">dx</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">dy</span>
    <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">grid</span><span class="p">[</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">[</span><span class="n">ny</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">[</span><span class="n">ny</span><span class="p">][</span><span class="n">nx</span><span class="p">]</span>
  <span class="k">end</span>
  <span class="n">count</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="4-next-generation">4. <strong>Next Generation</strong></h3>

<p>Here’s where we apply the rules:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">next_generation</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span>
  <span class="n">new_grid</span> <span class="o">=</span> <span class="no">Grid</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">size</span><span class="p">,</span> <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">.</span><span class="nf">size</span><span class="p">)</span>

  <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="p">,</span> <span class="n">y</span><span class="o">|</span>
    <span class="n">row</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">x</span><span class="o">|</span>
      <span class="n">neighbors</span> <span class="o">=</span> <span class="n">count_neighbors</span><span class="p">(</span><span class="n">grid</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">grid</span><span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
        <span class="n">new_grid</span><span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">neighbors</span> <span class="o">==</span> <span class="mi">2</span> <span class="o">||</span> <span class="n">neighbors</span> <span class="o">==</span> <span class="mi">3</span>
      <span class="k">else</span>
        <span class="n">new_grid</span><span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">neighbors</span> <span class="o">==</span> <span class="mi">3</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="n">new_grid</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="5-displaying-the-grid">5. <strong>Displaying the Grid</strong></h3>

<p>For simplicity, let’s use ASCII:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">display</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span>
  <span class="nb">system</span> <span class="s1">'clear'</span>
  <span class="n">grid</span><span class="p">.</span><span class="nf">cells</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="o">|</span>
    <span class="nb">puts</span> <span class="n">row</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">cell</span><span class="o">|</span> <span class="n">cell</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">?</span> <span class="s1">'■'</span> <span class="p">:</span> <span class="s1">' '</span> <span class="p">}.</span><span class="nf">join</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="6-main-loop">6. <strong>Main Loop</strong></h3>

<p>Now, let’s tie it all together:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">width</span><span class="p">,</span> <span class="n">height</span> <span class="o">=</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">20</span>
<span class="n">grid</span> <span class="o">=</span> <span class="no">Grid</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">)</span>
<span class="n">initialize_random</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span>

<span class="kp">loop</span> <span class="k">do</span>
  <span class="nb">display</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span>
  <span class="n">grid</span> <span class="o">=</span> <span class="n">next_generation</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span>
  <span class="nb">sleep</span> <span class="mf">0.1</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="running-your-game">Running Your Game</h2>

<p>Save this code in a file, say <code class="language-plaintext highlighter-rouge">game_of_life.rb</code>, and run it with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby game_of_life.rb
</code></pre></div></div>

<p>You should see the grid updating every 0.1 seconds, showing the evolution of life forms.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Building Conway’s Game of Life in Ruby not only teaches you about cellular automata but also reinforces Ruby’s syntax, object-oriented programming concepts, and how to manage state changes in a simple yet effective manner. This project can be extended by adding user input for initial patterns, implementing wrap-around edges for an infinite grid, or even visualizing it with a GUI library like Shoes or GTK.</p>

<p>Remember, the beauty of programming lies in experimentation. Modify the rules, change the grid’s behavior, or even introduce new elements to see how they affect the simulation. Happy coding!</p>]]></content><author><name></name></author><category term="ruby" /><category term="general" /><summary type="html"><![CDATA[Building Conway’s Game of Life in Ruby: A Step-by-Step Guide]]></summary></entry><entry><title type="html">Chatwoot: The Open-Source Powerhouse Alternative</title><link href="https://geezercode.notifysvc.com/blog/chatwoot-the-open-source-powerhouse-alternative" rel="alternate" type="text/html" title="Chatwoot: The Open-Source Powerhouse Alternative" /><published>2024-08-28T12:59:52-06:00</published><updated>2024-08-28T12:59:52-06:00</updated><id>https://geezercode.notifysvc.com/blog/chatwoot:-the-open-source-powerhouse-alternative</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/chatwoot-the-open-source-powerhouse-alternative"><![CDATA[<hr />

<h1 id="chatwoot-the-open-source-powerhouse-alternative-to-live-chat-and-tidio"><strong>Chatwoot: The Open-Source Powerhouse Alternative to Live-Chat and Tidio</strong></h1>

<p>In the world of customer support, having a reliable, feature-rich platform can make all the difference. While services like Live-Chat and Tidio have their merits, <strong>Chatwoot</strong> emerges as a formidable open-source alternative, offering flexibility, control, and a community-driven approach to customer engagement. Here’s why Chatwoot stands out and how you can set it up on a Linux VM.</p>

<h2 id="why-choose-chatwoot"><strong>Why Choose Chatwoot?</strong></h2>

<h3 id="open-source-flexibility"><strong>Open Source Flexibility</strong></h3>
<p>Chatwoot’s open-source nature means you’re not just using a service; you’re part of a community. You can customize, extend, and integrate it as you see fit, without being locked into proprietary systems.</p>

<h3 id="multi-channel-support"><strong>Multi-Channel Support</strong></h3>
<p>From website live-chat, email, to social media platforms like X (formerly Twitter), and even WhatsApp, Chatwoot consolidates all your customer interactions into one inbox, simplifying management.</p>

<h3 id="cost-effective"><strong>Cost-Effective</strong></h3>
<p>Being open-source, Chatwoot can significantly reduce costs associated with customer support tools, especially beneficial for startups and small businesses.</p>

<h3 id="self-hosted-security"><strong>Self-Hosted Security</strong></h3>
<p>Hosting Chatwoot on your own Linux VM gives you full control over your data, enhancing security and privacy, which is crucial in today’s digital landscape.</p>

<h2 id="installing-chatwoot-on-a-linux-vm"><strong>Installing Chatwoot on a Linux VM</strong></h2>

<p>Here’s a straightforward guide to get Chatwoot running on an Ubuntu-based Linux VM:</p>

<h3 id="prerequisites"><strong>Prerequisites:</strong></h3>
<ul>
  <li>A Linux VM (Ubuntu 20.04 LTS or newer recommended)</li>
  <li>Minimum 4GB RAM, 4vCPU for small to medium traffic</li>
  <li>Ensure you have <code class="language-plaintext highlighter-rouge">sudo</code> or root access</li>
</ul>

<h3 id="step-by-step-installation"><strong>Step-by-Step Installation:</strong></h3>

<ol>
  <li><strong>Update Your System:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt upgrade <span class="nt">-y</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Install Dependencies:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-y</span> git postgresql postgresql-contrib nodejs npm redis-server imagemagick
</code></pre></div>    </div>

    <ul>
      <li><strong>Node.js:</strong> Chatwoot requires Node.js. Install the latest LTS version:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-sL</span> https://deb.nodesource.com/setup_20.x | <span class="nb">sudo</span> <span class="nt">-E</span> bash -
<span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-y</span> nodejs
</code></pre></div>        </div>
      </li>
      <li><strong>Yarn:</strong> Instead of npm, you might want to use Yarn:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>npm <span class="nb">install</span> <span class="nt">--global</span> yarn
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li><strong>Set Up Ruby:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-y</span> software-properties-common
<span class="nb">sudo </span>add-apt-repository <span class="nt">-y</span> ppa:rael-gc/rvm
<span class="nb">sudo </span>apt-get update
<span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-y</span> rvm
<span class="nb">source</span> /etc/profile.d/rvm.sh
rvm <span class="nb">install </span>ruby-3.2.2
rvm use 3.2.2 <span class="nt">--default</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Install Chatwoot:</strong>
    <ul>
      <li>Clone the repository and setup:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/chatwoot/chatwoot.git
<span class="nb">cd </span>chatwoot
bundle <span class="nb">install
</span>yarn <span class="nb">install</span>
</code></pre></div>        </div>
      </li>
      <li>Configure your environment variables in <code class="language-plaintext highlighter-rouge">.env</code> file. You’ll need to set up your database, Redis, and email settings.</li>
    </ul>
  </li>
  <li><strong>Database Setup:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> <span class="nt">-u</span> postgres psql
<span class="c"># Inside psql, create user and database for Chatwoot</span>
CREATE USER chatwoot CREATEDB<span class="p">;</span>
CREATE DATABASE chatwoot_production WITH OWNER chatwoot<span class="p">;</span>
<span class="se">\q</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Migrate and Seed the Database:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">RAILS_ENV</span><span class="o">=</span>production bundle <span class="nb">exec </span>rake db:create
<span class="nv">RAILS_ENV</span><span class="o">=</span>production bundle <span class="nb">exec </span>rake db:reset
</code></pre></div>    </div>
  </li>
  <li><strong>Precompile Assets:</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">RAILS_ENV</span><span class="o">=</span>production bundle <span class="nb">exec </span>rake assets:precompile
</code></pre></div>    </div>
  </li>
  <li><strong>Start Chatwoot:</strong>
Use <code class="language-plaintext highlighter-rouge">cwctl</code> if available, or manually start the services:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails s <span class="nt">-e</span> production
</code></pre></div>    </div>

    <p>For background workers:</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">RAILS_ENV</span><span class="o">=</span>production bundle <span class="nb">exec </span>sidekiq <span class="nt">-C</span> config/sidekiq.yml
</code></pre></div>    </div>
  </li>
  <li><strong>Accessing Chatwoot:</strong>
Open your web browser and navigate to <code class="language-plaintext highlighter-rouge">http://your_vm_ip:3000</code>. Follow the setup to create your admin account.</li>
</ol>

<h2 id="conclusion"><strong>Conclusion</strong></h2>

<p>Chatwoot not only stands as a robust alternative to Live-Chat and Tidio but also brings the power of open-source to customer support. Its installation might seem technical, but with this guide, you’re well-equipped to set up your own instance on a Linux VM. Enjoy the freedom, control, and community support that comes with Chatwoot, enhancing your customer interaction like never before.</p>

<p>Happy coding!</p>]]></content><author><name></name></author><category term="ruby-on-rails" /><category term="ruby" /><category term="crm" /><category term="general" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">AstroNvim - My Fingers Are Crossed</title><link href="https://geezercode.notifysvc.com/blog/astronvim-my-fingers-are-crossed" rel="alternate" type="text/html" title="AstroNvim - My Fingers Are Crossed" /><published>2024-04-26T13:28:28-06:00</published><updated>2024-04-26T13:28:28-06:00</updated><id>https://geezercode.notifysvc.com/blog/astronvim---my-fingers-are-crossed</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/astronvim-my-fingers-are-crossed"><![CDATA[<p>Literally 2 months after I posted my <a href="https://geezercode.notifysvc.com/blog/lunarvim-programmer's-editor.html">Lunarvim blog entry</a> about the programmer’s editor carousel, I found out the project is <a href="https://www.reddit.com/r/neovim/comments/1caaldi/lunarvim_has_been_abandoned_by_maintainers/">abandoned</a>!</p>

<p>Though this initial saddened me, I believe I’ve found a better NeoVim distribution in AstroNvim.</p>

<p>Here are the editors I tried after Lunarvim died, rated in order of awesomeness:</p>

<ol>
  <li>AstroNvim</li>
  <li>LazyVim</li>
  <li>NvChad</li>
</ol>

<ul>
  <li><a href="https://www.lazyvim.org/">LazyVim</a> second place to Astro. It was easy to setup but a bit more painful to customize.</li>
  <li><a href="https://nvchad.com/">NvChad</a> third place. It seems very capable. Not difficult to setup but again, painful to customize when compared to AstroNvim.</li>
</ul>

<p>AstroNvim is fairly lean. Only a few plugins come installed by default and the keystroke mappings are sane and easy to customize using WhichKey or standard vim and nvim mappings. Lazy is great for plugins. Treesitter is there and highly useful. Supports LuaSnip with vscode style snippet files. LSP is pre-configured with sane defaults and easy to install language support for things that are missing. AutoCmds are easy to configure, etc.</p>

<p>It only took me about an hour to move my Lunarvim plugins, configs and settings over to AstroNvim.</p>

<p>Now I’m just hoping that it too will not become abandoned or unmaintained.</p>

<p>🤞🏼</p>]]></content><author><name></name></author><category term="general" /><summary type="html"><![CDATA[Lunarvim is now abandoned. Long Live AstroNvim!]]></summary></entry><entry><title type="html">Lunarvim - Programmer’s Editor</title><link href="https://geezercode.notifysvc.com/blog/lunarvim-programmer's-editor" rel="alternate" type="text/html" title="Lunarvim - Programmer’s Editor" /><published>2024-02-19T09:41:59-07:00</published><updated>2024-02-19T09:41:59-07:00</updated><id>https://geezercode.notifysvc.com/blog/lunarvim---programmer&apos;s-editor</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/lunarvim-programmer&apos;s-editor"><![CDATA[<p>Like me, have you been riding the programmer’s editor carousel?  Writing code since 1990 I’ve used several editors and seen them come and go.</p>

<p>The pattern is a common one where an enterprising programmer decides they want to create their own editor, providing time-saving features.  In most cases the new editors start off with the best intentions. They come off the line lean, with enough useful features to gain significant coder adoption. Then, in most cases, they become fat, slow and bloated or fall into un-maintained mode where bugs are no longer fixed and features are not updated.</p>

<p>The problem with most graphical IDE editors:</p>

<ul>
  <li>They are platform specific and non-portable. (Textmate, etc.)</li>
  <li>They “protect” coders from learning the funatmentals of programming. (compiling, debugging, etc.)</li>
  <li>They track you. They may do this blatantly like “VS Code”, which pings home every single time you launch it, or they may do it backhandedly by automatically checking for updates without your permission.</li>
  <li>They don’t work on remote code. If you have to <code class="language-plaintext highlighter-rouge">ssh</code> into a remote server and edit things, you will have to use <code class="language-plaintext highlighter-rouge">nano</code>, <code class="language-plaintext highlighter-rouge">vim</code> or whatever the server admin allows but you most likely won’t be able to use a graphical editor. To be fair, some editors support SFTP file editing, like Sublime Text, but it can be a pain to setup and work with, and if the server admin does not allow SFTP you are out of luck.</li>
</ul>

<p>Fortunately, I’ve never had to use <code class="language-plaintext highlighter-rouge">ED</code> or <code class="language-plaintext highlighter-rouge">TECO</code>, but here is a list of editors that I’ve used extensively over my career along with their eventual problems that made me switch to something else. I started my programming career on a TI-99 computer, then quickly switched to Windows in the early 90’s, but soon switched to Mac and then finally Linux. This list reflects the progression of those platform choices.</p>

<h3 id="basic---ti-99">BASIC - TI-99</h3>

<p>A very featureless, proprietary text editor for the TI-99. Very tedious to use compared to modern editors.</p>

<p>Abandoned this editor and platform to move on to PC/DOS programming.</p>

<h3 id="visual-basic-2---windows">Visual Basic 2 - Windows</h3>

<p>A revolutionary experience in programming with it’s forms editor and IDE approach. It borrowed keystrokes and features from Emacs. Eventually became fat and bloated like most MS products. See <a href="https://en.wikipedia.org/wiki/Visual_Studio">Visual Studio</a></p>

<p>Moved on after getting a C++ programming job.</p>

<h3 id="visual-c-1---windows">Visual C++ 1 - Windows</h3>

<p>Similar to the VB IDE, but for C/C++</p>

<p>Moved on after switching to a Java programming job. NOTE: These Visual X editors eventually became <a href="https://en.wikipedia.org/wiki/Visual_Studio">Visual Studio</a>, which is now incredibly fat, slow and bloated. The problem with these MS editors is that people come out of school and start using them and fail to learn the fundamentals because the IDE hides too much of that from the user. New programmers get used to IDEs like this and then don’t know how to compile their code, setup a project or run a debugger without using them.</p>

<h3 id="textmate---mac"><a href="https://macromates.com/">Textmate</a> - Mac</h3>

<p>Not an IDE but a great text editor for Ruby programmers. Easily customizable using Ruby, supporting complicated snippets.</p>

<p>Moved on after switching from Mac to Linux.</p>

<h3 id="sublime-text---linux"><a href="https://www.sublimetext.com/">Sublime Text</a> - Linux</h3>

<p>Also not an IDE but a lean, comprehensive, fast programmer’s editor, highly customizable using Python.</p>

<p>Moved on because there were some version hiccups where it wasn’t updated for over a year and Sublime Text 3 has become fat and bloated like most other editors.</p>

<h3 id="atom---linux">Atom - Linux</h3>

<p>A very promising programmer’s editor, easy to hack and customize for your needs as long as you know Javascript and CSS.</p>

<p>Moved on because it was abandoned in 2022, and I did not like having to drop down to Javascript in order to customize it.</p>

<h3 id="vscodium---linux">VSCodium - Linux</h3>

<p>A fat IDE with hundreds of plugins to support many languages and comprehensive customizations.</p>

<p>Moved on because it is slow and just feels fat and bloated, especially when all I want to do is quickly write a Bash script or other type of small text file.</p>

<h3 id="vim---linux">Vim - Linux</h3>

<p>Finally decided to switch to Vim after wanting to for years.  The benefits of Vim are:</p>

<ul>
  <li>It is terminal/curses based which means it can be used over SSH on remote servers</li>
  <li>It is incredibly lean and fast to load, edit and save files</li>
  <li>It is portable to virtually every platform</li>
  <li>It can be easily extended to meet a programmer’s needs</li>
  <li>It doesn’t track you by phoning home every time you launch it</li>
  <li>It is extremely stable</li>
</ul>

<p>The reason for delaying the switch for so long is mainly the learning curve. As alluded to above, you get used to an IDE and it’s extensive features with easy to use keymaps and mouse features. Non-Vim users, like myself for so many years, become spoiled and fear the learning curve. I didn’t want to re-learn how to do block selections, block editing, the new Vim keystrokes, Command mode vs Insert mode, etc.</p>

<p>It took about 2 weeks to become proficient in Vim after finally switching.  I am still learning how to use it more efficiently but the benefits have far outweighed the drawbacks and fear.</p>

<h3 id="lunarvim---linux"><a href="https://www.lunarvim.org/">Lunarvim</a> - Linux</h3>

<p>After finally switching to <a href="https://www.lunarvim.org/">Lunarvim</a> (an IDE layer for <a href="https://neovim.io/">Neovim</a>), I’ve found editor nirvana.  Lunarvim makes it easy to customize and maintain Vim as a very usable IDE. Lunarvim features LUA config scripts, snippets and you can use Neovim and Vim plugins</p>

<p>I want to acknowledge the religious fervor surrounding two of the oldest editors, <code class="language-plaintext highlighter-rouge">Emacs</code> and <code class="language-plaintext highlighter-rouge">Vim</code>.  I have no experience with vanilla Emacs but I’ve used editors that were heavily based on it. Emacs was released in 1976, Vim in 1991. They are both still going strong and have a huge userbase.</p>

<p>If you decide to switch from a graphical IDE to Vim, Neovim or Lunarvim, my advice is to be patient but persistent. It is easy to be tempted to switch back to your graphical IDE of choice when you have deadlines to meet and are still learning Vim.  Don’t do it!  Fight through and learn the Vim keystrokes or find plugins to help you.  Once you finally dive in and abandon the graphical alternatives you will find yourself becomming more proficient and faster at writing code than ever before.</p>

<p>Happy coding!</p>]]></content><author><name></name></author><category term="general" /><summary type="html"><![CDATA[Tired of riding the editor carousel? Try Lunarvim and take back control...]]></summary></entry><entry><title type="html">Brave/Chrome Tab Favicon circles</title><link href="https://geezercode.notifysvc.com/blog/brave-chrome-tab-favicon-circles" rel="alternate" type="text/html" title="Brave/Chrome Tab Favicon circles" /><published>2024-02-16T09:48:29-07:00</published><updated>2024-02-16T09:48:29-07:00</updated><id>https://geezercode.notifysvc.com/blog/brave-chrome-tab-favicon-circles</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/brave-chrome-tab-favicon-circles"><![CDATA[<p>After recently updating my Brave browser, I’ve noticed a change on browser tabs. Tabs that go “inactive” get little circles drawn around the favicon which deforms them and just generally looks bad. Personally, I’m not a fan of this change. If you are reading this you probably dislike this recent change as well.</p>

<p>After looking into the Chrome flags I discovered this:</p>

<p><img src="/assets/images/brave-browser-tabs.png" alt="Browser Tab Circles" /></p>

<p>There are several options here. I chose “Enable Without Ring and 30% Opacity” which looks much more intuitive. The favicons now get faded but they don’t have the little circle around them. Choose whichever option works best for you.</p>

<p>Cheers!</p>]]></content><author><name></name></author><category term="general" /><summary type="html"><![CDATA[Get rid of the favicon circles in the Brave browser.]]></summary></entry><entry><title type="html">SQL - Aggregates over time</title><link href="https://geezercode.notifysvc.com/blog/sql-aggregates-over-time" rel="alternate" type="text/html" title="SQL - Aggregates over time" /><published>2024-02-12T11:18:50-07:00</published><updated>2024-02-12T11:18:50-07:00</updated><id>https://geezercode.notifysvc.com/blog/sql---aggregates-over-time</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/sql-aggregates-over-time"><![CDATA[<p>Need to aggregate data over time? This is a common requirement, and a great way to deliver aggregated reports for your clients.</p>

<h2 id="postgres-sql">Postgres SQL</h2>

<p>Here’s the Postgres SQL to aggregate data by month:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="n">date_trunc</span><span class="p">(</span><span class="s1">'month'</span><span class="p">,</span> <span class="n">delivered_at</span><span class="p">)</span> <span class="k">as</span> <span class="k">month</span><span class="p">,</span> <span class="k">count</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="k">as</span> <span class="k">count</span>
<span class="k">from</span> <span class="nv">"user_properties"</span>
<span class="k">where</span> <span class="nv">"user_properties"</span><span class="p">.</span><span class="nv">"user_id"</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">group</span> <span class="k">by</span> <span class="n">date_trunc</span><span class="p">(</span><span class="s1">'month'</span><span class="p">,</span> <span class="n">delivered_at</span><span class="p">)</span>
<span class="k">order</span> <span class="k">by</span> <span class="nv">"month"</span> <span class="k">desc</span>
<span class="p">;</span>
</code></pre></div></div>

<p>Results look like this:</p>

<table>
  <thead>
    <tr>
      <th>month</th>
      <th>count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2024-04-01 00:00:00.000</td>
      <td>12</td>
    </tr>
    <tr>
      <td>2024-03-01 00:00:00.000</td>
      <td>28</td>
    </tr>
    <tr>
      <td>2024-02-01 00:00:00.000</td>
      <td>15</td>
    </tr>
    <tr>
      <td>2024-01-01 00:00:00.000</td>
      <td>35</td>
    </tr>
  </tbody>
</table>

<p>Substitute your own ids and DateTime column for your needs.</p>

<h2 id="activerecord-scope">ActiveRecord Scope</h2>

<p>Here is the same result as an ActiveRecord scope:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scope</span> <span class="ss">:delivered_by_month</span><span class="p">,</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">user_id</span><span class="o">|</span>
    <span class="no">UserProperty</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="s2">"date_trunc('month', delivered_at) as month, count(id) as count"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">user_id: </span><span class="n">user_id</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">group</span><span class="p">(</span><span class="s2">"date_trunc('month', delivered_at)"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">order</span><span class="p">(</span><span class="ss">month: :desc</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can use this in your HTML template like this (note: we use HAML here):</p>

<div class="language-haml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">%table</span><span class="nf">#lead-deliveries</span><span class="nc">.table.display.is-striped.is-fullwidth</span>
  <span class="nt">%thead</span>
    <span class="nt">%tr</span>
      <span class="nt">%th</span> Count
      <span class="nt">%th</span> Month
  <span class="nt">%tbody</span>
    <span class="p">-</span> <span class="vi">@leads</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">delivery</span><span class="o">|</span>
      <span class="nt">%tr</span>
        <span class="nt">%td</span><span class="p">=</span> <span class="n">delivery</span><span class="p">.</span><span class="nf">count</span>
        <span class="nt">%td</span><span class="p">=</span> <span class="n">delivery</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">month</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">strftime</span><span class="p">(</span><span class="s1">'%B, %Y'</span><span class="p">)</span>
</code></pre></div></div>

<p>…which will look something like this:</p>

<p><img src="/assets/images/sql-aggregates.png" alt="SQL Aggregates" /></p>

<p>Cheers!</p>]]></content><author><name></name></author><category term="sql" /><category term="ruby" /><category term="ruby-on-rails" /><summary type="html"><![CDATA[Need to aggregate data over time? This is a common requirement, and a great way to deliver aggregated reports for your clients.]]></summary></entry><entry><title type="html">Linux .desktop file for AppImage</title><link href="https://geezercode.notifysvc.com/blog/linux-.desktop-file-for-appimage" rel="alternate" type="text/html" title="Linux .desktop file for AppImage" /><published>2023-07-11T06:57:27-06:00</published><updated>2023-07-11T06:57:27-06:00</updated><id>https://geezercode.notifysvc.com/blog/linux-.desktop-file-for-appimage</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/linux-.desktop-file-for-appimage"><![CDATA[<p>As a Linux user, I’m not a fan of <a href="https://snapcraft.io/">Snaps</a>. I prefer the canonical package repositories but sometimes, for certain packages, canonical doesn’t have it, or if it does the version is quite old. In those cases I look for an <a href="https://appimage.org/">AppImage</a>.</p>

<p>An AppImage is a self contained app with all of it’s required libraries and everything it needs to run on Linux the way the author intends. To run an AppImage you just have to download it and/or unpack/unzip it into a directory, make it executable then run it. There is no “installation” process, nothing to create a desktop icon or menu entry.</p>

<p>I use <a href="https://albertlauncher.github.io/">Albert</a> on Linux, which I highly recommend as an app-launcher. To get Albert to see my AppImages or to add an AppImage to the application menus we have to create a <code class="language-plaintext highlighter-rouge">.desktop</code> file at <code class="language-plaintext highlighter-rouge">$HOME/.local/share/applications</code>.  For example, here’s my entry for <a href="https://github.com/hackjutsu/Lepton">Lepton</a>…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Desktop Entry]
Name=Lepton
GenericName=Gist Editor
Comment=Lepton Gist GUI
Exec=sh -c "/home/midwire/AppImages/Lepton-1.10.0.AppImage --in-process-gpu"
Keywords=Gist
Icon=lepton.png
Terminal=false
Type=Application
Categories=Programming;
</code></pre></div></div>

<p>The first line is a standard string <code class="language-plaintext highlighter-rouge">[Desktop Entry]</code>. This is required.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Name</code> can be anything you like</li>
  <li><code class="language-plaintext highlighter-rouge">GenericName</code> can be anything you like</li>
  <li><code class="language-plaintext highlighter-rouge">Comment</code> is the description of the app</li>
  <li><code class="language-plaintext highlighter-rouge">Exec</code> is the command-line needed to run the app</li>
  <li><code class="language-plaintext highlighter-rouge">Keywords</code> will allow you to find the menu item. Semi-colon separated</li>
  <li><code class="language-plaintext highlighter-rouge">Icon</code> is optional but will provide a custom icon for the app entry. Icons must be located at ~/.icons and only use the base filename, not the full path.</li>
  <li><code class="language-plaintext highlighter-rouge">Terminal</code> can be true or false depending on whether you want to run the app in a terminal</li>
  <li><code class="language-plaintext highlighter-rouge">Type</code> always use <code class="language-plaintext highlighter-rouge">Application</code> for AppImages</li>
  <li><code class="language-plaintext highlighter-rouge">Categories</code> is optional but allows the system menu to place the entry where it belongs in your categorized menus</li>
</ul>

<p>And that’s it. Now your AppImage will appear in Albert and in your system menus.</p>

<p><img src="/assets/images/albert-lepton.png" alt="Lepton in Albert" /></p>]]></content><author><name></name></author><category term="linux" /><summary type="html"><![CDATA[Add an AppImage to your menu or to Albert on Linux]]></summary></entry><entry><title type="html">Slack chat links using the raw editor</title><link href="https://geezercode.notifysvc.com/blog/slack-chat-links-using-the-raw-editor" rel="alternate" type="text/html" title="Slack chat links using the raw editor" /><published>2022-12-07T09:24:56-07:00</published><updated>2022-12-07T09:24:56-07:00</updated><id>https://geezercode.notifysvc.com/blog/slack-chat-links-using-the-raw-editor</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/slack-chat-links-using-the-raw-editor"><![CDATA[<p>For those of us who don’t use the in-app Slack rich editor, it is still possible to create titled hyperlinks. You just use wiki-syntax.</p>

<p>Simple as:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[This is a link to Ruby on Rails](https://rubyonrails.org/)
</code></pre></div></div>

<p>It is the little things that make the world go around.</p>

<p>Cheers</p>

<p>–Geeze</p>]]></content><author><name></name></author><category term="slack" /><summary type="html"><![CDATA[It's the little things that make the world go around. A quick post to explain how to add a hyperlink in a Slack message using the raw editor.]]></summary></entry><entry><title type="html">Rails Rendering With Non-Standard File Extensions</title><link href="https://geezercode.notifysvc.com/blog/rails-rendering-with-non-standard-file-extensions" rel="alternate" type="text/html" title="Rails Rendering With Non-Standard File Extensions" /><published>2022-09-17T07:45:21-06:00</published><updated>2022-09-17T07:45:21-06:00</updated><id>https://geezercode.notifysvc.com/blog/rails-rendering-with-non-standard-file-extensions</id><content type="html" xml:base="https://geezercode.notifysvc.com/blog/rails-rendering-with-non-standard-file-extensions"><![CDATA[<p>Rails 3 deprecated having a period in the template and layout filenames when rendering. Then that <a href="https://github.com/rails/rails/commit/6c57177f2c7f4f934716d588545902d5fc00fa99">deprecation was removed in 2011</a>, only to be <a href="https://github.com/rails/rails/pull/39164">added back in again in 2020</a>. 🤣 So if you try to render a template like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">render_to_string</span><span class="p">(</span>
  <span class="ss">template: </span><span class="s1">'notifiers/base_notifier.html.inky-haml'</span><span class="p">,</span>
  <span class="ss">layout: </span><span class="s1">'mailer.html.inky-haml'</span>
<span class="p">)</span>
</code></pre></div></div>

<p>You will get a nasty little warning: <code class="language-plaintext highlighter-rouge">DEPRECATION WARNING: Rendering actions with '.' in the name is deprecated:</code> 😡🤬</p>

<p>So how do we render the correct template and layout without running into this deprecation?</p>

<p>The documentation for render mentions several <a href="https://guides.rubyonrails.org/layouts_and_rendering.html#options-for-render">options we can pass to the method</a>.  After reading that, the first likely solution would be to try <code class="language-plaintext highlighter-rouge">formats</code> or <code class="language-plaintext highlighter-rouge">variants</code>, neither of which will remove the deprecation.</p>

<p>The <code class="language-plaintext highlighter-rouge">formats</code> option is an array in order of preferred formats for the template or layout you are interested in using.  But <code class="language-plaintext highlighter-rouge">formats</code> do not equate to filename extensions. Instead, they equate to the ultimate content type that you want to render.  For example: <code class="language-plaintext highlighter-rouge">html</code>, <code class="language-plaintext highlighter-rouge">json</code>, <code class="language-plaintext highlighter-rouge">js</code>, <code class="language-plaintext highlighter-rouge">xml</code>, etc.  We may want to try something like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">render_to_string</span><span class="p">(</span>
  <span class="ss">template: </span><span class="s1">'notifiers/base_notifier'</span><span class="p">,</span>
  <span class="ss">formats: </span><span class="p">[</span><span class="s1">'inky-haml'</span><span class="p">,</span> <span class="ss">:html</span><span class="p">],</span>
  <span class="ss">layout: </span><span class="s1">'mailer'</span>
<span class="p">)</span>
</code></pre></div></div>

<p>But this will raise an error because <code class="language-plaintext highlighter-rouge">inky-haml</code> is not a format.  But it is a <code class="language-plaintext highlighter-rouge">handler</code>.</p>

<p><strong>SOLUTION: Use Handlers</strong></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">render_to_string</span><span class="p">(</span>
  <span class="ss">template: </span><span class="s1">'notifiers/base_notifier'</span><span class="p">,</span>
  <span class="ss">handlers: </span><span class="p">[</span><span class="s1">'inky-haml'</span><span class="p">],</span>
  <span class="ss">layout: </span><span class="s1">'mailer'</span>
<span class="p">)</span>
</code></pre></div></div>

<p>… Although it isn’t documented, this works because installing the <code class="language-plaintext highlighter-rouge">inky</code> gem adds this handler to the allowed <code class="language-plaintext highlighter-rouge">handlers</code> array.  Rails tries to determine the correct handler on it’s own, but sometimes it doesn’t work quite right, as in the case where you have multiple layouts or templates with similar names:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mailer.html.erb
mailer.text.erb
mailer.html.inky-haml
</code></pre></div></div>

<p>Now, go forth and render with confidence…</p>

<p>Cheers,</p>

<p>– Geeze</p>

<p><em>For reference, a list of formats will look something like this:</em></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="ss">formats: </span><span class="p">[</span>
  <span class="ss">:atom</span><span class="p">,</span>
  <span class="ss">:bmp</span><span class="p">,</span>
  <span class="ss">:css</span><span class="p">,</span>
  <span class="ss">:csv</span><span class="p">,</span>
  <span class="ss">:doc</span><span class="p">,</span>
  <span class="ss">:doc</span><span class="p">,</span>
  <span class="ss">:docx</span><span class="p">,</span>
  <span class="ss">:docx</span><span class="p">,</span>
  <span class="ss">:gif</span><span class="p">,</span>
  <span class="ss">:gzip</span><span class="p">,</span>
  <span class="ss">:html</span><span class="p">,</span>
  <span class="ss">:ics</span><span class="p">,</span>
  <span class="ss">:jpeg</span><span class="p">,</span>
  <span class="ss">:js</span><span class="p">,</span>
  <span class="ss">:json</span><span class="p">,</span>
  <span class="ss">:m4a</span><span class="p">,</span>
  <span class="ss">:mp3</span><span class="p">,</span>
  <span class="ss">:mp4</span><span class="p">,</span>
  <span class="ss">:mpeg</span><span class="p">,</span>
  <span class="ss">:multipart_form</span><span class="p">,</span>
  <span class="ss">:ogg</span><span class="p">,</span>
  <span class="ss">:otf</span><span class="p">,</span>
  <span class="ss">:pdf</span><span class="p">,</span>
  <span class="ss">:pdf</span><span class="p">,</span>
  <span class="ss">:png</span><span class="p">,</span>
  <span class="ss">:rss</span><span class="p">,</span>
  <span class="ss">:svg</span><span class="p">,</span>
  <span class="ss">:text</span><span class="p">,</span>
  <span class="ss">:tiff</span><span class="p">,</span>
  <span class="ss">:ttf</span><span class="p">,</span>
  <span class="ss">:url_encoded_form</span><span class="p">,</span>
  <span class="ss">:vcf</span><span class="p">,</span>
  <span class="ss">:vtt</span><span class="p">,</span>
  <span class="ss">:webm</span><span class="p">,</span>
  <span class="ss">:woff</span><span class="p">,</span>
  <span class="ss">:woff2</span><span class="p">,</span>
  <span class="ss">:xls</span><span class="p">,</span>
  <span class="ss">:xlsx</span><span class="p">,</span>
  <span class="ss">:xml</span><span class="p">,</span>
  <span class="ss">:yaml</span><span class="p">,</span>
  <span class="ss">:zip</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Handlers look like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="ss">handlers: </span><span class="p">[</span>
  <span class="ss">:"inky-axlsx"</span><span class="p">,</span>
  <span class="ss">:"inky-builder"</span><span class="p">,</span>
  <span class="ss">:"inky-caracal"</span><span class="p">,</span>
  <span class="ss">:"inky-coffee"</span><span class="p">,</span>
  <span class="ss">:"inky-erb"</span><span class="p">,</span>
  <span class="ss">:"inky-haml"</span><span class="p">,</span>
  <span class="ss">:"inky-html"</span><span class="p">,</span>
  <span class="ss">:"inky-jbuilder"</span><span class="p">,</span>
  <span class="ss">:"inky-prawn"</span><span class="p">,</span>
  <span class="ss">:"inky-raw"</span><span class="p">,</span>
  <span class="ss">:"inky-ruby"</span><span class="p">,</span>
  <span class="ss">:axlsx</span><span class="p">,</span>
  <span class="ss">:builder</span><span class="p">,</span>
  <span class="ss">:caracal</span><span class="p">,</span>
  <span class="ss">:coffee</span><span class="p">,</span>
  <span class="ss">:erb</span><span class="p">,</span>
  <span class="ss">:haml</span><span class="p">,</span>
  <span class="ss">:html</span><span class="p">,</span>
  <span class="ss">:inky</span><span class="p">,</span>
  <span class="ss">:jbuilder</span><span class="p">,</span>
  <span class="ss">:prawn</span><span class="p">,</span>
  <span class="ss">:raw</span><span class="p">,</span>
  <span class="ss">:ruby</span>
<span class="p">]</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="ruby-on-rails" /><category term="ruby" /><summary type="html"><![CDATA[Render non-standard files without running into the [DEPRECATION WARNING: Rendering actions with '.' in the name is deprecated:] message.]]></summary></entry></feed>