<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Davey Shafik</title>
    <description>The latest articles on DEV Community by Davey Shafik (@dshafik).</description>
    <link>https://dev.to/dshafik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1390973%2F412933aa-ec0b-4588-a45d-edb69dba8ff3.jpeg</url>
      <title>DEV Community: Davey Shafik</title>
      <link>https://dev.to/dshafik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dshafik"/>
    <language>en</language>
    <item>
      <title>Laravel Pipelines &amp; Composable Job Middleware</title>
      <dc:creator>Davey Shafik</dc:creator>
      <pubDate>Wed, 02 Oct 2024 09:54:57 +0000</pubDate>
      <link>https://dev.to/dshafik/laravel-pipelines-composable-job-middleware-2n03</link>
      <guid>https://dev.to/dshafik/laravel-pipelines-composable-job-middleware-2n03</guid>
      <description>&lt;p&gt;A feature you are probably familiar with in Laravel — even if you don't know it — is the Pipeline.&lt;/p&gt;

&lt;p&gt;Laravel features a Pipeline implementation that allows you to easily create a series of pipes through which a value should pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Pipeline\Pipeline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pipeline&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stringable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;through&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stringable&lt;/span&gt; &lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
             &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello World!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stringable&lt;/span&gt; &lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 

&lt;span class="c1"&gt;// Hello World!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the heck is a Pipe?
&lt;/h2&gt;

&lt;p&gt;A pipe is a &lt;code&gt;callable&lt;/code&gt;, or any class with a specific public method (by default: &lt;code&gt;handle()&lt;/code&gt;) — it should take two arguments, a "passable", and a Closure. The passable is the value you are sending through the pipeline, and the Closure is a wrapper that will call the next pipe in the pipeline, passing through the argument passed to it (which should be the passable) and handle the return value.&lt;/p&gt;

&lt;p&gt;In the example above, the pipe is a short closure that accepts our &lt;code&gt;Stringable&lt;/code&gt; passable, and then calls the &lt;code&gt;$next&lt;/code&gt; closure passing in a modified string.&lt;/p&gt;

&lt;p&gt;A more complex set of pipes might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stringable&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$greetings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Hey'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hola'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Howdy'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Arr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$greetings&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Auth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stringable&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hasUser&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pipeline&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stringable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;through&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;AddName&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stringable&lt;/span&gt; &lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two pipes work together to construct a greeting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first pipe (&lt;code&gt;Greeting&lt;/code&gt;) will add a random friendly greeting word&lt;/li&gt;
&lt;li&gt;Then it will call the second pipe (&lt;code&gt;AddName&lt;/code&gt;) that will conditionally add the users name&lt;/li&gt;
&lt;li&gt;Finally the updated string will be returned &lt;em&gt;back&lt;/em&gt; to the first pipe to make sure it ends in an exclamation point.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuos71s588ex8qjerp7ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuos71s588ex8qjerp7ch.png" alt="Greeting Pipeline Sequence Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a different handler method
&lt;/h3&gt;

&lt;p&gt;If you look closely at the example above, I use &lt;code&gt;__invoke()&lt;/code&gt; with the first pipe, but &lt;code&gt;handle()&lt;/code&gt; on the second. The &lt;code&gt;Pipeline&lt;/code&gt; class will accept any valid callable, or, if it's a string, it will resolve the class using the service container and if invokable (has &lt;code&gt;__invoke()&lt;/code&gt;) it will execute that, otherwise it will use the handler method, which defaults to &lt;code&gt;handle()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can change the named handler method by calling &lt;code&gt;Pipeline-&amp;gt;via()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pipeline&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$passable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;through&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;via&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'METHOD NAME HERE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Does This Look So Familiar?
&lt;/h2&gt;

&lt;p&gt;If this is starting to look familiar, it's because it looks a heck of a lot like HTTP middleware… right? Well, that's because it &lt;strong&gt;is&lt;/strong&gt; HTTP middleware… or rather, internally, Laravel uses Pipelines for multiple different things, including the &lt;a href="https://github.com/laravel/framework/blob/46dc7a45ba773c955cc48180f2f8fb5e6c1efd8b/src/Illuminate/Foundation/Http/Kernel.php#L173-L176" rel="noopener noreferrer"&gt;HTTP request lifecycle&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Illuminate\Foundation\Http\Kernel-&amp;gt;sendRequestThroughRouter()&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;through&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldSkipMiddleware&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatchToRouter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Job Middleware
&lt;/h2&gt;

&lt;p&gt;What you might be surprised to find out is that Laravel Jobs &lt;strong&gt;also&lt;/strong&gt; go through a pipeline when executed, and can &lt;em&gt;also&lt;/em&gt; have &lt;a href="https://laravel.com/docs/11.x/queues#job-middleware" rel="noopener noreferrer"&gt;middleware&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Job middleware is extremely powerful, it can be used to track metrics about your jobs, to handle errors, to verify that a job hasn't expired for some reason, or whatever else you can imagine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Authenticated Users in Jobs
&lt;/h3&gt;

&lt;p&gt;A common problem that is encountered when reusing (particularly older code) within jobs is that they are tightly coupled to authentication using the &lt;code&gt;Auth&lt;/code&gt; facade to retrieve the user, and this fails because a job running in the background is no longer executing within the users authenticated web session.&lt;/p&gt;

&lt;p&gt;We can use middleware to ensure that jobs that require an authenticated user are logged in during job execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Queue\Jobs\Job&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Job&lt;/span&gt; &lt;span class="nv"&gt;$job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'getUser'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$job&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$job&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Composable Job Middleware
&lt;/h2&gt;

&lt;p&gt;A common pattern in Laravel is to use traits to add features to classes, for example &lt;code&gt;HasTimestamps&lt;/code&gt;, &lt;code&gt;SerializesModels&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;This would be a great way to add middleware to our jobs. Laravel will automatically call a jobs &lt;code&gt;middleware()&lt;/code&gt; method which should return an array of middleware to add to the pipeline. We can use this feature to create composable middleware.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;HasMiddleware&lt;/code&gt; Trait
&lt;/h3&gt;

&lt;p&gt;The goal of the &lt;code&gt;HasMiddleware&lt;/code&gt; trait is to add a &lt;code&gt;middleware()&lt;/code&gt; method that will dynamically create an array of middleware based on the traits it uses. To do this, for each middleware we create a trait that has a &lt;code&gt;middleware&amp;lt;TraitName&amp;gt;()&lt;/code&gt; method — e.g. for the &lt;code&gt;HasDeadline&lt;/code&gt; middleware trait, it would have a &lt;code&gt;middlewareHasDeadline()&lt;/code&gt; method — which is called by the &lt;code&gt;middleware()&lt;/code&gt; function and it's result is added to the list of middleware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Jobs\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;class_basename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;class_uses_recursive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;HasMiddleware&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'middleware'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;class_uses_recursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$trait&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;class_basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$trait&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$trait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;}());&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then add middleware by creating a trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Jobs\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Jobs\Middleware\Auth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;HasAuth&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasMiddleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

     &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;middlewareHasAuth&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You Job class can then &lt;code&gt;use&lt;/code&gt; the &lt;code&gt;HasAuth&lt;/code&gt; trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Jobs\Traits\HasAuth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyJob&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasAuth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Auth::user()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This trait &lt;code&gt;use&lt;/code&gt;s the &lt;code&gt;HasMiddleware&lt;/code&gt; trait itself so that the job doesn't need to &lt;code&gt;use&lt;/code&gt; it explicitly in addition to the &lt;code&gt;HasAuth&lt;/code&gt; trait.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Job Middleware
&lt;/h2&gt;

&lt;p&gt;Starting in &lt;a href="https://github.com/laravel/framework/releases/tag/v11.26.0" rel="noopener noreferrer"&gt;Laravel 11.26&lt;/a&gt;, you can now use the &lt;code&gt;artisan make:job-middleware&lt;/code&gt; command to generate Job middleware.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Summary…
&lt;/h2&gt;

&lt;p&gt;The Laravel Pipeline is very versatile — although for &lt;a href="https://bagvalueobjects.com" rel="noopener noreferrer"&gt;Bag&lt;/a&gt; I went with &lt;a href="https://pipeline.thephpleague.com" rel="noopener noreferrer"&gt;League\Pipeline&lt;/a&gt; as it was faster and functionally, it's almost identical — and can be used for any composable series of operation that act on a value — be that HTTP request middleware, Job middleware, or anything else you want.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>indepth</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Finding Terminal Utopia</title>
      <dc:creator>Davey Shafik</dc:creator>
      <pubDate>Wed, 22 May 2024 09:57:11 +0000</pubDate>
      <link>https://dev.to/dshafik/finding-terminal-utopia-583k</link>
      <guid>https://dev.to/dshafik/finding-terminal-utopia-583k</guid>
      <description>&lt;p&gt;For the last few weeks I’ve been setting up a new laptop from scratch for the first time in a… very long time, taking the time especially to re-evaluate my terminal setup and integrating some new tools, and re-integrating some old tools in a new way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Almost all of the tools and scripts mentioned below can be easily installed using homebrew. If you want to use &lt;a href="https://gist.github.com/dshafik/67fe3e0ba5096a00c91cccb0792a884b#file-zshrc" rel="noopener noreferrer"&gt;my .zshrc config&lt;/a&gt; as-is, you’ll want to use homebrew.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to skip ahead and see the final outcome, checkout the screencast at the end of this post.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Building a Better Prompt
&lt;/h2&gt;

&lt;p&gt;Everything in the terminal starts with your prompt. Before I moved to zsh, I used &lt;code&gt;bash-it&lt;/code&gt; with &lt;code&gt;bash&lt;/code&gt; to create a fancy prompt that gave me things like current directory, git status and branch, and tool versions. Moving to zsh, I installed &lt;code&gt;oh-my-zsh&lt;/code&gt; and my prompt took a giant leap forward with display of command exit status, command timing, icons, and more tool versions. Unfortunately, my prompt got so bloated that I started to dread opening a new terminal.&lt;/p&gt;

&lt;p&gt;Thankfully, I found &lt;a href="https://starship.rs" rel="noopener noreferrer"&gt;Starship&lt;/a&gt;, a super fast, super configurable prompt written in Rust. It works with most shells, on most operating systems.&lt;/p&gt;

&lt;p&gt;Even with additional prompt information like Kubernetes context and namespace, Google Cloud CLI version, and a prettier (to me!) more usable prompt, it’s blazing fast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-2.30.00%E2%80%AFAM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttn9ycr6ikfk1qaajmd1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see that I’m in the &lt;code&gt;bag&lt;/code&gt; directory, which is on the main branch with some untracked files, using NodeJS v22.2.0, and PHP 8.3.7. You can also see that the previous command took five seconds to run, and the current (24 hours) time, and you can see how quickly it renders using &lt;code&gt;starship explain&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-2.32.32%E2%80%AFAM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhonjgc2pmepgecdvx09r.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the total rendering time is less than 150ms. And &lt;a href="https://gist.github.com/dshafik/67fe3e0ba5096a00c91cccb0792a884b#file-starship-toml" rel="noopener noreferrer"&gt;my configuration&lt;/a&gt; (which is minimally updated from the default) includes support for dozens of other languages and useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the Prompt
&lt;/h2&gt;

&lt;p&gt;Before we add any other applications, let’s add some zsh scripts to make using the parts after the prompt nicer.&lt;/p&gt;

&lt;p&gt;First, we’re going to configure how zsh stores your history. The history is a log of every command you’ve entered into the shell. We are going to set the following options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Save the history in your home directory as .zsh_history&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HISTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.zsh_history 

&lt;span class="c"&gt;# Set the history size to 2000 commands&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HISTSIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2000               

&lt;span class="c"&gt;# Store the same number to disk&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SAVEHIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HISTSIZE&lt;/span&gt;          

&lt;span class="c"&gt;# Share history between sessions&lt;/span&gt;
setopt share_history               

&lt;span class="c"&gt;# Remove duplicates first when HISTSIZE is met&lt;/span&gt;
setopt hist_expire_dups_first      

&lt;span class="c"&gt;# If a command is issued multiple times in a row, ignore dupes&lt;/span&gt;
setopt hist_ignore_dups  

&lt;span class="c"&gt;# Allow editing the command before executing again&lt;/span&gt;
setopt hist_verify                 

&lt;span class="c"&gt;# Do not add commands prefixed with a space to the history&lt;/span&gt;
setopt hist_ignore_space           
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s change how we interact with the history. By default, you use &lt;code&gt;Ctrl+r&lt;/code&gt; or &lt;code&gt;Ctrl+Shift+R&lt;/code&gt; which will bring up the bck-i-search UI, that will match history commands as you type, to navigate forward or backwards from the current match you need to then press &lt;code&gt;Ctrl+r&lt;/code&gt; or &lt;code&gt;Ctrl+Shift+R&lt;/code&gt; again multiple times until you find the item you want.&lt;/p&gt;

&lt;p&gt;This is not intuitive, or efficient, so our first task is fix that using &lt;a href="https://github.com/zsh-users/zsh-autosuggestions" rel="noopener noreferrer"&gt;zsh-autosuggestions&lt;/a&gt;. zsh-autosuggestions will provide autocomplete suggestions from your history (and other locations) as you type, simply press the right arrow key, or use the end-of-line keyboard shortcut (e.g. &lt;code&gt;ctrl+e&lt;/code&gt;) to accept the suggestion. If you only want part of a suggestion, you can use the forward-word keyboard shortcut, which is &lt;code&gt;option+right arrow&lt;/code&gt; or &lt;code&gt;alt+right arrow&lt;/code&gt; which will complete only up to the end of the next word and continue providing suggestions from there.&lt;/p&gt;

&lt;p&gt;This works great if you can remember the exact start of the previous command you want to use, but what if you only remember part of it? Enter &lt;a href="https://github.com/zsh-users/zsh-history-substring-search" rel="noopener noreferrer"&gt;zsh-history-substring-search&lt;/a&gt;. This script allows you to navigate your history using substring matches, and you can bind it to your up and down arrow keys so that you can simply type the substring and then hit the up key to start finding matches.&lt;/p&gt;

&lt;p&gt;Lastly, let’s make &lt;em&gt;what&lt;/em&gt; commands we’re running easier to read, using &lt;a href="https://github.com/zsh-users/zsh-syntax-highlighting" rel="noopener noreferrer"&gt;zsh-syntax-highlighting&lt;/a&gt;. This script (which must be loaded &lt;em&gt;before&lt;/em&gt; the zsh-history-substring-search above) will provide syntax highlighting of commands as you write them. This will help you avoid typos for incorrect commands, and make complex commands easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing Directories For The Better
&lt;/h2&gt;

&lt;p&gt;One of the most — if not &lt;em&gt;the most&lt;/em&gt; — common commands that I use is &lt;code&gt;cd&lt;/code&gt;. A very simple commands, it changes your current working directory to the one specified.&lt;/p&gt;

&lt;p&gt;One simple trick to improve your use of &lt;code&gt;cd&lt;/code&gt; is knowing that &lt;code&gt;cd -&lt;/code&gt; will take you back to whatever directory you were previously in. This alone had a massive impact on my ability to navigate around my directory structure.&lt;/p&gt;

&lt;p&gt;There is, however, an even better solution: &lt;code&gt;zoxide&lt;/code&gt;, a &lt;code&gt;cd&lt;/code&gt; alternate, and can be used as a drop-in replacement.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;zoxide&lt;/code&gt; uses fuzzy matching and a frequency of usage ranking to intelligently let you change directories based on substrings. For example, to reach the &lt;code&gt;bag&lt;/code&gt; repo that lives at &lt;code&gt;~/src/bag&lt;/code&gt;, I can use &lt;code&gt;cd bag&lt;/code&gt; from anywhere, if however I want to enter the &lt;code&gt;bag-benchmarks&lt;/code&gt; repo, I can use &lt;code&gt;cd bag-&lt;/code&gt; and it will take me there instead.&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;zoxide&lt;/code&gt; uses &lt;code&gt;fzf&lt;/code&gt; for completions and interactive selection, but &lt;code&gt;fzf&lt;/code&gt;‘s utility doesn’t end there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Clarity Through Fuzziness
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fzf&lt;/code&gt; is really the hero of this story, as it can integrate into many different aspects of your terminal to bring improvements.&lt;/p&gt;

&lt;p&gt;For example, it can be used as part of tab suggestions. When you hit &lt;code&gt;&amp;lt;tab&amp;gt;&lt;/code&gt; to get suggestions, it will display a UI with a keyboard navigable, searchable, list of possibilities:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-8.24.49%E2%80%AFPM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa73oe7jghfu8xmlr1hux.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also works for arguments:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-8.25.54%E2%80%AFPM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4gvyyq6cab3xnitlnaa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But fzf can do &lt;strong&gt;so much&lt;/strong&gt; more. To expand on it’s capabilities, let’s add two helpers: &lt;code&gt;fd&lt;/code&gt; and &lt;code&gt;eza&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find Everything
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;fd&lt;/code&gt; is a simpler, faster alternative to &lt;code&gt;find&lt;/code&gt;, which is valuable in and of itself, but paired with &lt;code&gt;fzf&lt;/code&gt;, it can enable faster/better suggestions. Adding the following will use &lt;code&gt;fd&lt;/code&gt; for both file and folder completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_fzf_compgen_path&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    fd &lt;span class="nt"&gt;--hidden&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; .git &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

_fzf_compgen_dir&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    fd &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;d &lt;span class="nt"&gt;--hidden&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; .git &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, &lt;code&gt;fzf&lt;/code&gt; will suggest both directories and files (except &lt;code&gt;.git&lt;/code&gt;) when trying to auto-complete paths (see above screenshot with &lt;code&gt;ls&lt;/code&gt;), but will only show directories (except &lt;code&gt;.git&lt;/code&gt;) when using commands like &lt;code&gt;cd&lt;/code&gt; that only accept a directory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-11.37.30%E2%80%AFPM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8rqkxgt2u1dydmc59hw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Show Everything
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;eza&lt;/code&gt; is a drop-in replacement for &lt;code&gt;ls&lt;/code&gt;, and provides a much richer file list experience, from adding rich colors, to showing icons and git status.&lt;/p&gt;

&lt;p&gt;I alias &lt;code&gt;eza&lt;/code&gt; to &lt;code&gt;ls&lt;/code&gt; with the following options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"eza --icons=always --color=always --git"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows me to continue to use &lt;code&gt;ls&lt;/code&gt; the way I always have (compact format by default, adding &lt;code&gt;-al&lt;/code&gt; to show all files in list format) but will add &lt;code&gt;eza&lt;/code&gt; colors, icons, and show git information is list view.&lt;/p&gt;

&lt;p&gt;For example, calling &lt;code&gt;ls -al&lt;/code&gt; will show the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-21-at-8.39.44%E2%80%AFPM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6it3upx01v53r5bj0uo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see the standard &lt;code&gt;ls -al&lt;/code&gt; style output but with excellent highlighting, as well as the introduction of git status (between the date time and the icon) and the icons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; &lt;code&gt;eza&lt;/code&gt; expects you to be using a &lt;a href="https://www.nerdfonts.com" rel="noopener noreferrer"&gt;nerdfont&lt;/a&gt;, which you will install as part of installing Starship, but if you chose not to do so, you will want to do that separately. I use &lt;a href="https://www.programmingfonts.org/#source-code-pro" rel="noopener noreferrer"&gt;SauceCodePro&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  See Everything
&lt;/h3&gt;

&lt;p&gt;Now that you have &lt;code&gt;eza&lt;/code&gt; installed, you can setup &lt;code&gt;fzf&lt;/code&gt; previews, which can be context aware, similar to the file and directory lists shown when tab completing those.&lt;/p&gt;

&lt;p&gt;For example, with the integration of &lt;code&gt;eza&lt;/code&gt; and &lt;code&gt;fzf&lt;/code&gt;, that list of directories for &lt;code&gt;cd&lt;/code&gt; can include a tree-based preview of the selected directory. To invoke &lt;code&gt;fzf&lt;/code&gt; you use the placeholder &lt;code&gt;**&lt;/code&gt; and then hit &lt;code&gt;&amp;lt;tab&amp;gt;&lt;/code&gt;, e.g. &lt;code&gt;cd**&lt;/code&gt; could show the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-22-at-12.43.38%E2%80%AFAM.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprlbceuvbuej1zh762bj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then make your selection using the up/down arrow keys and hit &lt;code&gt;&amp;lt;enter&amp;gt;&lt;/code&gt; to replace the &lt;code&gt;**&lt;/code&gt; with the selected directory.&lt;/p&gt;

&lt;p&gt;With some additional configuration, and a simple script, we can show either a directory tree preview, or use &lt;code&gt;bat&lt;/code&gt; to show a syntax highlighted preview of the file contents.&lt;/p&gt;

&lt;p&gt;Add the following to your &lt;code&gt;.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; _fzf_comprun&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;local command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
   &lt;span class="nb"&gt;shift

   &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
     &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'eza --tree --color=always {} | head -200'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
     &lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s2"&gt;"eval 'echo &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;'{}"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
     ssh&lt;span class="p"&gt;)&lt;/span&gt; fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'dig {}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
     &lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;bat&lt;span class="p"&gt;)&lt;/span&gt; fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'bat -n --color=always {}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
     &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'$HOME/bin/fzf-preview.sh {}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
   &lt;span class="k"&gt;esac&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following as an executable script at &lt;code&gt;$HOME/bin/fzf-preview.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;eza &lt;span class="nt"&gt;--tree&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="nt"&gt;--icons&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="nt"&gt;--git&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-200&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;bat &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we can override the default &lt;code&gt;&amp;lt;tab&amp;gt;&lt;/code&gt; completion behavior to use &lt;code&gt;fzf&lt;/code&gt; with preview by adding the following configuration (and using the same &lt;code&gt;fzf-preview.sh&lt;/code&gt; script above) by adding the following to your &lt;code&gt;.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="c"&gt;# Enable using fzf preview with eza when using tab completion with `cd`&lt;/span&gt;
 zstyle &lt;span class="s1"&gt;':completion:*'&lt;/span&gt; menu no
 zstyle &lt;span class="s1"&gt;':fzf-tab:complete:*'&lt;/span&gt; fzf-preview &lt;span class="s1"&gt;'$HOME/bin/fzf-preview.sh $realpath'&lt;/span&gt;
 zstyle &lt;span class="s1"&gt;':fzf-tab:complete:cd:*'&lt;/span&gt; fzf-preview &lt;span class="s1"&gt;'eza --tree --color=always --icons=always --git $realpath | head -200'&lt;/span&gt;
 zstyle &lt;span class="s1"&gt;':fzf-tab:*'&lt;/span&gt; switch-group &lt;span class="s1"&gt;'&amp;lt;'&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reaching Nerdvana
&lt;/h2&gt;

&lt;p&gt;Now that you know all the pieces, you need to put them together, which is why I have made my entire &lt;code&gt;.zshrc&lt;/code&gt; available &lt;a href="https://gist.github.com/dshafik/67fe3e0ba5096a00c91cccb0792a884b#file-zshrc" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this &lt;code&gt;.zshrc&lt;/code&gt; you will have the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starship prompt&lt;/li&gt;
&lt;li&gt;command syntax highlighting&lt;/li&gt;
&lt;li&gt;automatic suggestions for completions as you type&lt;/li&gt;
&lt;li&gt;substring search using the up/down keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zoxide&lt;/code&gt; for a better &lt;code&gt;cd&lt;/code&gt; experience (aliased)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eza&lt;/code&gt; for a better &lt;code&gt;ls&lt;/code&gt; experience (aliased)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fd&lt;/code&gt; for better file search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fzf&lt;/code&gt; for fuzzy finding and previews with &lt;code&gt;&amp;lt;tab&amp;gt;&lt;/code&gt; activation for directories and files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see a demo of all of these features below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/xGT6D1sI6cpsv8lBaOeRrfcXt" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2FxGT6D1sI6cpsv8lBaOeRrfcXt.svg" alt="asciicast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While you don’t need to adopt all of these tools and features, together they create a powerful terminal experience.&lt;/p&gt;

&lt;p&gt;Is there something missing? Let me know in the comments below!&lt;/p&gt;

&lt;p&gt;If you want to learn more about &lt;code&gt;bat&lt;/code&gt;, used to show syntax highlighted file previews, and other developer-focused tools, check out my earlier post: &lt;a href="https://www.daveyshafik.com/archives/70845-five-command-line-tools-for-developers.html" rel="noopener noreferrer"&gt;Five Must-Have Command Line Tools for Developers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>macos</category>
      <category>terminal</category>
      <category>zsh</category>
    </item>
    <item>
      <title>Five Must-Have Command Line Tools for Developers</title>
      <dc:creator>Davey Shafik</dc:creator>
      <pubDate>Tue, 21 May 2024 00:49:48 +0000</pubDate>
      <link>https://dev.to/dshafik/five-must-have-command-line-tools-for-developers-1hl6</link>
      <guid>https://dev.to/dshafik/five-must-have-command-line-tools-for-developers-1hl6</guid>
      <description>&lt;p&gt;There are many tools that I use to improve my terminal experience, these are the top five that I use every single day to make developing code easier, quicker, and more enjoyable.&lt;/p&gt;

&lt;h2&gt;
  
  
  ack
&lt;/h2&gt;

&lt;p&gt;Starting with the most well known first, &lt;code&gt;ack&lt;/code&gt; is like &lt;code&gt;grep&lt;/code&gt; but optimized for programmers, it is both faster and simpler than &lt;code&gt;grep&lt;/code&gt;, and has better results.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ack&lt;/code&gt; has several quality of life improvements by default: recursive by default (but ignores VCS folders like .git), better output display including highlighting, and it uses &lt;a href="https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions"&gt;PCRE&lt;/a&gt; for pattern matching.&lt;/p&gt;

&lt;p&gt;Just compare the &lt;em&gt;default&lt;/em&gt; output for &lt;code&gt;grep -R&lt;/code&gt; and &lt;code&gt;ack&lt;/code&gt; (respectively):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-20-at-3.36.33%E2%80%AFPM.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fml3j95zhxs300jfz4vqh.png" width="800" height="303"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Search for “Interface” in the “src” directory using grep: grep -R Interface src&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-20-at-3.36.55%E2%80%AFPM.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yqdo4f6t3d3p0rltnia.png" width="800" height="757"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same search using ack: ack Interface src&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sure, the output for &lt;code&gt;ack&lt;/code&gt; takes up far more space, but it groups matches by filename (highlighted), adds line numbers, and highlights the matching text.&lt;/p&gt;

&lt;p&gt;Additionally, you can easily include specific file types using the &lt;code&gt;-t&lt;/code&gt; or &lt;code&gt;--type=&lt;/code&gt; flags, e.g. to only search PHP file types which includes &lt;code&gt;.php .phpt .php3 .php4 .php5 .phtml&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; files with a PHP shebang (e.g. &lt;code&gt;#!/usr/bin/env php&lt;/code&gt;) on the first line you can use &lt;code&gt;-t php&lt;/code&gt; or &lt;code&gt;--type=php&lt;/code&gt;. You can also use the type name itself as a flag instead e.g. &lt;code&gt;--php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can easily expand these types by adding a &lt;code&gt;.ackrc&lt;/code&gt; file to your HOME directory and with a few lines either add additional file matches, or add a new file type entirely. For example, you can use &lt;code&gt;--type-add&lt;/code&gt; to add &lt;code&gt;composer.json&lt;/code&gt; (specifically) as a filename to search when searching through &lt;code&gt;php&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--type-add=php:is:composer.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create a new file type for &lt;code&gt;.env&lt;/code&gt; files called &lt;code&gt;env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--type-set=env:ext:env
--type-add=env:match:/^.env.*$/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to create a new file type for blade templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--type-set=blade:match:/.blade.php$/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these in place a search for &lt;code&gt;ack --php dshafik/bag&lt;/code&gt; will return results from &lt;code&gt;composer.json&lt;/code&gt;, while a search for &lt;code&gt;ack --blade someVariables&lt;/code&gt; will only match results in &lt;code&gt;*.blade.php&lt;/code&gt; files, and &lt;code&gt;ack --env APP&lt;/code&gt; will match all lines containing &lt;code&gt;APP&lt;/code&gt; in your &lt;code&gt;.env*&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  delta
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;delta&lt;/code&gt; describes itself as “a syntax-highlighting pager for git, diff, grep, and blame output”, but I find that to be somewhat ambiguous: it is a syntax-highlighting pager for &lt;code&gt;git diff&lt;/code&gt;, &lt;code&gt;git grep&lt;/code&gt;, and &lt;code&gt;git blame&lt;/code&gt; output. It will actually be used by all git operations that output diffs, not just &lt;code&gt;git diff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Integration with &lt;code&gt;git&lt;/code&gt; is easy, just add the following to the &lt;code&gt;.gitconfig&lt;/code&gt; file in your HOME directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[core]&lt;/span&gt;
&lt;span class="py"&gt;pager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;delta&lt;/span&gt;

&lt;span class="nn"&gt;[interactive]&lt;/span&gt;
&lt;span class="py"&gt;diffFilter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;delta --color-only&lt;/span&gt;

&lt;span class="nn"&gt;[delta]&lt;/span&gt;
&lt;span class="py"&gt;navigate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true # use n and N to move between diff sections&lt;/span&gt;

&lt;span class="c"&gt;# delta detects terminal colors automatically; set one of these to disable auto-detection
# dark = true
# light = true
&lt;/span&gt;
&lt;span class="nn"&gt;[merge]&lt;/span&gt;
&lt;span class="py"&gt;conflictstyle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;diff3&lt;/span&gt;

&lt;span class="nn"&gt;[diff]&lt;/span&gt;
&lt;span class="py"&gt;colorMoved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally, I also like to add &lt;code&gt;line-numbers = true&lt;/code&gt; to the &lt;code&gt;[delta]&lt;/code&gt; configuration to enable line numbers.&lt;/p&gt;

&lt;p&gt;Once you’ve completed this, &lt;code&gt;git&lt;/code&gt;will start to output much easier to read diffs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-20-at-4.35.42%E2%80%AFPM.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffiecf9053qo6lably77y.png" width="800" height="429"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;delta output with git diff&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see here, it calls out the filename at the top, and is smart enough to show the relevant code structure that contains the change and the line on which the diff context starts, in this case the namespace, starting from line 7, but it could also be a class or a function or method. It also features per-character diff highlighting, on top of the standard line highlighting — in this case you can see that I added a new value to the Attribute options bit mask.&lt;/p&gt;
&lt;h2&gt;
  
  
  httpie
&lt;/h2&gt;

&lt;p&gt;The next tool has been in my toolkit for a long time, &lt;em&gt;httpie&lt;/em&gt;, a python tool for making HTTP requests. It is usually referred to as “curl for humans”. Once installed, &lt;em&gt;httpie&lt;/em&gt; is available as the &lt;code&gt;http&lt;/code&gt; command, which then folllows a syntax similar to the HTTP protocol itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http &amp;lt;METHOD&amp;gt; &amp;lt;URL&amp;gt; &amp;lt;HEADER&amp;gt;:&amp;lt;HEADER VALUE&amp;gt; &amp;lt;HEADER2&amp;gt;:&amp;lt;HEADER2-VALUE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for example, this will display the response from &lt;a href="https://bagvalueobjects.com"&gt;bagvalueobjects.com&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http GET https://bagvalueobjects.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, it will show the response headers and the response body, but this is easy to change using the &lt;code&gt;-p&lt;/code&gt; flag. The following will show &lt;em&gt;only&lt;/em&gt; the request and response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http -pHh GET https://bagvalueobjects.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value for &lt;code&gt;-p&lt;/code&gt; are &lt;code&gt;H&lt;/code&gt; (request headers), &lt;code&gt;h&lt;/code&gt; (response headers), &lt;code&gt;B&lt;/code&gt; (request body), &lt;code&gt;b&lt;/code&gt; (response body).&lt;/p&gt;

&lt;p&gt;Request headers and body? Well, yes, you can send POST requests just as easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http -pHhBb POST http://httpbin.org/post foo=bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that we have a new argument at the end &lt;code&gt;foo=bar&lt;/code&gt;, this will send a JSON body with the value &lt;code&gt;{"foo":"bar"}&lt;/code&gt;. You can add as many properties as you like to the end of the command. You can also pass the &lt;code&gt;-f&lt;/code&gt; or &lt;code&gt;--form&lt;/code&gt; argument and it will send the key-value pairs as &lt;code&gt;multipart/form-data&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;If you need to send complex JSON, use &lt;code&gt;:=&lt;/code&gt; instead, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http -pHhBb POST http://httpbin.org/post foo:='["bar", "baz", "bat"]'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will send the body &lt;code&gt;{"foo":["bar", "baz", "bat"]}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are tons of other options including being able to use files as the post body, send files, support for sessions, authentication and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  gron
&lt;/h2&gt;

&lt;p&gt;Many folks are familiar with &lt;code&gt;jq&lt;/code&gt;, a great command line utility for working with JSON, if you’re not familiar with it, I highly recommend that you check it out.&lt;/p&gt;

&lt;p&gt;Sometimes however, &lt;code&gt;jq&lt;/code&gt; is just too complicated, or the result isn’t exactly usable. Sometimes, you just want to be able &lt;code&gt;ack&lt;/code&gt; (née &lt;code&gt;grep&lt;/code&gt;) some JSON for a specific value. &lt;code&gt;gron&lt;/code&gt; will break down JSON into valid Javascript assignments, for example, the simple body from our &lt;em&gt;httpie&lt;/em&gt; example above might looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this creates a variable &lt;code&gt;json&lt;/code&gt;, and assigns an empty object to it, it them sets the &lt;code&gt;foo&lt;/code&gt; key to an empty array, and then sets each of the numeric keys to it’s specific value.&lt;/p&gt;

&lt;p&gt;Because it’s broken down to one assignment per line, you can now easily and reliable pipe it through ack or grep and find whatever you need.&lt;/p&gt;

&lt;p&gt;A more practical example: list all the require-dev requirements in a composer.json files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gron composer.json | ack require-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;captainhook/captainhook-phar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^5.23&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;captainhook/hook-installer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fakerphp/faker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.23&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;infection/infection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^0.28.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;larastan/larastan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;laravel/pint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orchestra/testbench&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^8.0|^9.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phpunit/phpunit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^10.5|^11&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ramsey/conventional-commits&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev-allow-sf-7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;roave/security-advisories&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev-latest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;require-dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;symfony/var-dumper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then turn this back into JSON, and feed it into &lt;code&gt;jq&lt;/code&gt; to get the values using &lt;code&gt;gron -u&lt;/code&gt; or &lt;code&gt;gron --ungron&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gron composer.json | ack require-dev | gron -u | jq '."require-dev" | keys[]'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"captainhook/captainhook-phar"
"captainhook/hook-installer"
"fakerphp/faker"
"infection/infection"
"larastan/larastan"
"laravel/pint"
"orchestra/testbench"
"phpunit/phpunit"
"ramsey/conventional-commits"
"roave/security-advisories"
"symfony/var-dumper"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, let’s use &lt;code&gt;gron&lt;/code&gt; to &lt;em&gt;remove&lt;/em&gt; any custom scripts from our composer.json. We can use this by again, piping the output through &lt;code&gt;ack&lt;/code&gt;, but this time using the &lt;code&gt;-v&lt;/code&gt; argument to exclude matches, and then passing it back to &lt;code&gt;gron -u&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gron composer.json | ack -v json.scripts | gron -u
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting JSON will be identical to the original, but with the entire &lt;code&gt;scripts&lt;/code&gt; key missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  bat
&lt;/h2&gt;

&lt;p&gt;The last tool I want to cover is a new addition to my local environment, and that is &lt;code&gt;bat&lt;/code&gt;, a replacement for &lt;code&gt;cat&lt;/code&gt; with syntax highlighting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bat&lt;/code&gt; has a few advantages over &lt;code&gt;cat&lt;/code&gt; besides syntax highlighting by default: the output is paged, includes the file name, and line numbers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.daveyshafik.com/wp-content/uploads/2024/05/Screenshot-2024-05-20-at-5.28.54%E2%80%AFPM.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu44dtyfs6ur6zkopn8aq.png" width="800" height="429"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;syntax highlighted code using bat&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;bat&lt;/code&gt; doesn’t provide as much utility as most of the other tools on this list, it’s simplicity makes it perfect as a drop-in replacement for &lt;code&gt;cat&lt;/code&gt;, allowing you to just add &lt;code&gt;alias cat=bat&lt;/code&gt; to your terminal configuration and forget about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;If you want to try any of these tools for yourself, they are all available using &lt;code&gt;brew&lt;/code&gt;, which is available on all platforms.&lt;/p&gt;

&lt;p&gt;This list was aimed specifically at improving your development workflows, however, I have also made many changes to my terminal environment as a whole which also aids me in getting my work done faster, and easier. If you want to hear more, let me know in the comments below!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>macos</category>
      <category>terminal</category>
      <category>cli</category>
    </item>
    <item>
      <title>Introducing Bag 1.0: Immutable Values Objects for PHP</title>
      <dc:creator>Davey Shafik</dc:creator>
      <pubDate>Sun, 19 May 2024 02:15:44 +0000</pubDate>
      <link>https://dev.to/dshafik/introducing-bag-immutable-values-objects-for-php-gmj</link>
      <guid>https://dev.to/dshafik/introducing-bag-immutable-values-objects-for-php-gmj</guid>
      <description>&lt;p&gt;For the last couple of years I’ve been using Value Objects in my projects to bring language-level strict types to what would typically be array data structures in my code. From method inputs to JSON API responses, value objects have almost entirely replaced arrays throughout. The ability to get runtime type checking and IDE auto-complete has eliminated many potential bugs, from key typos, to assigning an incorrectly typed value by accident: what type is an “amount” property in a credit card transaction API response? An integer of cents (or other minor units), a Money object such as &lt;a href="https://github.com/brick/money"&gt;brick/money&lt;/a&gt; or &lt;a href="https://github.com/moneyphp/money"&gt;moneyphp/money&lt;/a&gt;? Or worst of all, a float?&lt;/p&gt;

&lt;p&gt;About 18 months ago, I started using the excellent spatie/laravel-data v3 package for a new project, but I quickly realized there were a few features missing, most notably, factory support. Additionally, the collection class didn’t extend the Laravel Collection class and is anemic by comparison.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; spatie/laravel-data v4 adds support for factories, and has a slightly more capable &lt;code&gt;DataCollection&lt;/code&gt; class. See below for details.&lt;/p&gt;

&lt;p&gt;So I extended the base &lt;code&gt;Data&lt;/code&gt; class and added factory support, following a similar pattern to Eloquent factories, including support for Sequences, and I extended &lt;code&gt;DataCollection&lt;/code&gt; to add some missing functionality, and mostly, it was good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Spatie/Laravel-Data v4
&lt;/h2&gt;

&lt;p&gt;Earlier this year, Spatie released v4 with support for Laravel 11 (and up until last month, no support for Laravel 11 in v3), with significant changes, including support for Factories, and a better &lt;code&gt;DataCollection&lt;/code&gt; class. Unfortunately, the Factories built into v4 were incompatible with my own, and didn’t have the same feature set, and while better, the updated &lt;code&gt;DataCollection&lt;/code&gt; class was still lackluster.&lt;/p&gt;

&lt;p&gt;The upgrade process was difficult, and while ultimately successful, I was unhappy with the outcome. Then I decided that I would much prefer if my value objects were immutable, which was impossible using either v3 or v4, and ultimately, that, along with the difficult upgrade path to v4, led me to create &lt;strong&gt;&lt;a href="https://www.bagvalueobjects.com"&gt;Bag&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Bag?
&lt;/h2&gt;

&lt;p&gt;Bag is a new library built from scratch — inspired by spatie/laravel-data — that provides immutable value objects for PHP. Built on top of Laravel’s excellent Validation and Collection classes, as well as Eloquent Factory Sequences, it is the value object library I wanted spatie/laravel-data to be.&lt;/p&gt;

&lt;p&gt;Additionally, I leaned harder into the use of Attributes, for identifying Collection classes to be used for each value object class, wrapping, hiding data in both &lt;code&gt;toArray()&lt;/code&gt; and &lt;code&gt;toJson()&lt;/code&gt;/&lt;code&gt;jsonSerialize()&lt;/code&gt;, and for identifying transformers (what Spatie calls “&lt;a href="https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/creating-a-data-object#content-magical-creation"&gt;magical data object creation&lt;/a&gt;“).&lt;/p&gt;

&lt;p&gt;I simplified input/output casting (as opposed to casting being for inputs, and transformers being for output),&lt;/p&gt;

&lt;p&gt;I also added support for Variadics, something that the Spatie library does not allow. For a more detailed comparison of the two libraries, see &lt;a href="https://bagvalueobjects.com/why.html#compared-to-spatie-laravel-data"&gt;the Bag documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Despite having a few more features, simple benchmarks of Bag do show it as being about 40-45% faster than spatie/laravel-data v4, and a whopping 70-78% faster than v3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark Methodology
&lt;/h3&gt;

&lt;p&gt;The benchmark script was intentionally very simple, using Laravel’s &lt;code&gt;Benchmark&lt;/code&gt; class to get the average of 10 runs of a loop (default: 1000 iterations) that creates instances of a Bag or Spatie value object. I ran it both with 1,000 iterations and 10,000 iterations.&lt;/p&gt;

&lt;p&gt;The value objects have the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Class-level Input/Output Name Mapping to/from SnakeCase&lt;/li&gt;
&lt;li&gt;A single property input name mapping from CamelCase&lt;/li&gt;
&lt;li&gt;A single property input name mapping from an alias&lt;/li&gt;
&lt;li&gt;A property with integer and required validations&lt;/li&gt;
&lt;li&gt;A property input/output case from/to DateTime/formatted date string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see all the code for the benchmarks &lt;a href="https://gist.github.com/dshafik/0c94d445ef1432527ffaf57d4301f3cc"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Current results look like the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Iterations&lt;/th&gt;
&lt;th&gt;Bag&lt;/th&gt;
&lt;th&gt;Spatie v3&lt;/th&gt;
&lt;th&gt;Spatie v4&lt;/th&gt;
&lt;th&gt;Difference (ms)&lt;/th&gt;
&lt;th&gt;Difference (% Faster)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;427.693ms&lt;/td&gt;
&lt;td&gt;975.182ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-547.489&lt;/td&gt;
&lt;td&gt;+78.63%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;429.019ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;679.554ms&lt;/td&gt;
&lt;td&gt;-250.535&lt;/td&gt;
&lt;td&gt;+45.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;4,663.161ms&lt;/td&gt;
&lt;td&gt;9,906.135ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-5,242.974&lt;/td&gt;
&lt;td&gt;+71.96%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;4,483.669ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;6,914.524ms&lt;/td&gt;
&lt;td&gt;-2,430.851&lt;/td&gt;
&lt;td&gt;+42.68%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example Benchmark Data&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;If you want to try out Bag, check out the Getting Started documentation. &lt;/p&gt;

&lt;p&gt;For Bag, currently I consider it to be feature complete and have released v1.0.0. I am currently working on support for Bag-less value objects: that is, support for using any class as a value object — if you want to contribute to this idea, you can &lt;a href="https://github.com/dshafik/bag/issues/30"&gt;comment on the RFC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And with that, I’ll leave you with the adorable mascot for Bag for you to enjoy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bcdsxlytbxr6ae3kayb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bcdsxlytbxr6ae3kayb.png" alt="The Bag Mascot" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bag</category>
      <category>library</category>
      <category>php</category>
    </item>
  </channel>
</rss>
