commit a113f43196c87c987885403a2bcd7d4735622dd1 Author: Zach Daniel Date: Mon Nov 27 11:21:17 2023 -0500 init diff --git a/.check.exs b/.check.exs new file mode 100644 index 0000000..4fc0524 --- /dev/null +++ b/.check.exs @@ -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"}, + + {:doctor, false} + + ## 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"} + ] +] diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 0000000..25624ef --- /dev/null +++ b/.credo.exs @@ -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 `. 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, [max_arity: 13]}, + {Credo.Check.Refactor.LongQuoteBlocks, false}, + {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`. + # + ] + } + ] +} diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7aa6f74 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -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 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..1253191 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -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. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..a592354 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -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_graphql-- + +**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. diff --git a/.github/ISSUE_TEMPLATE/proposal.md b/.github/ISSUE_TEMPLATE/proposal.md new file mode 100644 index 0000000..f347dcb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/proposal.md @@ -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 + end +``` + +Or + +```elixir + Api.read(:resource, bar: 10) # <- Adding `bar` here would cause +``` + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..8c13744 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,4 @@ +### Contributor checklist + +- [ ] Bug fixes include regression tests +- [ ] Features include unit/acceptance tests diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml new file mode 100644 index 0000000..0f6cfe0 --- /dev/null +++ b/.github/workflows/elixir.yml @@ -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 }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..843b623 --- /dev/null +++ b/.gitignore @@ -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_money-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..44acbf0 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +erlang 26.0.2 +elixir 1.15.4 diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..b98cb0d --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +github: zachdaniel \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4eb51a5 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfa66f9 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# AshMoney + +A money extension for Ash. diff --git a/documentation/tutorials/get-started-with-ash-money.md b/documentation/tutorials/get-started-with-ash-money.md new file mode 100644 index 0000000..c8c64e7 --- /dev/null +++ b/documentation/tutorials/get-started-with-ash-money.md @@ -0,0 +1,50 @@ +# Getting Started With AshMoney + +The primary thing that AshMoney provides is `AshMoney.Types.Money`. This is backed by `ex_money`. You can use it out of the box like so: + +```elixir +attribute :balance, AshMoney.Types.Money +``` + +Or you can add it to your compile time list of types for easier reference: + +```elixir +config :ash, :custom_types, money: AshMoney.Types.Money +``` + +Then compile ash again, `mix deps.compile ash --force`, and refer to it like so: + +```elixir +attribute :balance, :money +``` + +## AshPostgres Support + +Thanks to `ex_money_sql`, there are excellent tools for lowering support for money into your postgres database. This allows for things like aggregates that sum amounts, and referencing money in expressions: + +```elixir +sum :sum_of_usd_balances, :accounts, :balance do + filter expr(fragment("(?).currency_code", balance).currency_code == "USD") +end +``` + +To install it, add `AshMoney.AshPostgresExtension` to your list of `installed_extensions` in your repo, and generate migrations. + +```elixir +defmodule YourRepo do + def installed_extensions do + [..., AshMoney.AshPostgresExtension] + end +end +``` + +## AshGraphql Support + +Add the following to your schema file: + +```elixir + object :money do + field(:amount, non_null(:decimal)) + field(:currency, non_null(:string)) + end +``` \ No newline at end of file diff --git a/lib/ash_money.ex b/lib/ash_money.ex new file mode 100644 index 0000000..112b3bf --- /dev/null +++ b/lib/ash_money.ex @@ -0,0 +1,8 @@ +defmodule AshMoney do + @moduledoc """ + `AshMoney` provides a type for working with money in your Ash resources. + + It also provides an `AshPostgres.Extension` that can be used to add support for + money types and operators to your Postgres database. + """ +end diff --git a/lib/ash_money/ash_postgres_extension.ex b/lib/ash_money/ash_postgres_extension.ex new file mode 100644 index 0000000..0c79ab5 --- /dev/null +++ b/lib/ash_money/ash_postgres_extension.ex @@ -0,0 +1,26 @@ +if Code.ensure_loaded?(AshPostgres.CustomExtension) do + defmodule AshMoney.AshPostgresExtension do + @moduledoc """ + Installs the `money_with_currency` type and operators/functions for Postgres. + """ + use AshPostgres.CustomExtension, name: :ash_money, latest_version: 1 + + def install(1) do + """ + #{Money.DDL.execute_each(Money.DDL.create_money_with_currency())} + #{Money.DDL.execute_each(Money.DDL.define_plus_operator())} + #{Money.DDL.execute_each(Money.DDL.define_minmax_functions())} + #{Money.DDL.execute_each(Money.DDL.define_sum_function())} + """ + end + + def uninstall(1) do + """ + #{Money.DDL.execute_each(Money.DDL.drop_sum_function())} + #{Money.DDL.execute_each(Money.DDL.drop_minmax_functions())} + #{Money.DDL.execute_each(Money.DDL.drop_plus_operator())} + #{Money.DDL.execute_each(Money.DDL.drop_money_with_currency())} + """ + end + end +end diff --git a/lib/ash_money/types/money.ex b/lib/ash_money/types/money.ex new file mode 100644 index 0000000..921e7f9 --- /dev/null +++ b/lib/ash_money/types/money.ex @@ -0,0 +1,103 @@ +defmodule AshMoney.Types.Money do + @moduledoc """ + A money type for Ash that uses the `ex_money` library. + """ + use Ash.Type + + @impl Ash.Type + def constraints do + [ + storage_type: [ + type: :atom, + default: :money_with_currency, + doc: "The storage type for the money value. Can be `:money_with_currency` or `:map`." + ] + ] + end + + @impl true + def cast_in_query?(constraints) do + Keyword.get(constraints, :storage_type, :money_with_currency) == :money_with_currency + end + + @impl Ash.Type + def storage_type(constraints) do + if constraints[:items] do + Keyword.get(constraints[:items], :storage_type, :money_with_currency) + else + Keyword.get(constraints, :storage_type, :money_with_currency) + end + end + + @impl Ash.Type + def cast_input(nil, _constraints), do: {:ok, nil} + + def cast_input({amount, currency}, constraints) do + case Money.new(amount, currency) do + {:error, error} -> {:error, error} + money -> cast_input(money, constraints) + end + end + + def cast_input(value, constraints) do + if constraints[:storage_type] == :map do + Money.Ecto.Map.Type.cast(value) + else + Money.Ecto.Composite.Type.cast(value) + end + end + + @impl Ash.Type + def cast_stored(nil, _), do: {:ok, nil} + def cast_stored(%Money{} = value, _), do: {:ok, value} + + def cast_stored({_amount, _currency} = value, _constraints), + do: Money.Ecto.Composite.Type.load(value) + + def cast_stored(value, _constraints) do + Money.Ecto.Map.Type.load(value) + end + + @impl Ash.Type + def dump_to_embedded(nil, _constraints), do: {:ok, nil} + + def dump_to_embedded(value, _constraints) do + Money.Ecto.Map.Type.dump(value) + end + + @impl Ash.Type + def dump_to_native(nil, _constraints), do: {:ok, nil} + + def dump_to_native(value, constraints) do + if constraints[:storage_type] == :map do + Money.Ecto.Map.Type.dump(value) + else + Money.Ecto.Composite.Type.dump(value) + end + end + + if Code.ensure_loaded?(AshGraphql.Type) do + @behaviour AshGraphql.Type + @spec graphql_type(term()) :: atom() + def graphql_type(_), do: :money + + @spec graphql_input_type(term()) :: atom() + def graphql_input_type(_), do: :money_input + end + + if Code.ensure_loaded?(AshPostgres.Type) do + @behaviour AshPostgres.Type + + @impl AshPostgres.Type + def value_to_postgres_default(__MODULE__, constraints, %Money{ + amount: amount, + currency: currency + }) do + if Keyword.get(constraints, :storage_type, :money_with_currency) == :map do + {:ok, ~s[fragment("'{\\"amount\\": #{amount}, \\"currency\\": \\"#{currency}\\"}'")]} + else + {:ok, ~s[fragment("('#{currency}', #{amount})")]} + end + end + end +end diff --git a/logos/.gitkeep b/logos/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/logos/.gitkeep @@ -0,0 +1 @@ + diff --git a/logos/ascii-art.ans b/logos/ascii-art.ans new file mode 100644 index 0000000..61ac852 --- /dev/null +++ b/logos/ascii-art.ans @@ -0,0 +1,27 @@ +                                        (                                       +                                      *(((                                      +                                     (((((((                                    +                                   ((((((((((                                   +                                  (((((((((((((                                 +                                ((((((((((((((((/                               +                               (((((((((((((((((((                              +                              ((((((((((((((((((((((                            +                            (##((((((((((((((((((((((                           +                          #(###### ((((((((((((((((((((                         +                        (# ##########(((((((((((((((((((.                       +                       ## ############# ((((((((((((((((((                      +                     ###*#################,((((((((((((((((,                    +                    ######################## ((((((((((((((((                   +                  ######################### ##  (((((((((((((((                 +                ,#####(##################.#####/##((((((((((((((                +               ######.################# #########(## (((((((((((((              +             ####### ################ ############ ####*(((((((((((             +            #######.############### ##################### (((((((((((           +          #######################,#*((/,#############.######*(((((((((*         +        .###################### ###((((((( ###########.####### (((((((((        +       ######################.####/((((((((((( ################## (((((((*      +     /##########.##########/###### ((((((((((((((/####### #########/(((((((     +    ########### #########,######## (((((((((((((((((( ################ ((((((   +  ############ ########,##########.(((((((((((((((((((((*%##,############/((((, + ############ #######(############((((((((((((((((((((((((((  ############# (((( + \ No newline at end of file diff --git a/logos/ash-auth-logo.svg b/logos/ash-auth-logo.svg new file mode 100644 index 0000000..d60bff0 --- /dev/null +++ b/logos/ash-auth-logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/logos/ash-colored-wordmark.png b/logos/ash-colored-wordmark.png new file mode 100644 index 0000000..dee4c1e Binary files /dev/null and b/logos/ash-colored-wordmark.png differ diff --git a/logos/ash-colored-wordmark.svg b/logos/ash-colored-wordmark.svg new file mode 100644 index 0000000..60564e4 --- /dev/null +++ b/logos/ash-colored-wordmark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/logos/ash_logo_black.svg b/logos/ash_logo_black.svg new file mode 100644 index 0000000..88af35e --- /dev/null +++ b/logos/ash_logo_black.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logos/ash_logo_orange.svg b/logos/ash_logo_orange.svg new file mode 100644 index 0000000..e128b44 --- /dev/null +++ b/logos/ash_logo_orange.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logos/ash_logo_pride.svg b/logos/ash_logo_pride.svg new file mode 100644 index 0000000..a5d1f1e --- /dev/null +++ b/logos/ash_logo_pride.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logos/ash_logo_trans.svg b/logos/ash_logo_trans.svg new file mode 100644 index 0000000..97e49b9 --- /dev/null +++ b/logos/ash_logo_trans.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logos/ash_logo_white.svg b/logos/ash_logo_white.svg new file mode 100644 index 0000000..de44d69 --- /dev/null +++ b/logos/ash_logo_white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logos/ashley.png b/logos/ashley.png new file mode 100644 index 0000000..d9564f7 Binary files /dev/null and b/logos/ashley.png differ diff --git a/logos/ashley.svg b/logos/ashley.svg new file mode 100644 index 0000000..91b42a1 --- /dev/null +++ b/logos/ashley.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logos/cropped-for-header-black-text.png b/logos/cropped-for-header-black-text.png new file mode 100644 index 0000000..118c318 Binary files /dev/null and b/logos/cropped-for-header-black-text.png differ diff --git a/logos/cropped-for-header-white-text.png b/logos/cropped-for-header-white-text.png new file mode 100644 index 0000000..b003939 Binary files /dev/null and b/logos/cropped-for-header-white-text.png differ diff --git a/logos/logo-black-text.png b/logos/logo-black-text.png new file mode 100644 index 0000000..536e3d4 Binary files /dev/null and b/logos/logo-black-text.png differ diff --git a/logos/logo-only.png b/logos/logo-only.png new file mode 100644 index 0000000..cf6366e Binary files /dev/null and b/logos/logo-only.png differ diff --git a/logos/logo-white-text.png b/logos/logo-white-text.png new file mode 100644 index 0000000..9180074 Binary files /dev/null and b/logos/logo-white-text.png differ diff --git a/logos/small-logo.png b/logos/small-logo.png new file mode 100644 index 0000000..9fc9aa1 Binary files /dev/null and b/logos/small-logo.png differ diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..f59f7d5 --- /dev/null +++ b/mix.exs @@ -0,0 +1,178 @@ +defmodule AshMoney.MixProject do + use Mix.Project + + @version "0.1.0" + + @description """ + Money extension for Ash. + """ + + def project do + [ + app: :ash_money, + version: @version, + elixir: "~> 1.15", + start_permanent: Mix.env() == :prod, + deps: deps(), + aliases: aliases(), + package: package(), + elixirc_paths: elixirc_paths(Mix.env()), + dialyzer: [plt_add_apps: [:ash]], + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + coveralls: :test, + "coveralls.github": :test + ], + docs: docs(), + description: @description, + source_url: "https://github.com/ash-project/ash_money", + homepage_url: "https://github.com/ash-project/ash_money" + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + defp package do + [ + name: :ash_money, + licenses: ["MIT"], + files: ~w(lib .formatter.exs mix.exs README* LICENSE* + CHANGELOG* documentation), + links: %{ + GitHub: "https://github.com/ash-project/ash_money" + } + ] + end + + defp elixirc_paths(:test) do + elixirc_paths(:dev) ++ ["test/support"] + end + + defp elixirc_paths(_) do + ["lib"] + end + + defp extras() do + "documentation/**/*.{md,livemd,cheatmd}" + |> Path.wildcard() + |> Enum.map(fn path -> + title = + path + |> Path.basename(".md") + |> Path.basename(".livemd") + |> Path.basename(".cheatmd") + |> String.split(~r/[-_]/) + |> Enum.map_join(" ", &capitalize/1) + |> case do + "F A Q" -> + "FAQ" + + other -> + other + end + + {String.to_atom(path), + [ + title: title + ]} + end) + end + + defp capitalize(string) do + string + |> String.split(" ") + |> Enum.map(fn string -> + [hd | tail] = String.graphemes(string) + String.capitalize(hd) <> Enum.join(tail) + end) + end + + defp groups_for_extras() do + [ + Tutorials: [ + ~r'documentation/tutorials' + ], + "How To": ~r'documentation/how_to', + Topics: ~r'documentation/topics', + DSLs: ~r'documentation/dsls' + ] + end + + defp docs do + [ + main: "get-started-with-ash-money", + source_ref: "v#{@version}", + logo: "logos/small-logo.png", + extra_section: "GUIDES", + before_closing_head_tag: fn type -> + if type == :html do + """ + + """ + end + end, + extras: extras(), + groups_for_extras: groups_for_extras(), + groups_for_modules: [ + AshMoney: [ + AshMoney, + AshMoney.Types.Money + ], + AshPostgres: [ + AshMoney.AshPostgresExtension + ], + Internals: ~r/.*/ + ] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:ash, ash_version("~> 2.0")}, + {:ex_money, "~> 5.15"}, + {:ex_money_sql, "~> 1.0", optional: true}, + {:ex_doc, github: "elixir-lang/ex_doc", only: [:dev, :test], runtime: false}, + {:ex_check, "~> 0.12", only: [:dev, :test]}, + {:credo, ">= 0.0.0", only: [:dev, :test], runtime: false}, + {:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false}, + {:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false}, + {:git_ops, "~> 2.5", only: [:dev, :test]}, + {:excoveralls, "~> 0.13", only: [:dev, :test]}, + {:mix_test_watch, "~> 1.0", only: :dev, runtime: false} + ] + end + + defp aliases do + [ + sobelow: "sobelow --skip", + credo: "credo --strict", + docs: [ + "docs", + "spark.replace_doc_links" + ] + ] + end + + defp ash_version(default_version) do + case System.get_env("ASH_VERSION") do + nil -> default_version + "local" -> [path: "../ash"] + "main" -> [git: "https://github.com/ash-project/ash.git"] + version -> "~> #{version}" + end + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..f2b7356 --- /dev/null +++ b/mix.lock @@ -0,0 +1,44 @@ +%{ + "ash": {:hex, :ash, "2.17.4", "b672ab9bcd7351e9adb3bf332655b6c777bab6c45e7cb88b3a89117f6a200837", [: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: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [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.1.50 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "941d9edf1ba3511e3a3c40711de168ee476197fa0615f15b16548178e1d50e97"}, + "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, + "cldr_utils": {:hex, :cldr_utils, "2.24.2", "364fa30be55d328e704629568d431eb74cd2f085752b27f8025520b566352859", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "3362b838836a9f0fa309de09a7127e36e67310e797d556db92f71b548832c7cf"}, + "comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"}, + "credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"}, + "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, + "dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"}, + "digital_token": {:hex, :digital_token, "0.6.0", "13e6de581f0b1f6c686f7c7d12ab11a84a7b22fa79adeb4b50eec1a2d278d258", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2455d626e7c61a128b02a4a8caddb092548c3eb613ac6f6a85e4cbb6caddc4d1"}, + "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 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", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"}, + "ecto_sql": {:hex, :ecto_sql, "3.11.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"}, + "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, + "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, + "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, + "ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"}, + "ex_cldr": {:hex, :ex_cldr, "2.37.5", "9da6d97334035b961d2c2de167dc6af8cd3e09859301a5b8f49f90bd8b034593", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "74ad5ddff791112ce4156382e171a5f5d3766af9d5c4675e0571f081fe136479"}, + "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.15.1", "e92ba17c41e7405b7784e0e65f406b5f17cfe313e0e70de9befd653e12854822", [:mix], [{:ex_cldr, "~> 2.34", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "31df8bd37688340f8819bdd770eb17d659652078d34db632b85d4a32864d6a25"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.32.3", "b631ff94c982ec518e46bf4736000a30a33d6b58facc085d5f240305f512ad4a", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, ">= 2.14.2", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7b626ff1e59a0ec9c3c5db5ce9ca91a6995e2ab56426b71f3cbf67181ea225f5"}, + "ex_doc": {:git, "https://github.com/elixir-lang/ex_doc.git", "ae05a4f0791ce5463e28595ff39407b45d2b8b5c", []}, + "ex_money": {:hex, :ex_money, "5.15.2", "660139ab73313c2c9ca9b7b4496bbee62e6b0ba87780d2a9af135e0e8a2c6feb", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:gringotts, "~> 1.1", [hex: :gringotts, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.0 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "59b96d83afa69070c595c9afd78ed0802e211c8741a5644e05ee44567f480845"}, + "ex_money_sql": {:hex, :ex_money_sql, "1.10.1", "5c0981b29de1e6cd4ef06b5480ceb08525620d9b4939caa52eb1a0deefc7c71a", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ex_money, "~> 5.7", [hex: :ex_money, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "75439d10272369a38fc52bb1dc251982d65aca77a0f3ce422e8927f782df43f9"}, + "excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"}, + "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"}, + "git_ops": {:hex, :git_ops, "2.6.0", "e0791ee1cf5db03f2c61b7ebd70e2e95cba2bb9b9793011f26609f22c0900087", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "b98fca849b18aaf490f4ac7d1dd8c6c469b0cc3e6632562d366cab095e666ffe"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.1.1", "eee6fc570d77ad6851c7bc08de420a47fd1e449ef5ccfa6a77ef68b72e7e51ad", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "f82262b54dee533467021723892e15c3267349849f1f737526523ecba4e6baae"}, + "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"}, + "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, + "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, + "sourceror": {:hex, :sourceror, "0.14.1", "c6fb848d55bd34362880da671debc56e77fd722fa13b4dcbeac89a8998fc8b09", [:mix], [], "hexpm", "8b488a219e4c4d7d9ff29d16346fd4a5858085ccdd010e509101e226bbfd8efc"}, + "spark": {:hex, :spark, "1.1.51", "8458de5abbb89d18dd5c9235dd39e3757076eba84a5078d1cdc2c1e23c39aa95", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "ed8410aa8db08867b8fff3d65e54deeb7f6f6cf2b8698fc405a386c1c7a9e4f0"}, + "stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"}, +} diff --git a/test/ash_money_test.exs b/test/ash_money_test.exs new file mode 100644 index 0000000..da506d7 --- /dev/null +++ b/test/ash_money_test.exs @@ -0,0 +1,4 @@ +defmodule AshMoneyTest do + use ExUnit.Case + doctest AshMoney +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()