<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>About Code</title>
	<atom:link href="https://urubatan.dev/feed/" rel="self" type="application/rss+xml"/>
	<link>https://urubatan.dev</link>
	<description>I help ruby developers to use the best tools for each job so they can solve hard problems, with less bugs and have more free time.</description>
	<lastBuildDate>Wed, 22 May 2024 20:42:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
<site xmlns="com-wordpress:feed-additions:1">157740214</site>	<item>
		<title>Why Embrace Nesting in Ruby Modules?</title>
		<link>https://urubatan.dev/why-embrace-nesting-in-ruby-modules/</link>
					<comments>https://urubatan.dev/why-embrace-nesting-in-ruby-modules/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Wed, 22 May 2024 02:51:49 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[tips]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=496</guid>

					<description><![CDATA[In Ruby, you have a choice: define classes or modules either inline or nested within another module. While both approaches achieve similar results, there are key functional differences to consider, making nesting the preferred approach. The Subtle Distinction in Referencing Let&#8217;s look at an example using User and Admin::User classes: module Admin class Nested #...]]></description>
										<content:encoded><![CDATA[
<p>In Ruby, you have a choice: define classes or modules either inline or nested within another module. While both approaches achieve similar results, there are key functional differences to consider, making nesting the preferred approach.</p>



<p><strong>The Subtle Distinction in Referencing</strong></p>



<p>Let&#8217;s look at an example using <code>User</code> and <code>Admin::User</code> classes:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">module</span> Admin
  <span class="kw1">class</span> Nested  <span class="co1"># Nested approach</span>
    <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">test</span> = User.<span class="me1">name</span>
  <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw1">class</span> <span class="re2">Admin::Inline</span>  <span class="co1"># Inline approach</span>
  <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">test</span> = User.<span class="me1">name</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw3">puts</span> <span class="re2">Admin::Nested</span>.<span class="me1">test</span>  <span class="co1"># =&gt; &quot;Admin::User&quot; (correct)</span>
<span class="kw3">puts</span> <span class="re2">Admin::Inline</span>.<span class="me1">test</span>  <span class="co1"># =&gt; &quot;User&quot; (incorrect)</span></pre></div></div></div></div></div></div></div>






<p>Here, <code>Admin::Nested</code> correctly references <code>Admin::User</code> using <code>User.name</code>. However, <code>Admin::Inline</code> unexpectedly returns just &#8220;User&#8221;. This is because Ruby&#8217;s constant lookup prioritizes the current namespace first. Since <code>Admin::Inline</code> isn&#8217;t explicitly nested under <code>Admin</code>, it doesn&#8217;t search within that namespace for <code>User</code>.</p>



<p>To achieve the intended behavior with inline classes, you&#8217;d need to use the fully qualified name:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> <span class="re2">Admin::InlineQualified</span>
  <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">test</span> = ::<span class="re2">Admin::User</span>.<span class="me1">name</span>  <span class="co1"># Using fully qualified name</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw3">puts</span> <span class="re2">Admin::InlineQualified</span>.<span class="me1">test</span>  <span class="co1"># =&gt; &quot;Admin::User&quot; (correct)</span></pre></div></div></div></div></div></div></div>






<p><strong>Inheritance Nuances</strong></p>



<p>The same concept applies to inheritance. Consider these <code>BaseController</code> classes:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> BaseController
<span class="kw1">end</span>
&nbsp;
<span class="kw1">module</span> Admin
  <span class="kw1">class</span> BaseController  <span class="co1"># Nested approach</span>
  <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw3">puts</span> <span class="re2">Admin::NestedController</span>.<span class="me1">superclass</span>  <span class="co1"># =&gt; Admin::BaseController (correct)</span>
<span class="kw3">puts</span> <span class="re2">Admin::InlineController</span>.<span class="me1">superclass</span>  <span class="co1"># =&gt; BaseController (incorrect)</span></pre></div></div></div></div></div></div></div>






<p>Similar to the previous example, <code>Admin::NestedController</code> accurately retrieves its superclass <code>Admin::BaseController</code>. However, <code>Admin::InlineController</code> mistakenly returns the top-level <code>BaseController</code>.</p>



<p><strong>Understanding Nesting&#8217;s Benefits</strong></p>



<p>Nesting offers several advantages:</p>



<ol class="wp-block-list">
<li><strong>Clarity and Explicitness:</strong> Nested modules clearly indicate the hierarchy and relationships between modules and classes.</li>



<li><strong>Correct Constant Resolution:</strong> Nesting ensures Ruby searches for constants within the appropriate namespace, preventing unexpected behavior.</li>



<li><strong>Automatic Module Creation:</strong> When defining a nested class like <code>Api::UsersController</code>, Ruby automatically creates the <code>Api</code> module if it doesn&#8217;t exist.</li>
</ol>



<p><strong>Recommendations and Best Practices</strong></p>



<p>While inline modules can work with extra care (using fully qualified names), nesting is generally recommended for its clarity, predictability, and adherence to Ruby conventions. Static code analysis tools like Rubocop can <a href="https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ClassAndModuleChildren">enforce this practice</a>, making it easy to integrate into your development workflow.</p>



<p><strong>In Summary</strong></p>



<p>By embracing nesting in Ruby modules, you gain a more organized, predictable, and robust codebase, avoiding potential gotchas and ensuring your code behaves as intended.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/why-embrace-nesting-in-ruby-modules/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">496</post-id>	</item>
		<item>
		<title>An easy way to have a local “Github Copilot” for free</title>
		<link>https://urubatan.dev/an-easy-way-to-have-a-local-github-copilot-for-free/</link>
					<comments>https://urubatan.dev/an-easy-way-to-have-a-local-github-copilot-for-free/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Sat, 27 Apr 2024 14:19:58 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=483</guid>

					<description><![CDATA[I tried copilot some time ago and it wasn&#8217;t really good with Ruby, so I abandoned it, and I wanted to try it again, but didn&#8217;t want to pay for it so I simply gave up. Then I found out about this amazing plugin for VSCode that does most of what copilot does, the plugin...]]></description>
										<content:encoded><![CDATA[
<p>I tried copilot some time ago and it wasn&#8217;t really good with Ruby, so I abandoned it, and I wanted to try it again, but didn&#8217;t want to pay for it so I simply gave up.</p>



<p>Then I found out about this amazing plugin for VSCode that does most of what copilot does, the plugin is called Continue, and you can install it easily into your VSCode by clicking <a href="https://marketplace.visualstudio.com/items?itemName=Continue.continue">here</a>. Also if you use Jetbrains IDEs there is also a Continue plugin for them, so you can use this same tutorial, just install the plugin for your IDE.</p>



<p>It has a great advantage over VSCode if you think about the security of your code that it can run completely local from your machine, but for that you&#8217;ll also need to install <a href="https://ollama.com/">Ollama</a>, it is a local server for Large Language Models, and Continue can talk to it to complete code, use chat with AI in the IDE, comment your code, explain things, &#8230;</p>



<p>Very similar to many of the Copilot features.</p>



<p>Again, I used copilot a long time ago, so if you are a Copilot user, and decide to test Continue, please comment bellow on what Copilot offers that is better than Continue, I don&#8217;t have the knowledge to really compare them.</p>



<p>After you install Ollama for your platform (they have installers for Mac, Windows and Linux), you need to download some models to work with continue, I tested some and here is my small list:</p>



<p><a href="https://ollama.com/library/deepseek-coder">DeepSeek Coder</a> &#8211; it is great for ruby, creates unit tests for your classes with a reasonable coverage.</p>



<p><a href="https://ollama.com/library/codegemma:instruct">CodeGemma Instruct</a> &#8211; it works amazing for Python, wasn&#8217;t that great for Ruby in my tests</p>



<p><a href="https://ollama.com/library/starcoder2">Starcoder2</a> &#8211; It is the recommended code completion model for Continue, it is working great up to now, suggesting good code completions and saving some typing, I wouldn&#8217;t bet my job on the suggestions but if you know what you are doing it&#8217;ll save you some keystrokes. </p>



<p>To install these models in Ollama, after you install it run these commands:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">ollama pull deepseek-coder:6.7b
ollama pull codegemma:instruct
ollama pull starcoder2:7b</pre></div></div></div></div></div></div></div>






<p>After this you&#8217;ll need to configure Continue, it comes with a few web API models for trial, but since one of my ideas of using it was to have security and not have anything sent over the web I removed the models and configured it to use only Ollama that runs on my machine, here is my configuration file:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="json"><pre class="de1">{
  &quot;models&quot;: [
    {
      &quot;title&quot;: &quot;DeepSeek Coder&quot;,
      &quot;provider&quot;: &quot;ollama&quot;,
      &quot;model&quot;: &quot;deepseek-coder:6.7b&quot;
    },
    {
      &quot;title&quot;: &quot;Codegemma Instruct&quot;,
      &quot;provider&quot;: &quot;ollama&quot;,
      &quot;model&quot;: &quot;codegemma:instruct&quot;
    },
    {
      &quot;title&quot;: &quot;Starcoder2 7b&quot;,
      &quot;provider&quot;: &quot;ollama&quot;,
      &quot;model&quot;: &quot;starcoder2:7b&quot;
    }
  ],
  &quot;slashCommands&quot;: [
    {
      &quot;name&quot;: &quot;edit&quot;,
      &quot;description&quot;: &quot;Edit selected code&quot;
    },
    {
      &quot;name&quot;: &quot;comment&quot;,
      &quot;description&quot;: &quot;Write comments for the selected code&quot;
    },
    {
      &quot;name&quot;: &quot;share&quot;,
      &quot;description&quot;: &quot;Export this session as markdown&quot;
    },
    {
      &quot;name&quot;: &quot;cmd&quot;,
      &quot;description&quot;: &quot;Generate a shell command&quot;
    }
  ],
  &quot;customCommands&quot;: [
    {
      &quot;name&quot;: &quot;test&quot;,
      &quot;prompt&quot;: &quot;Write a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.&quot;,
      &quot;description&quot;: &quot;Write unit tests for highlighted code&quot;
    }
  ],
  &quot;contextProviders&quot;: [
    {
      &quot;name&quot;: &quot;code&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;docs&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;diff&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;open&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;terminal&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;problems&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;folder&quot;,
      &quot;params&quot;: {}
    },
    {
      &quot;name&quot;: &quot;codebase&quot;,
      &quot;params&quot;: {}
    }
  ],
  &quot;tabAutocompleteModel&quot;: {
    &quot;title&quot;: &quot;Starcoder2 7b&quot;,
    &quot;provider&quot;: &quot;ollama&quot;,
    &quot;model&quot;: &quot;starcoder2:7b&quot;
  },
  &quot;allowAnonymousTelemetry&quot;: false
}</pre></div></div></div></div></div></div></div>






<p>I replaced the starcoders:3b for the starcoder2:7b because I thought it&#8217;d work better, I haven&#8217;t really tested the 3b (3b means 3 billion parameters used in training, 7b means 7 billion parameters used in training)</p>



<p>All 3 models installed are specific for coding, they ate trained in more code than natural language, but they understand natural language commands, like &#8220;write a unit test for this code using only the public methods&#8221;.</p>



<p>It wasn&#8217;t really smart to know I was in a rails project and wrote the unit test directly using rspec, so I added &#8220;write a unit test for this code using only the public methods using minitest in a rails project&#8221; and the result was perfect.</p>



<p>It also knows how to write somewhat useful comments for your code, and rewrite code with better variable names.</p>



<p>Once you open Continue for the first time it&#8217;ll show you a simple but really useful tutorial in a python file.</p>



<p>Looking at the documentation for Continue, it has some really cool features like integrating with GitHub issues, pool requests, Jira, &#8230; you just need to configure it. </p>



<p>Please let me know if you think this mini tutorial was useful <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/an-easy-way-to-have-a-local-github-copilot-for-free/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">483</post-id>	</item>
		<item>
		<title>SPA without touching Javascript – The magic of Ruby on rails and Hotwire</title>
		<link>https://urubatan.dev/spa-without-touching-javascript-the-magic-of-ruby-on-rails-and-hotwire/</link>
					<comments>https://urubatan.dev/spa-without-touching-javascript-the-magic-of-ruby-on-rails-and-hotwire/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Fri, 05 Apr 2024 17:00:50 +0000</pubDate>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[hotwire]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[spa]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=451</guid>

					<description><![CDATA[It&#8217;s been a while since I wrote the last post here, so I decided to write something fun and maybe a good sample for anyone that is learning rails or is a little outdated. The idea here is to write a simple CRUD from scratch, I&#8217;ll use a Grid layout to keep everything in the...]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s been a while since I wrote the last post here, so I decided to write something fun and maybe a good sample for anyone that is learning rails or is a little outdated.</p>



<p>The idea here is to write a simple CRUD from scratch, I&#8217;ll use a Grid layout to keep everything in the same page, the app will be a Single Page application, similar to those written with heavy JavaScript frameworks, but we&#8217;ll not touch a line of JavaScript, out code will be only Ruby and ERB, using the power of Ruby on Rails, and the new standard Hotwire, to be more specific in this example, we&#8217;ll be heavily using TurboFrame that is a part of Hotwire.</p>



<p>The full app creation is in the video bellow, but if you don&#8217;t want video you can simply skip it an read the step by step.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<div class="jetpack-video-wrapper"><iframe title="Building an SPA without JavaScript: Step-by-Step Guide with Ruby on Rails and Hotwire" width="640" height="360" src="https://www.youtube.com/embed/CwRc9HkdbOQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>
</div></figure>



<p>We&#8217;ll start creating a new rails application:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">rails new samplespa</pre></div></div></div></div></div></div></div>






<p>In that application we&#8217;ll edit the file config/routes.rb, and make the content match:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1">Rails.<span class="me1">application</span>.<span class="me1">routes</span>.<span class="me1">draw</span> <span class="kw1">do</span>
  resources <span class="re3">:people</span>
  <span class="co1"># Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html</span>
&nbsp;
  <span class="co1"># Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.</span>
  <span class="co1"># Can be used by load balancers and uptime monitors to verify that the app is live.</span>
  get <span class="st0">&quot;up&quot;</span> <span class="sy0">=&gt;</span> <span class="st0">&quot;rails/health#show&quot;</span>, as: <span class="re3">:rails_health_check</span>
&nbsp;
  <span class="co1"># Defines the root path route (&quot;/&quot;)</span>
  root <span class="st0">&quot;people#index&quot;</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>The changes are the addition of &#8220;resources :people&#8221; that will be the only controller in our application and making the root of the app &#8220;people#index&#8221;.</p>



<p>Now we&#8217;ll create our model with the command:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">rails g model person name:string email:string description:rich_text</pre></div></div></div></div></div></div></div>






<p>I&#8217;m using rich_text as the description type because that will allow us to use ActionText to edit that field, but we&#8217;ll need to activate it in the application first, for that we&#8217;ll run the command:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">rails g action_text:install</pre></div></div></div></div></div></div></div>






<p>That command will create two migrations and configure the application to use ActionText for rich text editing.</p>



<p>We&#8217;ll also edit our create_people migration to make the fields not null, by adding &#8220;null:false&#8221; as options, like bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> CreatePeople <span class="sy0">&lt;</span> <span class="re2">ActiveRecord::Migration</span><span class="br0">&#91;</span><span class="nu0">7.1</span><span class="br0">&#93;</span>
  <span class="kw1">def</span> change
    create_table <span class="re3">:people</span> <span class="kw1">do</span> <span class="sy0">|</span>t<span class="sy0">|</span>
      t.<span class="kw3">string</span> <span class="re3">:name</span>, null: <span class="kw2">false</span>
      t.<span class="kw3">string</span> <span class="re3">:email</span>, null: <span class="kw2">false</span>
&nbsp;
      t.<span class="me1">timestamps</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>Now we are ready to migrate our database with the command:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">rails db:migrate</pre></div></div></div></div></div></div></div>






<p>Next we&#8217;ll edit the person model in the file app/models/person.rb to add some validation like bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> Person <span class="sy0">&lt;</span> ApplicationRecord
  has_rich_text <span class="re3">:description</span>
&nbsp;
  validates <span class="re3">:name</span>, <span class="re3">:email</span>, <span class="re3">:description</span>, presence: <span class="kw2">true</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>the has_rich_text tells the model to store the description field as ActionText in the proper tables.</p>



<p>Now we create our controller, we can use the rails generator or simply create the file people_controller.rb in the folder app/controllers, the initial content will be this:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> PeopleController <span class="sy0">&lt;</span> ApplicationController
  before_action <span class="re3">:load_everyone</span>
  <span class="kw1">def</span> index
    <span class="kw1">if</span> <span class="re1">@people</span>.<span class="me1">any</span>?
      <span class="re1">@person</span> = <span class="re1">@people</span>.<span class="me1">first</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  private
  <span class="kw1">def</span> load_everyone
    <span class="re1">@people</span> = Person.<span class="me1">order</span><span class="br0">&#40;</span>id: <span class="re3">:desc</span><span class="br0">&#41;</span>
  <span class="kw1">end</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>The idea is to have the list of people loaded for every method, that isn&#8217;t a problem because Rails is smart and will only actually run the query when we fetch data from @people </p>



<p>Now we create our first and only HTML view, it&#8217;ll be in the folder app/views/people (create the folder manually if you didn&#8217;t use the generator for the controller)</p>



<p>The file will be called index.html.erb and the content is really simple, check bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;div class=&quot;people_container&quot;&gt;
  &lt;div class=&quot;people_list&quot;&gt;
    &lt;%= render 'people_list' %&gt;
  &lt;/div&gt;
  &lt;div class=&quot;person_detail&quot;&gt;
    &lt;%= render 'person_detail' %&gt;
  &lt;/div&gt;
&lt;/div&gt;</pre></div></div></div></div></div></div></div>






<p>It has a container div, and two section divs for layout, in those we render the default content for the page.</p>



<p>Now lets take a look at our default CSS to layout this amazing app.</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="css"><pre class="de1">body <span class="br0">&#123;</span>
  <span class="kw1">height</span><span class="sy0">:</span> <span class="re3">100vh</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re1">.people_container</span> <span class="br0">&#123;</span>
  <span class="kw1">display</span><span class="sy0">:</span> grid<span class="sy0">;</span>
  <span class="kw1">grid-template-columns</span><span class="sy0">:</span> <span class="re3">200px</span> <span class="kw2">auto</span><span class="sy0">;</span>
  <span class="kw1">height</span><span class="sy0">:</span> <span class="re3">100%</span><span class="sy0">;</span>
  <span class="kw1">width</span><span class="sy0">:</span> <span class="re3">100%</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re1">.people_list</span> <span class="br0">&#123;</span>
  <span class="kw1">grid-column</span><span class="sy0">:</span> <span class="nu0">1</span><span class="sy0">;</span>
  <span class="kw1">height</span><span class="sy0">:</span> <span class="re3">100%</span><span class="sy0">;</span><span class="br0">&#125;</span>
&nbsp;
<span class="re1">.person_detail</span> <span class="br0">&#123;</span>
  <span class="kw1">grid-column</span><span class="sy0">:</span> <span class="nu0">2</span><span class="sy0">;</span>
  <span class="kw1">height</span><span class="sy0">:</span> <span class="re3">100%</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>






<p>It is very simple using grid layout in the container with two columns, one with 200px and the other with the rest, and we set the position of our two inner divs in the grid, also making sure the body has the full height, so height:100% works (I do that every time now, to make sure I don&#8217;t forget due to bad experiences in the past trying to fill the vertical portion in the screen)</p>



<p>Now we need to create the partials used in the main view, the first will be the list, create the file app/views/people/_people_list.html.erb with the following content:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;%= turbo_frame_tag 'people_list' do %&gt;
&lt;ul&gt;
  &lt;% @people.each do |person| %&gt;
    &lt;li&gt;
      &lt;%= link_to person.name, person, data: {turbo_stream: true, turbo_method: 'get'} %&gt;
    &lt;/li&gt;
  &lt;% end %&gt;
  &lt;li&gt;
    &lt;%= link_to 'Create Person Record', new_person_path, data: {turbo_stream: true, turbo_method: 'get'} %&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;% end %&gt;</pre></div></div></div></div></div></div></div>






<p>Pay attention to the &#8220;data: {turbo_stream: true, turbo_method: &#8216;get&#8217;}&#8221;, it&#8217;ll add to the links generated the properties data-turbo-stream=&#8221;true&#8221; and data-turbo-method=&#8221;get&#8221;, the first will activate turbo_stream for the link, it is usually only active for forms, and the later will activate turbo_stream for the method GET, because it is usually only for POST/PATCH, in the video the first try was wrong, I used torbo_frame instead of turbo_stream.</p>



<p>Other important thing is the turbo_frame_tag in the view, it defines areas where turbo frame can easily change content in the page.</p>



<p>Now lets create the file app/view/people/_person_detail.html.erb with the content:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;%= turbo_frame_tag 'person_detail' do %&gt;
  &lt;div&gt;
    &lt;div&gt;
      &lt;b&gt;Name:&lt;/b&gt; &lt;%= @person&amp;.name %&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;b&gt;Email:&lt;/b&gt; &lt;%= @person&amp;.email %&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;b&gt;Description:&lt;/b&gt; &lt;%= @person&amp;.description %&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;% if @person.present? %&gt;
        &lt;%= link_to 'Edit', edit_person_path(@person), data: {turbo_stream: true, turbo_method: 'get'} %&gt;
      &lt;% end %&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;% end %&gt;</pre></div></div></div></div></div></div></div>






<p>This is a simple and ugly details view that will render the person details, it uses safe navigation for the person because it is possible that with an empty people list, the person will be nil and it&#8217;d crash our application.</p>



<p>Now we need to make the links in both the people_list, and person_detail partials work, meaning we need to add to our controller the methods &#8220;new&#8221;, &#8220;show&#8221; and edit, just to start.</p>



<p>So open the controller app/controllers/people_controller.rb and add these 3 methods:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1">  <span class="kw1">def</span> show
    <span class="re1">@person</span> = Person.<span class="me1">find</span> params<span class="br0">&#91;</span><span class="re3">:id</span><span class="br0">&#93;</span>
    respond_to <span class="kw1">do</span> <span class="sy0">|</span><span class="kw3">format</span><span class="sy0">|</span>
      <span class="kw3">format</span>.<span class="me1">turbo_stream</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw1">def</span> new
    <span class="re1">@person</span> = Person.<span class="me1">new</span>
    respond_to <span class="kw1">do</span> <span class="sy0">|</span><span class="kw3">format</span><span class="sy0">|</span>
      <span class="kw3">format</span>.<span class="me1">turbo_stream</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw1">def</span> edit
    <span class="re1">@person</span> = Person.<span class="me1">find</span> params<span class="br0">&#91;</span><span class="re3">:id</span><span class="br0">&#93;</span>
    respond_to <span class="kw1">do</span> <span class="sy0">|</span><span class="kw3">format</span><span class="sy0">|</span>
      <span class="kw3">format</span>.<span class="me1">turbo_stream</span> <span class="kw1">do</span>
        render <span class="re3">:new</span>
      <span class="kw1">end</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>There are a few important things here, in all methods we have a respond_to and the format is turbo_stream, that will require a turbo_stream view, we&#8217;ll need at least two for the application, the show and new views, the edit method uses the new view since it is the same idea of showing a form.</p>



<p>To make it work, lets create a file named app/views/people/show.turbo_stream.erb with the following content:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;%= turbo_stream.replace 'person_detail', partial: 'person_detail' %&gt;
&lt;%= turbo_stream.replace 'people_list', partial: 'people_list' %&gt;</pre></div></div></div></div></div></div></div>






<p>The view is very simple and uses a helper from ruby to make our lives easier, we are replacing the contents of the previously defined turbo_frame_tag areas, with the referred partials, and we are redrawing the list so that we can use the same show view for update and create actions too, so if we change the person&#8217;s name or add a new person, the list will also be updated.</p>



<p>And the other file app/views/people/new.turbo_stream.erb with the content:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;%= turbo_stream.replace 'person_detail', partial: 'person_form' %&gt;</pre></div></div></div></div></div></div></div>






<p>The idea of this view is to render a form, so we need to create the form partial too, lets create the file app/views/people/_person_form.html.erb with the content:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="erb"><pre class="de1">&lt;%= turbo_frame_tag 'person_detail' do %&gt;
  &lt;%= form_for @person do |f|%&gt;
    &lt;div&gt;
      &lt;div&gt;
        &lt;%= f.label :name %&gt;
        &lt;%= f.text_field :name%&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;%= f.label :email %&gt;
        &lt;%= f.text_field :email%&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;%= f.label :description %&gt;
        &lt;%= f.rich_text_area :description %&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;%= f.submit %&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;% end %&gt;
&lt;% end %&gt;</pre></div></div></div></div></div></div></div>






<p>It is a simple form using rails helpers, and the rich_text_area field type, it also goes into the same person_detail turbo frame area, to make our code cleaner.</p>



<p>And the last things needed to make our code work are the create and update methods in our controller, the code is quite simple, lets check bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">def</span> create
    <span class="re1">@person</span> = Person.<span class="me1">create</span> params.<span class="kw3">require</span><span class="br0">&#40;</span><span class="re3">:person</span><span class="br0">&#41;</span>.<span class="me1">permit</span><span class="br0">&#40;</span><span class="re3">:name</span>, <span class="re3">:email</span>, <span class="re3">:description</span><span class="br0">&#41;</span>
    respond_to <span class="kw1">do</span> <span class="sy0">|</span><span class="kw3">format</span><span class="sy0">|</span>
      <span class="kw3">format</span>.<span class="me1">turbo_stream</span> <span class="kw1">do</span> 
        <span class="kw1">if</span> <span class="re1">@person</span>.<span class="me1">valid</span>?
          render <span class="re3">:show</span>
        <span class="kw1">else</span>
          render <span class="re3">:new</span>
        <span class="kw1">end</span>
      <span class="kw1">end</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw1">def</span> update
    <span class="re1">@person</span> = Person.<span class="me1">find</span><span class="br0">&#40;</span>params<span class="br0">&#91;</span><span class="re3">:id</span><span class="br0">&#93;</span><span class="br0">&#41;</span>
    <span class="re1">@person</span>.<span class="me1">update</span> params.<span class="kw3">require</span><span class="br0">&#40;</span><span class="re3">:person</span><span class="br0">&#41;</span>.<span class="me1">permit</span><span class="br0">&#40;</span><span class="re3">:name</span>, <span class="re3">:email</span>, <span class="re3">:description</span><span class="br0">&#41;</span>
    respond_to <span class="kw1">do</span> <span class="sy0">|</span><span class="kw3">format</span><span class="sy0">|</span>
      <span class="kw3">format</span>.<span class="me1">turbo_stream</span> <span class="kw1">do</span> 
        <span class="kw1">if</span> <span class="re1">@person</span>.<span class="me1">valid</span>?
          render <span class="re3">:show</span>
        <span class="kw1">else</span>
          render <span class="re3">:new</span>
        <span class="kw1">end</span>
      <span class="kw1">end</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>They are standard methods, without good validation and accepting pretty much anything the client sends, rails will handle escaping the SQL to make our app more secure, and we are using safe parameters, through the methods require and permit, to make sure we only pass the existing parameters to the Person model, and the main difference in the format.turbo_stream block is that if the operation failed, the form is rendered again (and we could add code to show the errors), and show the person details otherwise.</p>



<p>With that we have the full SPA working, without writing a single line of Javascript, if you want to check the full code it is on <a href="https://github.com/urubatan/samplespa">Github</a> you can just checkout, run the app and change it to study.</p>



<p>To make it les ugly, but still pretty bad, I added Pico CSS to the application.html.erb file, in the header section, if you want to do that add this line:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="html"><pre class="de1">&lt;link
  rel=&quot;stylesheet&quot;
  href=&quot;https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css&quot;
/&gt;</pre></div></div></div></div></div></div></div>






<p>And if you do that, you&#8217;ll also need to update the basic_layout.css file to make the rich text editor buttons visible again with this:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="css"><pre class="de1"><span class="re1">.trix-button</span> <span class="br0">&#123;</span>
  <span class="kw1">background-color</span><span class="sy0">:</span> <span class="kw4">white</span> !important<span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>






<p>Please comment bellow if you liked this quick tutorial, and please suggest other subjects, if there aren&#8217;t any suggestions I&#8217;ll probably write about how to do pop up dialogs with Hotwire/Stimulus in the next post.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/spa-without-touching-javascript-the-magic-of-ruby-on-rails-and-hotwire/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">451</post-id>	</item>
		<item>
		<title>I see Dead Jobs everywhere (sidekiq DeadSet)</title>
		<link>https://urubatan.dev/i-see-dead-jobs-everywhere-sidekiq-deadset/</link>
					<comments>https://urubatan.dev/i-see-dead-jobs-everywhere-sidekiq-deadset/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Tue, 07 Feb 2023 17:43:45 +0000</pubDate>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[server-api]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[sidekiq]]></category>
		<category><![CDATA[tips]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=433</guid>

					<description><![CDATA[When you are using Sidekiq to handle asynchronous jobs, some times there are exceptions and jobs failing, and I say sometimes because your environment is probably perfect, there is no lag, all services your jobs depend on are always on and responsive, and you probably write better code than most other developers 😛 otherwise it...]]></description>
										<content:encoded><![CDATA[
<p>When you are using Sidekiq to handle asynchronous jobs, some times there are exceptions and jobs failing, and I say sometimes because your environment is probably perfect, there is no lag, all services your jobs depend on are always on and responsive, and you probably write better code than most other developers <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f61b.png" alt="😛" class="wp-smiley" style="height: 1em; max-height: 1em;" /> otherwise it probably happens quite often&#8230;</p>



<p>But Sidekiq will retry that job for you, of course this is configurable:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="co1"># Retry 5 times before giving up</span>
sidekiq_options <span class="kw1">retry</span>: <span class="nu0">5</span>
<span class="co1"># Don't retry at all</span>
sidekiq_options <span class="kw1">retry</span>: <span class="kw2">false</span></pre></div></div></div></div></div></div></div>






<p> The default number of retries is 25, and with that many tries most of the transient problems will fix itselves probably.</p>



<p>But sometimes not even being that insistent is enough to avoid the problem, and in that case sidekiq will send your job to a DeadSet where all the job ghosts live.</p>



<p>For most applications, it is good to know when some or all jobs go to that DeadSet, meaning Sidekiq will not retry it anymore.</p>



<p>In this situation you can log the error, send one email for a human to fix the issue by hand, or handle it the best way possible for your business.</p>



<p>To intercept this, sidekiq the sidekiq_retries_exhausted hook, that you can configure either per worker class as bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1"><span class="kw1">class</span> ImportantWorker
  <span class="kw1">include</span> <span class="re2">Sidekiq::Worker</span>
  sidekiq_options <span class="kw1">retry</span>: <span class="nu0">5</span>
&nbsp;
  sidekiq_retries_exhausted <span class="kw1">do</span> <span class="sy0">|</span>msg, exception<span class="sy0">|</span>
    <span class="co1"># example with using Rails' logger</span>
    Rails.<span class="me1">logger</span>.<span class="me1">warn</span><span class="br0">&#40;</span><span class="st0">&quot;Failed #{msg&amp;#91;'class']} with #{msg&amp;#91;'args']}: #{msg&amp;#91;'error_message']}&quot;</span>, error: exception<span class="br0">&#41;</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw1">def</span> perform<span class="br0">&#40;</span>important_arguments<span class="br0">&#41;</span>
    <span class="co1"># do some work</span>
  <span class="kw1">end</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>or you can configure a global handler for the entire application, adding a handler to Sidekiq like bellow:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1">Sidekiq.<span class="me1">configure_server</span> <span class="kw1">do</span> <span class="sy0">|</span>config<span class="sy0">|</span>
  <span class="co1"># other config stuff...</span>
&nbsp;
  config.<span class="me1">death_handlers</span> <span class="sy0">&amp;</span>lt;<span class="sy0">&amp;</span>lt; <span class="sy0">-&gt;</span><span class="br0">&#40;</span>job, ex<span class="br0">&#41;</span> <span class="kw1">do</span>
    Rails.<span class="me1">logger</span>.<span class="me1">error</span> <span class="st0">&quot;Surprise, an error!, #{job&amp;#91;'class']} #{job&amp;#91;&quot;</span>jid<span class="st0">&quot;]} just died with error #{ex.message}.&quot;</span>
  <span class="kw1">end</span>
<span class="kw1">end</span></pre></div></div></div></div></div></div></div>






<p>I usually add this code to the config/initializers/sidekiq.rb file where all the sidekiq related configurations live together.</p>



<p>Of course just logging like this will not solve your problem, but intercepting the dead jobs event will allow you to write more robust asynchronous job processing applications.</p>



<p>Please add a comment bellow if you have any other issues with asynchronous jobs that I could help with.</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/i-see-dead-jobs-everywhere-sidekiq-deadset/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">433</post-id>	</item>
		<item>
		<title>Quick tips that help: rails notes</title>
		<link>https://urubatan.dev/quick-tips-that-help-rails-notes/</link>
					<comments>https://urubatan.dev/quick-tips-that-help-rails-notes/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Thu, 05 Jan 2023 15:22:34 +0000</pubDate>
				<category><![CDATA[Sem categoria]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=428</guid>

					<description><![CDATA[This is actually not new, the command is there for a long time, but it is one of those things that help and you keep forgetting about. It works by searching your code for comments with specific keywords, the default is searching for FIXME, OPTIMIZE and TODO. You can add more tags either by using...]]></description>
										<content:encoded><![CDATA[
<p>This is actually not new, the command is there for a long time, but it is one of those things that help and you keep forgetting about.</p>



<p>It works by searching your code for comments with specific keywords, the default is searching for FIXME, OPTIMIZE and TODO.</p>



<p>You can add more tags either by using the &#8220;&#8211;annotations&#8221; parameter or by registering the tags in your rails config</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><pre class="de1">config.<span class="me1">annotations</span>.<span class="me1">register_tags</span><span class="br0">&#40;</span><span class="st0">&quot;DEPRECATEME&quot;</span>, <span class="st0">&quot;TESTME&quot;</span><span class="br0">&#41;</span></pre></div></div></div></div></div></div></div>






<p>The output is pretty simple and yet helpful, for example this is the output of one of my projects:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1"><span class="re1">$rails</span> notes
app<span class="sy0">/</span>controllers<span class="sy0">/</span>some_controller_with_domains.rb:
  <span class="sy0">*</span> <span class="br0">&#91;</span> <span class="nu0">67</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> need to check allowed domains
  <span class="sy0">*</span> <span class="br0">&#91;</span> <span class="nu0">86</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> need to check allowed domains
&nbsp;
app<span class="sy0">/</span>controllers<span class="sy0">/</span>search_controller.rb:
  <span class="sy0">*</span> <span class="br0">&#91;</span> <span class="nu0">71</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> ugly hack <span class="kw1">for</span> exact match
  <span class="sy0">*</span> <span class="br0">&#91;</span><span class="nu0">190</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> ugly hack <span class="kw1">for</span> exact match
&nbsp;
app<span class="sy0">/</span>webpacker<span class="sy0">/</span>javascript<span class="sy0">/</span>core<span class="sy0">/</span>components<span class="sy0">/</span>SomeJavascriptFile.js:
  <span class="sy0">*</span> <span class="br0">&#91;</span> <span class="nu0">33</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>TODO<span class="br0">&#93;</span> Remove from collection <span class="kw1">if</span> status no longer matches search
  <span class="sy0">*</span> <span class="br0">&#91;</span><span class="nu0">167</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>TODO<span class="br0">&#93;</span> Not implemented
&nbsp;
app<span class="sy0">/</span>webpacker<span class="sy0">/</span>javascript<span class="sy0">/</span>core<span class="sy0">/</span>components<span class="sy0">/</span>SomeOtherJavascriptFIle.js:
  <span class="sy0">*</span> <span class="br0">&#91;</span> <span class="nu0">38</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> not actually properly memoized
&nbsp;
app<span class="sy0">/</span>webpacker<span class="sy0">/</span>javascript<span class="sy0">/</span>core<span class="sy0">/</span>components<span class="sy0">/</span>JavascriptWithWeirdness.js:
  <span class="sy0">*</span> <span class="br0">&#91;</span><span class="nu0">107</span><span class="br0">&#93;</span> <span class="br0">&#91;</span>FIXME<span class="br0">&#93;</span> Weirdness on first update</pre></div></div></div></div></div></div></div>



]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/quick-tips-that-help-rails-notes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">428</post-id>	</item>
		<item>
		<title>Ruby 3.2.0 released with WASI, YJIT, RegExp improvements, …</title>
		<link>https://urubatan.dev/ruby-3-2-0-released-with-wasi-yjit-regexp-improvements/</link>
					<comments>https://urubatan.dev/ruby-3-2-0-released-with-wasi-yjit-regexp-improvements/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Wed, 04 Jan 2023 18:46:08 +0000</pubDate>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[3.2.0]]></category>
		<category><![CDATA[regexp]]></category>
		<category><![CDATA[yjib]]></category>
		<category><![CDATA[yjit]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=424</guid>

					<description><![CDATA[Last 25 of December while we were all celebrating with our families the Ruby core team released our Christmas gift. The release 3.2.0 of Ruby is faster and brought some amazing tools for us. WebAssembly support Ruby 3.2.0 added initial support for WebAssembly through WASI, this allows running CRuby in a web browser, for example...]]></description>
										<content:encoded><![CDATA[
<p>Last 25 of December while we were all celebrating with our families the Ruby core team released our Christmas gift.</p>



<p>The release 3.2.0 of Ruby is faster and brought some amazing tools for us.</p>



<h2 class="wp-block-heading">WebAssembly support</h2>



<p>Ruby 3.2.0 added initial support for WebAssembly through WASI, this allows running CRuby in a web browser, for example through <a href="https://try.ruby-lang.org/playground/" target="_blank" rel="noreferrer noopener">TryRuby Playground</a> but it also allows running CRuby securely in different platforms, like running the exact same binary version of ruby in a x86 and arm64 environments.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="640" height="336" src="https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=640%2C336&#038;ssl=1" alt="" class="wp-image-425" srcset="https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=1024%2C538&amp;ssl=1 1024w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=300%2C157&amp;ssl=1 300w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=768%2C403&amp;ssl=1 768w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=1536%2C806&amp;ssl=1 1536w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=2048%2C1075&amp;ssl=1 2048w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?resize=850%2C446&amp;ssl=1 850w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?w=1280&amp;ssl=1 1280w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image.png?w=1920&amp;ssl=1 1920w" sizes="(max-width: 640px) 100vw, 640px" /></figure>



<p>It isn&#8217;t complete yet, the WebAssembly support for Fiber, exception and GC isn&#8217;t complete, but is implements a <a href="https://github.com/kateinoigakukun/wasi-vfs/wiki/Getting-Started-with-CRuby" target="_blank" rel="noreferrer noopener">VFS on top of web assembly</a>, what allows the packaging of an entire ruby app into a single .wasm file.</p>



<h2 class="wp-block-heading">Production Ready YJIT</h2>



<p>YJIT is faster and uses less memory than before! it now supports both x86-64 and arm64/aarch64 CPUs on Linux, MacOS, BSD and other UNIX platforms.</p>



<p>To compile ruby with YJIT support you&#8217;ll need Rust 1.58.0+ so make sure you install it before running ./configure (or before asking rvm or a similar tool to install it for you)</p>



<p>Wanna know more about YJIT? check this <a href="https://www.youtube.com/watch?v=yZSe1BhiTvI&amp;list=PLM_LdV8tMIaxpMTv9dnWk3cq61xJcE2Pv&amp;index=18" target="_blank" rel="noreferrer noopener">amazing presentation</a></p>



<h2 class="wp-block-heading">Regexp matching way faster</h2>



<p>Sometimes depending on the Regexp we are using in our ruby code, it is possible to use our notebooks to fry some eggs <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f61b.png" alt="😛" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>But this will change and you&#8217;ll need to use the correct pan for that in the future&#8230;</p>



<p>The improved regexp matching can keep the regexp time mostly linear in almost all tests done by the Ruby Core team check the graph bellow.</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" decoding="async" width="600" height="371" src="https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image-1.png?resize=600%2C371&#038;ssl=1" alt="" class="wp-image-426" srcset="https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image-1.png?w=600&amp;ssl=1 600w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2023/01/image-1.png?resize=300%2C186&amp;ssl=1 300w" sizes="(max-width: 600px) 100vw, 600px" /></figure>



<p>Regexp.timeout can be used for the cases where these improvements fail, for example in complex cases where you use lots of back-references or look-around</p>



<pre class="wp-block-code"><code>Regexp.<strong>timeout</strong> <strong>=</strong> 1.0

/^a*b?a*()\1$/ <strong>=~</strong> "a" <strong>*</strong> 50000 <strong>+</strong> "x"
<em>#=> Regexp::TimeoutError is raised in one second</em>
</code></pre>



<p>You can also make one specific Regexp ignore the timeout if needed</p>



<pre class="wp-block-code"><code>Regexp.<strong>timeout</strong> <strong>=</strong> 1.0

<em># This regexp has no timeout</em>
long_time_re <strong>=</strong> Regexp.<strong>new</strong>('^a*b?a*()\1$', timeout: Float<strong>::</strong>INFINITY)

long_time_re <strong>=~</strong> "a" <strong>*</strong> 50000 <strong>+</strong> "x" <em># never interrupted</em></code></pre>



<p>there are other improvements like better syntax error reporting.</p>



<p>A few language improvements like allowing the usage of rest and keyword rest arguments as method parameters</p>



<pre class="wp-block-code"><code>  <strong>def</strong> <strong>foo</strong>(<strong>*</strong>)
    bar(<strong>*</strong>)
  <strong>end</strong>
  <strong>def</strong> <strong>baz</strong>(<strong>**</strong>)
    quux(<strong>**</strong>)
  <strong>end</strong></code></pre>



<p>There is a new Data class to represent immutable objects similar to the Struct class today.</p>



<pre class="wp-block-code"><code>Measure <strong>=</strong> Data.<strong>define</strong>(:amount, :unit)
  distance <strong>=</strong> Measure.<strong>new</strong>(100, 'km')            <em>#=&gt; #&lt;data Measure amount=100, unit="km"&gt;</em>
  weight <strong>=</strong> Measure.<strong>new</strong>(amount: 50, unit: 'kg') <em>#=&gt; #&lt;data Measure amount=50, unit="kg"&gt;</em>
  weight.<strong>with</strong>(amount: 40)                      <em>#=&gt; #&lt;data Measure amount=40, unit="kg"&gt;</em>
  weight.<strong>amount</strong>                                <em>#=&gt; 50</em>
  weight.<strong>amount</strong> <strong>=</strong> 40                           <em>#=&gt; NoMethodError: undefined method `amount='</em></code></pre>



<p>Struct now also support keyword arguments</p>



<pre class="wp-block-code"><code>  Post <strong>=</strong> Struct.<strong>new</strong>(:id, :name)
  Post.<strong>new</strong>(1, "hello") <em>#=> #&lt;struct Post id=1, name="hello"></em>
  <em># From Ruby 3.2, the following code also works without keyword_init: true.</em>
  Post.<strong>new</strong>(id: 1, name: "hello") <em>#=> #&lt;struct Post id=1, name="hello"></em></code></pre>



<p>You can read the full release notes in <a href="https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/" target="_blank" rel="noreferrer noopener">this link</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/ruby-3-2-0-released-with-wasi-yjit-regexp-improvements/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">424</post-id>	</item>
		<item>
		<title>Rubyconf Thailand quick summary and slides</title>
		<link>https://urubatan.dev/rubyconf-thailand-quick-summary-and-slides/</link>
					<comments>https://urubatan.dev/rubyconf-thailand-quick-summary-and-slides/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Thu, 22 Dec 2022 14:08:33 +0000</pubDate>
				<category><![CDATA[presentations]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[presentation]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=419</guid>

					<description><![CDATA[Early this December happened the second edition of one of the best ruby conferences I&#8217;ve participated up to now! The Bangkok.rb team did an amazing job of organizing 4 amazing keynote speakers and a total of 19 speakers and attendees from over 20 diferent countries. The organization team did an amazing job of receiving the...]]></description>
										<content:encoded><![CDATA[
<p>Early this December happened the second edition of one of the best ruby conferences I&#8217;ve participated up to now!</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="640" height="427" src="https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image.png?resize=640%2C427&#038;ssl=1" alt="" class="wp-image-420" srcset="https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image.png?w=900&amp;ssl=1 900w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image.png?resize=300%2C200&amp;ssl=1 300w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image.png?resize=768%2C513&amp;ssl=1 768w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image.png?resize=850%2C568&amp;ssl=1 850w" sizes="auto, (max-width: 640px) 100vw, 640px" /></figure>



<p>The <a href="https://www.meetup.com/pt-BR/bangkok-rb/" target="_blank" rel="noreferrer noopener">Bangkok.rb</a> team did an amazing job of organizing 4 amazing keynote speakers and a total of 19 speakers and attendees from over 20 diferent countries.</p>



<p>The organization team did an amazing job of receiving the speakers and made everyone feel at home from the start with the amazing dinner at a boat in the beautiful Chao Phraya river.</p>



<p>The conference was beautifully organized and the speeches were amazing varying from performance improvement tips, framework specific talking about <a href="https://hanamirb.org/" target="_blank" rel="noreferrer noopener">Hanami</a> and <a href="https://www.phoenixframework.org/" target="_blank" rel="noreferrer noopener">Phenix</a>, to internals of <a href="https://puma.io/" target="_blank" rel="noreferrer noopener">Puma</a>, two amazing <a href="https://www.jruby.org/" target="_blank" rel="noreferrer noopener">JRuby</a> talks, data processing, team organization, GitOps, <a href="https://rubyonrails.org/" target="_blank" rel="noreferrer noopener">Rails</a> and a lot more!</p>



<p>This was a single track conference where all the speeches grabbed my attention, I loved all of them.</p>



<p>And to complete the scenario I was lucky enough to be one of the speakers talking about Refactoring</p>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="640" height="480" src="https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image-1.png?resize=640%2C480&#038;ssl=1" alt="" class="wp-image-421" srcset="https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image-1.png?w=680&amp;ssl=1 680w, https://i0.wp.com/urubatan.dev/wp-content/uploads/2022/12/image-1.png?resize=300%2C225&amp;ssl=1 300w" sizes="auto, (max-width: 640px) 100vw, 640px" /></figure>



<p>If anyone is interested my slides are already online here <a href="https://www.slideshare.net/urubatan/ruby-code-smells" target="_blank" rel="noreferrer noopener">Ruby code smells</a>.</p>



<p>The speech was more abstract trying to point what to avoid in the code, but I&#8217;ll write a few blog posts more specific on the subject with samples and tips on refactoring to anyone that is learning refactoring best practices.</p>



<p>In the mean time, enjoy the return to presencial conferences to make friends and grow your network of technical contacts, but also to enjoy life and grab a beer with a fellow developer.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/rubyconf-thailand-quick-summary-and-slides/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">419</post-id>	</item>
		<item>
		<title>Testing download contents with Cucumber+Capybara and chromedriver</title>
		<link>https://urubatan.dev/testing-download-contents-with-cucumbercapybara-and-chromedriver/</link>
					<comments>https://urubatan.dev/testing-download-contents-with-cucumbercapybara-and-chromedriver/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Sat, 05 Jun 2021 22:22:22 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[cucu]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=415</guid>

					<description><![CDATA[Working on a project these days where I work with Cucumber and Capybara, I had to test the contents of filed downloaded from the application, the downloads were being sent using &#8220;send_file&#8221; and chrome was adding them directly to the user Downloads folder, and the capybara tests had no access to the file contents after...]]></description>
										<content:encoded><![CDATA[
<p>Working on a project these days where I work with <a href="https://cucumber.io/" target="_blank" rel="noreferrer noopener nofollow">Cucumber </a>and <a href="http://teamcapybara.github.io/capybara/" target="_blank" rel="noreferrer noopener nofollow">Capybara</a>, I had to test the contents of filed downloaded from the application, the downloads were being sent using &#8220;send_file&#8221; and chrome was adding them directly to the user Downloads folder, and the capybara tests had no access to the file contents after that.</p>



<p>My goal was to have the test code in Gherkin like this:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="gherkin"><ol><li class="li1"><pre class="de1">When I click on &quot;Download&quot;</pre></li><li class="li1"><pre class="de1">Then the downloaded content should be:</pre></li><li class="li1"><pre class="de1">    &quot;&quot;&quot;</pre></li><li class="li1"><pre class="de1">    id,name,age</pre></li><li class="li1"><pre class="de1">    1,John Doe,35</pre></li><li class="li1"><pre class="de1">    3,Mary Jane, 27</pre></li><li class="li1"><pre class="de1">    8,Little Joe, 9</pre></li><li class="li1"><pre class="de1">    &quot;&quot;&quot;</pre></li></ol></div></div></div></div></div></div></div>






<p>But after cucumber executes</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="gherkin"><ol><li class="li1"><pre class="de1">When I click on &quot;Download&quot;</pre></li></ol></div></div></div></div></div></div></div>






<p>The file was downloaded, it doesn&#8217;t wait for the download to finish and the next step had no access to the downloaded file.</p>



<p>After some research I found out that I could configure a profile to Chrome before instantiating the browser in Capybara.</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><ol><li class="li1"><pre class="de1">Capybara.<span class="me1">register_driver</span> <span class="re3">:chrome</span> <span class="kw1">do</span> <span class="sy0">|</span>app<span class="sy0">|</span></pre></li><li class="li1"><pre class="de1">  profile = <span class="re2">Selenium::WebDriver::Chrome::Profile</span>.<span class="me1">new</span></pre></li><li class="li1"><pre class="de1">  profile<span class="sy0">&amp;</span><span class="co1">#91;'download.default_directory'] = TestDownloadHelpers::PATH.to_s</span></pre></li><li class="li1"><pre class="de1">  <span class="re2">Capybara::Selenium::Driver</span>.<span class="me1">new</span><span class="br0">&#40;</span>app, browser: <span class="re3">:chrome</span>, profile: profile<span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1"><span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">Capybara.<span class="me1">javascript_driver</span> = <span class="re3">:chrome</span></pre></li></ol></div></div></div></div></div></div></div>






<p>In a simple explanation, this snippet sets a profile in the browser to be instantiated, configuring the default download directory to the tmp/downloads folder in my rails app.</p>



<p>This also mean that we can set ay number of other chrome properties as required, but we&#8217;ll get there sometime in the future <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Then I created this download helper in a new file under features/support</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="ruby"><ol><li class="li1"><pre class="de1"><span class="kw1">module</span> TestDownloadHelpers</pre></li><li class="li1"><pre class="de1">  TIMEOUT = <span class="nu0">30</span></pre></li><li class="li1"><pre class="de1">  PATH    = Rails.<span class="me1">root</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">'tmp/downloads'</span><span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  extend <span class="kw2">self</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> downloads</pre></li><li class="li1"><pre class="de1">    Dir<span class="sy0">&amp;</span><span class="co1">#91;PATH.join('*')].sort_by{ |f| File.mtime(f) }</span></pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> download</pre></li><li class="li1"><pre class="de1">    downloads.<span class="me1">last</span></pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> download_content</pre></li><li class="li1"><pre class="de1">    wait_for_download</pre></li><li class="li1"><pre class="de1">    <span class="kw4">File</span>.<span class="kw3">open</span><span class="br0">&#40;</span>download, <span class="sy0">&amp;</span>amp;:read<span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> wait_for_download</pre></li><li class="li1"><pre class="de1">    Timeout.<span class="me1">timeout</span><span class="br0">&#40;</span>TIMEOUT<span class="br0">&#41;</span> <span class="kw1">do</span></pre></li><li class="li1"><pre class="de1">      <span class="kw3">sleep</span> <span class="nu0">0.1</span> <span class="kw1">until</span> downloaded?</pre></li><li class="li1"><pre class="de1">    <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> downloaded?</pre></li><li class="li1"><pre class="de1">    !downloading? <span class="sy0">&amp;</span>amp;<span class="sy0">&amp;</span>amp; downloads.<span class="me1">any</span>?</pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> downloading?</pre></li><li class="li1"><pre class="de1">    downloads.<span class="me1">grep</span><span class="br0">&#40;</span><span class="sy0">/</span>\.<span class="me1">crdownload</span>$<span class="sy0">/</span><span class="br0">&#41;</span>.<span class="me1">any</span>?</pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">  <span class="kw1">def</span> clear_downloads</pre></li><li class="li1"><pre class="de1">    <span class="kw4">FileUtils</span>.<span class="me1">mkdir_p</span><span class="br0">&#40;</span>PATH<span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1">    <span class="kw4">FileUtils</span>.<span class="me1">rm_f</span><span class="br0">&#40;</span>downloads<span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1">  <span class="kw1">end</span></pre></li><li class="li1"><pre class="de1"><span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">World<span class="br0">&#40;</span>DownloadHelpers<span class="br0">&#41;</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">Before <span class="kw1">do</span></pre></li><li class="li1"><pre class="de1">  clear_downloads</pre></li><li class="li1"><pre class="de1"><span class="kw1">end</span></pre></li><li class="li1"><pre class="de1">&nbsp;</pre></li><li class="li1"><pre class="de1">After <span class="kw1">do</span></pre></li><li class="li1"><pre class="de1">  clear_downloads</pre></li><li class="li1"><pre class="de1"><span class="kw1">end</span></pre></li></ol></div></div></div></div></div></div></div>






<p>An important point here is that the &#8220;downloading?&#8221; method uses the chrome partial download extension, so this entire solution only works for Chrome, if you need a multi browser solution this code will need a lot of updating.</p>



<p>With these small changes I was able to download files directly to the temp download folder, then check the downloaded file contents correctly with this simple snippet.</p>



<pre class="wp-block-code" lang="ruby">Then('the downloaded content should be:') do |content|
  text = download_content
  expect(text).to eq(content)
end</pre>



<p>Making my initial test pass, and allowing me to completely test my downloads.</p>



<p>I hope this small snippets help someone else <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Please feel free to leave any comments bellow.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/testing-download-contents-with-cucumbercapybara-and-chromedriver/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">416</post-id>	</item>
		<item>
		<title>When Kubernetes is not the right choice?</title>
		<link>https://urubatan.dev/when-kubernetes-is-not-the-right-choice/</link>
					<comments>https://urubatan.dev/when-kubernetes-is-not-the-right-choice/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Tue, 17 Mar 2020 21:23:04 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[opinion]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[red-flag]]></category>
		<category><![CDATA[redflag]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=406</guid>

					<description><![CDATA[As with anything in the technology field, there is no one size fits all solution&#8230; And even kubernetes solving a big amount of problems you might have when exposing and scaling your application, it also brings some problems that you need to weight before deciding if this is the right technology for you or not....]]></description>
										<content:encoded><![CDATA[
<p>As with anything in the technology field, there is no one size fits all solution&#8230;</p>



<p>And even kubernetes solving a big amount of problems you might have when exposing and scaling your application, it also brings some problems that you need to weight before deciding if this is the right technology for you or not.</p>



<p>One of the things to consider, is that kubernetes is built to support stateless services, sure it supports drive mounts, but you&#8217;ll have many problems with pods waiting for long hours to stop waiting for the drives to dismount, or pods waiting to start because no available node is compatible with the drive mount it needs.</p>



<p>Another thing that might be a red-flag is that kubernetes might decide that a pod needs to move to another node due to high cpu or memory usage, and if your application doesn&#8217;t have many copies of each pod running, this might cause some minor service outage (unless you combine this with the problem related above, then it might become a huge problem)</p>



<p>A third red-flag for kubernetes might be if you do not have enough hardware resources, for example if you work in a small project or company, and your hardware is a little more than your application needs to run, the extra load that kubernetes need to run the additional resources, DNS, load balancing, kubelet, &#8230; might cause you problems for your application, because nothing of that is free.</p>



<p>One other thing is that if you want to deploy your own kubernetes server, you need to study a lot before doing that, it is way easier to pay for a hosted service from Google, AWS, Azure, &#8230; Kubernetes has a lot of moving parts, and is really complex to maintain.</p>



<p>But if none  of these problems apply to you, you can read the other posts about how to package and deploy your application to kubernetes:</p>



<ol class="wp-block-list"><li><a href="https://urubatan.dev/how-to-use-docker-to-have-an-uniform-development-environment-for-your-rails-project/">How to use docker to to have an uniform development, test and production environment</a></li><li><a href="https://urubatan.dev/rails-from-zero-to-kubernetes-first-pod/">First pod</a></li><li><a href="https://urubatan.dev/rails-from-zero-to-kubernetes-a-service-to-access-your-pod/">A service to access your pod</a></li><li><a href="https://urubatan.dev/rails-from-zero-to-kubernetes-horizontal-autoscaling/">Horizontal autoscaling</a></li><li><a href="https://urubatan.dev/rails-from-zero-to-kubernetes-ingress-reverse-proxy/">Ingress reverse proxy</a></li></ol>



<p>Of course these are not the only &#8220;red flags&#8221; for kubernetes, but if any of these apply to you, you might want to consider other solutions for your deployment, maybe docker swarm is enough for you, maybe you do not need containers at all, &#8230;</p>



<p>If you have any questions or comments about this, please leave a comment or talk direct to me through any social network <img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/when-kubernetes-is-not-the-right-choice/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">406</post-id>	</item>
		<item>
		<title>Rails from “zero” to kubernetes – ingress reverse proxy</title>
		<link>https://urubatan.dev/rails-from-zero-to-kubernetes-ingress-reverse-proxy/</link>
					<comments>https://urubatan.dev/rails-from-zero-to-kubernetes-ingress-reverse-proxy/#respond</comments>
		
		<dc:creator><![CDATA[Rodrigo Urubatan]]></dc:creator>
		<pubDate>Tue, 14 Jan 2020 10:00:25 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[expose]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[ingress]]></category>
		<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[service]]></category>
		<guid isPermaLink="false">https://urubatan.dev/?p=388</guid>

					<description><![CDATA[Following up on the 'Rails from "zero" to kubernetes' series, we'll see today how to expose your service to the internet, since I think we all agree that mapping directly the port of your rails app to the internet is not a really wise choice.

Kubernetes has a "simple but works" solution called ingress, ingress is an HTTP proxy, it will work as a reverse proxy for HTTP and HTTPS for any service that exposes one one port.
]]></description>
										<content:encoded><![CDATA[
<p>Following up on the &#8216;<a href="https://urubatan.dev/tag/kubernetes/">Rails from &#8220;zero&#8221; to kubernetes</a>&#8216; series, we&#8217;ll see today how to expose your service to the internet, since I think we all agree that mapping directly the port of your rails app to the internet is not a really wise choice.</p>



<p>Kubernetes has a &#8220;simple but works&#8221; solution called ingress, ingress is an HTTP proxy, it will work as a reverse proxy for HTTP and HTTPS for any service that exposes one one port.</p>



<p>You can also use ingress to map multiple hosts, or multiple paths to different services.</p>



<p>The important restriction for ingress is that it will only proxy the ports 80 and 443.</p>



<p>To continue with the same example that are already working, we&#8217;ll change the <a href="https://urubatan.dev/rails-from-zero-to-kubernetes-a-service-to-access-your-pod/">service from the previous post</a> to ClusterIP, this way it will not be directly accessible to the outside world, the YAML will be like this:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="yaml"><pre class="de1"><span class="co3">apiVersion</span><span class="sy2">: </span>v1<span class="co3">
kind</span><span class="sy2">: </span>Service<span class="co4">
metadata</span>:<span class="co4">
  labels</span>:<span class="co3">
    run</span><span class="sy2">: </span>rails-sample<span class="co3">
  name</span><span class="sy2">: </span>rails-sample<span class="co4">
spec</span>:<span class="co4">
  ports</span>:<span class="co3">
  - port</span><span class="sy2">: </span>3000<span class="co3">
    protocol</span><span class="sy2">: </span>TCP<span class="co3">
    targetPort</span><span class="sy2">: </span>3000<span class="co4">
  selector</span>:<span class="co3">
    run</span><span class="sy2">: </span>rails-sample<span class="co3">
  type</span><span class="sy2">: </span>ClusterIP</pre></div></div></div></div></div></div></div>






<p>Then we&#8217;ll create an ingress mapping to that service, we&#8217;ll add some path mapping just to use it as an example, the initial ingress sample will map the path &#8220;/app&#8221; to our rails service like the sample bellow.</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="yaml"><pre class="de1"><span class="co3">apiVersion</span><span class="sy2">: </span>extensions/v1beta1<span class="co3">
kind</span><span class="sy2">: </span>Ingress<span class="co4">
metadata</span>:<span class="co4">
  annotations</span>:<span class="co3">
    nginx.ingress.kubernetes.io/rewrite-target</span><span class="sy2">: </span>/<span class="co3">
  name</span><span class="sy2">: </span>rails-sample<span class="co4">
spec</span>:<span class="co4">
  rules</span>:<span class="co4">
  - http</span>:<span class="co4">
      paths</span>:<span class="co3">
        - path</span><span class="sy2">: </span>/app<span class="co4">
          backend</span>:<span class="co3">
            serviceName</span><span class="sy2">: </span>rails-sample<span class="co3">
            servicePort</span><span class="sy2">: </span>3000</pre></div></div></div></div></div></div></div>






<p>The annotation &#8220;nginx.ingress.kubernetes.io/rewrite-target: /&#8221; will rewrite the paths to prevent errors in our application routing.</p>



<p>To check the ingress status and configuration, we can run the command &#8220;kubectl get ingress&#8221;, this will show the current ingress configuration and hosts mappings.</p>



<p>Ingress also supports name based virtual services, for example, if the host name &#8220;my.sample.service&#8221; points to your machine, you can map only this host name to your rails application with the following ingress configuration:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="yaml"><pre class="de1"><span class="co3">apiVersion</span><span class="sy2">: </span>extensions/v1beta1<span class="co3">
kind</span><span class="sy2">: </span>Ingress<span class="co4">
metadata</span>:<span class="co3">
  name</span><span class="sy2">: </span>rails-sample<span class="co4">
spec</span>:<span class="co4">
  rules</span>:<span class="co3">
    - host</span><span class="sy2">: </span>my.sample.service<span class="co4">
      http</span>:<span class="co4">
        paths</span>:<span class="co4">
        - backend</span>:<span class="co3">
            serviceName</span><span class="sy2">: </span>rails-sample<span class="co3">
            servicePort</span><span class="sy2">: </span>3000</pre></div></div></div></div></div></div></div>






<p>The rewrite annotation and the &#8220;path&#8221; were removed because in this sample, we are mapping the host name directly to our rails app.</p>



<p>It is possible to merge both samples, mapping many host names and many paths inside each host name to a different service.</p>



<p>Since ingress is also an HTTPS proxy, it will be wise to provide a certificate for it, so we&#8217;ll map correctly our &#8220;my.sample.service&#8221; domain.</p>



<p>You can create a self signed certificate with the following commands:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="bash"><pre class="de1">$ openssl req <span class="re5">-x509</span> <span class="re5">-nodes</span> <span class="re5">-days</span> <span class="nu0">365</span> <span class="re5">-newkey</span> rsa:<span class="nu0">2048</span> <span class="re5">-keyout</span> sample.key <span class="re5">-out</span> sample.crt <span class="re5">-subj</span> <span class="st0">&quot;/CN=my.sample.service/O=my.sample.service&quot;</span>
$ kubectl create secret tls rails-sample-tls <span class="re5">--key</span> sample.key <span class="re5">--cert</span> sample.crt <span class="re5">-o</span>=yaml <span class="sy0">&gt;</span> secret.yaml</pre></div></div></div></div></div></div></div>






<p>in the &#8220;req&#8221; part, remember that the common name must match your domain name, in my case &#8220;my.sample.service&#8221;</p>



<p>That last command creates the secret in kubernetes and saves it in the &#8220;secret.yaml&#8221; file for future reference, of course in a real application you&#8217;ll not use a self signed certificate, but you&#8217;ll buy one for you real domain.</p>



<p>Now lets configure out ingress to use this certificate, we&#8217;ll just update the yaml file with the following config:</p>





<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="yaml"><pre class="de1"><span class="co3">apiVersion</span><span class="sy2">: </span>extensions/v1beta1<span class="co3">
kind</span><span class="sy2">: </span>Ingress<span class="co4">
metadata</span>:<span class="co3">
  name</span><span class="sy2">: </span>rails-sample<span class="co4">
spec</span>:<span class="co4">
  tls</span>:<span class="co4">
    - hosts</span><span class="sy2">:
</span>      - my.sample.service<span class="co3">
      secretName</span><span class="sy2">: </span>rails-sample-tls<span class="co4">
  rules</span>:<span class="co3">
    - host</span><span class="sy2">: </span>my.sample.service<span class="co4">
      http</span>:<span class="co4">
        paths</span>:<span class="co4">
        - backend</span>:<span class="co3">
            serviceName</span><span class="sy2">: </span>rails-sample<span class="co3">
            servicePort</span><span class="sy2">: </span>3000</pre></div></div></div></div></div></div></div>






<p>You can either &#8220;kubectl edit ingress rails-sample&#8221; and change it as shown above, or change your file and &#8220;kubectl apply&#8221; it.</p>



<p>Now you can edit your /etc/hosts file and add &#8220;127.0.0.1    my.sample.service&#8221; to it, and access your app through https://my.sample.service.</p>



<p>You&#8217;ll need to tell your browser to thrust your self signed certificate.</p>



<p>And with this you can now deploy your HTTP/HTTPS applications using ingress, but sometimes this is not good enough, for example if you need for some reason expose another port through HTTPS, ingress will not allow that, and if you need that, stay tuned in this blog, and we&#8217;ll see another options that I&#8217;ve used in the next post.</p>



<p>As always, please leave comments with any questions or suggestions.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://urubatan.dev/rails-from-zero-to-kubernetes-ingress-reverse-proxy/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">388</post-id>	</item>
	</channel>
</rss>