├── test
├── test_helper.exs
└── slim_phoenix_test.exs
├── lib
├── templates
│ ├── 404.html.eex
│ ├── about.html.eex
│ ├── contact.html.eex
│ └── home.html.eex
├── slim_phoenix
│ ├── conn.ex
│ ├── router.ex
│ ├── cowboy_handler.ex
│ └── application.ex
├── slim_phoenix.ex
├── views
│ └── page_view.ex
└── controllers
│ └── page_controller.ex
├── .gitignore
├── mix.lock
├── mix.exs
├── README.md
└── config
└── config.exs
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/lib/templates/404.html.eex:
--------------------------------------------------------------------------------
1 |
404 error from template file
2 |
--------------------------------------------------------------------------------
/lib/templates/about.html.eex:
--------------------------------------------------------------------------------
1 | About page from template file
2 |
--------------------------------------------------------------------------------
/lib/templates/contact.html.eex:
--------------------------------------------------------------------------------
1 | Contact page from template file
2 |
--------------------------------------------------------------------------------
/lib/templates/home.html.eex:
--------------------------------------------------------------------------------
1 | Hello <%= name %>, this is homepage from the template file
2 |
--------------------------------------------------------------------------------
/test/slim_phoenix_test.exs:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenixTest do
2 | use ExUnit.Case
3 | doctest SlimPhoenix
4 |
5 | test "the truth" do
6 | assert 1 + 1 == 2
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/slim_phoenix/conn.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.Conn do
2 | defstruct [:req_path, :res_code, :res_body]
3 |
4 | def put_res_body(conn, body) do
5 | Map.put(conn, :res_body, body)
6 | end
7 |
8 | def put_res_code(conn, code) do
9 | Map.put(conn, :res_code, code)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/slim_phoenix.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix do
2 | @moduledoc """
3 | Documentation for SlimPhoenix.
4 | """
5 |
6 | @doc """
7 | Hello world.
8 |
9 | ## Examples
10 |
11 | iex> SlimPhoenix.hello
12 | :world
13 |
14 | """
15 | def hello do
16 | :world
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/views/page_view.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.PageView do
2 | import SlimPhoenix.Conn, only: [put_res_body: 2]
3 |
4 | def render(path, assigns \\ [])
5 | for path <- Path.wildcard("lib/templates/*.eex") do
6 | base_path = Path.basename(path, ".eex")
7 | template_content = File.read!(path)
8 | def unquote(:"render")(unquote(base_path), assigns) do
9 | EEx.eval_string unquote(template_content), assigns
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
--------------------------------------------------------------------------------
/lib/slim_phoenix/router.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.Router do
2 | alias SlimPhoenix.PageController
3 |
4 | def call(conn) do
5 | content_for(conn.req_path, conn)
6 | end
7 |
8 | defp content_for("/", conn) do
9 | conn
10 | |> PageController.home
11 | end
12 |
13 | defp content_for("/contact", conn) do
14 | conn
15 | |> PageController.contact
16 | end
17 |
18 | defp content_for("/about", conn) do
19 | conn
20 | |> PageController.about
21 | end
22 |
23 | defp content_for(_, conn) do
24 | conn
25 | |> PageController.page_not_found
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/slim_phoenix/cowboy_handler.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.CowboyHandler do
2 | def init(_type, req, opts) do
3 | {:ok, req, opts}
4 | end
5 |
6 | def handle(request, opts) do
7 | [ router | _ ] = opts
8 | { path, request } = :cowboy_req.path request
9 | conn = %SlimPhoenix.Conn{req_path: path}
10 | conn = router.call(conn)
11 |
12 | { :ok, reply } = :cowboy_req.reply(
13 | conn.res_code,
14 | [{"content-type", "text/html"}],
15 | conn.res_body,
16 | request
17 | )
18 |
19 | {:ok, reply, opts}
20 | end
21 |
22 | def terminate(_reason, _request, _state), do: :ok
23 | end
24 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
2 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
3 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
4 | "plug": {:hex, :plug, "1.3.0", "6e2b01afc5db3fd011ca4a16efd9cb424528c157c30a44a0186bcc92c7b2e8f3", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
5 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []}}
6 |
--------------------------------------------------------------------------------
/lib/controllers/page_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.PageController do
2 | import SlimPhoenix.Conn, only: [put_res_code: 2, put_res_body: 2]
3 |
4 | def home(conn) do
5 | conn
6 | |> put_res_code(200)
7 | |> render("home.html", name: "Shankardevy")
8 | end
9 |
10 | def about(conn) do
11 | conn
12 | |> put_res_code(200)
13 | |> render("about.html")
14 | end
15 |
16 | def contact(conn) do
17 | conn
18 | |> put_res_code(200)
19 | |> render("contact.html")
20 | end
21 |
22 | def page_not_found(conn) do
23 | conn
24 | |> put_res_code(404)
25 | |> render("404.html")
26 | end
27 |
28 | defp render(conn, template, assigns \\ []) do
29 | data = SlimPhoenix.PageView.render(template, assigns)
30 | conn
31 | |> put_res_body(data)
32 | end
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/lib/slim_phoenix/application.ex:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.Application do
2 | @moduledoc false
3 |
4 | use Application
5 |
6 | def start(_type, _args) do
7 | import Supervisor.Spec, warn: false
8 |
9 | # This is not the proper way to start the cowboy server in the supervision tree.
10 | # Proper way of starting the cowboy server will be explored later in the book.
11 | # For now, this helps to understand the basic concepts.
12 | dispatch_config = :cowboy_router.compile([
13 | { :_, # Match any host
14 | [{ :_, SlimPhoenix.CowboyHandler, [SlimPhoenix.Router] }] # Map all path to CowboyHandler
15 | }
16 | ])
17 |
18 | :cowboy.start_http(:http, 100,[{:port, 8080}],[{ :env, [{:dispatch, dispatch_config}]}])
19 |
20 | children = []
21 | opts = [strategy: :one_for_one, name: SlimPhoenix.Supervisor]
22 | Supervisor.start_link(children, opts)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule SlimPhoenix.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :slim_phoenix,
6 | version: "0.1.0",
7 | elixir: "~> 1.4",
8 | build_embedded: Mix.env == :prod,
9 | start_permanent: Mix.env == :prod,
10 | deps: deps()]
11 | end
12 |
13 | # Configuration for the OTP application
14 | #
15 | # Type "mix help compile.app" for more information
16 | def application do
17 | # Specify extra applications you'll use from Erlang/Elixir
18 | [extra_applications: [:logger],
19 | mod: {SlimPhoenix.Application, []}]
20 | end
21 |
22 | # Dependencies can be Hex packages:
23 | #
24 | # {:my_dep, "~> 0.3.0"}
25 | #
26 | # Or git/path repositories:
27 | #
28 | # {:my_dep, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
29 | #
30 | # Type "mix help deps" for more examples and options
31 | defp deps do
32 | [{:cowboy, "~> 1.0.0"},
33 | {:plug, "~> 1.0"}]
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Slim Phoenix
2 |
3 | A minimal rebuild of Phoenix to demonstrate various components of Phoenix, Plug, Cowboy.
4 |
5 | The code in this repo is part of my upcoming book on Phoenix. It incrementally builds a simple Phoenix replica starting from using a plain cowboy integration and then building a router, controller, view and template on top of it one at a time.
6 |
7 | If you are curious and can't wait till the book the release, go clone the repo and run it locally. Checkout the repo to the initial commit and look for the changes with each new commit.
8 |
9 | ## How to Run locally
10 |
11 | * Clone this project locally.
12 | * From inside the project directory, run `mix do deps.get` to get dependencies.
13 | * Visit [http://localhost:8080](http://localhost:8080)
14 | * '/' render "lib/templates/home.html.eex"
15 | * '/contact' render "lib/templates/contact.html.eex"
16 | * '/about' render "lib/templates/about.html.eex"
17 | * any other path renders "lib/templates/404.html.eex"
18 |
19 | ## Subscribe
20 |
21 | To get notified when the book is released, please subscribe at [http://shankardevy.github.io/phoenix-book/](http://shankardevy.github.io/phoenix-book/)
22 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | # This file is responsible for configuring your application
2 | # and its dependencies with the aid of the Mix.Config module.
3 | use Mix.Config
4 |
5 | # This configuration is loaded before any dependency and is restricted
6 | # to this project. If another project depends on this project, this
7 | # file won't be loaded nor affect the parent project. For this reason,
8 | # if you want to provide default values for your application for
9 | # 3rd-party users, it should be done in your "mix.exs" file.
10 |
11 | # You can configure for your application as:
12 | #
13 | # config :slim_phoenix, key: :value
14 | #
15 | # And access this configuration in your application as:
16 | #
17 | # Application.get_env(:slim_phoenix, :key)
18 | #
19 | # Or configure a 3rd-party app:
20 | #
21 | # config :logger, level: :info
22 | #
23 |
24 | # It is also possible to import configuration files, relative to this
25 | # directory. For example, you can emulate configuration per environment
26 | # by uncommenting the line below and defining dev.exs, test.exs and such.
27 | # Configuration from the imported file will override the ones defined
28 | # here (which is why it is important to import them last).
29 | #
30 | # import_config "#{Mix.env}.exs"
31 |
--------------------------------------------------------------------------------