Jekyll2024-01-10T20:44:24+00:00http://joshfrankel.me/feed.xmlDevelopment SimplifiedA blog about ruby, sql, performance, and patterns. By Josh Frankel.
Josh Frankel (@joshmfrankel)http://joshfrankel.me/Assignment destructuring; and my other favorite Ruby 3 features2024-01-10T00:00:00+00:002024-01-10T00:00:00+00:00http://joshfrankel.me/blog/assignment-destructuring-and-my-other-favorite-ruby-3-features<p>With the advent of Ruby 3, we’ve got a list of notable features. From security and performance improvements to Static Analysis, Ruby continues to mature and truly stay an exciting ecosystem to work within. Let’s dig into some of my favorite Ruby 3 features.
<!--excerpt--></p>
<h2 id="short-hand-keyword-arguments">Short-hand keyword arguments</h2>
<p>I value intention revealing argument and method names. One pattern I’ve often run into is having to pass the same named keyword argument as the variable I am utilizing. For example:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">my_method</span><span class="p">(</span><span class="n">first</span><span class="p">:)</span>
<span class="nb">puts</span> <span class="n">first</span>
<span class="k">end</span>
<span class="c1"># Some time later we have a variable</span>
<span class="n">first</span> <span class="o">=</span> <span class="s2">"the first"</span>
<span class="n">my_method</span><span class="p">(</span><span class="ss">first: </span><span class="n">first</span><span class="p">)</span>
</code></pre></div></div>
<p>This ends up with the redundant syntax <code class="language-plaintext highlighter-rouge">(first: first)</code>.</p>
<p>Instead, with short-hand keyword arguments we can remove the passed in variable if that variable has the same name as the named argument.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">first</span> <span class="o">=</span> <span class="s2">"the first"</span>
<span class="n">my_method</span><span class="p">(</span><span class="n">first</span><span class="p">:)</span> <span class="c1"># Nice and clean</span>
</code></pre></div></div>
<h2 id="hash-destructuring">Hash destructuring</h2>
<p>This is a feature I’ve been wanting since I’ve used <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">JavaScript’s destructuring assignment</a>. It allows you to shorthand Hash structures into easily utilized variables.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">my_hash</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">first: </span><span class="s2">"the first"</span><span class="p">,</span> <span class="ss">second: </span><span class="s2">"not first"</span><span class="p">}</span>
<span class="n">my_hash</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">first: </span><span class="p">}</span>
<span class="c1"># First is now a variable</span>
<span class="n">first</span> <span class="c1">#=> "the first"</span>
</code></pre></div></div>
<p>This syntax uses the hash rocket or rightward assignment operator in order to destructure the Hash. You can even take this one step further with a one-liner:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">my_hash</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">first: </span><span class="s2">"the first"</span><span class="p">,</span> <span class="ss">second: </span><span class="s2">"not first"</span> <span class="p">}</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">first: </span><span class="p">}</span>
<span class="c1"># First is still a variable</span>
<span class="n">first</span> <span class="c1">#=> "the first"</span>
<span class="c1"># So is my_hash</span>
<span class="n">my_hash</span> <span class="c1">#=> {:first=>"the first", :second=>"not first"}</span>
</code></pre></div></div>
<h2 id="anonymous-argument-passing">Anonymous argument passing</h2>
<p>Anytime you utilize the splat syntax <code class="language-plaintext highlighter-rouge">*</code> for positional arguments or <code class="language-plaintext highlighter-rouge">**</code>, if you had a child method call you’d have to specify the exact arguments. This is even when you just want to ferry the anonymous arguments onto the next method. With Ruby 3.2 you can now continually pass anonymous arguments just how you’d expect:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">my_method</span><span class="p">(</span><span class="o">**</span><span class="p">)</span>
<span class="n">my_child_method</span><span class="p">(</span><span class="o">**</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">my_child_method</span><span class="p">(</span><span class="o">**</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="o">**</span><span class="p">)</span> <span class="c1"># Note: The parenthesis are important for utilizing anonymous arguments as it won't work otherwise</span>
<span class="k">end</span>
<span class="n">my_method</span><span class="p">(</span><span class="ss">first: </span><span class="s2">"the first"</span><span class="p">,</span> <span class="ss">second: </span><span class="s2">"not first"</span><span class="p">)</span>
<span class="c1">#=> {:first=>"the first", :second=>"not first"}</span>
</code></pre></div></div>
<h2 id="looking-ahead">Looking ahead</h2>
<p>I’d love to spend some time with TypeProf and rbs files for the newly added static analysis. Additionally, Ractor and Fiber would be great concepts to learn as a futher way of expanding different approaches to coding challenges in Ruby.</p>
<p>Looking forward to what the future brings for Ruby!</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/With the advent of Ruby 3, we’ve got a list of notable features. From security and performance improvements to Static Analysis, Ruby continues to mature and truly stay an exciting ecosystem to work within. Let’s dig into some of my favorite Ruby 3 features.Using Turbo Frame tags within a ViewComponent2023-10-06T00:00:00+00:002023-10-06T00:00:00+00:00http://joshfrankel.me/blog/using-turbo-frame-tags-within-a-view-component<p>ViewComponents and Hotwire are incredible for orchestrating a beautiful, reusable View layer. While working with the two, I came across a case where I wanted a component to update via a Turbo Frame but was unable to utilize the <strong>turbo-rails</strong> gem’s built-in <code class="language-plaintext highlighter-rouge">turbo_frame_tag</code>. Let’s get into this simple fix.
<!--excerpt--></p>
<p>What we need here is access to the <code class="language-plaintext highlighter-rouge">turbo_frame_tag</code> from all ViewComponent html.erb files. We can
easily enable this by including the following module into the base ApplicationComponent. Here’s
an example of a working version below:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicationComponent</span> <span class="o"><</span> <span class="no">ViewComponent</span><span class="o">::</span><span class="no">Base</span>
<span class="kp">include</span> <span class="no">Turbo</span><span class="o">::</span><span class="no">FramesHelper</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>
<p>With the above code we can now access the frame tag within any of our templates:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- my_component.html.erb --></span>
<span class="nt"><</span><span class="err">%=</span> <span class="na">turbo_frame_tag</span> <span class="err">...</span>
</code></pre></div></div>
<p>I discovered this not through the documentation (as I couldn’t find it) but using
Github’s find symbol in project functionality. This lead me to the <a href="https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb">definition location</a>
for <code class="language-plaintext highlighter-rouge">turbo_frame_tag</code> which gave me the proper module to include.</p>
<p>Good luck and start making those ViewComponents even snappier.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/ViewComponents and Hotwire are incredible for orchestrating a beautiful, reusable View layer. While working with the two, I came across a case where I wanted a component to update via a Turbo Frame but was unable to utilize the turbo-rails gem’s built-in turbo_frame_tag. Let’s get into this simple fix.Grouping SQL results by month2023-09-19T00:00:00+00:002023-09-19T00:00:00+00:00http://joshfrankel.me/blog/grouping-sql-results-by-month<p>When working with big datasets, it is useful to be able to investigate correlations between
records. Knowing most popular components of an application or highest usage of a feature can help a team prioritize their efforts. One technique I’ve used is to group results by month to visualize the trend line
of a query.
<!--excerpt--></p>
<h2 id="visualizing-data">Visualizing data</h2>
<p>Being able to visualize data is extremely useful. I recommend <a href="https://www.metabase.com/">Metabase</a> for analyzing your data. If you configure this I would also recommend that you have it running of a follower database. This is a database that matches production but is only used for read only procedures. Also it won’t slow down production since it is a copy of what is live.</p>
<p>When communicating with your team, being able to visualize data allows for easier communication. Showing numbers can be harder to understand the importance of versus a graph showing trend over time.</p>
<h2 id="setting-the-stage">Setting the stage</h2>
<p>For this article, let’s use a simple blog app as our architecture here. We’re only going to utilize a single database table <strong>Posts</strong>. Using this data we want to answer the following question, “How many Posts are published each month?”.</p>
<p>The first step I take is to build the foundational query. This will retrieve all
posts and return a total count.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="n">posts</span><span class="p">.</span><span class="n">id</span><span class="p">)</span> <span class="k">as</span> <span class="n">total_posts</span>
<span class="k">FROM</span> <span class="n">posts</span>
</code></pre></div></div>
<h2 id="truncating-timestamps">Truncating Timestamps</h2>
<p>Now that we have the basic query above we can calculate posts per month. There are several
different approaches to solving this but we’re going to focus on the <code class="language-plaintext highlighter-rouge">DATE_TRUNC</code> technique. This
Postgres function allows us to take a timestamps and truncate it to the nearest unit of time we specify. We also need to group our results by the truncated date so that the aggregate <code class="language-plaintext highlighter-rouge">COUNT</code> can still be utilized. So from the above if we want to truncate our published_at date by month we can write the following query:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span>
<span class="k">COUNT</span><span class="p">(</span><span class="n">posts</span><span class="p">.</span><span class="n">id</span><span class="p">)</span> <span class="k">as</span> <span class="n">total_posts</span><span class="p">,</span>
<span class="n">DATE_TRUNC</span><span class="p">(</span><span class="s1">'month'</span><span class="p">,</span> <span class="n">posts</span><span class="p">.</span><span class="n">published_at</span><span class="p">)</span> <span class="k">as</span> <span class="k">month</span>
<span class="k">FROM</span> <span class="n">posts</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="k">month</span>
</code></pre></div></div>
<p>The above will split results out by month giving you output that looks like the following
table:</p>
<table>
<thead>
<tr>
<th>Month</th>
<th>Total Posts</th>
</tr>
</thead>
<tbody>
<tr>
<td>September 1, 2023 12:00am</td>
<td>30</td>
</tr>
<tr>
<td>August 1, 2023 12:00am</td>
<td>42</td>
</tr>
<tr>
<td>July 1, 2023 12:00am</td>
<td>66</td>
</tr>
</tbody>
</table>
<p>What this essentially did was take every <strong>posts.published_at</strong> and truncate them to the first
of the month for the month they resided in. We’ve now answered our first question, “How many Posts are published each month”.</p>
<p>There’s a gotcha here though. Over time the number of Posts published grows and grows. At some point in the future there are so many Posts that running this query become non-performant. Luckily we can constraint it by time interval to look at a subset of all available data.</p>
<h2 id="using-intervals">Using Intervals</h2>
<p>Intervals refer to a specific period of time which can be used to calculate offset dates. We can use this to constrain our results to the last 6 months with the below sql:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span>
<span class="k">COUNT</span><span class="p">(</span><span class="n">posts</span><span class="p">.</span><span class="n">id</span><span class="p">)</span> <span class="k">as</span> <span class="n">total_posts</span><span class="p">,</span>
<span class="n">DATE_TRUNC</span><span class="p">(</span><span class="s1">'month'</span><span class="p">,</span> <span class="n">posts</span><span class="p">.</span><span class="n">published_at</span><span class="p">)</span> <span class="k">as</span> <span class="k">month</span>
<span class="k">FROM</span> <span class="n">posts</span>
<span class="k">WHERE</span> <span class="n">posts</span><span class="p">.</span><span class="n">published_at</span> <span class="o">></span> <span class="k">CURRENT_DATE</span> <span class="o">-</span> <span class="n">INTERVAL</span> <span class="s1">'6 months'</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="k">month</span>
</code></pre></div></div>
<p>The new where clause <code class="language-plaintext highlighter-rouge">WHERE posts.published_at > CURRENT_DATE - INTERVAL '6 months'</code> ensures that all published posts are only within the last 6 months of the current date set by postgres. This addition allows us to keep our results efficient and quick.</p>
<p>If you place the above query into a system like Metabase, set the x-axis to <strong>month</strong> and the y-axis to <strong>total posts</strong> you can end up with visualizations like the screenshot below. This makes it easy to see trends and possible correlations over time.</p>
<p><img src="/img/2023/metabase-month-graph.png" alt="Metabase graph of Total posts by month" /></p>
<p>You can utilize the above for more complex statistics and datasets. Got another timestamp trick you like to use? Let me know in the comments below.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/When working with big datasets, it is useful to be able to investigate correlations between records. Knowing most popular components of an application or highest usage of a feature can help a team prioritize their efforts. One technique I’ve used is to group results by month to visualize the trend line of a query.When is an Array an Array? Strategies for checking Array equality in Ruby2023-07-28T00:00:00+00:002023-07-28T00:00:00+00:00http://joshfrankel.me/blog/when-is-an-array-an-array-strategies-for-checking-array-equality-in-ruby<p>Object equality is an interesting topic. You can check for matching values (with or without ordering). You can
also check to see if the object has the same in-memory id. I’ve written code to diff two arrays before but
I’ve never sat down to think about all the ways to accomplish this. Also I was able to explore what I believe
are the most optimal approaches.</p>
<!--excerpt-->
<h2 id="basic-equality">Basic equality</h2>
<p>The most basic example of checking two objects is the equality method <code class="language-plaintext highlighter-rouge">==</code>. This is a common check done between Ruby
objects and Array is no different. So what exactly does it do? According to the documentation, it will check the <code class="language-plaintext highlighter-rouge">.size</code>
of both arrays along with each individual index value against the other array. Looking at the <a href="https://ruby-doc.org/3.2.2/Array.html#method-i-3D-3D">source code for == in Ruby 3.2.2</a>
we can see this at work:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rb_ary_equal</span><span class="p">(</span><span class="no">VALUE</span> <span class="n">ary1</span><span class="p">,</span> <span class="no">VALUE</span> <span class="n">ary2</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ary1</span> <span class="o">==</span> <span class="n">ary2</span><span class="p">)</span> <span class="k">return</span> <span class="no">Qtrue</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="no">RB_TYPE_P</span><span class="p">(</span><span class="n">ary2</span><span class="p">,</span> <span class="no">T_ARRAY</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">rb_respond_to</span><span class="p">(</span><span class="n">ary2</span><span class="p">,</span> <span class="n">idTo_ary</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">Qfalse</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">rb_equal</span><span class="p">(</span><span class="n">ary2</span><span class="p">,</span> <span class="n">ary1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="no">RARRAY_LEN</span><span class="p">(</span><span class="n">ary1</span><span class="p">)</span> <span class="o">!=</span> <span class="no">RARRAY_LEN</span><span class="p">(</span><span class="n">ary2</span><span class="p">))</span> <span class="k">return</span> <span class="no">Qfalse</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="no">RARRAY_CONST_PTR_TRANSIENT</span><span class="p">(</span><span class="n">ary1</span><span class="p">)</span> <span class="o">==</span> <span class="no">RARRAY_CONST_PTR_TRANSIENT</span><span class="p">(</span><span class="n">ary2</span><span class="p">))</span> <span class="k">return</span> <span class="no">Qtrue</span><span class="p">;</span>
<span class="k">return</span> <span class="n">rb_exec_recursive_paired</span><span class="p">(</span><span class="n">recursive_equal</span><span class="p">,</span> <span class="n">ary1</span><span class="p">,</span> <span class="n">ary2</span><span class="p">,</span> <span class="n">ary2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The first conditional checks to see if the arrays are identical in memory:</p>
<p><code class="language-plaintext highlighter-rouge">if (ary1 == ary2) return Qtrue;</code></p>
<p>The third conditional is an early return false if the size of the two array’s doesn’t match:</p>
<p><code class="language-plaintext highlighter-rouge">if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;</code></p>
<p>The last line is a recursive call which I assume is the iteratively check every index’s value
against the other array. The remainder of the method (conditional 2 and 4) I’m less sure about but looking
at the code they both can perform early returns to break out of the recursive method call. Assumedly either
when there are no more indexes to check against or the arrays are found not to match.</p>
<p>Now working our way back to the higher-level Ruby implementation, what does a practical example of this look like?</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_1</span> <span class="o">==</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>The above checks the two arrays against each other and finds them identical.</p>
<p>There are two other strategies you can take here. Both the <code class="language-plaintext highlighter-rouge">Array#eql?</code> and <code class="language-plaintext highlighter-rouge">Array#<=></code> methods will allow you to check array equality. They work
slightly differently from each other:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Array#eql?</code> - Similiar to Array#== but utilizes Object#== internally</li>
<li><code class="language-plaintext highlighter-rouge">Array#<=></code> - Generally used within sort_by blocks to check difference in arrays. Returns 0 for matching, 1 when there are extra indexes, and -1 when there are missing indexes.</li>
</ul>
<p>Here’s a quick example of each:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span><span class="p">.</span><span class="nf">eql?</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1">#=> true</span>
<span class="c1"># Spaceship operator returns: 0 (equal), -1 (elements less than other array), 1 (elements more than other array)</span>
<span class="p">(</span><span class="n">array_1</span> <span class="o"><=></span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="o">==</span> <span class="mi">0</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>Personally, I find <code class="language-plaintext highlighter-rouge">==</code> to be the easiest to read. This is all well and good but what happens if the arrays are in different orders?</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">array_1</span> <span class="o">==</span> <span class="n">array_2</span> <span class="c1">#=> false</span>
</code></pre></div></div>
<p>Now they are no longer equivalent according to <code class="language-plaintext highlighter-rouge">==</code>. So from this we can infer that <code class="language-plaintext highlighter-rouge">==</code> is order-dependent. We’ll need
another strategy in order, ;-), to test for equality properly.</p>
<h2 id="equality-without-order-dependence">Equality without order dependence</h2>
<p>Using our above example, let’s say we want the two arrays to return true when checking equality. We don’t care if they
are in the same order just that they contain the same elements. We can achieve that simply by first sorting the arrays.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">array_1</span><span class="p">.</span><span class="nf">sort</span> <span class="o">==</span> <span class="n">array_2</span><span class="p">.</span><span class="nf">sort</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>The above ensures that the array elements are sorted using the same algorithm keeping them in identical ordering.</p>
<p>Now there is the additional processing overhead of having to first sort the arrays (1 iteration per array) and then check the index
values against the other array (another iteration). This means that it will be slightly less efficient in checking since it
needs to iterate once on each array and then another time to check for equality.</p>
<p>Before we improve efficiency, let’s throw another challenge in the mix. What happens if one of the array’s contains duplicate elements?</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">array_1</span><span class="p">.</span><span class="nf">sort</span> <span class="o">==</span> <span class="n">array_2</span><span class="p">.</span><span class="nf">sort</span> <span class="c1">#=> false</span>
</code></pre></div></div>
<p>Even though array_1 and array_2 have the exact same values within them, array_2 contains duplicates of the value 2. This
fails the equality check.</p>
<h2 id="equality-without-duplicates">Equality without duplicates</h2>
<p>Taking a line from the above approach we can add yet another iteration to our arrays ontop of the existing <code class="language-plaintext highlighter-rouge">.sort</code> to
remove the duplicate values. We achieve this by using <code class="language-plaintext highlighter-rouge">.uniq</code> which iterates through the array and removes duplicates.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">array_1</span><span class="p">.</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">sort</span> <span class="o">==</span> <span class="n">array_2</span><span class="p">.</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">sort</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>We’ve now succeeded in checking the equality between two arrays without regard for order or duplicate values. Internally sort and uniq work like so:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">#uniq</code> - “self is traversed in order, and the <a href="https://apidock.com/ruby/Array/first">first</a> occurrence is kept.”</li>
<li><code class="language-plaintext highlighter-rouge">#sort</code> - “Comparisons for the <a href="https://apidock.com/ruby/Array/sort">sort</a> will be done using the <=> operator”</li>
</ul>
<p>Now each array undergoes two iterations (sort and uniq) before finally having an equality iteration. This becomes even
less efficient and requires a bit more overhead to understand. There’s one more Array strategy I want to discuss next regarding
array intersections before moving onto my preferred strategy.</p>
<h2 id="equality-with-array-intersection">Equality with Array intersection</h2>
<p>This approach utilizes a lesser used syntax to perform an <a href="https://ruby-doc.org/3.2.2/Array.html#method-i-26">array intersection</a>. An array intersection means to take
two arrays and find the common elements without duplicates. It utilizes the <code class="language-plaintext highlighter-rouge">Array#&</code> syntax like I’ve demostrated below:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="p">(</span><span class="n">array_1</span> <span class="o">&</span> <span class="n">array_2</span><span class="p">)</span> <span class="c1">#=> [1, 2, 3]</span>
<span class="p">(</span><span class="n">array_1</span> <span class="o">&</span> <span class="n">array_2</span><span class="p">)</span> <span class="o">==</span> <span class="n">array_1</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>I’ve split the above into two steps. The first is performing the intersection on the two arrays. The result of doing this
is a new array that contains common values using the ordering of the first array utilizes. By using that last statement,
we know that if the ordering of the first array is used and duplicates are removed then if the result is equal to the
first array the arrays must be equivalent. Shown as <code class="language-plaintext highlighter-rouge">(array_1 & array_2) == array_1</code>.</p>
<p>Internally this is iterating through values along with checking for duplicates with <code class="language-plaintext highlighter-rouge">eql?</code>.</p>
<p>Outside of the Array class there is an alternative which I find to be highly readable in code. Let’s take a look at an
improved readability approach.</p>
<h2 id="array-equality-with-sets">Array Equality with Sets</h2>
<blockquote class="Info Info--full">
<p>
<i class="fas fa-quote-left"></i>
Set implements a collection of unordered values with no duplicates. This is a hybrid of Array's intuitive inter-operation facilities and Hash's fast lookup.
</p>
<a href="https://ruby-doc.org/stdlib-2.7.1/libdoc/set/rdoc/Set.html">https://ruby-doc.org/</a>
</blockquote>
<p>A Set is like an array but by definition is an enumerable without duplicates and order independent. Therefore we can convert
our arrays to Sets and achieve the same effect as we did with the chained <code class="language-plaintext highlighter-rouge">.uniq.sort</code> above.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array_1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">array_2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="no">Set</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">array_1</span><span class="p">)</span> <span class="o">==</span> <span class="no">Set</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">array_2</span><span class="p">)</span> <span class="c1">#=> true</span>
<span class="n">array_1</span><span class="p">.</span><span class="nf">to_set</span> <span class="o">==</span> <span class="n">array_2</span><span class="p">.</span><span class="nf">to_set</span> <span class="c1">#=> true</span>
</code></pre></div></div>
<p>Very clean looking and readable. Interestingly enough, <code class="language-plaintext highlighter-rouge">Set#==</code> internally uses <code class="language-plaintext highlighter-rouge">Object#==</code> identically to have <code class="language-plaintext highlighter-rouge">Array#eql?</code> achieves
equality checks. <a href="https://ruby-doc.org/stdlib-2.7.1/libdoc/set/rdoc/Set.html#method-i-3D-3D">Here’s the documentation for <code class="language-plaintext highlighter-rouge">Set#==</code></a>.</p>
<h2 id="so-which-strategy-is-the-best">So which strategy is the best?</h2>
<p>Honestly, it depends. Personally I believe that the <code class="language-plaintext highlighter-rouge">Set.new</code> approach above is the most elegant and simplest solution to understand, though there
are merits to the explict straightforwardness of <code class="language-plaintext highlighter-rouge">.uniq.sort</code> or the slickness of array intersection. Ultimately, its up to you to decide which is best for
your own coding style. I will say that one of the above strategies may prove to be more performant than another. My guess would be
that <code class="language-plaintext highlighter-rouge">Set.new</code> is the fastest strategy but I have not performed benchmarks to verify this.</p>
<p>Do you have another Array equality strategy I missed? Know of a more efficient method? I’d love to discuss in the comments below.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/Object equality is an interesting topic. You can check for matching values (with or without ordering). You can also check to see if the object has the same in-memory id. I’ve written code to diff two arrays before but I’ve never sat down to think about all the ways to accomplish this. Also I was able to explore what I believe are the most optimal approaches.How to fix homebrew postgres error 2562023-07-20T00:00:00+00:002023-07-20T00:00:00+00:00http://joshfrankel.me/blog/how-to-fix-homebrew-postgres-error-256<p>At home, I’m a Linux user but at work most companies use Mac for engineers. It’s been
a couple years since I’ve used Mac so I’m learning my way back around it along with
Homebrew. One issue I ran into the other day was postgresql service not starting
sucessfully. No amount of <code class="language-plaintext highlighter-rouge">brew services restart postgresql</code> would fix it. Turns
out to be a pretty simple fix.</p>
<!--excerpt-->
<p>I first noticed the issue when trying to run a Ruby on Rails server locally. The response
from running <code class="language-plaintext highlighter-rouge">rails s</code> looked something like:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket <span class="s2">"/tmp/.s.PGSQL.5432"</span>?
</code></pre></div></div>
<p>Alright, so this is saying the server isn’t running or listening to requests. Checking
<code class="language-plaintext highlighter-rouge">brew services list</code> to see the output rendered:</p>
<p><img src="/img/2023/brew-service-list.png" alt="Brew services list output" /></p>
<p>Next was looking up what a 256 error which led me to an excellent <a href="https://stackoverflow.com/questions/39710384/cannot-connect-to-postgres-server-running-through-brew-services">StackOverflow post</a>.
Following the post suggested three step process of: stopping the service, removing the pid file, and restarting the service.</p>
<p><img src="/img/2023/brew-services-stop.png" alt="Brew services stop output" /></p>
<p>The pid file can be found in one of two locations:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">/usr/local/var/postgres/postmaster.pid</code></li>
<li><code class="language-plaintext highlighter-rouge">/opt/homebrew/var/postgresql/postmaster.pid</code> (Apple M1)</li>
</ul>
<p>I found mine within the <code class="language-plaintext highlighter-rouge">/opt/</code> directory. Last was just restarting the service.</p>
<p><img src="/img/2023/brew-services-start.png" alt="Brew services start output" /></p>
<p>After this running <code class="language-plaintext highlighter-rouge">rails s</code> worked as expected.</p>
<p>Thanks for reading it was a quick one.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/At home, I’m a Linux user but at work most companies use Mac for engineers. It’s been a couple years since I’ve used Mac so I’m learning my way back around it along with Homebrew. One issue I ran into the other day was postgresql service not starting sucessfully. No amount of brew services restart postgresql would fix it. Turns out to be a pretty simple fix.Don’t Slack on Site Reliability2023-01-17T00:00:00+00:002023-01-17T00:00:00+00:00http://joshfrankel.me/blog/dont-slack-on-site-reliability<p>Alright, bit of a clickbait title but if you know me you’ll know my love for
bad puns. Ensuring a healthy running application is an important part of your
infrastructure reliability. Site Reliabiltiy is an entire discipline by itself as there
are a wealth of topics and concepts associated with it. That being said having
really solid Site Reliability as a start-up isn’t always the first priority.
This article details some of the steps I’ve taken to have basic application
monitoring in place easily and cheaply.</p>
<!--excerpt-->
<p>Going into this article I’ll assume you have an error reporting service tracking
application errors, <a href="https://www.honeybadger.io/">I use Honeybadger</a>, along with
a communication platform, such as Slack.</p>
<p><img src="/img/2023/dangerous-to-go-alone.jpg" alt="Its dangerous to go alone. Take this. Honeybadger and Slack" /></p>
<h1 id="site-reliability-and-start-up-life">Site Reliability and Start-up life</h1>
<p>Start-ups are scrappy little things that move fast and break things. This mentality
really resonates with me as you can get a lot done if you practice “good enough” programming. However,
we still need a way to ensure if (more like when) we break things we are able to recover and track them.
With that said here are the goals we’re looking to gain greater visibility with:</p>
<ol>
<li>Error Reporting</li>
<li>Application uptime</li>
<li>Infrastructure & Service outtages</li>
</ol>
<p>Slack happens to be a great tool for ensuring communication stays open between contributors
within an organization. I find myself nowadays using it almost exclusively over Email.
Given how often I’m in there I wanted to also consolidate my application notifications there as well.
This helps avoid context switching between external services, email, etc.</p>
<p>With that in mind let’s dig into making a foundational site reliability layer for our application.</p>
<h1 id="errors-in-realtime">Errors in realtime</h1>
<p>First off we’ll need some new channels in Slack to monitor application statuses. You can name these however you want. I
personally used the following: <code class="language-plaintext highlighter-rouge">devops</code> and <code class="language-plaintext highlighter-rouge">devops-staging</code> to denote production and staging environments.</p>
<p>In your Error Reporting service of choice (again we’ll be using Honeybadger), there should be some way of
setting up and integration for Slack. Honeybadger’s is <strong>located under Settings -> Alerts & Integrations</strong>.</p>
<p><img src="/img/2023/honeybadger-integration.png" alt="Honeybadger create integration" /></p>
<p>This will send you through an Oauth process and ask permission from Slack to post to a specific
channel on behalf of the Honeybadger bot.</p>
<p><img src="/img/2023/honeybadger-oauth.png" alt="Slack authorizing Honeybadger OAuth" /></p>
<p>From this point you can decide what types of Error messaging is sent to your Slack channel. Additionally,
you can specify environment to monitor. This is super useful for our first channel <code class="language-plaintext highlighter-rouge">devops</code> as it can focus
on production and demo environments which are high-impact to customers. <code class="language-plaintext highlighter-rouge">devops-staging</code> can instead specify
staging and review environments while still important can be moved to a seperate channel. It’s a good idea
to expose errors in both your production and staging environments, as staging can give you early warning to
an issue that is about to be deployed to production.</p>
<blockquote class="Info Info--full">
<p>
<i class="fas fa-quote-left"></i>
It's a good idea
to expose errors in both your production and staging environments, as staging can give you early warning to
an issue that is about to be deployed to production.
</p>
</blockquote>
<p>With this in place, you’ll begin seeing errors appear in real-time within Slack. A tip with managing Error Reporting
services, is to always ensure the total number of unresolved errors is zero. As errors emerge they should be immediately
remediated either through code or leaving comments on them within the Error Reporting service.</p>
<p><img src="/img/2023/honeybadger-slack-error.png" alt="Slack displaying Honeybadger error" /></p>
<p>Conviently having your errors within Slack allows fast notification as well as helpful actions that can be taken
directly from Slack.</p>
<h1 id="application-uptime">Application uptime</h1>
<p>In addition to error reporting, we also want to know if the application stops responding to web requests. This is known
as application uptime. When this occurs it should be resolved immediately to reduce service disruption.
Honeybadger still has our back here as there is a specific page for uptime checks.</p>
<p><img src="/img/2023/honeybadger-uptime-setup.png" alt="Honeybadger uptime setup page" /></p>
<p>Using your production application’s url, will allow Honeybadger to periodically check to ensure a 200 success is received. If at
any point the check fails (or a redirect occurs), it will notify the Honeybadger service. In order to put this into Slack
as well we’ll need to modify the previously created Slack integration (reminder: Settings -> Alerts & Integrations). On the
integration setup page there is a section down the page which allows for us to attach configured uptime checks to the Slack
integration</p>
<p><img src="/img/2023/honeybadger-uptime-integration.png" alt="Honeybadger uptime integration setup" /></p>
<p>If you look on the right side of the above picture, you’ll see Uptime Sites “marketing site”. I find it useful if you
are running an application which has both a marketing url and application url if both are monitored through your Slack
notifications. With this in place we’ll start receiving alerts for application outtages.</p>
<p><img src="/img/2023/slack-honeybadger-uptime-alert.png" alt="Slack notification from Honeybadger on application uptime" /></p>
<h1 id="infrastructure--service-outtages">Infrastructure & Service outtages</h1>
<p>Similiar to Application Uptime, we have external services which our application relies on but which we have little
to no control over. Such services as Heroku, Github, AWS, and others which power web applications. We’d like to be aware
of these in case customers (and internal team members) experience issues that we can describe the underlying issue with
clarity.</p>
<p>We still want to see these outtages within Slack. So how do we accomplish that? There are definitely some plugins (most paid)
which allow for setting up uptime alerts but there’s a free and simple way to get these. Slack gives access to the <code class="language-plaintext highlighter-rouge">/feed</code>
command which allows a specific channel to subscribe to an RSS or atom news feed. Given that most services have status pages
with subscription options we can leverage that to add these to a feed listing.</p>
<p>Often services use the naming convention of <code class="language-plaintext highlighter-rouge">status.service-name.com</code> for their status page. Generally these contain
methods for subscribing to the raw feed url. Sometimes these urls are hidden or require subscribing with an email. Heroku’s
for instance is in a drop-down menu under their RSS icon labeled “Heroku Status”.</p>
<p><img src="/img/2023/heroku-status-feed.png" alt="Heroku status feed" /></p>
<p>Mailgun on the other hand is much easier to find being under the subscribe menu’s rss icon.</p>
<p><img src="/img/2023/mailgun-rss.png" alt="Mailgun status feed" /></p>
<p>Once you’ve discovered the feed urls for your services, you can easily add them to a Slack channel using <code class="language-plaintext highlighter-rouge">/feed subscribe url-to-feed</code>.
I’d recommend these are added to a seperate channel than <code class="language-plaintext highlighter-rouge">devops</code> or <code class="language-plaintext highlighter-rouge">devops-staging</code>. Personally I used <code class="language-plaintext highlighter-rouge">vendor-status</code>. With this
in place you’ll begin to see notifications for infrastructure service disruptions.</p>
<p><img src="/img/2023/github-status-feed.png" alt="Github service disruption Slack message" /></p>
<h1 id="conclusion">Conclusion</h1>
<p>With the above in place, we have a basic level of monitoring in place for our early application. This allows for application
status to be communicated through Slack giving greater visibility and faster remediation response times.</p>
<p>Like I’ve mentioned before this is geared towards a start-up application. That is an early application. This is not meant
to be a in-depth or full harness for monitoring an application. Merely this is a way to keep a pulse on application health
while at the beginning stages of a new product.</p>
<p>What’d you think of moving application status into Slack? Got a tip that could be added to this article? I’d love
to discuss in the comments below.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/Alright, bit of a clickbait title but if you know me you’ll know my love for bad puns. Ensuring a healthy running application is an important part of your infrastructure reliability. Site Reliabiltiy is an entire discipline by itself as there are a wealth of topics and concepts associated with it. That being said having really solid Site Reliability as a start-up isn’t always the first priority. This article details some of the steps I’ve taken to have basic application monitoring in place easily and cheaply.Creating blurred background images with overlay text in CSS2022-08-09T12:18:00+00:002022-08-09T12:18:00+00:00http://joshfrankel.me/blog/creating-blurred-background-images-with-overlay-text-in-css<p>Blurring images is a common style on the web. It can give needed contrast and visual
interest. Now you can pre-blur an image with a photo editing program, but for the
purpose of this article we’ll be focused on CSS approaches.</p>
<!--excerpt-->
<h2 id="a-simple-animated-blur">A Simple Animated Blur</h2>
<p>The first type of blur is for background images that <strong>don’t</strong> have content overlays. They are simply an image that needs to be blurred out.
This can be useful for blurring an image and then bringing it into focus when the hover state is activated. Try hovering over the following background image.</p>
<style>
.demo-container {
outline: 1px solid #000;
width: 150px;
}
.simple-background {
height: 150px;
width: 150px;
background-image: url(/img/2022/brave-browser.png);
background-size: cover;
filter: blur(30px);
transition: 100ms filter linear;
}
.simple-background:hover {
filter: none;
}
</style>
<div class="demo-container">
<div class="simple-background"></div>
</div>
<p>This is achieved by using the following css:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.simple-background</span> <span class="p">{</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">background-image</span><span class="p">:</span> <span class="sx">url(/img/2022/brave-browser.png)</span><span class="p">;</span>
<span class="nl">background-size</span><span class="p">:</span> <span class="n">cover</span><span class="p">;</span>
<span class="nl">filter</span><span class="p">:</span> <span class="n">blur</span><span class="p">(</span><span class="m">30px</span><span class="p">);</span>
<span class="nl">transition</span><span class="p">:</span> <span class="m">100ms</span> <span class="n">filter</span> <span class="n">linear</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.simple-background</span><span class="nd">:hover</span> <span class="p">{</span>
<span class="nl">filter</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>First we set a Gaussian blur on the element using <code class="language-plaintext highlighter-rouge">filter: blur(30px);</code>. Next after we’ve set the url for the background-image we supply<code class="language-plaintext highlighter-rouge">background-size: cover;</code>. This scales the image to fit the container and helps o avoid a tiling effect. Next a <code class="language-plaintext highlighter-rouge">transition: 100ms filter linear;</code> on the starting style to allow for the element to transition its blur effect. Last, <code class="language-plaintext highlighter-rouge">filter: none;</code> is used when hovering over the element.</p>
<h2 id="a-blurred-image-with-overlay-text">A blurred image with overlay text</h2>
<p>Things become more complex if you want to blur the background while also having a text overlay. Let’s take a look at the following example:</p>
<style>
.content-example {
background-image: url(/img/2022/brave-browser.png);
height: 150px;
width: 150px;
background-size: cover;
position: relative;
}
.content-example--badBlur {
filter: blur(30px);
}
.content-example p {
color: #fff;
background: #ccc;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 40% 0%;
}
</style>
<div class="demo-container">
<div class="content-example">
<p>Sample content</p>
</div>
</div>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"content-example"</span><span class="nt">></span>
<span class="nt"><p></span>Sample content<span class="nt"></p></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>We have content that needs to sit on top of a background-image. Watch what happens
if we try to blur the background-image by adding <code class="language-plaintext highlighter-rouge">filter: blur(30px);</code> to the .content-example class.</p>
<div class="demo-container">
<div class="content-example content-example--badBlur">
<p>Sample content</p>
</div>
</div>
<p>The inner text also became blurred. This might be obvious since we blurred the parent
element that all children would also become blurred. So, how do we blur the background-image
without blurring the text?</p>
<p>That’s where <strong>backdrop-filter</strong> comes in handy.</p>
<h3 id="introducing-backdrop-filter">Introducing backdrop filter</h3>
<p>Backdrop-filter allows you to blur the background behind an element instead of the element
itself. Now in order for this to work with background-image we’ll need to structure our
html so that the parent div has the background-image while the inner has the backdrop-filter. Then the inner div needs to fill the remaining width and height to overlay the image.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"advanced__background"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"advanced__backgroundBody"</span><span class="nt">></span>
<span class="nt"><p></span>Sample content<span class="nt"></p></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>We require one additional div to add the backdrop-filter attribute. From the above
example <code class="language-plaintext highlighter-rouge">advanced__backgroundBody</code> blurs the background by utilizing the width and height of
its parent element. So while <code class="language-plaintext highlighter-rouge">advanced__backgroundBody</code> doesn’t have any color applied to it
the element sits in the same position as <code class="language-plaintext highlighter-rouge">advanced__background</code> where the background-image is
located. This allows for blurring to occur in the background while preserving the foreground text “Sample content”. Below is the matching css for the new HTML structure:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.advanced__background</span> <span class="p">{</span>
<span class="nl">background-image</span><span class="p">:</span> <span class="sx">url(/img/2022/brave-browser.png)</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">background-size</span><span class="p">:</span> <span class="n">cover</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.advanced__backgroundBody</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="py">backdrop-filter</span><span class="p">:</span> <span class="n">blur</span><span class="p">(</span><span class="m">30px</span><span class="p">);</span>
<span class="nl">transition</span><span class="p">:</span> <span class="m">100ms</span> <span class="n">backdrop-filter</span> <span class="n">linear</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.advanced__backgroundBody</span><span class="nd">:hover</span> <span class="p">{</span>
<span class="py">backdrop-filter</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.advanced__backgroundBody</span> <span class="nt">p</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="m">#fff</span><span class="p">;</span>
<span class="nl">background</span><span class="p">:</span> <span class="m">#ccc</span><span class="p">;</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">bottom</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">40%</span> <span class="m">0%</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Similiar to the simple blur, we need nearly the same css. The main difference as mentioned above
is that the child div (<strong>advanced__backgroundBody</strong>) now has <code class="language-plaintext highlighter-rouge">backdrop-filter: blur(30px); width: 100%; height: 100%;</code> set on it. The filter and sizing work together to blur out anything behind the element. We even have a transition for the backdrop while hovering. Here’s the final result:</p>
<style>
.advanced__background {
background-image: url(/img/2022/brave-browser.png);
height: 150px;
width: 150px;
background-size: cover;
}
.advanced__backgroundBody {
position: relative;
height: 100%;
width: 100%;
backdrop-filter: blur(30px);
transition: 100ms backdrop-filter linear;
}
.advanced__backgroundBody:hover {
backdrop-filter: none;
}
.advanced__backgroundBody p {
color: #fff;
background: #ccc;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 40% 0%;
}
</style>
<div class="demo-container">
<div class="advanced__background">
<div class="advanced__backgroundBody">
<p>Sample content</p>
</div>
</div>
</div>
<p>And that’s it! You now have a background image which is blurred out for effect
while retaining the legibility of the foreground text.</p>
<p>As always, thanks for reading.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/Blurring images is a common style on the web. It can give needed contrast and visual interest. Now you can pre-blur an image with a photo editing program, but for the purpose of this article we’ll be focused on CSS approaches.Lemme pencil you in: Using iCalendar and Rails to sync calendar events2022-05-09T00:00:00+00:002022-05-09T00:00:00+00:00http://joshfrankel.me/blog/lemme-pencil-you-in-using-icalendar-and-rails-to-sync-calendar-events<p>If you’ve worked with calendars, then you know the frustration of having two systems not keep events in sync. There are essentially three levels to keeping events in sync. The first is integration with every email provider you wish to support. Not a light initiative. The second is to find a product which aggregates multiple providers into a single API interface. Better solution, but open your wallet. Lastly, build a lightweight system that relies on the iCalendar standard. This last option is the focus of the article. Throughout the remainder of this post, we’ll explore pushing an event to an external provider’s calendar and keeping it in sync across systems without writing a single API request or integration.
<!--excerpt--></p>
<h2 id="what-were-building">What we’re building</h2>
<p>To start, let’s say we want an application that stores a calendar event as a database record. What is the easiest way to ensure that the event stays in sync between the database and an attendee’s personal calendar?</p>
<p>Let’s add some guardrails to this project. This way we can avoid scope creep.</p>
<ol>
<li>Creating a new calendar event adds it to an attendee’s external calendar application</li>
<li>Updating or cancelling event effect the attendee’s event details</li>
<li>Event organizers receive attendee RSVP status updates</li>
</ol>
<h2 id="how-this-works">How this works</h2>
<p>Here’s a basic sequence diagram of the various layers to this process. Looks like a lot, but most of this is implemented by the provider email account, such as Gmail or Outlook.</p>
<p><img src="/img/2022/icalendar-workflow-example.png" alt="Basic iCalendar workflow for adding calendar events" /></p>
<ol>
<li>Organizer creates an event</li>
<li>Our application generates an iCalendar attachment and sends the attendee an email</li>
<li>Attendee’s mail client parses incoming email and adds valid iCalendar attachments to their connected Calendar</li>
<li>Attendee changes their RSVP status, which sends email to Organizer</li>
</ol>
<p>Really, the only thing we can control is steps 1 and 2. Step 3 and 4 are entirely in the hands of whatever the mail client is. This is a good time to call out a caveat with this approach, which is we are at the mercy of how the mail client parses incoming mail and iCalendar files. That being said, iCalendar is a widely accepted standard.</p>
<p>The above sequence diagram illustrates how we’ll accomplish a zero-integration strategy for placing calendar events on attendee calendars. This works by following the <abbr title="Internet Calendaring and Scheduling Core Object Specification">iCalendar</abbr> standard for attaching calendar events to emails. For Mail Clients that have connected calendars (e.g. Gmail & Google Calendar), they parse incoming emails. When the email contains a valid iCalendar file, it is automatically added to the attendee’s calendar.</p>
<p>Pretty slick right?</p>
<p>The documentation regarding iCalendar is thorough and useful when working with calendar event attachments. <a href="https://datatracker.ietf.org/doc/html/rfc5545">Check it out here</a>.</p>
<p>Now, there are a couple caveats with using this approach.</p>
<ol>
<li>Updating an event in your external calendar will not update it in the application</li>
<li>The organizer won’t have the event on their calendar unless they are also added as an attendee</li>
<li>Relies on iCalendar specification while standard, is up to the provider to implement correctly</li>
</ol>
<p>With that out of the way, let’s build ourselves a basic Rails application to work with.</p>
<h2 id="creating-a-new-application">Creating a new application</h2>
<p>We’ll need to generate a new sample application for this functionality.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails new ics_demo <span class="nt">--skip-javascript</span> <span class="nt">--skip-helpers</span>
<span class="c"># Setup ActionMailbox</span>
rails action_mailbox:install
rails db:migrate
</code></pre></div></div>
<p>Add <code class="language-plaintext highlighter-rouge">gem 'icalendar'</code> to your Gemfile. This gem implements the iCalendar standard within Ruby, making it dead simple to craft calendar attachments.</p>
<p>Next we’ll need a mailer for which our attachments will be placed on. Run <code class="language-plaintext highlighter-rouge">rails generate mailer EventInvitation event_create event_update event_cancel</code></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails generate mailer EventInvitation event_create event_update event_cancel
create app/mailers/event_invitation_mailer.rb
invoke erb
create app/views/event_invitation_mailer
invoke test_unit
create <span class="nb">test</span>/mailers/event_invitation_mailer_test.rb
create <span class="nb">test</span>/mailers/previews/event_invitation_mailer_preview.rb
</code></pre></div></div>
<p>Change the receiver email address to an email you can access. You’ll want this to be different from your primary Gmail address (more on this later).</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">EventInvitationMailer</span> <span class="o"><</span> <span class="no">ApplicationMailer</span>
<span class="k">def</span> <span class="nf">event_create</span>
<span class="vi">@greeting</span> <span class="o">=</span> <span class="s2">"Event created"</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"[your_email]@gmail.com"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">event_update</span>
<span class="vi">@greeting</span> <span class="o">=</span> <span class="s2">"Event Updated"</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"[your_email]@gmail.com"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">event_cancel</span>
<span class="vi">@greeting</span> <span class="o">=</span> <span class="s2">"Event Cancelled"</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"[your_email]@gmail.com"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># event_invitation_mailer.html.erb</span>
<span class="o"><</span><span class="sx">%= @greeting %>
</span></code></pre></div></div>
<p>Change delivery error setting in development.rb for easier debugging</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span><span class="p">.</span><span class="nf">action_mailer</span><span class="p">.</span><span class="nf">raise_delivery_errors</span> <span class="o">=</span> <span class="kp">true</span>
</code></pre></div></div>
<p>Now, in order to test this we’ll need a reliable way to send emails in development. Google Apps to the rescue!</p>
<h3 id="google-app-password-for-development">Google App Password for development</h3>
<p>First we need to create new app password for your Google account: <a href="https://myaccount.google.com/apppasswords">https://myaccount.google.com/apppasswords</a>. The reason we use an app password is it allows us to provide an authorized SMTP setup without having to deal with two-factor authentication.</p>
<blockquote class="Info Info--full">
<p>
<i class="fas fa-quote-left"></i>
Make sure that the receiver address is different from your email used for the Google App password, or else you won't be able
to associate calendar events correctly.
</p>
</blockquote>
<p>Select <code class="language-plaintext highlighter-rouge">Mail</code> for app and <code class="language-plaintext highlighter-rouge">Other</code> for device. Name the app Rails Mailer or what you like. Make sure to save the app password.</p>
<p>Add the <a href="https://github.com/bkeepers/dotenv">dotenv</a> gem to your gemfile. <code class="language-plaintext highlighter-rouge">gem 'dotenv-rails', groups: [:development, :test]</code>. This allows us to create a root level <code class="language-plaintext highlighter-rouge">.env</code> file which contains environment variables. Make sure to re-bundle.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># .env</span>
<span class="nv">GMAIL_USERNAME</span><span class="o">=</span><span class="s2">"your_email@gmail.com"</span>
<span class="nv">GMAIL_PASSWORD</span><span class="o">=</span><span class="s2">"XXXX-your-app-password"</span>
</code></pre></div></div>
<p>Following the <a href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration-for-gmail">Rails guide for setting up Gmail as an ActionMailer provider</a>, you’ll end up with the following:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># development.rb</span>
<span class="n">config</span><span class="p">.</span><span class="nf">action_mailer</span><span class="p">.</span><span class="nf">delivery_method</span> <span class="o">=</span> <span class="ss">:smtp</span>
<span class="n">config</span><span class="p">.</span><span class="nf">action_mailer</span><span class="p">.</span><span class="nf">smtp_settings</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">address: </span><span class="s1">'smtp.gmail.com'</span><span class="p">,</span>
<span class="ss">port: </span><span class="mi">587</span><span class="p">,</span>
<span class="ss">domain: </span><span class="s1">'example.com'</span><span class="p">,</span>
<span class="ss">user_name: </span><span class="no">ENV</span><span class="p">[</span><span class="s2">"GMAIL_USERNAME"</span><span class="p">],</span>
<span class="ss">password: </span><span class="no">ENV</span><span class="p">[</span><span class="s2">"GMAIL_PASWWORD"</span><span class="p">],</span>
<span class="ss">authentication: </span><span class="s1">'plain'</span><span class="p">,</span>
<span class="ss">enable_starttls_auto: </span><span class="kp">true</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Emails can be sent from development by running the following code in your <code class="language-plaintext highlighter-rouge">rails console</code></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">EventInvitationMailer</span><span class="p">.</span><span class="nf">event_create</span><span class="p">.</span><span class="nf">deliver_now</span>
</code></pre></div></div>
<p>Check your inbox to confirm you’ve received the message. Time to see those events popping up on your calendar. Check your spam or filters if you don’t see the message appear.</p>
<p><img src="/img/2022/icalendar-basic-example.png" alt="Basic email sent via Google App" /></p>
<h2 id="attaching-an-icalendar-event-to-an-email">Attaching an iCalendar event to an email</h2>
<p>Now that we’ve configured our basic application for mail delivery, we should start sending
calendar events out as email attachments. Add the following to the body of your mailer’s <strong>rsvp</strong> method:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">event_create</span>
<span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="n">event</span><span class="p">.</span><span class="nf">dtstart</span> <span class="o">=</span> <span class="mi">1</span><span class="p">.</span><span class="nf">hour</span><span class="p">.</span><span class="nf">from_now</span>
<span class="n">event</span><span class="p">.</span><span class="nf">dtend</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span><span class="nf">hours</span><span class="p">.</span><span class="nf">from_now</span>
<span class="n">event</span><span class="p">.</span><span class="nf">sequence</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">ip_method</span> <span class="o">=</span> <span class="s2">"REQUEST"</span>
<span class="n">attachments</span><span class="p">[</span><span class="s2">"invite.ics"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">mime_type: </span><span class="s2">"text/calendar; method=REQUEST"</span><span class="p">,</span>
<span class="ss">content: </span><span class="n">ical</span><span class="p">.</span><span class="nf">to_ical</span>
<span class="p">}</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"different_address@gmail.com"</span><span class="p">,</span> <span class="ss">subject: </span><span class="s2">"My First Event"</span>
<span class="k">end</span>
</code></pre></div></div>
<p><strong>dtstart</strong> indicates the starting time of the event and is required. <strong>dtend</strong> indicates the ending time and is required.</p>
<p><strong>Sequence</strong> is the order in which an iCalendar file is received in. It determines which action should take place. Additionally, it prevents out-of-sequence updates (e.g. updating an event before it is created).</p>
<p>Try sending yourself an email. You should see it come through along with the
calendar event being placed on your associated calendar. Again, make sure that the email address you are sending to is different than the address you used for the Google App password.</p>
<p><img src="/img/2022/icalendar-auto-event-in-mail.png" alt="Basic ics Attachment" /></p>
<p>The new event looks pretty nice in our mailbox. If you hover over the email in
Gmail’s listing view there is also now a new button called RSVP with some quick
actions.</p>
<p><img src="/img/2022/icalendar-rsvp-from-inbox.png" alt="Rsvp from inbox" /></p>
<p>Super handy!</p>
<p>Now the above is the absolute minimum to enable this process and will only create events currently. Updating or Canceling an event take different request
parameters which we’ll investigate next. But, before we dig into updating and canceling, we need a
mechanism for telling external systems that the event we’re updating or cancelling refers to the
same event we created. This is where the <code class="language-plaintext highlighter-rouge">event.uid</code> property comes in handy.</p>
<h2 id="tying-events-together">Tying events together</h2>
<p><strong>Event uid</strong> is the unique identifier for the event. Think about this like a primary key for the calendar event. It allows multiple iCalendar files to be sent through email and will associate them to the same uid. Useful for if the same event gets sent multiple times or updated. This way it won’t keep adding new ones to your calendar. We can utilize the built-in iCalendar gem method <code class="language-plaintext highlighter-rouge">new_uid</code> to generate a new uid.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">uid</span> <span class="o">=</span> <span class="n">ical</span><span class="p">.</span><span class="nf">new_uid</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">uid</span> <span class="o">=</span> <span class="n">uid</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Alternatively, if you have a uuid on your database record which is associated with the calendar event, you can utilize that to ensure
stability between your system and the external calendar. I prefer this approach in the long-run.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Creating a record that represents the calendar event</span>
<span class="n">some_record</span> <span class="o">=</span> <span class="no">Event</span><span class="p">.</span><span class="nf">new</span> <span class="c1">#=> <Event uid="1c4bd300-ab99-425a-82fd-1e2720d23cdb"></span>
<span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">uid</span> <span class="o">=</span> <span class="n">some_record</span><span class="p">.</span><span class="nf">uid</span> <span class="c1"># Use the record to map the icalendar files uid</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Onto updating and cancelling events.</p>
<h2 id="updating-an-event">Updating an Event</h2>
<p>In order to update the calendar event, the only real change to our <strong>event_create</strong> method is to specify the
sequence number and ensure the uid matches the original event. Sequence number maps to the order in which iCalendar files
were recieved in. If you notice the creating of an event example above, I used
<code class="language-plaintext highlighter-rouge">event.sequence = 1</code>. That’s because when an event is first created it will
always be the first.</p>
<p>Now for updating an event we need sequence to be greater than 1.
However, do we really want to manage the number for how many updates were made? Probably, not.
This is why I came up with a nifty little trick using the integer representation of Time. Seen
below as <code class="language-plaintext highlighter-rouge">event.sequence = Time.now.to_i</code>. Using this will ensure that the order in which
updates are made always stays accurate with the upside of us not having to care about saving
the current sequence number.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">event_update</span>
<span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">dtstart</span> <span class="o">=</span> <span class="mi">1</span><span class="p">.</span><span class="nf">hour</span><span class="p">.</span><span class="nf">from_now</span>
<span class="n">event</span><span class="p">.</span><span class="nf">dtend</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span><span class="nf">hours</span><span class="p">.</span><span class="nf">from_now</span>
<span class="c1"># Now we don't need to store which sequence number we're on</span>
<span class="n">event</span><span class="p">.</span><span class="nf">sequence</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">to_i</span>
<span class="c1"># This should be the equal to the id used when the event was created</span>
<span class="n">event</span><span class="p">.</span><span class="nf">uid</span> <span class="o">=</span> <span class="n">some_record</span><span class="p">.</span><span class="nf">uid</span>
<span class="k">end</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">ip_method</span> <span class="o">=</span> <span class="s2">"REQUEST"</span>
<span class="n">attachments</span><span class="p">[</span><span class="s2">"invite.ics"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">mime_type: </span><span class="s2">"text/calendar; method=REQUEST"</span><span class="p">,</span>
<span class="ss">content: </span><span class="n">ical</span><span class="p">.</span><span class="nf">to_ical</span>
<span class="p">}</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"different_address@gmail.com"</span><span class="p">,</span> <span class="ss">subject: </span><span class="s2">"My Test Email"</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Now if you have your Calendar open you can watch in real-time as the event’s time changes. You can test this out in your console with:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">EventInvitationMailer</span><span class="p">.</span><span class="nf">event_update</span><span class="p">.</span><span class="nf">deliver_now</span>
</code></pre></div></div>
<p><img src="/img/2022/auto-update-demo.gif" alt="Auto update calendar event" /></p>
<h2 id="cancelling-an-event">Cancelling an Event</h2>
<blockquote class="Info Info--full">
<p>
<i class="fas fa-quote-left"></i>
Surprisingly, the dtstart property is required for cancelling an event
</p>
</blockquote>
<p>Like updating event you’ll need to maintain the same <strong>uid</strong> and ensure that the <strong>sequence</strong> is further along than previous calendar events. The important change here
is the addition of the <strong>status</strong> property with value “CANCELLED”. This tell’s the
calendar that the event has been cancelled by the organizer and therefore should
be removed from attendee’s calendars.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">event_cancel</span>
<span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="c1"># Suprisingly, dtstart is required in order to properly cancel an event</span>
<span class="n">event</span><span class="p">.</span><span class="nf">dtstart</span> <span class="o">=</span> <span class="mi">1</span><span class="p">.</span><span class="nf">hour</span><span class="p">.</span><span class="nf">from_now</span>
<span class="n">event</span><span class="p">.</span><span class="nf">sequence</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">to_i</span>
<span class="n">event</span><span class="p">.</span><span class="nf">uid</span> <span class="o">=</span> <span class="n">uid</span>
<span class="n">event</span><span class="p">.</span><span class="nf">status</span> <span class="o">=</span> <span class="s2">"CANCELLED"</span> <span class="c1"># Required</span>
<span class="k">end</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">ip_method</span> <span class="o">=</span> <span class="s2">"REQUEST"</span> <span class="c1"># "CANCEL" also seems valid</span>
<span class="n">attachments</span><span class="p">[</span><span class="s2">"invite.ics"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">mime_type: </span><span class="s2">"text/calendar; method=REQUEST"</span><span class="p">,</span> <span class="c1"># "CANCEL" also seems valid</span>
<span class="ss">content: </span><span class="n">ical</span><span class="p">.</span><span class="nf">to_ical</span>
<span class="p">}</span>
<span class="n">mail</span> <span class="ss">to: </span><span class="s2">"different_address@gmail.com"</span><span class="p">,</span> <span class="ss">subject: </span><span class="s2">"My Test Email"</span>
<span class="k">end</span>
</code></pre></div></div>
<p>If done correctly, you’ll receive an email with a header that states the event
was cancelled.</p>
<p><img src="/img/2022/event-cancelled.png" alt="Cancelled event in inbox" /></p>
<p>Additionally, you can watch the calendar event disappear from your Google calendar.</p>
<p><img src="/img/2022/cancel-event-demo.gif" alt="Cancel event demo in Calendar" /></p>
<p>There’s the basics of using iCalendar with external systems. Now, we’ve really only scratched the surface of what is possible here. We also haven’t solved our third goal, “3. Event organizers receive attendee RSVP status updates”. Let’s dig into solving that, along with some settings I recommend making your events more robust.</p>
<h2 id="recommended-settings">Recommended settings</h2>
<p>I’ve compiled my own recommendations to help make iCalendar events
more detailed and useful. Each of these adds more context to the calendar event, which is helpful
to inform prospective attendees.</p>
<h3 id="organizer">Organizer</h3>
<p>This is whoever created the event. RSVPing to the event should send the organizer an email. <strong>CN</strong> stands for displayable name. This is a great way to make invites
look more personal for both organizers and attendees. Adding this also satisfies our third goal, hooray!</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">event</span><span class="p">.</span><span class="nf">organizer</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Values</span><span class="o">::</span><span class="no">CalAddress</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
<span class="s2">"MAILTO:organizer@gmail.com"</span><span class="p">,</span>
<span class="ss">cn: </span><span class="s2">"</span><span class="se">\"</span><span class="s2">Josh F</span><span class="se">\"</span><span class="s2">"</span>
<span class="p">)</span>
</code></pre></div></div>
<blockquote class="Info Info--full">
<p>
<i class="fas fa-quote-left"></i>
A gotcha with receiving rsvp status changes as an organizer is that if you use a Gmail address, it must be verified as also the event's creator. I was able to test this locally by instead using an outlook.com address
</p>
</blockquote>
<p><img src="/img/2022/organizer-rsvp-receipt.png" alt="Organizer rsvp receipt" /></p>
<p>Without this, your event will display “Unknown Organizer”. Additionally, if RSVPing to the event sends an email within your Calendar client, without a valid email address you’ll receive a bounce back saying the mail failed to deliver. You can disable this functionality by specifying an <code class="language-plaintext highlighter-rouge">rsvp</code> value for the <code class="language-plaintext highlighter-rouge">attendee</code> of false, like so:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">event</span><span class="p">.</span><span class="nf">attendee</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Values</span><span class="o">::</span><span class="no">CalAddress</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
<span class="s2">"mailto:different_address@gmail.com"</span><span class="p">,</span>
<span class="ss">rsvp: </span><span class="s2">"FALSE"</span>
<span class="p">)</span>
</code></pre></div></div>
<p>If you utilize organizer, you’ll also need to specify it when you <strong>cancel</strong> an event, or else the calendar system will not remove the event as it will think you’re trying to change organizers.</p>
<h3 id="summary">Summary</h3>
<p>This is the title of the Event placed on the calendar. Without this, your
events will display “No title”. Useful information to show your event attendees. <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.1.12">See documentation</a></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">summary</span> <span class="o">=</span> <span class="s2">"Test Event 15"</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="description">Description</h3>
<p>This is the body of the calendar event. This contains a more detailed
message regarding the nature of the event itself. <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.1.5">See documentation</a></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">description</span> <span class="o">=</span> <span class="s2">"This is the description"</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="sequence">Sequence</h3>
<p>This is the order in which an iCalendar file is received in. It determines which action should take place. Additionally, it prevents out-of-sequence updates (e.g. updating an event before it is created). <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.7.4">See documentation</a></p>
<p>A trick you can do here is to use the current time as an integer value to ensure that the sequence is always increasing without having the burden of managing the next number in the sequence.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Calendar</span><span class="p">.</span><span class="nf">new</span>
<span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span>
<span class="n">event</span><span class="p">.</span><span class="nf">sequence</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">to_i</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="location">Location</h3>
<p>This is the physical location where an event is taking place at. Both Gmail and Outlook display this as part of the calendar invite. <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.1.7">See documentation</a></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">location</span> <span class="o">=</span> <span class="s2">"201 E Randolph St, Chicago, IL 60602"</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Below is an example of what a calendar event looks like when you fill out all its details:</p>
<p><img src="/img/2022/event-details.png" alt="Example of event details" /></p>
<h3 id="attendee-automatically-planned-attendance">Attendee automatically planned attendance</h3>
<p>In addition to having the event on your calendar, you can also specify an attendee’s initial rsvp status. This allows you to automatically RSVP attenee’s as planning to attend. In order to activate this, we need to provide additional details about the attendee for the event. Add the following to your ical.event block:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ical</span><span class="p">.</span><span class="nf">event</span> <span class="k">do</span> <span class="o">|</span><span class="n">event</span><span class="o">|</span>
<span class="o">...</span>
<span class="n">event</span><span class="p">.</span><span class="nf">attendee</span> <span class="o">=</span> <span class="no">Icalendar</span><span class="o">::</span><span class="no">Values</span><span class="o">::</span><span class="no">CalAddress</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
<span class="s2">"mailto:different_address@gmail.com"</span><span class="p">,</span>
<span class="ss">partstat: </span><span class="s2">"accepted"</span>
<span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>
<p><strong>partstat</strong> refers to the attendee’s participation status. In our case, marking it as
<strong>accepted</strong> sets the attendee as planning on coming as well as making it visible on their calendar.</p>
<p><img src="/img/2022/auto-calendar-event.gif" alt="Auto calendar event demo" /></p>
<p>There are several other properties that can be added to CalAddress which <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.2">can be found here</a></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Now we have a basic no-integration method for adding events to attendee’s calendar application. Like mentioned earlier, there is a lot more that can be done here. I’m actually working
on a potential tech spike which incorporates ActionMailbox. Stay tuned for more.</p>
<p>Was this helpful for your calendar event system? Did you have any tips for working
with iCalendar? I’d love to hear about them in the comments below. Thanks for reading.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/If you’ve worked with calendars, then you know the frustration of having two systems not keep events in sync. There are essentially three levels to keeping events in sync. The first is integration with every email provider you wish to support. Not a light initiative. The second is to find a product which aggregates multiple providers into a single API interface. Better solution, but open your wallet. Lastly, build a lightweight system that relies on the iCalendar standard. This last option is the focus of the article. Throughout the remainder of this post, we’ll explore pushing an event to an external provider’s calendar and keeping it in sync across systems without writing a single API request or integration.Introducing SimpleCov+ Action: A Github action for ensuring test coverage2022-05-06T00:00:00+00:002022-05-06T00:00:00+00:00http://joshfrankel.me/blog/introducing-simplecov+-action-a-github-action-for-ensuring-test-coverage<p>Testing your code thoroughly is an important part of a well functioning
and easy to change application. Lack of adequate test coverage can be frustrating
when refactoring, upgrading, or tracking down a bug. I’ve always wanted a way
to ensure that each file maintains
a minimum test coverage and if not fail continuous integration checks. After much
searching, I decided to go ahead and build my own. Introducing SimpleCov+ Action
for use within your Github actions.
<!--excerpt--></p>
<h2 id="why-use-it">Why use it</h2>
<p>So why would you want to use this in your build workflow?</p>
<p>SimpleCov+ Action provides a mechanism for reading SimpleCov results and using them
to either pass or fail your Github workflow. Many of the solutions I came across concerned me, as failing a build based on test coverage could become a blocker for new work. I wanted a solution that was configurable based on coverage threshold and was accurate for my test suite. In general, averages (as in average test coverage), are easily influenced by outlier results. This was another concern I had with using such a system.</p>
<p><img src="/img/2022/simple-cov-check-basic.png" alt="SimpleCov Basic line view" /></p>
<p>So here are the basic functions that I’ve built-in to this Github action.</p>
<h3 id="minimum-coverage-threshold">Minimum coverage threshold</h3>
<p>By default, it will fail builds, if the overall test suite coverage is lower than 90%.
This can be configured using the <code class="language-plaintext highlighter-rouge">minimum_coverage</code> key and an integer value. Since
you can configure the value, you can slowly raise it as you cover more and more code.</p>
<p><img src="/img/2022/simple-cov-check-basic-detailed.png" alt="SimpleCov Basic line view" /></p>
<h3 id="line-vs-branch">Line vs Branch</h3>
<p>As in the name, it relies on the SimpleCov gem, so that needs to be both
installed and configured correctly. With the newer versions of SimpleCov, you can
specify line vs branch coverage. This allows you to specify how well tested things
like conditional logic branches are vs simple line-by-line coverage.</p>
<p>The configuration value to change coverage type is called <code class="language-plaintext highlighter-rouge">minimum_coverage_type</code></p>
<h3 id="per-file-test-coverage">Per file test coverage</h3>
<p>If you also utilize the simplecov-json gem, you can activate the advanced mode, which will
fail builds based on file coverage percentage. This is really useful for
avoiding some well tested files throwing the results of our overall test suite.</p>
<h2 id="why-its-awesome">Why it’s awesome</h2>
<p>Test coverage is an important, albeit overlooked, part of a great test suite and healthy application.
SimpleCov+ Action helps in several key ways to improve overall confidence in your testing suite.</p>
<h3 id="it-is-incremental">It is incremental</h3>
<p>Let’s say you’re adding test coverage to your application for the first time.
You’re likely going to find many files that are lacking adequate test coverage (or if you’re lucky just a few). By using <code class="language-plaintext highlighter-rouge">minimum_coverage</code> configuration, you can incrementally increase
test coverage a little, a bit at a time. I’ve personally used this to identify what my lowest covered file is and then set the <code class="language-plaintext highlighter-rouge">minimum_coverage</code> to 10% greater. This way you can chunk out the work necessary to increase overall test suite coverage a little at a time.</p>
<h3 id="it-gives-accurate-results">It gives accurate results</h3>
<p>Using the advanced version via simplecov-json, gives the ability to avoid situations where
one file is 100% covered and another is 20% covered, skewing your overall test suite coverage. Let’s say your minimum coverage threshold is 50%. In the case above, the coverage would calculate to 60%, but you’ve got a file at 20%. That isn’t a great metric for ensuring good test coverage when outliers throw results. That’s why I recommend using the advanced file-by-file coverage version. Now for the above situation we’ll keep the same threshold of 50%. Applied to the example, the build would now fail and display the file that only has 20% coverage as the failure point. This is much more useful for ensuring quality code coverage.</p>
<p><img src="/img/2022/simple-cov-check-advanced-detailed.png" alt="SimpleCov Advanced detail view" /></p>
<p>The screenshot above shows the result panel of the action, which will display each file
that failed to meet the minimum test coverage.</p>
<h3 id="it-ensures-new-features-are-well-tested">It ensures new features are well tested</h3>
<p>Having a minimum coverage necessary to deploy code, locks your application into a
base level of coverage. This means that if new features want to be released to production
they must contain proper test coverage. With this action in place, your code coverage
never drops below your minimum coverage threshold.</p>
<h3 id="it-increases-test-suite-confidence">It increases test suite confidence</h3>
<p>Good test coverage ensures that refactoring code goes smoothly. There’s nothing more
frustrating than realizing a large section of the codebase needs reworking only to find
out there is no test coverage of it. Now you have two jobs: refactoring the feature and adding the missing test coverage.
With test coverage becoming a priority, you reduce engineer frustration as well as make the system easier to change.</p>
<h2 id="so-what-are-you-waiting-for">So what are you waiting for?</h2>
<p>Go ahead and try it out. You can start with a low <code class="language-plaintext highlighter-rouge">minimum_coverage</code> and work your way
up to a larger percentage. Every bit of coverage helps in the longevity of
an application.</p>
<p>The current version can be found in the <a href="https://github.com/marketplace/actions/simplecov-action">Github marketplace</a>. SimpleCov+ Action also utilizes itself to ensure the action’s code maintains adequate test coverage. Because of this, there is a great example for how to set this up in your workflow. <a href="https://github.com/marketplace/actions/simplecov-action#example-configuration">Check out example configurations here</a>.</p>
<p>If you end up using this in your project, let me know how it goes in the comments below! Thanks for reading.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/Testing your code thoroughly is an important part of a well functioning and easy to change application. Lack of adequate test coverage can be frustrating when refactoring, upgrading, or tracking down a bug. I’ve always wanted a way to ensure that each file maintains a minimum test coverage and if not fail continuous integration checks. After much searching, I decided to go ahead and build my own. Introducing SimpleCov+ Action for use within your Github actions.Simulating a select dropdown change in Jest2022-04-20T00:00:00+00:002022-04-20T00:00:00+00:00http://joshfrankel.me/blog/simulating-a-select-dropdown-change-in-jest<p>While building a mobile friendly tab component, I created a select dropdown that allowed the user to control
the displayed content. This worked in tandem with the existing click the name of the tab setup. Testing a simple
click event is trivial by using <code class="language-plaintext highlighter-rouge">.click()</code> but I quickly found that simulating a select dropdown change wasn’t as
straightforward nor as documented. I stumbled on the following solution which hopefully helps someone else stuck.
<!--excerpt--></p>
<p>A typical Jest test for a click event may look like:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">it</span><span class="p">(</span><span class="dl">"</span><span class="s2">applies a class to an element</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">tabs</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelectorAll</span><span class="p">(</span><span class="dl">"</span><span class="s2">[data-tabs-target='tab']</span><span class="dl">"</span><span class="p">)</span>
<span class="c1">// Simulate clicking the third tab</span>
<span class="nx">tabs</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">click</span><span class="p">()</span>
<span class="nf">expect</span><span class="p">(</span><span class="nx">tabs</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nx">className</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">)).</span><span class="nf">toEqual</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p>The above is a good representation of what it looks like for the user to click
the third tab element.</p>
<p>Ok, so what about a select element?</p>
<p>First off, select elements dispatch a change event. This means that simply clicking
the select and then the option doesn’t work. Now there are a couple different methods
for changing the current select elements value.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">select</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#mySelect</span><span class="dl">"</span><span class="p">)</span>
<span class="c1">// Change the value</span>
<span class="nx">select</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">tab 3</span><span class="dl">'</span>
<span class="c1">// Change the selected value</span>
<span class="nx">select</span><span class="p">.</span><span class="nx">selected</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">tab 3</span><span class="dl">'</span>
<span class="c1">// Change the selected index</span>
<span class="nx">select</span><span class="p">.</span><span class="nx">selectedIndex</span> <span class="o">=</span> <span class="mi">2</span>
<span class="c1">// set expectations</span>
</code></pre></div></div>
<p>I found that the <code class="language-plaintext highlighter-rouge">select.value =</code> option to work best with my scenario. There’s a problem here though. Jest doesn’t know that the select element has changed. That’s because the event wasn’t emmitted yet. We can do that by explictly telling the select
element that it triggered a change event.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">select</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#mySelect</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">select</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">tab 3</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">select</span><span class="p">.</span><span class="nf">dispatchEvent</span><span class="p">(</span><span class="k">new</span> <span class="nc">Event</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">));</span>
<span class="c1">// set expectations</span>
</code></pre></div></div>
<p>With <code class="language-plaintext highlighter-rouge">dispatchEvent(new Event('change'))</code> the select element now properly sends
the change event with the previously updated value. This simulates a select dropdown
change and allows for testing your JavaScript components which rely upon them.</p>
<p>That’s it! Thanks for reading.</p>Josh Frankel (@joshmfrankel)http://joshfrankel.me/While building a mobile friendly tab component, I created a select dropdown that allowed the user to control the displayed content. This worked in tandem with the existing click the name of the tab setup. Testing a simple click event is trivial by using .click() but I quickly found that simulating a select dropdown change wasn’t as straightforward nor as documented. I stumbled on the following solution which hopefully helps someone else stuck.