{"id":21926991,"url":"https://github.com/rzane/contextual","last_synced_at":"2025-06-16T10:36:57.238Z","repository":{"id":57485691,"uuid":"122544638","full_name":"rzane/contextual","owner":"rzane","description":":rainbow: Generate your Ecto contexts using this macro and eliminate boilerplate","archived":false,"fork":false,"pushed_at":"2018-03-26T20:09:54.000Z","size":42,"stargazers_count":19,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-29T10:50:35.128Z","etag":null,"topics":["context","ecto","elixir","generate","phoenix"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rzane.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-22T22:48:08.000Z","updated_at":"2024-07-17T07:50:15.000Z","dependencies_parsed_at":"2022-09-08T18:00:39.352Z","dependency_job_id":null,"html_url":"https://github.com/rzane/contextual","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fcontextual","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fcontextual/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fcontextual/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fcontextual/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rzane","download_url":"https://codeload.github.com/rzane/contextual/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248728266,"owners_count":21152190,"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":["context","ecto","elixir","generate","phoenix"],"created_at":"2024-11-28T22:12:54.006Z","updated_at":"2025-04-19T17:25:24.610Z","avatar_url":"https://github.com/rzane.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Contextual [![Build Status](https://travis-ci.org/rzane/contextual.svg?branch=master)](https://travis-ci.org/rzane/contextual)\n\n`Contextual` provides a macro that will generate your Ecto [contexts](https://hexdocs.pm/phoenix/contexts.html) for you.\n\nImagine you have a schema called `MyApp.Posts.Post`. Typically, you'd create a context\nto encapsulate `Ecto` access for creating, updating, and deleting posts.\n\nYou could use the built-in Phoenix generators to solve this problem, but then you're\nleft with a bunch of boilerplate code that distracts the reader from the\nactual complexity in your contexts.\n\nInstead, `use Contextual` and delete a bunch of boilerplate code. Or not, it's entirely your decision.\n\n## Installation\n\nThe package can be installed by adding `contextual` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [{:contextual, \"~\u003e 1.0.0\"}]\nend\n```\n\nDocumentation can be found at [https://hexdocs.pm/contextual](https://hexdocs.pm/contextual).\n\n## Usage\n\nContextual requires three options:\n\n* `:name` - A tuple of `{:singular, :plural}` naming for your resource.\n* `:schema` - An `Ecto.Schema`\n* `:repo` - An `Ecto.Repo`\n\nHere's what your context might look like:\n\n```elixir\ndefmodule MyApp.Posts do\n  use Contextual,\n    name: {:post, :posts},\n    schema: MyApp.Posts.Post,\n    repo: MyApp.Repo\nend\n```\n\n`MyApp.Posts` now has the following functions:\n\n```elixir\nalias MyApp.Posts\nalias MyApp.Posts.Post\n\nimport Ecto.Query\n\n# List a collection\nposts = Posts.list_posts()\nposts = Posts.list_posts(from(p in Post, where: p.title == \"Meatloaf\"))\n\n# Get a record by ID\npost = Posts.get_post(19)\npost = Posts.get_post!(19)\n{:ok, post} = Posts.fetch_post(19)\n\n# Get a record by attributes\npost = Posts.get_post_by(title: \"Meatloaf\")\npost = Posts.get_post_by!(title: \"Meatloaf\")\n{:ok, post} = Posts.fetch_post_by(title: \"Meatloaf\")\n\n# Create a changeset for a given post\nchangeset = Posts.change_post()\nchangeset = Posts.change_post(post)\nchangeset = Posts.change_post(%{title: \"Meatloaf\"})\nchangeset = Posts.change_post(post, %{title: \"Meatloaf\"})\n\n# Create a post\n{:ok, post} = Posts.create_post(%{title: \"Meatloaf\"})\npost = Posts.create_post!(%{title: \"Meatloaf\"})\n\n# Update a post\n{:ok, post} = Posts.update_post(post, %{title: \"Meatloaf\"})\npost = Posts.update_post!(post, %{title: \"Meatloaf\"})\n\n# Delete a post\n{:ok, post} = Posts.delete_post(post)\npost = Posts.delete_post!(post)\n```\n\n### Choosing which functions are generated\n\nThat seems reasonable. If you only wanted to define `get_post`, you can provide the `:only` option.\n\n```elixir\ndefmodule MyApp.Posts do\n  use Contextual,\n    name: {:post, :posts},\n    schema: MyApp.Posts.Post,\n    repo: MyApp.Repo,\n    only: [:get]\nend\n```\n\nSimilarly, `Contextual` provides an `:except` option, which is basically just the opposite of `:only`.\n\n### Customizing function names\n\nContextual allows you to choose how your functions are named by using a name generator.\n\nFirst, create a module that will serve as a name generator:\n\n```elixir\ndefmodule MyApp.ContextNaming do\n  def generate_name(:list, {_singular, plural}) do\n    :\"all_#{plural}\"\n  end\n\n  def generate_name(:get, {singular, _plural}) do\n    :\"find_#{singular}\"\n  end\n\n  def generate_name(_, _), do: :default\nend\n```\n\nThe `generate_name/2` function must return an atom. If the function returns `:default`, Contextual will\nfallback to the default naming convention.\n\nNext, you'll need to configure your context to use your name generator:\n\n```elixir\ndefmodule MyApp.Posts do\n  use Contextual,\n    name: {:post, :posts},\n    schema: MyApp.Posts.Post,\n    repo: MyApp.Repo,\n    name_generator: {MyApp.ContextNaming, :generate_name}\nend\n```\n\nNow, you'll have `all_posts` instead of `list_posts` and `find_post` instead of `get_post`.\n\n### Typespecs\n\nContextual will automatically generate documentation and typespecs for thefunctions\nthat it defines. However, it expects your schema to have a type `t` defined.\n\nTo get the typespecs working properly, you'll need to define that type on your schema:\n\n```elixir\ndefmodule MyApp.Posts.Post do\n  # ...\n  @type t :: %__MODULE__{}\nend\n```\n\n## Contributing\n\nThe tests run against PostgreSQL. The `test` command will setup a test database for you.\n\nTo run the tests, just run:\n\n    $ mix test\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzane%2Fcontextual","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frzane%2Fcontextual","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzane%2Fcontextual/lists"}