{"id":22161969,"url":"https://github.com/codedge-llc/commandex","last_synced_at":"2025-05-08T00:06:43.121Z","repository":{"id":54149933,"uuid":"233700593","full_name":"codedge-llc/commandex","owner":"codedge-llc","description":"Make Elixir actions a first-class data type.","archived":false,"fork":false,"pushed_at":"2024-09-09T17:52:38.000Z","size":60,"stargazers_count":42,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-08T00:06:35.560Z","etag":null,"topics":["elixir","hex"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/commandex","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/codedge-llc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["codedge-llc"]}},"created_at":"2020-01-13T21:48:47.000Z","updated_at":"2025-03-04T23:06:46.000Z","dependencies_parsed_at":"2024-12-23T10:27:33.486Z","dependency_job_id":"e3fa1303-1dd0-4f67-bed0-c7df4142a65c","html_url":"https://github.com/codedge-llc/commandex","commit_stats":{"total_commits":24,"total_committers":4,"mean_commits":6.0,"dds":0.375,"last_synced_commit":"ec56354fbe2e6e57ed0e6e20e12dc42b14c32d86"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codedge-llc%2Fcommandex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codedge-llc%2Fcommandex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codedge-llc%2Fcommandex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codedge-llc%2Fcommandex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codedge-llc","download_url":"https://codeload.github.com/codedge-llc/commandex/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252973691,"owners_count":21834108,"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","hex"],"created_at":"2024-12-02T04:17:48.037Z","updated_at":"2025-05-08T00:06:43.099Z","avatar_url":"https://github.com/codedge-llc.png","language":"Elixir","funding_links":["https://github.com/sponsors/codedge-llc"],"categories":[],"sub_categories":[],"readme":"# Commandex\n\n\u003e Make Elixir actions a first-class data type.\n\n[![CI](https://github.com/codedge-llc/commandex/actions/workflows/ci.yml/badge.svg)](https://github.com/codedge-llc/commandex/actions/workflows/ci.yml)\n[![Version](https://img.shields.io/hexpm/v/commandex.svg)](https://hex.pm/packages/commandex)\n[![Total Downloads](https://img.shields.io/hexpm/dt/commandex.svg)](https://hex.pm/packages/commandex)\n[![License](https://img.shields.io/hexpm/l/commandex.svg)](https://github.com/codedge-llc/commandex/blob/main/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/codedge-llc/commandex.svg)](https://github.com/codedge-llc/commandex/commits/main)\n[![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/commandex/)\n\nCommandex structs are a loose implementation of the command pattern, making it easy\nto wrap parameters, data, and errors into a well-defined struct.\n\n## Installation\n\nAdd commandex as a `mix.exs` dependency:\n\n```elixir\ndef deps do\n  [\n    {:commandex, \"~\u003e 0.5.1\"}\n  ]\nend\n```\n\n## Example Usage\n\nA fully implemented command module might look like this:\n\n```elixir\ndefmodule RegisterUser do\n  import Commandex\n\n  command do\n    param :email\n    param :password\n\n    data :password_hash\n    data :user\n\n    pipeline :hash_password\n    pipeline :create_user\n    pipeline :send_welcome_email\n  end\n\n  def hash_password(command, %{password: nil} = _params, _data) do\n    command\n    |\u003e put_error(:password, :not_given)\n    |\u003e halt()\n  end\n\n  def hash_password(command, %{password: password} = _params, _data) do\n    put_data(command, :password_hash, Base.encode64(password))\n  end\n\n  def create_user(command, %{email: email} = _params, %{password_hash: phash} = _data) do\n    %User{}\n    |\u003e User.changeset(%{email: email, password_hash: phash})\n    |\u003e Repo.insert()\n    |\u003e case do\n      {:ok, user} -\u003e put_data(command, :user, user)\n      {:error, changeset} -\u003e command |\u003e put_error(:repo, changeset) |\u003e halt()\n    end\n  end\n\n  def send_welcome_email(command, _params, %{user: user}) do\n    Mailer.send_welcome_email(user)\n    command\n  end\nend\n```\n\nThe `command/1` macro will define a struct that looks like:\n\n```elixir\n%RegisterUser{\n  success: false,\n  halted: false,\n  errors: %{},\n  params: %{email: nil, password: nil},\n  data: %{password_hash: nil, user: nil},\n  pipelines: [:hash_password, :create_user, :send_welcome_email]\n}\n```\n\nAs well as two functions:\n\n```elixir\n\u0026RegisterUser.new/1\n\u0026RegisterUser.run/1\n```\n\n`\u0026new/1` parses parameters into a new struct. These can be either a keyword list\nor map with atom/string keys.\n\n`\u0026run/1` takes a command struct and runs it through the pipeline functions defined\nin the command. Functions are executed _in the order in which they are defined_.\nIf a command passes through all pipelines without calling `halt/1`, `:success`\nwill be set to `true`. Otherwise, subsequent pipelines after the `halt/1` will\nbe ignored and `:success` will be set to `false`.\n\nRunning a command is easy:\n\n```elixir\n%{email: \"example@example.com\", password: \"asdf1234\"}\n|\u003e RegisterUser.new()\n|\u003e RegisterUser.run()\n|\u003e case do\n  %{success: true, data: %{user: user}} -\u003e\n    # Success! We've got a user now\n\n  %{success: false, errors: %{password: :not_given}} -\u003e\n    # Respond with a 400 or something\n\n  %{success: false, errors: _errors} -\u003e\n    # I'm a lazy programmer that writes catch-all error handling\nend\n```\n\nFor even leaner implementations, you can run a command by passing\nthe params directly into `\u0026run/1` without using `\u0026new/1`:\n\n```elixir\n%{email: \"example@example.com\", password: \"asdf1234\"}\n|\u003e RegisterUser.run()\n```\n\n## Contributing\n\n### Testing\n\nUnit tests can be run with `mix test`.\n\n### Formatting\n\nThis project uses Elixir's `mix format` and [Prettier](https://prettier.io) for formatting.\nAdd hooks in your editor of choice to run it after a save. Be sure it respects this project's\n`.formatter.exs`.\n\n### Commits\n\nGit commit subjects use the [Karma style](http://karma-runner.github.io/5.0/dev/git-commit-msg.html).\n\n## License\n\nCopyright (c) 2020-2024 Codedge LLC (https://www.codedge.io/)\n\nThis library is MIT licensed. See the [LICENSE](https://github.com/codedge-llc/commandex/blob/main/LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedge-llc%2Fcommandex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodedge-llc%2Fcommandex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedge-llc%2Fcommandex/lists"}