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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>We could potentially add support for this in the api extensions so that they can allow sorting on fields for <code>to_one</code> relationships.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="319147" 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/ash-3-0-teasers/61857/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-319147" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="319147"
                     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="319701" data-post-id="319701">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h1><a name="p-319701-ash-30-teaser-3-better-defaults-less-surprises-part-1-1" class="anchor" href="#p-319701-ash-30-teaser-3-better-defaults-less-surprises-part-1-1" aria-label="Heading link" rel="nofollow"></a>Ash 3.0 Teaser <span class="hashtag-raw">#3:</span>  Better Defaults, Less Surprises, Part 1</h1>
<p>I meant to make this post a few days ago, but I got a bit busy handling a heisenbug that a user found when using aggregates in policies <img src="https://forum.elixirforum.com/images/emoji/apple/bug.png?v=15" title=":bug:" class="emoji" alt=":bug:" loading="lazy" width="20" height="20">. That is all sorted now so I can focus on making progress on 3.0 again! <img src="https://forum.elixirforum.com/images/emoji/apple/partying_face.png?v=15" title=":partying_face:" class="emoji" alt=":partying_face:" loading="lazy" width="20" height="20"></p>
<p>You may also notice that, from here on out, we will be exchanging the term <code>api</code> with <code>domain</code>. See <a href="https://forum.elixirforum.com/t/ash-3-0-teasers/61857/9" rel="nofollow">teaser #2</a> for more <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>A big part of the purpose of 3.0 is changing default behaviors. Ash 2.0 had a lot of “permissive” defaults that could make things extremely quick to get started but could easily bite you later down the road. Ultimately, with Ash, we care more about “year five” than “day one”, so we don’t really want to make design choices that will be a foot-gun later down the road for the sake of easy initial adoption.</p>
<p>There are a lot of these changes, so this will be a two parter!</p>
<h1><a name="p-319701-domainauthorizationauthorize-now-defaults-to-by_default-2" class="anchor" href="#p-319701-domainauthorizationauthorize-now-defaults-to-by_default-2" aria-label="Heading link" rel="nofollow"></a><code>domain.authorization.authorize</code> now defaults to <code>:by_default</code></h1>
<p>The original default was <code>:when_requested</code>, which would cause authorization to trigger when an <code>actor</code> option was provided, or when <code>authorize?: true</code> was set. This makes it <em>very easy</em> to accidentally forget to authorize an action invocation. <code>:by_default</code> always sets <code>authorize? true</code> (has no effect if you are not using authorizers), unless you explicitly say <code>authorize?: false</code>..</p>
<p>We’ve known for a very long time that this was not the ideal default behavior, but it was a significant breaking change and so had to wait for 3.0. You can revert to the old behavior (but should eventually update) by setting the value in each domain module back to <code>:when_requested</code>.</p>
<h1><a name="p-319701-unknown-action-inputs-now-produce-errors-3" class="anchor" href="#p-319701-unknown-action-inputs-now-produce-errors-3" aria-label="Heading link" rel="nofollow"></a>unknown action inputs now produce errors</h1>
<p>When passing parameters to actions, we would previously <em>ignore</em> unknown parameters. This made it very easy to misspell an input and not realize it, or otherwise believe that an input was being used when it wasn’t.</p>
<h1><a name="p-319701-bulk-updatedestroy-strategy-defaults-to-atomic-4" class="anchor" href="#p-319701-bulk-updatedestroy-strategy-defaults-to-atomic-4" aria-label="Heading link" rel="nofollow"></a>Bulk update/destroy strategy defaults to <code>:atomic</code></h1>
<p>When calling a bulk update/destroy, it may not be able to be done atomically (i.e one single UPDATE query + after hooks). You can specify a list of allowed strategies when calling a bulk action. The strategies available are</p>
<ul>
<li><code>atomic</code> - Must be doable as a single operation at the data layer (plus after action/after batch logic)</li>
<li><code>atomic_batches</code> - Can be a series of atomic operations (as above). This can be great for massive inputs where you want to do them in batches. What we do is stream the provided query or list into batches, and update by primary key as a single atomic operation.</li>
<li><code>stream</code> - We stream <em>each record</em> and do the logic to update each record one at a time.</li>
</ul>
<p>Ash will use the “best” strategy that it can use (i.e the first one that can be used in the list above starting at the top). In 2.0, the default for the strategy option is <code>[:atomic, :atomic_batches, :stream]</code>, allowing all three by default.</p>
<p>In 3.0 the default for the strategy option is <code>[:atomic]</code>. This is in line with making defaults something that will help you choose the safest option. If an action cannot be done atomically, you will be told a reason, and you can adjust the strategy option or modify the action.</p>
<h1><a name="p-319701-require_atomic-on-updatedestroy-actions-defaults-to-true-5" class="anchor" href="#p-319701-require_atomic-on-updatedestroy-actions-defaults-to-true-5" aria-label="Heading link" rel="nofollow"></a><code>require_atomic?</code> on update/destroy actions defaults to <code>true</code></h1>
<p>When writing actions, we want them to be concurrency safe. What this means is that no update/destroy action will be performed <em>non</em> atomically (in this context, to be done atomically means that all changes, validations, and attribute changes can be done atomically. You will get a warning at compile time if your action is known not to be able to be done atomically, and an error at runtime if you attempt to run them.</p>
<p>For example, what might happen is something like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">update :update do
  # I add an anonymous function change
  change fn changeset, _ -&gt; 
    Ash.Changeset.change_attribute(changeset, :attribute, :value)
  end
end
</code></pre>
<p>I get an error at compile time, like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">warning: [Resource]
 actions -&gt; update:
  update cannot be done atomically, because the changes `[Ash.Resource.Change.Function]` cannot be done atomically
</code></pre>
<p>So I adjust the action to use a builtin change that has an atomic implementation. I could also extract the logic into its own module and <code>use Ash.Resource.Change</code> and add the <code>atomic/2</code> callback.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">update :update do
  # I add an anonymous function change
  change set_attribute(:attribute, :value)
end
</code></pre>
<p>And now we’re good! I can also add <code>require_atomic? false</code> to the action if I know that the changes on this action are safe to run non-atomically.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">update :update do
  require_atomic? false
  
  # I add an anonymous function change
  change fn changeset, _ -&gt; 
    Ash.Changeset.change_attribute(changeset, :attribute, :value)
  end
end
</code></pre>
<h1><a name="p-319701-closing-6" class="anchor" href="#p-319701-closing-6" aria-label="Heading link" rel="nofollow"></a>Closing</h1>
<p>This one was a lot, and there are more to come! Keep in mind that not all breaking changes will be included in these teasers, but the goal is to include all major/significant changes. Can’t wait for 3.0 to get out there.</p>
<p>Teaser <span class="hashtag-raw">#4:</span> <a href="https://forum.elixirforum.com/t/ash-3-0-teasers/61857/28" class="inline-onebox" rel="nofollow">Ash 3.0 Teasers! - #28 by zachdaniel</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="319701" data-batch-url="/posts/batch_likers">
                        22
                      </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/ash-3-0-teasers/61857/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-319701" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="319701"
                     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="319948" data-post-id="319948">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This is a great candidate for an example in a guide!</p>
<p>I didn’t know this was possible</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="319948" 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/ash-3-0-teasers/61857/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-319948" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="319948"
                     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="320878" data-post-id="320878">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hey <a class="mention" href="/u/zachdaniel" rel="nofollow">@zachdaniel</a> I have a greenfield project and I’m keen to try out Ash v3. I’m happy to deal with a bit of early adopter pain (and even contribute bug reports/docs/fixes if it’s helpful).</p>
<p>What’s the best path to take for now? Just follow the v2 guides on hexdocs and wait for the v3 rc to switch things over? Or start from the <code>v3</code> branch?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="320878" 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/ash-3-0-teasers/61857/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-320878" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="320878"
                     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="320882" data-post-id="320882">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Right now it’s really just not possible, and won’t be for about a week or two. Some breaking internal changes break essentially all of our associated packages. But very soon <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_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="320882" data-batch-url="/posts/batch_likers">
                        2
                      </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/ash-3-0-teasers/61857/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-320882" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="320882"
                     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="320883" data-post-id="320883">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>No worries, looking forward to it <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_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="320883" 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/ash-3-0-teasers/61857/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-320883" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="320883"
                     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="320889" data-post-id="320889">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Ash 3.0 Teaser <span class="hashtag-raw">#4:</span> Better Defaults, Less Surprises, Part 2</p>
<p>3.0 is coming along very well! Got a few more updates along the same vein of better defaults and less surprises!</p>
<h2><a name="p-320889-ashnotloaded-for-not-selected-values-1" class="anchor" href="#p-320889-ashnotloaded-for-not-selected-values-1" aria-label="Heading link" rel="nofollow"></a><code>%Ash.NotLoaded{}</code> for not selected values</h2>
<p>When you run an Ash.Query or an Ash.Changeset that has a select applied, anything that isn’t selected currently gets a <code>nil</code> value. This can be very confusing and often leads to nontrivial bugs. In Ash 3.0, you will instead get <code>%Ash.NotLoaded{}</code>, allowing you to distinguish between values that are <em>actually</em> <code>nil</code> and values that have just not been loaded.</p>
<h2><a name="p-320889-actions-no-longer-accept-all-public-writable-attributes-by-default-2" class="anchor" href="#p-320889-actions-no-longer-accept-all-public-writable-attributes-by-default-2" aria-label="Heading link" rel="nofollow"></a>Actions no longer accept all public, writable attributes by default</h2>
<p>Thanks to <a class="mention" href="/u/sevenseacat" rel="nofollow">@sevenseacat</a> for bringing this to our attention originally and illustrating just how risky this can be!</p>
<p>In Ash 2.0, actions automatically accept all public writable attributes. This makes it very easy to accidentally include an attribute in your actions, especially when adding a new attribute. For instance:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">actions do
  defaults [:create, :read]
end
</code></pre>
<p>If you add an attribute to the above resource, you may not realize that it is now accepted in that create action by default.</p>
<p>In Ash 3.0, all actions accept <em>nothing</em> by default. You can adopt the old behavior in your resource with</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">actions do
  default_accept :*
end
</code></pre>
<p>This will help prevent potentially leaking new attributes.</p>
<p>A small quality of life improvement that results from this is that you no longer need to specify <code>attribute_writable?: true</code> on your <code>belongs_to</code> relationships to modify their attribute. This is because making those attributes modifiable in a resource requires including it in the <code>accept</code> list (or using <code>accept :*</code>), and so it is no longer implicit.</p>
<h2><a name="p-320889-private-true-is-now-public-false-and-public-false-is-now-the-default-3" class="anchor" href="#p-320889-private-true-is-now-public-false-and-public-false-is-now-the-default-3" aria-label="Heading link" rel="nofollow"></a><code>private?: true</code> is now <code>public?: false</code>, and <code>public?: false</code> is now the default</h2>
<p>In Ash 2.0, all fields default to <code>private?: false</code>.</p>
<p>Public attributes, relationships, calculations and aggregates are meant to be exposed over public interfaces. By defaulting to <code>public?: true</code>, it makes it <em>very</em> easy to add a new field and not realize that you’ve added it to your GraphQL or JSON API, etc.</p>
<p>In Ash 3.0, this option has been renamed to its inverse, <code>public?</code>. Additionally, it now defaults to <code>false</code>. Where you may have seen this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">attributes do
  attribute :first_name, :string
  attribute :last_name, :string
  attribute :super_secret, :string, private?: true
end
</code></pre>
<p>you will now see</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">attributes do
  attribute :first_name, :string, public?: true
  attribute :last_name, :string, public?: true
  attribute :super_secret, :string
end
</code></pre>
<p>As you can see this may often be more verbose, as many resources have more public fields than private fields. But it is also much safer in general. It is much better to have an experience of “oh, how come X isn’t showing in my public interface”, then “oh, we’re showing some data over our API that we didn’t intend to show”. Often times we have to make trade offs for the sake of security and safety, and this is one of those cases.</p>
<h2><a name="p-320889-custom-expressions-4" class="anchor" href="#p-320889-custom-expressions-4" aria-label="Heading link" rel="nofollow"></a>Custom Expressions</h2>
<p>This isn’t on theme, as it’s a new feature as opposed to a better default, but I wanted to spice things up <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20">. Custom expressions will allow you to extend Ash’s expression syntax. Since an example is worth a thousand words:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defmodule MyApp.Expressions.LevenshteinDistance do
    use Ash.CustomExpression,
      name: :levenshtein_distance,
      arguments: [
        [:string, :string]
      ]

    def expression(AshPostgres.DataLayer, [left, right]) do
      expr(fragment("levenshtein(?, ?)", left, right))
    end

    # It is good practice to always define an expression for `Ash.DataLayer.Simple`,
    # as that is what Ash will use to run your custom expression in Elixir.
    # This allows us to completely avoid communicating with the database in some cases.

    def expression(data_layer, [left, right]) when data_layer in [
      AshPostgres.DataLayer.Ets,
      AshPostgres.DataLayer.Simple
    ] do
      expr(fragment(&amp;levenshtein/2, left, right))
    end

    # always define this fallback clause as well
    def expression(_data_layer, _args), do: :unknown

    defp levenshtein(left, right) do
      # ......
    end
  end
</code></pre>
<p>With the above custom expression, defined, I can configure it like so:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">config :ash, :custom_expressions, [MyApp.Expressions.LevenshteinDistance]
</code></pre>
<p>And I can then use it in expressions:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Ash.Query.filter(User, levenshtein_distance(full_name, ^search) &lt; 5)
</code></pre>
<p>This will also allow libraries and other packages to provide cross-data layer expressions for you to use with their custom values and expressions.</p>
<h2><a name="p-320889-thats-all-5" class="anchor" href="#p-320889-thats-all-5" aria-label="Heading link" rel="nofollow"></a>Thats all!</h2>
<p>Thats all I have for you today, thanks for everyone following along <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"> We are on track to have a release candidate this month, ready for the adventurous folks to give it a spin <img src="https://forum.elixirforum.com/images/emoji/apple/partying_face.png?v=15" title=":partying_face:" class="emoji" alt=":partying_face:" loading="lazy" width="20" height="20"></p>
<p>Teaser <span class="hashtag-raw">#5:</span> <a href="https://forum.elixirforum.com/t/ash-3-0-teasers/61857/30" class="inline-onebox" rel="nofollow">Ash 3.0 Teasers! - #30 by zachdaniel</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="320889" data-batch-url="/posts/batch_likers">
                        22
                      </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/ash-3-0-teasers/61857/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-320889" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="320889"
                     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="320891" data-post-id="320891">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The expressions stuff is so coooool.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="320891" 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/ash-3-0-teasers/61857/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-320891" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="320891"
                     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="321197" data-post-id="321197">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h1><a name="p-321197-ash-30-teaser-5-model-your-domain-derive-the-rest-1" class="anchor" href="#p-321197-ash-30-teaser-5-model-your-domain-derive-the-rest-1" aria-label="Heading link" rel="nofollow"></a>Ash 3.0 Teaser <span class="hashtag-raw">#5:</span> Model your <em>domain</em>, derive the rest.</h1>
<h2><a name="p-321197-code-interfaces-on-domain-modules-2" class="anchor" href="#p-321197-code-interfaces-on-domain-modules-2" aria-label="Heading link" rel="nofollow"></a>Code Interfaces on domain modules</h2>
<p>When building APIs with Ash extensions, like AshGraphql and AshJsonApi, you are combining resource actions into a single interaction point. The <code>code_interface</code> tooling is a method for you to define a similar interaction point for your <em>code</em>. However, the general pattern encouraged defining functions that were called on each resource module. For example:</p>
<p><code>MyApp.Accounts.User.register_with_password(email, password)</code></p>
<p>However, this pattern often encourages “reaching in” to individual resources in your domain in your code, which can often make refactoring very difficult. For API extensions this is less of an issue because each action is exposed in such a way that it can  have its implementation and backing resource modified in a way that is transparent to the caller (in some cases this is easier than in others).</p>
<p>However, in Elixir we’re used to encapsulating interfaces in a module designed for that purpose. To that end, in Ash 3.0 we now support specifying code interfaces on the domain, and in general we <em>encourage</em> this over defining code interfaces on the resource. Here is what it looks like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.Accounts do
  use Ash.Domain

  resources do
    resource MyApp.Accounts.User do
      define :register_user, action: :register_with_password, args: [:username, :password]
    end
  end
end
</code></pre>
<p>With this definition, you’d have</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">MyApp.Accounts.register_user("username", "password")
</code></pre>
<p>This allows for defining the code-level interface for your action in one central place, and emphasizes the role of your domains as a central element for a given group of resources.</p>
<h2><a name="p-321197-policies-on-the-domain-3" class="anchor" href="#p-321197-policies-on-the-domain-3" aria-label="Heading link" rel="nofollow"></a>Policies on the domain</h2>
<p>Authorization is an example of a cross cutting concern that we often want to apply in broad strokes across our application (not always, but sometimes). To this end, you can specify policies on the domain directly. When calling a resource, the policies from the relevant domain are included at the beginning of the list of policies for that resource. Then, authorization proceeds as normal.</p>
<p>The reason the domain policies go first is because of <code>bypass</code> policies. This allows you to do things like declare an admin bypass once in each domain, instead of each resource, or to define a policy like “deny inactive users” in the same way. For example:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.Accounts do
  use Ash.Domain,
    # note that it goes in `extensions`, not `authorizers`.
    extensions: [Ash.Policy.Authorizer]

  resources do
    resource MyApp.Accounts.User
  end

  policies do
    bypass always() do
      authorize_if actor_attribute_equals(:admin, true)
    end

    policy actor_attribute_equals(:active, false) do
      forbid_if always()
    end
  end
end
</code></pre>
<p>With this in place, any action calls to any resources using this domain will include these policies ahead of their own <img src="https://forum.elixirforum.com/images/emoji/apple/partying_face.png?v=15" title=":partying_face:" class="emoji" alt=":partying_face:" loading="lazy" width="20" height="20"></p>
<h2><a name="p-321197-the-beginning-of-long-term-focus-on-dx-docs-4" class="anchor" href="#p-321197-the-beginning-of-long-term-focus-on-dx-docs-4" aria-label="Heading link" rel="nofollow"></a>The beginning of long term focus on DX &amp; docs</h2>
<p>During the remainder of the time before the 3.0 release, and while it is in release candidacy, we will be focusing on DX improvements and documentation. Some examples of these improvements actually come from the 2.0 release of another one of our packages, <a href="https://hex.pm/packages/spark" rel="nofollow">spark</a>, which Ash 3.0 has been upgrade to support. Keep in mind that you have to be using <code>ElixirLS</code> (as it is based on elixir_sense) to get the benefits of our custom autocomplete extension.</p>
<h3><a name="p-321197-autocomplete-options-to-use-ashresource-5" class="anchor" href="#p-321197-autocomplete-options-to-use-ashresource-5" aria-label="Heading link" rel="nofollow"></a>Autocomplete options to <code>use Ash.Resource</code></h3>
<p>This actually applies to any spark DSL, but for Ash users this will most notably show when calling <code>use Ash.Resource</code>. For example:</p>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://cleanshot.com/share/5mJYPrwL">
  <header class="source">
      <img src="https://cleanshot.com/favicon@2x.png" class="site-icon" alt="" width="32" height="32">

      <a href="https://cleanshot.com/share/5mJYPrwL" target="_blank" rel="noopener nofollow ugc">CleanShot Cloud</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:690/409;"><img src="https://brief.cleanshot.cloud/media/873/QdK49UwL0juGe9ehhrjh5eHkkKwVlgJq2UO83USZ.gif.jpg?width=1200&amp;height=630&amp;scaling=fit&amp;anchor=center&amp;play=1&amp;signature=d808e4763ec574ae11e7ac6c7bc4feb328cbdcca43de04f038071750c1dc6d2b" class="thumbnail" alt="" width="690" height="409"></div>

<h3><a href="https://cleanshot.com/share/5mJYPrwL" target="_blank" rel="noopener nofollow ugc">CleanShot 2024-03-15 at 17.43.11</a></h3>

  <p>GIF uploaded to CleanShot Cloud</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<h3><a name="p-321197-autocomplete-of-options-for-functions-in-ash-and-generated-code-interface-functions-6" class="anchor" href="#p-321197-autocomplete-of-options-for-functions-in-ash-and-generated-code-interface-functions-6" aria-label="Heading link" rel="nofollow"></a>Autocomplete of options for functions in Ash <em>and</em> generated code interface functions</h3>
<h4><a name="p-321197-ash-functions-7" class="anchor" href="#p-321197-ash-functions-7" aria-label="Heading link" rel="nofollow"></a>Ash functions</h4>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://cleanshot.com/share/TjKgm7sR">
  <header class="source">
      <img src="https://cleanshot.com/favicon@2x.png" class="site-icon" alt="" width="32" height="32">

      <a href="https://cleanshot.com/share/TjKgm7sR" target="_blank" rel="noopener nofollow ugc">CleanShot Cloud</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:690/347;"><img src="https://brief.cleanshot.cloud/media/873/C36kKkED10DIEbhBiP23poTk7eMR4sE9c3OfWi5a.gif.jpg?width=1200&amp;height=630&amp;scaling=fit&amp;anchor=center&amp;play=1&amp;signature=2212ef651063406c1fbc72c02f0ea98327d0dd4e032c704a075f5f4ef8d4398e" class="thumbnail" alt="" width="690" height="348"></div>

<h3><a href="https://cleanshot.com/share/TjKgm7sR" target="_blank" rel="noopener nofollow ugc">CleanShot 2024-03-15 at 17.45.48</a></h3>

  <p>GIF uploaded to CleanShot Cloud</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<h4><a name="p-321197-code-interface-functions-8" class="anchor" href="#p-321197-code-interface-functions-8" aria-label="Heading link" rel="nofollow"></a>Code Interface Functions</h4>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://cleanshot.com/share/0GtC0TzC">
  <header class="source">
      <img src="https://cleanshot.com/favicon@2x.png" class="site-icon" alt="" width="32" height="32">

      <a href="https://cleanshot.com/share/0GtC0TzC" target="_blank" rel="noopener nofollow ugc">CleanShot Cloud</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:690/257;"><img src="https://brief.cleanshot.cloud/media/873/iAxrJ8JqYaRk0n4VwXd88hzQzS1KZDFTgD1O6x1N.gif.jpg?width=1200&amp;height=630&amp;scaling=fit&amp;anchor=center&amp;play=1&amp;signature=d3b87efcde69da413e5f6b1bef91a4a5f48f41d2b9868b8481b88db88edc078f" class="thumbnail" alt="" width="690" height="258"></div>

<h3><a href="https://cleanshot.com/share/0GtC0TzC" target="_blank" rel="noopener nofollow ugc">CleanShot 2024-03-15 at 17.47.47</a></h3>

  <p>GIF uploaded to CleanShot Cloud</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<p>Additionally, code interface documentation has been updated to include any argument and accepted attribute descriptions. All together, this should drastically help with discoverability of what your code interface offers, and give you extremely high quality, well documented functions for interacting with your resources.</p>
<h3><a name="p-321197-conclusion-9" class="anchor" href="#p-321197-conclusion-9" aria-label="Heading link" rel="nofollow"></a>Conclusion</h3>
<p>We’re getting closer and closer to the release candidate of Ash, but we’re already looking past that point. The experience that users have with Ash is extremely important to me, and I’m ecstatic that it is finally the right time to shift my efforts to these very important areas.</p>
<p>Until next time!</p>
<p>Teaser <span class="hashtag-raw">#6:</span>  <a href="https://forum.elixirforum.com/t/ash-3-0-teasers/61857/36" class="inline-onebox" rel="nofollow">Ash 3.0 Teasers! - #36 by zachdaniel</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="321197" data-batch-url="/posts/batch_likers">
                        20
                      </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/ash-3-0-teasers/61857/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-321197" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="321197"
                     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="321248" data-post-id="321248">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>One suggestion regarding the accept change.</p>
<p>Since now we need to be explicit, if you resource has a bunch of attributes, the actions can easily become very verbose since you will need to explicitly add the list of attributes the action accepts.</p>
<p>One thing that we could have to help with the situation is add an option to create an explicit except list if the user so desire.</p>
<p>For example, let’s say I have the attributes <code>a</code>, <code>b</code>, <code>c</code>, …, <code>z</code> in my resource and I want to create an action that will accept all attribute except atribute <code>c</code> and <code>f</code>. It would be nice if we could do something like this:</p>
<p><code>accept {:all_except, [:c, :f]}</code></p>
<p>Granted, this will automatically make that action accept new attributes by default when they are added, but IMO that is OK since we are being explicit here instead of the old behavior where it was implicit.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="321248" 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/ash-3-0-teasers/61857/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-321248" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="321248"
                     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/61857/load_more?page=4">Load more posts (9 remaining)</a>
</div></template></turbo-stream>