mirror of
https://github.com/ash-project/ash_state_machine.git
synced 2024-09-19 12:53:20 +12:00
init
This commit is contained in:
commit
95a75b542a
22 changed files with 766 additions and 0 deletions
21
.check.exs
Normal file
21
.check.exs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[
|
||||||
|
## all available options with default values (see `mix check` docs for description)
|
||||||
|
# parallel: true,
|
||||||
|
# skipped: true,
|
||||||
|
|
||||||
|
## list of tools (see `mix check` docs for defaults)
|
||||||
|
tools: [
|
||||||
|
## curated tools may be disabled (e.g. the check for compilation warnings)
|
||||||
|
# {:compiler, false},
|
||||||
|
|
||||||
|
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
|
||||||
|
# {:credo, "mix credo --format oneline"},
|
||||||
|
|
||||||
|
{:check_formatter, command: "mix spark.formatter --check"}
|
||||||
|
|
||||||
|
## custom new tools may be added (mix tasks or arbitrary commands)
|
||||||
|
# {:my_mix_task, command: "mix release", env: %{"MIX_ENV" => "prod"}},
|
||||||
|
# {:my_arbitrary_tool, command: "npm test", cd: "assets"},
|
||||||
|
# {:my_arbitrary_script, command: ["my_script", "argument with spaces"], cd: "scripts"}
|
||||||
|
]
|
||||||
|
]
|
184
.credo.exs
Normal file
184
.credo.exs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
# This file contains the configuration for Credo and you are probably reading
|
||||||
|
# this after creating it with `mix credo.gen.config`.
|
||||||
|
#
|
||||||
|
# If you find anything wrong or unclear in this file, please report an
|
||||||
|
# issue on GitHub: https://github.com/rrrene/credo/issues
|
||||||
|
#
|
||||||
|
%{
|
||||||
|
#
|
||||||
|
# You can have as many configs as you like in the `configs:` field.
|
||||||
|
configs: [
|
||||||
|
%{
|
||||||
|
#
|
||||||
|
# Run any config using `mix credo -C <name>`. If no config name is given
|
||||||
|
# "default" is used.
|
||||||
|
#
|
||||||
|
name: "default",
|
||||||
|
#
|
||||||
|
# These are the files included in the analysis:
|
||||||
|
files: %{
|
||||||
|
#
|
||||||
|
# You can give explicit globs or simply directories.
|
||||||
|
# In the latter case `**/*.{ex,exs}` will be used.
|
||||||
|
#
|
||||||
|
included: [
|
||||||
|
"lib/",
|
||||||
|
"src/",
|
||||||
|
"test/",
|
||||||
|
"web/",
|
||||||
|
"apps/*/lib/",
|
||||||
|
"apps/*/src/",
|
||||||
|
"apps/*/test/",
|
||||||
|
"apps/*/web/"
|
||||||
|
],
|
||||||
|
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
|
||||||
|
},
|
||||||
|
#
|
||||||
|
# Load and configure plugins here:
|
||||||
|
#
|
||||||
|
plugins: [],
|
||||||
|
#
|
||||||
|
# If you create your own checks, you must specify the source files for
|
||||||
|
# them here, so they can be loaded by Credo before running the analysis.
|
||||||
|
#
|
||||||
|
requires: [],
|
||||||
|
#
|
||||||
|
# If you want to enforce a style guide and need a more traditional linting
|
||||||
|
# experience, you can change `strict` to `true` below:
|
||||||
|
#
|
||||||
|
strict: false,
|
||||||
|
#
|
||||||
|
# To modify the timeout for parsing files, change this value:
|
||||||
|
#
|
||||||
|
parse_timeout: 5000,
|
||||||
|
#
|
||||||
|
# If you want to use uncolored output by default, you can change `color`
|
||||||
|
# to `false` below:
|
||||||
|
#
|
||||||
|
color: true,
|
||||||
|
#
|
||||||
|
# You can customize the parameters of any check by adding a second element
|
||||||
|
# to the tuple.
|
||||||
|
#
|
||||||
|
# To disable a check put `false` as second element:
|
||||||
|
#
|
||||||
|
# {Credo.Check.Design.DuplicatedCode, false}
|
||||||
|
#
|
||||||
|
checks: [
|
||||||
|
#
|
||||||
|
## Consistency Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Consistency.ExceptionNames, []},
|
||||||
|
{Credo.Check.Consistency.LineEndings, []},
|
||||||
|
{Credo.Check.Consistency.ParameterPatternMatching, []},
|
||||||
|
{Credo.Check.Consistency.SpaceAroundOperators, []},
|
||||||
|
{Credo.Check.Consistency.SpaceInParentheses, []},
|
||||||
|
{Credo.Check.Consistency.TabsOrSpaces, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Design Checks
|
||||||
|
#
|
||||||
|
# You can customize the priority of any check
|
||||||
|
# Priority values are: `low, normal, high, higher`
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.AliasUsage, false},
|
||||||
|
# You can also customize the exit_status of each check.
|
||||||
|
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||||
|
# set this value to 0 (zero).
|
||||||
|
#
|
||||||
|
{Credo.Check.Design.TagTODO, [exit_status: 2]},
|
||||||
|
{Credo.Check.Design.TagFIXME, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Readability Checks
|
||||||
|
#
|
||||||
|
{Credo.Check.Readability.AliasOrder, []},
|
||||||
|
{Credo.Check.Readability.FunctionNames, []},
|
||||||
|
{Credo.Check.Readability.LargeNumbers, []},
|
||||||
|
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
|
||||||
|
{Credo.Check.Readability.ModuleAttributeNames, []},
|
||||||
|
{Credo.Check.Readability.ModuleDoc, []},
|
||||||
|
{Credo.Check.Readability.ModuleNames, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesInCondition, []},
|
||||||
|
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
|
||||||
|
{Credo.Check.Readability.PredicateFunctionNames, []},
|
||||||
|
{Credo.Check.Readability.PreferImplicitTry, []},
|
||||||
|
{Credo.Check.Readability.RedundantBlankLines, []},
|
||||||
|
{Credo.Check.Readability.Semicolons, []},
|
||||||
|
{Credo.Check.Readability.SpaceAfterCommas, []},
|
||||||
|
{Credo.Check.Readability.StringSigils, []},
|
||||||
|
{Credo.Check.Readability.TrailingBlankLine, []},
|
||||||
|
{Credo.Check.Readability.TrailingWhiteSpace, []},
|
||||||
|
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
|
||||||
|
{Credo.Check.Readability.VariableNames, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Refactoring Opportunities
|
||||||
|
#
|
||||||
|
{Credo.Check.Refactor.CondStatements, []},
|
||||||
|
{Credo.Check.Refactor.CyclomaticComplexity, false},
|
||||||
|
{Credo.Check.Refactor.FunctionArity, []},
|
||||||
|
{Credo.Check.Refactor.LongQuoteBlocks, []},
|
||||||
|
{Credo.Check.Refactor.MapInto, false},
|
||||||
|
{Credo.Check.Refactor.MatchInCondition, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
|
||||||
|
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
|
||||||
|
{Credo.Check.Refactor.Nesting, [max_nesting: 6]},
|
||||||
|
{Credo.Check.Refactor.UnlessWithElse, []},
|
||||||
|
{Credo.Check.Refactor.WithClauses, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
## Warnings
|
||||||
|
#
|
||||||
|
{Credo.Check.Warning.BoolOperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
|
||||||
|
{Credo.Check.Warning.IExPry, []},
|
||||||
|
{Credo.Check.Warning.IoInspect, []},
|
||||||
|
{Credo.Check.Warning.LazyLogging, false},
|
||||||
|
{Credo.Check.Warning.MixEnv, false},
|
||||||
|
{Credo.Check.Warning.OperationOnSameValues, []},
|
||||||
|
{Credo.Check.Warning.OperationWithConstantResult, []},
|
||||||
|
{Credo.Check.Warning.RaiseInsideRescue, []},
|
||||||
|
{Credo.Check.Warning.UnusedEnumOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedFileOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedKeywordOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedListOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedPathOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedRegexOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedStringOperation, []},
|
||||||
|
{Credo.Check.Warning.UnusedTupleOperation, []},
|
||||||
|
{Credo.Check.Warning.UnsafeExec, []},
|
||||||
|
|
||||||
|
#
|
||||||
|
# Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controversial and experimental checks (opt-in, just replace `false` with `[]`)
|
||||||
|
#
|
||||||
|
{Credo.Check.Readability.StrictModuleLayout, false},
|
||||||
|
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
|
||||||
|
{Credo.Check.Consistency.UnusedVariableNames, false},
|
||||||
|
{Credo.Check.Design.DuplicatedCode, false},
|
||||||
|
{Credo.Check.Readability.AliasAs, false},
|
||||||
|
{Credo.Check.Readability.MultiAlias, false},
|
||||||
|
{Credo.Check.Readability.Specs, false},
|
||||||
|
{Credo.Check.Readability.SinglePipe, false},
|
||||||
|
{Credo.Check.Readability.WithCustomTaggedTuple, false},
|
||||||
|
{Credo.Check.Refactor.ABCSize, false},
|
||||||
|
{Credo.Check.Refactor.AppendSingleItem, false},
|
||||||
|
{Credo.Check.Refactor.DoubleBooleanNegation, false},
|
||||||
|
{Credo.Check.Refactor.ModuleDependencies, false},
|
||||||
|
{Credo.Check.Refactor.NegatedIsNil, false},
|
||||||
|
{Credo.Check.Refactor.PipeChainStart, false},
|
||||||
|
{Credo.Check.Refactor.VariableRebinding, false},
|
||||||
|
{Credo.Check.Warning.LeakyEnvironment, false},
|
||||||
|
{Credo.Check.Warning.MapGetUnsafePass, false},
|
||||||
|
{Credo.Check.Warning.UnsafeToAtom, false}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom checks can be created using `mix credo.gen.check`.
|
||||||
|
#
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
.formatter.exs
Normal file
11
.formatter.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
spark_locals_without_parens = []
|
||||||
|
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
import_deps: [:ash],
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||||
|
locals_without_parens: spark_locals_without_parens,
|
||||||
|
export: [
|
||||||
|
locals_without_parens: spark_locals_without_parens
|
||||||
|
]
|
||||||
|
]
|
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at zach@zachdaniel.dev. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
10
.github/CONTRIBUTING.md
vendored
Normal file
10
.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Contributing to Ash
|
||||||
|
|
||||||
|
* We have a zero tolerance policy for failure to abide by our code of conduct. It is very standard, but please make sure
|
||||||
|
you have read it.
|
||||||
|
* Issues may be opened to propose new ideas, to ask questions, or to file bugs.
|
||||||
|
* Before working on a feature, please talk to the core team/the rest of the community via a proposal. We are
|
||||||
|
building something that needs to be cohesive and well thought out across all use cases. Our top priority is
|
||||||
|
supporting real life use cases like yours, but we have to make sure that we do that in a sustainable way. The
|
||||||
|
best compromise there is to make sure that discussions are centered around the *use case* for a feature, rather
|
||||||
|
than the propsed feature itself.
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug, needs review
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
-https://hexdocs.pm/ash_json_api--
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
A minimal set of resource definitions and calls that can reproduce the bug.
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
** Runtime
|
||||||
|
- Elixir version
|
||||||
|
- Erlang version
|
||||||
|
- OS
|
||||||
|
- Ash version
|
||||||
|
- any related extension versions
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
36
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
name: Proposal
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement, needs review
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Express the feature either with a change to resource syntax, or with a change to the resource interface**
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
attributes do
|
||||||
|
attribute :foo, :integer, bar: 10 # <- Adding `bar` here would cause <x>
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Api.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
### Contributor checklist
|
||||||
|
|
||||||
|
- [ ] Bug fixes include regression tests
|
||||||
|
- [ ] Features include unit/acceptance tests
|
14
.github/workflows/elixir.yml
vendored
Normal file
14
.github/workflows/elixir.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
ash-ci:
|
||||||
|
uses: ash-project/ash/.github/workflows/ash-ci.yml@main
|
||||||
|
secrets:
|
||||||
|
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
ash_fsm-*.tar
|
||||||
|
|
||||||
|
# Temporary files, for example, from tests.
|
||||||
|
/tmp/
|
2
.tool-versions
Normal file
2
.tool-versions
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
erlang 25.2.2
|
||||||
|
elixir 1.14.3
|
1
FUNDING.yml
Normal file
1
FUNDING.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
github: zachdaniel
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Zachary Scott Daniel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# AshFsm
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `ash_fsm` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:ash_fsm, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at <https://hexdocs.pm/ash_fsm>.
|
||||||
|
|
63
lib/ash_fsm.ex
Normal file
63
lib/ash_fsm.ex
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
defmodule AshFsm do
|
||||||
|
@moduledoc """
|
||||||
|
Documentation for `AshFsm`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
defmodule Event do
|
||||||
|
@moduledoc """
|
||||||
|
The configuration for an event.
|
||||||
|
"""
|
||||||
|
defstruct [:action, :from, :to]
|
||||||
|
end
|
||||||
|
|
||||||
|
@event %Spark.Dsl.Entity{
|
||||||
|
name: :event,
|
||||||
|
target: Event,
|
||||||
|
args: [:action],
|
||||||
|
identifier: :action,
|
||||||
|
schema: [
|
||||||
|
action: [
|
||||||
|
type: :atom,
|
||||||
|
doc: "The corresponding action that is invoked for the event."
|
||||||
|
],
|
||||||
|
from: [
|
||||||
|
type: {:or, [{:list, :atom}, :atom]},
|
||||||
|
doc:
|
||||||
|
"The states in which this action may be called. If not specified, then any state is accepted."
|
||||||
|
],
|
||||||
|
to: [
|
||||||
|
type: {:or, [{:list, :atom}, :atom]},
|
||||||
|
doc:
|
||||||
|
"The states that this action may move to. If not specified, then any state is accepted."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@events %Spark.Dsl.Section{
|
||||||
|
name: :events,
|
||||||
|
entities: [
|
||||||
|
@event
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@fsm %Spark.Dsl.Section{
|
||||||
|
name: :fsm,
|
||||||
|
schema: [
|
||||||
|
state_attribute: [
|
||||||
|
type: :atom,
|
||||||
|
doc: "The attribute to store the state in.",
|
||||||
|
default: :state
|
||||||
|
],
|
||||||
|
initial_states: [
|
||||||
|
type: {:or, [{:list, :atom}, :atom]},
|
||||||
|
doc:
|
||||||
|
"The allowed starting states of this state machine. If not specified, all states are allowed."
|
||||||
|
]
|
||||||
|
],
|
||||||
|
sections: [
|
||||||
|
@events
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
use Spark.Dsl.Extension, sections: [@fsm]
|
||||||
|
end
|
8
lib/info.ex
Normal file
8
lib/info.ex
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule AshFsm.Info do
|
||||||
|
use Spark.InfoGenerator, extension: AshFsm, sections: [:fsm]
|
||||||
|
|
||||||
|
@spec fsm_all_states(Ash.Resource.record() | map()) :: list(atom)
|
||||||
|
def fsm_all_states(resource_or_dsl) do
|
||||||
|
Spark.Dsl.Extension.get_persisted(resource_or_dsl, :all_fsm_states, [])
|
||||||
|
end
|
||||||
|
end
|
40
lib/transformers/fill_in_event_defaults.ex
Normal file
40
lib/transformers/fill_in_event_defaults.ex
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
defmodule AshFsm.Transformers.FillInEventDefaults do
|
||||||
|
use Spark.Dsl.Transformer
|
||||||
|
alias Spark.Dsl.Transformer
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
def transform(dsl_state) do
|
||||||
|
initial_states = List.wrap(AshFsm.Info.fsm_initial_states!(dsl_state))
|
||||||
|
|
||||||
|
events =
|
||||||
|
dsl_state
|
||||||
|
|> AshFsm.Info.fsm_events()
|
||||||
|
|
||||||
|
all_states =
|
||||||
|
events
|
||||||
|
|> Enum.flat_map(fn event ->
|
||||||
|
List.wrap(event.from) ++ List.wrap(event.to)
|
||||||
|
end)
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.concat(List.wrap(initial_states))
|
||||||
|
|
||||||
|
dsl_state =
|
||||||
|
case AshFsm.Info.fsm_initial_states(dsl_state) do
|
||||||
|
{:ok, value} when not is_nil(value) and value != [] ->
|
||||||
|
dsl_state
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Transformer.set_option(dsl_state, [:fsm], :initial_states, all_states)
|
||||||
|
end
|
||||||
|
|
||||||
|
events
|
||||||
|
|> Enum.reduce(dsl_state, fn event, dsl_state ->
|
||||||
|
Transformer.replace_entity(dsl_state, [:fsm], %{
|
||||||
|
event
|
||||||
|
| from: event.from || all_states,
|
||||||
|
to: event.to || all_states
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|> Transformer.persist(:all_fsm_states, all_states)
|
||||||
|
end
|
||||||
|
end
|
BIN
logos/small-logo.png
Normal file
BIN
logos/small-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
153
mix.exs
Normal file
153
mix.exs
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
defmodule AshFsm.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
@version "0.1.0"
|
||||||
|
|
||||||
|
@description """
|
||||||
|
An Ash.Resource extension for building finite state machines
|
||||||
|
"""
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :ash_fsm,
|
||||||
|
version: @version,
|
||||||
|
elixir: "~> 1.14",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
package: package(),
|
||||||
|
aliases: aliases(),
|
||||||
|
deps: deps(),
|
||||||
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
|
dialyzer: [plt_add_apps: [:ash]],
|
||||||
|
docs: docs(),
|
||||||
|
description: @description,
|
||||||
|
source_url: "https://github.com/ash-project/ash_fsm",
|
||||||
|
homepage_url: "https://github.com/ash-project/ash_fsm"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp package do
|
||||||
|
[
|
||||||
|
name: :ash_fsm,
|
||||||
|
licenses: ["MIT"],
|
||||||
|
files: ~w(lib .formatter.exs mix.exs README* LICENSE*
|
||||||
|
CHANGELOG* documentation),
|
||||||
|
links: %{
|
||||||
|
GitHub: "https://github.com/ash-project/ash_fsm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp elixirc_paths(:test) do
|
||||||
|
elixirc_paths(:dev) ++ ["test/support"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp elixirc_paths(_) do
|
||||||
|
["lib"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp extras() do
|
||||||
|
"documentation/**/*.md"
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> Enum.map(fn path ->
|
||||||
|
title =
|
||||||
|
path
|
||||||
|
|> Path.basename(".md")
|
||||||
|
|> String.split(~r/[-_]/)
|
||||||
|
|> Enum.map(&String.capitalize/1)
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|> case do
|
||||||
|
"F A Q" ->
|
||||||
|
"FAQ"
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
|
end
|
||||||
|
|
||||||
|
{String.to_atom(path),
|
||||||
|
[
|
||||||
|
title: title
|
||||||
|
]}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp groups_for_extras() do
|
||||||
|
"documentation/*"
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> Enum.map(fn folder ->
|
||||||
|
name =
|
||||||
|
folder
|
||||||
|
|> Path.basename()
|
||||||
|
|> String.split(~r/[-_]/)
|
||||||
|
|> Enum.map(&String.capitalize/1)
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
|
{name, folder |> Path.join("**") |> Path.wildcard()}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp docs do
|
||||||
|
[
|
||||||
|
main: "AshFsm",
|
||||||
|
source_ref: "v#{@version}",
|
||||||
|
logo: "logos/small-logo.png",
|
||||||
|
extra_section: "GUIDES",
|
||||||
|
spark: [
|
||||||
|
extensions: [
|
||||||
|
%{
|
||||||
|
module: AshFsm,
|
||||||
|
name: "AshFsm",
|
||||||
|
target: "Ash.Resource",
|
||||||
|
type: "Fsm Resource"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
module: AshGraphql.Api,
|
||||||
|
name: "AshGraphql Api",
|
||||||
|
target: "Ash.Api",
|
||||||
|
type: "GraphQL Api"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
extras: extras(),
|
||||||
|
groups_for_extras: groups_for_extras(),
|
||||||
|
groups_for_modules: [
|
||||||
|
AshGraphql: [
|
||||||
|
AshGraphql
|
||||||
|
],
|
||||||
|
Introspection: [
|
||||||
|
AshGraphql.Resource.Info,
|
||||||
|
AshGraphql.Api.Info
|
||||||
|
],
|
||||||
|
Miscellaneous: [
|
||||||
|
AshGraphql.Resource.Helpers
|
||||||
|
],
|
||||||
|
Internals: ~r/.*/
|
||||||
|
]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:ash, "~> 2.7"},
|
||||||
|
{:spark, path: "../spark", override: true}
|
||||||
|
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||||
|
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp aliases do
|
||||||
|
[
|
||||||
|
sobelow: "sobelow --skip",
|
||||||
|
credo: "credo --strict",
|
||||||
|
docs: ["docs", "ash.replace_doc_links"],
|
||||||
|
"spark.formatter": "spark.formatter --extensions AshGraphql.Resource,AshGraphql.Api"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
16
mix.lock
Normal file
16
mix.lock
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
%{
|
||||||
|
"ash": {:hex, :ash, "2.7.1", "e8915f4ebb4dbf8e8f689020430adf30f68ac2f4bd2613e416e6f69fd8f4dc69", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, "~> 1.0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b6e708fac305147f99d292823782bf8f7d2897efae7fe4fdf688e3c7cad777f"},
|
||||||
|
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
||||||
|
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||||
|
"ecto": {:hex, :ecto, "3.10.1", "c6757101880e90acc6125b095853176a02da8f1afe056f91f1f90b80c9389822", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d2ac4255f1601bdf7ac74c0ed971102c6829dc158719b94bd30041bbad77f87a"},
|
||||||
|
"elixir_make": {:hex, :elixir_make, "0.7.6", "67716309dc5d43e16b5abbd00c01b8df6a0c2ab54a8f595468035a50189f9169", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5a0569756b0f7873a77687800c164cca6dfc03a09418e6fcf853d78991f49940"},
|
||||||
|
"ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"},
|
||||||
|
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
||||||
|
"nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"},
|
||||||
|
"picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
|
||||||
|
"sourceror": {:hex, :sourceror, "0.12.2", "2ae55efd149193572e0eb723df7c7a1bda9ab33c43373c82642931dbb2f4e428", [:mix], [], "hexpm", "7ad74ade6fb079c71f29fae10c34bcf2323542d8c51ee1bcd77a546cfa89d59c"},
|
||||||
|
"spark": {:hex, :spark, "1.0.8", "bc5c7db652b6adca5c26e79b70d000c96864d9a30a7b8030aa3137ed86bad3df", [:mix], [{:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "0ce14815466ac5c5a1cbdf742f85cab6369d3f25bda13ea7dde80e3a9bd699fd"},
|
||||||
|
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
|
||||||
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
|
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
|
||||||
|
}
|
31
test/ash_fsm_test.exs
Normal file
31
test/ash_fsm_test.exs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
defmodule AshFsmTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest AshFsm
|
||||||
|
|
||||||
|
defmodule TwoStates do
|
||||||
|
use Ash.Resource,
|
||||||
|
data_layer: Ash.DataLayer.Ets,
|
||||||
|
extensions: [AshFsm]
|
||||||
|
|
||||||
|
fsm do
|
||||||
|
initial_states([:pending])
|
||||||
|
|
||||||
|
events do
|
||||||
|
event(:begin, from: :pending, to: :executing)
|
||||||
|
event(:complete, from: :executing, to: :complete)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ets do
|
||||||
|
private? true
|
||||||
|
end
|
||||||
|
|
||||||
|
attributes do
|
||||||
|
uuid_primary_key :id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
IO.inspect(AshFsm.Info.fsm_all_states(TwoStates))
|
||||||
|
end
|
||||||
|
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
Loading…
Reference in a new issue