├── .gitignore
├── README.md
├── examples
├── company-xyz
│ ├── .gitignore
│ ├── README.md
│ ├── apps
│ │ └── web
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── config
│ │ │ └── config.exs
│ │ │ ├── lib
│ │ │ ├── web.ex
│ │ │ └── web
│ │ │ │ └── router.ex
│ │ │ ├── mix.exs
│ │ │ ├── priv
│ │ │ └── static
│ │ │ │ └── .gitkeep
│ │ │ └── test
│ │ │ ├── test_helper.exs
│ │ │ └── web_test.exs
│ ├── config
│ │ └── config.exs
│ ├── mix.exs
│ └── mix.lock
├── hello_world
│ ├── .gitignore
│ ├── README.md
│ ├── config
│ │ └── config.exs
│ ├── lib
│ │ ├── hello_world.ex
│ │ └── hello_world
│ │ │ └── handler.ex
│ ├── mix.exs
│ ├── mix.lock
│ └── test
│ │ ├── hello_world_test.exs
│ │ └── test_helper.exs
└── hello_world_plug
│ ├── .gitignore
│ ├── config
│ └── config.exs
│ ├── lib
│ ├── hello_world_plug.ex
│ └── hello_world_plug
│ │ └── router.ex
│ ├── mix.exs
│ ├── mix.lock
│ └── test
│ ├── hello_world_plug_test.exs
│ └── test_helper.exs
└── manuscript
├── 00-introduction.md
├── 01-building-a-basic-application.md
├── 02-the-something-more.md
├── Book.txt
├── basic-language-knowledge.md
├── environment-configuration.md
└── images
└── title_page.png
/.gitignore:
--------------------------------------------------------------------------------
1 | _book/
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Web Development with Elixir
2 |
3 | Are you interested in web development? Seen Erlang and
4 | Elixir around and want to know what the fuss is about?
5 | Well, read along to learn how to apply Elixir for use
6 | in web development!
7 |
8 | Follow with me as well explore using Elixir for web
9 | development, and we will figure out how to leverage
10 | the Erlang VM to do our bidding at the same time.
11 |
12 | We are primarily going to focus on using a small and
13 | by no means exhaustive set of libraries to cover
14 | these topics:
15 |
16 | - Getting started
17 | - Routing
18 | - Templating
19 | - Middleware
20 | - Working with data sources
21 | - Hosting
22 | - ...and more!
23 |
24 | [Check it out!][book]
25 |
26 | ## In-progress Additions
27 |
28 | Any additions that are in progress and/or planned will
29 | be tracked on a [public Trello board][trello]. If you
30 | have any requests/suggestions, please submit a PR or
31 | join the discussion on Trello.
32 |
33 | ## Publication and Distribution
34 |
35 | This book will be published and distributed on
36 | [Leanpub][leanpub]. While the book can be obtained
37 | here in raw format and on Leanpub in a variety of
38 | formats (HTML, PDF, EPUB, & MOBI) for free, donations
39 | are accepted via Leanpub with 100% of donations going
40 | straight to the [Electronic Frontier Foundation][eff].
41 |
42 | ## Copyright
43 |
44 | © 2014-2015 Shane Logsdon
45 |
46 | [leanpub]: https://leanpub.com
47 | [book]: https://leanpub.com/web-development-using-elixir
48 | [eff]: https://www.eff.org/
49 | [trello]: https://trello.com/b/8YnuGVXJ/web-development-using-elixir
50 |
--------------------------------------------------------------------------------
/examples/company-xyz/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 |
--------------------------------------------------------------------------------
/examples/company-xyz/README.md:
--------------------------------------------------------------------------------
1 | CompanyXyz
2 | ==========
3 |
4 | ** TODO: Add description **
5 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/README.md:
--------------------------------------------------------------------------------
1 | CompanyXyz.Web
2 | ==============
3 |
4 | ** TODO: Add description **
5 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/config/config.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | config :logger, :console,
4 | level: :info,
5 | format: "$date $time [$level] $metadata$message\n",
6 | metadata: [:user_id]
7 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/lib/web.ex:
--------------------------------------------------------------------------------
1 | defmodule CompanyXyz.Web do
2 | use Application
3 | import Plug.Adapters.Cowboy, only: [child_spec: 4, child_spec: 3]
4 |
5 | def start(_type, _args) do
6 | import Supervisor.Spec, warn: false
7 |
8 | children = [
9 | child_spec(:http, CompanyXyz.Web.Router, [])
10 | ]
11 |
12 | opts = [strategy: :one_for_one, name: CompanyXyz.Web.Supervisor]
13 | Supervisor.start_link(children, opts)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/lib/web/router.ex:
--------------------------------------------------------------------------------
1 | defmodule CompanyXyz.Web.Router do
2 | use Plug.Router
3 |
4 | use Plug.Debugger, otp_app: :web
5 | plug Plug.Static, at: "/static", from: :web
6 | plug :match
7 | plug :dispatch
8 |
9 | get "/" do
10 | conn |> send_resp(200, index_body)
11 | end
12 |
13 | get "/contact" do
14 | conn |> send_resp(200, contact_body)
15 | end
16 |
17 | post "/contact" do
18 | conn |> send_resp(200, contact_success_body)
19 | end
20 |
21 | match _ do
22 | conn |> send_resp
23 | end
24 |
25 | defp index_body do
26 | """
27 |
28 |
29 |
30 | Company XYZ
31 |
32 |
33 | Company XYZ welcomes you!
34 | Contact Us
35 |
36 |
37 | """
38 | end
39 |
40 | defp contact_body do
41 | """
42 |
43 |
44 |
45 | Company XYZ
46 |
47 |
48 | Contact Company XYZ
49 |
53 | Home
54 |
55 |
56 | """
57 | end
58 |
59 | defp contact_success_body do
60 | """
61 |
62 |
63 |
64 | Company XYZ
65 |
66 |
67 | Thanks for contacting us!
68 | Home
69 |
70 |
71 | """
72 | end
73 | end
74 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule CompanyXyz.Web.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :web,
6 | version: "0.0.1",
7 | deps_path: "../../deps",
8 | lockfile: "../../mix.lock",
9 | elixir: "~> 1.0",
10 | deps: deps]
11 | end
12 |
13 | def application do
14 | [applications: [:logger, :cowboy, :plug],
15 | mod: {CompanyXyz.Web, []}]
16 | end
17 |
18 | defp deps do
19 | [{:cowboy, "~> 1.0.0"},
20 | {:plug, "~> 0.9.0"}]
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/priv/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slogsdon/web-development-using-elixir/06bbdc77816a182bb3b0505438c701ecb00d8ceb/examples/company-xyz/apps/web/priv/static/.gitkeep
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/examples/company-xyz/apps/web/test/web_test.exs:
--------------------------------------------------------------------------------
1 | defmodule CompanyXyz.WebTest do
2 | use ExUnit.Case
3 |
4 | test "the truth" do
5 | assert 1 + 1 == 2
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/company-xyz/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 third-
9 | # party users, it should be done in your mix.exs file.
10 |
11 | # Sample configuration:
12 | #
13 | # config :logger, :console,
14 | # level: :info,
15 | # format: "$date $time [$level] $metadata$message\n",
16 | # metadata: [:user_id]
17 |
18 | # It is also possible to import configuration files, relative to this
19 | # directory. For example, you can emulate configuration per environment
20 | # by uncommenting the line below and defining dev.exs, test.exs and such.
21 | # Configuration from the imported file will override the ones defined
22 | # here (which is why it is important to import them last).
23 | #
24 | # import_config "#{Mix.env}.exs"
25 | # This file is responsible for configuring your application
26 | # and its dependencies with the aid of the Mix.Config module.
27 | use Mix.Config
28 |
29 | # The configuration defined here will only affect the dependencies
30 | # in the apps directory when commands are executed from the umbrella
31 | # project. For this reason, it is preferred to configure each child
32 | # application directly and import its configuration, as done below.
33 | import_config "../apps/*/config/config.exs"
34 |
35 | # Sample configuration (overrides the imported configuration above):
36 | #
37 | # config :logger, :console,
38 | # level: :info,
39 | # format: "$date $time [$level] $metadata$message\n",
40 | # metadata: [:user_id]
41 |
--------------------------------------------------------------------------------
/examples/company-xyz/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule CompanyXyz.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [apps_path: "apps",
6 | deps: deps]
7 | end
8 |
9 | # Dependencies can be Hex packages:
10 | #
11 | # {:mydep, "~> 0.3.0"}
12 | #
13 | # Or git/path repositories:
14 | #
15 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
16 | #
17 | # Type `mix help deps` for more examples and options.
18 | #
19 | # Dependencies listed here are available only for this project
20 | # and cannot be accessed from applications inside the apps folder
21 | defp deps do
22 | []
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/examples/company-xyz/mix.lock:
--------------------------------------------------------------------------------
1 | %{"cowboy": {:hex, :cowboy, "1.0.0"},
2 | "cowlib": {:hex, :cowlib, "1.0.1"},
3 | "plug": {:hex, :plug, "0.9.0"},
4 | "ranch": {:hex, :ranch, "1.0.0"}}
5 |
--------------------------------------------------------------------------------
/examples/hello_world/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 |
--------------------------------------------------------------------------------
/examples/hello_world/README.md:
--------------------------------------------------------------------------------
1 | HelloWorld
2 | ==========
3 |
4 | ** TODO: Add description **
5 |
--------------------------------------------------------------------------------
/examples/hello_world/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 third-
9 | # party users, it should be done in your mix.exs file.
10 |
11 | # Sample configuration:
12 | #
13 | # config :logger, :console,
14 | # level: :info,
15 | # format: "$date $time [$level] $metadata$message\n",
16 | # metadata: [:user_id]
17 |
18 | # It is also possible to import configuration files, relative to this
19 | # directory. For example, you can emulate configuration per environment
20 | # by uncommenting the line below and defining dev.exs, test.exs and such.
21 | # Configuration from the imported file will override the ones defined
22 | # here (which is why it is important to import them last).
23 | #
24 | # import_config "#{Mix.env}.exs"
25 |
--------------------------------------------------------------------------------
/examples/hello_world/lib/hello_world.ex:
--------------------------------------------------------------------------------
1 | defmodule HelloWorld do
2 | use Application
3 |
4 | def start(_type, _args) do
5 | import Supervisor.Spec, warn: false
6 |
7 | children = [
8 | # worker(__MODULE__, [], function: :run)
9 | ]
10 |
11 | opts = [strategy: :one_for_one, name: HelloWorld.Supervisor]
12 | Supervisor.start_link(children, opts)
13 | end
14 |
15 | def run do
16 | routes = [
17 | {"/", HelloWorld.Handler, []}
18 | ]
19 | dispatch = :cowboy_router.compile([{:_, routes}])
20 | opts = [port: 8080]
21 | env = [dispatch: dispatch]
22 | {:ok, _pid} = :cowboy.start_http(:http, 100, opts, [env: env])
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/examples/hello_world/lib/hello_world/handler.ex:
--------------------------------------------------------------------------------
1 | defmodule HelloWorld.Handler do
2 | def init({:tcp, :http}, req, opts) do
3 | headers = [{"content-type", "text/plain"}]
4 | body = "Hello world!"
5 | {:ok, resp} = :cowboy_req.reply(200, headers, body, req)
6 | {:ok, resp, opts}
7 | end
8 |
9 | def handle(req, state) do
10 | {:ok, req, state}
11 | end
12 |
13 | def terminate(_reason, _req, _state) do
14 | :ok
15 | end
16 | end
--------------------------------------------------------------------------------
/examples/hello_world/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule HelloWorld.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :hello_world,
6 | version: "0.0.1",
7 | elixir: "~> 1.0.0",
8 | deps: deps]
9 | end
10 |
11 | def application do
12 | [applications: [:logger, :cowboy],
13 | mod: {HelloWorld, []}]
14 | end
15 |
16 | defp deps do
17 | [{:cowboy, "~> 1.0.0"}]
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/examples/hello_world/mix.lock:
--------------------------------------------------------------------------------
1 | %{"cowboy": {:hex, :cowboy, "1.0.0"},
2 | "cowlib": {:hex, :cowlib, "1.0.0"},
3 | "ranch": {:hex, :ranch, "1.0.0"}}
4 |
--------------------------------------------------------------------------------
/examples/hello_world/test/hello_world_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HelloWorldTest do
2 | use ExUnit.Case
3 |
4 | test "the truth" do
5 | assert 1 + 1 == 2
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/hello_world/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/config/config.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/lib/hello_world_plug.ex:
--------------------------------------------------------------------------------
1 | defmodule HelloWorldPlug do
2 | use Application
3 |
4 | def start(_type, _args) do
5 | import Supervisor.Spec, warn: false
6 |
7 | children = [
8 | worker(__MODULE__, [], function: :run)
9 | ]
10 |
11 | opts = [strategy: :one_for_one, name: HelloWorldPlug.Supervisor]
12 | Supervisor.start_link(children, opts)
13 | end
14 |
15 | def run do
16 | opts = [port: 8080]
17 | Plug.Adapters.Cowboy.http HelloWorldPlug.Router, [], opts
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/lib/hello_world_plug/router.ex:
--------------------------------------------------------------------------------
1 | defmodule HelloWorldPlug.Router do
2 | use Plug.Router
3 |
4 | plug :match
5 | plug :dispatch
6 |
7 | get "/" do
8 | conn
9 | |> Plug.Conn.send_resp(200, "Hello world!")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule HelloWorldPlug.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :hello_world_plug,
6 | version: "0.0.1",
7 | elixir: "~> 1.0",
8 | deps: deps]
9 | end
10 |
11 | def application do
12 | [applications: [:logger, :cowboy, :plug],
13 | mod: {HelloWorldPlug, []}]
14 | end
15 |
16 | defp deps do
17 | [{:plug, "~> 0.8.1"},
18 | {:cowboy, "~> 1.0.0"}]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/mix.lock:
--------------------------------------------------------------------------------
1 | %{"cowboy": {:hex, :cowboy, "1.0.0"},
2 | "cowlib": {:hex, :cowlib, "1.0.0"},
3 | "plug": {:hex, :plug, "0.8.1"},
4 | "ranch": {:hex, :ranch, "1.0.0"}}
5 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/test/hello_world_plug_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HelloWorldPlugTest do
2 | use ExUnit.Case
3 |
4 | test "the truth" do
5 | assert 1 + 1 == 2
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/hello_world_plug/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/manuscript/00-introduction.md:
--------------------------------------------------------------------------------
1 | # Web Development with Elixir
2 |
3 | ## What you need
4 |
5 | - Some Elixir knowledge. If you don't, take a look at the [Getting Started](http://elixir-lang.org/getting_started/1.html) section of to get going.
6 | - Elixir v1.0.0 installed. If you don't, see [Installing Elixir](http://elixir-lang.org/install.html).
7 |
8 |
--------------------------------------------------------------------------------
/manuscript/01-building-a-basic-application.md:
--------------------------------------------------------------------------------
1 | # Building a Basic Application
2 |
3 | Starting with any project in a new-to-you language, a big question is usually, "Where do I put my files?" Well, fret not! We'll use Mix to generate a project skeleton for us.
4 |
5 | > Mix is a build tool that provides tasks for creating, compiling, testing Elixir projects, as well as handle dependencies, and more.
6 |
7 | Let's create our project with `mix new`:
8 |
9 | ```
10 | $ mix new hello_world --sup
11 | * creating README.md
12 | * creating .gitignore
13 | * creating mix.exs
14 | * creating config
15 | * creating config/config.exs
16 | * creating lib
17 | * creating lib/hello_world.ex
18 | * creating test
19 | * creating test/test_helper.exs
20 | * creating test/hello_world_test.exs
21 |
22 | Your mix project was created successfully.
23 | You can use mix to compile it, test it, and more:
24 |
25 | cd hello_world
26 | mix test
27 |
28 | Run `mix help` for more commands.
29 |
30 | $ cd hello_world
31 | ```
32 |
33 | We passed two arguments to the `mix new` task: a project name in snake case (`hello_world`) and the `--sup` flag. Mix used the project name as our OTP application name and converted it to camel case (`HelloWorld`) for use in module names when creating our project. The `--sup` flag let Mix know that we wanted it to create an OTP supervisor and an OTP application callback in our main `HelloWorld` module.
34 |
35 | Follow along in your own environment, or pull down the project from the [`HelloWorld`](https://github.com/slogsdon/web-development-using-elixir/tree/master/examples/hello_world) project page.
36 |
37 | ## Mixing It Up
38 |
39 | Moving on into the future, Mix will help us with our dependencies, testing our app, running our app, and more, but how does Mix know enough about our project to help us so much? Our Mixfile, of course! Let's open our `mix.exs` to take a look:
40 |
41 | ```elixir
42 | defmodule HelloWorld.Mixfile do
43 | use Mix.Project
44 |
45 | def project do
46 | [app: :hello_world,
47 | version: "0.0.1",
48 | elixir: "~> 1.0.0",
49 | deps: deps]
50 | end
51 |
52 | def application do
53 | [applications: [:logger],
54 | mod: {HelloWorld, []}]
55 | end
56 |
57 | defp deps do
58 | []
59 | end
60 | end
61 | ```
62 |
63 | Two key things to look at are `project/0` and `application/0` (the `project` and `application` functions).
64 |
65 | `project/0` provides your project's configuration (in keyword list form) to Mix.`app: :hello_world` sets our application's name in Erlang/OTP land.
66 |
67 | `version: "0.0.1"` sets our application's version, and `elixir: "~> 1.0.0"` sets the version of Elixir on which our application depends. All versions in the Elixir world typically follow [semantic versioning](http://semver.org/), so keep that in mind when you're dealing with them.
68 |
69 | `deps: deps` sets our project's dependencies with the `deps/0` function, which is currently an empty list. Using the [Hex Package manager](https://hex.pm/), dependencies are added as two-item tuples like `{:dependency, "~> 1.0.0"}`.
70 |
71 | `application/0` provides your project's OTP application configuration (in keyword list form) to Mix. `applications: [:logger]` lists the OTP applications on which your project depends. These applications will be started for you automatically when your application runs.
72 |
73 | `mod: {HelloWorld, []}` provides details for OTP to run your application. With Mix building your project's skeleton, you shouldn't need to change this, but the first element in the tuple is the module that contains the `start/2` callback function for the `Application` behaviour, which in our case is `HelloWorld`. If you ever rename your project and/or rename/refactor your modules, be sure to update this line to reflect any changes.
74 |
75 | ## Getting Down to Business
76 |
77 | Now for us to get a web application running, we'll need a server that can speak HTTP, so it's time for you to build a `HTTP/1.1` compliant server. You have fun with that. I'll wait.
78 |
79 | Don't want to do that just to write a "Hello World" app? I don't blame you, so instead of writing our own, let's use [Cowboy](https://github.com/ninenines/cowboy), a very popular Erlang web server. First thing's first. Let's add Cowboy as a dependency in our `mix.exs` file:
80 |
81 | ```elixir
82 | defp deps do
83 | [{:cowboy, "~> 1.0.0"}]
84 | end
85 | ```
86 |
87 | and to prepare a bit for the future, we need to add `:cowboy` under `applications` in `application/0`:
88 |
89 | ```elixir
90 | def application do
91 | [applications: [:logger, :cowboy],
92 | mod: {HelloWorld, []}]
93 | end
94 | ```
95 |
96 | Now, let's pull down our deps from Hex:
97 |
98 | ```
99 | $ mix deps.get
100 | Running dependency resolution
101 | Unlocked: cowboy
102 | Dependency resolution completed successfully
103 | cowlib: v1.0.0
104 | cowboy: v1.0.0
105 | ranch: v1.0.0
106 | * Getting cowboy (package)
107 | Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/cowboy-1.0.0.tar)
108 | Using locally cached package
109 | Unpacked package tarball (/Users/shane/.hex/packages/cowboy-1.0.0.tar)
110 | * Getting cowlib (package)
111 | Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/cowlib-1.0.0.tar)
112 | Using locally cached package
113 | Unpacked package tarball (/Users/shane/.hex/packages/cowlib-1.0.0.tar)
114 | * Getting ranch (package)
115 | Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/ranch-1.0.0.tar)
116 | Using locally cached package
117 | Unpacked package tarball (/Users/shane/.hex/packages/ranch-1.0.0.tar)
118 | ```
119 |
120 | Thanks to the power of Hex and S3, that shouldn't have taken long. We're now able to implement Cowboy into our project to leverage all of the goodness it provides.
121 |
122 | ## Adding the Fun (but Necessary) Bits
123 |
124 | Open up `lib/hello_world.ex` so we can get to work. Most of the work here will entail getting Cowboy set up so that it will listen for incoming requests. Even though we have set up our `mix.exs` file to start the `:cowboy` OTP application, Cowboy doesn't start HTTP or HTTPS listeners by default and relys on developers (us) to do so.
125 |
126 | We'll create a helper function that defines a set of routes for our application, compile those routes into a form the Cowboy dispatcher knows how to use, and finally, start the HTTP listener with some basic options.
127 |
128 | ```elixir
129 | def run do
130 | # Routes for our application
131 | routes = [
132 | {"/", HelloWorld.Handler, []}
133 | ]
134 |
135 | # Compile our routes so Cowboy knows
136 | # how to dispatch requests
137 | dispatch = :cowboy_router.compile([{:_, routes}])
138 |
139 | # Set some options
140 | opts = [port: 8080]
141 | env = [dispatch: dispatch]
142 | {:ok, _pid} = :cowboy.start_http(:http, 100, opts, [env: env])
143 | end
144 | ```
145 |
146 | Optionally, we can add a worker child for our supervisor tree to run this function automatically when our application starts, otherwise, the function will need to be run manually, such as in an IEx session.
147 |
148 | Resulting in:
149 |
150 | ```elixir
151 | defmodule HelloWorld do
152 | use Application
153 |
154 | def start(_type, _args) do
155 | import Supervisor.Spec, warn: false
156 |
157 | children = [
158 | # Optional worker child for HelloWorld.run/0
159 | worker(__MODULE__, [], function: :run)
160 | ]
161 |
162 | opts = [strategy: :one_for_one, name: HelloWorld.Supervisor]
163 | Supervisor.start_link(children, opts)
164 | end
165 |
166 | def run do
167 | routes = [
168 | {"/", HelloWorld.Handler, []}
169 | ]
170 | dispatch = :cowboy_router.compile([{:_, routes}])
171 | opts = [port: 8080]
172 | env = [dispatch: dispatch]
173 | {:ok, _pid} = :cowboy.start_http(:http, 100, opts, [env: env])
174 | end
175 | end
176 | ```
177 |
178 | When called, `run/0` will allow our application to respond to all requests to `http://localhost:8080/`.
179 |
180 | ### Handle Yourself Properly
181 |
182 | We defined a singular route that has some sort of relation to an undefined `HelloWorld.Handler` module, but what should be in this module? Create `lib/hello_world/handler.ex`, and put this in it:
183 |
184 | ```elixir
185 | defmodule HelloWorld.Handler do
186 | def init({:tcp, :http}, req, opts) do
187 | headers = [{"content-type", "text/plain"}]
188 | body = "Hello world!"
189 | {:ok, resp} = :cowboy_req.reply(200, headers, body, req)
190 | {:ok, resp, opts}
191 | end
192 |
193 | def handle(req, state) do
194 | {:ok, req, state}
195 | end
196 |
197 | def terminate(_reason, _req, _state) do
198 | :ok
199 | end
200 | end
201 | ```
202 |
203 | `init/3` handles the bulk of the work here. It let's Cowboy know what types of connections we wish to handle with this module (HTTP via TCP) and actually creates a response for each incoming request. We use `:cowboy_req.reply/4` to build our response with a status code, a list of headers, a response body, and the request itself as Cowboy stashes supporting information about the request in that variable.
204 |
205 | `handle/2` and `terminate/3` aren't terribly useful in this example, but in other cases, they offer the means to control the lifespan of the Erlang process that is spawned to handle the request. For now, consider them necessary boilerplate code.
206 |
207 | ## Running Our Marvelous Work
208 |
209 | Now's the time for our hard work to pay off. Pass mix as a script to IEx with `iex -S mix`:
210 |
211 | ```
212 | $ iex -S mix
213 | Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
214 |
215 | ==> ranch (compile)
216 | Compiled src/ranch_transport.erl
217 | Compiled src/ranch_sup.erl
218 | Compiled src/ranch_ssl.erl
219 | Compiled src/ranch_tcp.erl
220 | Compiled src/ranch_protocol.erl
221 | Compiled src/ranch_listener_sup.erl
222 | Compiled src/ranch_app.erl
223 | Compiled src/ranch_acceptors_sup.erl
224 | Compiled src/ranch_acceptor.erl
225 | Compiled src/ranch.erl
226 | Compiled src/ranch_server.erl
227 | Compiled src/ranch_conns_sup.erl
228 | ==> cowlib (compile)
229 | Compiled src/cow_qs.erl
230 | Compiled src/cow_spdy.erl
231 | Compiled src/cow_multipart.erl
232 | Compiled src/cow_http_te.erl
233 | Compiled src/cow_http_hd.erl
234 | Compiled src/cow_date.erl
235 | Compiled src/cow_http.erl
236 | Compiled src/cow_cookie.erl
237 | Compiled src/cow_mimetypes.erl
238 | ==> cowboy (compile)
239 | Compiled src/cowboy_sub_protocol.erl
240 | Compiled src/cowboy_middleware.erl
241 | Compiled src/cowboy_websocket_handler.erl
242 | Compiled src/cowboy_sup.erl
243 | Compiled src/cowboy_static.erl
244 | Compiled src/cowboy_spdy.erl
245 | Compiled src/cowboy_router.erl
246 | Compiled src/cowboy_websocket.erl
247 | Compiled src/cowboy_rest.erl
248 | Compiled src/cowboy_loop_handler.erl
249 | Compiled src/cowboy_protocol.erl
250 | Compiled src/cowboy_http_handler.erl
251 | Compiled src/cowboy_handler.erl
252 | Compiled src/cowboy_clock.erl
253 | Compiled src/cowboy_bstr.erl
254 | Compiled src/cowboy_app.erl
255 | Compiled src/cowboy.erl
256 | Compiled src/cowboy_http.erl
257 | Compiled src/cowboy_req.erl
258 | Compiled lib/hello_world/handler.ex
259 | Compiled lib/hello_world.ex
260 | Generated hello_world.app
261 | Interactive Elixir (1.0.0) - press Ctrl+C to exit (type h() ENTER for help)
262 | iex(1)>
263 | ```
264 |
265 | If you didn't add `HelloWorld.run/0` as a worker child in the supervisor tree, be sure to run that in the IEx console.
266 |
267 | ```
268 | iex(1)> HelloWorld.run
269 | {:ok, #PID<0.141.0>}
270 | iex(2)>
271 | ```
272 |
273 | From here, you should be able to open a browser and point it to `http://localhost:8080/` to see the "Hello world!" message presented to us from our handler.
274 |
275 | ```
276 | $ curl -i http://localhost:8080
277 | HTTP/1.1 200 OK
278 | connection: keep-alive
279 | server: Cowboy
280 | date: Tue, 14 Oct 2014 00:52:09 GMT
281 | content-length: 12
282 | content-type: text/plain
283 |
284 | Hello world!
285 | ```
286 |
287 | Check that out. Way to go! You now have the knowledge to create a basic web application, but I bet you're saying something like, "There has to be something more."
288 |
289 | You're right.
290 |
--------------------------------------------------------------------------------
/manuscript/02-the-something-more.md:
--------------------------------------------------------------------------------
1 | # The Something More
2 |
3 | While interoperating with Erlang code can provide many benefits,
4 | the resulting Elixir code can end up very non-idiomatic, and the
5 | necessary bits needed to work with Cowboy directly adds some
6 | bloat to our project which would otherwise be fairly lean.
7 |
8 | So what's the something more? Meet [Plug][plug].
9 |
10 | ## Plug In
11 |
12 | What's Plug? Let's see what the project's code repository has to say:
13 |
14 | > Plug is:
15 | >
16 | > 1. A specification for composable modules in between web applications
17 | > 2. Connection adapters for different web servers in the Erlang VM
18 |
19 | We should deconstruct those statements.
20 |
21 | ### A Specification
22 |
23 | Part of what lets Plug is its [specification][plug-spec], letting
24 | implementors know what is given to and expected from a plug a.k.a. a
25 | composable module. According to the spec, there are two types of
26 | plugs: function and module.
27 |
28 | Function plugs are single functions that follow the signature
29 | `(Plug.Conn.t, Plug.opts) :: Plug.Conn.t`. In other words, a function
30 | plug accepts a connection and an optional set of options for the plug
31 | to use, which can be any term that is a tuple, atom, integer, float,
32 | or list of the aforementioned types, and returns a connection.
33 |
34 | Module plugs operate similarly, requiring a public function `call/2`
35 | that follows the same signature as a function plug. Module plugs also
36 | require an `info/1` function to be use to preprocess options sent to
37 | it. Often, `call/2` is invoked as `call(conn, info(opts))`, especially
38 | (as we'll see later on) when a plug stack is compiled.
39 |
40 | ### Connection Adapters
41 |
42 | With Erlang being great for scalable network programming, it seems
43 | only natural that people wanted to use it for web programming over
44 | the years since Erlang's release in the mid-1980s. Erlang's standard
45 | installation comes with a long list of modules, including one for
46 | [creating HTTP servers][inets], but the `httpd` module isn't really
47 | made for production-level servers.
48 |
49 | Because of that, many people have developed some awesome HTTP servers
50 | over the years, including [Cowboy][cowboy], [Mochiweb][mochiweb],
51 | [Yaws][yaws], [Elli][elli], and [Misultin][misultin]. Wouldn't it be
52 | great if we could change the web server used in our application
53 | based on needs or even on a whim? Well, you're in luck because that
54 | is part of what Plug is trying to achieve with connection adapters.
55 |
56 | Currently, Plug only (officially) [supports Cowboy][cowboy-adapter],
57 | but there is an [open PR][elli-adapter-pr] for adding an adapter for
58 | Elli, with others probably hidden around the internets. If you ever
59 | wanted to use an Erlang or Elixir web server with Plug that isn't
60 | supported, all you would need to do is make sure you implemented
61 | the [`Plug.Conn.Adapter` behaviour][plug-adapter-behaviour].
62 | Simple, huh?
63 |
64 | Ok, enough with the yammering. Let's get back into some code.
65 |
66 | ## Route to Your Heart
67 |
68 | One benefit with using Plug is `Plug.Router`, a routing DSL with
69 | some visual similarities to [Sinatra][sinatra]. It really takes the
70 | pain away from compiling a dispatch list using the raw, Erlang term
71 | based format that Cowboy expects.
72 |
73 | ```elixir
74 | defmodule HelloWorldPlug.Router do
75 | use Plug.Router
76 |
77 | plug :match
78 | plug :dispatch
79 |
80 | get "/" do
81 | conn
82 | end
83 | end
84 | ```
85 |
86 | See? Isn't that better? `Plug.Router` has macros for handling `GET`,
87 | `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS` requests, with a
88 | general `match` macro that can be used to handle other HTTP methods
89 | you may require for your application.
90 |
91 | ## Connecting Connections
92 |
93 | Looking back at the previous code snippet (the route), you may have
94 | some questions. *What's that `conn` thing?* *Why aren't we doing
95 | anything with it?* *Why do we just return it?* These would be
96 | perfectly good things to ask considering we basically glanced over
97 | the code quickly before.
98 |
99 | Essentially, the code let's us know that we expect our application
100 | to handle `GET` requests to `/` (the root). The `get` macro we use
101 | injects a `conn` variable into the local with some macro magic (we
102 | will learn a little about this later), and because, for the moment,
103 | we're lazy and don't know any better, we let the `conn` pass
104 | through unmodified as our return expression.
105 |
106 | Wait. What's that? You don't want to be lazy anymore and want to
107 | send a message to your applications visitors? Let's say hi!
108 |
109 | ```elixir
110 | get "/" do
111 | conn
112 | |> Plug.Conn.send_resp(200, "Hello world!")
113 | end
114 | ```
115 |
116 | Here, we use `send_resp/3` from the `Plug.Conn` module to send a
117 | `200 OK` response with our message. `Plug.Conn` has a variety of
118 | functions for reading from and creating new `conn`s. We will touch
119 | on a lot of these as we progress, but if you're interested now,
120 | take a look to see [what it has to offer][plug-conn].
121 |
122 | [plug]: https://github.com/elixir-lang/plug
123 | [plug-spec]: https://github.com/elixir-lang/plug/blob/master/lib/plug.ex#L3-L21
124 | [plug-adapter-behaviour]: https://github.com/elixir-lang/plug/blob/master/lib/plug/conn/adapter.ex#L8
125 | [plug-conn]: https://github.com/elixir-lang/plug/blob/master/lib/plug/conn.ex
126 |
127 | [cowboy]: https://github.com/ninenines/cowboy
128 | [cowboy-adapter]: https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex
129 | [mochiweb]: https://github.com/mochi/mochiweb
130 | [misultin]: https://github.com/ostinelli/misultin
131 | [elli]: https://github.com/knutin/elli
132 | [elli-adapter-pr]: https://github.com/elixir-lang/plug/pull/11
133 | [yaws]: https://github.com/klacke/yaws
134 | [inets]: http://www.erlang.org/doc/man/httpd.html
135 |
136 | [sinatra]: http://www.sinatrarb.com/intro.html
137 |
--------------------------------------------------------------------------------
/manuscript/Book.txt:
--------------------------------------------------------------------------------
1 | 00-introduction.md
2 | 01-building-a-basic-application.md
3 | 02-the-something-more.md
--------------------------------------------------------------------------------
/manuscript/basic-language-knowledge.md:
--------------------------------------------------------------------------------
1 | #Language basic knowledge
2 | ## "Hello, Elixir"
3 |
4 | ```
5 | $ iex
6 | Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [kernel-poll:false]
7 |
8 | Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
9 | iex(1)>
10 | ```
11 |
12 | ```elixir
13 | iex(1)> IO.puts "hello, elixir!"
14 | hello, elixir!
15 | :ok
16 | ```
17 |
18 | ## Erlang foundation
19 |
20 | ```erlang
21 | 1> Base = "hello, erlang".
22 | "hello, erlang"
23 | 2> Base.
24 | "hello, erlang"
25 | 3> 3 + 3.
26 | 6
27 | 4> "hello, erlang".
28 | "hello, erlang"
29 | 5> [104, 101, 108, 108, 111, 44, 32, 101, 114, 108, 97, 110, 103].
30 | "hello, erlang"
31 | 6> <<"hello, erlang">>.
32 | <<"hello, erlang">>
33 | 7> <<104, 101, 108, 108, 111, 44, 32, 101, 114, 108, 97, 110, 103>>.
34 | <<"hello, erlang">>
35 | 8> [Base, 33].
36 | ["hello, erlang",33]
37 | 9> [Base, Base].
38 | ["hello, erlang","hello, erlang"]
39 | 10> erlang:iolist_to_binary([Base, <<33>>]).
40 | <<"hello, erlang!">>
41 | 11> erlang:iolist_to_binary([Base, 33]).
42 | <<"hello, erlang!">>
43 | 12> Fun = fun (B, C) -> erlang:iolist_to_binary([B, C]) end.
44 | #Fun
45 | 13> Fun("hello, erlang", 33).
46 | <<"hello, erlang!">>
47 | 14> q().
48 | ok
49 | 15>
50 | ```
51 |
52 | ### Basic module
53 |
54 | ```erlang
55 | -module(stringer).
56 | -export([concat/2]).
57 |
58 | concat(S, C) when is_list(S) ->
59 | S ++ [C];
60 | concat(B, C) ->
61 | erlang:iolist_to_binary([B, C]).
62 | ```
63 |
64 | ## Elixir foundation
65 |
66 | ```elixir
67 | iex(1)> base = "hello, elixir"
68 | "hello, elixir"
69 | iex(2)> base
70 | "hello, elixir"
71 | iex(3)> 3 + 3
72 | 6
73 | iex(4)> 'hello, elixir'
74 | 'hello, elixir'
75 | iex(5)> [104, 101, 108, 108, 111, 44, 32, 101, 108, 105, 120, 105, 114]
76 | 'hello, elixir'
77 | iex(6)> "hello, elixir"
78 | "hello, elixir"
79 | iex(7)> <<104, 101, 108, 108, 111, 44, 32, 101, 108, 105, 120, 105, 114>>
80 | "hello, elixir"
81 | iex(8)> [base, 33]
82 | ["hello, elixir", 33]
83 | iex(9)> [base, base]
84 | ["hello, elixir", "hello, elixir"]
85 | iex(10)> :erlang.iolist_to_binary([base, <<33>>])
86 | "hello, elixir!"
87 | iex(11)> :erlang.iolist_to_binary([base, 33])
88 | "hello, elixir!"
89 | iex(12)> fun = fn (b, c) -> :erlang.iolist_to_binary([b, c]) end
90 | #Function<12.50752066/2 in :erl_eval.expr/5>
91 | iex(13)> fun.("hello, elixir", 33)
92 | "hello, elixir!"
93 | iex(14)>
94 | BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
95 | (v)ersion (k)ill (D)b-tables (d)istribution
96 | ^C
97 | ```
98 |
99 | ### Basic module
100 |
101 | ```elixir
102 | defmodule Stringer do
103 | def concat(l, c) when is_list(l) do
104 | l ++ [c]
105 | end
106 | def concat(b, c) do
107 | :erlang.iolist_to_binary([b, c])
108 | end
109 | end
110 | ```
111 |
112 | ## Control statements, pattern matching, and functions
113 |
114 | ```elixir
115 | if conditional do
116 | block1
117 | else
118 | block2
119 | end
120 | ```
121 |
122 | ```elixir
123 | case conditional do
124 | true -> block1
125 | false -> block2
126 | end
127 | ```
128 |
129 | ```elixir
130 | cond do
131 | conditional1 -> block1
132 | conditional2 -> block2
133 | conditional3 -> block3
134 | end
135 | ```
136 |
137 | With `case`, non-boolean values can be matched.
138 |
139 | ```elixir
140 | case value do
141 | {:ok, v} -> block1
142 | {:error, reason} -> block2
143 | _ -> block3
144 | end
145 | ```
146 |
147 | ```elixir
148 | def fun1(value) do
149 | case value do
150 | {:ok, v} -> block1
151 | {:error, reason} -> block2
152 | _ -> block3
153 | end
154 | end
155 | ```
156 |
157 | Can reduce the complexity by moving the pattern matching to the function definitions
158 |
159 | ```elixir
160 | def fun1({:ok, v}) do
161 | block1
162 | end
163 | def fun1({:error, reason}) do
164 | block2
165 | end
166 | def fun1(_) do
167 | block3
168 | end
169 | ```
170 |
171 | ## Data structures
172 |
173 | ```elixir
174 | # lists
175 | [1, 2, 3, 4]
176 | [1 | [2 | [3 | [4 | []]]]]
177 | [ head | tail ] # head is list item and tail is list of >= 0 items
178 | [ {:prop1, value1},
179 | {:prop2, value2} ] # proplist
180 | [ prop1: value1,
181 | prop2: value2 ] # keyword list
182 | ```
183 |
184 | ```elixir
185 | # map
186 | %{ prop1: value1,
187 | prop2: value2}
188 | %{ "prop1": value1,
189 | "prop2": value2 }
190 | %{ my_map | prop1: updated_value1 }
191 | ```
192 |
193 | ```elixir
194 | defmodule MyStruct do
195 | defstruct prop1: default_value1,
196 | prop2: default_value2
197 | end
198 |
199 | struct(MyStruct, prop1: value1,
200 | prop2: value2)
201 | %{ __struct__: MyStruct,
202 | prop1: value1,
203 | prop2: value2 }
204 | %{ my_struct | prop1: updated_value1 }
205 | ```
206 |
207 | ```elixir
208 | iex(1)> defmodule MyStruct do
209 | ...(1)> defstruct prop1: nil, prop2: nil
210 | ...(1)> end
211 | {:module, MyStruct,
212 | <<70, 79, 82, 49, 0, 0, 5, 24, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 133, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>,
213 | %MyStruct{prop1: nil, prop2: nil}}
214 | iex(2)> s = struct(MyStruct)
215 | %MyStruct{prop1: nil, prop2: nil}
216 | iex(3)> s.__struct__
217 | MyStruct
218 | iex(4)> Enum.map s, fn p -> IO.inspect p end
219 | ** (Protocol.UndefinedError) protocol Enumerable not implemented for %MyStruct{prop1: nil, prop2: nil}
220 | (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
221 | (elixir) lib/enum.ex:116: Enumerable.reduce/3
222 | (elixir) lib/enum.ex:1477: Enum.reduce/3
223 | (elixir) lib/enum.ex:1092: Enum.map/2
224 | iex(4)> Map.keys s
225 | [:__struct__, :prop1, :prop2]
226 | iex(5)> %{__struct__: MyStruct, prop1: nil, prop2: nil}
227 | %MyStruct{prop1: nil, prop2: nil}
228 | ```
229 |
230 | ## Concurrency
231 |
232 | ```elixir
233 | # processes
234 | pid = spawn(fn -> :timer.sleep(1000) end)
235 | pid = spawn_link(fn -> :timer.sleep(1000) end)
236 | {pid, ref} = spawn_monitor(fn -> :timer.sleep(1000) end)
237 | ```
238 |
239 | ```elixir
240 | # message passing
241 | send(pid, message)
242 | receive do
243 | message1 -> block1
244 | message2 -> block2
245 | end
246 | receive do
247 | message1 -> block1
248 | message2 -> block2
249 | after
250 | timeout -> block3 # in ms
251 | end
252 | ```
253 |
254 | ## Object-oriented vs. functional actors
255 |
256 | ```ruby
257 | irb(main):001:0> "HELLO".downcase
258 | => "hello"
259 | ```
260 |
261 | ```elixir
262 | iex(1)> String.downcase("HELLO")
263 | "hello"
264 | ```
265 |
266 | ```ruby
267 | irb(main):001:0> "HELLO".downcase.capitalize
268 | => "Hello"
269 | ```
270 |
271 | ```elixir
272 | iex(1)> String.capitalize(String.downcase("HELLO"))
273 | "Hello"
274 | ```
275 |
276 | ```elixir
277 | iex(2)> "HELLO" |> String.downcase |> String.capitalize
278 | "Hello"
279 | ```
280 |
281 | ```ruby
282 | irb(main):001:0> "hello, ruby".concat(33)
283 | => "hello, ruby!"
284 | irb(main):002:0> "hello, ruby".send(:concat, 33)
285 | => "hello, ruby!"
286 | ```
287 |
288 | ```elixir
289 | iex(1)> pid = spawn_link(Stringer, :loop, ["hello, elixir"])
290 | #PID<0.98.0>
291 | iex(2)> send pid, {:concat, 33}
292 | {:ok, "hello, elixir!"}
293 | {:ok, "hello, elixir!"}
294 | ```
295 |
296 | ## Summary
297 |
--------------------------------------------------------------------------------
/manuscript/environment-configuration.md:
--------------------------------------------------------------------------------
1 | # Environment Configuration
2 |
3 | ## Installation
4 |
5 | If using [Homebrew][homebrew] or [Linuxbrew][linuxbrew]:
6 |
7 | ```
8 | $ brew install elixir
9 | ```
10 |
11 | Or [Chocolatey NuGet][chocolatey]:
12 |
13 | ```
14 | C:\> choco install elixir
15 | ```
16 |
17 | > Needing another method for installing Elixir? Consult [the install documentation][elixir-docs-install] on the Elixir website.
18 |
19 | During the installation, your package manager of choice will download and install both Elixir and Erlang, the underlying system Elixir uses to get a fair amount of its features, plus any other dependencies that may not be installed on your system. This process could take a considerable amount of time if your package manager cannot find pre-built executables for Elixir and Erlang, but in most cases, it should be a quick process.
20 |
21 | Once installed, verify your environment has the Elixir executables on its `PATH` with `elixir --version`, `iex --version`, and `mix --version`:
22 |
23 | > These should all report the same version number.
24 |
25 | ```
26 | $ elixir --version
27 | Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [kernel-poll:false]
28 |
29 | Elixir 1.2.0
30 | $ iex --version
31 | Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [kernel-poll:false]
32 |
33 | IEx 1.2.0
34 | $ mix --version
35 | Mix 1.2.0
36 | ```
37 |
38 | ### What are these executables?
39 |
40 | Elixir and Erlang come with a set of executable files for working with the Elixir, Erlang, and the Erlang virtual machine (VM), also known as the Bogdan/Björn’s Erlang Abstract Machine (BEAM):
41 |
42 | - `elixir`: Allows Elixir and/or BEAM files to be ran against the VM. Corresponds to Erlang's `erl_call`
43 | - `elixirc`: Compiles Elixir files to BEAM bytecode. Corresponds to Erlang's `erlc`
44 | - `mix`: Elixir's default build tool
45 | - `iex`: Interactive Elixir, Elixir's REPL. Corresponds to Erlang's `erl`
46 |
47 | We will be using `mix` and `iex` in most cases. Mix commands wrap `elixir` and `elixirc` when appropriate, so interfacing with them directly is not necessary at all times.
48 |
49 | > ### An aside: Docker
50 | >
51 | > Don't want to install Elixir to your machine but have Docker available? You can use a Docker continer to house your Elixir installation:
52 | >
53 | > ```
54 | > $ docker pull elixir
55 | > $ docker run -it --rm -h elixir.local elixir elixir --version
56 | > Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]
57 | >
58 | > Elixir 1.2.4
59 | > $ docker run -it --rm -h elixir.local elixir iex --version
60 | > Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]
61 | >
62 | > IEx 1.2.4
63 | > $ docker run -it --rm -h elixir.local elixir mix --version
64 | > Mix 1.2.4
65 | > ```
66 | >
67 | > Using Docker can increase the complexity of using Elixir, so only use it if you *really* have a reason. We're going to proceed using a locally-installed version of Elixir.
68 |
69 | [homebrew]: http://brew.sh/
70 | [linuxbrew]: http://linuxbrew.sh/
71 | [chocolatey]: https://chocolatey.org/
72 | [elixir-docs-install]: http://elixir-lang.org/install.html
73 |
74 | ## Mix commands
75 |
76 | Packaged with the Elixir standard installation, Mix is a build tool that provides tasks for creating, compiling, testing Elixir projects, as well as handle dependencies, and more. Here are a few fairly common Mix commands:
77 |
78 | | Command | Description |
79 | |----------------|---------------------------------------|
80 | | `mix` | Runs the default task |
81 | | `mix compile` | Compiles source files |
82 | | `mix deps.get` | Gets all out of date dependencies |
83 | | `mix do` | Executes the tasks separated by comma |
84 | | `mix new` | Creates a new Elixir project |
85 | | `mix run` | Runs the given file or expression |
86 | | `mix test` | Runs a project's tests |
87 |
88 | > Find an entire list of commands with the Mix command `mix help`.
89 |
90 | Mix and Elixir provide a set of default and pre-installed Mix commands, but more can be provided through Mix archives and project dependencies. We'll be exploring these and other Mix commands in later chapters.
91 |
92 | ## Editor development tools
93 |
94 | Most popular editors have at least some support for Elixir, from basic language/syntax support to deeper code introspection. While the below are a couple useful packages for Emacs and Vim, this is only a small portion of an ever-growing list of Elixir's editor and IDE support.
95 |
96 | ### Emacs
97 |
98 | - [emacs-elixir][emacs-elixir] - Basic language support
99 | - [alchemist.el][alchemist-el] - All around Elixir tooling
100 |
101 | ### Vim
102 |
103 | - [vim-elixir][vim-elixir] - Basic language support
104 | - [alchemist.vim][alchemist-vim] - Based on alchemist.el's `alchemist-server` abstraction
105 |
106 | [emacs-elixir]: https://github.com/elixir-lang/emacs-elixir
107 | [alchemist-el]: https://github.com/tonini/alchemist.el
108 | [vim-elixir]: https://github.com/elixir-lang/vim-elixir
109 | [alchemist-vim]: https://github.com/slashmili/alchemist.vim
110 |
111 | ## Summary
112 |
113 |
--------------------------------------------------------------------------------
/manuscript/images/title_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slogsdon/web-development-using-elixir/06bbdc77816a182bb3b0505438c701ecb00d8ceb/manuscript/images/title_page.png
--------------------------------------------------------------------------------