• Stars
    star
    214
  • Rank 184,678 (Top 4 %)
  • Language
    Elixir
  • License
    MIT License
  • Created over 11 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Object Relational Mapper for Elixir

Atlas

Atlas is an Object Relational Mapper for Elixir. (Work in progress. Expect breaking changes)

Build Status

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:

defmodule User do
  use Atlas.Model

  @table :users
  @primary_key :id

  field :id, :integer
  field :email, :string
  field :is_site_admin, :boolean
  field :archived, :boolean
  field :state, :string

  validates_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_ohio


  def lives_in_ohio(record) do
    unless record.state == "OH", do: {:state, "You must live in Ohio"}
  end

  def admins do
    where(archived: false) |> where(is_site_admin: true)
  end
  
  def admin_with_email(email) do
    admins |> where(email: email)
  end
end

iex> admin = Repo.first User.admin_with_email("[email protected]")
%User{id: 5, email: "[email protected]", archived: false, is_site_admin: true...}

Query Builder

Examples

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
user@example.com

iex> User.where(archived: true)
     |> User.order(updated_at: :desc)
     |> Repo.first

%User{id: 5, archived: true, is_site_admin: false...}

Queries are composable

defmodule UserSearch do
  import User

  def perform(options) do
    is_admin = Keyword.get options, :is_site_admin, false
    email    = Keyword.get options, :email, nil
    scope    = User.scoped

    scope = scope |> where(is_site_admin: is_admin)
    if email, do: scope = scope |> where(email: email)

    scope |> Repo.all
  end
end

iex> 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

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
end

defmodule Manager do
  use Atlas.Validator
  
  validates_numericality_of :age, greater_than_or_equal: 21, message: "managers must be at least 21"
end
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:

defmodule User do
  use Atlas.Model
  field :email, :string
  field :name,  :string

  def assign(user, :email, value), do: user.update(email: String.downcase(value))
end

iex> User.assign(user, :email, "[email protected]")
User[email: "[email protected]"]

iex> User.new(email, "[email protected]")
User[email: "[email protected]"]

Example attribute retrieval:

defmodule User do
  use Atlas.Model
  field :email, :string
  field :name,  :string

  def email(user), do: user.email |> String.upcase
end

iex> user = User.new(email: "[email protected]")
iex> User.email(user)
CHRIS@EXAMPLE.COM

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

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.

defmodule Repo do
  use Atlas.Repo, adapter: Atlas.Adapters.Postgres

  def config(:dev) do
    [
      database: "",
      username: "",
      password: "",
      host: "",
      pool: 5,
      log_level: :debug
    ]
  end

  def config(:test) do
    [
      database: "",
      username: "",
      password: "",
      host: "",
      pool: 5,
      log_level: :debug
    ]
  end

  def config(:prod) do
    [
      database: "",
      username: "",
      password: "",
      host: "",
      pool: 5,
      log_level: :warn
    ]
  end
end

Repo.start_link

Testing

Testing requires a lib/atlas/repos/dev_repo.ex to exist. Here's an example:

defmodule Repo do
  use Atlas.Repo, adapter: Atlas.Adapters.Postgres

  def config(:dev) do
    [
      database: "",
      username: "",
      password: "",
      host: "localhost",
      pool: 5,
      log_level: :debug
    ]
  end

  def config(:test) do
    [
      database: "atlas_test",
      username: "chris",
      password: "",
      host: "localhost",
      pool: 5,
      log_level: :debug
    ]
  end

  def config(:prod) do
    [
      database: "",
      username: "",
      password: "",
      host: "",
      pool: 5,
      log_level: :warn
    ]
  end
end

More Repositories

1

render_sync

Real-time Rails Partials
Ruby
1,401
star
2

phoenix_chat_example

JavaScript
688
star
3

phoenix_live_view_example

Elixir
537
star
4

todo_trek

Elixir
367
star
5

elixir_express

Elixir
283
star
6

labrador

A loyal data retriever for your Rails development databases.
CSS
254
star
7

mailgun

Elixir Mailgun Client
Elixir
194
star
8

phoenix_haml

Phoenix Template Engine for Haml
Elixir
158
star
9

single_file_phx_bumblebee_ml

Elixir
93
star
10

dot_emacs

evil-mode
Emacs Lisp
93
star
11

elixirconf_training

JavaScript
74
star
12

single_file_phoenix_fly

Elixir
58
star
13

sketchpad

Elixir
42
star
14

channelsac

Ruby
31
star
15

sync_example

Ruby
24
star
16

phoenix_vs_rails_showdown

Ruby
22
star
17

phoenix_presence_example

Elixir
14
star
18

semantic-ui-brunch-phoenix

Elixir
13
star
19

phoenix_brunch_react

Elixir
11
star
20

dot_vim

Vim Script
11
star
21

fly_ollama

Dockerfile
10
star
22

ex_copter

Elixir client for the Parrot AR 2.0 Quadcopter
Elixir
10
star
23

phoenix_takes_flight

CSS
9
star
24

metrics

Elixir
8
star
25

blinky_presence

Phoenix Presence with Nerves example
Elixir
8
star
26

file_presenter

Elixir
6
star
27

gitit

Introspect project directory and launch browser to github repo page
CoffeeScript
6
star
28

bclose.vim

Delete a buffer without closing the window
Vim Script
6
star
29

riak_tasks

Simple riak cluster bootstrap and management for dev and test environments
Elixir
5
star
30

rumbl-example

Elixir
5
star
31

phoenix_brunch_vue

Elixir
5
star
32

pubsub_stress

JavaScript
5
star
33

phlux

JavaScript
4
star
34

sublime-files

Sublime Text 2 settings and packages
3
star
35

fly_yugabyte

Shell
3
star
36

hex_issue_example

Elixir
2
star
37

phoenix_pubsub_federation

Elixir
2
star
38

coffeekup_rails

Ruby
2
star
39

phoenix_render_example

Elixir
2
star
40

phoenix_federation_server

Elixir
2
star
41

fishcakez_channel_example

Elixir
2
star
42

historian

Ruby
1
star
43

validate

Coffeescript client-side validation library
CoffeeScript
1
star
44

phoenix_debugger_bug

Elixir
1
star
45

riaktor

Coming soon
1
star
46

my_plug

Elixir
1
star
47

live_eex

Elixir
1
star
48

chrismccord.com-old-

JavaScript
1
star
49

dotfiles

Shell
1
star