{"id":23330378,"url":"https://github.com/curiosum-dev/contexted","last_synced_at":"2025-04-13T04:59:32.899Z","repository":{"id":167706891,"uuid":"622973758","full_name":"curiosum-dev/contexted","owner":"curiosum-dev","description":"Elixir library designed to help you manage the complexity of Phoenix contexts. It offers tools for module separation, subcontext creation, and auto-generating CRUD operations.","archived":false,"fork":false,"pushed_at":"2025-04-07T09:46:16.000Z","size":145,"stargazers_count":72,"open_issues_count":6,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T04:59:21.995Z","etag":null,"topics":["elixir","elixir-lang","elixircontext","phoenix","phoenix-framework"],"latest_commit_sha":null,"homepage":"https://curiosum.com","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/curiosum-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-03T12:48:59.000Z","updated_at":"2025-04-07T09:46:19.000Z","dependencies_parsed_at":"2023-10-23T13:36:14.036Z","dependency_job_id":"c598348d-27e5-4058-8807-add914337bdc","html_url":"https://github.com/curiosum-dev/contexted","commit_stats":null,"previous_names":["curiosum-dev/contexted"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curiosum-dev%2Fcontexted","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curiosum-dev%2Fcontexted/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curiosum-dev%2Fcontexted/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/curiosum-dev%2Fcontexted/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/curiosum-dev","download_url":"https://codeload.github.com/curiosum-dev/contexted/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665758,"owners_count":21142123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","elixir-lang","elixircontext","phoenix","phoenix-framework"],"created_at":"2024-12-20T22:16:48.921Z","updated_at":"2025-04-13T04:59:32.864Z","avatar_url":"https://github.com/curiosum-dev.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/f0352656-397d-4d90-999a-d3adbae1095f\"\u003e\n\n  \u003ch1\u003eContexted\u003c/h1\u003e\n  \u003cp\u003e\u003cstrong\u003eTools for clean, maintainable Elixir \u0026 Phoenix contexts\u003c/strong\u003e\u003c/p\u003e\n\n  [![Contact Us](https://img.shields.io/badge/Contact%20Us-%23F36D2E?style=for-the-badge\u0026logo=maildotru\u0026logoColor=white\u0026labelColor=F36D2E)](https://curiosum.com/contact)\n  [![Visit Curiosum](https://img.shields.io/badge/Visit%20Curiosum-%236819E6?style=for-the-badge\u0026logo=elixir\u0026logoColor=white\u0026labelColor=6819E6)](https://curiosum.com/services/elixir-software-development)\n  [![License: MIT](https://img.shields.io/badge/License-MIT-1D0642?style=for-the-badge\u0026logo=open-source-initiative\u0026logoColor=white\u0026labelColor=1D0642)](https://github.com/curiosum-dev/contexted/blob/main/LICENSE)\n\u003c/div\u003e\n\n\n\u003cbr/\u003e\n\n## 📦 Short overview\n\n[Contexts](https://hexdocs.pm/phoenix/contexts.html) in Elixir \u0026 Phoenix are getting complicated over time.\nCross-referencing, big modules and repetitiveness are the most common reasons for this problem.\n\nContexted arms you with a set of tools to maintain contexts well.\n\n\u003cdiv\u003e\n  \u003ca href=\"https://github.com/curiosum-dev/contexted/actions/workflows/ci.yml\"\u003e\n    \u003cimg alt=\"CI Status\" src=\"https://github.com/curiosum-dev/contexted/actions/workflows/ci.yml/badge.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://hex.pm/packages/contexted\"\u003e\n    \u003cimg alt=\"Hex Version\" src=\"https://img.shields.io/hexpm/v/contexted.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://hexdocs.pm/contexted\"\u003e\n    \u003cimg alt=\"Hex Docs\" src=\"http://img.shields.io/badge/hex.pm-docs-green.svg?style=flat\"\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cbr/\u003e\n\n---\n\n_Note: Official documentation for contexted library is [available on hexdocs][hexdoc]._\n\n[hexdoc]: https://hexdocs.pm/contexted\n\n---\n\n\u003cbr/\u003e\n\n## 📚 Table of Contents\n\n- [Features](#-features)\n- [Installation](#-installation)\n- [Step by step overview](#-step-by-step-overview)\n  - [Keep contexts separate](#keep-contexts-separate)\n    - [Exclude files and folders from check context cross-references](#exclude-files-and-folders-from-cross-references-context-check)\n  - [Dividing each context into smaller parts](#dividing-each-context-into-smaller-parts)\n    - [Being able to access docs and specs in auto-delegated functions](#being-able-to-access-docs-and-specs-in-auto-delegated-functions)\n  - [Don't repeat yourself with CRUD operations](#dont-repeat-yourself-with-crud-operations)\n- [Contributing](#-contributing)\n- [License](#-license)\n\n\u003cbr/\u003e\n\n## ✨ Features\n\n- `Contexted.Tracer` - trace and enforce definite separation between specific context modules.\n- `Contexted.Delegator` - divide the big context module into smaller parts and use delegations to build the final context.\n- `Contexted.CRUD` - auto-generate the most common CRUD operations whenever needed.\n\n\u003cbr/\u003e\n\n## 📥 Installation\n\nAdd the following to your `mix.exs` file:\n\n```elixir\ndefp deps do\n  [\n    {:contexted, \"~\u003e 0.3.4\"}\n  ]\nend\n```\n\nThen run `mix deps.get`.\n\n\u003cbr/\u003e\n\n## 🧭 Step by step overview\n\nTo describe a sample usage of this library, let's assume that your project has three contexts:\n\n- `Account`\n- `Subscription`\n- `Blog`\n\nOur goal, as the project grows, is to:\n\n1. Keep contexts separate and not create any cross-references. For this to work, we'll raise errors during compilation whenever such a cross-reference happens.\n2. Divide each context into smaller parts so that it is easier to maintain. In this case, we'll refer to each of these parts as **Subcontext**. It's not a new term added to the Phoenix framework but rather a term proposed to emphasize that it's a subset of Context. For this to work, we'll use delegates.\n3. Not repeat ourselves with common business logic operations. For this to work, we'll be using CRUD functions generator, since these are the most common.\n\n\u003cbr/\u003e\n\n### Keep contexts separate\n\nIt's very easy to monitor cross-references between context modules with the `contexted` library.\n\nFirst, add `contexted` as one of the compilers in _mix.exs_:\n\n```elixir\ndef project do\n  [\n    ...\n    compilers: [:contexted] ++ Mix.compilers(),\n    ...\n  ]\nend\n```\n\nNext, define a list of contexts available in the app inside config file:\n\n```elixir\nconfig :contexted, contexts: [\n  # list of context modules goes here, for instance:\n  # [App.Account, App.Subscription, App.Blog]\n]\n```\n\nAnd that's it. From now on, whenever you will cross-reference one context with another, you will see an error raised during compilation. Here is an example of such an error:\n\n```\n== Compilation error in file lib/app/accounts.ex ==\n** (RuntimeError) You can't reference App.Blog context within App.Accounts context.\n```\n\nRead more about `Contexted.Tracer` and its options in [docs](https://hexdocs.pm/contexted/Contexted.Tracer.html).\n\n\u003cbr/\u003e\n\n#### Exclude files and folders from cross-references context check\n\nIn special cases, you may need to exclude certain folders or files from cross-reference checks due to project structure or naming conventions. To do this, add a list of exclusions in config `exclude_paths` option:\n\n```elixir\nconfig :contexted,\n  exclude_paths: [\"app/test\"]\n```\n\n\u003cbr/\u003e\n\n### Dividing each context into smaller parts\n\nTo divide big Context into smaller Subcontexts, we can use `delegate_all/1` macro from `Contexted.Delegator` module.\n\nLet's assume that the `Account` context has `User`, `UserToken` and `Admin` resources. Here is how we can split the context module:\n\n```elixir\n# Users subcontext\n\ndefmodule App.Account.Users do\n  def get_user(id) do\n    ...\n  end\nend\n\n# UserTokens subcontext\n\ndefmodule App.Account.UserTokens do\n  def get_user_token(id) do\n    ...\n  end\nend\n\n# Admins subcontext\n\ndefmodule App.Account.Admins do\n  def get_admin(id) do\n    ...\n  end\nend\n\n# Account context\n\ndefmodule App.Account do\n  import Contexted.Delegator\n\n  delegate_all App.Account.Users\n  delegate_all App.Account.UserTokens\n  delegate_all App.Account.Admins\nend\n```\n\nFrom now on, you can treat the `Account` context module as the API for the \"outside\" world.\n\nInstead of calling:\n\n```elixir\nApp.Account.Users.find_user(1)\n```\n\nYou will simply do:\n\n```elixir\nApp.Account.find_user(1)\n```\n\n\u003cbr/\u003e\n\n#### Being able to access docs and specs in auto-delegated functions\n\nBoth docs and specs are attached as metadata of module once it's compiled and saved as `.beam`. In reference to the example of `App.Account` context, it's possible that `App.Account.Users` will not be saved in `.beam` file before the `delegate_all` macro is executed. Therefore, first, all of the modules have to be compiled, and saved to `.beam` and only then we can create `@doc` and `@spec` of each delegated function.\n\nAs a workaround, in `Contexted.Tracer.after_compiler/1` all of the contexts `.beam` files are first deleted and then recompiled. This is an opt-in functionality, as it extends compilation time. If you want to enable it, set the following config values:\n\n```elixir\nconfig :contexted,\n  app: :your_app_name, # replace 'your_app_name' with your real app name\n  enable_recompilation: true\n```\n\nYou may also want to enable it only for certain environments, like `dev`.\n\n*Please also note that when this functionality is enabled, during the recompilation process, warnings are temporarily silenced to avoid logging conflict warnings. It will still log warnings as intended, during the first compilation, therefore it won't have any affect on normal compilation flow.*\n\nRead more about `Contexted.Delegator` and its options in [docs](https://hexdocs.pm/contexted/Contexted.Delegator.html).\n\n\u003cbr/\u003e\n\n### Don't repeat yourself with CRUD operations\n\nIn most web apps CRUD operations are very common. Most of these, have the same pattern. Why not autogenerate them?\n\nHere is how you can generate common CRUD operations for `App.Account.Users`:\n\n```elixir\ndefmodule App.Account.Users do\n  use Contexted.CRUD,\n    repo: App.Repo,\n    schema: App.Accounts.User\nend\n```\n\nThis will generate the following functions:\n\n```elixir\niex\u003e App.Accounts.Users.__info__(:functions)\n[\n  change_user: 1,\n  change_user: 2,\n  create_user: 0,\n  create_user: 1,\n  create_user!: 0,\n  create_user!: 1,\n  delete_user: 1,\n  delete_user!: 1,\n  get_user: 1,\n  get_user!: 1,\n  list_users: 0,\n  update_user: 1,\n  update_user: 2,\n  update_user!: 1,\n  update_user!: 2\n]\n```\n\n\nRead more about `Contexted.CRUD` and its options in [docs](https://hexdocs.pm/contexted/Contexted.CRUD.html).\n\n\u003cbr/\u003e\n\n# 🤝 Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n\u003cbr/\u003e\n\n# 📄 License\n\n[Curiosum](https://curiosum.com)\n\nDistributed under the MIT License. See [LICENSE](LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuriosum-dev%2Fcontexted","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcuriosum-dev%2Fcontexted","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuriosum-dev%2Fcontexted/lists"}