<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="194427" data-post-id="194427">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="ityonemo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/ityonemo/120/11341_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  ityonemo
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Here’s how you would do it:</p>
<p><a href="http://beam-wisdoms.clau.se/en/latest/indepth-beam-instructions.html#apply-113-apply-last" class="onebox" target="_blank" rel="noopener nofollow ugc">http://beam-wisdoms.clau.se/en/latest/indepth-beam-instructions.html#apply-113-apply-last</a></p>
<pre data-code-wrap="elixir"><code class="lang-elixir">apply arity &lt;registers: [...] Module fun&gt;
</code></pre>
<p>becomes:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">mov integer: arity, x: (arity + 2)
swap x:0, x: arity 
swap x:1, x: (arity + 1)
swap x: 2, x: (arity + 2)
call_ext 2 {extfunc, "Elixir.Validation", whitelist, 3} &lt;Validation.whitelist/3&gt; # takes "Module, fun, arity", possibly changes identity of module, returning it to x: 0 register
swap x:0, x: arity
swap x:1, x: (arity + 1)
swap x:2, x: (arity + 2)
apply arity
</code></pre>
<p>But yes, you would probably want to run tenant code in a separate BEAM instance, but it might not be horrible to have it cotenant on the same kvm instance or on the same metal, connected via plain old erlang distribution.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="194427" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-194427" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="194427"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="194455" data-post-id="194455">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="hauleth" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauleth/120/18942_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  hauleth
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="ityonemo" data-post="22" data-topic="32840">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/ityonemo/48/11341_2.png" class="avatar"> ityonemo:</div>
<blockquote>
<p>connected via plain old erlang distribution</p>
</blockquote>
</aside>
<p>I am not sure if Erlang Distribution is safe enough. It was mostly designed for connecting to trusted nodes.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="194455" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-194455" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="194455"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="195374" data-post-id="195374">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="AndrewDryga" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/AndrewDryga/120/2784_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  AndrewDryga
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Instead of a raw simple <code>apply</code> there are tons of functions that accept MFA and use it somehow inside (mostly by calling it) and some of them are parts of Elixir/Erlang. Not sure how you would be able to deal with that, lot’s a lot’s of people told me OTP is not a good platform to develop a sandbox.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="195374" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-195374" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="195374"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="195378" data-post-id="195378">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="ityonemo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/ityonemo/120/11341_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  ityonemo
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>If you decompile the modules, they are all apply opcodes under the hood.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="195378" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-195378" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="195378"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="195408" data-post-id="195408">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="hauleth" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauleth/120/18942_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  hauleth
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yet, you will need to dig into the all called functions. And AFIK not all are <code>apply</code> opcode as <code>spawn/3</code> is BIF. It also will make logging less useful, as you will not be able to use <code>report_cb</code> metadata value for formatting reports.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="195408" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-195408" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="195408"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="205313" data-post-id="205313">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="qhwa" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qhwa/120/17564_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  qhwa
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h2><a name="p-205313-data-fetching-and-caching-1" class="anchor" href="#p-205313-data-fetching-and-caching-1" aria-label="Heading link" rel="nofollow"></a>Data fetching and caching</h2>
<p>Occasionally, we need to fetch some configuration data in our applications, for example, client tokens, a list of ids, etc. Most of the time we want to fetch them again in some duration of time to keep it up to date.</p>
<p>I’m thinking about writing a small library to ease such a task. It could be used as:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.EnabledUserIds do
  use DataFetcher, name: :enabled_user_ids, ttl: :timer.seconds(6_000)
  
  # you need only implement this callback:

  @impl true
  def fetch(_),
   # returns any term, in this case, a list of user ids
   do: request_enabled_user_ids_from_some_service()

  def enabled_user_ids,
    # `data!` is a macro where you can get the data fetched
    do: data!()  
</code></pre>
<p>I used to use <a href="https://github.com/whitfin/cachex" rel="noopener nofollow ugc">cachex</a> for such works, but I don’t feel like the code I set it up because I need to handle the different returning values from <code>{:ok, cached_data}</code> and <code>{:commit, new_data}</code>. Furthermore, if I have high volume of traffic right when the cache expires, all the requests will hit downstream service immediately.</p>
<p>So my idea is to have a process to fetch, hold, then respond to queries with the data.  Then it spawns a new process to fetch it again and only kills itself when the new process is successful. Therefore we have no downtime.</p>
<p>I’ve already finished the proof of concept and it works. This is a small idea for a common task so I’m not sure if I am reinventing anything or if anyone is interested in it.</p>
<p>Please give me feedback, thank you, fellows! <img src="https://forum.elixirforum.com/images/emoji/apple/smile.png?v=15" title=":smile:" class="emoji" alt=":smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="205313" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-205313" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="205313"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="205316" data-post-id="205316">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wojtekmach" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/120/999_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wojtekmach
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Hex Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="qhwa" data-post="27" data-topic="32840">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qhwa/48/17564_2.png" class="avatar"> qhwa:</div>
<blockquote>
<p>Furthermore, if I have high volume of traffic right when the cache expires, all the requests will hit downstream service immediately.</p>
<p>So my idea is to have a process to fetch, hold, then respond to queries with the data. Then it spawns a new process to fetch it again and only kills itself when the new process is successful. Therefore we have no downtime.</p>
</blockquote>
</aside>
<p>Under high load, there are at least two problems with using a process to hold and respond to queries with data:</p>
<ol>
<li>That single process can easily become a bottleneck</li>
<li>Data is copied on message passing</li>
</ol>
<p>You can avoid problem 1 by looking up data in the <em>caller</em> process from an ets table. If your data changes very rarely, (ideally never) you can replace ets with persistent_term which would also avoid problem 2.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="205316" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-205316" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="205316"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="205321" data-post-id="205321">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="qhwa" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qhwa/120/17564_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  qhwa
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-Hex-Core-Team" data-username="wojtekmach" data-post="28" data-topic="32840">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/48/999_2.png" class="avatar"> wojtekmach:</div>
<blockquote>
<p>Under high load, there are at least two problems with using a process to hold and respond to queries with data:</p>
<ol>
<li>That single process can easily become a bottleneck</li>
<li>Data is copied on message passing</li>
</ol>
<p>You can avoid problem 1 by looking up data in the <em>caller</em> process from an ets table. If your data changes very rarely, (ideally never) you can replace ets with persistent_term which would also avoid problem 2.</p>
</blockquote>
</aside>
<p>Thanks <a class="mention" href="/u/wojtekmach" rel="nofollow">@wojtekmach</a> this makes my TIL! I’ll change the storage from process memory into ets.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="205321" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-205321" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="205321"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="206553" data-post-id="206553">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="BartOtten" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/BartOtten/120/25440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  BartOtten
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’ve been playing with Macro’s after reading <a href="https://forum.elixirforum.com/t/metaprogramming-elixir-pragprog/5886" rel="nofollow">Metaprogramming Elixir</a> by <a class="mention" href="/u/chrismccord" rel="nofollow">@chrismccord</a>.</p>
<p>As a result <code>:phx_alt_routes</code> was born, albeit in a private repository. First I want to validate the concept. It already works with dead views and live views. Simply by</p>
<ul>
<li>creating one config module (adapter)</li>
<li>replacing alias <code>MyWebApp.Router.Helpers</code> with <code>use MyWebApp.AltRoutes.Router.Helpers, MyWebApp.Router.Helpers</code> in my_web_app.ex</li>
<li>adding <code>Phx.AltRoutes.MountHelper</code> to the :live_view set.</li>
<li>adding <code>Phx.AltRoutes.Plug</code> to the connection plug set</li>
</ul>
<h1><a name="p-206553-phxaltroutesrouter-1" class="anchor" href="#p-206553-phxaltroutesrouter-1" aria-label="Heading link" rel="nofollow"></a>Phx.AltRoutes.Router</h1>
<p>Provides a set of macros for generating alternative Phoenix routes with configurable assignments.</p>
<p>When a <code>Gettext</code> module is defined in the configuration, the <code>alt_scope/2</code> macro uses it to build the alternative routes using the <code>Gettext</code> module. This allows routes to be translated along with other <code>Gettext</code> messages.</p>
<p>To summarize the process.</p>
<ul>
<li>The <code>alt_scope/2</code> macro splits the path of every nested route.</li>
<li>The segments are extracted into <code>routes.po</code> files using <code>mix gettext.extract</code>.</li>
<li>The individual segments are translated</li>
<li>The translations are used to create additional alternative routes with path <code>{prefix}/{translated_segment1}/{translated_segment2}/</code> and helper <code>{prefix}_{original_helper}</code></li>
<li>For every alternative route the configured bindings are added to <code>assigns</code> and <code>session</code>.</li>
<li>A few helper assignments are also added, which can be used by <code>Phx.AltRoutes.Router.Helpers</code></li>
</ul>
<h2><a name="p-206553-example-2" class="anchor" href="#p-206553-example-2" aria-label="Heading link" rel="nofollow"></a>Example</h2>
<p>Given this configuration:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyAppWeb.Phx.AltRoutes do
  use Phx.AltRoutes,
    routes: %{
      "dutch" =&gt; %{
        path_prefix: "nl",
        bindings: %{locale: "nl_NL"},
      },
      "english" =&gt; %{
        path_prefix: "en",
        bindings: %{ocale: "en_GB"},
      },
    },
    gettext_module: MyAppWeb.Gettext
</code></pre>
<p>To generate alternative routes for routes, those routes are nested in a <code>alt_scope</code>.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyAppWeb.Router do
  use Phoenix.Router
  alt_scope MyAppWeb.Phx.AltRoutes do
    get "/users/:id/edit", UserController, :edit
    get "/users/show/:id", UserController, :show
  end
end
</code></pre>
<p>This will generate <code>routes.po</code> files including the entries for <code>users</code>, <code>edit</code> and <code>show</code>. You can translate the segments. The macro expand the routes during compile time with alternative variants. The example above is equal to:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyAppWeb.Router do
  use Phoenix.Router
  scope "/" do
    get "/users/:id/edit", UserController, :edit
    get "/users/show/:id", UserController, :show

  scope "/en" do
    get "/users/:id/edit", UserController, :edit, assigns: %{locale: "en_GB", helper_prefix: "en"}, session: %{"locale": "en_GB", helper_prefix: "en"}, as: :en_user_edit_path
    get "/users/show/:id", UserController, :show, assigns: %{locale: "en_GB", helper_prefix: "en"}, session: %{"locale": "en_GB", helper_prefix: "en"} , as: :en_user_show_path
  end

  scope "/nl" do
    get "/gebruikers/:id/bewerken", UserController, :edit, assigns: %{locale: "nl_NL", helper_prefix: "nl"}, session: %{"locale": "nl_NL", helper_prefix: "nl"}, as: :nl_user_edit_path
    get "/gebruikers/tonen/:id", UserController, :show, :edit, assigns: %{locale: "nl_NL", helper_prefix: "nl"}, session: %{"locale": "nl_NL", helper_prefix: "nl"}, as: :nl_user_show_path
  end
end
</code></pre>
<p>The routes can be nested, which makes it easy to create <code>/region/sub-region/</code> routes.</p>
<h1><a name="p-206553-phxaltroutesrouterhelpers-3" class="anchor" href="#p-206553-phxaltroutesrouterhelpers-3" aria-label="Heading link" rel="nofollow"></a>Phx.AltRoutes.Router.Helpers</h1>
<p>Provides a macro that wraps Phoenix.Router.Helpers functions in order to generates the correct alternative route. It uses the helper_prefix assignment added to the session by Phx.AltRoutes.Router.</p>
<p>Thinking of it…the assignments can probably be fetched using only the helper_prefix key…</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="206553" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-206553" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="206553"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="206862" data-post-id="206862">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="edisonywh" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/edisonywh/120/14709_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  edisonywh
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I was working on refactoring <a href="https://slickinbox.com" rel="noopener nofollow ugc">Slick Inbox</a> and I looked at my admin tool and didn’t like what I saw. It’s built with LIveView, but I find that I am repeating a lot of the same things (search, pagination etc) on every admin page that I have. They’re pretty simple pages, they list the entities in the DB (Newsletter, User, Webhook etc), I can edit them etc, it’s just so I don’t need to always SSH into my DB to do admin stuffs (like manually verifying user).</p>
<p>I looked at the repetitive stuffs, extracted them, and right now it’s looking like it could actually be generalisable for a library idea, tentatively named <strong>Backoffice</strong>, but I thought it would be good to check here first.</p>
<p>I actually found that it somehow end up looking quite a bit like <a href="https://github.com/aesmail/kaffy/tree/master/lib/kaffy" rel="noopener nofollow ugc">Kaffy</a>, which looks like a pretty great project, but my approach slightly differs in some way.</p>
<ol>
<li>
<p>Kaffy uses controllers. Backoffice uses LiveView</p>
</li>
<li>
<p>You use Kaffy by <code>using</code> it in your router, the available paths are hidden from you, and you need to find it elsewhere (config.exs or a different Config module)</p>
</li>
<li>
<p>Kaffy works by having one controller, and then it renders things for you with a single controller <a href="https://github.com/aesmail/kaffy/blob/master/lib/kaffy/routes.ex" rel="noopener nofollow ugc">as seen here</a>. Backoffice would seamlessly integrate into your router file, you still need to declare your individual Live pages, but you can <code>use</code> Backoffice and that basically bootstraps your Live to an admin interface.</p>
</li>
</ol>
<p>With Kaffy:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># https://github.com/aesmail/kaffy-demo/blob/master/lib/bakery_web/router.ex#L16

# router.exs
defmodule YourApp.Router do
  use Kaffy.Routes
end
</code></pre>
<p>I’m not a fan of this idea since it’s not immediately obvious what pages are available (the routes are defined in <code>config.exs</code> which could potentially call out to a different Config module as well, but I prefer things to be colocated)</p>
<p>With Backoffice, it would sit right next to your current set-up.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># router.exs

scope "/admin", YourAppWeb, do
    live("/users", UserLive.Index, :index) # these are your existing pages
    live("/users/:id/edit", UserLive.Index, :edit)

    live("/newsletters", Backoffice.NewsletterLive.Index, :index)
    live("/newsletters/:id/edit", Backoffice.NewsletterLive.Index, :edit)
end

</code></pre>
<p>You still need to create your own LiveView module, but you can see it sits nicely in your router, and Backoffice provides you a starting template of some sorts that you can then customize. For example, at a minimum you’d need:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule YourAppWeb.Backoffice.NewsletterLive.Index do
  use Backoffice.Resources,
    repo: YourAppWeb.Repo,
    resource: YourAppWeb.Newsletter
end
</code></pre>
<p>This is essentially a wrapper to use <code>Phoenix.LiveView</code> and defines most of the callbacks for you. It also <strong>handles pagination</strong> for you (right now I’m depending on Scrivener.Ecto), and <strong>search</strong> (with a raw <code>ilike</code> query that doesn’t deal with input sanitization for now.. works for my internal usage <img src="https://forum.elixirforum.com/images/emoji/apple/man_shrugging.png?v=15" title=":man_shrugging:" class="emoji" alt=":man_shrugging:" loading="lazy" width="20" height="20">).</p>
<p>It has a list of things that you can override/customize, like what Kaffy does, so that part is not too surprising.</p>
<p>Kaffy already seems to be pretty great, but I didn’t like the way it configures. Backoffice is basically just me extracting out my current set-up, which may or may not be a good set-up as it might differ from how other people are building LiveViews.</p>
<p>I have already extracted it out in my codebase for a proof of concept and it’s working fine so far, but it still does make some assumption at a few places, so there’s quite a bit of work involved to remove those assumptions too.</p>
<p>What do you think? Is this something that is worth putting more effort into?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="206862" data-batch-url="/posts/batch_likers">
                        7
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/reality-check-your-library-idea/32840/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-206862" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="206862"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/32840/load_more?page=4">Load more posts (11 remaining)</a>
</div></template></turbo-stream>