<?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: Jan Peterka</title>
    <description>The latest articles on DEV Community by Jan Peterka (@janmpeterka).</description>
    <link>https://dev.to/janmpeterka</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%2F169937%2F98e64758-ae85-4339-af32-b5e8b90f290a.jpg</url>
      <title>DEV Community: Jan Peterka</title>
      <link>https://dev.to/janmpeterka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janmpeterka"/>
    <language>en</language>
    <item>
      <title>Using Phlex helps me be a better programmer</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Sat, 26 Jul 2025 12:33:09 +0000</pubDate>
      <link>https://dev.to/janmpeterka/using-phlex-helps-me-be-a-better-programmer-16ce</link>
      <guid>https://dev.to/janmpeterka/using-phlex-helps-me-be-a-better-programmer-16ce</guid>
      <description>&lt;h2&gt;
  
  
  What is Phlex?
&lt;/h2&gt;

&lt;p&gt;If you are in the Ruby on Rails land, you might have noticed &lt;a href="https://www.phlex.fun/" rel="noopener noreferrer"&gt;Phlex&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A little Ruby gem for building HTML and SVG view components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;, as it says on the website.&lt;/p&gt;

&lt;p&gt;It was concieved by &lt;a href="https://joel.drapper.me/" rel="noopener noreferrer"&gt;Joel Drapper&lt;/a&gt; as a new approach to view layer in Rails (and other web app frameworks, but I don't have any experience in that department).&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing view layer in Rails app.
&lt;/h2&gt;

&lt;p&gt;I'm pretty much new kid in Rails department (started with it ~5 years ago), so I don't feel like talking about the whole history of view stack in this framework. But even my experience with it (mostly in my ~15yr old $WORK project, and some new projects I started from scratch using Rails 7-8 ) is bit cumbersome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there's the "golden standard" - &lt;code&gt;erb&lt;/code&gt; templating.

&lt;ul&gt;
&lt;li&gt;I don't like erb. It's just a lot of writing, and the code us just ugly in my view.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;at $WORK, we use &lt;code&gt;slim&lt;/code&gt; instead, and I prefer that, so I mostly use that!

&lt;ul&gt;
&lt;li&gt;but for example when I was creating Rails generator I intend to publish, it makes sense to write it in &lt;code&gt;erb&lt;/code&gt;, as much more people use that.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;we also use ViewComponents, which I then used (mostly copy-pasted) in my other projects to save time.

&lt;ul&gt;
&lt;li&gt;they are probably super powerful, but I have trouble understanding them. Also, I don't like having two files (&lt;code&gt;rb&lt;/code&gt; + &lt;code&gt;html.*&lt;/code&gt;) for one thing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;and of course you have partials and helper

&lt;ul&gt;
&lt;li&gt;I 'm currently very suspicous of helpers. They are great until you shove logic into them (which can happen easily).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Also, for a long time, there were no component libraries for Rails! That's currently changing, as I'll soon mention.&lt;/p&gt;

&lt;p&gt;As you see, I was not super happy about the stuff I used for view layer, but there wasn't much to do about that.&lt;br&gt;
So, when Phlex came by, I was curious - would this be better for me?&lt;/p&gt;
&lt;h2&gt;
  
  
  Different, in a good way?
&lt;/h2&gt;

&lt;p&gt;I wasn't sure.&lt;br&gt;
One of the big things that Phlex does differently is that &lt;strong&gt;you only write Ruby&lt;/strong&gt;. No HTML. No &lt;code&gt;erb&lt;/code&gt; or &lt;code&gt;slim&lt;/code&gt; or other templating. It's all ruby code.&lt;br&gt;
Don't get me wrong, I absolutely love Ruby, but at the same time, I'm bit sceptical about approaches that try to "get rid" of some language - I don't for example think you should write Ruby/&lt;a href="https://skulpt.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;/.. instead of javascript.&lt;/p&gt;

&lt;p&gt;With this mix of scepticism and curiosity, when I started new project, I wanted to play with Phlex a bit.&lt;br&gt;
What did help was that I found &lt;a href="https://rubyui.com/" rel="noopener noreferrer"&gt;RubyUI&lt;/a&gt;, nice library of components made with Phlex, which did speed up the initial prototype development by a lot.&lt;/p&gt;

&lt;p&gt;It took me a bit to get a grasp, and I stumbled multiple time, as I don't fully understand how it all works. Also, I still had basic layouts and used simple_form (I wanted to use &lt;a href="https://github.com/beautifulruby/superform" rel="noopener noreferrer"&gt;superform&lt;/a&gt;, which looks amazing, but it didn't support Phlex 2.x at the time I started the project. It should now, so maybe I'll give it a try now!), so there's some mixing of old code with new, which is all doable, just slows one down at first.&lt;/p&gt;

&lt;p&gt;But then?&lt;br&gt;
I found out I love it.&lt;br&gt;
I'll show an example of a page I got to rewrite to Phlex on a different project, where I now use classic approach + ViewComponents, and try to describe why Phlex makes sense to me now:&lt;/p&gt;

&lt;p&gt;Here's original controller:&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;# app/controllers/dashboards_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@current_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
    &lt;span class="n"&gt;dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

    &lt;span class="vi"&gt;@future_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selected_future_events&lt;/span&gt;

    &lt;span class="vi"&gt;@sboodles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="vi"&gt;@unread_announcements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unread_announcements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@pinned_announcements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pinned_announcements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="vi"&gt;@tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assigned_tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomplete&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Views&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Dashboard&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;dashboard&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;selected_future_events&lt;/span&gt;
      &lt;span class="n"&gt;nearest_performance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:performance?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;nearest_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nearest_performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;nearest_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exclude?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nearest_performance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nearest_events&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;nearest_performance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;nearest_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight slim"&gt;&lt;code&gt;&lt;span class="err"&gt;# &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;/views/dashboards/show&lt;span class="nc"&gt;.html.slim&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;content_for&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nástěnka"&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@current_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"právě probíhá"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"events/card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;event: &lt;/span&gt;&lt;span class="vi"&gt;@current_events&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="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@future_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"nejbližší události"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;.grid.md&lt;/span&gt;:grid-cols-3&lt;span class="nc"&gt;.gap-4&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@future_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"events/card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@sboodles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"aktivní sboodly"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@unread_announcements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"nové příspěvky"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;.grid.md&lt;/span&gt;:grid-cols-3&lt;span class="nc"&gt;.gap-4&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@unread_announcements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;announcement&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"announcements/card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;announcement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@pinned_announcements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"připnuté příspěvky"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;.grid.md&lt;/span&gt;:grid-cols-3&lt;span class="nc"&gt;.gap-4&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@pinned_announcements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;announcement&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"announcements/card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;announcement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"nejbližší úkoly"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;.grid.md&lt;/span&gt;:grid-cols-3&lt;span class="nc"&gt;.gap-4&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;task&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"tasks/card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;editable: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's a new, Phlex, one:&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;# app/controllers/dashboards_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Views&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Dashboard&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;dashboard&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/views/dashboard/show.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Views::Dashboard::Show&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Views&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;prop&lt;/span&gt; &lt;span class="ss"&gt;:dashboard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:positional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reader: :private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view_template&lt;/span&gt;
    &lt;span class="n"&gt;content_for&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dashboard"&lt;/span&gt;

    &lt;span class="n"&gt;current_event&lt;/span&gt;

    &lt;span class="n"&gt;future_events&lt;/span&gt;

    &lt;span class="n"&gt;active_sboodles&lt;/span&gt;

    &lt;span class="n"&gt;unread_announcements&lt;/span&gt;

    &lt;span class="n"&gt;pinned_announcements&lt;/span&gt;

    &lt;span class="n"&gt;tasks&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;future_events&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

      &lt;span class="no"&gt;Heading&lt;/span&gt; &lt;span class="s2"&gt;"future events"&lt;/span&gt;

      &lt;span class="n"&gt;grid&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="no"&gt;EventCard&lt;/span&gt; &lt;span class="n"&gt;event&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;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# there are of course all the methods, I just don't want to list them all&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, so multiple interesting things are happening here, not all Phlex specific, but Phlex (+ Literal) did definitely push me it the right direction:&lt;/p&gt;

&lt;h3&gt;
  
  
  Controller got much smaller
&lt;/h3&gt;

&lt;p&gt;I love small controllers. I have a strong belief that the controller method should only do one thing (not counting loading and rendering/redirecting), preferrably on one line of code.&lt;br&gt;
In here, I didn't previously follow this directive. I got lazy. I just kept adding instance variables, because the template will just pick them up. It doesn't cost me anything.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sidenote: I like how this interface is made explicit in partials using &lt;a href="https://guides.rubyonrails.org/action_view_overview.html#strict-locals" rel="noopener noreferrer"&gt;strict locals&lt;/a&gt;. But for the "main" view, there's nothing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, with Phlex it does. Because I'm explicitely passing values to method:&lt;br&gt;
1️⃣ on the controller side I have to edit &lt;code&gt;render Views::Dashboard::Show.new dashboard&lt;/code&gt;&lt;br&gt;
2️⃣ on the view side I have to specify incoming attributes&lt;br&gt;
If you are confused about what's &lt;code&gt;prop :dashboard, Dashboard, :positional, reader: :private&lt;/code&gt; about, it's from different gem (&lt;a href="//literal.fun"&gt;Literal&lt;/a&gt;) by Joel Drapper, which works great with Phlex. I could do the same with&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;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dashboard&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt;

&lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:dashboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but &lt;a href="https://literal.fun/docs/properties" rel="noopener noreferrer"&gt;Literal Properties&lt;/a&gt; makes this much simpler, and I get type checking "for free".&lt;/p&gt;

&lt;p&gt;So it made sense to finally make a "presenter" object &lt;code&gt;Dashboard&lt;/code&gt; to pass around, which is what I wanted to do anyway, just didn't have that much reason to.&lt;/p&gt;

&lt;h3&gt;
  
  
  View can have layers of abstraction
&lt;/h3&gt;

&lt;p&gt;I'm a bit fan of having only one (and the same) layer of abstraction in one place. So I like that my &lt;code&gt;view_template&lt;/code&gt; now describes basic structure of my dashboard. You can look at the code and just know what to expect. Then, if you need to dig deeper, you do, very easily!&lt;/p&gt;

&lt;h3&gt;
  
  
  All in one place
&lt;/h3&gt;

&lt;p&gt;Very easily indeed, as you have all the code in same place! Using traditional templates, I did have &lt;code&gt;show.html.slim&lt;/code&gt;, and then all the partials.&lt;br&gt;
Now the "partials" are in same place.&lt;/p&gt;

&lt;p&gt;These last points show why I did get to like having all in Ruby - you can use all the Ruby tricks and patters and it also leads me to keeping my code clean, much cleaner then I do in templates (where I'm just like "fuck it" and do anything necessary half the time). This is only my personal failing, not any objective problem with templates! But if there's a way to nudge me into writing better code? Yes please!&lt;/p&gt;

&lt;p&gt;You can notice multiple more ways this did force me to make the code bit nicer:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;grid&lt;/code&gt; is just a very simple shared method:&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;grid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;cols: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"grid md:grid-cols-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; gap-2 mb-3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&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;as I 1️⃣ didn't like to repeat the code, and 2️⃣ also I don't like the look of long list of classes in my code (maybe I shouldn't use Tailwind than I hear you cry? Well, I like it for fast prototyping and changing, and when contained in components and helpers, it doesn't bother me!)&lt;/p&gt;

&lt;p&gt;Then I also created component &lt;code&gt;EventCard&lt;/code&gt;, but I already wrote a lot of code, so maybe let's not get into that (it's just - again, in my view - more elegant than using ViewComponent, but not that different).&lt;/p&gt;

&lt;p&gt;Ok, let's wrap this up.&lt;/p&gt;

&lt;p&gt;I wanted to share my exploration of Phlex as a different way of managing both components and views themselves. You can also choose to use Phlex only for components (which I originally planned to), but I got very much convinced that using it for views makes my code better and myself happier.&lt;/p&gt;

&lt;p&gt;Here's a summary of whys:&lt;br&gt;
1️⃣ I enjoy having one Ruby file instead of Ruby + HTML for components&lt;br&gt;
2️⃣ I love how Phlex makes it soo easy to work with "components", and bit cumbersome to work with anything else, that it basically forces me to write everything that way - which gives me reusable and consistent elements and layouts!&lt;br&gt;
3️⃣ I love using private methods for parts of the page (all in one file) instead of partials (that you have to search through).&lt;br&gt;
4️⃣ Literal Properties are super convenient with both components and views&lt;br&gt;
5️⃣ And using them nudges me into very clean and simple interfaces, following patterns that I believe in, but sometimes don't follow fully.&lt;/p&gt;

&lt;p&gt;Do you have experience with Phlex? Do you like it? I'd love to hear from you!&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Concerning God objects in Rails legacy app</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Mon, 17 Mar 2025 15:06:00 +0000</pubDate>
      <link>https://dev.to/janmpeterka/concerning-god-objects-in-rails-legacy-app-1oc0</link>
      <guid>https://dev.to/janmpeterka/concerning-god-objects-in-rails-legacy-app-1oc0</guid>
      <description>&lt;p&gt;I have a lot of fun lately with diving into old and new parts of our ~15yo app at $DAYJOB, and trying to improve it bits by bits.&lt;/p&gt;

&lt;p&gt;One of the issues, as many older apps may have, are "god objects" - objects with so much logic inside them, they are mostly impossible to understand and maintain.&lt;/p&gt;

&lt;p&gt;It can happen to any object - you start with something small and concrete, possibly adhering to Single responsibility principle even. But then, you need to add &lt;strong&gt;just one more method&lt;/strong&gt; to the object, and one more after that, some scopes, validations, callbacks.&lt;/p&gt;

&lt;p&gt;And when new developer joins the project, they see that this object has so many to do, and so many lines, it won't hurt to just add another.&lt;/p&gt;

&lt;p&gt;And then you see their pull request, think "oh, we should someday clean this, it's terrible", but that's not the juniors responsibility, so you just sigh and approve this new addition, solidifying this monstrosity.&lt;/p&gt;

&lt;p&gt;I was once the junior, having no worries about the code design, just doing what I saw the more experienced devs doing. So I contributed my share to this mess.&lt;br&gt;
Now, I'm bit more experienced, bit more aware of consequences, and bit less careless about this kind of code.&lt;br&gt;
Now I feel it's my responsibility to visit this Augean Stables, and emerge, if not victorious, than at least proud of some improvements I'm able to do.&lt;/p&gt;

&lt;p&gt;Luckily, I also really enjoy it (well, most of the time).&lt;/p&gt;

&lt;p&gt;So how do I start?&lt;/p&gt;
&lt;h2&gt;
  
  
  Finding topics
&lt;/h2&gt;

&lt;p&gt;I try to find out bundles of behavior and data that belong together - our object surely contains more than one responsibility (or topic) now, lets see what is there.&lt;br&gt;
For example, we have object Job in our app. It does a lot of things (and have more then one role, which complicates things a bit), but let's say that it mostly describes demands users create in our application.&lt;/p&gt;

&lt;p&gt;Demand has and does a lot of things - it can be commented and bided upon, can be reviewed, rated, priced, approved, verified, categorized, located, and many more things.&lt;/p&gt;

&lt;p&gt;Let's say we have something like this:&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;# app/models/job.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# ...a lot of code&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_rated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# ...different code&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_rating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# does some logic&lt;/span&gt;
    &lt;span class="n"&gt;send_notification_about_updated_rating&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...more code&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification_about_updated_rating&lt;/span&gt;
    &lt;span class="c1"&gt;# does some notifying &lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ...it never ends...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have any experience with Rails, you probably see where I'm heading.&lt;/p&gt;

&lt;p&gt;Yes, I will heavily lean on Concerns (and ruby &lt;code&gt;modules&lt;/code&gt; in general).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note on cautiosness: Now, there are people who don't like Concerns, as they can lead you to the same god object as I described, just hidden in multiple files. And I understand their concerns, and yes - that can happen and is not great. See Jason Swett's article in resources at the end of this article to read more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, whether you like them or not, and whether you want them in your app, they can be a great tool for what I'm trying to achieve here. Bear with me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting topics to Concerns
&lt;/h2&gt;

&lt;p&gt;Once I found some &lt;em&gt;topics&lt;/em&gt; in my object, I will create a new concern for them, and carefully find anything related to move there:&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;# app/models/concerns/job/rateable.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Job::Rateable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&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;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_rated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&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;def&lt;/span&gt; &lt;span class="nf"&gt;add_rating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# does some logic&lt;/span&gt;
    &lt;span class="n"&gt;send_notification_about_updated_rating&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;send_notification_about_updated_rating&lt;/span&gt;
    &lt;span class="c1"&gt;# does some notifying &lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/job.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Job&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rateable&lt;/span&gt; &lt;span class="c1"&gt;# I can also omit the `Job::` part, but I like it. &lt;/span&gt;

  &lt;span class="c1"&gt;# ...a lot of code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...different code  &lt;/span&gt;
  &lt;span class="c1"&gt;# ...more code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...it never ends...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note on testing: Even though moving things to concern seems pretty safe, I got failing tests after this many times (and sometimes errors in production, when tests were missing or incomplete). So, first step should be to check for tests, and have a general idea about what is tested. Also, if there are no tests, it is useful to add some.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If successful, I now have one file with all related code to this topic (e.g. Job can be reviewed).&lt;br&gt;
This has immense benefit in itself - hopefully now I can get proper idea about what's used, what behavior is expected. I don't need to understand all logic, just overall feel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Improving code
&lt;/h2&gt;

&lt;p&gt;Now I can start to restructure this a bit. Possibly there are some methods that can be marked as private, making it clearer what is the public interface of our Job in the topic of reviewing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note on private methods in Ruby: There's some discussion on how to use private in ruby. Marking method private doesn't really stop you from using it - you can call it using &lt;code&gt;send&lt;/code&gt;, for example.&lt;br&gt;
I do use it mostly to make it clear that it's just internal details, not public interface. It makes it clearer to me what I'm dealing with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example, notifying about rating is probably only done on methods in our concern, so we can mark it private:&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;# app/models/concerns/job/rateable.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Job::Rateable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&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;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_rated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&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;def&lt;/span&gt; &lt;span class="nf"&gt;add_rating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# does some logic&lt;/span&gt;
    &lt;span class="n"&gt;send_notification_about_updated_rating&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification_about_updated_rating&lt;/span&gt;
    &lt;span class="c1"&gt;# does some notifying &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;blockquote&gt;
&lt;p&gt;Note on testing: Now I'm doing real changes to my code, and I'm very happy to have tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great, my idea about this area of code is getting better and better - I have everything in one place, I know what is public interface and what are internals, maybe I even found some old code that is no longer used in our app (&lt;code&gt;git log -p -S&lt;/code&gt; is my good friend here), which is a nice bonus.&lt;/p&gt;

&lt;p&gt;But, we still have a model with a lot of code, lot of responsibility on them.&lt;/p&gt;

&lt;p&gt;Luckily, we have all we need to move one step further, and create truly well designed code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Once again, it's just PORO
&lt;/h2&gt;

&lt;p&gt;As we have all the logic in one place, we might be able to extract it to one or more POROs (plain old ruby objects):&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;# app/models/job_rating.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JobRating&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:job&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;job&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;add_rating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# does some logic&lt;/span&gt;
    &lt;span class="n"&gt;send_notification_about_updated_rating&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification_about_updated_rating&lt;/span&gt;
    &lt;span class="c1"&gt;# does some notifying &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;We now have self-contained class, with one responsibility - adding rating (in real world, it probably also includes updating rating, deleting rating and so on. but still.). Now, finally, job has no bussiness in managing its ratings - that's job of this object!&lt;/p&gt;

&lt;p&gt;But wait! Do we have to find all places where we call &lt;code&gt;job.add_rating&lt;/code&gt; and rewrite it to &lt;code&gt;JobRating.new(job).add_rating&lt;/code&gt;? No!&lt;br&gt;
First, that would be ugly as hell. &lt;br&gt;
Second, that's a lot of work I'm not willing to do.&lt;br&gt;
Third, now all these places would need to know that there's JobRating class, ugh.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note on preferences: Maybe you are happy to stop here, and get rid of the Concern that helped us get here. That's okay too! Using concern the way I do in next part has its drawbacks - it's less clear where the logic resides and you have to follow the trail of delegation, also it makes it cosy to just stick more and more to the concern, getting the same issues we had before. I like structuring my code this way, but you don't have to!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Delegating responsibilities
&lt;/h2&gt;

&lt;p&gt;No, there's better way - we leverage Concerns again:&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;# app/models/concerns/job/rateable.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Job::Rateable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&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;Concern&lt;/span&gt;

  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:add_rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :rating&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:not_rated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&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;def&lt;/span&gt; &lt;span class="nf"&gt;rating&lt;/span&gt;
    &lt;span class="vi"&gt;@rating&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;JobRating&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="nb"&gt;self&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we don't have to change anything anywhere! Rest of our codebase doesn't care - it just wants to add rating to job! Now job accepts this request, and delegates it to rating object.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note on inspiration: This pattern is not my discovery, but I'm now unable to find the article I read introducing it. If you know where it came from, let me know and I will add it to resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here I showed how using Concern as a stepping stone helps me to make messy code cleaner and better separated.&lt;br&gt;
What do you think? Do you also use this approach, or do you have other tricks to clean this sort of code?&lt;br&gt;
And what are your opinions on (over)using Concerns? Let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources and more reading
&lt;/h2&gt;

&lt;p&gt;Here's some reading about Concerns, if you want a bit more context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.writesoftwarewell.com/how-rails-concerns-work-and-how-to-use-them/#why-and-when-should-you-use-concerns" rel="noopener noreferrer"&gt;Concerns in Rails: Everything You Need to Know&lt;/a&gt; (Akshay Khot @ Write Software Well)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.codewithjason.com/used-intelligently-rails-concerns-great" rel="noopener noreferrer"&gt;When used intelligently, Rails concerns are great&lt;/a&gt; (Jason Swett)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.37signals.com/vanilla-rails-is-plenty/" rel="noopener noreferrer"&gt;Vanilla Rails is plenty&lt;/a&gt; (Jorge Manrubia)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kaspth/active_record-associated_object" rel="noopener noreferrer"&gt;ActiveRecord AssociatedObject gem&lt;/a&gt; (gem by Kasper Timm Hansen, making this pattern more hidden and ergonomic)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Managing current user in Rails, our way - part 3</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Thu, 06 Mar 2025 13:12:19 +0000</pubDate>
      <link>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-part-3-508i</link>
      <guid>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-part-3-508i</guid>
      <description>&lt;h2&gt;
  
  
  Finding the right representation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you haven't read &lt;a href="https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-3802"&gt;first part&lt;/a&gt; of this series, start there. It explains the basics of how and whys behind this one. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I said before, we used &lt;code&gt;current_user == :false&lt;/code&gt; to indicate that we checked whether we can log in user for this request, and found out we cannot - meaning it's "anonymous user", or "guest".&lt;/p&gt;

&lt;p&gt;We tried to think and code different approaches - for some time we played with having &lt;code&gt;AnonymousUser&lt;/code&gt; class, or &lt;code&gt;NilUser&lt;/code&gt; (borrowing from nil object pattern - read more &lt;a href="https://dev.to/daviducolo/exploring-the-null-object-pattern-in-ruby-k5l"&gt;here&lt;/a&gt; or &lt;a href="https://avdi.codes/null-objects-and-falsiness/" rel="noopener noreferrer"&gt;here&lt;/a&gt;), to finally find an approach we liked - using &lt;a href="https://refactoring.guru/design-patterns/decorator/ruby/example#lang-features" rel="noopener noreferrer"&gt;decorated object&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What that means? The idea is simple - what if &lt;code&gt;current_user&lt;/code&gt; was not three different types of objects (User, AnonymousUser, nil), but just one - let's call it &lt;code&gt;CurrentUser&lt;/code&gt;. Here is some (incomplete) code:&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;# app/models/current_user.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUser&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

  &lt;span class="n"&gt;delegate_missing_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;authenticated?&lt;/span&gt; &lt;span class="o"&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;present?&lt;/span&gt;
  &lt;span class="c1"&gt;# Or equivalent, if you are not fan of Ruby endless methods:&lt;/span&gt;
  &lt;span class="c1"&gt;# def authenticated?&lt;/span&gt;
  &lt;span class="c1"&gt;#   user.present?&lt;/span&gt;
  &lt;span class="c1"&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;First thing you might notice is that this is PORO (plain old ruby object) - no inheritance, no composition.&lt;br&gt;
Its usage is very simple:&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;log_in&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="vi"&gt;@current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CurrentUser&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="n"&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;So - we will try to always have some instance of CurrentUser in our &lt;code&gt;current_user&lt;/code&gt; attribute, which will answer if user is &lt;code&gt;authenticated?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is great improvement in that we can lean much more on duck-typing - our older code often looked like this:&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;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="c1"&gt;# or sometimes just&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:false&lt;/span&gt;
&lt;span class="c1"&gt;# or some variations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We would much prefer to be confident that our &lt;code&gt;current_user&lt;/code&gt; can always tell us - is the user authenticated?&lt;/p&gt;

&lt;p&gt;So, that's improvement.&lt;/p&gt;

&lt;p&gt;Also, as we used &lt;code&gt;delegate_missing_to :user&lt;/code&gt;, in cases when we are interested in other user information (name, email or any other user attributes/methods), we can interact with &lt;code&gt;current_user&lt;/code&gt; without having a clue what it really is - if it walks like a user, and quacks like a user, you know the rest. The &lt;code&gt;allow_nil: true&lt;/code&gt; part helps us even when there is no user - it will just respond with &lt;code&gt;nil&lt;/code&gt;, which is legitimate value in cases like name or email (we would probably want to specify some &lt;code&gt;false&lt;/code&gt; values on some attributes, but more on that later).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Summary:&lt;br&gt;
We used Decorator pattern to achieve duck-typed interface when interacting with current_user object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was happy with this approach, changed places where current_user was assigned to use CurrentUser, and ran test suite.&lt;/p&gt;

&lt;p&gt;Of course, it was massively failing. Why? Now we get to some bit more interesting and/or confusing parts of Rails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using our PORO in associations
&lt;/h2&gt;

&lt;p&gt;It's pretty common to want use the &lt;code&gt;current_user&lt;/code&gt; in association.&lt;br&gt;
Example:&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;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;initiator: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This failed on &lt;code&gt;ActiveRecord::AssociationTypeMismatch&lt;/code&gt; error. What does that mean?&lt;br&gt;
Rails/ActiveRecord knows, that initiator should be a user, as we probably defined something like this:&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;# app/models/log.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:initiator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or, more likely, we even have just&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/some_object.rb
class SomeObject &amp;lt; ApplicationRecord
  belongs_to :user
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;, where the class is implicit.&lt;/p&gt;

&lt;p&gt;So, when we try to use instance of different class, ActiveRecord is trying to save us from our obvious mistake, and doesn't allow that.&lt;/p&gt;

&lt;p&gt;There was one way to sidestep it, which is ugly, but I will mention it nonetheless:&lt;/p&gt;

&lt;p&gt;If we wrote it like this:&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;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;initiator_id: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;, it will work.&lt;/p&gt;

&lt;p&gt;But don't that, and try to fool ActiveRecord instead!&lt;/p&gt;

&lt;p&gt;This was probably most difficult part of all this where I was properly frustrated and claimed defeat multiple times.&lt;br&gt;
In the end (as it often is), it was not so difficult when I (with help with my team leader and many people on the internet) realized two things I was missing:&lt;br&gt;
1) delegation&lt;br&gt;
I had delegation set by &lt;code&gt;delegate_missing_to&lt;/code&gt;, but I didn't realize this only does instance methods delegation, and I also wanted to delegate Class methods, to behave like the User in all ways. So many class methods &lt;br&gt;
Once I understood, solution was very simple:&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;CurrentUser&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="n"&gt;delegate_missing_to&lt;/span&gt; &lt;span class="ss"&gt;:User&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="o"&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;2) pretense&lt;br&gt;
This solved most problems but one - I still needed to convince ActiveRecord that yes, this really is a User object, wink wink just trust me.&lt;br&gt;
For this I had to do something bit nasty:&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;CurrentUser&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:is_a?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# This prevents ActiveRecord::AssociationTypeMismatch&lt;/span&gt;

  &lt;span class="o"&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;This didn't happen automatically with &lt;code&gt;delegate_missing_to&lt;/code&gt;, as it was not missing - it was defined by inheriting from &lt;code&gt;Object&lt;/code&gt; class.&lt;br&gt;
When we do this, CurrentUser is really not easily recognizable from User. It has some drawbacks - we cannot use &lt;code&gt;is_a?&lt;/code&gt;, as it will not tell the truth (but there are not many valid uses of that anyway).&lt;br&gt;
Now we can use CurrentUser object instead of a User object all around the place, and ActiveRecord won't be any wiser.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Summary:&lt;br&gt;
I learned a bit more about ActiveRecord magic, and how to bend it to my will.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  comparing to other objects
&lt;/h2&gt;

&lt;p&gt;Another thing that we often do with our &lt;code&gt;current_user&lt;/code&gt; is to compare it with other objects, like this:&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="n"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;creator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, even though &lt;code&gt;current_user&lt;/code&gt; behaves as a user in many ways, this will not be true:&lt;br&gt;
&lt;code&gt;&amp;lt;CurrentUser...&amp;gt; is not equal to &amp;lt;User...&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To solve this, we will again delegate:&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;CurrentUser&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:==&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :user&lt;/span&gt; &lt;span class="c1"&gt;# This allows us to compare to proper User objects&lt;/span&gt;

  &lt;span class="o"&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;And voila, they are equal.&lt;br&gt;
What happens when we change the order - &lt;code&gt;post.creator == current_user&lt;/code&gt;?&lt;br&gt;
You guessed it, again we get false. I made it work this way, but I'm not super happy about 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;==&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# This is here to be able to compare to CurrentUser decorator&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&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;Yeah, not great. First thing, it will make every comparison to user bit slower.&lt;br&gt;
Another is, User now knows too much - it expect there to be some object or objects that pretend to be of User class, and respond to &lt;code&gt;user&lt;/code&gt; method.&lt;br&gt;
This might bring us grief later, so I'm still looking for better way. If you have any ideas, let me know please!&lt;/p&gt;

&lt;p&gt;This solved some other failing tests, and showed another problem, and another way Rails use objects.&lt;/p&gt;
&lt;h2&gt;
  
  
  using with ActiveJob
&lt;/h2&gt;

&lt;p&gt;Next error I got from my tests was Serialization Error for ActiveJob.&lt;br&gt;
What is that? Well, when I want to create ActiveJob, using current_user as an argument, Rails needs to send identifier of the object to ActiveJob, which is then able to pick the object up correctly.&lt;/p&gt;

&lt;p&gt;For that, I had to add custom ActiveJob serializer:&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;# app/serializers/current_user_serializer.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUserSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ObjectSerializer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serialize?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:current_user_decorator?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user_decorator?&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;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;def&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;CurrentUser&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="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this is one case when we really need to recognize if the object is CurrentUser (and, as you remember from previous part, we lost the ability to use &lt;code&gt;is_a?&lt;/code&gt;), so we added &lt;code&gt;current_user_decorator?&lt;/code&gt; method to it. It's a bit suspicious code, having to check it this way, but I didn't yet find any better. &lt;/p&gt;

&lt;p&gt;After loading this into app (as explained in &lt;a href="https://guides.rubyonrails.org/active_job_basics.html#serializers" rel="noopener noreferrer"&gt;Rails guide&lt;/a&gt;), ActiveJob knows how to work with CurrentUser.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - url helpers
&lt;/h2&gt;

&lt;p&gt;Last problem I encountered with decorator was with url helpers.&lt;br&gt;
When you pass ActiveRecord objects to url_helpers, it is able to make urls from them:&lt;br&gt;
&lt;code&gt;user_path(user) =&amp;gt; /user/123&lt;/code&gt;&lt;br&gt;
But when I pass random object, it goes terribly wrong:&lt;br&gt;
&lt;code&gt;user_path(current_user) =&amp;gt; /user/&amp;lt;CurrentUser...&amp;gt;&lt;/code&gt;.&lt;br&gt;
We could once again go around this problem with explicitly using &lt;code&gt;id&lt;/code&gt; (&lt;code&gt;user_path(current_user.id)&lt;/code&gt; generates correct url), but we don't want to lose this flexibility Rails provides us with. &lt;/p&gt;

&lt;p&gt;Luckily, solutions is extremely simple here - url helpers use &lt;code&gt;to_param&lt;/code&gt; method to do this neat trick with converting object to id.&lt;br&gt;
So we just need to define or delegate &lt;code&gt;to_param&lt;/code&gt; on our CurrentUser object:&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;# either&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:to_param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, I got to passing test suite, which was a nice sight (as I started with hundreds of failing tests). Maybe we will find some other troubles (I'm thinking about ActionMail).&lt;/p&gt;

&lt;p&gt;So, we can be almost happy with what our current CurrentUser does.&lt;br&gt;
Almost.&lt;/p&gt;
&lt;h2&gt;
  
  
  stacking dolls situation
&lt;/h2&gt;

&lt;p&gt;There are multiple places where we can change the current_user with code like &lt;code&gt;current_user = Current.user(user)&lt;/code&gt;. But, as we started to pass CurrentUser as a User all around our app, we are not sure (and we did a lot of work not to care about it) whether user is User or CurrentUser.&lt;br&gt;
So, what happens when we create new CurrentUser, but pass another CurrentUser as a user?&lt;br&gt;
We get a stacking doll situation - we can have current_user, which has CurrentUser as a user, which again has CurrentUser as a user, which...&lt;br&gt;
Maybe it's not that much of a problem with delegation, but it sure is not great, so let's get rid of that.&lt;/p&gt;

&lt;p&gt;Let's look at our CurrentUser initiation:&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;# app/models/current_user.rb, simplified&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUser&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;current_user_decorator?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&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 think for a moment what we can expect to get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nil&lt;/code&gt;, when user is anonymous&lt;/li&gt;
&lt;li&gt;instance of &lt;code&gt;User&lt;/code&gt;, when user is known&lt;/li&gt;
&lt;li&gt;instance of &lt;code&gt;CurrentUser&lt;/code&gt;, when passing current_user around&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this understanding, we can change our code to something like this:&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;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&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;nil?&lt;/span&gt;
            &lt;span class="kp"&gt;nil&lt;/span&gt;
          &lt;span class="k"&gt;elsif&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;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:current_user_decorator?&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&lt;/span&gt; &lt;span class="c1"&gt;# This prevents stacked CurrentUser objects&lt;/span&gt;
          &lt;span class="k"&gt;elsif&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;is_a?&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="n"&gt;user&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Only nil, or instance of User or CurrentUser can be passed"&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;We solved two things here - we "unstack" current_user, and we validate the input (so we cannot pass other objects to it by mistake).&lt;br&gt;
Again we used our &lt;code&gt;current_user_decorator?&lt;/code&gt; method to recognize instances of CurrentUser.&lt;/p&gt;

&lt;p&gt;All right, that's enough for now. This was really long (maybe I should split this to two posts), and we did a lot of work.&lt;/p&gt;

&lt;p&gt;There are still some changes and improvements in our pipeline, but those will wait.&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Managing current user in Rails, our way - part 2</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Thu, 06 Mar 2025 13:07:37 +0000</pubDate>
      <link>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-part-2-2if0</link>
      <guid>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-part-2-2if0</guid>
      <description>&lt;h2&gt;
  
  
  let's be current with Current attributes
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-3802"&gt;first post&lt;/a&gt; of this series, I tried to show state of our code, and why we weren't happy with it.&lt;/p&gt;

&lt;p&gt;In this part, I will start with the easiest change - introduction CurrentAttributes to our app.&lt;/p&gt;




&lt;p&gt;After getting utterly confused and lost in the code I tried to outline for you, the first, mostly simple part, was getting rid of &lt;code&gt;Thread.current&lt;/code&gt; and using modern &lt;code&gt;Current&lt;/code&gt; (attributes API)[&lt;a href="https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html" rel="noopener noreferrer"&gt;https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html&lt;/a&gt;].&lt;br&gt;
If you have never heard of it, this explains it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  Current Attributes
&lt;/h3&gt;

&lt;p&gt;Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request. This allows you to keep all the per-request attributes easily available to the whole system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, I looked for our usage of Thread (and our CurrentThread "wrapper"), and mostly just replaced it with the same using &lt;code&gt;Current&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Easy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sidenote: I will use &lt;code&gt;current_user&lt;/code&gt; in remaining code, as we do in our app now. However, if your app uses &lt;code&gt;Current.user&lt;/code&gt; instead (as pure Rails 8+ apps might), it is approximately the same (not really, in that Current is global). &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was easy and short. Not so much for our next part!&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Managing current user in Rails, our way - part 1</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Thu, 06 Mar 2025 13:02:58 +0000</pubDate>
      <link>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-3802</link>
      <guid>https://dev.to/janmpeterka/managing-current-user-in-rails-our-way-3802</guid>
      <description>&lt;p&gt;I'm working on ~15 years old Rails application at my $DAYJOB and sometimes (loads of times, actually) there is some quirky code that we are not really happy with these days.&lt;/p&gt;

&lt;p&gt;One example which I stumbled across years ago and tried to make sense of (and clean a bit) multiple times, mostly unsuccessfully, was how we manage current user.&lt;/p&gt;

&lt;p&gt;You see, in modern Rails apps, this is usually either managed by gems like &lt;code&gt;devise&lt;/code&gt;, or using &lt;code&gt;Current&lt;/code&gt; attributes, possibly with code from authentication generator introduced in Rails 8.&lt;/p&gt;

&lt;p&gt;Not us. We have our custom code that contains some of the oldest code in our project, and which (considering it's authentication) I am pretty afraid to touch.&lt;/p&gt;

&lt;p&gt;However, we are slightly getting to somewhat better version of our code, and in this post, I will try to describe our original code, why we wanted to change it and how we did it (or failed to do).&lt;/p&gt;

&lt;p&gt;Let's begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 0 - let's paint a scene
&lt;/h2&gt;

&lt;p&gt;Core of our current user logic are two methods, defined on &lt;code&gt;ApplicationController&lt;/code&gt; - &lt;code&gt;current_user&lt;/code&gt; and &lt;code&gt;current_user=&lt;/code&gt;.&lt;br&gt;
That's not that crazy - if you have experience with &lt;code&gt;devise&lt;/code&gt; or similar, that's pretty common thing. Yes, maybe you have &lt;code&gt;log_in&lt;/code&gt; instead of &lt;code&gt;current_user=&lt;/code&gt;, but well, it's pretty ok.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;current_user&lt;/code&gt; can have multiple different types:&lt;br&gt;
On the beginning of request (or before first calling &lt;code&gt;current_user&lt;/code&gt; or &lt;code&gt;current_user=&lt;/code&gt;), it was &lt;code&gt;nil&lt;/code&gt;.&lt;br&gt;
Then, it could be set to two values:&lt;br&gt;
1) instance of &lt;code&gt;User&lt;/code&gt;&lt;br&gt;
2) value &lt;code&gt;:false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Wait, what? &lt;code&gt;:false&lt;/code&gt;? Yeah, weird.&lt;br&gt;
It was the way how to say "we know it's anonymous user", in opposite of &lt;code&gt;nil&lt;/code&gt; representing "we just have no idea yet.&lt;br&gt;
So even though te implementation was weird, the general approach of three possible values ("we don't know", "we know they are unknown", "we can link them to our users") made sense and was a starting point for the following changes.&lt;/p&gt;

&lt;p&gt;Last thing was that we used was &lt;code&gt;Thread.current&lt;/code&gt; (and around it our PORO CurrentThread) to be able to access current user not only from controllers and templates, but also, sparingly, from models or background job.&lt;/p&gt;

&lt;p&gt;We had some suspicion it leaks and it brought us some overall doubt about this part of code.&lt;/p&gt;

&lt;p&gt;So, after a lot of tinkering, thinking, head scratching and writing and deleting of code, we were able to make some improvements, discover interesting patterns and look into many things Rails does "magically" for us, until it doesn't.&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Deploying Rails app with Kamal - why is my image so big?</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Thu, 14 Mar 2024 11:56:25 +0000</pubDate>
      <link>https://dev.to/janmpeterka/deploying-rails-app-with-kamal-why-is-my-image-so-big-4eo8</link>
      <guid>https://dev.to/janmpeterka/deploying-rails-app-with-kamal-why-is-my-image-so-big-4eo8</guid>
      <description>&lt;p&gt;I'm currently in process of rewriting app from Python/Flask to Ruby/Rails, and one of changes is in deployment process.&lt;/p&gt;

&lt;p&gt;I decided to use containers and &lt;a href="https://kamal-deploy.org/" rel="noopener noreferrer"&gt;Kamal&lt;/a&gt;, as it's upcoming default for Rails.&lt;br&gt;
Mind you, I have no prior experience with neither Docker nor deployment tools like Capistrano.&lt;/p&gt;

&lt;p&gt;There are many things that I got stuck on (and I plan to write about them maybe later), but today I was trying to solve one problem - images Kamal generated were about 3GB.&lt;br&gt;
Whoa, that's a lot! It takes a long time to build, to upload to registry, to download to server. It's huge waste of resources!&lt;/p&gt;

&lt;p&gt;I had to dig into it, and here's what I learned:&lt;/p&gt;

&lt;p&gt;To find out how big my images really are (and compare before/after some changes), there's &lt;code&gt;docker images&lt;/code&gt;. It lists all my images and their sizes.&lt;/p&gt;

&lt;p&gt;But whats inside?&lt;br&gt;
I found out about this handy command to show me what's contained inside the image:&lt;br&gt;
&lt;code&gt;docker history registry.example/example-all:latest&lt;/code&gt;&lt;br&gt;
which shows me something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
211b6b39d381   About a minute ago   CMD ["./bin/rails" "server"]                    0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      About a minute ago   EXPOSE map[3000/tcp:{}]                         0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      About a minute ago   ENTRYPOINT ["/rails/bin/docker-entrypoint"]     0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      About a minute ago   USER rails:rails                                0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      About a minute ago   RUN /bin/sh -c useradd rails --create-home -…   74.9MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      About a minute ago   COPY /rails /rails # buildkit                   2.7GB     buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      17 minutes ago       COPY /usr/local/bundle /usr/local/bundle # b…   148MB     buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      18 minutes ago       RUN /bin/sh -c apt-get update -qq &amp;amp;&amp;amp;     apt…   148MB     buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      18 minutes ago       ENV RAILS_ENV=production BUNDLE_DEPLOYMENT=1…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      19 minutes ago       WORKDIR /rails                                  0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          CMD ["irb"]                                     0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          RUN /bin/sh -c mkdir -p "$GEM_HOME" &amp;amp;&amp;amp; chmod…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV PATH=/usr/local/bundle/bin:/usr/local/sb…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV BUNDLE_SILENCE_ROOT_WARNING=1 BUNDLE_APP…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV GEM_HOME=/usr/local/bundle                  0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          RUN /bin/sh -c set -eux;   savedAptMark="$(a…   58.1MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV RUBY_DOWNLOAD_SHA256=cfb231954b8c241043a…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV RUBY_DOWNLOAD_URL=https://cache.ruby-lan…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV RUBY_VERSION=3.2.3                          0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          ENV LANG=C.UTF-8                                0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          RUN /bin/sh -c set -eux;  mkdir -p /usr/loca…   45B       buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          RUN /bin/sh -c set -eux;  apt-get update;  a…   46.5MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 weeks ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B        
&amp;lt;missing&amp;gt;      8 weeks ago          /bin/sh -c #(nop) ADD file:b86ae1c7ca3586d8f…   74.8MB    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I saw that my problem lies in &lt;code&gt;COPY rails/ rails/&lt;/code&gt;, meaning my app directory is huge.&lt;/p&gt;

&lt;p&gt;Why's that? Here's another handy command to see that:&lt;br&gt;
&lt;code&gt;du --max-depth 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This shows me size of directories inside my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8   ./.bundle
108 ./test
824388  ./.ruby-lsp
176 ./config
554380  ./vendor
56  ./lib
108 ./db
125260  ./tmp
28  ./bin
3034272 ./old
24  ./.kamal
39508   ./.git
8   ./.vscode
2300452 ./storage
232 ./spec
8216    ./app
20  ./public
427176  ./log
4614096 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thing is, I started deploying from my laptop, so I have all this files that are not present when deploying from CI/CD (as would be optimal way).&lt;/p&gt;

&lt;p&gt;So, we have &lt;code&gt;/storage&lt;/code&gt; (with all files), &lt;code&gt;/.ruby-lsp&lt;/code&gt; (from editor), &lt;code&gt;/old&lt;/code&gt; (thats just some migration data), and &lt;code&gt;/vendor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I don't want these in my image!&lt;br&gt;
Let's not include them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .dockerignore

old/
*.log

vendor/
tmp/
.ruby-lsp/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After another build, I check my image again, and I got to much nicer ~600MB image.&lt;/p&gt;

&lt;p&gt;There might be still some room for improvement, but I'm quite happy with this - my build time is down significantly, my self-hosted registry is not full of useless data, and I learned something new.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>kamal</category>
      <category>devops</category>
    </item>
    <item>
      <title>How I migrated from Flask-Security(-Too) to Devise</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Thu, 07 Dec 2023 11:07:49 +0000</pubDate>
      <link>https://dev.to/janmpeterka/how-i-migrated-from-flask-security-too-to-devise-44do</link>
      <guid>https://dev.to/janmpeterka/how-i-migrated-from-flask-security-too-to-devise-44do</guid>
      <description>&lt;p&gt;Recently I started a rewrite of one of my smallish projects (in terms of complexity, but with ~200 users, so I must handle that with care) from Flask to Ruby on Rails.&lt;br&gt;
Why? I'm planning on writing another article about that, so I don't want to go into details here, but in short, I believe it will enable me to write the app both faster and better. But there are some hurdles to overcome to get there.&lt;/p&gt;

&lt;p&gt;For some time, I ignored the authentication part of the app. So today, I made my first attempt at making that work.&lt;/p&gt;

&lt;p&gt;In Flask, I used the &lt;code&gt;Flask-Security-Too&lt;/code&gt; package from the start. I never tried to implement any authentication logic myself (as you probably never should for, you know, security reasons).&lt;br&gt;
I knew that in Rails, I could use the &lt;code&gt;devise&lt;/code&gt; gem.&lt;br&gt;
But it means jumping from one black box into another. I knew that it might bring some difficulties.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: I'm no expert on security by any means! I understand really little about this, just enough to make it work. There might be some security concerns I'm not aware of. Read this just as a story, not a tutorial!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There was some little work involved in correctly adding columns to the database to support &lt;code&gt;devise&lt;/code&gt; (as some column names are the same in both libraries and some are not), but not that much. The most annoying is that I had a &lt;code&gt;password&lt;/code&gt; column, and that caused some problems with Rails/devise. But after some tries with migrations, I got sorted that out.&lt;/p&gt;

&lt;p&gt;The main problem, as expected, was being able to validate the same hashes in Rails that &lt;code&gt;Flask-Security&lt;/code&gt; created automatically for me.&lt;/p&gt;
&lt;h1&gt;
  
  
  The Problem - Password hashes
&lt;/h1&gt;

&lt;p&gt;Short aside if this is new for you: what are password hashes?&lt;br&gt;
It would be very unwise to save passwords to the database as-is. I certainly don't want to have that responsibility on me.&lt;br&gt;
So instead password hashes are usually stored in the database, and on login attempt hash is calculated from the provided password and compared to the saved one.&lt;br&gt;
So the app knows the same password was provided as when the user registered, but is none the wiser about the actual value.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When I looked into my database, I had hashes like &lt;code&gt;$2a$12$etyAllfaDgIDC61ZUS9UB.peoAIYpZborbU4T/6c8SejvrIhbIL46&lt;/code&gt; there.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, back on track. The trouble is that there are many different ways to hash passwords.&lt;br&gt;
You can use different algorithms (sha256, bcrypt, argon,..), they can have different configurations, there may be some salt or pepper involved,..&lt;/p&gt;

&lt;p&gt;There's basically NO CHANCE that a different library (&lt;code&gt;devise&lt;/code&gt; in my case) will hash, and therefore validate, passwords the same way as some other (&lt;code&gt;flask-security&lt;/code&gt; for me).&lt;/p&gt;

&lt;p&gt;So it was no surprise that after adding &lt;code&gt;devise&lt;/code&gt; to my project, logging in with my credentials wasn't working.&lt;/p&gt;

&lt;p&gt;The easy way out for me would be the following - just toss out all passwords and send mail to my users that they need to use the &lt;code&gt;Forgotten password&lt;/code&gt; feature. Well, not gonna happen, thank you very much.&lt;/p&gt;

&lt;p&gt;So I needed to find out way to migrate successfully.&lt;/p&gt;

&lt;p&gt;There were two parts to the solution:&lt;br&gt;
1) Slightly changing mechanism for Devise-provided password-validation to validate using its built-in mechanism first, and if that fails, try our own validator, simulating how &lt;code&gt;Flask-Security&lt;/code&gt; does validation.&lt;br&gt;
2) Writing the validator&lt;/p&gt;
&lt;h2&gt;
  
  
  Allowing both Devise and Flask-Security-like validation
&lt;/h2&gt;

&lt;p&gt;The first part is quite straightforward:&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;# app/models/user.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# Include default devise modules. Others available are:&lt;/span&gt;
  &lt;span class="c1"&gt;# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable&lt;/span&gt;
  &lt;span class="n"&gt;devise&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="ss"&gt;:recoverable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validatable&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_password?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Encryptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encrypted_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;flask_security_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="kp"&gt;false&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="o"&gt;...&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;It's probably pretty self-explanatory, I'm just overriding the default &lt;code&gt;valid_password?&lt;/code&gt; method Devise is providing, adding another way to validate the password. Nice, let's move into the second part - how will &lt;code&gt;flask_security_compare&lt;/code&gt; work?&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulating Flask-Security hashing
&lt;/h2&gt;

&lt;p&gt;Now this was a bit more difficult (took me a few hours to solve).&lt;/p&gt;

&lt;p&gt;First, I needed to understand what Flask-Security is doing when hashing passwords.&lt;/p&gt;

&lt;p&gt;Luckily, I was already using &lt;code&gt;bcrypt&lt;/code&gt; as my hashing algorithm (as it's the default for &lt;code&gt;Flask-Security&lt;/code&gt;), the same as Devise uses.&lt;br&gt;
I needed to check that it is configured the same way, mainly the number of stretches (technique making hashing slower, thus making brute-force attacks more difficult).&lt;/p&gt;

&lt;p&gt;Sidenote:&lt;br&gt;
Remember the hash I showed you before? Here it is again &lt;code&gt;$2a$12$etyAllfaDgIDC61ZUS9UB.peoAIYpZborbU4T/6c8SejvrIhbIL46&lt;/code&gt;._&lt;br&gt;
&lt;code&gt;$2a&lt;/code&gt; tells us it's bcrypt using 2a variation&lt;br&gt;
&lt;code&gt;$12&lt;/code&gt; is the number of stretches&lt;/p&gt;

&lt;p&gt;Now for a tricky part.&lt;/p&gt;

&lt;p&gt;When the password is hashed using &lt;code&gt;bcrypt&lt;/code&gt;, there's &lt;code&gt;salt&lt;/code&gt; provided. That's a way to make pre-calculating hashes nearly impossible (preventing usage of the infamous rainbow tables).&lt;/p&gt;

&lt;p&gt;Where does this salt come from?&lt;/p&gt;

&lt;p&gt;It's generated randomly.&lt;/p&gt;

&lt;p&gt;For a moment I was afraid that it meant I would be unable to simulate this - how can I recreate something random? Of course, that's not the case - bcrypt needs to keep this value around. And it does so directly in the hash string:&lt;/p&gt;

&lt;p&gt;in &lt;code&gt;$2a$12$etyAllfaDgIDC61ZUS9UB.peoAIYpZborbU4T/6c8SejvrIhbIL46&lt;/code&gt;, &lt;code&gt;etyAllfaDgIDC61ZUS9UB.&lt;/code&gt; is the salt (22 characters after the initial part).&lt;br&gt;
Only the rest (&lt;code&gt;peoAIYpZborbU4T/6c8SejvrIhbIL46&lt;/code&gt;) is the actual hash!&lt;/p&gt;

&lt;p&gt;Nice! And even nicer - I don't need to parse the value manually, I can use the library for that (as suggested by Copilot):&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="n"&gt;legacy_password_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"$2a$12$etyAllfaDgIDC61ZUS9UB.peoAIYpZborbU4T/6c8SejvrIhbIL46"&lt;/span&gt;
&lt;span class="n"&gt;bcrypt_password_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BCrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Password&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="n"&gt;legacy_password_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;bcrypt_password_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;salt&lt;/span&gt;
&lt;span class="c1"&gt;# $2a$12$etyAllfaDgIDC61ZUS9UB.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, we have a salt, let's hash some passwords!&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="n"&gt;hashed_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BCrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bcrypt_password_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, I did get a result, but it was different than what I had in the database...&lt;/p&gt;

&lt;p&gt;The reason is that &lt;code&gt;Flask-Security&lt;/code&gt; uses one more security enhancement.&lt;/p&gt;

&lt;p&gt;It doesn't use the password directly for hashing but encodes it to HMAC using a server-wide secret key (set as &lt;code&gt;SECURITY_PASSWORD_SALT&lt;/code&gt; env variable).&lt;/p&gt;

&lt;p&gt;I had to look into &lt;code&gt;Flask-Security&lt;/code&gt; &lt;a href="https://github.com/Flask-Middleware/flask-security/blob/1c8062dd6acda6e1e8928df8a991061a705a8d17/flask_security/utils.py#L307" rel="noopener noreferrer"&gt;source code&lt;/a&gt; to find how exactly it's done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns a Base64 encoded HMAC+SHA512 of the password signed with
    the salt specified by *SECURITY_PASSWORD_SALT*.

    :param password: The password to sign
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASSWORD_SALT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The configuration value `SECURITY_PASSWORD_SALT` must &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not be None when the value of `SECURITY_PASSWORD_HASH` is &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;set to &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;config_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASSWORD_HASH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&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="nf"&gt;encode_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;encode_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then recreated the algorithm in ruby:&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;get_hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&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;'openssl'&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'base64'&lt;/span&gt;

    &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'LEGACY_SECURITY_SALT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"The configuration value `LEGACY_SECURITY_SALT` must not be None when the value of `SECURITY_PASSWORD_HASH` is set to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SECURITY_PASSWORD_HASH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Digest&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="s1"&gt;'sha512'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hmac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HMAC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hmac&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;and used that to write the &lt;code&gt;flask_security_compare&lt;/code&gt; function I need:&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;flask_security_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bcrypt_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BCrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Password&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="n"&gt;legacy_password_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;hmaced_password&lt;/span&gt; &lt;span class="o"&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;get_hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hashed_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BCrypt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hmaced_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bcrypt_password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secure_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encrypted_password&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;And it still didn't work.&lt;br&gt;
I was a little desperate by then, but after some debugging and comparing of strings I found out small problem:&lt;br&gt;
The ruby implementation sometimes added &lt;code&gt;\n&lt;/code&gt; symbols into the &lt;code&gt;hmaced_password&lt;/code&gt;.&lt;br&gt;
Fast fix is this: &lt;code&gt;Base64.encode64(hmac).chomp.gsub(/\n/, '')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now I'm just curious if this works with every password there is in my database. Let's hope.&lt;br&gt;
And maybe show a banner to users warning them that it might happen.&lt;/p&gt;

&lt;p&gt;Oh, I forgot one more improvement - if the user logs in using the "old mechanism", it can rehash the password to update the hash. This way, I can remove the &lt;code&gt;flask&lt;/code&gt; branch later (like in a year or so). I already did something similar when I was migrating from SHA256 to the bcrypt algorithm in my older project.&lt;/p&gt;

&lt;p&gt;Now I'm done.&lt;/p&gt;

&lt;p&gt;So, I learned a bit about how password hashing works in &lt;code&gt;Flask-Security&lt;/code&gt;, which was interesting. And I'm really happy that I can continue with my rewrite to Rails, knowing that users will be able to log in using their old passwords.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Again, this is not a tutorial. Please, if you do some authentication stuff, be careful. And if you find any mistakes here, let me know in the comments!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Update:&lt;br&gt;
I realized I didn't address properly one potential downside to this. As password validation tries two way of hashing, it makes guessing password easier, as there are potentially two strings that produce the desired hash. That's not great, as it's lowering security of our authentication, but as we use otherwise safe way, it shouldn't do much damage (half the time for brute-forcing will still be a lot). We could migitage this by having &lt;code&gt;flask_password_hash&lt;/code&gt; and &lt;code&gt;password_hash&lt;/code&gt; as different fields, and only allowing one of them to be present.  &lt;/p&gt;

</description>
      <category>rails</category>
      <category>flask</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Writing Jinja as a proper Python developer</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Tue, 04 Oct 2022 09:08:02 +0000</pubDate>
      <link>https://dev.to/janmpeterka/writing-jinja-as-a-proper-python-developer-1c4i</link>
      <guid>https://dev.to/janmpeterka/writing-jinja-as-a-proper-python-developer-1c4i</guid>
      <description>&lt;h3&gt;
  
  
  ⚠️ disclaimer
&lt;/h3&gt;

&lt;p&gt;I have no idea what is the correct way to do things. I probably do some things really wrong, and I will be glad to learn that. Here I'm just sharing my experience and where I'm now in using Flask for multiple small-to-medium projects (all basically CRUD apps). Your experience may be different, and I will gladly hear it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✍️ writing Jinja as a proper Python developer
&lt;/h2&gt;

&lt;p&gt;You probably heard about the DRY principle. I'm not going into the heated discussion if that's something you should code by. Actually, let's forget there's the DRY principle at all. &lt;br&gt;
Let's just say that I'm a lazy person. &lt;br&gt;
So, the idea of writing code I don't have to multiple times is, well, not appealing to me.&lt;/p&gt;

&lt;p&gt;But it happens. Especially when the project is young and dynamic and you have no idea what code you will need, or how you will structure things.&lt;br&gt;
One place I extremely hate writing code over and over again is HTML. There are so many &lt;code&gt;&amp;lt;&lt;/code&gt;s and &lt;code&gt;&amp;gt;&lt;/code&gt;s and other things. And soo much that's &lt;em&gt;obvious&lt;/em&gt; and shouldn't be written.&lt;br&gt;
you know - if you are writing the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag &lt;strong&gt;of course&lt;/strong&gt; you will use href argument. Why is it even &lt;code&gt;kwarg&lt;/code&gt;? I would never be &lt;code&gt;kwarg&lt;/code&gt; if I was writing it in Python!&lt;br&gt;
And as I'm programming Flask CRUD app, most of the time &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; is pointing to some object show/edit page. Should I write&lt;br&gt;
&lt;code&gt;&amp;lt;a href="{{ url_for('UserView:show', id=user.id) }}&amp;gt; {{ user.name }} &amp;lt;/a&amp;gt;"&lt;/code&gt; every time? Absolutely not! &lt;br&gt;
As I'm exposed to Ruby on Rails in my work, I've seen &lt;code&gt;link_to&lt;/code&gt; in templates a lot. And I thought: that's really nice! That's what I should be doing, right? Just write &lt;code&gt;{{ link_to(user) }}&lt;/code&gt; in my template, when I want a really simple link. Why can't I do that? &lt;/p&gt;

&lt;p&gt;Let's look into what did I discover on my journey into great developer experience with Jinja templates. &lt;/p&gt;

&lt;p&gt;The question was - how can I create some "helpers" like this in Jinja?&lt;br&gt;
And the answer is... &lt;br&gt;
There are many ways depending on context. Sigh. &lt;br&gt;
Well, let's look into them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9imbvy2rb0j2ug0i6xv4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9imbvy2rb0j2ug0i6xv4.gif" alt="Show me what you got!" width="498" height="283"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  partials
&lt;/h3&gt;

&lt;p&gt;By partial, I mean a separate template that I &lt;code&gt;include&lt;/code&gt; in my "main" template. They are useful when there's some big part of a page (like a table of all users) that I use on multiple pages (index, show).&lt;br&gt;
I can give them some variables using &lt;code&gt;{% with users=... %}&lt;/code&gt;, but if I try adding some complex logic, it's not really great.&lt;/p&gt;
&lt;h3&gt;
  
  
  macros
&lt;/h3&gt;

&lt;p&gt;When you were reading this article, you probably thought "he's talking about macros, right?". Well, of course, macros are THE suggested way to handle a bit more generalized, repeated pieces of HTML. &lt;br&gt;
It's mostly the only advice you get on StackOverflow (&lt;a href="https://stackoverflow.com/questions/55841442/can-you-create-components-in-flask-jinja-to-insert-in-various-templates/73944514#73944514" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://stackoverflow.com/questions/15106741/parameterized-reusable-blocks-with-jinja2-flask-templating-engine" rel="noopener noreferrer"&gt;here&lt;/a&gt; just two examples). And macros are great! I used them happily for some time - having macro for rendering form fields, icons, and other repeated elements. I had macros that use other macros! But... &lt;br&gt;
Jinja logic is somewhat.. ..more verbose than I'm used to from Python. Not a great code. And getting some more complex logic in, it's not really what you want.&lt;br&gt;
Another thing with macros is that you have to import them into your templates. Normally it's just a matter of adding everything to &lt;code&gt;base.html.j2&lt;/code&gt; template, but as I use Turbo (another article on that), sometimes I render just a partial, so I need to import explicitly there too. I know that &lt;a href="https://peps.python.org/pep-0020/" rel="noopener noreferrer"&gt;Zen of Python&lt;/a&gt; says "Explicit is better than implicit.", but in this case, I don't know. Having &lt;code&gt;link_to&lt;/code&gt; accessible globally seems like a sensible thing to me (and another zen says "Although practicality beats purity.").&lt;/p&gt;

&lt;p&gt;So, how do we do that? &lt;br&gt;
We will have to come back from the dark, messy realms of HTML to the sweet embrace of Python.&lt;/p&gt;
&lt;h3&gt;
  
  
  helper functions
&lt;/h3&gt;

&lt;p&gt;I did know that I can register custom Jinja filters and test (you know, because I read Jinja API &lt;a href="https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; more times than I would like to).&lt;br&gt;
But for a long time one great method was hiding from me: &lt;code&gt;application.add_template_global()&lt;/code&gt;.&lt;br&gt;
What is it? Simple, just a method to register custom functions to use in Jinja. They are used in code like macro, but they are your normal Python functions. &lt;br&gt;
Nice, let's use that!&lt;/p&gt;

&lt;p&gt;I created a new directory: &lt;code&gt;app/components&lt;/code&gt; and there created this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/links.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Markup&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;link_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="c1"&gt;# (do some logic)
&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_html_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then, to make things nice, I added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/__init__.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.links&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_all_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_template_global&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and finally, in my &lt;code&gt;app/__init__.py&lt;/code&gt; (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/__init__.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
 &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;(...)&lt;/span&gt;
 &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.components&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_all_components&lt;/span&gt;
 &lt;span class="nf"&gt;register_all_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can use &lt;code&gt;{{ link_to(something) }}&lt;/code&gt; anywhere in my templates. Smooth!&lt;/p&gt;

&lt;p&gt;You can probably guess what &lt;code&gt;some logic&lt;/code&gt; in &lt;code&gt;link_to&lt;/code&gt; does, and I don't want to include full code here as it's but sprawled, but basic functionality goes like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can pass either object or string to &lt;code&gt;link_to&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if it's a string

&lt;ul&gt;
&lt;li&gt;it either includes &lt;code&gt;:&lt;/code&gt;, so it's recognized as Flask-Classfull reference to route (eg. "UserView:index"), so it's converted to &lt;code&gt;url_for(something)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;or it's just pure URL, then I just pass it as it is&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;or it is object

&lt;ul&gt;
&lt;li&gt;then I get generated route - so, from &lt;code&gt;user&lt;/code&gt; object I can expect &lt;code&gt;UserView&lt;/code&gt;, add &lt;code&gt;:show&lt;/code&gt; and &lt;code&gt;id=user.id&lt;/code&gt;.
This logic is handled by UserPresenter which gives us &lt;code&gt;user.path_to_show&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I was happy with this. But I wasn't happy for long. Because soon I got repeating code again, and it was just not beautiful enough. So my journey to perfect developer experience continued and I found...&lt;/p&gt;

&lt;h3&gt;
  
  
  kittens!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rp1rmrjrewpzqvyq6ac.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rp1rmrjrewpzqvyq6ac.gif" alt="Kitten" width="498" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, sadly kittens are of no direct help with HTML. They are way too cute for that.&lt;/p&gt;

&lt;p&gt;We need to continue our search...&lt;/p&gt;

&lt;h3&gt;
  
  
  TemplateComponents
&lt;/h3&gt;

&lt;p&gt;Have you never heard of TemplateComponents? That's understandable, they don't exist. &lt;br&gt;
Inspiration for this pattern comes from Ruby on Rails, and specifically, gem(package) created and used by Github - &lt;a href="https://viewcomponent.org" rel="noopener noreferrer"&gt;ViewComponent&lt;/a&gt;.&lt;br&gt;
As &lt;code&gt;views&lt;/code&gt; in Rails and Django are &lt;code&gt;templates&lt;/code&gt; in Flask (I don't know why I would be happy with MVC instead of MTV), let's call it TemplateComponents!&lt;/p&gt;

&lt;p&gt;The idea is to move the core of these reusable pieces of HTML into Python objects, getting all the benefits of OOP.&lt;/p&gt;

&lt;p&gt;How would that look?&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;p&gt;1️⃣ Components are classes, in my case located in &lt;code&gt;app/components/&lt;/code&gt;. &lt;br&gt;
2️⃣ Component itself does some logic and then renders jinja template (with minimum logic from &lt;code&gt;app/templates/components/&lt;/code&gt; &lt;br&gt;
3️⃣ They are used by helper functions (defined in the same place), these helpers are registered by flask to be globally accessible, eg.(&lt;code&gt;application.add_template_global(icon)&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Here's an example how it can be set up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/__init__.py
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
 &lt;span class="p"&gt;(...)&lt;/span&gt;
 &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.components&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_all_components&lt;/span&gt;

 &lt;span class="nf"&gt;register_all_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing new here...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/__init__.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.links&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link_to_edit&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_all_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_template_global&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_template_global&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_to_edit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, we know this part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/links.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.components&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt;


&lt;span class="c1"&gt;# core
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
 &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="c1"&gt;# helpers
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;link_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path_to_show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_value_link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;link_to_edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.components&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;
 &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path_to_edit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;edit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, here's something new. But Link itself doesn't have a lot of logic, so how..&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/base.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Markup&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;
 &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

 &lt;span class="nd"&gt;@property&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;folder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;components/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.html.j2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

 &lt;span class="nd"&gt;@property&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="c1"&gt;# All public variables of the view are passed to the template
&lt;/span&gt; &lt;span class="n"&gt;class_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;
 &lt;span class="n"&gt;view_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;
 &lt;span class="n"&gt;all_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;class_attributes&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;view_attributes&lt;/span&gt;
 &lt;span class="n"&gt;public_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all_attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_attributes&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;# kwargs has higher priority, therefore rewrites public attributes
&lt;/span&gt; &lt;span class="n"&gt;merged_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;public_attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;merged_values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, right! Because some logic will probably be used in any component, so let's move that to Component class!&lt;/p&gt;

&lt;p&gt;And here's a template it renders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# app/templates/components/links/link.html.j2

&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
 &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
 &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;class&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;class&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mind you, this is the first version of my Component system. I haven't even moved all my macros there, so there will be probably some changes and improvements. But the basic idea is here.&lt;/p&gt;

&lt;p&gt;For now, I'm not pushing component classes to jinja context - so I cannot do &lt;code&gt;{{ Link(something) }}&lt;/code&gt;.&lt;br&gt;
It would be possible, but for now, it makes sense to me to use them through helper functions such as &lt;code&gt;link_to&lt;/code&gt; and &lt;code&gt;link_to_edit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why am I happy (for now) with this approach?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can handle complex logic easily (as I'm using Python, not Jinja)&lt;/li&gt;
&lt;li&gt;there's a clear way to split logic for

&lt;ul&gt;
&lt;li&gt;components in general (handled by Component class) - getting template name, rendering it&lt;/li&gt;
&lt;li&gt;kinds of components (handled by Link class for example) - possibly setting some defaults - style,...&lt;/li&gt;
&lt;li&gt;and specific helpers (link_to, link_to_edit) - setting specific values - path and defaults.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;it's easily testable!

&lt;ul&gt;
&lt;li&gt;I can test class or helper. not doing it now as I only have simple use cases, but in the future, it will make sense - so I can check parts of my HTML in a fast unit test instead of a complicated slow FE test.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;As always, I'm not saying this is the best approach. I was trying to show all the styles I used for this part of creating a web app. All may be useful, it depends on what you need. &lt;/p&gt;

&lt;p&gt;Question for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how do you handle your templates?&lt;/li&gt;
&lt;li&gt;how important is "Explicit is better than implicit" zen important to you?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to hear your ideas, experiences, and critique!&lt;/p&gt;

</description>
      <category>flask</category>
      <category>jinja</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Flask tutorials</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Tue, 04 Oct 2022 08:57:10 +0000</pubDate>
      <link>https://dev.to/janmpeterka/beyond-flask-tutorials-hcf</link>
      <guid>https://dev.to/janmpeterka/beyond-flask-tutorials-hcf</guid>
      <description>&lt;h3&gt;
  
  
  disclaimer
&lt;/h3&gt;

&lt;p&gt;I have no idea what is the correct way to do things. I probably do some things really wrong, and I will be glad to learn that. Here I'm just sharing my experience and where I'm now in using Flask for multiple small projects (all basically CRUD apps). Your experience may be different, and I will gladly hear it.&lt;/p&gt;

&lt;h3&gt;
  
  
  what's this about
&lt;/h3&gt;

&lt;p&gt;As many (I guess), I started to learn Flask with &lt;a href="https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world" rel="noopener noreferrer"&gt;Flask Megatutorial&lt;/a&gt; by Miguel Grinberg (who's for me a Flask guru). You can learn a lot from tutorials, but at some point you get to situations where you don't find a reference for how to do this a clean way, "Flask way". So, there are many things I tried and changed and changed again. It's not that the code is not working, but when you are thinking about how to keep your code sustainable, you are just trying to find a good way all the time.&lt;br&gt;
I want to show how I use Flask. How I think about structure, what tools and packages I use for my app.&lt;/p&gt;

&lt;p&gt;I will randomly add some articles as I'm handling different parts of my application/s. If I will see something to be worth sharing, I will try to do so.&lt;/p&gt;

&lt;p&gt;I hope for this to become place for me to structure my thoughts and experiences, for you to be inspired, and for all of us to exchange thoughts, discuss and learn.&lt;/p&gt;

&lt;p&gt;Let's start!&lt;/p&gt;

&lt;p&gt;Questions for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you use Flask? And have you comparison with other web frameworks?&lt;/li&gt;
&lt;li&gt;Is there something you are stuck on, something that doesn't just "feel right" in you Flask project?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flask</category>
      <category>jinja</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Gratitude journal - dev edition (29.3. - git)</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Sun, 29 Mar 2020 07:39:25 +0000</pubDate>
      <link>https://dev.to/janmpeterka/gratitude-journal-dev-edition-29-3-git-1000</link>
      <guid>https://dev.to/janmpeterka/gratitude-journal-dev-edition-29-3-git-1000</guid>
      <description>&lt;p&gt;So, how are you holding up?&lt;/p&gt;

&lt;p&gt;In pursuit of happiness in these difficult times, I will continue my gratitude journal.&lt;br&gt;
Last week I &lt;a href="https://dev.to/janmpeterka/gratitude-journal-dev-edition-23-3-stimulus-3969"&gt;wrote&lt;/a&gt; about Stimulus.js and why am I grateful for it.&lt;/p&gt;

&lt;p&gt;This week I will focus on something fundamental for most of us - &lt;strong&gt;git&lt;/strong&gt;.&lt;br&gt;
I am probably not only one who cannot imagine working without &lt;code&gt;git&lt;/code&gt; these days, but it wasn't always that way.&lt;/p&gt;

&lt;p&gt;When I started learning to code, I had no idea there is anything like version control systems. I just made new and new files with names like &lt;code&gt;code_2_0_new&lt;/code&gt; and so. You may know what I am talking about.&lt;/p&gt;

&lt;p&gt;I learned to use &lt;code&gt;git&lt;/code&gt; after that - or to be more precise, I mostly learned how to operate GitHub (which was the same for me at a time).&lt;/p&gt;

&lt;p&gt;So, I was very surprised when I was supposed to use TortoiseSVN in my first job. It was really weird, ugly and complicated. Soon, I happily returned to git.&lt;/p&gt;

&lt;p&gt;Since then, I am using git almost daily - at work (learning how to use it for collaboration) or in my hobby projects (adding things like pre-commit, integrating with deploys and so).&lt;br&gt;
I am no expert in git, I have a lot to learn, but even now, it helps me with my programming tremendously.&lt;/p&gt;

&lt;p&gt;So for today, I am grateful for git (and in extension, for all git software I used so far - &lt;a href="https://desktop.github.com" rel="noopener noreferrer"&gt;GitHub desktop&lt;/a&gt;, &lt;a href="https://www.gitkraken.com" rel="noopener noreferrer"&gt;GitKraken&lt;/a&gt; and my current &lt;a href="https://www.sublimemerge.com" rel="noopener noreferrer"&gt;Sublime Merge&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;What are you grateful for? :)&lt;/p&gt;

</description>
      <category>gratitude</category>
      <category>git</category>
    </item>
    <item>
      <title>Gratitude journal - dev edition (23.3 - Stimulus)</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Mon, 23 Mar 2020 13:30:15 +0000</pubDate>
      <link>https://dev.to/janmpeterka/gratitude-journal-dev-edition-23-3-stimulus-3969</link>
      <guid>https://dev.to/janmpeterka/gratitude-journal-dev-edition-23-3-stimulus-3969</guid>
      <description>&lt;p&gt;I started gratitude journaling a few months ago, and it feels nice.&lt;br&gt;
It feels good to stop for a moment, find something you are grateful for or makes you happy, especially in these precarious times.&lt;/p&gt;

&lt;p&gt;So, I decided to start another gratitude journal publically here, this one focused on technology, software and generally things I am grateful as a developer.&lt;/p&gt;

&lt;p&gt;I will try to make a post at least once a week.&lt;br&gt;
And for now...&lt;/p&gt;

&lt;p&gt;..this week I am grateful for &lt;strong&gt;Stimulus&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I guess many of you know about this &lt;a href="https://stimulusjs.org" rel="noopener noreferrer"&gt;little js framework&lt;/a&gt; from Basecamp.&lt;br&gt;
I have personally seen it first about six months ago thanks to my work at &lt;a href="https://dev.to/nejremeslnici"&gt;NejŘemeslníci.cz&lt;/a&gt;, where our front-end developers started using it some time ago.&lt;/p&gt;

&lt;p&gt;As I am not front-end developer myself (I prefer backend, mostly in Python and recently in Ruby on Rails), I only have some little knowledge in js, and when I needed to use some (e.g. for my web app ketocalc), I just wrote some terrible code, which "just worked" (but was terribly unreadable, unmaintainable and added terrible class-naming scheme to my html).&lt;/p&gt;

&lt;p&gt;Well, at work I learned some basics of stimulus and it seemed nice.&lt;br&gt;
So this weekend I decided to finally improve code in my project, so I stopped being ashamed of it whenever I worked on that project.&lt;/p&gt;

&lt;p&gt;I didn't manage to crack how to use neither &lt;code&gt;npm&lt;/code&gt; nor &lt;code&gt;webpack&lt;/code&gt;, luckily I can use stimulus without it (I know, I know, it's not optimal and I promise, I will get there someday).&lt;br&gt;
I simplified my code (both js and HTML) immensely, replaced AJAX with fetch, DRYed my code, and got rid of most of jQuery.&lt;br&gt;
I am so happy now.&lt;/p&gt;

&lt;p&gt;I know that most of it is not because of &lt;code&gt;stimulus&lt;/code&gt;. But it helped me with the process of writing nice, reusable code, and I love how it connects to HTML. I think it's really great js framework, especially for someone not very fond or skilled in frontend.&lt;/p&gt;

&lt;p&gt;So, that's for this weeks gratitude.&lt;br&gt;
Thanks for reading this, and let me know in comments what are you grateful for :)&lt;/p&gt;

</description>
      <category>gratitude</category>
      <category>stimulus</category>
    </item>
    <item>
      <title>Cookies and GDPR</title>
      <dc:creator>Jan Peterka</dc:creator>
      <pubDate>Fri, 14 Feb 2020 15:56:48 +0000</pubDate>
      <link>https://dev.to/janmpeterka/cookies-and-gdpr-1g1m</link>
      <guid>https://dev.to/janmpeterka/cookies-and-gdpr-1g1m</guid>
      <description>&lt;h3&gt;
  
  
  Recapitulation
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/janmpeterka/publishing-my-first-web-application-intro-4olf"&gt;first article&lt;/a&gt; I defined some base information and plans.&lt;/p&gt;

&lt;p&gt;In this article we will try to understand first part of legal requirements for publishing app - cookies and personal data.&lt;/p&gt;

&lt;p&gt;I'm not a lawyer, this article is my own grasp of which is needed. If you find any factical (or other) mistakes, please let me know in comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cookies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  General rules (EU applicable)
&lt;/h3&gt;

&lt;p&gt;You can find info on many sites (such as here or here), but in general, these are the rules:&lt;/p&gt;

&lt;p&gt;For most cookies uses, you need your users to opt-in - meaning give their consent to using cookies. It might be a checkbox in register form, or pop-up window they need to accept.&lt;/p&gt;

&lt;p&gt;You also have to list what kinds of cookies you use, and enable users to disable those not critical for using the app (necessary and functional cookies). &lt;/p&gt;

&lt;p&gt;Also, users need to have the option to revoke their consent later, and it should be obvious to them how to do that.&lt;/p&gt;

&lt;h4&gt;
  
  
  My app
&lt;/h4&gt;

&lt;p&gt;I use Flask-Sessions for session management, which is server-site. I only use cookies to save session and remember-token (as I checked in my browser). So, this cookie is by no means personal info.&lt;/p&gt;

&lt;p&gt;My usage of cookies qualifies them as necessary cookies. &lt;/p&gt;

&lt;p&gt;I don’t use any third-site services and cookies (such as Google Analytics).&lt;/p&gt;

&lt;h4&gt;
  
  
  That means the following:
&lt;/h4&gt;

&lt;p&gt;My cookies are exemption from the consent requirement. I have to inform users about using cookies, but I don’t need their consent.&lt;/p&gt;

&lt;h4&gt;
  
  
  My solutions
&lt;/h4&gt;

&lt;p&gt;As I only need a simple banner informing users about using cookies, I can make some myself, or use some generator. I used this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal data and GDPR
&lt;/h2&gt;

&lt;h3&gt;
  
  
  General definitions
&lt;/h3&gt;

&lt;p&gt;General Data Protection Regulation (GDPR) is an EU-wide set of rules for managing personal data.&lt;/p&gt;

&lt;p&gt;Here are some of the definitions we will need:&lt;/p&gt;

&lt;p&gt;According to the Regulation of 679/2016n:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;‘personal data’ means any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person;&lt;/p&gt;

&lt;p&gt;‘processing’ means any operation or set of operations which is performed on personal data or on sets of personal data, whether or not by automated means, such as collection, recording, organisation, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure or destruction;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  App overview
&lt;/h3&gt;

&lt;p&gt;What personal data do I collect?&lt;/p&gt;

&lt;p&gt;I collect data which identify person, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name (first and last)&lt;/li&gt;
&lt;li&gt;email address (which classifies as online identifier)&lt;/li&gt;
&lt;li&gt;Google ID (if logged in via Google OAuth)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the definition of personal data from above says that any information relating to an identifiable person is personal data. Which means that any data that is connected to persons info (through foreign key etc) is personal data. For my app, it means everything user creates (e.g. recipes) or I collect about them (e.g. last login time)&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I process them?
&lt;/h3&gt;

&lt;p&gt;I collect this data when users create them (account, recipes,..).&lt;/p&gt;

&lt;p&gt;I collect some data about app usage (last login, login count, recipe page show count).&lt;/p&gt;

&lt;p&gt;I don’t give the data to any other person or business. &lt;/p&gt;

&lt;h2&gt;
  
  
  General rules
&lt;/h2&gt;

&lt;p&gt;So I will try to just pinpoint some main rules needed in my app:&lt;/p&gt;

&lt;h5&gt;
  
  
  I need users active, informed consent before collecting data
&lt;/h5&gt;

&lt;p&gt;I will solve this by adding checkbox to registration form which confirms that the user is informed about what personal data I collect and how I process them, with a link to Privacy Policy (further describing what I do with data - more on that later).&lt;/p&gt;

&lt;h5&gt;
  
  
  Users have the right to access their data and to information about their data (article 15)
&lt;/h5&gt;

&lt;p&gt;Well, as users can see all their data in the app at the moment, it shouldn’t be a problem. However there are some information I keep that isn't shown on the user profile. For example - last login date, number of logins, how many times a certain page was seen,.. Those are information I use for improving usage of my app (having info about the number of active users) and for features like suggested recipes (based on most looked at).&lt;/p&gt;

&lt;p&gt;If user requests this information, I am able to get them from database manually (GDPR states I have a month to fulfill this request, so it’s entirely possible), but in the case of more request I would probably add some automated way for users to get their data.&lt;/p&gt;

&lt;p&gt;As of informing about purposes of processing data, these will be added to Privacy Policy. I also need to include info about how they can get their data - in my case they can contact me via apps mail.&lt;/p&gt;

&lt;h5&gt;
  
  
  Users have the right to restrict my use of their data (article 18)
&lt;/h5&gt;

&lt;p&gt;What does that mean?&lt;/p&gt;

&lt;p&gt;According to article 18, I have to stop processing their data if they request it based on claim that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data is inaccurate || processing is unlawful || app doesn’t need the data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t see that happening in my app, but still I need to give them info about how to request this.&lt;/p&gt;

&lt;p&gt;Also according to how ‘processing restriction’ is defined, I need to be able to mark these data. I don’t do that now automatically, but if I receive such a request, I can implement this (by adding parameter or flag to user).  &lt;/p&gt;

&lt;h5&gt;
  
  
  Right to change inaccurate or incomplete data (article 16)
&lt;/h5&gt;

&lt;p&gt;Ok, so I allow my users to change their name and email. That’s a good idea anyway, and also already implemented in my app. I just need to let them know about this option in Privacy Policy as well.&lt;/p&gt;

&lt;h5&gt;
  
  
  Right to know who’s collecting their data and what for (recital 58)
&lt;/h5&gt;

&lt;p&gt;Important thing here is that any information I give users or publicly about data collection and processing must be easily accessible and easy to understand. Therefore it should be in languages my app is available in - currently only Czech, possibly in English in the future. That would mean adding all law documents in English as well.&lt;/p&gt;

&lt;h5&gt;
  
  
  Right to be forgotten
&lt;/h5&gt;

&lt;p&gt;Also important role - users can request deletion of their data if their personal data is no longer needed for the purpose for which it was originally collected.&lt;/p&gt;

&lt;p&gt;In case of my app, it means mainly option to delete their account and all personal data associated with it.&lt;/p&gt;

&lt;p&gt;One possible way is to implement this to user profile editing (as seen in many big apps). However, as my app is small and with little users, I am okay with just adding info that they can contact me with this request, as with the others.&lt;/p&gt;

&lt;h5&gt;
  
  
  Data security (article 32)
&lt;/h5&gt;

&lt;p&gt;According to this article I must ensure integrity and availability of data processing system. This is a bit hard to understand, but we will look more into this in section about security. One important part of this rule is that I have a responsibility to test and evaluate security of my app. It might mean doing a security audit, but it’s probably okay to just do my best to secure data and built secure app.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
