<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/vendor/feed/atom.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
                        <id>https://myray.app/feed</id>
                                <link href="https://myray.app/feed" rel="self"></link>
                                <title><![CDATA[myray.app]]></title>
                    
                                <subtitle>Read about new features, upcoming updates, and useful tips for using Ray.</subtitle>
                                                    <updated>2026-02-18T09:33:52+00:00</updated>
                        <entry>
            <title><![CDATA[Making electron's IPC typesafe end-to-end]]></title>
            <link rel="alternate" href="https://myray.app/blog/making-electrons-ipc-typesafe-end-to-end" />
            <id>https://myray.app/making-electrons-ipc-typesafe-end-to-end</id>
            <author>
                <name><![CDATA[Dries]]></name>
            </author>
            <summary type="html">
                <![CDATA[<h2>Leveraging TypeScript at its fullest potential</h2>
<p>The thing we love most about libraries that are fully typed is type inference, especially through generics. It almost feels like magic. A good example is TanStack’s <a href="https://tanstack.com/query/latest/docs/framework/react/typescript#type-inference">React Query</a>. I always get a warm, fuzzy feeling when my API data types are inferred and ripple through all the callbacks available in the <code>useQuery</code> or <code>useMutation</code> hooks.</p>
<p>But these features are often achieved through some pretty wild-looking type definitions. That’s something we rarely attempt in our own application code, because let’s be honest, who understands something like this on the first try, let alone comes up with it?</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationValueOf</span><span style="color: #ECEFF4">&lt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #8FBCBB">T</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Record</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #ECEFF4">&gt;,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationKeyOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #ECEFF4">&gt;,</span></span>
<span class="line"><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`${</span><span style="color: #81A1C1">infer</span><span style="color: #A3BE8C"> </span><span style="color: #8FBCBB">Head</span><span style="color: #ECEFF4">}</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">${</span><span style="color: #81A1C1">infer</span><span style="color: #A3BE8C"> </span><span style="color: #8FBCBB">Tail</span><span style="color: #ECEFF4">}`</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">keyof</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">T</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">T</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">] </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Record</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Tail</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationKeyOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">				</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationValueOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Tail</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">				</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">never</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Required</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">] </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Record</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">			  </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Tail</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationKeyOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">Required</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">			      </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationValueOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">Required</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">T</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">Head</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Tail</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">undefined</span></span>
<span class="line"><span style="color: #D8DEE9FF">				  </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">never</span></span>
<span class="line"><span style="color: #D8DEE9FF">			  </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">never</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">never</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">keyof</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">T</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">T</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">never</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>If you’ve been writing TypeScript for a few years and have peeked at some library internals, you might find this readable. But for most TypeScript developers, this looks like pure wizardry.</p>
<p>In this blogpost we want to show you how going the extra mile in Ray has made our code much safer.</p>
<h2>Let's dive in!</h2>
<p>What this “monstrosity” above enables is the ability to extract a value from an object using a key in dot notation. For example, suppose I want to create a type for a map of callbacks that uses another object's property values as the type of the callback function's arguments (bear with me, this might sound like a very niche problem). My first naïve implementation might look like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">foo</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #D8DEE9">bar</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">hello</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #D8DEE9">baz</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">world</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">quux</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">!</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MyKeys</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	[</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">keyof</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #D8DEE9FF">)[</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">void</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callbacksObj</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MyKeys</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">foo</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	  </span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{},</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// the value argument is typed as object with string literals &quot;hello&quot; and &quot;world&quot;: &#39;{bar: &quot;hello&quot;, baz: &quot;world&quot;}&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">quux</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{},</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// the value argument is typed as string literal: &#39;!&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This works nicely, but there’s one problem: the type of <code>value</code> for property <code>foo</code> is the object with properties <code>bar</code> and <code>baz</code>. If your goal is to go one level deep, you’re done. But what if you want a callback only for <code>foo.bar</code> but not for <code>foo.baz</code>?</p>
<p>That’s where our “monstrosity” (<code>DotNotationValueOf</code>) from earlier comes into play:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">foo</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #D8DEE9">bar</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">hello</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #D8DEE9">baz</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">world</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">quux</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">!</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MyKeys</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	[</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationKeyOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationValueOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">typeof</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">myObj</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">K</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">void</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">obj</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MyKeys</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">foo.bar</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{},</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// the value argument is typed as string literal: &#39;hello&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">quux</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{},</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// the value argument is typed as string literal: &#39;!&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>As you can see, we can now access <code>foo.bar</code> and have our callback argument <code>value</code> correctly typed as string literal <code>'hello'</code>!</p>
<h2>How we used this in our codebase</h2>
<p>In Ray, we have an incredibly useful hook that syncs data from our Electron store to the renderer process called <code>useStoreSync</code>. A small example:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">useStoreSync</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">server</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">settings.shouldConfirmCloseConnection</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span><span style="color: #88C0D0">setShouldConfirmConnectionClose</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #D8DEE9">defaultValue</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">connections</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">connections</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span><span style="color: #88C0D0">setServerConnections</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">connections</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #D8DEE9">defaultValue</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> []</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// definition</span></span>
<span class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useStoreSync</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ElectronStoreNames</span><span style="color: #ECEFF4">&gt;(</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">storeName</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">storeKeys</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">StoreKeys</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;,</span></span>
<span class="line"><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>As you can see, we rely heavily on generics to make this work, but the underlying code isn’t much more complex than our earlier example. The <code>StoreKeys</code> type looks like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">StoreKeys</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ElectronStoreNames</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	[</span><span style="color: #8FBCBB">K</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationKeyOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">ElectronStore</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;&gt;</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">?:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">callback</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DotNotationValueOf</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">ElectronStore</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">K</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">void</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		defaultValue</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Additional types so you understand better what&#39;s happening above:</span></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">StoreMapType</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    archive</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ArchiveStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    archivev2</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ArchiveStoreV2Props</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    license</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">LicenseStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    onboarding</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">OnboardingStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    settings</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SettingsStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    server</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ServerStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    windowState</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">WindowStateStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    shortcuts</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ShortcutStoreProps</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ElectronStore</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ElectronStoreNames</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">StoreMapType</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>The <code>DotNotationValueOf&lt;ElectronStore&lt;N&gt;, K&gt;</code> part is what allows us to access the keys of our store in a dot notation manner.</p>
<p>At the start of our rewrite, the Electron stores were pretty flat JSON objects, but we quickly ran into the need for “namespacing.” We wanted to organize related keys under logical namespaces within the same store. For example, in our Server store, we wanted a <code>settings</code> namespace.</p>
<p>Initially, we prefixed flags with something like <code>settingShouldConfirmCloseConnection</code>, but that never felt quite right. We hadn’t yet figured out how to support dot notation in our <code>useStoreSync</code> hook, so we ended up with this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ServerStoreProps</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    connections</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ServerConnection</span><span style="color: #D8DEE9FF">[]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    settingShouldConfirmCloseConnection</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">boolean</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Later, we discovered a neat type definition inside the <a href="https://github.com/sindresorhus/electron-store">electron-store</a> library that allowed dot notation keys—the <code>DotNotationKeyOf</code> type from earlier. We borrowed and adapted it for our use case.</p>
<p>That type was the missing piece we needed to move from prefixed keys to cleanly nested ones:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ServerStoreProps</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    connections</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ServerConnection</span><span style="color: #D8DEE9FF">[]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    settings</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        shouldConfirmCloseConnection</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">boolean</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Much cleaner!</p>
<h2>Why going the extra mile with your types is worth it</h2>
<p>The goal of this blog post is not to show off our cool types, but to make a case that showcases the power of being meticulous, so you might re-evaluate your own types within your own codebase. Slapped an <code>any</code> or <code>unknown</code> on something because you couldn't be bothered to figure out the types? We've all been there... I could have just called it a day after writing this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">[</span><span style="color: #D8DEE9">k</span><span style="color: #D8DEE9FF">: </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">]: </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #88C0D0">callback</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unknown</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">void</span><span style="color: #D8DEE9FF">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #D8DEE9">defaultValue</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">unknown</span><span style="color: #D8DEE9FF">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>While this works, you lose all your type safety. What happens if <code>settings.shouldConfirmCloseConnection</code> is renamed to <code>settings.shouldConfirmClose</code>? You’ll likely get a runtime error. Maybe you catch it during development, in your tests or during QA, but maybe you don’t.</p>
<p>Going the extra mile saves you a ton of headaches and makes you sleep soundly at night. Another added benefit is improvements in productivity thanks to IntelliSense: you don’t have to remember property names exactly or their types, your editor will do that for you.</p>
<h2>And a final example: Fully typed RPC handlers</h2>
<p>Alongside our <code>useStoreSync</code> hook, we also have a simple, fully typed RPC implementation built on top of Electron’s IPC:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// @shared/rpc.ts</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCMap</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">archive:get-archive-logs-by-id</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        params</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> id</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        response</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            archiveLogs</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ArchiveLogs</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Utility types for RPC</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">keyof</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCMap</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCParams</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCMap</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF">][</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">params</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCMap</span><span style="color: #D8DEE9FF">[</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF">][</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">response</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * RPC handler for main process</span></span>
<span class="line"><span style="color: #616E88"> *</span></span>
<span class="line"><span style="color: #616E88"> * Used in the main process to handle RPC calls.</span></span>
<span class="line"><span style="color: #616E88"> *</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">param</span><span style="color: #616E88"> </span><span style="color: #D8DEE9">name</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">param</span><span style="color: #616E88"> </span><span style="color: #D8DEE9">handler</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">rpcHandle</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #ECEFF4">&gt;(</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">handler</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">params</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCParams</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Promise</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">RPCResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;</span></span>
<span class="line"><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ipcMain</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">handle</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">formatRpcName</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handler</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">params</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * RPC invoke for renderer process</span></span>
<span class="line"><span style="color: #616E88"> *</span></span>
<span class="line"><span style="color: #616E88"> * Used in the render process to invoke methods on the main side.</span></span>
<span class="line"><span style="color: #616E88"> *</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">param</span><span style="color: #616E88"> </span><span style="color: #D8DEE9">name</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">param</span><span style="color: #616E88"> </span><span style="color: #D8DEE9">params</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">returns</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">rpcInvoke</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #ECEFF4">&gt;(</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCParams</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">undefined</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Promise</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">RPCResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">&gt;&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">invoke</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">formatRpcName</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">formatRpcName</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">N</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">RPCName</span><span style="color: #ECEFF4">&gt;(</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">N</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">rpc:</span><span style="color: #ECEFF4">${</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">}`</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>First of all, we define a type that is effectively a map of all our events and their respective payloads and responses. This allows us to create 3 utility types <code>RPCName</code>, <code>RPCParams</code> and <code>RPCResponse</code>. We use generics based on the RPC name so we can let the type inference do it's magic later on in our <code>rpcHandle</code> and <code>rpcInvoke</code> functions.</p>
<p>The <code>rpcHandle</code> function is used in our main process. While its definition is fully typed, it contains some “unsafe” code internally because Electron’s <code>ipcMain.handle</code> method types the event name as just <code>string</code> and its parameters as <code>any</code>. However, this isn’t a real concern, since we shield our code from that type &quot;unsafe&quot; Electron code by wrapping it with our own typesafe <code>rpcHandle</code>. Same story for our <code>rpcInvoke</code>.</p>
<h3>Example usage</h3>
<p>In the preload file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// preload.ts</span></span>
<span class="line"><span style="color: #D8DEE9">contextBridge</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">exposeInMainWorld</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">electron</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">rpc</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">invoke</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rpcInvoke</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>In the main process:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// @main/rpc-handler.ts</span></span>
<span class="line"><span style="color: #88C0D0">rpcHandle</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">archive:get-archive-logs-by-id</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ArchiveStore</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getArchiveLogsById</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Promise</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">resolve</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">archiveLogs</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>In the renderer process:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// @renderer/useRpcInvoke.ts</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useRpcInvoke</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">invoke</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">electron</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">rpc</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">invoke</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Example inside a React component</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">invoke</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useRpcInvoke</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">activeArchiveId</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setActiveArchiveId</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">archiveLogs</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setArchiveLogs</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">ArchiveLogs</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">([])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">useEffect</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #88C0D0">invoke</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">archive:get-archive-logs-by-id</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">activeArchiveId</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">archiveLogs</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">setArchiveLogs</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">archiveLogs</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> [</span><span style="color: #D8DEE9">activeArchiveId</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">invoke</span><span style="color: #D8DEE9FF">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This setup looks deceptively simple, but it took time to reach this level of elegance. It’s intuitive, maintainable and most importantly, protects us from runtime errors by surfacing issues at compile time.</p>
<h2>Additional concerns</h2>
<h3>What about leveraging useSyncExternalStore from React for your useStoreSync hook?</h3>
<p>Our <code>useStoreSync</code> hooks basically acts like a hand rolled (and suboptimal) <code>useSyncExternalStore</code>. We weren't aware of how handy this hook actually is when we first came up with our own implementation.</p>
<p>One of the future improvements on our roadmap is to create a fully typed store sync hook with <code>useSyncExternalStore</code> for each individual <a href="https://github.com/sindresorhus/electron-store">electron-store</a>. We would love to have an API like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">archives</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useArchiveStore</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// Where archives is inferred as Archive[]!</span></span>
<span class="line"></span></code></pre>
<p>But that's the beauty of programming, you constantly evolve on your ideas because you learn of better ways to do things. Sometimes the best solutions only present themselves after implementing a suboptimal one.</p>
<p>Stay tuned for part 2 when we revisit this system by implementing a new store hook leveraging <code>useSyncExternalStore</code>!</p>
]]>
            </summary>
                                    <updated>2026-02-18T09:33:52+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Quick tips: Sending Laravel output to Ray automatically]]></title>
            <link rel="alternate" href="https://myray.app/blog/quick-tips-sending-laravel-output-to-ray-automatically" />
            <id>https://myray.app/quick-tips-sending-laravel-output-to-ray-automatically</id>
            <author>
                <name><![CDATA[Jimi]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>If you use Laravel and <a href="/docs/php/laravel/installation">the Laravel integration for Ray</a>, you can configure it to automatically send various useful events, queries, requests and other calls from your project to Ray.</p>
<p>Not all calls are enabled by default. You can see what events are available in your <code>ray.php</code> file in <a href="/docs/php/laravel/configuration">the root of your Laravel project</a>. You can enable them there (globally) or override them individually using your .env file.</p>
<p>Here are some useful lines in that file we like to use during development:</p>
<h2>Duplicate &amp; slow queries</h2>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Disabled by default</span></span>
<span class="line"><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">send_duplicate_queries_to_ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">env</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">SEND_DUPLICATE_QUERIES_TO_RAY</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">),</span></span>
<span class="line"><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">send_slow_queries_to_ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">env</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">SEND_SLOW_QUERIES_TO_RAY</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">),</span></span>
<span class="line"></span></code></pre>
<p>These ones are very useful to enable when debugging ... queries. While you can configure these to be logged by Laravel, it's much easier to have these appear directly in your Ray window.</p>
<h2>Exceptions</h2>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Enabled by default</span></span>
<span class="line"><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">send_exceptions_to_ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">env</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">SEND_DUPLICATE_QUERIES_TO_RAY</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">),</span></span>
<span class="line"></span></code></pre>
<p>Send your whole stack trace from Laravel to Ray, which is equally interactive and a bit easier to navigate. It even allows you to share the trace (<a href="https://flareapp.io/">through Flare</a>) if you need a colleague to have a look at your error.</p>
<p><img src="https://content.spatie.be/assets/screenshot-2026-01-23-at-13.46.07.png" alt="" /></p>
<h2>Native dumps</h2>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Disabled by default</span></span>
<span class="line"><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">send_dumps_to_ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">env</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">SEND_DUPLICATE_QUERIES_TO_RAY</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">),</span></span>
<span class="line"></span></code></pre>
<p>While we (obviously) recommend using the <code>ray()</code> function and its many <a href="/docs/php/laravel/configuration">different methods</a>, you can keep using <code>dd()</code> and <code>dump()</code> by enabling this setting. They will still print debug output in your application though.</p>
<h2>Want to know more?</h2>
<p>You can find a lot of useful information in <a href="/docs/getting-started/installation">our Laravel (and PHP, or Javascript) docs</a> on how to use Ray, and what different methods we offer. We also recently launched Ray 3.0, rebuilt for speed and efficiency. If you haven't used Ray in a while, now's a good time to download the trial!</p>
]]>
            </summary>
                                    <updated>2026-02-16T08:21:43+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Introducing Ray 3.0]]></title>
            <link rel="alternate" href="https://myray.app/blog/introducing-ray-30" />
            <id>https://myray.app/introducing-ray-30</id>
            <author>
                <name><![CDATA[Jimi]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>This release took longer than we expected, but we used that time to completely rebuild Ray, and make it faster, look and feel better, and pack this release full of great new features.</p>
<p>If you're already on Ray 2.8 or any of the Ray 3.0 betas, the quickest way to upgrade is to download the latest version for your platform on <a href="https://myray.app/">https://myray.app/</a> or under <a href="https://spatie.be/profile/purchases">your purchases</a> on your Spatie profile. <strong>Any license bought before the release of Ray 3 remains valid.</strong></p>
<h2>What's new in Ray 3.0</h2>
<h3>1. Better performance</h3>
<p>Rebuilt for speed and efficiency, Ray 3.0 is noticeably faster, loads quicker, and handles heavy dumping without a sweat. It also uses significantly less memory, reducing memory usage by 60% in some cases.</p>
<img src="https://content.spatie.be/assets/ray_mail_hello_3.png" />
<h3>2. Let AI agents interact with Ray</h3>
<p>Ray now includes a <a href="https://myray.app/docs/features/mcp">Model Context Protocol (MCP) server</a> that lets AI agents read from and write to Ray, interact with it directly, and trigger many of the same actions you’d normally perform manually in the app.</p>
<img src="https://content.spatie.be/assets/ray-mail-ai.png" />
<h3>3. Archiving messages</h3>
<p>Never lose a message again! Clearing your screen now saves any previously sent messages to an archive. Use the archive to compare output between sessions or debug hard-to-reproduce issues.</p>
<img src="https://content.spatie.be/assets/ray-mail-archive-dark.png" />
<h3>4. Fresh new look</h3>
<p>Ray has a cool, new design that looks and feels great in both light and dark modes. We've also added a few cool themes, including one for our fellow Artisans and one that speaks to your inner Hacker 👀.</p>
<img src="https://content.spatie.be/assets/ray-mail-themes.png" />
<p>You can find the full release notes here: <a href="https://myray.app/changelog">https://myray.app/changelog</a>.</p>
<h2>Upgrading to Ray 3.0</h2>
<p>If you're new to Ray, <a href="https://myray.app/">download the trial</a> and try it out. If you like it, support us by buying a yearly license, or for a limited time only, <strong>buying a lifetime license</strong> and never worry about renewing again.</p>
<p>If you're already on Ray 2.8 or any of the Ray 3.0 betas, the quickest way to upgrade is to download the latest version for your platform here <a href="https://myray.app/">https://myray.app/</a> or under <a href="https://spatie.be/profile/purchases">your purchases</a> on your Spatie profile. <strong>Any license bought before the release of Ray 3 remains valid.</strong></p>
<h2>Help us build our roadmap</h2>
<p>We got tons of great feedback (thank you testers!) during the beta phase, and we would like to keep that conversation going. This time, <strong>we want to hear what should be on our roadmap next</strong>.</p>
<p>Ray 3.0 is just the beginning, and we've got plenty more ideas and improvements planned for upcoming releases. If you've got feature requests, ideas, or workflows where Ray could help you out, share them on <a href="https://github.com/spatie/ray/discussions">our GitHub discussion page</a>.</p>
<p>We’re all ears.</p>
]]>
            </summary>
                                    <updated>2026-02-02T16:05:49+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Ray 3.0 Beta is here!]]></title>
            <link rel="alternate" href="https://myray.app/blog/ray-30-beta-is-here" />
            <id>https://myray.app/ray-30-beta-is-here</id>
            <author>
                <name><![CDATA[Dries]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>It’s been a bit quiet from us these last few weeks, but for good reason. We wanted to hand you a beta we’re genuinely proud of. Our full rebuild of Ray has almost hit 100% feature parity with Ray v2 — just… better, everywhere. You can download the beta here: <a href="https://next.myray.app/">next.myray.app</a>. Your Ray v2 license works with Ray 3.0! So it should be a seamless upgrade.</p>
<h2>Ray 3 Highlights</h2>
<p>In Ray v2, clearing your logs created a new “screen” while keeping all old logs in memory. If that sounds like a memory leak waiting to happen… yeah, it kind of was. We did it so you could jump back to previous screens easily, but it came at a cost.</p>
<p>Ray 3.0 completely changes this with the new message archive. It’s a dedicated screen that stores all your past Ray sessions in one easy-to-reach place. The best part? Past sessions live on disk and are only loaded when you open the archive.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-3-0-beta-is-here/archive.png" alt="Archive screen" /></p>
<p>No more memory ballooning during those late-night debugging marathons. Ray 3.0 just keeps humming along.</p>
<p>The biggest visible change in Ray 3.0 is the UI. We went for a more modern look and added animations, small interactions, smooth transitions — the whole thing just feels sharper and more alive.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-3-0-beta-is-here/main.png" alt="Ray main application" /></p>
<p>We also added a system that lets us support even more themes in the future. Today, Ray ships with two: <strong>Moonlight</strong> and <strong>Daylight</strong>, with more on the way.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-3-0-beta-is-here/daylight.png" alt="Ray Daylight theme" /></p>
<p>Even though we rebuilt Ray from scratch, nothing really changes for you day-to-day. It’s still the Ray you open every morning. Just faster, smoother, and cleaner under the hood.</p>
<p>We rebuilt Ray because we wanted a codebase we can maintain and extend without fighting it. Ray started as a fun little side project, and the codebase showed it. Now that we understand how people actually use Ray and where the rough edges are, starting fresh just made sense.</p>
<p>As a bonus, this new foundation lets us ship new features much more easily. A tiny teaser of what <em>might</em> be coming: let’s just say… every app has it these days. AI, MCP, something something… 🤖</p>
<h2>You can help us!</h2>
<p>Ray 3.0 is still in beta, which means there are probably a few bugs hiding in the corners. That’s where you come in! We’ve set up a public <a href="https://github.com/spatie/ray-app-v3-beta">GitHub repository</a> for Ray 3.0 where you can report issues and help us smooth out the rough edges.</p>
<p>But it’s not just about bug reports, we’d love to chat with you during the open beta. If you have ideas, features you’re missing, or something in your workflow Ray could help with, drop them in the <a href="https://github.com/spatie/ray-app-v3-beta/discussions/1">dedicated GitHub discussion</a>. We’re all ears.</p>
<p>So what are you waiting for, head over to <a href="https://next.myray.app/">next.myray.app</a> to get your hands on Ray 3.0!</p>
]]>
            </summary>
                                    <updated>2025-11-24T14:33:26+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Sign of life]]></title>
            <link rel="alternate" href="https://myray.app/blog/sign-of-life" />
            <id>https://myray.app/sign-of-life</id>
            <author>
                <name><![CDATA[Dries]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>It's been a while since our last blog post. But we're back! Bringing you bi-weekly updates on our progress building the third iteration of Ray. Want to get these updates in your mailbox? <a href="https://next.myray.app">Subscribe to our Ray v3 newsletter</a>.</p>
<p>As stated in previous blog posts, we are building Ray from the ground up. New learnings, new approaches and feedback from the community are shaping Ray to be its best iteration yet. And this is only the beginning. We have some cool features planned at a later stage, so keep an eye on this blog in the coming months.</p>
<h2>So what happened?</h2>
<p>Life. To put it simple. We are a small company so our resources are limited. But it got embarrassing to read all the support tickets and feature requests, so we went out and found some reinforcements (hey that's me!)</p>
<p>I'll be working on Ray for the coming months to finish what Sébastien started almost 12 months ago.</p>
<h2>What can you expect?</h2>
<p>You're getting early access! 🎉 If you have a lifetime license for Ray, we'll be giving you access to our beta builds once we feel they are good enough for daily use. We can't wait to hear what you think. Your feedback will help shape the 3.0.0 release.</p>
<p>We'll also continue sharing our learnings on this blog while working on Ray, so we hope you'll learn a thing or two together with us.</p>
<h2>And finally...</h2>
<p>... let's take you through some of the new screens in Ray v3!</p>
<p>The new home screen where all your logs will be kept...</p>
<p><img src="https://content.spatie.be/assets/ray/sign-of-life/ray-home-screen.png" alt="Ray home screen" /></p>
<p>Logs coming in!</p>
<p><img src="https://content.spatie.be/assets/ray/sign-of-life/ray-logs.png" alt="Ray logs" /></p>
<p>Did someone say... archiving sessions to disk?</p>
<p><img src="https://content.spatie.be/assets/ray/sign-of-life/ray-archive-screen.png" alt="Ray archive screen" /></p>
<p>We're super excited and hope you are too! And lifetime Ray supporters, keep an eye on your mailbox the coming weeks. 👀</p>
<p>Like already mentioned above, to get updates on v3, <a href="https://next.myray.app">subscribe to our Ray newsletter</a>.</p>
]]>
            </summary>
                                    <updated>2025-10-29T10:00:47+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Mixing up our styling recipe for Ray v3]]></title>
            <link rel="alternate" href="https://myray.app/blog/mixing-up-our-styling-recipe-for-ray-v3" />
            <id>https://myray.app/mixing-up-our-styling-recipe-for-ray-v3</id>
            <author>
                <name><![CDATA[Sébastien]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When it comes to styling, Tailwind is almost always our go-to choice, and Ray is no exception. Its utility-first approach provides a solid foundation with well-structured design tokens, consistent class names, and an enjoyable developer experience. It's like butter in a cake, it’s essential to making our styling come together!</p>
<p>But, like any good cake, it needs some finishing touches to really shine! Simply adding Tailwind doesn’t solve every challenge we face. As Ray grows in complexity, these challenges become more apparent. While Tailwind provides a solid foundation, it doesn’t directly answer questions like: How do we handle component variants? How do we override styles cleanly? Or how do we compose styles in a way that remains scalable?</p>
<p>To tackle these questions, we did what any developer would do: turn to our trusted search engine, Google! After a bit of digging around, we've found a few tools to help us extend and enhance how we use Tailwind in Ray v3. Now, we won’t leave you in suspense for long. Chances are, many teams are already using them, but just in case you haven’t, here’s what we’ve been using to give our 'cake' those finishing touches: <a href="https://cva.style/docs">CVA</a>, <a href="https://github.com/lukeed/clsx">clsx</a>, <a href="https://github.com/dcastil/tailwind-merge">tailwind-merge</a>, and <a href="https://github.com/tailwindlabs/prettier-plugin-tailwindcss">prettier-plugin-tailwindcss</a>.</p>
<h2><a href="https://github.com/tailwindlabs/prettier-plugin-tailwindcss">prettier-plugin-tailwindcss</a></h2>
<p>So ... about that cake! Let’s start by getting our layers perfectly aligned. For that, we use <strong>prettier-plugin-tailwindcss</strong>, a handy plugin that automatically sorts classes according to Tailwind's recommended order. If you're interested, Jonathan Reinink and Adam Wathan wrote a great blog post about it a few years ago, <a href="https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted">take a look</a>! It's quite simple to integrate, go to your <code>.prettierrc</code> file and add it as one of the plugins:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">plugins</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">prettier-plugin-tailwindcss</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>To finish things off, we want the plugin to work with our other tools like <strong>clsx</strong> and <strong>cva</strong> as well. If we want to order the classnames inside strings provided to function calls, we need to define what these functions are. Go to the <code>options</code> key in your <code>.prettierrc</code> and add them:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">tailwindFunctions</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">clsx</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">cva</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #ECEFF4">],</span></span>
<span class="line"></span></code></pre>
<h2><a href="https://github.com/dcastil/tailwind-merge">tailwind-merge</a></h2>
<p>Back to our cake, we want to ensure our cake has no clashing flavors. Optionally, but a nice addition, <strong>tailwind-merge</strong> helps resolve conflicts when combining Tailwind CSS classes. For example:</p>
<p>For example, let’s say you want to apply both <code>bg-blue-500</code> and <code>bg-red-500</code> to the same element. Without <strong>tailwind-merge</strong>, Tailwind would apply both, and the class that comes last in the CSS order would overwrite the other. However, with <strong>tailwind-merge</strong>, conflicting classes are automatically merged, ensuring that only the correct class is applied.</p>
<p>While you don’t need to use this on every class, it can be especially useful when creating one-off components. For instance, if you need a component with a thicker border than usual, <strong>tailwind-merge</strong> ensures that custom classes override the default ones without causing conflicts. The developer behind it has written some clear <a href="https://github.com/dcastil/tailwind-merge/blob/v2.5.5/docs/when-and-how-to-use-it.md">documentation</a>, which I recommend reading before implementing it.</p>
<p>We did encounter a small issue where custom color and font size classes conflicted. When applying a font size class, it would sometimes remove the color. This issue occurred because we customize our color and font size values in the Tailwind configuration. To fix this, we needed to extend <strong>tailwind-merge</strong> with our custom settings. Here's how you can do it:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">twMerge</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">extendTailwindMerge</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">extend</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">classGroups</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-size</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-10</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-11</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-12</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-14</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-16</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-20</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-24</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-36</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Because we use numbers to identify our font sizes in our classes, we can alternatively use a regular expression as well:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">twMerge</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">extendTailwindMerge</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">extend</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">classGroups</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-size</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">string</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Array</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">isArray</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">match</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">/</span><span style="color: #81A1C1">^</span><span style="color: #EBCB8B">\d</span><span style="color: #81A1C1">+$</span><span style="color: #ECEFF4">/</span><span style="color: #D8DEE9FF">))]</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<h2><a href="https://github.com/lukeed/clsx">clsx</a></h2>
<p>You must be thinking &quot;don't start with the cake again!&quot; Well, we don't want to disappoint! So our secret ingredient, making sure every piece of the cake fits perfectly together: <strong>clsx</strong>. A tiny and fast utility for conditionally constructing class name strings. In Ray, we use it everywhere to manage class names that need to change based on certain conditions.</p>
<p>A simple example of conditionally appending classes looks like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #88C0D0">clsx</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">bg-red-500 transition-opacity</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">isOpen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">opacity-0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">isOpen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">opacity-100</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">}&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span></code></pre>
<p>For better readability, we often prefer a more object-based approach:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #88C0D0">clsx</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">bg-red-500 transition-opacity</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">opacity-0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">isOpen</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">opacity-100</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">isOpen</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span></code></pre>
<p>When dealing with components that have a lot of Tailwind classes, we like to group them into logical chunks for better organization:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #88C0D0">clsx</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">flex items-center justify-between gap-4</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">bg-red-500 text-white</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">hover:bg-red-700</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	)</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span></code></pre>
<p>The best part about <strong>clsx</strong> is that it allows you to combine all of these approaches! Whether you’re conditionally appending, using objects, or grouping classes, it makes managing class names on components much simpler and more readable.</p>
<h2><a href="https://cva.style/docs">cva</a></h2>
<p>Okay, one more time before finishing our cake story: Just like that perfect layer of frosting, <strong>cva</strong> gives us a readable way to manage component variants in a clean and scalable manner. This is particularly helpful for foundational components like buttons, banners, and others, which often have many variants. We typically don’t use <strong>cva</strong> for larger components, as they tend to have a more fixed structure. However, we’re not ruling it out entirely, edge cases can always arise. As Ray grows in complexity, we’ve realized that handling multiple variants for components can get tricky. Simply relying on <strong>clsx</strong> doesn't always provide the solid, readable structure we need to keep things maintainable.</p>
<p>A very simple example is applying <strong>cva</strong> to our Banner component:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bannerVariants</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">cva</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">flex bg-gradient-to-r px-4 py-2</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">variants</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">primary</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">from-banner-primary-gradient-start to-banner-primary-gradient-end</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">from-banner-secondary-gradient-start to-banner-secondary-gradient-end</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">md</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">px-4 py-2</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">sm</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">px-3 py-1</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defaultVariants</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">primary</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">   </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">md</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Another way we have used <strong>cva</strong> is to group text styles together to create groups of classes that define how a certain type of text should look like. This creates a more consistent way to apply and manage our font styles.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">textVariants</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">cva</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">variants</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">h1</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-sans text-20 font-bold leading-120</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">h2</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-sans text-16 font-semibold leading-120</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-sans leading-150</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">lg</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">default</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">sm</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">default</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">medium</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">semibold</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">compoundVariants</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">lg</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-16</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">default</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-14</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">sm</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">text-12</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">default</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-medium</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">semibold</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">font-semibold</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    ]</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defaultVariants</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">body</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">default</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">default</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<h2>Putting It All Together</h2>
<p>All of the tools mentioned above can be seamlessly combined to manage our Tailwind classes and variants:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Banner</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">className</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #88C0D0">twMerge</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">clsx</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">bannerVariants</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">variant</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">className</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">}&gt;</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/div&gt;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>By mixing <strong>cva</strong>, <strong>clsx</strong>, <strong>tailwind-merge</strong>, and <strong>prettier-plugin-tailwindcss</strong>, we’ve baked a pretty solid styling toolkit for Ray. Together, they let us tackle component variants, keep our classes tidy, and make styling as smooth as possible, no frosting left behind! And with that, our cake is finally finished. Who knows, maybe for the next project, we’ll bake up something new!</p>
]]>
            </summary>
                                    <updated>2024-12-06T14:46:13+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[IPC in Electron]]></title>
            <link rel="alternate" href="https://myray.app/blog/ipc-in-electron" />
            <id>https://myray.app/ipc-in-electron</id>
            <author>
                <name><![CDATA[Sébastien]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>As you may know, Ray is built with React and Electron, a stack we're really happy with! For version 3.0, we’re sticking with this stack, but we’ve completely overhauled how the renderer process and main process communicate within our Electron app. Consider this the so called 'bridge' between the two processes.</p>
<p>If you've ever built an Electron app, you've probably found yourself scouring the <a href="https://www.electronjs.org/docs/latest/tutorial/ipc">Inter-Process Communication</a> documentation. But don’t worry, this isn’t just a copy-paste from the docs though, believe us, that would have saved some time here.</p>
<p>Instead, this post dives into how we specifically implemented IPC for Ray 3.0. While this approach isn’t necessarily the way, it's a way that works for our team and makes the complex feel simple. Or at least, that's what we try to do here.</p>
<h2>What is IPC?</h2>
<p>Let’s quickly cover Inter-Process Communication (IPC) for anyone who might need a refresher.</p>
<p>In an Electron app, IPC is what allows the <strong>main process</strong> and the <strong>renderer process</strong> to talk to each other. These two processes are separate, and they each have different roles: the main process controls the application itself (handling things like file access, menus, and creating windows), while the renderer process is responsible for rendering the app’s user interface, typically using HTML, CSS, and JavaScript. In our case, that's where the React part comes in!</p>
<p>But since they’re isolated from each other, they can’t share information directly, this is where IPC comes in to save the day! Electron’s IPC system enables these processes to send messages back and forth, making it possible for the renderer process to ask the main process to do things (like open a file or read from the filesystem) and vice versa.</p>
<p>IPC works through Electron’s <code>ipcMain</code> and <code>ipcRenderer</code> modules, which act like a messaging bridge. When the renderer process needs data or wants to trigger an action in the main process, it can send a message to <code>ipcMain</code>. And when the main process has something to tell the renderer—like notifying it of new data—it can send messages to <code>ipcRenderer</code>.</p>
<p><strong>In short, IPC is the bridge that lets the main and renderer processes stay connected, each doing their job to keep the app responsive and functional.</strong></p>
<h2>Ray's implementation</h2>
<p>Following the Electron docs, we’ve implemented our IPC setup in the <strong>preload file</strong>. The preload script runs in a secure, isolated context, giving us a safe way to expose only the necessary API methods to the renderer process while keeping sensitive operations in the main process.</p>
<p>Our first implementation of the bridge was quite static:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9">contextBridge</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">exposeInMainWorld</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">electron</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">openSettingsWindow</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">tab</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">open-settings-window</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tab</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">openExternalBrowser</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">url</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">open-external-browser</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">url</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">getAppVersion</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">invoke</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">get-app-version</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">renderLogs</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">logs</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">on</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">render-logs</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logs</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>The code above shows a small part of our initial setup. Over time, however, the preload file grew as we repeatedly had to add new functions, communication methods, and unique event names. It quickly became cumbersome and felt like we were duplicating similar functions with only minor variations. Worse, it was hard to tell from the function names alone whether communication was going from the main process to the renderer or vice versa.</p>
<p>While researching secure practices for Electron apps, I came across this <a href="https://github.com/reZach/secure-electron-template/issues/43">GitHub Issue log</a>, where developers were discussing how to remove listeners from the <code>ipcRenderer</code> channel. In one comment, a developer shared <a href="https://github.com/reZach/secure-electron-template/issues/43#issuecomment-772303787">their own Electron bridge implementation</a> that was dynamic, simple, and clear. It was exactly what we needed!</p>
<p>In other words, we “took inspiration” (that’s the nice way of saying copy-paste) from their implementation, then adapted it to suit our needs. The result? A dynamic, streamlined bridge that made our IPC setup far easier to manage!</p>
<p>This is the final implementation of the bridge:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9">contextBridge</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">exposeInMainWorld</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">electron</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">invoke</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Object</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">values</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">twoWayEvents</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">invoke</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">send</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Object</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">values</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rendererToMainEvents</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">receive</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Object</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">values</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">mainToRendererEvents</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">subscription</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">args</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">callback</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">args</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">on</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">subscription</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">subscription</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">receiveOnce</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Object</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">values</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">mainToRendererEvents</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">once</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">args</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">callback</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">args</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">removeAllListeners</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">channel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Object</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">values</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">mainToRendererEvents</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcRenderer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeAllListeners</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">channel</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>As you can see, this approach creates a much more dynamic way for the two processes to communicate. We no longer have to write separate functions for each event, and the function names alone make it clear how communication flows between processes. You might recognize this pattern as an event emitter, which is essentially the foundation of inter-process communication.</p>
<h2>Using the bridge</h2>
<p>There are three main functions widely used throughout Ray's renderer process: <code>invoke</code>, <code>send</code>, and <code>receive</code>. Each is designed to integrate smoothly with React’s lifecycle and includes an extra layer of security, as shown in the Github issue log as well. Events are only sent or received if they belong to a whitelisted array of approved events, if an event isn’t on that list, it simply won’t execute. In the main process, we need something else to communicate with the renderer process through the bridge: some sort of EventEmitter.</p>
<h3>Main process</h3>
<p>In the main process, we use Electron's <code>ipcMain</code> module to handle communication with the renderer process. The Electron bridge in the preload file is primarily there to enable the renderer to send messages to the main process. For communication in the opposite direction, main to renderer, we created a class called <code>EventEmitter</code>. This class leverages the <code>ipcMain</code> module to listen for and respond to events.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">EventEmitter</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Singleton</span><span style="color: #D8DEE9FF">(  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">on</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcMain</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">on</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">off</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcMain</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handle</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcMain</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">handle</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">callback</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">offHandle</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">ipcMain</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeHandler</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">emit</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">browserWindow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">browserWindow</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">emit</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">emitAll</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">browserWindows</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">browserWindows</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">forEach</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">([</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">browserWindow</span><span style="color: #ECEFF4">])</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">browserWindow</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">emit</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">params</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>If the event names are whitelisted, these methods enable the main process to communicate with the renderer process, allowing both sending and receiving of events. You can even specify which window should receive the event!</p>
<h3>Renderer process</h3>
<p>In the renderer process, it's pretty straightforward on how to use the methods provided the the electron bridge, but you have to take into account React's life cycle.</p>
<p>The <code>send</code> method enables one-way communication from the renderer process to the main process. The main process responds to the event using either <code>ipcMain.on</code> or our custom <code>EventEmitter.on</code>. It’s the simplest method to use and can be implemented anywhere:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Renderer process</span></span>
<span class="line"><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">electron</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Main process</span></span>
<span class="line"><span style="color: #D8DEE9">EventEmitter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">on</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Do something</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>The <code>invoke</code> method is similar to <code>send</code>, with the key difference being that it supports two-way communication. This means it waits for a response from the main process. When using <code>invoke</code>, it must be called inside an <code>async</code> method, and it will return the response from the main process. The main process responds to the event using either <code>ipcMain.handle</code> or our custom <code>EventEmitter.handle</code>.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Renderer process</span></span>
<span class="line"><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">doSomething</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">response</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">electron</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">invoke</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #88C0D0">doSomethingWithResponse</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">response</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Main process</span></span>
<span class="line"><span style="color: #D8DEE9">EventEmitter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">handle</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">responseFromMain</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Finally, the <code>receive</code> method. This is called within the <code>useEffect</code> hook of the React lifecycle. The tricky part is that it also needs to be cleaned up when the React component is destroyed. In the Electron bridge, the <code>receive</code> function returns a cleanup function that removes the listener, which corresponds to the destroy function in the <code>useEffect</code> hook. It’s essential to always return the <code>window.electron.receive</code> method when using it; otherwise, it won’t be properly removed, potentially leading to performance issues.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Renderer process</span></span>
<span class="line"><span style="color: #88C0D0">useEffect</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">electron</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">receive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">	    </span><span style="color: #616E88">// do something with value</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> [</span><span style="color: #D8DEE9">dependencies</span><span style="color: #D8DEE9FF">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// Main process</span></span>
<span class="line"><span style="color: #616E88">// Send event to all open windows</span></span>
<span class="line"><span style="color: #D8DEE9">EventEmitter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">emitAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">browserWindows</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #616E88">// Send event to one specific window</span></span>
<span class="line"><span style="color: #D8DEE9">EventEmitter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">emit</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">browserWindow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>And that’s a wrap! This is how we handle communication between the renderer and main processes in Ray v3.0. As you can see, we didn’t reinvent the wheel—we did some research, discovered a clear and effective method shared by others, and used it as a foundation. From there, we customized it to fit our specific needs and workflow.</p>
<p>By leveraging these techniques, we were able to create a more dynamic, secure, and efficient system for IPC in Ray. Hopefully, this gives you some useful insights into how we approached this challenge, and maybe even inspires your own solutions!</p>
]]>
            </summary>
                                    <updated>2024-11-14T08:47:17+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Using Ray with your Pest tests]]></title>
            <link rel="alternate" href="https://myray.app/blog/using-ray-with-your-pest-tests" />
            <id>https://myray.app/using-ray-with-your-pest-tests</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Writing and debugging tests are essential for many <a href="https://laravel.com/">Laravel</a> developers. <strong>Ray</strong> and <strong><a href="https://pestphp.com/">Pest</a></strong> work brilliantly together to provide a streamlined debugging and testing experience. Ray is great for real-time feedback on code execution, while Pest, known for its clean and expressive syntax, simplifies writing and running tests. Together, they bring clarity and efficiency to writing tests in Laravel.</p>
<h2>Open a new window when running tests</h2>
<p>When running Pest tests, opening a new Ray window lets you keep test output separate from other Ray debugging data. This approach is especially helpful if you're actively developing while testing, as it keeps test debugging clear and distinct.</p>
<p>You can use Ray's newScreen() method to open a new Ray window. Here's how to apply it in your Pest test setup (Pest.php):</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">uses</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">beforeEach</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Tests</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This command opens a fresh Ray window each time a test run begins, letting you focus on output from your Pest tests without mixing in any debugging data from your main app logic.</p>
<h2>Using Pest's Ray method</h2>
<p>Pest has a Ray method that you can call on any expectation so you can inspect them.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">expect</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// Sends $value to Ray for inspection</span></span>
<span class="line"></span></code></pre>
<h2>Checking for Ray statements</h2>
<p>Most of the time, you don't want to ship code that still has Ray calls in it. One easy way to avoid this is to leverage Pest's <a href="https://pestphp.com/docs/arch-testing">architecture tests</a>.</p>
<p>Using the <a href="https://pestphp.com/docs/arch-testing#content-php">PHP preset</a> Pest will show a failing test each time it detects a Ray statement still in your code.</p>
<p>To enable this preset, you simply have to add the following code to your Pest.php file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">arch</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">preset</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">php</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This is only one possible way to prevent Ray calls from slipping into your production code. You can also check <a href="https://myray.app/blog/automatically-remove-ray-calls-in-your-code">our blogpost on how to automatically remove calls with Rector</a>.</p>
]]>
            </summary>
                                    <updated>2024-11-18T10:34:56+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Our mysterious control components]]></title>
            <link rel="alternate" href="https://myray.app/blog/our-mysterious-control-components" />
            <id>https://myray.app/our-mysterious-control-components</id>
            <author>
                <name><![CDATA[Sébastien]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Ray version 3.0 is being built from the ground up, with a larger focus on user experience and enhanced performance and maintainability of the application. This also means that we've explored ideas and applied patterns to the way we build the components and interfaces in React. Sidenote: this is a shameless, but fancy way of saying we've refactored some code! Anyway, although they are quite opinionated and tailored to our preferred working style, we want to share them with you!</p>
<p>So drumroll please, or try to imagine a drumroll… In Ray version 3.0, but also in other projects for clients, we like to apply the concept of &quot;<strong>control components</strong>&quot; in React.</p>
<h2>What is this magical pattern?</h2>
<p>We all know that the best pattern is the one that works… until someone else tries to maintain it! Therefore, this pattern is not set in stone, and the term might be used elsewhere to describe something completely different. In our case, control components are components that don’t really have an interface—aside from the fact that they are React components, which allows them to benefit from React’s reactivity and lifecycle features. Their main purpose is to <strong>collect data</strong> and respond to changes through events or an API. Afterward, they <strong>distribute</strong> the collected data and ensure it’s available throughout the React application in some form. These components are used at the <strong>top level</strong> of the React application and typically don’t have any children to avoid unnecessary re-renders. In most cases, they are initialized and <strong>used only once</strong>.</p>
<h2>For example</h2>
<p>In Ray, we make use of Electron, which operates through two different processes. I’ll keep this brief, as it’s not the main focus of this post: the <strong>main process</strong> runs in a Node.js environment, and its primary purpose is to create and manage application windows, while the <strong>renderer process</strong> is responsible for rendering the web content.</p>
<p>Ray also uses <strong>electron-store</strong>, which ensures that data can persist on your local machine. This electron-store resides inside the main process. However, there are times when we need to access those values in the renderer process, or when we need to trigger an action in the renderer process that affects something in the main process.</p>
<p>This brings us to the challenge of communication between the main and renderer processes. Electron handles this through <strong>inter-process communication</strong> (IPC). But the question is: where do we manage this &quot;communication&quot; within our renderer process? This is where our &quot;<strong>control components</strong>&quot; come into play.</p>
<p>The communication between the processes consists of <strong>listeners</strong> and <strong>emitters</strong>. For example, let’s take a look at our <code>LicenseControl</code> component in Ray:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">LicenseControl</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">getStoreValue</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useStoreValue</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setHasActiveLicense</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useSetAtom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">setHasActiveLicenseAtom</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setAmountOfLogRequests</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useSetAtom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">setAmountOfLogRequestsAtom</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">useEffect</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">getStoreValue</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">boolean</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">license</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">hasActiveLicense</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">result</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">setHasActiveLicense</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">getStoreValue</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">number</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">license</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">amountOfLogRequests</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">then</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">result</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">setAmountOfLogRequests</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> [])</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">useReceiveEvent</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">set-has-active-license</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">hasActiveLicense</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">boolean</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setHasActiveLicense</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">hasActiveLicense</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">useReceiveEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">number</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">set-amount-of-log-requests</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">amountOfLogRequests</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">number</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setAmountOfLogRequests</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">amountOfLogRequests</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;&gt;&lt;/&gt;;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p><strong>Small sidenote for context:</strong> We use <strong>Jotai</strong> as our &quot;store&quot; to access and update values across all components in our application.</p>
<p>Now, let’s dissect this control component:</p>
<ul>
<li>It returns a React <strong>Fragment</strong> (or <code>null</code>), indicating that it has no user interface.</li>
<li>It contains a <code>useEffect</code> hook that initializes values as soon as the component is mounted and saves them in our Jotai store, making them accessible from any component.</li>
<li>It listens for events coming from the main process and executes a callback when an event is triggered. This syncs values from the main process to our React stores, ensuring the data can be accessed by any component.</li>
</ul>
<p>In other words, all this component does is sync values and handle changes between the main process and the renderer process. So why do we call this a &quot;control component&quot;?</p>
<ul>
<li>The code is centralized, so it avoids scattering logic across multiple components, establishing a single source of truth.</li>
<li>Events that originate from various parts of the application and trigger actions elsewhere are consolidated in one place.</li>
<li>It prevents unnecessary re-renders in underlying components, as it has no children.</li>
<li>The data collected by this component is distributed throughout the React application and can be accessed universally.</li>
</ul>
<h2>Other projects</h2>
<p>Aside from Ray, we’ve also applied this pattern in other projects, where we’ve created components to handle keyboard or mouse controls. Let’s quickly take a look at the mouse control component:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">MouseControl</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setMousePosition</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useMouseContext</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handlePointerDown</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientX</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientY</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MouseEvent</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">   </span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">setMousePosition</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">clientX</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handlePointerMove</span><span style="color: #ECEFF4">({</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientX</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientY</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MouseEvent</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMousePosition</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">clientX</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clientY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">useEffect</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">addEventListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">pointermove</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handlePointerMove</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">addEventListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">pointerdown</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handlePointerDown</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeEventListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">pointermove</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handlePointerMove</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">window</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">removeEventListener</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">pointerdown</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handlePointerDown</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> [])</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;&gt;&lt;/&gt;;</span><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>The mouse events are collected in one place. The component react to the events and passes on the data collected to the store. In its turn, it can be accessed by any component in the React application. Each event has one source to distribute the data.</p>
<p>The mouse events are collected in one place. The component reacts to these events and passes the collected data to the store, where it can be accessed by any component in the React application. Each event has a single source responsible for distributing the data.</p>
<p>Another example is when we used a gesture library called <a href="https://use-gesture.netlify.app/">@use-gesture</a> to enable panning or zooming on a canvas. We separated the <code>use-gesture</code> hooks into their own control component. This control component collects gesture data and sends it to a store, making it accessible to any component across the React application. This was a necessary abstraction to ensure the entire interface responds to changes triggered by gestures.</p>
<h2>Conclusion</h2>
<p>To summarize what a control component does: it’s a component without an interface that collects data from various sources, responds to events, and distributes the data so it’s accessible throughout the application. While we prefer using a store for this, it can also be done using Context Providers or other methods, depending on your approach.</p>
<p>Keep in mind, this is simply an opinionated concept that we at Spatie like to use in the front-end for these specific use cases. It’s by no means &quot;the right way&quot; to structure your code. Every team or developer finds their own approach to organizing their codebases. After all, every developer knows that &quot;the right way&quot; is just another way of saying &quot;The way I did it last time&quot;.</p>
]]>
            </summary>
                                    <updated>2024-10-07T14:46:10+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Ray's architecture: how we structure an Electron app]]></title>
            <link rel="alternate" href="https://myray.app/blog/ray-architecture" />
            <id>https://myray.app/ray-architecture</id>
            <author>
                <name><![CDATA[Sébastien]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>At this year's Laracon US, Freek took the stage to share some exciting updates from the Spatie team: we are hard at work on Ray 3.0! This new version promises a complete visual overhaul, enhancing both the design and overall user experience.</p>
<p>In addition to making Ray look better, we are focused on improving performance, addressing existing issues and rewriting key parts of the codebase to ensure Ray is more scalable and maintainable in the long run. In other words: our front-end development team is sipping on extra coffee.</p>
<p>A key aspect of enhancing the application's manageability is rethinking its architecture. We'll dive into how we approach this to ensure long-term stability and scalability.</p>
<h2>Technology</h2>
<p>To start things off, Ray is build with React and Electron. We chose Electron because it allows us to maintain a single codebase while delivering the application across multiple operating systems. Like any technology, it comes with its own set of challenges and trade-offs, but this gives us a chance to refine areas that need improvement.</p>
<h2>Architecture</h2>
<p>For version 3.0 we've decided to re-evaluate the source folder architecture of the application. We want to make it easier to locate resources and better organize tasks and responsibilities. It’s tailored to our team’s needs and reflects our specific preferences and requirements for this project.</p>
<p>The main architecture is divided into 5 source folders, of which we will explore the two most important ones: main and renderer.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-v3-architecture/ray-main-architecture.png" alt="" /></p>
<p>But first let's provide some context on what these contain:</p>
<ul>
<li><strong>Main</strong>: This refers to the Electron main process, which serves as the application entry point and runs in Node.js.</li>
<li><strong>Renderer</strong>: This represents the Electron renderer process, responsible for rendering the web content.</li>
<li><strong>Preload</strong>: This folder includes code that executes before the renderer process starts loading web content. It enhances the renderer by exposing APIs that enable communication between the main and renderer processes.</li>
<li><strong>Resources</strong>: This directory holds assets such as icons, images, fonts, as well as certificates and app icons needed for the build process.</li>
<li><strong>Shared</strong>: This contains context-agnostic code used by both the main and renderer processes, including types, helpers, utilities, and classes.</li>
</ul>
<h2>Modules</h2>
<p>The modules folder acts as a centralized hub for managing functionalities within a project. It organizes self-contained units of features, with each module encapsulating its own logic, types, components, and classes. In our opinion, its structure promotes separation of concerns, enhances scalability, and facilitates grouping related items together for better organization.</p>
<p>Here is a simple example on how modules are handled in the main process (<strong>license</strong>) and the renderer process (<strong>settings</strong>):</p>
<p><img src="https://content.spatie.be/assets/ray/ray-v3-architecture/ray-modules-architecture.png" alt="" /></p>
<p>Each of these &quot;modules&quot; have their own specific responsibilities and tasks. These modules are being combined in pages and communicate with each other through for example, stores.</p>
<p>Outside of the modules folder, there are additional directories such as components, stores, and hooks that house more general items not tied to any specific context. These folders contain shared resources that are used across various modules, ensuring consistency and reusability throughout the application.</p>
<p>The key aspect is that everything related to a module is neatly organized within their respective module. So if a bug pops up in, for example, the settings window, then there’s no need to hit the panic button. We’ve organized the folder structure so our team can navigate it with a calm composure, no need for coffee-fueled frenzies! Although, we still have to test out that last one.</p>
<h2>Structures</h2>
<p>The structures folder is where we keep core elements that underpin the application's architecture.</p>
<ul>
<li><strong>Classes</strong>: Core classes that define the fundamental building blocks of our application. These can extend classes from other libraries in order to customize them a bit more towards the needs of the application.</li>
<li><strong>Types</strong>: Type definitions that ensure consistency and type safety across the codebase.</li>
<li><strong>Factories</strong>: Functions or classes designed to create and manage instances, such as a factory that accepts a class and returns a singleton. These factories streamline object creation and ensure that we maintain control over instance management.</li>
</ul>
<p><img src="https://content.spatie.be/assets/ray/ray-v3-architecture/ray-structures-architecture.png" alt="" /></p>
<h2>Components</h2>
<p>Building components can be approached in several ways, but our team prefers a method that divides a component into multiple files, each with a distinct responsibility.</p>
<p>This approach ensures that a component is divided into manageable parts, allowing each part to handle its specific responsibilities and communicate through React context or by passing props. By clearly naming these different parts, we enhance readability and make the component structure more intuitive for our team.</p>
<p>For illustration, let's consider a Collapse component. Note that not every component is structured this way, as it could be overkill for simpler cases. We aim for readability and consistency. Here's how we can organize a complex and flexible component:</p>
<p><img src="https://content.spatie.be/assets/ray/ray-v3-architecture/ray-collapse-architecture.png" alt="" /></p>
<ul>
<li><strong><code>index.ts</code></strong>: Decide what is being exported to the outside world.</li>
</ul>
<ul>
<li><strong><code>Collapse.tsx</code></strong>: The main entry point of the component.</li>
<li><strong><code>CollapseHeader.tsx</code></strong>: A subcomponent responsible for rendering the header section of the collapse component.</li>
<li><strong><code>CollapseContent.tsx</code></strong>: A subcomponent that handles the content area of the collapse component.</li>
<li><strong><code>Collapse.context.ts</code></strong>: This approach enables effective communication between parts of a component, especially in a compositional component setup, if applicable.</li>
<li><strong><code>Collapse.types.ts</code></strong>: Defines the types and interfaces used by the <code>Collapse</code> component.</li>
<li><strong><code>Collapse.cva.ts</code></strong>: Contains Tailwind classes for creating variant-based components using the <a href="%5Bhttps://cva.style/docs%5D(https://cva.style/docs)">CVA</a> library.</li>
</ul>
<p>This modular approach helps keep each part of the component organized and focused on its specific role, making the codebase easier to navigate and maintain.</p>
<h2>Stores</h2>
<p>Working with React gives us numerous options for state management, but having many choices isn’t always a good thing. It’s like every month there’s a new store library popping up, and choosing one can feel like trying to pick your favorite food. Our decision is based on the specific needs of the project and what we like to work with.</p>
<p>So for Ray, we've decided to go with <a href="https://jotai.org/">Jotai</a>. Since Ray doesn’t require complex state handling, Jotai’s simplicity and easy-to-learn API fit perfectly. We’ve used it in a previous project and appreciated its developer-friendly experience. It also seems to scale well with growing applications. Think of it as using <code>useState</code>, but outside of a component.</p>
<p>Jotai doesn't have one way to do things, it is not opinionated and you kind of have to find your own of working with it. If you want to get familiar with it, check out its <a href="https://jotai.org/">documentation</a>.</p>
<p>Here's how we can organize a store, let's consider a logs store containing all the logs that are send to the Ray application:</p>
<p><img src="https://content.spatie.be/assets/ray/ray-v3-architecture/ray-store-architecture.png" alt="" /></p>
<p>We structure our stores as follows:</p>
<ul>
<li><strong><code>index.ts</code></strong>: The entry point of the store, where we export what should be accessible externally.</li>
<li><strong><code>logs.ts</code></strong>: Contains the main state, including read-only and derived atoms.</li>
<li><strong><code>logs.actions.ts</code></strong>: Houses action atoms, which are primarily setters that modify the read-only atoms in the state.</li>
<li><strong><code>logs.types.ts</code></strong> (optional): Defines specific types for the store, if needed.</li>
<li><strong><code>logs.helpers.ts</code></strong> (optional): Includes helper functions for the store, mainly used in actions, if necessary.</li>
</ul>
<h2>Ray 3.0</h2>
<p>As of now, we're working on Ray version 3.0, and all these architectural decisions are made early in the project. Of course, these choices may evolve as the project progresses. We’re continuously evaluating how to implement features to optimize user experience, but our goal is to maintain consistency and build a more readable and manageable codebase.</p>
<p>Fingers crossed everything goes smoothly. We might need a few extra cups of coffee, but rest assured, we’re working hard to get Ray version 3.0 into your hands!</p>
]]>
            </summary>
                                    <updated>2024-09-19T12:31:11+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Using Ray inside your Blade views]]></title>
            <link rel="alternate" href="https://myray.app/blog/using-ray-inside-your-blade-views" />
            <id>https://myray.app/using-ray-inside-your-blade-views</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Ray makes it simple to debug Laravel applications, even when working with Blade views. In this blog post, we'll explore some directives you can use to leverage Ray within Blade views to streamline the debugging process.</p>
<h2>Logging Variables with @ray</h2>
<p>Debugging often starts with understanding the values of variables at different points in your code. Ray provides a convenient <code>@ray</code> directive that allows you to log variables directly from your Blade views. This directive can handle multiple variables simultaneously, making it a powerful tool for inspecting your data.</p>
<p>Here's how you can use the <code>@ray</code> directive inside a Blade view:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">{{</span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9FF"> inside a view </span><span style="color: #81A1C1">--</span><span style="color: #ECEFF4">}}</span></span>
<span class="line"><span style="color: #81A1C1">@</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">variable</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">anotherVariable</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span></code></pre>
<p>This simple line sends the values of <code>$variable</code> and <code>$anotherVariable</code> to Ray, where you can inspect them in real time. This eliminates the need for temporary print statements or other cumbersome debugging methods.</p>
<h2>Displaying All Variables with @xray</h2>
<p>Sometimes, you may want to see all the variables available in your Blade file. The <code>@xray</code> directive does precisely that. By placing <code>@xray</code> in your view, Ray will show you all the accessible variables at that point.</p>
<p>Consider this controller:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">PostController</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">show</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">Post</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">foo</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">bar</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">		returen </span><span style="color: #88C0D0">view</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">posts.show</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> compact</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">title</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">post</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">foo</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>And the directive being called in the <code>show.blade.php</code> file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">@</span><span style="color: #D8DEE9FF">xray</span></span>
<span class="line"></span></code></pre>
<p>This will result in the following output in Ray:</p>
<p><img src="https://content.spatie.be/assets/ray/using-ray-inside-your-blade-views/xray.png" alt="" /></p>
<p>This directive is particularly useful when you're unsure of what variables are being passed to your view or when you need a comprehensive overview of your data.</p>
<h2>Measuring performance with @measure</h2>
<p>Performance optimization is a crucial aspect of development, and Ray makes it easy to measure the performance of your Blade views. The <code>@measure</code> directive is a shortcut for the <code>ray()-&gt;measure()</code> method, allowing you to measure the time and memory consumption directly in your views.</p>
<p>Consider the following example:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">{{</span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9FF"> inside a view </span><span style="color: #81A1C1">--</span><span style="color: #ECEFF4">}}</span></span>
<span class="line"><span style="color: #81A1C1">@</span><span style="color: #D8DEE9FF">measure</span></span>
<span class="line"><span style="color: #81A1C1">@</span><span style="color: #88C0D0">php</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">sleep</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">4</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #81A1C1">@</span><span style="color: #D8DEE9FF">measure</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/ray/using-ray-inside-your-blade-views/measure-blade.png" alt="" /></p>
<p>In this snippet, the <code>@measure</code> directive is used before and after a <code>sleep(4)</code> statement. Ray will display the time and memory usage between these two points, providing valuable insights into the performance of your code.</p>
<p>Happy debugging!</p>
]]>
            </summary>
                                    <updated>2024-09-19T12:30:15+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Leveraging the Power of Macros in Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/leveraging-the-power-of-macros-in-ray" />
            <id>https://myray.app/leveraging-the-power-of-macros-in-ray</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>One of Ray's powerful features you might not have heard of is adding your own logic to it via <strong>macros</strong>. Macros in Ray allow you to define custom methods that can be reused throughout your application, making debugging more efficient and tailored to your needs.</p>
<p>Let’s take a simple example of a macro that adds an uppercase method, which will uppercase the value that is passed to it:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #8FBCBB">Ray</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">macro</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">uppercase</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">uppercasedValue</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strtoupper</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">send</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">uppercasedValue</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$this;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>You can use it like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">uppercase</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">abc</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// Outputs in Ray: ABC</span></span>
<span class="line"></span></code></pre>
<p>This basic example shows how you can extend Ray's functionality with just a few lines of code. The uppercase macro takes a string, converts it to uppercase, and sends the result to Ray. It's a neat trick that can be useful for quick text transformations.</p>
<p>Ok, that’s very cute, but let’s try something more practical: Imagine you often need to debug Orders and OrderLines in your application. Manually formatting this data every time can be tedious and error-prone. This is where macros truly shine, allowing you to encapsulate complex logic in a reusable way.</p>
<p>Below, we created a macro that receives an object of class Order and formats it so it can be used with the <a href="https://myray.app/docs/php/vanilla-php/customizing-output#displaying-a-table">table</a> method Ray offers.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #8FBCBB">Ray</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">macro</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">order</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">Order</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">tableData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Customer</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">customer</span><span style="color: #ECEFF4">(),</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Total</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">€</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">.</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">total</span><span style="color: #ECEFF4">(),</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Orderlines:</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">orderLineTableData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">orderLines</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">mapWithKeys</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">OrderLine</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">line</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C"> - </span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">.</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">line</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">product</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">.</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> (x </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">line</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">quantity</span><span style="color: #A3BE8C">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">€</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">line</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">total</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toArray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">tableData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">array_merge</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">tableData</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">orderLineTableData</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">table</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">tableData</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Order  #</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">value</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">orderNumber</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$this;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"></span></code></pre>
<p>This would allow you to use it like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">order</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">order</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Which results in the following output in Ray:</p>
<img class="mb-4" src="https://content.spatie.be/assets/ray/leveraging-the-power-of-macros-in-ray/ray-macros.png" alt="results in Ray" width="420" />
<br>
<p>This is just one example of how you could leverage macros in Ray, but we are sure Ray users can think of countless other great use cases for them.</p>
<p>You can read more about macros in the documentation: <a href="https://myray.app/docs/php/vanilla-php/custom">Adding Your Own Ray Functions in PHP</a>.</p>
]]>
            </summary>
                                    <updated>2024-09-19T12:21:00+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Handling email testing in Laravel with Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/handling-email-testing-in-laravel-with-ray" />
            <id>https://myray.app/handling-email-testing-in-laravel-with-ray</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Emails are an important aspect of many web applications, and making sure that emails are sent correctly and contain the expected content is essential. Laravel provides convenient tools for sending emails, but testing them effectively can sometimes be challenging. In this article, we'll look into how to use Ray for testing email in Laravel applications.</p>
<p>One of the way to test your email flow might be to use third party tools designed specifically for local email testing.</p>
<p>But there is yet another way - using tools you already have to test emails in Laravel.</p>
<p><a href="https://spatie.be/products/ray">Ray</a> is a debugging tool that provides real-time insights into your application's execution flow, and once installed, it pretty much just works. But did you know Ray can also be used to test emails?</p>
<p>Ray reads the content of the log file and displays it in the Ray window. In order to use Ray to test emails in Laravel, make sure you choose <code>log</code> as your mailer log in your <code>.env</code> file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">MAIL_MAILER</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">log</span></span>
<span class="line"></span></code></pre>
<p>Let's say we are sending a giveaway entry confirmation to a user. Once we have our mailable created and configured, we will set up a simple test url that will trigger the email. We will also pass in a name variable and expect the value to show in the email:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #8FBCBB">Route</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">/giveaway-entered</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">name</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Zuzana</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">Mail</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">to</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">zuzana@example.com</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">send</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">GiveawayEntered</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This is what our email view file might look like:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">h1</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">Hello</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">name</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}}</span><span style="color: #81A1C1">!&lt;/</span><span style="color: #D8DEE9FF">h1</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">You have entered the giveaway</span><span style="color: #81A1C1">!&lt;/</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">Good luck</span><span style="color: #81A1C1">!&lt;/</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">Best regards</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;br&gt;</span><span style="color: #D8DEE9FF"> The Giveaway Team</span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">p</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span></code></pre>
<p>When we access the route, Ray output will be triggered automatically and it will display our email message:</p>
<p><img src="https://content.spatie.be/assets/ray/testing-emails-in-laravel-with-ray/ray-giveaway-confirmation.png" alt="" /></p>
<p>Another use case is the password reset flow. Again, there is no need to configure Ray to capture the email - as long as the <code>MAIL_MAILER</code> is set to <code>log</code>, it will work automatically.</p>
<p>When the password reset is triggered, this is what Ray displays:</p>
<p><img src="https://content.spatie.be/assets/ray/testing-emails-in-laravel-with-ray/ray-password-reset.png" alt="" /></p>
<p>From here, you can click on this link and it will open the password reset form in the browser.</p>
<p>Using Ray to test your emails, you can view email data in real-time, which provides immediate feedback on the email's structure, content, and any variables passed into it. This makes debugging emails in Laravel a breeze.</p>
<p>Check out other Ray functions in the <a href="https://myray.app/docs/getting-started/introduction">documentation</a>.</p>
]]>
            </summary>
                                    <updated>2024-05-16T13:09:10+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Pausing and disabling Ray execution]]></title>
            <link rel="alternate" href="https://myray.app/blog/pausing-and-disabling-ray-execution" />
            <id>https://myray.app/pausing-and-disabling-ray-execution</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Let's say you have multiple Ray calls that you need to temporarily disable. You could go and comment them all out, or you could use <code>ray()-&gt;disable()</code> at the top of the file (or higher up in the call stack) to temporarily disable Ray execution.</p>
<p>You can also check if Ray has already been disabled somewhere else in your application by running <code>ray()-&gt;disabled()</code>, and you can run <code>ray()-&gt;enable()</code> and <code>ray()-&gt;enabled()</code> too.</p>
<p>Disabling and enabling Ray execution is especially useful when running tests. You don't want your Ray output to be cluttered by Ray calls when running tests, which might happen if you have Ray calls scattered throughout your application.</p>
<p>An easy way to deal with this situation is to disable Ray at the beginning of the test and only enable it during the arrange step of the test. You can learn more about this technique in <a href="https://myray.app/blog/ray-as-a-debugging-companion-in-busy-integration-tests">this blog post</a>.</p>
<p>What if you want to halt code execution altogether? There is a method for that! Run <code>ray()-&gt;pause()</code> to pause ALL code execution, and when you are ready to move forward, you can simply press the Continue button in the output.</p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-pause-2.png" alt="" /></p>
<video class="aspect-video" controls loop autoplay muted>
  <source src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-pause-2.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
]]>
            </summary>
                                    <updated>2024-05-07T11:39:31+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Clearing Ray output]]></title>
            <link rel="alternate" href="https://myray.app/blog/clearing-ray-output" />
            <id>https://myray.app/clearing-ray-output</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When using Ray to debug your application, you may introduce various Ray calls in different places. Ray keeps the history of your calls which can be useful to refer to. But what can also happen is that your Ray window will get really busy and you end up with a long list of Ray outputs.</p>
<p><img src="https://content.spatie.be/assets/ray/clearing-ray-output/ray-multiple-outputs.png" alt="" /></p>
<p>The good news is that you can clear Ray window whenever you want so that only the most recent output is displayed.</p>
<h2>On page refresh</h2>
<p>Let's consider different scenarios. What if you wanted to clear the Ray screen every time a page was refreshed in Laravel?</p>
<p>A page refresh usually happens when a new HTTP request is made to the server. This could happen when the user clicks a link, submits a form or redirects to another page.</p>
<p>If we wanted to trigger something every time a page refreshes in Laravel, one place to do that would be inside a middleware. We would create a custom middleware that would only log something on page reload.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">OnPageReload</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handle</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">Request</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">request</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Closure</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">next</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Response</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearAll</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">next</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">request</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>We can use either <code>ray()-&gt;clearAll()</code> or <code>ray()-&gt;clearScreen()</code>. <code>clearAll()</code> will clear the current and all previous screens, while <code>clearScreen()</code> will clear only the current screen.</p>
<p>When we register the new middleware and add it to the global middleware array, it will be executed on each page request and Ray window will clear every time a page is refreshed.</p>
<p>Another option might be to place the <code>ray()-&gt;clearScreen()</code> in the <code>appServiceProvider@boot</code> method.</p>
<p>Finally, you could call the <code>clearAll</code> or <code>clearScreen</code> functions  from the <code>web.php</code> and <code>api.php</code> files. By adding logging directly inside these files, you can make sure that the Ray function will be called on each page refresh that matches one of those routes.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearAll</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>All approaches achieve the same result: the Ray function will be called and the Ray window will be cleared. Which one you chose depends on you.</p>
<h2>During testing</h2>
<p>Let's consider a different scenario now - testing.</p>
<p>When testing your application's functionality, you will probably use various Ray functions or log different states of the event. You can use Ray multiple times inside one test, you can also have logs inside classes the test hits. Imagine you run the test over and over again. What can happen is that your Ray window may get a bit too busy.</p>
<p>So what can you do? You could clear screen at the beginning of each test so that you only see the output of the most recently run test. If you use PHPUnit, you could add <code>ray()-&gt;clearScreen()</code> inside the <code>setUp()</code> method on the <code>TestCase</code> class. This way, every time a test is run, it will clear the Ray window first.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">protected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">parent::</span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>Passing the <code>$this-&gt;name</code> to the <code>newScreen</code> method will show the name of the test at the top of the Ray window.</p>
<p><img src="https://content.spatie.be/assets/ray/clearing-ray-output/ray-name-of-test.png" alt="" /></p>
<p>If you are using Pest instead of PHPUnit, you can achieve the same by setting it up in the Pest.php file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">uses</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Tests</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">TestCase</span><span style="color: #81A1C1">::class</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">beforeEach</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">in</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">__DIR__</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Another use case may be when working with loops. You could clear Ray screen before each loop is run, or even in the middle of a loop.</p>
<p>Let's say we are testing a notification service and want to see what happens when a notification is delayed. We might have a couple of Ray calls inside the <code>sendNotification()</code> method to debug the service. We created a helper function <code>delayedSendNotification()</code> that simulations a delayed notification sending, and run it a few times to see how our <code>NotificationService</code> handles this delay.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">testNotificationsSentWithDifferentDelays</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NotificationService</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1000</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>We could let Ray collect logs every time the <code>delayedSendNotification()</code> function is run, or we could only display the output at the last iteration:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">testNotificationsSentWithDifferentDelays</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NotificationService</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearScreen</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">delayedSendNotification</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">notificationService</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1000</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// assert behaviour</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>This way our Ray output is not cluttered and we see only what we want to see.</p>
<p>As you can see, there are various reasons and ways you might want to clear the Ray window. When using Ray, you are in full control of what output is displayed and when.</p>
]]>
            </summary>
                                    <updated>2024-05-03T07:42:20+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Let's celebrate with Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/lets-celebrate-with-ray" />
            <id>https://myray.app/lets-celebrate-with-ray</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Ray comes with an amazing suite of methods you can use to debug your application. But there is one special method I like to use to celebrate all my efforts debugging.</p>
<p>And the method is - <code>ray()-&gt;confetti()</code>. Who doesn't like a party!</p>
<video class="aspect-video" loop autoplay muted>
  <source src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-confetti-3.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
]]>
            </summary>
                                    <updated>2024-04-04T09:50:01+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Looking deeper into code with Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/looking-deeper-into-code-with-ray" />
            <id>https://myray.app/looking-deeper-into-code-with-ray</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p><a href="https://spatie.be/products/ray">Ray</a> is a lightweight desktop app that will help you to debug your code faster. But Ray is more than just a <code>dd()</code> call in disguise.</p>
<p>An interesting method that Ray comes with is <code>ray()-&gt;count()</code> which will count how many times a piece of code is called.</p>
<p>This method can be paired with <code>ray()-&gt;caller()</code> which will tell you what code called this method.</p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-caller-count.png" alt="" /></p>
<p>You can also use Ray to display your PHP info right in the output by calling <code>ray()-&gt;phpinfo()</code>.</p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-phpinfo.png" alt="" /></p>
]]>
            </summary>
                                    <updated>2024-04-04T11:58:45+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Ray now supports Laravel 11's Context]]></title>
            <link rel="alternate" href="https://myray.app/blog/ray-now-supports-laravel-11s-context-fdeat" />
            <id>https://myray.app/ray-now-supports-laravel-11s-context-fdeat</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Laravel 11 recently introduced <a href="https://laravel.com/docs/11.x/context">a nice new feature called Context</a>. We’ve updated Ray with a convenient method to display all set context.</p>
<h2>What is Context?</h2>
<p>This feature allows you to set some values anywhere in your app as context.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Illuminate</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Support</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Facades</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">add</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">url</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">request</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">url</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">add</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">trace_id</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Str</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">uuid</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toString</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>You can easily retrieve the set values anywhere in your app.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Illuminate</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Support</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Facades</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">all</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// returns all context</span></span>
<span class="line"></span></code></pre>
<p>Now, this isn’t all that special. With the examples given above, you’d be right to think that Context is just some array-backed cache.</p>
<p>But Context's special feature is that it makes all of this information available to any job dispatched in the request where context was set.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">dispatch</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">new</span><span style="color: #88C0D0"> </span><span style="color: #8FBCBB">MyJob</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// this job will receive the context</span></span>
<span class="line"></span></code></pre>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Illuminate</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Contracts</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Queue</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">ShouldQueue</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Illuminate</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Support</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Facades</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">MyJob</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">implements</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ShouldQueue</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// returns the context that was</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// set in the request that dispatched</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// this job</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">allContent</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Context</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">all</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// do some work</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// ...</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<h2>Context in Ray</h2>
<p>Ray now has a new method, aptly named <code>context</code>, showing all set context.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">context</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// displays all context</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">context</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">key</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">key2</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// displays only the given keys</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/context.png" alt="" /></p>
<p>Context can also be invisible. You can display those values using the <code>hiddenContext</code> method.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">hiddenContext</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// displays all hidden context</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">hiddenContext</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">key</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">key2</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// displays only the given hidden keys</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/context-hidden.png" alt="" /></p>
]]>
            </summary>
                                    <updated>2024-04-03T09:55:47+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[The ingredients we used for our new Ray docs]]></title>
            <link rel="alternate" href="https://myray.app/blog/the-ingredients-we-used-for-our-new-ray-docs" />
            <id>https://myray.app/the-ingredients-we-used-for-our-new-ray-docs</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>We recently revamped our Ray documentation. The goal was to give the docs a new home on the myray.app domain and make them easier to navigate by totally overhauling their structure and content. This blog post will give an overview of some packages, technologies, and techniques we used.</p>
<h2>TALL stack</h2>
<p>The Ray website is made with the TALL stack (Tailwind CSS, Alpine.js, Laravel, and Livewire), which covers all our needs.</p>
<p>Tailwind CSS makes it fast to add styling, and Alpine.js does the same for small bits of interactivity.</p>
<p>We use Livewire for our search box (more on that below) and to make everything blazing fast with <a href="https://livewire.laravel.com/docs/wire-navigate">wire:navigate</a>.</p>
<p>When you add <code>wire:navigate</code> to a link, Livewire will intercept any clicks on it, fetch the contents of the page in the background, and replace the HTML in the browser with an updated version, which is much faster than a full page reload. This results in a really smooth and snappy experience for the user, and we highly recommend trying it out on your projects.</p>
<h2>Markdown</h2>
<p>At <a href="https://spatie.be/">Spatie</a>, we prefer to write our documentation in Markdown. Markdown is a straightforward and fast way to write text and add some basic styling (headings, bold, bullet lists, …).</p>
<p>Because of its simplicity, Markdown works really well when stored in a version control system like Git, which is important to us because it allows easy collaboration. Our docs are available in <a href="https://github.com/spatie/myray.app/tree/main/docs">a public repository</a>, and we are always happy to receive pull requests to improve them. This is a great way to get started in opensource!</p>
<h3>Adding meta-data to our Markdown files</h3>
<p>We use our <a href="https://github.com/spatie/sheets">spatie/sheets</a> package to add some extra metadata to our Markdown files using the Front Matter specification. Here's an example of what that looks like:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">---</span></span>
<span class="line"><span style="color: #D8DEE9FF">title: Using Ray With Laravel</span></span>
<span class="line"><span style="color: #D8DEE9FF">menuTitle: Installation</span></span>
<span class="line"><span style="color: #D8DEE9FF">weight: 1</span></span>
<span class="line"><span style="color: #88C0D0">---</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">If you use Laravel, this is the way.</span></span>
<span class="line"><span style="color: #D8DEE9FF">...</span></span>
<span class="line"></span></code></pre>
<p>At the top of our Markdown file, there's a section where you can add extra properties. In this example, we define the page title and the label of the menu item.</p>
<p>By adding a weight property, we can manage this item's position in the menu.</p>
<p>The sheets package allows you to retrieve all pages in a Collection:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">pages</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Sheets</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">all</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">sortBy</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">weight</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">firstPage</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">pages</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">first</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">firstPage</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// Outputs: &#39;Using Ray With Laravel&#39;</span></span>
<span class="line"></span></code></pre>
<h3>Converting Markdown to HTML</h3>
<p>While the Sheets package can convert your Markdown to HTML, we decided to also use our <a href="https://spatie.be/docs/laravel-markdown/v1/introduction">spatie/laravel-markdown</a> package. This package leverages <a href="https://commonmark.thephpleague.com/">Commonmark</a> and adds some nice features like adding anchors to titles and creating a sub-navigation by listing all headers that are the page.</p>
<p>The package also offers code highlighting with <a href="https://github.com/spatie/shiki-php">Shiki</a> out of the box, which makes all the code snippets we include in our docs look beautiful.</p>
<h3>Leveraging wire:navigate in Markdown</h3>
<p>Because there are many internal links inside our Markdown files we wanted to use <code>wir:navigate</code> on them. Our colleague Seb handled this by creating the <a href="https://github.com/spatie/commonmark-wire-navigate">spatie/commonmark-wire-navigate package</a>. For more info on this topic, you can read his blogpost: <a href="https://myray.app/blog/adding-wire-navigate-to-markdown-links">Adding wire:navigate to Markdown links</a></p>
<h3>Adding HTML components inside Markdown</h3>
<p>We wanted to create more complex content like <a href="https://myray.app/docs/getting-started/integrations">the integrations overview</a>. To achieve this, we decided to create Blade components that we could embed inside our Markdown files.</p>
<p>Aaron Francis wrote a great blogpost on how to do this: <a href="https://aaronfrancis.com/2023/rendering-blade-components-in-markdown">Rendering Blade components in Markdown</a>.</p>
<h2>Search</h2>
<p>We use <code>spatie/laravel-site-search</code> to power the search. This package can automatically crawl and index your site's contents using Melisearch under the hood.</p>
<p>We use Alpine to handle the open/closed state of our search box and Livewire to search and return results.</p>
<p>To allow people to open the search box with the well-known <code>CMD+K</code> shortcut, we used Alpine's keyboard event listener. Here's a simplified version of what that looks like:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;body</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">x-data</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">{showSearchBox: false}</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #8FBCBB">@keydown.cmd.k.window.prevent</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">showSearchBox = true</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #616E88">&lt;!-- A backdrop that can be clicked to hide the search --&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">class</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fixed left-0 right-0 top-0 bottom-0 bg-indigo-100/75 z-40</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">x-cloak</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">x-show</span><span style="color: #ECEFF4">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">showSearchBox</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">@click</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">showSearchBox=false</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">&gt;&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #616E88">&lt;!-- The wrapper around the search component --&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">class</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fixed left-0 right-0 top-0 bottom-0 z-50</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">x-cloak</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">x-show</span><span style="color: #ECEFF4">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">showSearchBox</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    		</span><span style="color: #8FBCBB">x-trap</span><span style="color: #ECEFF4">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">showSearchBox</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">livewire:doc-search</span><span style="color: #81A1C1"> /&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span></code></pre>
<h2>Look under the hood yourself</h2>
<p>The repository for the Ray website is public, and you can go and take a look under the hood yourself if you want to know more: <a href="https://github.com/spatie/myray.app">spatie/myray.app: Ray promotional website</a>.</p>
]]>
            </summary>
                                    <updated>2024-03-27T12:17:55+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Adding wire:navigate to Markdown links]]></title>
            <link rel="alternate" href="https://myray.app/blog/adding-wire-navigate-to-markdown-links" />
            <id>https://myray.app/adding-wire-navigate-to-markdown-links</id>
            <author>
                <name><![CDATA[Sebastian]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>We just released our new Ray docs, <a href="https://myray.app/docs/getting-started/introduction">check 'em out</a> if you haven't already! They're hosted right here on myray.app and are extra snappy because they use Livewire's navigate feature.</p>
<p>Livewire's <a href="https://livewire.laravel.com/docs/navigate">navigate feature</a> was recently introduced in Livewire 3. Livewire can hijack a link click, fetch the destination's HTML and swap it with the current page. This results in SPA-like speedy navigation.</p>
<p>If you want to enable Livewire's fast navigation, add a <code>wire:navigate</code> attribute to a link.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;a</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">href</span><span style="color: #ECEFF4">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/docs/installation</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">wire:navigate</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">Installation</span><span style="color: #81A1C1">&lt;/a&gt;</span></span>
<span class="line"></span></code></pre>
<h2>Wire navigatifying our docs</h2>
<p>Back to the Ray docs. All of our documentation is written in Markdown. The downside is you can't add HTML attributes to links in Markdown.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">Read our </span><span style="color: #ECEFF4">[</span><span style="color: #88C0D0">installation guide</span><span style="color: #ECEFF4">](</span><span style="color: #D8DEE9FF; text-decoration: underline">/docs/installation</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">.</span></span>
<span class="line"></span></code></pre>
<p>We could replace all our links with HTML. But that's cumbersome and we're bound to forget a few places now or in the future.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">Read our &lt;a href=&quot;/docs/installation&quot; wire:navigate&gt;installation guide&lt;/a&gt;.</span></span>
<span class="line"></span></code></pre>
<p>Luckily, <code>league/commonmark</code>—the Markdown converter we all use and love—supports extensions. And we wrote just that: an extension to automatically append <code>wire:navigate</code> to all links.</p>
<p>You can configure which links the package should add attributes to. In this example, we'll set up the extension to add <code>wire:navigate</code> to all links that point to docs pages on myray.app.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">converter</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CommonMarkConverter</span><span style="color: #ECEFF4">([</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">wire_navigate</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">domain</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">myray.app</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">	    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">paths</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">docs</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">],</span></span>
<span class="line"><span style="color: #D8DEE9FF">	</span><span style="color: #ECEFF4">],</span></span>
<span class="line"><span style="color: #ECEFF4">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">converter</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">addExtension</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">WireNavigateExtension</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>If you want to learn more or give it a try, check out the <a href="https://github.com/spatie/commonmark-wire-navigate">repository on GitHub</a>. And don't forget to take a peek at our <a href="https://myray.app/docs/getting-started/introduction">new and improved docs</a>. We hope you enjoy them!</p>
]]>
            </summary>
                                    <updated>2024-03-27T14:44:57+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Customizing Ray output]]></title>
            <link rel="alternate" href="https://myray.app/blog/customizing-ray-output" />
            <id>https://myray.app/customizing-ray-output</id>
            <author>
                <name><![CDATA[Zuzana]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Did you know you can customize Ray's output?</p>
<p>When you call Ray without any customization, the output will be displayed in this window:</p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-original-output-2.png" alt="" /></p>
<p>While this looks good as it is,  there are things you can do to improve the style of the output.</p>
<p>You can make the output font size smaller or larger by calling <code>ray(…)-&gt;small()</code> or <code>ray(…)-&gt;large</code>, with the default size being <code>regular</code>. You can also separate items by calling <code>ray()-&gt;separator()</code> to improve the readability of the output.</p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-font-sizes-2.png" alt="" /></p>
<p>To futher improve readability of the Ray output, you can change the colour of the output indicator. You can use <code>green()</code>, <code>orange()</code>, <code>red()</code>, <code>blue()</code>,<code>purple()</code> or <code>gray()</code> colors and turn on the color filter on the output to only display items with specific color.</p>
<p><code>ray(…)-&gt;red()</code></p>
<p><img src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-colors-2.png" alt="" /></p>
<video class="aspect-video" controls loop autoplay muted>
  <source src="https://content.spatie.be/assets/ray/rays-amazing-functions/ray-colors-2.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
]]>
            </summary>
                                    <updated>2024-03-25T13:06:21+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Automatically remove Ray calls from your code]]></title>
            <link rel="alternate" href="https://myray.app/blog/automatically-remove-ray-calls-in-your-code" />
            <id>https://myray.app/automatically-remove-ray-calls-in-your-code</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When debugging, we can scatter a bunch of Ray calls throughout our code, and sometimes we forget to remove one or two before committing our code. To keep a clean codebase, this is something we want to avoid.</p>
<p>To help you clean up your debug code, we have now added a mechanism to remove all Ray calls from your code easily. There are two ways of using it:</p>
<h2>Using a simple command</h2>
<p>If you are using laravel-ray we added an artisan command you can run:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">php</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">artisan ray:clean</span></span>
<span class="line"></span></code></pre>
<p>And poof... all the <code>ray</code> calls in your project are now gone!</p>
<p>We also added a framework-agnostic command for framework agnostic PHP projects:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">./vendor/bin/remove-ray.sh</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #A3BE8C">pat</span><span style="color: #D8DEE9FF">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span></code></pre>
<p>These commands will remove all Ray calls and Ray macro's from your code.</p>
<p>For example:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">users</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">teamUid</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">teamUid</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">users</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">team_uid</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">uid</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">users</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">users</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>This will be cleaned up and give the following result:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">users</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">teamUid</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">users</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">team_uid</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">uid</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">users</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<h2>How this works</h2>
<p>Under the hood, we are using <a href="https://github.com/rectorphp/rector">Rector</a> for this. Rector is a CLI tool that helps you automate the refactoring of your code. Rector has many handy &quot;rules&quot; available and allows you to add your own, which is what we did.</p>
<p>If you are already using Rector, you can add our new Rector rule to your existing Rector config (rector.php):</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Spatie</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Ray</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Rector</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">RemoveRayCallRector</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">rectorConfig</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">rule</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">RemoveRayCallRector</span><span style="color: #81A1C1">::class</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>After adding this, all Ray calls will be removed every time you run Rector. To fully automate this, you can run Rector with a pre-commit hook or <a href="https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you">add it to your CLI</a>.</p>
]]>
            </summary>
                                    <updated>2024-04-04T11:33:36+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Pest comes with Ray integration out of the box]]></title>
            <link rel="alternate" href="https://myray.app/blog/pest-comes-with-ray-integration-out-of-the-box" />
            <id>https://myray.app/pest-comes-with-ray-integration-out-of-the-box</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Pest is an excellent test runner for PHP, that’s been getting more popular. It offers a developer friendly way of writing tests. At Spatie, we use it for all our projects.</p>
<p>Did you know that Pest comes with Ray support out of the box?</p>
<p>Let’s take a look at this very simple test from one of our own packages.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">it</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">can get all holidays of the current year</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">()</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">holidays</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=</span><span style="color: #88C0D0"> </span><span style="color: #8FBCBB">Holidays</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">Belgium</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">make</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    expect</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">holidays</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toBeArray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Imagine now that you want to send the content of <code>$holidays</code> to Ray. Well, thanks to Pest’s integration with Ray, you can just chain a <code>ray()</code> call after <code>expect</code> like this.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">it</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">can get all holidays of the current year</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">()</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">holidays</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=</span><span style="color: #88C0D0"> </span><span style="color: #8FBCBB">Holidays</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">Belgium</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">make</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    expect</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">holidays</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toBeArray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Pretty sweet! Only a couple of keystrokes are needed to send something to Ray!</p>
]]>
            </summary>
                                    <updated>2024-01-23T14:03:01+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Sniffing out stray ray() calls with Pest architecture testing]]></title>
            <link rel="alternate" href="https://myray.app/blog/sniffing-out-stray-ray-calls-with-pest-architecture-testing" />
            <id>https://myray.app/sniffing-out-stray-ray-calls-with-pest-architecture-testing</id>
            <author>
                <name><![CDATA[Sebastian]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Whether you're building a shiny new feature or refactoring bug deep in a legacy codebase, before you merge your work to production you want to get rid of all those <code>ray()</code> calls you littered the codebase with. You'll probably get rid of most of them on time, but every now and then a stray call gets forgotten during code review.</p>
<p>If you're using <a href="https://pestphp.com/">Pest</a>—a PHP testing framework—you can ensure not a single <code>ray()</code> call gets merged by sniffing them out with an architecture test.</p>
<p>Pest's architecture tests allow you to specify a set of rules for your application's architecture.</p>
<p>For example, here's a test that ensures the <code>App\Models</code> namespace only contain models. If the namespace includes a class that doesn't extend <code>Illuminate\Database\Eloquent\Model</code>, it will fail.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">arch</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">app</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expect</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">App\Models</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toExtend</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Illuminate\Database\Eloquent\Model</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<h2>Architecture testing and Ray</h2>
<p>Architecture tests can also assert that a function is <em>never</em> used. In our case, avoiding any <code>ray()</code> call in the codebase.</p>
<p>First, add Pest to your project. (Follow the <a href="https://pestphp.com/docs/installation">installation guide</a> for more in-depth instructions.)</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">composer</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">require pestphp/pest --dev --with-all-dependencies</span></span>
<span class="line"><span style="color: #88C0D0">./vendor/bin/pest</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--init</span></span>
<span class="line"></span></code></pre>
<p>Then, define your architecture test in <code>tests/ArchTest.php</code>.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">arch</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">it will not use ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expect</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">not</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toBeUsed</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This test will fail when <code>ray</code> appears in your codebase. While we're here, we can extend this to fail when we use any debugging function.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">arch</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">it will not use debugging functions</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expect</span><span style="color: #ECEFF4">([</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">dd</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">dump</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">each</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">not</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">toBeUsed</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>During development, you'll want to use debugging functions without Pest nagging about them. To opt out of the architecture test, use the <code>--exclude-arch</code> tag when running tests.</p>
<p>Now, you can exclude the <code>arch</code> group when running Pest during development.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">./vendor/bin/pest</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--exclude-arch</span></span>
<span class="line"></span></code></pre>
<p>Happy testing!</p>
]]>
            </summary>
                                    <updated>2024-03-07T09:35:25+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Automatically starting Ray on MacOS]]></title>
            <link rel="alternate" href="https://myray.app/blog/automatically-starting-ray-on-macos" />
            <id>https://myray.app/automatically-starting-ray-on-macos</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When using Ray to debug your project, you're not using a function like <code>dump($variable)</code> to display the value of a variable, but <code>ray($variable)</code>. This will send the value of your variable to the Ray desktop app. Of course, Ray can do <a href="https://spatie.be/docs/ray/v1/usage/reference">a whole lot more</a> than just displaying the content of a variable.</p>
<p>Behind the scenes, when you execute <code>ray()</code> an HTTP call will be made to a port the Ray desktop app listens to. But what happens if the Ray desktop app is not open? Well, nothing ... your call will be lost in the void. Would it be nice if we could automatically start up Ray whenever <code>ray()</code> is executed?</p>
<p>Since its inception, MacOS has had support for automating various tasks through AppleScript. Using a bit of AppleScript, we can launch the Ray application whenever it is not running.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">set appBundleID to </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">be.spatie.ray</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">tell application </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">System Events</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">not </span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">exists </span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">processes whose bundle identifier is appBundleID</span><span style="color: #ECEFF4">))</span><span style="color: #D8DEE9FF"> then</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        tell application </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Ray</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> to activate</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        delay </span><span style="color: #B48EAD">1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    end </span><span style="color: #81A1C1">if</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">end tell</span></span>
<span class="line"></span></code></pre>
<p>The above script should be executed whenever a <code>ray()</code> call is made. You can do that using <code>Ray::beforeSendRequest()</code>, which accepts a closure.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// somewhere where your app is booting</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">Ray</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">beforeSendRequest</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">script</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">        set appBundleID to &quot;be.spatie.ray&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">        tell application &quot;System Events&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">            if not (exists (processes whose bundle identifier is appBundleID)) then</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">                tell application &quot;Ray&quot; to activate</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">                delay 1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">            end if</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">        end tell</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">escapedScript</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">escapeshellarg</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">script</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">command</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">osascript -e </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">escapedScript</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">output</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">shell_exec</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">command</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>You should put this code somewhere where your app is booting. In a Laravel app, you could put this code in a service provider.</p>
<p>With this in place, the Ray desktop app will automatically boot up where you call <code>ray()</code> in your PHP code.</p>
<iframe class="w-full" style="aspect-ratio: 16/9" src="https://www.youtube-nocookie.com/embed/a8qo-fBo3Ik?&playlist=a8qo-fBo3Ik&controls=0&showinfo=0&autoplay=1&mute=1&loop=1"></iframe>
<p>A big thank you to <a href="https://twitter.com/SebastiaanKloos">Sebastian Kloos</a> for <a href="https://github.com/spatie/ray/pull/859">coming up</a> with this neat technique!</p>
<p>We're currently considering adding this code to the Ray PHP itself, but we wanted to already share this cool bit of knowledge with you.</p>
]]>
            </summary>
                                    <updated>2023-12-14T14:37:15+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Find where a method was called in PHP]]></title>
            <link rel="alternate" href="https://myray.app/blog/find-where-a-method-was-called-in-php" />
            <id>https://myray.app/find-where-a-method-was-called-in-php</id>
            <author>
                <name><![CDATA[Sebastian]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Methods that get called in a bunch of different places can be tricky to debug. For example, <em>why is this model being saved?</em></p>
<p>In PHP, you can find the caller function or method using <code>debug_backtrace()</code>.</p>
{
    public function save()
    {
        var_dump(debug_backtrace()[1]['function']);
    
        parent::save();
    }
}

<p>Let's break this down:</p>
<ul>
<li>
<p><code>debug_backtrace()</code> returns the full backtrace</p>
</li>
<li>
<p><code>[1]</code> will return the previous location in the backtrace (<code>[0]</code> would be the current location)</p>
</li>
<li>
<p><code>function</code> will return the function or method at the location</p>
</li>
</ul>
<h2>Logging the caller in Ray</h2>
<p>If you're using Ray, there's a <code>caller</code> helper to save you some keystrokes and send it straight to Ray.</p>
{
    public function save()
    {
        ray()->caller();
    
        parent::save();
    }
}

<p>Running <code>ray()-&gt;caller</code> will log the file, class, and method that was responsible for calling a method.</p>
]]>
            </summary>
                                    <updated>2023-12-15T09:37:25+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Detecting N+1 issues in your Laravel project with Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/detecting-n-1-issues-in-your-laravel-project-with-ray" />
            <id>https://myray.app/detecting-n-1-issues-in-your-laravel-project-with-ray</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>N+1 issues are a common pitfall when writing applications backed by a relational database.</p>
<p>These issues happen when you attempt to access data from a relationship by lazy loading it. This might seem innocent, but it can have a significant performance impact as your app keeps growing.</p>
<p>While it's not hard to resolve N+1 issues, they can quickly go under the radar for a long time. This is where the power of Ray comes in to help you avoid them!</p>
<p>Luckily, Laravel gives us some tools to detect and prevent N+1 issues.</p>
<p>Let's add the following code to our AppService provider's boot method:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">boot</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">Model</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">preventLazyLoading</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">app</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">isProduction</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">Model</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">handleLazyLoadingViolationUsing</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">model</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">relation</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">get_class</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">model</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">notify</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Attempted to lazy load [</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">relation</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">] on model [</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">class</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">].</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>With the <code>Model::preventLazyLoading()</code> method, we can tell Laravel never to allow lazy loading. Of course, we don't want to break our application in production in case we miss an N+1 issue, so we pass a check and don't turn off lazy loading on production.</p>
<p>The <code>Model::handleLazyLoadingViolationUsing()</code> method will allow us to define custom behavior for when an N+1 issue is detected.</p>
<p>In our case, we will call Ray using the <code>notify()</code> method. Using the notify method, we ensure Ray shows a notification so we don't miss the detected issue.</p>
<p>To make this more practical, let's test this with a simple example: imagine a blogging application with a Post and Comment model. Some simplified code for outputting posts and comments could look like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">posts</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Post</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">query</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">limit</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">5</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">foreach</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">posts</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&lt;h1&gt;</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">&lt;/h1&gt;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">foreach</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">comments</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">comment</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&lt;p&gt;</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">comment</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">body</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">&lt;/p&gt;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre>
<p>If we run this code, Ray will pop up a notification to tell us about the N+1 issue in our code:</p>
<p><img src="https://content.spatie.be/assets/ray/n1/notification.png" alt="" /></p>
<p>To investigate this more, we can use the showQueries() method from Ray to output all queries that are being executed:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"></span>
<span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">showQueries</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">posts</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Post</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">query</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">limit</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">5</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">foreach</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">posts</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&lt;h1&gt;</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">&lt;/h1&gt;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">foreach</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">comments</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">comment</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">echo</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&lt;p&gt;</span><span style="color: #81A1C1">{$</span><span style="color: #D8DEE9">comment</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">body</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">&lt;/p&gt;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre>
<p>Now, if we run this code, we can see that queries are being executed for each post that we loaded, which is not optimal:</p>
<p><img src="https://content.spatie.be/assets/ray/n1/screenshot-2023-11-10-at-16.44.30.png" alt="" /></p>
<p>To resolve this Eloquent allows us to define which relationships we want to eager load:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">posts</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Post</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">query</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">with</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">comments</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">limit</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">5</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"></span></code></pre>
<p>When we run this, we no longer get the N+1 notification, and we can see that we optimized the code so that it only has to execute two queries to get all the data. Win!</p>
]]>
            </summary>
                                    <updated>2024-04-11T07:57:58+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Debugging Livewire components using Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/debugging-livewire-components-using-ray" />
            <id>https://myray.app/debugging-livewire-components-using-ray</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p><a href="https://livewire.laravel.com">Livewire</a> is a fantastic package for creating interactive interfaces in a Laravel app. Using Livewire, you can write component classes that define interactive behavior.</p>
<p>Ray has a few tricks up its sleeve to make debugging Livewire components very easy. Let's take a look!</p>
<h2>Our example component</h2>
<p>We will work with a straightforward Livewire component to search for quotes in the database.</p>
<p>Here's what that component could look like:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">namespace</span><span style="color: #D8DEE9FF"> App</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Http</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Livewire</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> App</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Models</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Quote</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Illuminate</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Support</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Collection</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> Livewire</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">Component</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SearchComponent</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">Component</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">render</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">view</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">livewire.search</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #88C0D0">            </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">quotes</span><span style="color: #ECEFF4">&#39;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">getQuotes</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #ECEFF4">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">getQuotes</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Collection</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">empty</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">))</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Quote</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">search</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>And this is the content of that <code>livewire.search</code> Blade view:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">div </span><span style="color: #81A1C1">class=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-1 mb-4</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">input type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> wire</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF">model</span><span style="color: #81A1C1">.</span><span style="color: #D8DEE9FF">debounce</span><span style="color: #ECEFF4">.</span><span style="color: #B48EAD">200</span><span style="color: #D8DEE9FF">ms</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">searchingFor</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">@if</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">empty</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">@if</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">count</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">quotes</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">ul</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">@foreach</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">quotes</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">quote</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">li</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">{{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">quote</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">text</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">li</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">@endforeach</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">ul</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">@else</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">No</span><span style="color: #D8DEE9FF"> quotes were found</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">@endif</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">@endif</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span></code></pre>
<p>And here's how it looks like rendered in the browser.</p>
<p><img src="https://content.spatie.be/assets/ray/debugging-livewire-components-using-ray/7qa8xp3xk7eafb8kxyblnhvqy0arybae5o6c1uum.jpg" alt="" /></p>
<h2>Inspecting the Livewire component using Ray</h2>
<p>The simplest thing you can do to debug is to send the entire component state to Ray so that you can inspect all of its properties. Let's add a line <code>ray($this)</code> in the <code>render</code> method.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">render</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">view</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">livewire.search</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">quotes</span><span style="color: #ECEFF4">&#39;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">getQuotes</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>In Ray, you can now see all of the properties and configuration of the component.</p>
<p><img src="https://content.spatie.be/assets/ray/debugging-livewire-components-using-ray/cxbtgywgqzkozpdhqhcxkby6pnlkyht99v3qkioj.jpg" alt="" /></p>
<p>Granted, this is a straightforward example. But trust me when I say that when debugging more complex real-world components, seeing the entire state of a component is very handy.</p>
<h2>Let's do it live</h2>
<p>Ray can also shine when interacting with Livewire components. It can display state changes live.</p>
<p>Let's demonstrate this by sending <code>searchingFor</code> property to Ray and start typing in the component.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">render</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">view</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">livewire.search</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">quotes</span><span style="color: #ECEFF4">&#39;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">getQuotes</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>In Ray, you can now see the values change in real time.</p>
<p><img src="https://content.spatie.be/assets/ray/debugging-livewire-components-using-ray/dxeafb5t2liczhqv4bqtjt5lpvjiruvhe2mkrtuq.gif" alt="" /></p>
<p>Again, a straightforward example, but I can promise you that if your Livewire component is more complex and offers more ways of interacting, live inspecting parts of the state is magical.</p>
<p>If you only want to see the final state after having interacted with your component, you can add a <code>ray()-&gt;clearScreen()</code> line in the render method of your component. This clears Ray's screen when the component gets (re)rendered.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">render</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearScreen</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">view</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">livewire.search</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">quotes</span><span style="color: #ECEFF4">&#39;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">getQuotes</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>In this recording, you can see that in action.</p>
<p><img src="https://content.spatie.be/assets/ray/debugging-livewire-components-using-ray/yqvq7wr1jeuqoxxzxvyiftxkebtyrovlau6fyuur.gif" alt="" /></p>
<h2>Bonus tip: using the Ray Blade directive</h2>
<p>If you're working with a Blade view (either a regular one or one used by a Livewire component), you can use the <code>@ray</code> directive to send stuff to Ray.</p>
<p>So instead of adding that <code>ray($this-&gt;searchingFor);</code> line to the <code>render()</code> method, you can also do that in the view.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">{{</span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9FF"> start of view omitted </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> brevity </span><span style="color: #81A1C1">--</span><span style="color: #ECEFF4">}}</span></span>
<span class="line"><span style="color: #81A1C1">&lt;/</span><span style="color: #D8DEE9FF">div</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #81A1C1">@</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">@if</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">empty</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">searchingFor</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">{{</span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9FF"> end of view omitted </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> brevity </span><span style="color: #81A1C1">--</span><span style="color: #ECEFF4">}}</span></span>
<span class="line"></span></code></pre>
]]>
            </summary>
                                    <updated>2023-10-13T12:59:14+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Ray as a debugging companion in busy integration tests]]></title>
            <link rel="alternate" href="https://myray.app/blog/ray-as-a-debugging-companion-in-busy-integration-tests" />
            <id>https://myray.app/ray-as-a-debugging-companion-in-busy-integration-tests</id>
            <author>
                <name><![CDATA[Sebastian]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Ray is a great companion when debugging a test. However, in busy integration tests it can be hard to find out which logs matter and which don’t.</p>
<p>Tests are often structured in three steps: arrange, act, and assert.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CommentTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">TestCase</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">test_a_user_can_post_a_comment</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Arrange</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">factory</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">create</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">…</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Post</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">factory</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">create</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">…</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">			</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Act: What we&#39;re testing</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">post</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">addComment</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">My comment</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Assert</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">assertDatabaseHas</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">…</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>In integration tests, the &quot;arrange&quot; step of a test can get quite heavy. If any of the factories or call in arrange have <code>ray()</code> calls, they'll clutter up your Ray window before even acting on the test subject.</p>
<p>To have a more succinct output in Ray, we can start with a clean slate and only enable it in the act step.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CommentTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">TestCase</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">test_a_user_can_post_a_comment</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearScreen</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">disable</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Arrange</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">enable</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Act</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Assert</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p><code>ray()-&gt;clearScreen()</code> clears the output in Ray to start with a clean slate. <code>disable()</code> ignores future calls to Ray.</p>
<p>After arranging the test, <code>ray()-&gt;enable()</code> turns Ray back on, and we act on our test subject.</p>
<p>If you want Ray to be opt-in in tests, and always start with a clean slate, you can add <code>ray()-&gt;clearScreen()-&gt;disable()</code> to the <code>setUp()</code> method instead.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CommentTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">TestCase</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">parent::</span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">clearScreen</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">disable</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">test_a_user_can_post_a_comment</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Arrange</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">enable</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Act</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Assert</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>Now Ray will only log what you actually care about when testing.</p>
]]>
            </summary>
                                    <updated>2023-10-13T12:59:28+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Expanding objects and array using Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/expanding-objects-and-array-using-ray" />
            <id>https://myray.app/expanding-objects-and-array-using-ray</id>
            <author>
                <name><![CDATA[Sam]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When you send arrays and large objects to Ray, we'll display them beautifully in our app.  Let's send the entire app instance of Laravel to Ray.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">app</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This is what that looks like in Ray.</p>
<p><img src="https://content.spatie.be/assets/ray/expanding-objects-and-array-using-ray/wel2vqjrnsifgi9ysq3siipa6tz6o02oag85ro66.jpg" alt="" /></p>
<p>Because that object is huge, we'll display it collapsed. You can click any of the arrows to open up a node and get more details.</p>
<p>In the latest version of Ray, we added the ability to open up nodes programmatically. So, if you're interested in what's in the <code>reboundCallBacks</code> property, you can already expand it using the new <code>expand</code> method.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">app</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expand</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">reboundCallBacks</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>And now that <code>reboundCallBacks </code> property is automatically opened in Ray, there is no need to manually open it anymore.</p>
<p><img src="https://content.spatie.be/assets/ray/expanding-objects-and-array-using-ray/ffhq78bqb4kuetqtismiwyq9ickr5mfs3rln9mgd.jpg" alt="" /></p>
<p>You can also pass multiple keys, and even use dot notation to reach nested nodes.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">app</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expand</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">reboundCallbacks</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">extenders.url</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/ray/expanding-objects-and-array-using-ray/ohwujphoeuo97tigsk2eugjyk9lbs37ncycbtxv1.jpg" alt="" /></p>
<p>This will also work with arrays.</p>
<p>Consider this array:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">myArray</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">parentKey</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">childKey</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">childValue</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">],</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">anotherParentKey</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">anotherChildKey</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">anotherChildValue</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #ECEFF4">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>As demonstrated above, you can use <code>expand</code> to specify which nodes should be opened in Ray.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">myArray</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expand</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">parentKey.childKey</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/ray/expanding-objects-and-array-using-ray/viy9kt5ongobdww7qg8oyialqsze1ytszluwcson.jpg" alt="" /></p>
<p>Instead of passing a string, you can also pass a number. In that case, Ray will expand all nodes up to that level.</p>
<p>Here's an example that will expand all nodes.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">myArray</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">expand</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/ray/expanding-objects-and-array-using-ray/6z0yfjhpoieufgvxtjbzig8z6wm0ukddyfcff7gt.jpg" alt="" /></p>
<p>A lot of users asked for this feature, so we're happy to have this available in the latest release of Ray.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:00:09+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Copy output from Ray to your clipboard]]></title>
            <link rel="alternate" href="https://myray.app/blog/copy-output-from-ray-to-your-clipboard" />
            <id>https://myray.app/copy-output-from-ray-to-your-clipboard</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>At Spatie, we keep working on Ray continuously. We’re adding small quality-of-life improvements every week. We’re happy to announce that you can now copy logged arrays and objects to your clipboard from Ray.</p>
<p>Previously, copying the output of a variable could be a hassle because Ray outputs it with some extra styling and interactivity. With this update, this should get a lot easier.</p>
<p>If you update to the latest version of the Ray app and PHP package you will now see a copy button on the topright corner. Clicking the button will copy the output to your clipboard.</p>
<p>For example, if you log a variable that looks like this in Ray:</p>
<p><img src="https://content.spatie.be/assets/ray/copy-output-from-ray-to-your-clipboard/ysmgfn6gvhvsitbpyp7sxgbwy2yyomdougzhgemq.png" alt="" /></p>
<p>It can be copied to your clipboard like this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">array</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">title</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">A New Hope</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">year</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1977</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">han_shot_first</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">)</span></span>
<span class="line"></span></code></pre>
<p>The output is determined by PHP's <code>var_export</code>, and the same limitations as for <code>var_export</code> are in place, so more complex variables, like ones with circular references, will not be available to copy.</p>
<p>We hope you enjoy this QoL feature. If you would like to see the copy button expanded to other places in the app, we are happy to hear about your use-case!</p>
]]>
            </summary>
                                    <updated>2024-03-07T09:36:09+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Use Ray to find queries that are slowing you down in your Laravel project]]></title>
            <link rel="alternate" href="https://myray.app/blog/use-ray-to-find-queries-that-are-slowing-you-down-in-your-laravel-project" />
            <id>https://myray.app/use-ray-to-find-queries-that-are-slowing-you-down-in-your-laravel-project</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<h2>How to do this</h2>
<p>Of course, the first thing that you need is the Ray app and the laravel-ray package. If you haven’t installed the package yet you can do this by running:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">composer require spatie/laravel-ray</span><span style="color: #ECEFF4">`</span></span>
<span class="line"></span></code></pre>
<p>Next, you need to add the following to your .env file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">SEND_SLOW_QUERIES_TO_RAY=true</span><span style="color: #ECEFF4">`</span></span>
<span class="line"></span></code></pre>
<p>You can also do this by publishing the config file and overriding the default there.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">php artisan ray</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF">publish</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">config</span></span>
<span class="line"></span></code></pre>
<p>You can also configure what should be considered a slow query. By default, Ray will show all queries that are slower than 500ms. But depending on your situation you might want to be more or less ambitious.</p>
<p>For example, let’s say you want to report every query that’s slower than 100ms, then you would add this to your .env file:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">RAY_SLOW_QUERY_THRESHOLD_IN_MS</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">100</span></span>
<span class="line"></span></code></pre>
<h2>An example</h2>
<p>Imagine we have a users table and a posts table with tons of records, and we want to shop a set of users that forgot to take out ‘lorum ipsum’ in their post.</p>
<p>By nature, this wouldn’t be the fastest query, especially because we forgot to put an index on the user_id column.</p>
<p>Because the execution time went over the threshold of 100ms we set up Ray will make us aware of this slow query:</p>
<p><img src="https://content.spatie.be/assets/ray/use-ray-to-find-queries-that-are-slowing-you-down-in-your-laravel-project/cwy0tfoukgzlwimnhbvcvb5kff38c1gdcsenrkva.png" alt="" /></p>
<h2>Showing slow queries in specific places</h2>
<p>If you don’t want to log all slow queries in your application you can also opt to pick your own starting point to start logging. This can be done by calling the showSlowQueries() method in your code:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">showSlowQueries</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<h2>Other methods to improve your queries</h2>
<p>Besides logging slow queries Ray has a ton of other useful tools to improve the database interactions within your app like  <a href="https://spatie.be/docs/ray/v1/usage/laravel#content-showing-queries">Showing queries</a> and <a href="https://spatie.be/docs/ray/v1/usage/laravel#content-showing-queries">Counting queries</a>.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:01:00+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Ray is the best home for Next.js debugging output]]></title>
            <link rel="alternate" href="https://myray.app/blog/ray-is-the-best-home-for-nextjs-debugging-output" />
            <id>https://myray.app/ray-is-the-best-home-for-nextjs-debugging-output</id>
            <author>
                <name><![CDATA[Sam]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Next.js development comes with a unique challenge when it comes to debug logging. With different rendering options and environments, switching between the node.js developer console and the browser console all the time can become cumbersome. Both consoles take up about 50% of my laptop screen and can't format my logs the same way. This disjointed experience makes it difficult to track the origin, context, and timeline of console.logs.</p>
<p>Ray solves that problem: it can show your backend and frontend logs into a single standalone app, providing a unified and beautiful logging output.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-is-the-best-home-for-nextjs-debugging-output/3uxmgc2ny7ygkayqdffmwh1k64fumujbcmbuhxti.png" alt="" /></p>
<h2>Seeing all your next.js logs in one place</h2>
<p>Ray is a stand-alone desktop application that can handle debugging output from various sources. It is well known and heavily used in the PHP and Laravel ecosystem, but it also works for JS apps.</p>
<p>In a next.js context, Ray consolidates your backend and frontend logs into a single standalone app, providing a unified and beautiful logging output.</p>
<p>Ray's smart formatting options enable you to display log data such as dates, booleans, objects, and rendered or highlighted HTML. You can even update a log in place.</p>
<p>Additionally, Ray automatically adds information about the log's origin and render type. This enables quick links to the exact line of code that made the log happen.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-is-the-best-home-for-nextjs-debugging-output/1qcgiitagkkr4yaverkoganrdkbheuf7nlhhs6fb.png" alt="" /></p>
<p>With the ability to show and filter logs by color labels, you can easily organize and navigate your logs while remaining focused on your code.</p>
<h2>Gettinging started with Ray in next.js</h2>
<p>First, download and open the Ray app, then run <code>npm install next-ray</code> in your Next.js project and add the project root path to your <code>.env</code> file under <code>LOCAL_PATH=</code>.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-is-the-best-home-for-nextjs-debugging-output/lfdx7j0ruu0eryoo514c2tdvvyzpf2kwxe6focdd.png" alt="" /></p>
<p>With the package installed, let's send our first call to Ray.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">import ray from </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">next-ray</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Hello Ray!</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>That's all you need to never return to <code>console.log</code> again.</p>
<p>Want to get hooked on Ray from inside your client components? I've given you some extra functionality inside <code>next-ray/client</code></p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">import ray</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> useRay</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> useRayWithElement</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> Ray </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> from </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">next-ray/client</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"></span></code></pre>
<h2>Ways to use Ray in Next.js</h2>
<p>When debugging an RSC interaction, you can add <code>ray().clearScreen();</code> to the beginning of a <code>page.tsx</code> server component to always work with a single request on one Ray page.</p>
<p>When debugging a specific component, you can move the  <code>clearScreen</code> call inside a useEffect so you can tab through the pages of contextually similar logs.</p>
<p>You can use  <code>useRay(someState, { replace: false })</code> directly under  <code>useState</code> hooks to check when updates happen and what the new value is.</p>
<p>We provide a<code>&lt;Ray&gt;</code> component, that goes around components that quickly change like a loading state. You'll get a visual timeline of the component's behavior while fetching data. If you use tailwind or inline styles, they will be visible in the Ray app.</p>
<p><img src="https://content.spatie.be/assets/ray/ray-is-the-best-home-for-nextjs-debugging-output/dfsuhsnandbnoswlbkezvz2hjimhissmuz2gar5l.png" alt="" /></p>
<p>Data types enforce specific formatting options inside the Ray app. Your logs will be even more informative and readable.</p>
<p>You can use  <code>useRay(props, { type: 'toJson' })</code> for inspecting an entire JS object at once without adding descriptive labels.</p>
<p>These are some of the available methods:</p>
<ul>
<li>
<p><code>notify</code>: Notifications made easy.</p>
</li>
<li>
<p><code>error</code>: Send your caught errors straight to Ray to see the stack trace.</p>
</li>
<li>
<p><code>table</code>: Format your rows of data as a table.</p>
</li>
<li>
<p><code>measure</code>: Measure how long something takes.</p>
</li>
<li>
<p><code>confetti</code>: For that extra dash of 'yesss, it works!'.</p>
</li>
</ul>
<h2>In closing</h2>
<p>At Spatie, we love seeing how you integrate the Ray app into your workflows, so don't hesitate to tweet us your favorite features and what you want to see in the next version of Ray.</p>
<p>We like to thank <a href="https://github.com/permafrost-dev">Patrick from Permafrost dev</a> for his work on Ray's react integration and feedback on the next.js parts.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:01:29+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Use ray()->trace() to figure out where a call came from]]></title>
            <link rel="alternate" href="https://myray.app/blog/use-ray-trace-to-figure-out-where-a-call-came-from" />
            <id>https://myray.app/use-ray-trace-to-figure-out-where-a-call-came-from</id>
            <author>
                <name><![CDATA[Tim]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>An important problem of a programmer's productivity is finding his way through the code. A good project structure and your IDE can get you a long way, but sometimes it’s not enough. Luckily enough Ray has a bunch of tools to help you out.</p>
<h2>The problem</h2>
<p>Imagine an example where you have a NotifyUserAction being called, but you are unsure exactly why or where this call happens. Your IDE tells you the action is being used in 10 different files, and you don’t want to walk through all the code paths.</p>
<h2>The solution</h2>
<p>An easy way to figure this out is to add a call to Ray with the “trace” method.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NotifyUserAction</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">showApp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">trace</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Some logic to notify the user...</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>Now the only thing you have to do is use the application and look at Ray. Once your application hits the trace method, a nice backtrace will be shown in Ray.</p>
<p><img src="https://content.spatie.be/assets/ray/use-ray-trace-to-figure-out-where-a-call-came-from/lir2tj2q43hqula1wcihrah1p9xugpvfmgf3j5ow.png" alt="" /></p>
<p>The entries in the trace that belong to your application are highlighted in blue, making it easier to visually distinguish them from the code originating from the framework and packages.</p>
<p>If you are unsure what exact user action is triggering this part of your code and you want to click around your app until it shows, you can always chain in the showApp() method, which will bring Ray to the foreground once that part of the code is being hit.</p>
<p><code>ray()-&gt;showApp()-&gt;trace();</code></p>
<p>The trace method is only one of many powerfull tools Ray provides. Make sure to take a look at <a href="https://spatie.be/docs/ray/v1/usage/reference">the documentation</a> if you want to learn more.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:01:40+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Testing mails in Laravel using Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/testing-mails-in-laravel-using-ray" />
            <id>https://myray.app/testing-mails-in-laravel-using-ray</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>One of the most valuable features of Ray is its ability to preview emails in your local environment.</p>
<p>In this post, we'll explore how Ray can help you preview emails in your local environment and how it can simplify the process of developing and testing email workflows in Laravel.</p>
<h2>Rendering a mailable in Ray</h2>
<p>To use Ray in a Laravel app, you only need to install the <code>laravel-ray</code> package. Changes are that, as a Ray user, you've already done this.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #D8DEE9FF">composer </span><span style="color: #81A1C1">require</span><span style="color: #D8DEE9FF"> spatie</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">laravel</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">ray</span></span>
<span class="line"></span></code></pre>
<p>After that, there's no configuration needed. Whenever your Laravel app will now send a mailable, it will be displayed in Ray. Here's how that looks.</p>
<p><img src="https://content.spatie.be/assets/ray/testing-mails-in-laravel-using-ray/lgy4a5cn8rgid3ykxs7jx5jzeouu1gw0ugcwycix.jpg" alt="" /></p>
<p>How does this work behind the scenes? Laravel is configured to write all emails to the log in a local environment. This prevents mails from being sent accidentally. Ray listens to what is writing in the log, and when it detects mail, it will pick up on it and display it.</p>
<h2>Testing email workflows in Ray</h2>
<p>The mail displayed in Ray isn't static: all the links of the displayed mail will just work. This makes it easy to test entire email workflows using Ray.</p>
<p>A typical web app has a password reset flow where you get a mail that contains a link to reset your password. Using Ray, you can quickly test out the entire flow.</p>
<p><img src="https://content.spatie.be/assets/ray/testing-mails-in-laravel-using-ray/flow.gif" alt="" /></p>
<p>This technique doesn't only work for a password reset but for any email flow in your app.</p>
<h2>In closing</h2>
<p>Ray makes testing emails in a Laravel app very easy. There are lots more niceties for debugging Laravel apps in Ray. It can display all events, executed SQL queries, sent  HTTP requests, and much more. Learn all about it <a href="https://spatie.be/docs/ray/v1/usage/laravel">our extensive documentation for Laravel</a>.</p>
]]>
            </summary>
                                    <updated>2024-05-16T13:06:18+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Inspecting the return value of a function using Ray]]></title>
            <link rel="alternate" href="https://myray.app/blog/inspecting-the-return-value-of-a-function-using-ray" />
            <id>https://myray.app/inspecting-the-return-value-of-a-function-using-ray</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>When trying to fix a bug, you might want to see the return value of a function. In this post, we'd like to offer a simple solution.</p>
<h2>The problem</h2>
<p>Consider this simple code snippet:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fullName</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">first_name</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">last_name</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>You might think you can do this to display the result in Ray:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fullName</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">		    </span><span style="color: #616E88">//This does not work</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">first_name</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">last_name</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>This does not work because the <code>ray()</code> function returns an instance of the <code>Ray</code> object used to communicate with the Ray app. You can tack on one of <a href="https://spatie.be/docs/ray/v1/usage/reference">the many Ray functions</a>, such as <code>red()</code>, to colorize the output.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">hi there</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">red</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>This will be displayed in Ray as</p>
<p><img src="https://content.spatie.be/assets/ray/inspecting-the-return-value-of-a-function-using-ray/dsykcfbtqwjxyjkwz6ob4wniw1jyyup6dmvgqlhj.jpg" alt="" /></p>
<p>Back to our original problem: displaying a return value to Ray. This might be your second attempt:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fullName</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">first_name</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">last_name</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">result</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> result</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>That will work, but it is rather cumbersome. To see the output in Ray, you have to introduce a new variable. Send that to Ray, and use it as the return value. That's too much work for simply wanting to see the value.</p>
<h2>The solution</h2>
<p>To easily debug return value, Ray offers a handy <code>pass</code> function. Any value you pass to <code>pass</code> will be displayed in Ray and returned by the function.</p>
<p>That means you can do this:</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">User</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fullName</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">string</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">		    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">pass</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">first_name</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">{$this-&gt;</span><span style="color: #D8DEE9">last_name</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>Using <code>pass</code>, we didn't have to introduce a temporary variable. To see the return value in Ray, we can wrap the return value in a <code>pass</code> call.</p>
<p>I hope you enjoyed this little Ray tip.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:01:52+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Easily inspect the contents of a Laravel collection]]></title>
            <link rel="alternate" href="https://myray.app/blog/easily-inspect-the-contents-of-a-laravel-collection" />
            <id>https://myray.app/easily-inspect-the-contents-of-a-laravel-collection</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>One of my favorite things when working on a Laravel project is <a href="https://laravel.com/docs/10.x/collections#main-content">Laravel's powerful Collection class</a>. I use it all of the time to transform data.</p>
<p>Let's take a look at a straightforward example. You'll see longer collection chains with more complicated methods in real-world applications.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">([</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">a</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">b</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">c</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">map</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">fn</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strtoupper</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">reverse</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Imagine you want to see what the collection's contents look like in the middle of the chain in Ray. You might do something like this.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">collection</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">([</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">a</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">b</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">c</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">map</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">fn</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strtoupper</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">collection</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">collection</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">reverse</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>That will work: the collection's contents will be displayed in Ray.</p>
<p><img src="https://content.spatie.be/assets/ray/easily-inspect-the-contents-of-a-laravel-collection/u5shahlqpzzym9tfihbe482yqrdhzvpjwt26xwcd.jpg" alt="" /></p>
<p>But going about it this way is a lot of work: you have to introduce a new temporary variable and break up your chain.</p>
<p>Luckily there's a much simpler way of going about this. When the spatie/laravel-ray package is <a href="https://spatie.be/docs/ray/v1/configuration/laravel">installed</a> in your Laravel project, we automatically register a <code>ray</code> collection macro to easily send collections to the desktop application. So to see what the collection looks like, you can simply add a <code>ray()</code> call after that <code>map</code> operation.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">([</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">a</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">b</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">c</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">map</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">fn</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strtoupper</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">reverse</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Of course, you can use <code>ray()</code> multiple times in your chain, and you can even pass it a label which will be displayed in Ray.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">([</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">a</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">b</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">c</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">original</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">map</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">fn</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strtoupper</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">letter</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">uppercased</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">reverse</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">reversed</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p><img src="https://content.spatie.be/assets/ray/easily-inspect-the-contents-of-a-laravel-collection/fie7leohmldkbgairdbb5a4herta7bvoy9yof6do.jpg" alt="" /></p>
<p>And that's how easy it is to inspect collections in Ray.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:02:05+00:00</updated>
        </entry>
            <entry>
            <title><![CDATA[Automatically clear Ray when running tests via PHPUnit or Pest]]></title>
            <link rel="alternate" href="https://myray.app/blog/automatically-clear-ray-when-running-tests-via-phpunit-or-pest" />
            <id>https://myray.app/automatically-clear-ray-when-running-tests-via-phpunit-or-pest</id>
            <author>
                <name><![CDATA[Freek]]></name>
            </author>
            <summary type="html">
                <![CDATA[<p>Having your application covered with a good suite of tests has many benefits. The most significant benefit is the confidence that everything is working as expected. Tests also allow you to refactor without fear. You can read more about all the benefits at <a href="https://testing-laravel.com/importance-of-testing">Testing Laravel</a>.</p>
<p>You'll also write and run your tests when introducing new app functionality and fixing bugs. Your changes might not initially behave like you want to, so you start debugging using Ray. In your application code, you introduce some Ray calls here and there to help you understand what is happening.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">it</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">will send an invoice when the order is created</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">()</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #8FBCBB">Mail</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">fake</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">new</span><span style="color: #88C0D0"> </span><span style="color: #8FBCBB">CreateOrderAction</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">product</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #8FBCBB">Mail</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">assertSent</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">OrderCreatedMailable</span><span style="color: #81A1C1">::class</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">OrderCreatedMailable</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">mail</span><span style="color: #ECEFF4">)</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #81A1C1">return</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">mail</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">hasTo</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">usser</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>Here's what the output in Ray could be like.</p>
<p><img src="https://content.spatie.be/assets/ray/automatically-clear-ray-when-running-tests-via-phpunit-or-pest/yafa0nhxy3sk3ybg1zf9sv3cnypau07vizfa8ze1.jpg" alt="" /></p>
<p>Imagine you need a few attempts to fix the bug. You will execute the test multiple times after each little change you try. After a while, Ray will be flooding with output like this.</p>
<p><img src="https://content.spatie.be/assets/ray/automatically-clear-ray-when-running-tests-via-phpunit-or-pest/s0qyxd4y7tlnu8wn3fznylimlds0wbpjxofhvgv5.jpg" alt="" /></p>
<p>The problem here is that it is hard to see where your previous test run's output ends and where the latest run's output starts.</p>
<h2>Manually clearing the screen</h2>
<p>Of course, Ray has a lovely <code>newScreen</code> method that will clear the Ray screen, so only the output of your latest run in shown. You can even pass it a string, which will be used as a title in Ray.</p>
<p>Let's add it to our test.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #88C0D0">it</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">will send an invoice when the order is created</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">()</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">    ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Starting test</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #8FBCBB">Mail</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">fake</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">new</span><span style="color: #88C0D0"> </span><span style="color: #8FBCBB">CreateOrderAction</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">a</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">b</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #8FBCBB">Mail</span><span style="color: #81A1C1">::</span><span style="color: #88C0D0">assertSent</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">OrderCreatedMailable</span><span style="color: #81A1C1">::class</span><span style="color: #ECEFF4">,</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">function</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">OrderCreatedMailable</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">mail</span><span style="color: #ECEFF4">)</span><span style="color: #88C0D0"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #88C0D0">        </span><span style="color: #81A1C1">return</span><span style="color: #88C0D0"> </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">mail</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">hasTo</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #D8DEE9">usser</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">    </span><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>When running the code above, we only see the output of the latest run of the test. Nice!</p>
<p><img src="https://content.spatie.be/assets/ray/automatically-clear-ray-when-running-tests-via-phpunit-or-pest/juecybvsreqxyvexdlllt9xpejvhq2osy79omefr.jpg" alt="" /></p>
<h2>Automatically clearing the screen</h2>
<p>We can still do better. Instead of manually adding a <code>newScreen</code> call, we can do it automatically when running any test.</p>
<p>Let's remove the <code>newScreen</code> call from our test and add it to  the <code>setUp()</code> method of the base <code>TestCase.</code> The <code>$this-&gt;name()</code> call will return the name of the tests in PHPUnit 10 / Pest 2. If you're using an older version, use <code>getName()</code> instead of <code>name()</code>.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #81A1C1">use</span><span style="color: #D8DEE9FF"> PHPUnit</span><span style="color: #ECEFF4">\</span><span style="color: #D8DEE9FF">Framework</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">TestCase</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> BaseTestCase</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">TestCase</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">extends</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">BaseTestCase</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">protected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">parent::</span><span style="color: #88C0D0">setUp</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-</span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// ..</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre>
<p>When we now run our test, we see the name of the test as the title.</p>
<p><img src="https://content.spatie.be/assets/ray/automatically-clear-ray-when-running-tests-via-phpunit-or-pest/cy1ygorr449xp6d9swn8nue7l4gnchgiuis0emua.jpg" alt="" /></p>
<p>With this code, you don't need to manually add a <code>newScreen</code> call to any of your tests. And when you run an individual test, you'll always see the output of the latest run.</p>
<p>If you're using Pest, you could also set this up in the <code>Pest.php</code> file.</p>
<pre class="shiki" style="background-color: #2e3440ff"><code><span class="line"><span style="color: #616E88">// in Pest.php</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">uses</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Tests</span><span style="color: #ECEFF4">\</span><span style="color: #8FBCBB">TestCase</span><span style="color: #81A1C1">::class</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">beforeEach</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ray</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">newScreen</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">$this-&gt;</span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">())</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #81A1C1">-&gt;</span><span style="color: #88C0D0">in</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">__DIR__</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre>
<p>I hope this tip will help you when you are running your tests.</p>
]]>
            </summary>
                                    <updated>2023-10-13T13:02:17+00:00</updated>
        </entry>
    </feed>
