├── .gitignore
├── README.md
├── config
└── config.exs
├── lib
├── mix
│ └── tasks
│ │ └── server.ex
└── spirit.ex
├── mix.exs
└── test
├── spirit_test.exs
└── test_helper.exs
/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 | mix.lock
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Spirit
2 | ======
3 |
4 | n. a microframework for web development.
5 |
6 | ## Description
7 |
8 | We are big fans of [cuba] for `ruby` so we wanted to contribute to `elixir`
9 | community with a similar microframework.
10 |
11 | The intention of this project is to learn how `elixir` works and create a
12 | framework for our upcoming projects.
13 |
14 | We know there are many frameworks like [phoenix], [clint], [sugar] and others
15 | which we will be watching to learn and contribute but we still want to build
16 | a new one. It will teach us a lot!
17 |
18 | ## Installation
19 |
20 | Add `:spirit` to deps
21 |
22 | ```elixir
23 | defp deps do
24 | [
25 | { :spirit, "~> 0.0.1" }
26 | ]
27 | end
28 | ```
29 |
30 | And run `mix do deps.get, deps.compile`
31 |
32 | ## Usage
33 |
34 | Here's a simple application:
35 |
36 | ```elixir
37 | # cat lib/sample_app.ex
38 |
39 | defmodule SampleApp do
40 | use Spirit
41 |
42 | get "/hello" do
43 | send_resp(conn, 200, "
Hello World!
")
44 | end
45 |
46 | match _ do
47 | send_resp(conn, 404, "Not found :/")
48 | end
49 | end
50 | ```
51 |
52 | And the config file
53 |
54 | ```elixir
55 | # cat config/config.exs
56 |
57 | use Mix.Config
58 |
59 | config :spirit, app: SampleApp
60 | ```
61 |
62 | To run it, just do `mix server` and start browsing your application.
63 |
64 | > Check [spirit-example] to see the full example and step-by-step guide.
65 |
66 | ## Composition
67 |
68 | You can compose as many Spirit applications as you want using `forward`.
69 | This is a recommended practice when you have nested routes or want to group
70 | routes based on a criterion.
71 |
72 | ```elixir
73 | defmodule Users do
74 | use Spirit
75 |
76 | get "/" do
77 | send_resp(conn, 200, "Users index")
78 | end
79 |
80 | get "/:id" do
81 | # Show the User with `id`
82 | end
83 |
84 | post "/" do
85 | # Create a new user
86 | end
87 |
88 | match _ do
89 | send_resp(conn, 404, "Not found")
90 | end
91 | end
92 |
93 | defmodule MainApp do
94 | use Spirit
95 |
96 | get "/hi/:name" do
97 | send_resp(conn, 200, "hello #{name}!
")
98 | end
99 |
100 | forward "/users", to: Users
101 |
102 | get "/hello/*_rest" do
103 | send_resp(conn, 200, "matches all routes starting with /hello")
104 | end
105 |
106 | match _ do
107 | send_resp(conn, 404, "Not found")
108 | end
109 | end
110 | ```
111 |
112 |
113 | [cuba]: https://github.com/soveran/cuba
114 | [clint]: https://github.com/lpil/clint
115 | [sugar]: http://sugar-framework.github.io
116 | [phoenix]: http://phoenixframework.org
117 | [spirit-example]: https://github.com/citrusbyte/spirit-example
118 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | # Sample configuration
4 | #
5 | # config :spirit,
6 | # app: MySpiritRouter
7 | #
8 |
9 |
--------------------------------------------------------------------------------
/lib/mix/tasks/server.ex:
--------------------------------------------------------------------------------
1 | defmodule Mix.Tasks.Server do
2 | use Mix.Task
3 |
4 | def run(_args) do
5 | { :ok, app } = :application.get_env(:spirit, :app)
6 |
7 | app.start
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/spirit.ex:
--------------------------------------------------------------------------------
1 | defmodule Spirit do
2 |
3 | defmacro __using__(_) do
4 | quote do
5 | use Plug.Router
6 | use Plug.ErrorHandler
7 |
8 | plug :fetch_query_params
9 | plug Plug.Parsers, parsers: [:urlencoded, :json, :multipart],
10 | pass: ["*/*"],
11 | json_decoder: Poison
12 |
13 | plug :match
14 | plug :dispatch
15 |
16 | def start do
17 | {:ok, _} = Plug.Adapters.Cowboy.http(__MODULE__, [], port: port)
18 |
19 | IO.puts "Running on http://localhost:#{port}"
20 |
21 | no_halt
22 | end
23 |
24 | @doc """
25 | Redirects the response.
26 | ## Arguments
27 | * `conn` - `Plug.Conn`
28 | * `location` - `String`
29 | * `opts` - `Keyword`
30 |
31 | ## Returns
32 | `Plug.Conn`
33 | """
34 | @spec redirect(Plug.Conn.t, binary, Keyword.t) :: Plug.Conn.t
35 | def redirect(conn, location, opts \\ [])
36 |
37 | def redirect(%Plug.Conn{state: :sent} = conn, _, _) do
38 | conn
39 | end
40 |
41 | def redirect(conn, location, opts \\ []) do
42 | opts = [status: 302] |> Keyword.merge(opts)
43 |
44 | conn
45 | |> put_resp_header("Location", location)
46 | |> send_resp(opts[:status], "")
47 | end
48 |
49 | defp iex_running? do
50 | Code.ensure_loaded?(IEx) && IEx.started?
51 | end
52 |
53 | defp no_halt do
54 | unless iex_running? do
55 | :timer.sleep :infinity
56 | end
57 | end
58 |
59 | defp port do
60 | System.get_env("PORT") || 4000
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Spirit.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :spirit,
7 | version: "0.0.1",
8 | elixir: "~> 1.0.4",
9 | description: "Elixir microframework for web development.",
10 | deps: deps,
11 | package: package
12 | ]
13 | end
14 |
15 | # Configuration for the OTP application
16 | #
17 | # Type `mix help compile.app` for more information
18 | def application do
19 | [applications: [:cowboy, :plug]]
20 | end
21 |
22 | # Dependencies can be hex.pm packages:
23 | #
24 | # {:mydep, "~> 0.3.0"}
25 | #
26 | # Or git/path repositories:
27 | #
28 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1"}
29 | #
30 | # Type `mix help deps` for more examples and options
31 | defp deps do
32 | [
33 | {:cowboy, "~> 1.0"},
34 | {:plug, "~> 0.13"},
35 | {:poison, "~> 1.4"}
36 | ]
37 | end
38 |
39 | defp package do
40 | [
41 | contributors: ["Emiliano Mancuso"],
42 | licenses: ["MIT"],
43 | links: %{"Github" => "https://github.com/emancu/spirit"},
44 | files: ~w(mix.exs README.md lib test)
45 | ]
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/test/spirit_test.exs:
--------------------------------------------------------------------------------
1 | defmodule SpiritTest do
2 | use ExUnit.Case
3 |
4 | test "the truth" do
5 | assert 1 + 1 == 2
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------