Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/chrismccord/atlas
Object Relational Mapper for Elixir
https://github.com/chrismccord/atlas
Last synced: 19 days ago
JSON representation
Object Relational Mapper for Elixir
- Host: GitHub
- URL: https://github.com/chrismccord/atlas
- Owner: chrismccord
- License: mit
- Created: 2013-07-14T12:42:49.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2015-08-18T13:25:39.000Z (over 9 years ago)
- Last Synced: 2024-11-17T04:05:21.450Z (26 days ago)
- Language: Elixir
- Size: 835 KB
- Stars: 214
- Watchers: 16
- Forks: 18
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- freaking_awesome_elixir - Elixir - Object Relational Mapper for Elixir. (ORM and Datamapping)
- fucking-awesome-elixir - atlas - Object Relational Mapper for Elixir. (ORM and Datamapping)
- awesome-elixir - atlas - Object Relational Mapper for Elixir. (ORM and Datamapping)
README
# Atlas
Atlas is an Object Relational Mapper for Elixir. (Work in progress. Expect breaking changes)
[![Build Status](https://api.travis-ci.org/chrismccord/atlas.svg)](https://travis-ci.org/chrismccord/atlas)
## Current Features
- Postgres Adapter
- Validations
- Persistence
- Schema definitions
- Model query builder
- Auto-generated 'accessor' functions for each field definition## Roadmap
- Extend query builder to support joins
- Add model relationships, ie `belongs_to`, `has_many`, `has_many through:`
- Additional SQL adapters
- Schema migrations## Example Usage:
```elixir
defmodule User do
use Atlas.Model@table :users
@primary_key :idfield :id, :integer
field :email, :string
field :is_site_admin, :boolean
field :archived, :boolean
field :state, :stringvalidates_numericality_of :id
validates_presence_of :email
validates_length_of :email, within: 5..255
validates_format_of :email, with: %r/.*@.*/, message: "Email must be valid"
validates :lives_in_ohiodef lives_in_ohio(record) do
unless record.state == "OH", do: {:state, "You must live in Ohio"}
enddef admins do
where(archived: false) |> where(is_site_admin: true)
end
def admin_with_email(email) do
admins |> where(email: email)
end
endiex> admin = Repo.first User.admin_with_email("[email protected]")
%User{id: 5, email: "[email protected]", archived: false, is_site_admin: true...}
```## Query Builder
### Examples
```elixir
iex> User.where(email: "[email protected]")
|> User.where("state IS NOT NULL")
|> User.order(update_at: :asc)
|> Repo.all[%User{id: 5, archived: true, is_site_admin: false...}, %User{id: 5, archived: true, is_site_admin: false...}]
iex> user = User.where(email: "[email protected]") |> Repo.first
%User{id: 5, archived: false, is_site_admin: false...}
iex> user.email
[email protected]iex> User.where(archived: true)
|> User.order(updated_at: :desc)
|> Repo.first%User{id: 5, archived: true, is_site_admin: false...}
```#### Queries are composable
```elixir
defmodule UserSearch do
import Userdef perform(options) do
is_admin = Keyword.get options, :is_site_admin, false
email = Keyword.get options, :email, nil
scope = User.scopedscope = scope |> where(is_site_admin: is_admin)
if email, do: scope = scope |> where(email: email)scope |> Repo.all
end
endiex> UserSearch.perform(is_site_admin: true, email: "[email protected]")
[%User{email: "[email protected]"}]
```## Persistence
Atlas uses the Repository pattern to decouple persistence from behavior, as well as allow multiple database connections
to different repositories for a robust and flexible persistence layer. When creating/updating/destroying data,
a list of behaviors must be included to run validation callbacks against for the Repo to proceed or halt with requested
actions via the `as:` option.Examples
```elixir
defmodule User do
use Atlas.Model
@table :users
@primary_key :id
field :age, :integer
field :name, :string
validates_numericality_of :age, within: 1..150
validates_presence_of :name
enddefmodule Manager do
use Atlas.Validator
validates_numericality_of :age, greater_than_or_equal: 21, message: "managers must be at least 21"
end
``````elixir
iex> Repo.create(User, [age: 12, name: "Dilbert"], as: User)
{:ok, %User{age: 12...}}iex> user = Repo.first(User)
iex> Repo.update(user, [age: 18], as: [User, Manager])
{:error, %User{age: 18...}, ["managers must be at least 21"]}iex> Repo.create(User, [age: 0, name: "Chris"], as: User)
{:error, %User{age: 0..}, ["age must be between 1 and 150"]}
```## Accessors
Accessors for assigning and retrieving model attributes are automatically defined
from the shema field definitions.By default, Accessors are simply pass-throughs to the raw record setter and getter
values; however, accessors can be overriden by the module for extended behavior
and transformations before writing to, or after reading from the database.
`assign` functions transform attributes when creating a new Struct via `Model.new` and
before running model callbacks such as validations.Example attribute assignment:
```elixir
defmodule User do
use Atlas.Model
field :email, :string
field :name, :stringdef assign(user, :email, value), do: user.update(email: String.downcase(value))
endiex> User.assign(user, :email, "[email protected]")
User[email: "[email protected]"]iex> User.new(email, "[email protected]")
User[email: "[email protected]"]
```Example attribute retrieval:
```elixir
defmodule User do
use Atlas.Model
field :email, :string
field :name, :stringdef email(user), do: user.email |> String.upcase
endiex> user = User.new(email: "[email protected]")
iex> User.email(user)
[email protected]
```### Auto-generated finders
`with_[field name]` functions are automatically generated for all defined fields.
For example, a User module with a `field :email, :string` definition would include a `User.with_email` function
that returns the first record matching that field from the database.## Validation Support
```elixir
iex> user = User.new(email: "invalid")
%User{id: nil, email: "invalid", is_site_admin: nil...}iex> User.validate user
{:error, %User{newsletter_updated_at: ...}, [email: "Email must be valid", email: "_ must be between 5 and 255 characters",
email: "_ must not be blank"]}iex> User.full_error_messages user
["Email must be valid","email must be between 5 and 255 characters","email must not be blank","id must be a valid number"]```
## Repo Configuration
Define at least one Repository in your project that uses Atlas.Repo with a supported adapter.
Your Repo simply needs to be provide `config` functions for `:dev`, `:test`, and `:prod` environments.
After defining your repo, start its process within your application.```elixir
defmodule Repo do
use Atlas.Repo, adapter: Atlas.Adapters.Postgresdef config(:dev) do
[
database: "",
username: "",
password: "",
host: "",
pool: 5,
log_level: :debug
]
enddef config(:test) do
[
database: "",
username: "",
password: "",
host: "",
pool: 5,
log_level: :debug
]
enddef config(:prod) do
[
database: "",
username: "",
password: "",
host: "",
pool: 5,
log_level: :warn
]
end
endRepo.start_link
```## Testing
Testing requires a `lib/atlas/repos/dev_repo.ex` to exist. Here's an example:
```elixir
defmodule Repo do
use Atlas.Repo, adapter: Atlas.Adapters.Postgresdef config(:dev) do
[
database: "",
username: "",
password: "",
host: "localhost",
pool: 5,
log_level: :debug
]
enddef config(:test) do
[
database: "atlas_test",
username: "chris",
password: "",
host: "localhost",
pool: 5,
log_level: :debug
]
enddef config(:prod) do
[
database: "",
username: "",
password: "",
host: "",
pool: 5,
log_level: :warn
]
end
end
```