ash icon indicating copy to clipboard operation
ash copied to clipboard

Calculations: Add `field?: false` option to exclude from Resource struct fields

Open chazwatkins opened this issue 5 months ago • 3 comments

Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

AI Policy

  • [x] I agree to follow this project's AI Policy, or I agree that AI was not used while creating this issue.

Is your feature request related to a problem? Please describe.

Currently, all calculations defined on a resource become struct fields. Sometimes you want to create calculation that are only meant to be composed with other calculations within the resource and don't need to be included in the struct as fields.

Describe the solution you'd like

Introduce a field?: false option (defaulting to true) that:

  • Excludes the calculation from the resource struct fields. Reducing struct field clutter.
  • Prevents the calculation from being directly loadable via queries
  • Still allows the calculation to be referenced in expressions within the resource
  • Maintains all existing calculation functionality

Example

calculate :average_things, :float, expr(sum_of_things / count_of_things)

calculate :count_of_things, :integer, expr(count(things)) do
  field? false
end

calculate :sum_of_things, :integer, expr(sum(things, field: :value)) do
  field? false
end

In this example:

  • Only :average_things would be loadable and appear in the struct
  • :count_of_things and :sum_of_things remain available for internal composition

Describe alternatives you've considered

No response

Additional context

Suggested by Zach in Discord as a neat feature to support

chazwatkins avatar Jul 31 '25 14:07 chazwatkins

I'm taking this one

chazwatkins avatar Jul 31 '25 14:07 chazwatkins

Adding some thoughts on the solution.

Adding the DSL and excluding the calculation from the schema generation is straightforward.

Making it non-loadable but usable in expressions is going to be tricky for a couple reasons.

  1. The internals are using the public 'Ash.load', so there's not a good way to validate without affecting the internal usages. May have to move the 'Ash.load' logic into a separate module so validation logic can be put on the public interface.
  2. Accessing the calculations currently expect it to be on the struct, so have to figure out how allow it in the map for internal logic and strip it before it's returned to the caller.

I think it's achievable, but the PR will be large. I need to figure out how to break this into multiple PRs so it can be merged incrementally before we "release" the feature. Meaning it can be "dark-released" but not visible to users until it is fully ready. I don't want to have a massive PR that will take forever to review and cause unnecessary risk.

chazwatkins avatar Aug 02 '25 18:08 chazwatkins

That makes sense to me, thank you for looking into it 🔥

zachdaniel avatar Aug 03 '25 02:08 zachdaniel