├── .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 |
50 | 51 | 52 |
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 --------------------------------------------------------------------------------