<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan Shamatov</title>
    <description>The latest articles on DEV Community by Ivan Shamatov (@ivanshamatov).</description>
    <link>https://dev.to/ivanshamatov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F170169%2F4f792c5a-94fe-4b2b-8950-4e664d2e68d4.png</url>
      <title>DEV Community: Ivan Shamatov</title>
      <link>https://dev.to/ivanshamatov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ivanshamatov"/>
    <language>en</language>
    <item>
      <title>Advisory locks (рекомендательные блокировки)</title>
      <dc:creator>Ivan Shamatov</dc:creator>
      <pubDate>Mon, 27 Mar 2023 19:38:15 +0000</pubDate>
      <link>https://dev.to/ivanshamatov/advisory-locks-riekomiendatielnyie-blokirovki-272e</link>
      <guid>https://dev.to/ivanshamatov/advisory-locks-riekomiendatielnyie-blokirovki-272e</guid>
      <description>&lt;p&gt;&lt;em&gt;не путать с другими более оптимистичными или менее пессимистичными блокировками&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Возможно у вас в проекте уже используются эти &lt;a href="https://github.com/ClosureTree/with_advisory_lock"&gt;локи&lt;/a&gt;, но вы никогда не задумывались, что это за локи, как они работают и, самое важное, как НЕ работают. &lt;br&gt;
Эта блокировка в качестве аргумента принимает строку, которая будет служить уникальным ключом.&lt;/p&gt;
&lt;h2&gt;
  
  
  Начнем с того, что лок делать НЕ умеет
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Этот лок не блокирует данные, записи, таблицы, которые вы модифицируете внутри блока кода&lt;/strong&gt;&lt;br&gt;
Если у вас есть вот такой кусочек кода, который выполняется в потоке А&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_advisory_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"updating_users_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;и вот такой кусочек кода, который выполняется в потоке Б,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:permisson&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;permissions: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;update: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;то поток Б может модифицировать данные, которые модифицирует поток А, и наоборот.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Нельзя заменить транзакцию локом.&lt;/strong&gt;&lt;br&gt;
Рекомендательные блокировки (в постгресе) так вообще игнорируют границы транзакции если запрос на уровне сессии. Чтобы получить эффект "все или ничего" нужно использовать транзакцию, даже если используешь и лок. Например, вот так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_advisory_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"updating_user_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_organization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_periods&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. В общем случае, ключ для блокировок разных частей кода должен быть разным.&lt;/strong&gt;&lt;br&gt;
Вот два метода, и оба используют блокировку с одним и тем же ключом. Да, смотрится очень эффектно, но смысла в этом мало, потому как методы абсолютно независимые. Нет никакого резона, чтобы метод "update" ждал, пока метод "check" закончит свою работу, даже если он выполняется параллельно. И наоборот.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;with_user_lock&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;with_user_lock&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;task_completed?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;with_user_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_advisory_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"updating_user_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Конечно, есть исключения и можно придумать сценарий, когда один и тот же лок в разных методах имеет смысл, но вы должны четко понимать, что вы делаете и зачем.&lt;/p&gt;

&lt;h2&gt;
  
  
  Так а зачем же тогда нужен этот лок?
&lt;/h2&gt;

&lt;p&gt;Эта блокировка отлично подходит для контроля конкурентных запросов на уровне приложения (Application-level Concurrency Control). Это мьютекс, который используется для того, чтобы гарантировать, что два разных процесса, которые исполняют ОДИН и ТОТ ЖЕ код, не выполняют этот код одновременно.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateRoleJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_advisory_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"updating_user_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Если по какой-то причине у вас в очереди появилось две задачи для одного и того же пользователя, и если случилось так, что 2 разных воркера начнут обрабатывать эти две задачи, то вы можете быть уверены, что только один из них будет выполняться в данный момент, а второй будет ждать, пока первый закончит свою работу.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Каскадный rescue в ruby и в ActiveSupport::Rescuable</title>
      <dc:creator>Ivan Shamatov</dc:creator>
      <pubDate>Sat, 11 Mar 2023 08:56:17 +0000</pubDate>
      <link>https://dev.to/ivanshamatov/kaskadnyi-rescue-v-ruby-i-v-activesupportrescuable-h4g</link>
      <guid>https://dev.to/ivanshamatov/kaskadnyi-rescue-v-ruby-i-v-activesupportrescuable-h4g</guid>
      <description>&lt;p&gt;Возьмем пример&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue E"&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; 
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue StandardError"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Если вы используете вот такой вот каскадный &lt;code&gt;rescue&lt;/code&gt;, то вы наверняка знаете, что сработает только обработчик первой ошибки, которая подпадает под условие.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; here we rescue E
nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Если мы повторно сделаем &lt;code&gt;raise&lt;/code&gt; в обработке ошибки, то этот &lt;code&gt;raise&lt;/code&gt; уже отловлен не будет и приведет к эксепшену&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue E"&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; 
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue StandardError"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; here we rescue E
E: E
from &lt;span class="o"&gt;(&lt;/span&gt;pry&lt;span class="o"&gt;)&lt;/span&gt;:27:in &lt;span class="s1"&gt;'__pry__'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  rescue_from из ActiveSupport::Rescuable
&lt;/h2&gt;

&lt;p&gt;Если мы захотим перенести такую логику в контроллер, или у нас есть какой-то кастомный бизнес-экшен, который использует &lt;code&gt;ActiveSupport::Rescuable&lt;/code&gt;, то нужно помнить, что работать обработчик будет по-другому.&lt;/p&gt;

&lt;p&gt;Пусть у нас будет вот такой базовый бизнес-экшн.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rescuable&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;
    &lt;span class="n"&gt;call_impl&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
    &lt;span class="n"&gt;rescue_with_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;И мы наследуемся от него, определяя &lt;code&gt;call_impl&lt;/code&gt; и добавляя обработчики ошибок,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Action&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue E"&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue StandardError"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_impl&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;то получим вот такой результат.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; A.new.call
here we rescue StandardError
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;#&amp;lt;A::E: A::E&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Дело в том, что &lt;code&gt;rescue_from&lt;/code&gt; работает в FILO режиме (First In Last Out), то есть последний обработчик, который мы определили, будет первым попробованным.&lt;/p&gt;

&lt;p&gt;Поменяем местами обработчики местами&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Action&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue StandardError"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"here we rescue E"&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_impl&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;E&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;и вот результат&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;irb&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;:044:0&amp;gt; A.new.call
here we rescue E
Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
...
1: from &lt;span class="o"&gt;(&lt;/span&gt;irb&lt;span class="o"&gt;)&lt;/span&gt;:41:in &lt;span class="sb"&gt;`&lt;/span&gt;call_impl&lt;span class="s1"&gt;'
A::E (A::E)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Таким образом мы видим, что сработал именно обработчик ошибки &lt;code&gt;A::E&lt;/code&gt;, а не &lt;code&gt;StandardError&lt;/code&gt;, а так же, что &lt;code&gt;raise&lt;/code&gt; внутри обработчика ошибки не привел к еще повторному &lt;code&gt;rescue&lt;/code&gt; из &lt;code&gt;StandardError&lt;/code&gt;. &lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>dry-cli v0.6. Highlighting main features</title>
      <dc:creator>Ivan Shamatov</dc:creator>
      <pubDate>Fri, 06 Mar 2020 10:57:24 +0000</pubDate>
      <link>https://dev.to/ivanshamatov/dry-cli-updates-39kk</link>
      <guid>https://dev.to/ivanshamatov/dry-cli-updates-39kk</guid>
      <description>&lt;p&gt;Hey folks! &lt;br&gt;
For those who are not familiar with &lt;code&gt;dry-cli&lt;/code&gt; gem, let me introduce it to you. Long story short: &lt;code&gt;dry-cli&lt;/code&gt;, formerly known as &lt;code&gt;hanami-cli&lt;/code&gt;, was moved from &lt;code&gt;hanami&lt;/code&gt; repository to &lt;code&gt;dry-rb&lt;/code&gt; organization. For almost everyone, &lt;code&gt;hanami-cli&lt;/code&gt; sounds like a set of &lt;code&gt;cli&lt;/code&gt; utilities/generators/runners for &lt;code&gt;hanami&lt;/code&gt;, but in fact it wasn't the case.&lt;/p&gt;

&lt;p&gt;So it was decided to rename &lt;code&gt;hanami-cli&lt;/code&gt; to &lt;code&gt;dry-cli&lt;/code&gt;. &lt;code&gt;hanami-cli 0.3.1&lt;/code&gt; became a &lt;code&gt;dry-cli 0.4&lt;/code&gt;, &lt;code&gt;dry-cli 0.5&lt;/code&gt; release removed all the &lt;code&gt;hanami&lt;/code&gt; dependencies. And the latest release of &lt;code&gt;dry-cli 0.6&lt;/code&gt; has multiple new features. Do not worry, all backward compatibility is retained. &lt;/p&gt;

&lt;p&gt;So let me run through the CHANGELOG and highlight some of the new features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anonymous registry syntax&lt;/li&gt;
&lt;li&gt;Singular command app&lt;/li&gt;
&lt;li&gt;Inline syntax for commands&lt;/li&gt;
&lt;li&gt;Stderr added&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Anonymous registry syntax
&lt;/h3&gt;

&lt;p&gt;Initial &lt;code&gt;hanami-cli&lt;/code&gt; allowed to create any module you want, extend it with &lt;code&gt;Dry::CLI::Registry&lt;/code&gt; functionality and use &lt;code&gt;register&lt;/code&gt; method to add commands inside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;

  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;aliases: &lt;/span&gt;&lt;span class="sx"&gt;%w[v -v --version]&lt;/span&gt;
  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'echo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="no"&gt;Echo&lt;/span&gt;

  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'generate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;aliases: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'g'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Generate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Configuration&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please understand me correctly, I love Registry concept, but to decrease entry threshold the new anonymous registry syntax has been introduced. Now you don't have to think of an extra &lt;code&gt;Registry&lt;/code&gt; module, it will be prepared for you automatically at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;aliases: &lt;/span&gt;&lt;span class="sx"&gt;%w[v -v --version]&lt;/span&gt;
    &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'echo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="no"&gt;Echo&lt;/span&gt;

    &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'generate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;aliases: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'g'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Generate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Configuration&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="c1"&gt;# do not forget to execute `call` after configuring your CLI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Singular command app
&lt;/h3&gt;

&lt;p&gt;You can think of 2 types of CLI applications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Single command, like &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Multitool, like &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;heroku&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So when you invoke &lt;code&gt;git&lt;/code&gt; you also have to specify which command of &lt;code&gt;git&lt;/code&gt; do you need to run: &lt;code&gt;git pull&lt;/code&gt;, &lt;code&gt;git checkout&lt;/code&gt;, etc. For a long time, &lt;code&gt;hanami-cli&lt;/code&gt; (and later &lt;code&gt;dry-cli&lt;/code&gt;) was extremely useful for building multitools, but you had no ability to create a single command app.&lt;/p&gt;

&lt;p&gt;Now it is fixed. And to run a singular command app you may pass the command-class into &lt;code&gt;Dry.CLI&lt;/code&gt; constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inline syntax for commands
&lt;/h3&gt;

&lt;p&gt;I decided to go further and added a little bit of syntactic sugar. Think of it as something similar to the &lt;code&gt;sinatra&lt;/code&gt; simplified syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sinatra'&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="s1"&gt;'Hello world!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you still can make a separate class and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sinatra/base'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Sinatra&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="s1"&gt;'Hello world!'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to &lt;code&gt;sinatra&lt;/code&gt;, you have the ability to save some keystrokes with &lt;code&gt;dry-cli&lt;/code&gt;, while building a very simple CLI app. And with a combination of bundler inline syntax it looks awesome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'bundler/inline'&lt;/span&gt;

&lt;span class="n"&gt;gemfile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'dry-cli'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli/inline'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'List files in a directory'&lt;/span&gt;
&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'[DIR]'&lt;/span&gt;
&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;aliases: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;type: :boolean&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to run &lt;code&gt;chmod +x your_cli&lt;/code&gt; to be able to execute it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stderr added
&lt;/h3&gt;

&lt;p&gt;Nothing to show here, just a small improvement according to the best Unix's practices. All the diagnostic outputs and errors should go straight to the Stderr. The banner which reacts to the &lt;code&gt;-h&lt;/code&gt; flag is the result of command execution, so the output is valid and goes straight to the Stdout. &lt;/p&gt;

&lt;h3&gt;
  
  
  Future plans
&lt;/h3&gt;

&lt;p&gt;Since I've joined the team to work on this gem, I found lots of things to improve.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First of all, currently all the IO inside the commands are not being delegated to IO you pass into &lt;code&gt;Dry.CLI&lt;/code&gt; constructor. &lt;/li&gt;
&lt;li&gt;I'm looking for a better way to invoke commands from the other commands&lt;/li&gt;
&lt;li&gt;Several file utils have been extracted from hanami to be a part of the &lt;code&gt;dry-cli&lt;/code&gt; gem. We need a better support for them.&lt;/li&gt;
&lt;li&gt;Generation abilities (&lt;a href="https://github.com/dry-rb/dry-cli/pull/38"&gt;https://github.com/dry-rb/dry-cli/pull/38&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add support for subcommands with valid parent command (&lt;a href="https://github.com/dry-rb/dry-cli/pull/86"&gt;https://github.com/dry-rb/dry-cli/pull/86&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>dry</category>
      <category>cli</category>
      <category>hanami</category>
    </item>
  </channel>
</rss>
