<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en_GB">
  <link href="https://wilsonsilva.com/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://wilsonsilva.com/" rel="alternate" type="text/html" hreflang="en_GB" />
  <updated>2024-05-26T16:45:00.000Z</updated>
  <id>https://wilsonsilva.com/feed.xml</id>
  <title type="html">Wilson Silva</title>
  <subtitle>Hands-on technical architect. I write about Ruby, software design, AI-assisted development and building products.</subtitle>
  <author><name>Wilson Silva</name></author>
  <entry>
    <title type="html">Today I learned: 26 May 2024</title>
    <link href="https://wilsonsilva.com/today-i-learned-26-may-2024/" rel="alternate" type="text/html" title="Today I learned: 26 May 2024" />
    <published>2024-05-26T16:45:00.000Z</published>
    <updated>2024-05-26T16:45:00.000Z</updated>
    <id>repo://posts.collection/_posts/2024-05-26-til-26-may-2024.md</id>
    <summary type="html">Today I learned how to configure Charles Proxy and truststore to debug HTTPS/SSL Python requests</summary>
    <content type="html" xml:base="https://wilsonsilva.com/today-i-learned-26-may-2024/">&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Using Unify neural router to optimize LLM apps’ speed, latency and cost&lt;/li&gt;
&lt;li&gt;Configuring Charles Web Debugging Proxy to inspect HTTPS/SSL traffic&lt;/li&gt;
&lt;li&gt;Leveraging &lt;code&gt;truststore&lt;/code&gt; to mitigate self-signed certificate errors in Python&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://unify.ai&quot;&gt;Unify&lt;/a&gt; is a neural router for LLM endpoints. It automatically selects and routes prompts to the best
LLM endpoint to get the best output while optimizing for speed, latency, and cost to supercharge LLM applications.&lt;/p&gt;
&lt;p&gt;It provides a Python SDK, but not a Ruby one. Integrating it into the project was an opportunity to practice Python.
But this process was not smooth.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/posts/til-26-may-2024/unify-ai-chat-comparison-ui.png&quot; alt=&quot;Unify UI&quot;&gt;
  &lt;figcaption&gt;Unify UI&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-unify-neural-router-to-optimize-llm-apps-speed-latency-and-cost&quot;&gt;Using Unify neural router to optimize LLM apps’ speed, latency and cost&lt;/h2&gt;
&lt;p&gt;I followed their &lt;a href=&quot;https://unify.ai/docs/api/first_request.html&quot;&gt;Making Your First Request&lt;/a&gt; tutorial to learn how to use
the platform. These were the steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sign up on the &lt;a href=&quot;https://console.unify.ai/login&quot;&gt;Unify website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Copy the default API key from the &lt;a href=&quot;https://console.unify.ai/&quot;&gt;Unify console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Add the API key to an &lt;code&gt;.env&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Create a Python script with code to initialize the Unify client and generate a response&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; unify &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Unify, list_models, list_providers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; dotenv &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; load_dotenv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;load_dotenv()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;unify &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Unify(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;mistral-7b-instruct-v0.2@fireworks-ai&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;response &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; unify.generate(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;Does discipline equal freedom?&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I ran the Python script but didn’t see any output or errors. I wasn’t sure if the API was responding correctly.&lt;/p&gt;
&lt;h2 id=&quot;configuring-charles-web-debugging-proxy-to-inspect-httpsssl-traffic&quot;&gt;Configuring Charles Web Debugging Proxy to inspect HTTPS/SSL traffic&lt;/h2&gt;
&lt;p&gt;Then I launched Charles Web Debugging Proxy to debug the Unify API requests. This tool lets developers inspect HTTP
and HTTPS traffic between a client and server.&lt;/p&gt;
&lt;p&gt;I enabled the macOS proxy in Charles (&lt;code&gt;Proxy &gt; macOS Proxy&lt;/code&gt;) and re-ran the Python script. However, the request was
encrypted with SSL.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/posts/til-26-may-2024/ssl-encrypted-unify-request.png&quot; alt=&quot;SSL encrypted Unify request in Charles&quot;&gt;
  &lt;figcaption&gt;SSL encrypted Unify request in Charles&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Charles has SSL debugging, allowing decryption of encrypted data to review/troubleshoot the transmitted content. So I
configured it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;api.unify.ai:443&lt;/code&gt; to the SSL proxy settings &lt;code&gt;Proxy &gt; SSL Proxying Settings...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install the Charles root certificate: &lt;code&gt;Help &gt; SSL Proxying &gt; Install Charles Root Certificate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Trust the certificate in &lt;strong&gt;Keychain Access&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enable SSL Proxying: &lt;code&gt;Proxy &gt; Start SSL Proxying&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I had SSL proxying set up, but I still couldn’t see the traffic. Running the Python script now threw an SSL
certificate verification error:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;self-signed&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; certificate&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; certificate&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; chain&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; (_ssl.c:1000)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;leveraging-truststore-to-mitigate-self-signed-certificate-errors-in-python&quot;&gt;Leveraging &lt;code&gt;truststore&lt;/code&gt; to mitigate self-signed certificate errors in Python&lt;/h2&gt;
&lt;p&gt;To resolve the self-signed certificate error, I tried many approaches and all led to frustration. Here’s what worked:&lt;/p&gt;
&lt;p&gt;First, I installed the &lt;code&gt;truststore&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; truststore&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, at the top of my Python script, I added:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; truststore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;truststore.inject_into_ssl()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, I could see the decrypted request in Charles:&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/posts/til-26-may-2024/decrypted-unify-request.png&quot; alt=&quot;Decrypted Unify request in Charles&quot;&gt;
  &lt;figcaption&gt;Decrypted Unify request in Charles&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But why didn’t I see them in the terminal? I forgot to &lt;code&gt;print&lt;/code&gt; the &lt;code&gt;response&lt;/code&gt; 😂:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; unify &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Unify, list_models, list_providers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; dotenv &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; load_dotenv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;load_dotenv()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;unify &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Unify(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;mistral-7b-instruct-v0.2@fireworks-ai&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;response &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; unify.generate(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;Does discipline equal freedom?&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(response) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;# &amp;#x3C;- Catalyst for learning&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Who knew forgetting a &lt;code&gt;print&lt;/code&gt; statement could be so educational? 😅 Turns out, tiny mistakes can lead to big learnings,
like mastering debugging tools and sharing knowledge through blog posts.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Mistral Ruby gem</title>
    <link href="https://wilsonsilva.com/mistral-ruby-gem/" rel="alternate" type="text/html" title="Mistral Ruby gem" />
    <published>2024-05-05T15:20:00.000Z</published>
    <updated>2024-05-05T15:20:00.000Z</updated>
    <id>repo://posts.collection/_posts/2024-05-05-mistral-ruby-gem.md</id>
    <summary type="html">Why I ported Mistral’s Python client to Ruby</summary>
    <content type="html" xml:base="https://wilsonsilva.com/mistral-ruby-gem/">&lt;h2 id=&quot;why-i-ported-mistrals-client-to-ruby&quot;&gt;Why I ported Mistral’s client to Ruby&lt;/h2&gt;
&lt;h3 id=&quot;python-is-everywhere&quot;&gt;Python is everywhere&lt;/h3&gt;
&lt;p&gt;I am taking several AI courses to become proficient in AI development techniques like Retrieval Augmented Generation
(RAG), DSPy, and agentic workflows. Most AI libraries, courses, and tutorials &lt;strong&gt;use Python&lt;/strong&gt; as the primary language.&lt;/p&gt;
&lt;p&gt;I finished a course recently called &lt;a href=&quot;https://www.deeplearning.ai/short-courses/getting-started-with-mistral?ref=wilsonsilva.com&quot;&gt;Getting Started With Mistral&lt;/a&gt;.
It taught me effective prompting techniques, function calling, JSON mode, and RAG.&lt;/p&gt;
&lt;p&gt;To reinforce my learning, I began &lt;a href=&quot;https://github.com/wilsonsilva/getting-started-with-mistral-ai/tree/main&quot;&gt;transcribing the course&lt;/a&gt;.
I wrote all code samples in the same language they were taught: Python.&lt;/p&gt;
&lt;h3 id=&quot;sharing-a-common-language&quot;&gt;Sharing a common language&lt;/h3&gt;
&lt;p&gt;What if developers could learn AI using Python and then easily apply their knowledge to their language of choice?&lt;/p&gt;
&lt;p&gt;For this to happen in the Ruby ecosystem, we need libraries that match those in Python, with similar features and APIs.
That’s why I created a &lt;a href=&quot;https://github.com/wilsonsilva/mistral&quot;&gt;&lt;code&gt;Mistral gem&lt;/code&gt;&lt;/a&gt; that closely mirrors the official Python
client. It was also a great exercise to practice Python.&lt;/p&gt;
&lt;p&gt;Take a look at the code samples below to see how easy it is to translate between the two libraries thanks to their
similar APIs.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; MistralClient(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;api_key&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;os.environ[&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;MISTRAL_API_KEY&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;response &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; client.chat_stream(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;  model&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;mistral-small&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;  messages&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;[ChatMessage(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;role&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;Hi&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; chunk &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; response:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;  print&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(chunk.choices[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;].delta.content)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Mistral&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;api_key:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#F8F8F2&quot;&gt; ENV&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;MISTRAL_API_KEY&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; client.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;chat_stream&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  model:&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;mistral-small&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  messages:&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Mistral&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;ChatMessage&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;role:&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;content:&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Hi&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; chunk &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; response &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;  print&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(chunk.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;choices&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;delta&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;other-gems&quot;&gt;Other gems&lt;/h3&gt;
&lt;p&gt;When this was written, only two mistral gems existed: &lt;code&gt;mistral_rb&lt;/code&gt; and &lt;code&gt;mistral-ai&lt;/code&gt;. Here’s how they compare to each
other and my gem.&lt;/p&gt;



















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Feature&lt;/th&gt;&lt;th&gt;&lt;code&gt;mistral-ai&lt;/code&gt;&lt;/th&gt;&lt;th&gt;&lt;code&gt;mistral_rb&lt;/code&gt;&lt;/th&gt;&lt;th&gt;&lt;code&gt;mistral&lt;/code&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Completion without streaming&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Completion with streaming&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Embeddings&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Response formats&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;List models&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Function calling&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Type safety&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Familiar interface&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Automatic retries&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Configurable timeouts&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Code examples&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Runtime dependencies&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Python’s built-in dependency management is brittle. But tools like &lt;a href=&quot;https://python-poetry.org/&quot;&gt;Poetry&lt;/a&gt; offer a more robust solution, similar to &lt;a href=&quot;https://bundler.io/&quot;&gt;Bundler&lt;/a&gt; in the Ruby ecosystem.&lt;/li&gt;
&lt;li&gt;Gained experience with &lt;a href=&quot;https://docs.pydantic.dev/&quot;&gt;Pydantic&lt;/a&gt; for data validation and &lt;a href=&quot;https://docs.pytest.org/&quot;&gt;pytest&lt;/a&gt; for testing in Python&lt;/li&gt;
&lt;li&gt;Experimented with &lt;a href=&quot;https://github.com/minitest/minitest&quot;&gt;minitest&lt;/a&gt; as a lightweight alternative to &lt;a href=&quot;https://rspec.info/&quot;&gt;RSpec&lt;/a&gt; for testing Ruby code&lt;/li&gt;
&lt;li&gt;Deepened my understanding of implementing &lt;a href=&quot;https://ruby-doc.org/core-3.1.0/Enumerator.html&quot;&gt;enumerators&lt;/a&gt; effectively in Ruby&lt;/li&gt;
&lt;li&gt;Compared to Ruby, Python is ugly 😂&lt;/li&gt;
&lt;/ul&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Nostr 0.7.0 - Logging</title>
    <link href="https://wilsonsilva.com/nostr-0.7.0-logging/" rel="alternate" type="text/html" title="Nostr 0.7.0 - Logging" />
    <published>2024-04-13T13:22:00.000Z</published>
    <updated>2024-04-13T13:22:00.000Z</updated>
    <id>repo://posts.collection/_posts/2024-04-13-nostr-0.7.0-logging.md</id>
    <summary type="html">Changes introduced in version 0.7.0 of the nostr gem</summary>
    <content type="html" xml:base="https://wilsonsilva.com/nostr-0.7.0-logging/">&lt;p&gt;I’m excited to announce the release of version &lt;a href=&quot;https://github.com/wilsonsilva/nostr/blob/main/CHANGELOG.md#070-2024-04-13&quot;&gt;&lt;code&gt;0.7.0&lt;/code&gt; of the nostr gem&lt;/a&gt;,
which introduces logging and debugging capabilities to help you monitor and troubleshoot your Nostr client’s
interactions with relays.&lt;/p&gt;
&lt;p&gt;As an open-source maintainer, I appreciate community engagement. On March 13th, a developer asked
for my help to debug an issue with integrating the nostr gem into his application:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello Wilson,&lt;/p&gt;
&lt;p&gt;I’m Matej Lukášik a RoR developer. Recently I became interested in nostr and found your client. It looks pretty neat
and I really appreciate the care you took around documentation and examples.&lt;/p&gt;
&lt;p&gt;I would like to help. However, I can’t make it work :-D This are of websockets and event machine are quite new to me
and I don’t even know how to debug things properly.&lt;/p&gt;
&lt;p&gt;Would you ever have some time to show me around and make it work on my computer so that I can tinker with it later? I
would pay you your normal hourly rate if you like.&lt;/p&gt;
&lt;p&gt;What do you think? Would you find time to give me a paid introduction to the gem for an hour or so?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was happy to assist for free. We jumped on a call a few hours later.&lt;/p&gt;
&lt;p&gt;While debugging, I realized that the nostr gem needed default logging to provide developers with visibility into their
application’s relay interactions. This realization led to prioritizing logging in this release.&lt;/p&gt;
&lt;p&gt;I also realized Matej’s code was immediately exiting, potentially before the client could handle all asynchronous
operations.&lt;/p&gt;
&lt;p&gt;You see, the Nostr client relies on &lt;code&gt;EventMachine&lt;/code&gt;, which uses a reactor pattern. For the client to process all the
events and messages properly, the main thread needs to be kept alive while the &lt;code&gt;EventMachine&lt;/code&gt; reactor runs in the
background. By adding &lt;code&gt;gets&lt;/code&gt;, we pause the main thread, waiting for user input, and giving the background thread the
time it needs to do its job.&lt;/p&gt;
&lt;p&gt;This small addition to the example code ensures that the client can connect to relays, send messages, and receive
responses as expected. It’s a tiny but important detail, especially for newcomers who might be wondering why their
client disconnects unexpectedly.&lt;/p&gt;
&lt;p&gt;To make sure developers have a smooth experience with the nostr gem, I’ve updated the README with this helpful tidbit.&lt;/p&gt;
&lt;h2 id=&quot;built-in-logging-functionality&quot;&gt;Built-in Logging Functionality&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Nostr::Client&lt;/code&gt; class now comes with built-in logging functionality. By default, it uses the &lt;code&gt;ColorLogger&lt;/code&gt;, which
logs events in color. Here’s an example of how to use the default logger:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/posts/color-logger.png&quot; alt=&quot;Logging with colors&quot;&gt;
  &lt;figcaption&gt;Logging with colors&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you prefer plain text logging without colors, you can use the &lt;code&gt;PlainLogger&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &apos;nostr/client/plain_logger&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;logger:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;PlainLogger&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/posts/plain-logger.png&quot; alt=&quot;Logging without colors&quot;&gt;
  &lt;figcaption&gt;Logging without colors&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To disable logging entirely, simply pass &lt;code&gt;logger: nil&lt;/code&gt; when creating the client instance:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;logger:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;customizable-loggers&quot;&gt;Customizable Loggers&lt;/h2&gt;
&lt;p&gt;In addition to the default loggers, you can create your own custom logger by subclassing &lt;code&gt;Nostr::Client::Logger&lt;/code&gt; and
overriding its methods to tailor the logging format to your needs:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-text-decoration:underline&quot;&gt; CustomLogger&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; &amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;Nostr::Client::Logger&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; on_connect&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;relay&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;    puts&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Connected to relay: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;relay.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; on_message&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;    puts&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Received message: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; on_send&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;    puts&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Sent message: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; on_error&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;    puts&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Error: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; on_close&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-dark:#FD971F;--shiki-dark-font-style:italic&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#66D9EF&quot;&gt;    puts&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &quot;Connection closed: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; (Code: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;#{&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F8F8F2&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#F92672&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;logger:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; CustomLogger&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;more-changes-in-070&quot;&gt;More Changes in 0.7.0&lt;/h2&gt;
&lt;p&gt;This release includes several other improvements and updates. For a full list of changes, please refer to the
&lt;a href=&quot;https://github.com/wilsonsilva/nostr/blob/main/CHANGELOG.md#070-2024-04-13&quot;&gt;CHANGELOG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I would like to express my sincere gratitude to Matej Lukášik for reaching out, providing valuable feedback. His input
directly influenced the implementation of logging in this release.&lt;/p&gt;
&lt;p&gt;I encourage all users to continue sharing their experiences, suggestions, and feature requests. Your feedback is
invaluable in shaping the future of the nostr gem. Together, we can make it an even more powerful and developer-friendly
tool for building Nostr applications in Ruby.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Nostr 0.6.0 - Signatures</title>
    <link href="https://wilsonsilva.com/nostr-0.6.0-signatures/" rel="alternate" type="text/html" title="Nostr 0.6.0 - Signatures" />
    <published>2024-03-19T13:49:00.000Z</published>
    <updated>2024-03-19T13:49:00.000Z</updated>
    <id>repo://posts.collection/_posts/2024-03-19-nostr-0.6.0-signatures.md</id>
    <summary type="html">Changes introduced in version 0.6.0 of the nostr gem</summary>
    <content type="html" xml:base="https://wilsonsilva.com/nostr-0.6.0-signatures/">&lt;p&gt;I’m excited to share the release of version &lt;a href=&quot;https://github.com/wilsonsilva/nostr/blob/main/CHANGELOG.md#060-2024-03-15&quot;&gt;&lt;code&gt;0.6.0&lt;/code&gt; of the nostr gem&lt;/a&gt;.
Marking a significant update with a focus on enhancing cryptographic functionalities and introducing a systematic
approach to documenting architectural decisions.&lt;/p&gt;
&lt;h2 id=&quot;whats-new-in-060&quot;&gt;What’s new in &lt;code&gt;0.6.0&lt;/code&gt;?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Architecture Decision Records (ADRs)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message signing and verification&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event signature verification&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;KeyPair&lt;/code&gt; destructuring&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;detailed-overview&quot;&gt;Detailed Overview&lt;/h2&gt;
&lt;h3 id=&quot;architecture-decision-records-adrs&quot;&gt;Architecture Decision Records (ADRs)&lt;/h3&gt;
&lt;p&gt;I’ve begun documenting architectural decisions using Architecture Decision Records (ADRs). This structured approach
aids in recording architectural changes and their rationale.&lt;/p&gt;
&lt;p&gt;An ADR typically includes the following sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title&lt;/strong&gt;: A short, descriptive name for the architectural decision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status&lt;/strong&gt;: The current status (proposed, accepted, deprecated, etc.) of the decision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;: The circumstances that led to the decision being considered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decision&lt;/strong&gt;: A detailed explanation of the decision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consequences&lt;/strong&gt;: The expected impact of the decision, both positive and negative.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s an abbreviated example of an ADR for introducing the &lt;code&gt;Signature&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#A6E22E;--shiki-dark-font-weight:bold&quot;&gt;# ADR 0001: Introduction of Signature Class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#A6E22E;--shiki-dark-font-weight:bold&quot;&gt;## Status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;Accepted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#A6E22E;--shiki-dark-font-weight:bold&quot;&gt;## Context&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;Before the introduction of the &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#FD971F&quot;&gt;`Signature`&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; class, signatures were&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;handled as simple strings throughout the nostr gem, leading to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;repeated validation logic and the potential for errors.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#A6E22E;--shiki-dark-font-weight:bold&quot;&gt;## Decision&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;To encapsulate signature handling and validation in a single,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;cohesive unit, we introduced the &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#FD971F&quot;&gt;`Signature`&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; class. This class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;enforces the necessary validations at the point of creation,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;providing a more robust and error-resistant approach.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#A6E22E;--shiki-dark-font-weight:bold&quot;&gt;## Consequences&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;Positive:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#A6E22E&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Centralized signature validation logic, reducing repetition and potential errors.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#A6E22E&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Improved code readability and maintainability.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;Negative:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#A6E22E&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Requires updates to existing codebases to utilize the new class.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#A6E22E&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; Adds a slight learning curve for new developers.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;message-signing-and-verification&quot;&gt;Message signing and verification&lt;/h3&gt;
&lt;p&gt;Introducing a &lt;code&gt;Signature&lt;/code&gt; class addresses primitive obsession with string signatures by providing a dedicated class for
signature handling, including validation and error handling.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;signature&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Signature&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&apos;your_signature_here&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This may raise &lt;code&gt;InvalidSignatureTypeError&lt;/code&gt;, &lt;code&gt;InvalidSignatureLengthError&lt;/code&gt;, or &lt;code&gt;InvalidSignatureFormatError&lt;/code&gt; if the
signature is invalid.&lt;/p&gt;
&lt;p&gt;Furthermore, new functionalities for signing messages and verifying the authenticity of messages and events have been
added, ensuring data integrity within the nostr ecosystem.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;key_pair&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;KeyPair&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  private_key:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;PrivateKey&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&apos;7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  public_key:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;PublicKey&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&apos;15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;crypto&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Crypto&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &apos;Hello, nostr!&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;signature&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; crypto.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;sign_message&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(message, key_pair.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;private_key&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;crypto.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;valid_sig?&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(message, key_pair.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;public_key&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, signature) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;# =&gt; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;valid_sig?&lt;/code&gt; method checks if a signature is valid, whereas &lt;code&gt;check_sig!&lt;/code&gt; raises an error if the signature is
invalid.&lt;/p&gt;
&lt;h3 id=&quot;event-signature-verification&quot;&gt;Event signature verification&lt;/h3&gt;
&lt;p&gt;Signing an event ensures its authenticity. Verification confirms the event was not tampered with.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;event.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;sign&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(private_key)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;event.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;verify_signature&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt; # true or false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;keypair-destructuring&quot;&gt;&lt;code&gt;KeyPair&lt;/code&gt; destructuring&lt;/h3&gt;
&lt;p&gt;Enhancing usability, the &lt;code&gt;KeyPair&lt;/code&gt; class now supports destructuring, making it easier to work with private and public
keys.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;key_pair&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;KeyPair&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  private_key:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;PrivateKey&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&apos;7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;  public_key:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Nostr&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;PublicKey&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;&apos;15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;private_key, public_key &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; key_pair&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;coming-next&quot;&gt;Coming next&lt;/h2&gt;
&lt;p&gt;The next release will focus on enhancing error messages and debugging capabilities. Feedback is welcome as development
continues. Feel free to &lt;a href=&quot;https://github.com/wilsonsilva/nostr/discussions&quot;&gt;start a discussion on the GitHub repository&lt;/a&gt;.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Today I learned: 8 Sep 2023</title>
    <link href="https://wilsonsilva.com/today-i-learned-8-sep-2023/" rel="alternate" type="text/html" title="Today I learned: 8 Sep 2023" />
    <published>2023-09-08T05:00:00.000Z</published>
    <updated>2023-09-08T05:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2023-09-08-til-8-sep-2023.md</id>
    <summary type="html">Today I learned about Plunk, Plausible Analytics, Cloudflare Pages, Open Graph, Sass CLI, Vite’s HTML env replacement and Gandi.net fees</summary>
    <content type="html" xml:base="https://wilsonsilva.com/today-i-learned-8-sep-2023/">&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://useplunk.com?ref=wilsonsilva.com&quot;&gt;Plunk&lt;/a&gt; has a &lt;strong&gt;generous&lt;/strong&gt; free plan, ideal for low-budget indie hackers&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://plausible.io?ref=wilsonsilva.com&quot;&gt;Plausible Analytics&lt;/a&gt; is dead simple and privacy-friendly&lt;/li&gt;
&lt;li&gt;Deploying with Cloudflare pages is simple and fast&lt;/li&gt;
&lt;li&gt;Open Graph tags are useful to make links look good on social media&lt;/li&gt;
&lt;li&gt;Compiling Sass in the CLI&lt;/li&gt;
&lt;li&gt;Replacing env variables in HTML files with Vite&lt;/li&gt;
&lt;li&gt;Gandi.net domains cost 2 to 3 times more than other registrars&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;From today onwards, I will blog about my learning journeys and other things I find interesting. I’m not sure how often
I’ll do it, but I’ll try to do it as often as possible. This is the first post of this kind.&lt;/p&gt;
&lt;p&gt;Today I developed a basic landing page for my social accountability platform, &lt;a href=&quot;https://duty.camp&quot;&gt;duty.camp&lt;/a&gt;, with a
mission statement and a form to capture email addresses of people interested in the project.&lt;/p&gt;
&lt;p&gt;It is &lt;del&gt;a modified copy of once.com source code&lt;/del&gt; inspired by 37signals &lt;a href=&quot;https://once.com?ref=wilsonsilva.com&quot;&gt;once.com&lt;/a&gt; landing page.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/duty.camp-landing-page.png&quot; alt=&quot;Duty Camp landing page&quot;&gt;
  &lt;figcaption&gt;Duty Camp landing page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While doing that, I learned some useful things that I want to share with you.&lt;/p&gt;
&lt;h2 id=&quot;plunk&quot;&gt;Plunk&lt;/h2&gt;
&lt;p&gt;As part of my enrolment in &lt;em&gt;buildspace’s nights &amp;#x26; weekends&lt;/em&gt; season 4, I created a landing page for
&lt;a href=&quot;https://classes.pt?ref=wilsonsilva.com&quot;&gt;classes.pt&lt;/a&gt; using ConvertKit. I was happy with the result but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It took me too long to deliver a simple landing page&lt;/li&gt;
&lt;li&gt;The paid plan was too expensive, considering that I only captured 3 emails&lt;/li&gt;
&lt;li&gt;The 2-week trial period was too short&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was looking for a cheaper alternative. As always, I even considered developing my own! Fortunately, I found
&lt;a href=&quot;https://useplunk.com?ref=wilsonsilva.com&quot;&gt;Plunk&lt;/a&gt;, which has a generous free plan, allowing me to capture as much as
3000 emails per month! Plunk is&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“the affordable, developer-friendly email platform that brings together marketing, transactional and broadcast emails
into one single, complete solution”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I hope that one day I have enough revenue to repay the creator for his generosity.&lt;/p&gt;
&lt;h2 id=&quot;plausible-analytics&quot;&gt;Plausible Analytics&lt;/h2&gt;
&lt;p&gt;I also added &lt;a href=&quot;https://plausible.io?ref=wilsonsilva.com&quot;&gt;Plausible Analytics&lt;/a&gt; to the site. It’s a privacy-friendly
alternative to Google Analytics. It’s simple, fast and the 30 day trial period is more than enough to evaluate its
limited features. I’m happy with it and I’ll probably upgrade to the paid plan in the future. I got a visitor from
Belgium. I’m sure it was from the creator of Plunk, &lt;a href=&quot;https://twitter.com/driaug_?ref=wilsonsilva.com&quot;&gt;Dries Augustyns&lt;/a&gt;!&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/plausible-analytics-visitors.png&quot; alt=&quot;Plausible Analytics visitors by country&quot;&gt;
  &lt;figcaption&gt;Plausible Analytics visitors by country&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;All visits from Thailand are mine. I have no idea how I got visitors from the United States. Perhaps it was a bot or
Cloudflare.&lt;/p&gt;
&lt;h2 id=&quot;deploying-with-cloudflare-pages&quot;&gt;Deploying with Cloudflare Pages&lt;/h2&gt;
&lt;p&gt;Cloudflare Pages is a new service that allows you to deploy static sites for free. It allows deployments via
folder uploads as well as automated builds via git. I chose the latter. I was surprised how easy it was to set up.&lt;/p&gt;
&lt;p&gt;On top of that, it is great to use a single platform to manage the needs of a static site: domain registration, DNS,
CDN, SSL, deployment and much more.&lt;/p&gt;
&lt;h2 id=&quot;open-graph-tags&quot;&gt;Open Graph Tags&lt;/h2&gt;
&lt;p&gt;The Open Graph protocol enables any web page to become a rich object in a social graph. For instance, this is used on
Facebook to allow any web page to have the same functionality as any other object on Facebook.&lt;/p&gt;
&lt;p&gt;I knew that this could be achieved via meta tags, but never had the need to do it. By reading once’s source code, I
was surprised how easy it was to do it.&lt;/p&gt;
&lt;h2 id=&quot;compiling-sass-in-the-cli&quot;&gt;Compiling Sass in the CLI&lt;/h2&gt;
&lt;p&gt;I have used sass/scss in the past and I briefly remember the syntax. But I had no idea how to compile it directly and
what the underscore files were for. I assumed they were private files but I was wrong. They are partials and they are
used to split the sass code into multiple files. This is useful to keep the code organised and to avoid having a single
file with thousands of lines of code.&lt;/p&gt;
&lt;p&gt;Finally, to compile and retain the source map of a directory with this structure:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;assets/_sass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; base&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;    ├──&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; _fonts.scss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;    ├──&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; _reset.scss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt;    └──&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; _root.scss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; styles.scss&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Into &lt;code&gt;assets/css/styles.css&lt;/code&gt;, I just had to run the command &lt;code&gt;sass --watch assets/_sass:assets/css&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;replacing-env-variables-in-html-files-with-vite&quot;&gt;Replacing env variables in HTML files with Vite&lt;/h2&gt;
&lt;p&gt;Eventually, I found myself in need of a simple way to serve the page in development mode and production mode. In
development mode it should use localhost. In production mode it should use the domain name.&lt;/p&gt;
&lt;p&gt;Vite also supports &lt;a href=&quot;https://vitejs.dev/guide/env-and-mode.html#html-env-replacement&quot;&gt;replacing env variables in HTML files&lt;/a&gt;.
Any properties in &lt;code&gt;import.meta.env&lt;/code&gt; can be used in HTML files with a special &lt;code&gt;%ENV_NAME%&lt;/code&gt; syntax:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#F92672&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&gt;Vite is running in %MODE%&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#F92672&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#F92672&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&gt;Using data from %VITE_API_URL%&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#F92672&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the env doesn’t exist in &lt;code&gt;import.meta.env&lt;/code&gt;, e.g. &lt;code&gt;%NON_EXISTENT%&lt;/code&gt;, it will be ignored and not replaced, unlike
&lt;code&gt;import.meta.env.NON_EXISTENT&lt;/code&gt; in JS where it’s replaced as undefined.&lt;/p&gt;
&lt;h2 id=&quot;gandinet-high-fees&quot;&gt;Gandi.net high fees&lt;/h2&gt;
&lt;p&gt;Gandi.net has been overcharging me for all domains and offering me little to no value in return. I’m moving all my
domains 5 to Cloudflare. Here’s an example of how much I’m saving. Renewing duty.camp would cost me 95€, worth 102 USD
at the time of writing. In Cloudflare it costs 35 USD. &lt;strong&gt;That’s a 3x difference!&lt;/strong&gt;&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">In defense of ‘boring’ tech: Pieter Levels’ SaaS success stories</title>
    <link href="https://wilsonsilva.com/in-defense-of-boring-tech-pieter-levels-saas-success-stories/" rel="alternate" type="text/html" title="In defense of ‘boring’ tech: Pieter Levels’ SaaS success stories" />
    <published>2023-06-16T04:15:00.000Z</published>
    <updated>2023-06-16T04:15:00.000Z</updated>
    <id>repo://posts.collection/_posts/2023-06-16-in-defense-of-boring-tech-pieter-levels-saas-success-stories.md</id>
    <summary type="html">The significant value of mastering ‘boring’, reliable technologies over chasing tech trends</summary>
    <content type="html" xml:base="https://wilsonsilva.com/in-defense-of-boring-tech-pieter-levels-saas-success-stories/">&lt;p&gt;As we continually chase the latest tech trends, let’s not forget the value in embracing ‘boring’ but reliable technology. I personally aspire to emulate the likes of Pieter Levels, a well-known Indie Hacker. Utilizing tried-and-true tools like PHP and jQuery, he’s built a consistent monthly SaaS revenue stream of $250,000. His success is a testament to the fact that stable technology, mastered and applied effectively, often outweighs the allure of the newest trends. It’s a reminder that mastery of the fundamentals can lead to substantial success.&lt;/p&gt;
&lt;p&gt;The strategies effective for a solopreneur without any stakeholders, deadlines or code-sharing responsibilities may not translate as efficiently to the context of a medium to large-scale enterprise. There are no silver bullets, but Levels does seem to be Blade!&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/pieter-levels-saas-mrr-stats.jpeg&quot; alt=&quot;Pieter Levels SaaS MRR stats&quot;&gt;
  &lt;figcaption&gt;Pieter Levels SaaS MRR stats&lt;/figcaption&gt;
&lt;/figure&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">The perils of a hyperactive mind</title>
    <link href="https://wilsonsilva.com/the-perils-of-a-hyperactive-mind/" rel="alternate" type="text/html" title="The perils of a hyperactive mind" />
    <published>2023-04-18T04:15:00.000Z</published>
    <updated>2023-04-18T04:15:00.000Z</updated>
    <id>repo://posts.collection/_posts/2023-04-18-the-perils-of-a-hyperactive-mind.md</id>
    <summary type="html">My eagerness to start everything hinders my ability to finish anything</summary>
    <content type="html" xml:base="https://wilsonsilva.com/the-perils-of-a-hyperactive-mind/">&lt;p&gt;Ten years ago, I came across one of my favorite adages - &lt;a href=&quot;https://jamesclear.com/mental-toughness-marathon-monks&quot;&gt;&lt;em&gt;if you commit to nothing, you’ll be distracted by everything&lt;/em&gt;&lt;/a&gt;.
Back then, I was hyper-focused, hyper-committed, hyper-disciplined, and hyper-productive.&lt;/p&gt;
&lt;p&gt;Fast forward to April 2023, things have changed. Although I’m interested in starting everything, I lack the commitment
to finish anything. Here’s an example of what happened today:&lt;/p&gt;
&lt;p&gt;I had scheduled 30 minutes for studying Thai and gathered my study materials - two boxes of flashcards for vowels and
consonants. The consonant card box had instructions on memorablethai.com, while the vowel box had a video memory system
on howtolearnthai.com.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/learn-thai-flashcards.jpg&quot; alt=&quot;Learn Thai flashcards&quot;&gt;
  &lt;figcaption&gt;Learn Thai flashcards&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unfortunately, howtolearnthai.com was no longer available, and memorablethai.com redirected to
lannainnovation.com.&lt;/p&gt;
&lt;p&gt;After some searching, I found the &lt;em&gt;Innovative Video Memory System&lt;/em&gt; on YouTube in the form of playlists. However, this
was the second time the support materials were unavailable to me. So, I decided to download the YouTube videos to my
laptop.&lt;/p&gt;
&lt;p&gt;While attempting to download the 34 videos, I faced several issues with different websites. Some could only download a
video at a time, while others downloaded the video at ridiculously slow speeds. Eventually, I found the
Pytube Python library, which helped me accomplish my goal.&lt;/p&gt;
&lt;p&gt;I considered creating a Ruby equivalent. This led me to read about the YouTube API, but before I knew it, I was writing
this blog post about my experience. My mind had hopped from one interest to another, and I never got around to learning
Thai.&lt;/p&gt;
&lt;p&gt;I worked on this blog post for an hour, and although I didn’t learn Thai, I made progress towards my blogging goal.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Getting a motorcycle driver’s license in Phuket</title>
    <link href="https://wilsonsilva.com/getting-a-motorcycle-drivers-license-in-phuket/" rel="alternate" type="text/html" title="Getting a motorcycle driver’s license in Phuket" />
    <published>2023-04-07T04:30:00.000Z</published>
    <updated>2023-04-07T04:30:00.000Z</updated>
    <id>repo://posts.collection/_posts/2023-04-07-getting-a-motorcycle-drivers-license-in-phuket.md</id>
    <summary type="html">How I got my Thai motorcycle driver’s license and a motorcycle in Phuket, Thailand.</summary>
    <content type="html" xml:base="https://wilsonsilva.com/getting-a-motorcycle-drivers-license-in-phuket/">&lt;p&gt;I recently obtained my motorcycle driver’s license at &lt;a href=&quot;https://goo.gl/maps/iXz3pyp8GRMSLnMc6&quot;&gt;Honda Safety Riding Park Phuket&lt;/a&gt;. The &lt;a href=&quot;https://hondasafety.thaihonda.co.th/course?lang=en&quot;&gt;online booking&lt;/a&gt; process proved to be a bit challenging since the website was not entirely in English. However, I sought assistance from the staff by &lt;a href=&quot;https://www.facebook.com/HondaSafetyRidingParkPhuket/&quot;&gt;contacting them&lt;/a&gt; and also visited the park in person to get my questions answered.&lt;/p&gt;
&lt;p&gt;Securing a spot for the &lt;strong&gt;Pre-Riding course&lt;/strong&gt; required booking about 3 months in advance due to high demand. The cost was &lt;strong&gt;2000 THB&lt;/strong&gt;, and the practical lessons spanned 2 days. On the first day, I went through a physical aptitude test, received a brief introduction to the motorcycle’s basic functions, and then spent the rest of the day riding it through an obstacle course.&lt;/p&gt;
&lt;p&gt;The second day involved more riding practice, an easy practical riding test, and a relatively quick theoretical lesson with a few mock exams on a computer. To pass the real exam, I needed to get &lt;strong&gt;45&lt;/strong&gt; questions correct out of &lt;strong&gt;50&lt;/strong&gt;. The questions and answers were automatically translated from Thai to English, making the test a bit harder than expected.&lt;/p&gt;
&lt;p&gt;Thankfully, I passed the exam on my first attempt. After passing the exam, I received a certificate. The next step was to visit a medical clinic to get a medical test done, which cost around &lt;strong&gt;100 THB&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To proceed with obtaining the license, I needed a proof of address document, and I followed these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Gathered the required documents, including &lt;strong&gt;passport-sized pictures, passport copies, copy of my visa, TM30, and 300 baht for processing fees&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Visited the local &lt;a href=&quot;https://goo.gl/maps/82mwgP3kHbxn2fqU9&quot;&gt;Immigration Office in Phuket&lt;/a&gt; to submit all the required documents and processing fee for the proof of address document.&lt;/li&gt;
&lt;li&gt;The Immigration Office processed my application and provided me with the proof of address document. I made sure to obtain at least &lt;strong&gt;two copies&lt;/strong&gt; since they might be needed for other purposes, like buying a motorcycle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I had the medical check document, driving school certificate, passport, and proof of address document, I went to the &lt;a href=&quot;https://goo.gl/maps/AADAN29gL5uA1hsAA&quot;&gt;Phuket Provincial Transport Office&lt;/a&gt; to book an appointment. At the appointment, they repeated the physical reflexes tests, took my pictures, I paid a few more hundred baht, and they printed my driver’s license. It’s important to have extra proof of address documents for other purposes, as obtaining them from immigration can be time-consuming.&lt;/p&gt;
&lt;p&gt;Finally, I purchased a new motorcycle from a local dealer and rode it home with my new license.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/images/motorcycle.jpeg&quot; alt=&quot;Honda Click 160 Motorcycle&quot;&gt;
  &lt;figcaption&gt;Honda Click 160 Motorcycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For a guide on how to choose a motorcycle, a helmet and other Phuket questions, see &lt;a href=&quot;https://docs.google.com/document/d/1gcEWkhzbRgUygCvVWrcoubpKjL1LYvKMnrGyUjdAIiI/edit&quot;&gt;this collaborative document by Colin Dunn&lt;/a&gt;.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Documenting dry-structs</title>
    <link href="https://wilsonsilva.com/documenting-dry-structs/" rel="alternate" type="text/html" title="Documenting dry-structs" />
    <published>2018-09-16T00:00:00.000Z</published>
    <updated>2018-09-16T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2018-09-16-documenting-dry-structs.md</id>
    <summary type="html">YARD documentation for dry-structs</summary>
    <content type="html" xml:base="https://wilsonsilva.com/documenting-dry-structs/">&lt;p&gt;Documenting attributes using &lt;a href=&quot;https://yardoc.org/&quot;&gt;YARD&lt;/a&gt; for Plain Old Ruby Objects is fairly straightforward:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-text-decoration:underline&quot;&gt; Location&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the north–south position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @return [Float]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  attr_reader&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt; :latitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the east-west position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @return [Float] &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  attr_reader&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt; :longitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt; initialize&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;latitude:&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;longitude:&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;    @latitude  &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; latitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;    @longitude &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; longitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But sometimes the attributes are created using metaprogramming. One such example is
&lt;a href=&quot;https://dry-rb.org/gems/dry-struct/&quot;&gt;dry-struct&lt;/a&gt;, a gem built on top of &lt;a href=&quot;https://dry-rb.org/gems/dry-types/&quot;&gt;dry-types&lt;/a&gt;
which provides a &lt;a href=&quot;https://github.com/solnic/virtus&quot;&gt;virtus&lt;/a&gt;-like DSL for defining typed struct classes:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-text-decoration:underline&quot;&gt; Location&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; &amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;Dry::Struct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the north–south position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @return [Float]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  attribute &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;:latitude&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Types&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Strict&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#F8F8F2&quot;&gt;Float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the east-west position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @return [Float] &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  attribute &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;:longitude&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Types&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Strict&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#F8F8F2&quot;&gt;Float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The shortcoming of this approach is that YARD does not know how to generate documentation from the &lt;code&gt;attribute&lt;/code&gt;
declarations. This is where the directive
&lt;a href=&quot;https://www.rubydoc.info/gems/yard/file/docs/Tags.md#attribute&quot;&gt;&lt;code&gt;@!attribute&lt;/code&gt;&lt;/a&gt; comes in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It defines an attribute with a given name,
using indented block data as the attribute’s docstring. If the type specifier is supplied with &lt;code&gt;&quot;r&quot;&lt;/code&gt;, &lt;code&gt;&quot;w&quot;&lt;/code&gt;, or &lt;code&gt;&quot;rw&quot;&lt;/code&gt;,
the attribute is made readonly, writeonly or readwrite respectively. A readwrite attribute is the default, if no
type is specified. The comment containing this directive does not need to be attached to any source, but if it is,
that source code will be used as the method’s source.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-text-decoration:underline&quot;&gt; Location&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt; &amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;Dry::Struct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the north–south position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @!attribute [r] latitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  #   @return [Float]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  attribute &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;:latitude&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Types&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Strict&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#F8F8F2&quot;&gt;Float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # Geographic coordinate that specifies the east-west position&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # of a point on the Earth&apos;s surface.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  # @!attribute [r] longitude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#88846F&quot;&gt;  #   @return [Float]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;  attribute &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt;:longitude&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Types&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt;Strict&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-dark:#A6E22E;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#F8F8F2&quot;&gt;Float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Improving my expressiveness</title>
    <link href="https://wilsonsilva.com/improving-my-expressiveness/" rel="alternate" type="text/html" title="Improving my expressiveness" />
    <published>2017-04-05T00:00:00.000Z</published>
    <updated>2017-04-05T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2017-04-05-improving-my-expressiveness.md</id>
    <summary type="html">How I plan to become eloquent</summary>
    <content type="html" xml:base="https://wilsonsilva.com/improving-my-expressiveness/">&lt;h2 id=&quot;prologue&quot;&gt;Prologue&lt;/h2&gt;
&lt;p&gt;A few years ago, I assembled a mastermind group - a collective higher conscience to enforce discipline, support and accountability. Amongst other things, we were required to give weekly speeches.&lt;/p&gt;
&lt;p&gt;Then I taught troublesome children from vulnerable socioeconomic backgrounds the rightful path to peaceful coexistence, through public speaking. Children have a short attention span, so I had to be succinct.&lt;/p&gt;
&lt;p&gt;At work, I coached my colleagues on diverse software engineering subjects, such as architecture, defining boundaries, API and OO design, functional programming, best practices, and agile methodologies.&lt;/p&gt;
&lt;p&gt;While they always got my message, and usually agreed with it, I always felt that the delivery could be greatly improved. My heart shouldn’t race and my throat shouldn’t close. But they did, too many times.&lt;/p&gt;
&lt;h2 id=&quot;the-last-incident&quot;&gt;The last incident&lt;/h2&gt;
&lt;p&gt;Two days ago I attended a presentation on how to become an epic freelancer, in Chiang Mai, Thailand. The presentation was delivered by Josh Hoffman, a young and charismatic individual, just like me. Except that I’m not that charismatic. &lt;strong&gt;Yet.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Somewhere along his speech, he said:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
	I&apos;m gonna ask a question, and I appreciate some audience participation. As a freelancer, what is your most important asset?
  &lt;/p&gt;
  &lt;footer&gt;&lt;cite title=&quot;Josh Hoffman&quot;&gt;Josh Hoffman&lt;/cite&gt;&lt;/footer&gt;
&lt;/blockquote&gt;
&lt;p&gt;I raised my hand and, when I was given permission to speak, I answered with confidence: &lt;strong&gt;“time”&lt;/strong&gt;. Happiness flowed through his face as he exalted “bingo!”. He asked why, after a short pause to let the audience absorb his message.&lt;/p&gt;
&lt;p&gt;Then I elaborated:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
	Time can only be spent. It cannot be saved. You cannot have a jar of time. And &lt;strong&gt;time is life&lt;/strong&gt;. You work for money, and you &lt;strong&gt;spend life&lt;/strong&gt; getting money.
  &lt;/p&gt;
  &lt;footer&gt;&lt;cite title=&quot;Me being smart&quot;&gt;Me being smart&lt;/cite&gt;&lt;/footer&gt;
&lt;/blockquote&gt;
&lt;p&gt;He was very pleased with my answer, and gave me a high five. As he continued with his presentation, I noticed that I was very nervous and jittery, just because I said a few sentences in front of a small crowd. If the audience looked at me, they could &lt;strong&gt;see waves on my shirt, produced by my fast heartbeat.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-turning-point&quot;&gt;The turning point&lt;/h2&gt;
&lt;p&gt;Out of that frustration, I decided to be truly gifted with words, written or spoken. In content and in delivery.&lt;/p&gt;
&lt;p&gt;Words enable us to interpret and to express. If we have a limited vocabulary, we will also have a limited vision and a limited future. And I envision a better future.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I will write more often on my blog. Mostly to practice, but to share some strong opinions, loosely held, that I have about software engineering and how to make it more productive and enjoyable.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Repeating mistakes</title>
    <link href="https://wilsonsilva.com/repeating-mistakes/" rel="alternate" type="text/html" title="Repeating mistakes" />
    <published>2014-08-18T00:00:00.000Z</published>
    <updated>2014-08-18T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2014-08-18-i-refuse-to-make-the-same-mistake.md</id>
    <summary type="html">You shouldn’t make the same mistake twice, much less making a habit of it.</summary>
    <content type="html" xml:base="https://wilsonsilva.com/repeating-mistakes/">&lt;p&gt;I believe there’s absolutely no problem in making mistakes. It happens. The Japanese have an interesting philosophy
about it: “Shou ga nai”. Shou ga nai means “it can’t be helped” or “nothing can be done”. They accept reality and move
on, just like Buddhists do.&lt;/p&gt;
&lt;p&gt;Now, what I don’t believe in is the acceptance of making the same mistake over and over again. You shouldn’t make the
same mistake twice, much less making a habit of it.&lt;/p&gt;
&lt;p&gt;Some people say the best way to learn is by trial and error. I disagree with all my heart. While trial and error
presents an opportunity for learning and improvement, in my opinion, learning how to do something right without making
mistakes is way more satisfying.&lt;/p&gt;
&lt;p&gt;My philosophy about mistakes is best explained with the last verse of &lt;a href=&quot;https://genius.com/The-roots-the-fire-lyrics&quot;&gt;one of my favorite rap lyrics&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    I hate losing, I refuse to make the same mistake.
  &lt;/p&gt;
  &lt;footer&gt;&lt;cite title=&quot;Black Thought&quot;&gt;Black Thought&lt;/cite&gt;&lt;/footer&gt;
&lt;/blockquote&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">My first gem</title>
    <link href="https://wilsonsilva.com/my-first-gem/" rel="alternate" type="text/html" title="My first gem" />
    <published>2014-08-05T00:00:00.000Z</published>
    <updated>2014-08-05T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2014-08-05-my-first-gem.md</id>
    <summary type="html">I’ve done some gems, but none ever got to see the light.</summary>
    <content type="html" xml:base="https://wilsonsilva.com/my-first-gem/">&lt;p&gt;Oh, happy day! I feel good because of &lt;a href=&quot;https://github.com/wilsonsilva/process_exists&quot;&gt;this&lt;/a&gt;.
I’ve done some gems, but none ever got to see the light. My gem helps Ruby developers to know if a
&lt;a href=&quot;https://github.com/wilsonsilva/process_exists&quot;&gt;process exists&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-light monokai&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#272822;color:#24292e;--shiki-dark:#F8F8F2&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#E6DB74&quot;&gt; &apos;process_exists&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;pid&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#AE81FF&quot;&gt; 12&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#F8F8F2&quot;&gt;pid_exists&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F92672&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic&quot;&gt; Process&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#A6E22E&quot;&gt;exists?&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F8F8F2&quot;&gt;(pid)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">I can’t sleep</title>
    <link href="https://wilsonsilva.com/i-cant-sleep/" rel="alternate" type="text/html" title="I can’t sleep" />
    <published>2014-08-03T00:00:00.000Z</published>
    <updated>2014-08-03T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2014-08-03-i-cant-sleep.markdown</id>
    <summary type="html">At 10 pm I turned my computer off and went to bed. It didn’t work.</summary>
    <content type="html" xml:base="https://wilsonsilva.com/i-cant-sleep/">&lt;p&gt;At 10 pm I turned my computer off and went to bed. I grabbed one of my favourite books by Jim Rohn - “The Seasons of Life”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;and read it to make sure I was going to sleep with something positive on my mind. It had the opposite effect. Don’t
get me wrong, the book is great, but it made me go through my memories about life and work and that got me stressed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eric Thomas said in one of his speeches:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    Some of you love sleep more than you love success. If you&apos;re going to be successful, you got to be willing to 
    give up sleep!
  &lt;/p&gt;
  &lt;footer&gt;&lt;cite title=&quot;Eric Thomas&quot;&gt;Eric Thomas&lt;/cite&gt;&lt;/footer&gt;
&lt;/blockquote&gt;
&lt;p&gt;That speech just crossed my mind and I’m taking this opportunity
(the lack of sleepiness) to be productive. I’m going to work on my projects.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
  <entry>
    <title type="html">Staying lean</title>
    <link href="https://wilsonsilva.com/staying-lean/" rel="alternate" type="text/html" title="Staying lean" />
    <published>2014-03-04T00:00:00.000Z</published>
    <updated>2014-03-04T00:00:00.000Z</updated>
    <id>repo://posts.collection/_posts/2014-03-04-staying-lean.markdown</id>
    <summary type="html">I’m staying lean and agile. That’s my new way of life.</summary>
    <content type="html" xml:base="https://wilsonsilva.com/staying-lean/">&lt;p&gt;It took me 40 minutes to write this post. I wanted to explain how perfectionism was affecting the development of this
website, then I threw the draft away. I was being a perfectionist again! I’m staying lean and agile.
That’s my new way of life.&lt;/p&gt;</content>
    <author><name>Wilson Silva</name></author>
  </entry>
</feed>
