{"id":13508752,"url":"https://github.com/chrismccord/atlas","last_synced_at":"2025-04-06T20:13:49.167Z","repository":{"id":9507931,"uuid":"11402852","full_name":"chrismccord/atlas","owner":"chrismccord","description":"Object Relational Mapper for Elixir","archived":false,"fork":false,"pushed_at":"2015-08-18T13:25:39.000Z","size":855,"stargazers_count":215,"open_issues_count":7,"forks_count":18,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-30T19:08:20.463Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/chrismccord.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}},"created_at":"2013-07-14T12:42:49.000Z","updated_at":"2025-02-25T16:53:24.000Z","dependencies_parsed_at":"2022-09-08T06:21:57.163Z","dependency_job_id":null,"html_url":"https://github.com/chrismccord/atlas","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/chrismccord%2Fatlas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrismccord%2Fatlas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrismccord%2Fatlas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrismccord%2Fatlas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrismccord","download_url":"https://codeload.github.com/chrismccord/atlas/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247543595,"owners_count":20955865,"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":[],"created_at":"2024-08-01T02:00:57.955Z","updated_at":"2025-04-06T20:13:49.150Z","avatar_url":"https://github.com/chrismccord.png","language":"Elixir","funding_links":[],"categories":["ORM and Datamapping"],"sub_categories":[],"readme":"# Atlas\n\nAtlas is an Object Relational Mapper for Elixir. (Work in progress. Expect breaking changes)\n\n[![Build Status](https://api.travis-ci.org/chrismccord/atlas.svg)](https://travis-ci.org/chrismccord/atlas)\n\n## Current Features\n- Postgres Adapter\n- Validations\n- Persistence\n- Schema definitions\n- Model query builder\n- Auto-generated 'accessor' functions for each field definition\n\n## Roadmap\n- Extend query builder to support joins\n- Add model relationships, ie `belongs_to`, `has_many`, `has_many through:`\n- Additional SQL adapters\n- Schema migrations\n\n## Example Usage:\n\n```elixir\ndefmodule User do\n  use Atlas.Model\n\n  @table :users\n  @primary_key :id\n\n  field :id, :integer\n  field :email, :string\n  field :is_site_admin, :boolean\n  field :archived, :boolean\n  field :state, :string\n\n  validates_numericality_of :id\n  validates_presence_of :email\n  validates_length_of :email, within: 5..255\n  validates_format_of :email, with: %r/.*@.*/, message: \"Email must be valid\"\n  validates :lives_in_ohio\n\n\n  def lives_in_ohio(record) do\n    unless record.state == \"OH\", do: {:state, \"You must live in Ohio\"}\n  end\n\n  def admins do\n    where(archived: false) |\u003e where(is_site_admin: true)\n  end\n  \n  def admin_with_email(email) do\n    admins |\u003e where(email: email)\n  end\nend\n\niex\u003e admin = Repo.first User.admin_with_email(\"foo@bar.com\")\n%User{id: 5, email: \"foo@bar.com\", archived: false, is_site_admin: true...}\n```\n\n## Query Builder\n\n### Examples\n```elixir\niex\u003e User.where(email: \"user@example.com\")\n     |\u003e User.where(\"state IS NOT NULL\")\n     |\u003e User.order(update_at: :asc)\n     |\u003e Repo.all\n\n[%User{id: 5, archived: true, is_site_admin: false...}, %User{id: 5, archived: true, is_site_admin: false...}]\n\niex\u003e user =  User.where(email: \"user@example.com\") |\u003e Repo.first\n%User{id: 5, archived: false, is_site_admin: false...}\niex\u003e user.email\nuser@example.com\n\niex\u003e User.where(archived: true)\n     |\u003e User.order(updated_at: :desc)\n     |\u003e Repo.first\n\n%User{id: 5, archived: true, is_site_admin: false...}\n```\n\n#### Queries are composable\n```elixir\ndefmodule UserSearch do\n  import User\n\n  def perform(options) do\n    is_admin = Keyword.get options, :is_site_admin, false\n    email    = Keyword.get options, :email, nil\n    scope    = User.scoped\n\n    scope = scope |\u003e where(is_site_admin: is_admin)\n    if email, do: scope = scope |\u003e where(email: email)\n\n    scope |\u003e Repo.all\n  end\nend\n\niex\u003e UserSearch.perform(is_site_admin: true, email: \"user@example.com\")\n[%User{email: \"user@example.com\"}]\n```\n\n\n## Persistence\n\nAtlas uses the Repository pattern to decouple persistence from behavior, as well as allow multiple database connections \nto different repositories for a robust and flexible persistence layer. When creating/updating/destroying data, \na list of behaviors must be included to run validation callbacks against for the Repo to proceed or halt with requested \nactions via the `as:` option.\n\nExamples\n\n```elixir\ndefmodule User do\n  use Atlas.Model\n  \n  @table :users\n  @primary_key :id\n  \n  field :age,  :integer\n  field :name, :string\n  \n  validates_numericality_of :age, within: 1..150\n  validates_presence_of :name\nend\n\ndefmodule Manager do\n  use Atlas.Validator\n  \n  validates_numericality_of :age, greater_than_or_equal: 21, message: \"managers must be at least 21\"\nend\n```\n\n```elixir\niex\u003e Repo.create(User, [age: 12, name: \"Dilbert\"], as: User)\n{:ok, %User{age: 12...}}\n\niex\u003e user = Repo.first(User)\niex\u003e Repo.update(user, [age: 18], as: [User, Manager])\n{:error, %User{age: 18...}, [\"managers must be at least 21\"]}\n\niex\u003e Repo.create(User, [age: 0, name: \"Chris\"], as: User)\n{:error, %User{age: 0..}, [\"age must be between 1 and 150\"]}\n```\n\n## Accessors\n\nAccessors for assigning and retrieving model attributes are automatically defined\nfrom the shema field definitions.\n\nBy default, Accessors are simply pass-throughs to the raw record setter and getter\nvalues; however, accessors can be overriden by the module for extended behavior\nand transformations before writing to, or after reading from the database.\n`assign` functions transform attributes when creating a new Struct via `Model.new` and \nbefore running model callbacks such as validations.\n\nExample attribute assignment:\n\n```elixir\ndefmodule User do\n  use Atlas.Model\n  field :email, :string\n  field :name,  :string\n\n  def assign(user, :email, value), do: user.update(email: String.downcase(value))\nend\n\niex\u003e User.assign(user, :email, \"USER@example.com\")\nUser[email: \"user@example.com\"]\n\niex\u003e User.new(email, \"USER@example.com\")\nUser[email: \"user@example.com\"]\n```\n\nExample attribute retrieval:\n\n```elixir\ndefmodule User do\n  use Atlas.Model\n  field :email, :string\n  field :name,  :string\n\n  def email(user), do: user.email |\u003e String.upcase\nend\n\niex\u003e user = User.new(email: \"chris@example.com\")\niex\u003e User.email(user)\nCHRIS@EXAMPLE.COM\n```\n\n\n### Auto-generated finders\n\n`with_[field name]` functions are automatically generated for all defined fields.\nFor example, a User module with a `field :email, :string` definition would include a `User.with_email` function\nthat returns the first record matching that field from the database.\n\n## Validation Support\n```elixir\niex\u003e user = User.new(email: \"invalid\")\n%User{id: nil, email: \"invalid\", is_site_admin: nil...}\n\niex\u003e User.validate user\n{:error, %User{newsletter_updated_at: ...}, [email: \"Email must be valid\", email: \"_ must be between 5 and 255 characters\",\n  email: \"_ must not be blank\"]}\n\niex\u003e User.full_error_messages user\n[\"Email must be valid\",\"email must be between 5 and 255 characters\",\"email must not be blank\",\"id must be a valid number\"]\n\n```\n\n\n## Repo Configuration\nDefine at least one Repository in your project that uses Atlas.Repo with a supported adapter.\nYour Repo simply needs to be provide `config` functions for `:dev`, `:test`, and `:prod` environments. \nAfter defining your repo, start its process within your application. \n\n```elixir\ndefmodule Repo do\n  use Atlas.Repo, adapter: Atlas.Adapters.Postgres\n\n  def config(:dev) do\n    [\n      database: \"\",\n      username: \"\",\n      password: \"\",\n      host: \"\",\n      pool: 5,\n      log_level: :debug\n    ]\n  end\n\n  def config(:test) do\n    [\n      database: \"\",\n      username: \"\",\n      password: \"\",\n      host: \"\",\n      pool: 5,\n      log_level: :debug\n    ]\n  end\n\n  def config(:prod) do\n    [\n      database: \"\",\n      username: \"\",\n      password: \"\",\n      host: \"\",\n      pool: 5,\n      log_level: :warn\n    ]\n  end\nend\n\nRepo.start_link\n```\n\n## Testing\n\nTesting requires a `lib/atlas/repos/dev_repo.ex` to exist. Here's an example:\n\n```elixir\ndefmodule Repo do\n  use Atlas.Repo, adapter: Atlas.Adapters.Postgres\n\n  def config(:dev) do\n    [\n      database: \"\",\n      username: \"\",\n      password: \"\",\n      host: \"localhost\",\n      pool: 5,\n      log_level: :debug\n    ]\n  end\n\n  def config(:test) do\n    [\n      database: \"atlas_test\",\n      username: \"chris\",\n      password: \"\",\n      host: \"localhost\",\n      pool: 5,\n      log_level: :debug\n    ]\n  end\n\n  def config(:prod) do\n    [\n      database: \"\",\n      username: \"\",\n      password: \"\",\n      host: \"\",\n      pool: 5,\n      log_level: :warn\n    ]\n  end\nend\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrismccord%2Fatlas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrismccord%2Fatlas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrismccord%2Fatlas/lists"}