<?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: Jonathan Frias</title>
    <description>The latest articles on DEV Community by Jonathan Frias (@jonathanfrias).</description>
    <link>https://dev.to/jonathanfrias</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%2F940591%2F913fc009-f7f6-492a-97ca-e552c0b6dc1e.jpeg</url>
      <title>DEV Community: Jonathan Frias</title>
      <link>https://dev.to/jonathanfrias</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonathanfrias"/>
    <language>en</language>
    <item>
      <title>Do you really understand authorization?</title>
      <dc:creator>Jonathan Frias</dc:creator>
      <pubDate>Sun, 10 Nov 2024 19:09:57 +0000</pubDate>
      <link>https://dev.to/jonathanfrias/do-you-really-understand-authorization-1o5d</link>
      <guid>https://dev.to/jonathanfrias/do-you-really-understand-authorization-1o5d</guid>
      <description>&lt;p&gt;Pretty much every web application handles authorization, and there are so many different libraries. How do you pick the right one? You're about to find out.&lt;/p&gt;

&lt;p&gt;Fundamentally, authorization is a matching game to determine if some actor is allowed access to some resource. There's many ways to skin the cat, but they all follow the rules. Here are my rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authentication presumes authorization. Null Object pattern is your friend here. You can absolutely make a &lt;code&gt;Guest&lt;/code&gt; or &lt;code&gt;Anonymous&lt;/code&gt; user with no access to anything. There isn't any reason to treat unauthorized users differently besides native language support for &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authorization systems are written from either perspective: the user, or the resource.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any arbitrarily complex authorization code can be constructed by the basis of the following 5 concepts:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Users (You, customer, external system)&lt;/li&gt;
&lt;li&gt;Groups (This is a collection of users)&lt;/li&gt;
&lt;li&gt;Permissions (CRUD+, standard CRUD plus whatever)&lt;/li&gt;
&lt;li&gt;Roles (This is a collection of Permissions)&lt;/li&gt;
&lt;li&gt;Resource (Anything! A record, a method, object etc)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this mental framework Groups are just a collection of users and can be recursively defined. A group can be a tree of groups. Same with Roles.&lt;br&gt;
A Role being a collection of permissions was a bit jarring at first, but think about it for a bit, and you'll realize it's true. There's quite a few of libraries that use role when instead of permission so be careful.&lt;/p&gt;

&lt;p&gt;Now you have a solid mental framework to quickly review authorization options based on the trade offs that are being made by any particular library. I have yet to see any authorization library that supports all of them at the same time.&lt;/p&gt;

&lt;p&gt;Let's review a few popular ruby gems and we'll see the trade-offs using this framework&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CanCanCan&lt;/li&gt;
&lt;li&gt;Pundit&lt;/li&gt;
&lt;li&gt;Apartment&lt;/li&gt;
&lt;li&gt;acts_as_tenant&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  CanCanCan
&lt;/h2&gt;

&lt;p&gt;Let's look at &lt;code&gt;cancan&lt;/code&gt;/&lt;code&gt;cancancan&lt;/code&gt;, a popular gem originally created by the infamous Ryan Bates.&lt;/p&gt;

&lt;p&gt;Straight from the README:&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;Ability&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CanCan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ability&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="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;public: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&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;# additional permissions for logged in users (they can read their own posts)&lt;/span&gt;
    &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&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;admin?&lt;/span&gt;  &lt;span class="c1"&gt;# additional permissions for administrators&lt;/span&gt;
    &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Post&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;You can immediately see the tradeoffs in the short example.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;users&lt;/th&gt;
&lt;th&gt;groups&lt;/th&gt;
&lt;th&gt;permissions&lt;/th&gt;
&lt;th&gt;roles&lt;/th&gt;
&lt;th&gt;resource&lt;/th&gt;
&lt;th&gt;auditable&lt;/th&gt;
&lt;th&gt;complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CanCanCan supports users, permissions, and resources, limited support for auditing support, shallow learning curve, fail closed access via &lt;code&gt;load_and_authorize_resource&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;CanCanCan works from the "perspective" of the user.&lt;/p&gt;

&lt;p&gt;Supporting groups of users, or roles is left to the developer to implement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pundit
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;users&lt;/th&gt;
&lt;th&gt;groups&lt;/th&gt;
&lt;th&gt;permissions&lt;/th&gt;
&lt;th&gt;roles&lt;/th&gt;
&lt;th&gt;resource&lt;/th&gt;
&lt;th&gt;auditable&lt;/th&gt;
&lt;th&gt;complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pundit works from the perspective of the resource via policies.&lt;/p&gt;

&lt;p&gt;Via policies, Pundit supports resources, users, auditing, a bit complex, permissions, fail closed via &lt;code&gt;after_action :verify_authorized&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Supporting groups of users, or roles is left to the developer to implement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apartment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;users&lt;/th&gt;
&lt;th&gt;groups&lt;/th&gt;
&lt;th&gt;permissions&lt;/th&gt;
&lt;th&gt;roles&lt;/th&gt;
&lt;th&gt;resource&lt;/th&gt;
&lt;th&gt;auditable&lt;/th&gt;
&lt;th&gt;complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Advertised as "Multitenancy for Rails and ActiveRecord" means it supports groups. It does so via database replication/sharding/schema_search_path.&lt;/p&gt;

&lt;p&gt;This design means that it supports users, groups, and resources, and is trivially auditable. fail open/close depends on how you handle your database connections/search path, and has a steep learning curve and maintenance burden.&lt;/p&gt;

&lt;p&gt;It does not support permissions, roles.&lt;/p&gt;




&lt;h2&gt;
  
  
  acts_as_tenant
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;users&lt;/th&gt;
&lt;th&gt;groups&lt;/th&gt;
&lt;th&gt;permissions&lt;/th&gt;
&lt;th&gt;roles&lt;/th&gt;
&lt;th&gt;resource&lt;/th&gt;
&lt;th&gt;auditable&lt;/th&gt;
&lt;th&gt;complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This gem uses the ActiveRecord's &lt;code&gt;default_scope&lt;/code&gt; to filter out records based on the group. It is quite an elegant solution, even though it doesn't solve permissions, nor roles.&lt;/p&gt;




&lt;p&gt;There are various other role based authorization gems that have similar trade-offs. (Spoiler: They never support groups)&lt;/p&gt;

&lt;p&gt;If only there was a gem that supported it all:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trivial to use&lt;/li&gt;
&lt;li&gt;Groups&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;li&gt;Roles&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Trivial to Audit&lt;/li&gt;
&lt;li&gt;Fail-closed design&lt;/li&gt;
&lt;li&gt;Can work from the perspective of a user or resource&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gem doesn't exist.. yet&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>coding</category>
      <category>learning</category>
    </item>
    <item>
      <title>Fearless Multitenancy</title>
      <dc:creator>Jonathan Frias</dc:creator>
      <pubDate>Sat, 07 Oct 2023 04:26:12 +0000</pubDate>
      <link>https://dev.to/jonathanfrias/fearless-multitenancy-2j8j</link>
      <guid>https://dev.to/jonathanfrias/fearless-multitenancy-2j8j</guid>
      <description>&lt;p&gt;Most Rails apps have an authentication feature. In those apps, the vast majority of work is done within the context of an authenticated user. This is so prevalent that Rails developers create separate spaces for unauthenticated public views and controllers.&lt;/p&gt;

&lt;p&gt;Data access becomes a concern as a natural consequence of having multiple users. By default, when you load a Post or similar you have to remember to filter by some condition &lt;code&gt;Post.where(user: current_user)&lt;/code&gt;, you have always remember to filter for which records your &lt;code&gt;current_user&lt;/code&gt; has access. I've seen solutions around the internet that go to extreme lengths to avoid data sharing and exposure. For example, authorization layering, customer data shards, separate schemas, and the &lt;a href="https://github.com/influitive/apartment"&gt;Apartment&lt;/a&gt; gem.&lt;/p&gt;

&lt;p&gt;These solutions all have the same problem. They are complicated. With multiple databases, schemas, and shards it starts to feel like you're maintaining multiple apps. You have a ton of big data problems when you might not even have big data to begin with. If you use an Authorization layer, it's error-prone, and writing a ton of tests is the only way to have any confidence that you aren't sharing data between customers. That takes a ton of time and patience. Do you really want to keep passing the user around?&lt;/p&gt;

&lt;p&gt;Wait a minute!? Those solutions aren't extreme you say. Well, they all sound extreme to me when you realize that all those solutions can be rendered obsolete by a single &lt;a href="https://dev.to/jonathanfrias/when-globals-are-appropriate-b1o"&gt;global variable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Big claims, so let's back them up with some 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Current&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;CurrentAttributes&lt;/span&gt;
  &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ActiveRecord&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CurrentUserFilterable&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:default_scope&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="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"No Current.user!"&lt;/span&gt; &lt;span class="k"&gt;unless&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;user&lt;/span&gt;
      &lt;span class="c1"&gt;# Exclude the system user from this constraint&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="k"&gt;if&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system?&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;user: &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;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;belongs_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;default: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&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;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Here's a version if you use Sequel instead:&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CurrentUserFilterable&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dataset&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"No Current.user!"&lt;/span&gt; &lt;span class="k"&gt;unless&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;user&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="k"&gt;if&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sysetm?&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="no"&gt;Sequel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_source_alias&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;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;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_save&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;user_id&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&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;And in any resource that needs such protection with a &lt;code&gt;user_id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&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;CurrentUserFilterable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, this code uses a global variable for control access inside of ActiveRecord's &lt;code&gt;default_scope&lt;/code&gt;. Give yourself a minute to get over the initial shock. This seems restrictive. This seems like a loss of control. This seems like bad code. After all, we are combining two patterns that are widely considered as problems together at the same time: Global State, and ActiveRecord &lt;code&gt;default_scope&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have fallen to the allure of &lt;code&gt;default_scope&lt;/code&gt; and deeply regretted it so much so that I stopped using it and happily didn't look back until now.. more than 5 years later. The problem is always that it's &lt;em&gt;nigh&lt;/em&gt; impossible to write certain features because there are always exceptions to your default scope, and you can get into situations where it's impossible to proceed without getting rid of the scope or doing weird ruby things. That inflexibility is exactly what you want when dealing with security and data access. It becomes so hard to share data that it will not happen by accident.&lt;/p&gt;

&lt;p&gt;Instead of thinking of thinking of &lt;code&gt;default_scope&lt;/code&gt; as a bad design, think of it as good security. Clients &lt;strong&gt;always&lt;/strong&gt; want control over who can access data.&lt;/p&gt;

&lt;p&gt;Imagine all the things you no longer have to do. You no longer have to remember to always add &lt;code&gt;.where(user: current_user)&lt;/code&gt; to your queries removing an entire class of bugs from your data fetching layer. You no longer have the pain of running migrations multiple times for many different targets. You no longer have to write a ton of Rspec just to have confidence that you aren't leaking any data. You no longer need to rely on the &lt;code&gt;apartment&lt;/code&gt; gem or another complex multitenant strategy.&lt;/p&gt;

&lt;p&gt;You need a bit of supporting code to make this approach viable. Let's start with our &lt;code&gt;ApplicationController&lt;/code&gt;. You need to make sure it's set at the start of a request:&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;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&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;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_current_user&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;set_current_user&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&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;Since this change will also affect your Rails console, let's add a warning upfront:&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;# config/initializers/current_user_reminder.rb&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;console&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"****************************************"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** You are not logged in. To login,   **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** the following methods available:   **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** - admin                            **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** - anon (anonymous)                 **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** - sys                              **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"** - Current.user = User.find(?)      **"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"****************************************"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;admin&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"logged in as admin"&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;user&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;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"admin@example.com"&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;sys&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"logged in as system"&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;user&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;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;some_system_condition: &lt;/span&gt;&lt;span class="kp"&gt;true&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;anon&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"logging out"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Uncomment to log in automatically&lt;/span&gt;
  &lt;span class="c1"&gt;# admin&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, in any jobs or mailers, you will need to pass the user that you are acting as. My recommendation is to pass it as the first argument.&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;NotifyPostPublishedJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;UserMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;weekly_articles&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just for completeness, you might not cross boundaries by &lt;code&gt;User&lt;/code&gt;, you could just as easily replace &lt;code&gt;User&lt;/code&gt; with your &lt;code&gt;Organization&lt;/code&gt; or &lt;code&gt;Team&lt;/code&gt; or &lt;code&gt;Company&lt;/code&gt; model and achieve a similar effect.&lt;/p&gt;

&lt;p&gt;Hopefully, you can see that you align your core application with your business goals. This solves a base problem in multitenancy, but it doesn't solve authorization requirements within a tenant. Since, I've never really been 100% satisfied with any of the authorization gems in the Rails community so watch out for my next post where we approach this problem formally, and expose the issues with &lt;a href="https://github.com/CanCanCommunity/cancancan"&gt;every&lt;/a&gt; &lt;a href="https://github.com/heartcombo/devise"&gt;single&lt;/a&gt; &lt;a href="https://github.com/varvet/pundit"&gt;authorization&lt;/a&gt; gem.&lt;/p&gt;

&lt;p&gt;If you like these ideas and would like to work with me, drop me an email at &lt;a href="mailto:contact@gofrias.com"&gt;contact@gofrias.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>sql</category>
      <category>architecture</category>
    </item>
    <item>
      <title>When globals are appropriate</title>
      <dc:creator>Jonathan Frias</dc:creator>
      <pubDate>Mon, 02 Oct 2023 15:43:14 +0000</pubDate>
      <link>https://dev.to/jonathanfrias/when-globals-are-appropriate-b1o</link>
      <guid>https://dev.to/jonathanfrias/when-globals-are-appropriate-b1o</guid>
      <description>&lt;p&gt;There are times when globals make sense. A lot of people talk about the dangers of global variables and I agree that they should be used sparingly. It's a discussion that's been had over and over again. But let's see if we can decide a rule for when we are willing to allow ourselves to use a global.&lt;/p&gt;

&lt;p&gt;So what is a "global"?&lt;/p&gt;

&lt;p&gt;A global is a shared resource that can be accessed at any time, from anywhere. Since this type of access breaks many conventional notions of good software engineering, they are usually dismissed as bad. The mere existence of a global indicates an elevated sense of importance. In a way, the only way to express this criticality is in the form of a global.&lt;/p&gt;

&lt;p&gt;Globals must align with the essence of the software. Such that when removed or rendered impossible, you would abandon the project, or it would become a different project entirely.&lt;/p&gt;

&lt;p&gt;Let's talk about ruby. There are a few standard integrations that are essential to the functionality of the software and they are elevated to the status of global. If you use the fantastic &lt;a href="http://sequel.jeremyevans.net/"&gt;Sequel gem&lt;/a&gt;, the README literally recommends you assign the connection to the database as a global. This communicates that database interop is so essential to what you're working on that there's no point in abstracting it away behind any other layers. If somehow it became forever impossible to access the database, there's a reasonable chance you would abandon or pivot the entire project. When you have a concept THAT important, you are allowed to make it global. If you use Rails, there is already &lt;a href="https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html"&gt;built-in support&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integrations can be a special case&lt;/p&gt;

&lt;p&gt;There's a lot of code that exists simply to integrate into other services. If you are making a salesforce app integration, your salesforce client can be global. Why? You can build your plugin with standard techniques, but by its very nature, it will always depend on Salesforce or be obsolete. If you accept that as part of the essence of what you're working on, you can sidestep a ton of extra architecture and design and just be productive.&lt;/p&gt;

&lt;p&gt;I haven't seen any other justifiable uses of globals, and I still tend to steer clear of them, however, I hope this gives you a different perspective on the subject.&lt;/p&gt;

&lt;p&gt;If you like these ideas and would like to work with me, drop me an email at &lt;a href="mailto:contact@gofrias.com"&gt;contact@gofrias.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Handling Timezones in Rails</title>
      <dc:creator>Jonathan Frias</dc:creator>
      <pubDate>Wed, 21 Jun 2023 04:10:29 +0000</pubDate>
      <link>https://dev.to/jonathanfrias/handling-timezones-in-rails-2ibn</link>
      <guid>https://dev.to/jonathanfrias/handling-timezones-in-rails-2ibn</guid>
      <description>&lt;p&gt;The missing guide to handling timezones in Rails and PostgreSQL. &lt;/p&gt;

&lt;p&gt;I wanted to write this guide to be your new default practice to handling time zones in Ruby on Rails. This was a hard-won lesson I learned from working a calendar scheduling app a few years back. I want you to stop converting time in your application. This is already a solved problem, no need for Rails devs to do it!! I &lt;strong&gt;know&lt;/strong&gt; you have the sneaky timezone converter code, and it needs to go!&lt;/p&gt;

&lt;p&gt;I always read on the internet that your application should use UTC for all the database related time zone information, but there is some setup that is needed in order to realize the effectiveness of this practice.&lt;/p&gt;

&lt;p&gt;First things first, postgres itself recommends that you not store UTC timestamps with a &lt;code&gt;timestamp&lt;/code&gt; type. Instead you should use a &lt;code&gt;timestamptz&lt;/code&gt; type. See: &lt;a href="https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29"&gt;https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that rails has finally set up a configurable option to generate migrations with the appropriate types (See: &lt;a href="https://github.com/rails/rails/pull/41084"&gt;https://github.com/rails/rails/pull/41084&lt;/a&gt;, &lt;a href="https://github.com/rails/rails/pull/41395"&gt;https://github.com/rails/rails/pull/41395&lt;/a&gt;) &lt;br&gt;
These posts outline the core of the problem. We want to use timezones, and we want &lt;code&gt;ActiveSupport::TimeZone&lt;/code&gt;, &lt;code&gt;ActiveSupport::TimeWithZone&lt;/code&gt;, &lt;code&gt;DateTime&lt;/code&gt;, &lt;code&gt;Time&lt;/code&gt; to all work together as they are supposed to. &lt;/p&gt;

&lt;p&gt;If you read the above referenced links, you'll see that this subtly and changes the types and some of the behaviors of the time zones. These are all unfortunate results of the mess we have made. Since it's impossible for Rails to know what time zone you intended. Keep this in mind as you upgrade your timezones columns.&lt;/p&gt;

&lt;p&gt;When you store regional timestamps without the &lt;code&gt;timestamptz&lt;/code&gt; information, it forces you as an application developer to coerce this type into the appropriate zone for the user. What this essentially means is that in application code, you're going to have something that perhaps calculates the difference of hours between your client's timezone and your database default of &lt;code&gt;UTC&lt;/code&gt; time. This is the normal default response, but there's a better way.&lt;/p&gt;

&lt;p&gt;Instead do this. Set a few fallbacks for where your default application timezones should live. For example you can detect the location of the client ip address and use that to determine their default time zone. Here I just pick my own timezone to be the default.&lt;/p&gt;

&lt;p&gt;Then you store all of your &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt; and &lt;code&gt;whatever_at&lt;/code&gt; time information inside of a the appropriate &lt;code&gt;timestamptz&lt;/code&gt;, and you allow the timestamp to come from where it really belongs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;default application time zone (Maybe your HQ time zone)&lt;/li&gt;
&lt;li&gt;User defined timezone (This is a string column named timezone on your user model)&lt;/li&gt;
&lt;li&gt;Resource defined timezone (This is a string column named 'timezone' on your non-user model)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a helper method that can pretty easily be generalized or converted into a mixin/module as needed:&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;Event&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;started_at&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_time_zone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_zone&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;finished_at&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_time_zone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_zone&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;time_zone&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;TimeZone&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="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;default_timezone&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;default_time_zone&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;TimeZone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Pacific Time (US &amp;amp; Canada)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;time_zone&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;TimeZone&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="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;default_time_zone&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;default_time_zone&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;TimeZone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Eastern Time (US &amp;amp; Canada)"&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;Note that by going through &lt;code&gt;ActiveSupport::TimeZone#[]&lt;/code&gt;, I can always ensure that I am dealing with a valid time zone. It's free tz validation!&lt;/p&gt;

&lt;p&gt;List available timezones:&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;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TimeZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when I have an event provided by &lt;code&gt;Event.first.started_at&lt;/code&gt;, I will always have an event that's in the correct time zone for the event, and in my view code I can simply override &lt;strong&gt;per user/guest&lt;/strong&gt; 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="c1"&gt;# Handles if the user is a guest&lt;/span&gt;
&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_time_zone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time_zone&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing it this way, I always store data the way postgres recommends, I only work with time zone aware data types, and convert between time zone aware types, all while at the time time respecting the time zone of whoever created the original resource. For these cases, you have options when setting what the &lt;code&gt;time_zone&lt;/code&gt; should be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the user what time_zone they want the Event/resource to be&lt;/li&gt;
&lt;li&gt;Sensibly assume the resource is in the same time zone as the user manually set in their profile&lt;/li&gt;
&lt;li&gt;Guess time zone based on ip geolocation&lt;/li&gt;
&lt;li&gt;Fallback to some application default&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Either way, you follow the best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store your time information in &lt;code&gt;timestamptz&lt;/code&gt; by default&lt;/li&gt;
&lt;li&gt;Store the &lt;code&gt;time_zone&lt;/code&gt; with the resource that it belongs to&lt;/li&gt;
&lt;li&gt;Have a sensible default case&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>postgres</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
