├── README.md ├── slides.pdf └── triangle ├── .gitignore ├── README.md ├── config ├── config.exs ├── dev.exs └── test.exs ├── lib └── triangle.ex ├── mix.exs ├── mix.lock └── test ├── test_helper.exs └── triangle_test.exs /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![](http://share.ryandaigle.com/cpyq2-20160115205848.png) 3 | 4 | (best viewed w/ Deckset, generated [slides here](slides.pdf)) 5 | 6 | --- 7 | 8 | # Play along 9 | 10 | Only prerequisite is to have [Elixir installed](http://elixir-lang.org/install.html) 11 | 12 | *v1.0 or greater is fine* 13 | 14 | ```bash 15 | $ mix -v 16 | Mix 1.0.5 17 | 18 | $ elixir -v 19 | Elixir 1.0.5 20 | ``` 21 | 22 | — 23 | 24 | # You are not alone 25 | 26 | * Join us throughout the week on Triangle Devs Slack ([https://triangle-devs-slack-inviter.herokuapp.com](https://triangle-devs-slack-inviter.herokuapp.com)) in the **#elixir** channel 27 | * Weekly exercism.io Elixir exercises ([http://exercism.io/languages/elixir](http://exercism.io/languages/elixir)) 28 | * Me @rwdaigle 29 | 30 | ^ Lots of places to interact, but trying to center local Elixir community in Triangle Devs Slack room. 31 | 32 | — 33 | 34 | ## Getting To Know the Elixir Development Environment 35 | 36 | ^ When learning a new language, most people focus on syntax and design patterns. Leaves you unprepared for doing actual work (how create project, where do files live etc…) 37 | 38 | ^ Once have cursory understanding of syntax, I like to understand the language environment and tooling. 39 | 40 | ^ Hope to show you the lay of the land when it comes to Elixir app dev so you know how to even get to the point to start on your first play app. Meant to be at pace you can follow on your laptop… 41 | 42 | — 43 | 44 | # What is a language environment? 45 | 46 | * Build tool 47 | * Dependency resolution 48 | * Configuration management 49 | * Testing framework 50 | * Interactive shell 51 | 52 | ^ Might seem scattered, but all support the development process 53 | 54 | ^ These are all table-stakes for modern language - we as devs expect these utilities to be well defined so we can be productive from the start 55 | 56 | ^ First thing is how to create an app, what dir structure is expected, how app is bootstrapped etc… First tool: Mix 57 | 58 | — 59 | 60 | # Mix 61 | 62 | * Project bootstrapper (like `script/rails g`) 63 | * Build utility/script runner (like Rake) 64 | * Dependency manager (like Bundler) 65 | 66 | ^ You'll interact a lot with mix because it's the core command line tool of Elixir and has many responsibilities 67 | 68 | ^ Let's see how it creates projects for us 69 | 70 | — 71 | 72 | # Create new project* 73 | 74 | ```bash 75 | $ mix help 76 | mix # Run the default task (current: mix run) 77 | mix app.start # Start all registered apps 78 | … 79 | 80 | $ mix new triangle 81 | $ cd triangle 82 | $ ls 83 | README.md config lib mix.exs test 84 | ``` 85 | 86 | ^ Look at dir structure: config, lib and test dirs 87 | 88 | ^ mix.exs: Main entrypoint for app - define sub-apps (Elixir project can have many sub-apps, all composed as a single system), dependencies, metadata 89 | 90 | ^ Compile w/ `mix compile`, look at `_build` for build artifacts. Erlang VM code. Elixir compiles to Erlang bytecode. 91 | 92 | ^ mix test 93 | 94 | ^ Tests provide convenient way to get some code executing, so go there next 95 | 96 | — 97 | 98 | # Test* 99 | 100 | ```elixir 101 | # test/triangle_test.exs 102 | test "area" do 103 | assert Triangle.area(3, 5) == 3 * 5 / 2 104 | end 105 | ``` 106 | 107 | ```bash 108 | $ mix test 109 | ``` 110 | 111 | ```elixir 112 | # lib/triangle.ex 113 | def area(base, height), do: base * height / 2 114 | ``` 115 | 116 | ^ Edit `triangle_test.exs`. `ExUnit` is Elixir's unit testing framework 117 | 118 | ^ `area` function uses single-line body. Common when simple body definitions 119 | 120 | ^ Can also run individual tests: `mix test test/triangle_test.exs:4` 121 | 122 | ^ Tests are great way to exercise your project, but sometimes need more ad-hoc environment to play in… 123 | 124 | — 125 | 126 | # IEx 127 | 128 | * Interactive elixir, REPL 129 | * Loads Elixir environment w/ shell access 130 | * Dynamically reload code 131 | 132 | ^ IEx is the easiest way to start playing with Elixir and has lot of niceties to help you navigate and get around 133 | 134 | — 135 | 136 | # IEx playground* 137 | 138 | ```bash 139 | $ iex 140 | > c "lib/triangle.ex" 141 | > Triangle.area 2, 3 142 | 3.0 143 | > r Triangle 144 | > h Enum. 145 | ``` 146 | 147 | ```bash 148 | $ iex -S mix 149 | > Triangle.area 2, 3 150 | ``` 151 | 152 | ^ Start IEx w/ `iex`. Do `a = 1` 153 | 154 | ^ Do `h` to show helpers 155 | 156 | ^ `h Enum.` to show tab-completion 157 | 158 | ^ `ls` to see FS 159 | 160 | ^ `v 1` to get previous evaluations. Very friendly, navigable environment. 161 | 162 | ^ `c "lib/triangle.ex"` to compile and load sample app. `Triangle.area 2, 3` 163 | 164 | ^ Edit triangle.ex, and `r Triangle` to get updated def. 165 | 166 | ^ Ctrl-C, Ctrl-C to quit 167 | 168 | ^ Shortcut to load all app dependencies, `iex -S mix` 169 | 170 | — 171 | 172 | # Debugging 173 | 174 | * Print messages to stdout 175 | * Attach interactive shell to running process (IEx.pry) 176 | 177 | — 178 | 179 | # Print to stdout* 180 | 181 | ```elixir 182 | def area(base, height) do 183 | IO.puts "base: #{base}, height: #{height}" 184 | base * height / 2 185 | end 186 | ``` 187 | 188 | ^ Add puts statement to `area` and run test 189 | 190 | ^ IO.puts, IO.inspect 191 | 192 | ^ Familiar string interpolation available 193 | 194 | — 195 | 196 | # Attached shell* 197 | 198 | ```elixir 199 | # triangle.ex 200 | require IEx 201 | def area(base, height), do: IEx.pry && base * height / 2 202 | ``` 203 | 204 | ```bash 205 | $ iex -S mix 206 | > Triangle.area 2, 3 207 | pry(1)> base 208 | 2 209 | pry(2)> respawn 210 | ``` 211 | 212 | ^ IEx.pry attaches to current IEx session, lets you peak/manipulate local scope 213 | 214 | ^ `respawn` gets out of IEx.pry, but any changed state is not kept 215 | 216 | — 217 | 218 | # Dependencies 219 | 220 | * Package manager is called Hex (https://hex.pm) 221 | * Dependency resolution handled by Mix 222 | * Project dependencies defined in `mix.exs` 223 | 224 | ^ Three components to dependency management. Publically accessible repo of packages, way to fetch them, and place to define them 225 | 226 | — 227 | 228 | # Add dependency* 229 | 230 | ```elixir 231 | # triangle.ex 232 | require Metrix 233 | def area(base, height) do 234 | Metrix.measure "triangle.area", fn -> base * height / 2 end 235 | end 236 | ``` 237 | 238 | ```elixir 239 | # mix.exs 240 | def application do 241 | [applications: [:logger, :metrix]] 242 | end 243 | 244 | defp deps do 245 | [ 246 | {:metrix, "~> 0.2.0"} 247 | ] 248 | end 249 | ``` 250 | 251 | ```bash 252 | $ mix deps.get 253 | ``` 254 | 255 | ^ Add simple metrics library to Triangle as example 256 | 257 | ^ Edit `Triangle`, add metrix to `application` and `deps` of `mix.exs`. In `applications` if it's a sub-app with own supervisor tree. If just a lib, only in `deps`. Depends on implementation. 258 | 259 | ^ Notice: it's all code, no YAML 260 | 261 | ^ Run `mix deps.get`, look in `deps` dir to see dependencies locally vendored 262 | 263 | ^ mix test to see Metrix output 264 | 265 | — 266 | 267 | # Configuration 268 | 269 | * `config/` contains configurations 270 | * `Config` sets up app values 271 | 272 | ^ Every lang has different way, or no consistent way at all, to define environment-specific configuration. Elixir has first class `config` support. 273 | 274 | ^ Look in `config/config.exs` 275 | 276 | — 277 | 278 | # Using config values* 279 | 280 | ```elixir 281 | # config/config.ex 282 | config :triangle, :default_length, 4 283 | ``` 284 | 285 | ```elixir 286 | # triangle.ex 287 | def equilateral(length), do: {length, length, length} 288 | def equilateral do 289 | Application.get_env(:triangle, :default_length)) 290 | |> equilateral 291 | end 292 | ``` 293 | 294 | ```elixir 295 | # triangle_test.exs 296 | test "default equilateral side length of 4" do 297 | assert Triangle.equilateral == {4, 4, 4} 298 | end 299 | ``` 300 | 301 | ^ Add config value in `config/config.exs`, first arg must match app atom 302 | 303 | ^ Add `equilateral` def to Triangle, test 304 | 305 | ^ `Application.get_env/2` to get values 306 | 307 | — 308 | 309 | # Environments 310 | 311 | * `Mix.env` available at runtime 312 | * Environment specific configs in `config/` 313 | 314 | ^ Quite often need way to toggle between configs depending on env 315 | 316 | ^ Uses file-based configurations 317 | 318 | — 319 | 320 | # Environment configuration* 321 | 322 | ```bash 323 | $ touch config/dev.exs config/test.exs 324 | ``` 325 | 326 | ```elixir 327 | # config/config.exs 328 | import_config "#{Mix.env}.exs" 329 | ``` 330 | 331 | ```elixir 332 | # config/dev.exs 333 | use Mix.Config 334 | config :triangle, :default_length, 3 335 | ``` 336 | 337 | ```elixir 338 | # config/test.exs 339 | use Mix.Config 340 | config :triangle, :default_length, 2 341 | ``` 342 | 343 | ^ Add `config/` files for test and dev, add separate length values. Run `mix test` to see errors 344 | 345 | ^ Load iex to see dev value: Application.get_env(:triangle, :default_length)) 346 | 347 | ^ Show `Mix.env` value 348 | 349 | ^ Actual environment (not app vars) accessible via `System.get_env/1` 350 | 351 | ^ I find config/Application.get_env/System.env to be a bit confusing due to the overloading of "environment". 352 | 353 | — 354 | 355 | # Bye 356 | 357 | 358 | This talk/script can be found at: 359 | 360 | > [https://github.com/rwdaigle/elixir-environment-basics](https://github.com/rwdaigle/elixir-environment-basics) 361 | 362 | I can be found 363 | 364 | > @rwdaigle 365 | -------------------------------------------------------------------------------- /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwdaigle/elixir-environment-basics/3723328cbb788a513eb549f8a686c33b4d4db671/slides.pdf -------------------------------------------------------------------------------- /triangle/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | erl_crash.dump 4 | *.ez 5 | -------------------------------------------------------------------------------- /triangle/README.md: -------------------------------------------------------------------------------- 1 | Triangle 2 | ======== 3 | 4 | ** TODO: Add description ** 5 | -------------------------------------------------------------------------------- /triangle/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 | config :triangle, :default_length, 4 19 | 20 | # It is also possible to import configuration files, relative to this 21 | # directory. For example, you can emulate configuration per environment 22 | # by uncommenting the line below and defining dev.exs, test.exs and such. 23 | # Configuration from the imported file will override the ones defined 24 | # here (which is why it is important to import them last). 25 | # 26 | import_config "#{Mix.env}.exs" 27 | -------------------------------------------------------------------------------- /triangle/config/dev.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :triangle, :default_length, 3 4 | -------------------------------------------------------------------------------- /triangle/config/test.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :triangle, :default_length, 5 4 | -------------------------------------------------------------------------------- /triangle/lib/triangle.ex: -------------------------------------------------------------------------------- 1 | defmodule Triangle do 2 | 3 | def area(base, height) do 4 | Metrix.measure "triangle.area.service", fn -> 5 | base * height / 2 6 | end 7 | end 8 | 9 | def equilateral, do: equilateral(Application.get_env(:triangle, :default_length)) 10 | def equilateral(length), do: {length, length, length} 11 | 12 | end 13 | -------------------------------------------------------------------------------- /triangle/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Triangle.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :triangle, 6 | version: "0.0.1", 7 | elixir: "~> 1.0", 8 | build_embedded: Mix.env == :prod, 9 | start_permanent: Mix.env == :prod, 10 | deps: deps] 11 | end 12 | 13 | # Configuration for the OTP application 14 | # 15 | # Type `mix help compile.app` for more information 16 | def application do 17 | [applications: [:logger, :metrix]] 18 | end 19 | 20 | # Dependencies can be Hex packages: 21 | # 22 | # {:mydep, "~> 0.3.0"} 23 | # 24 | # Or git/path repositories: 25 | # 26 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 27 | # 28 | # Type `mix help deps` for more examples and options 29 | defp deps do 30 | [ 31 | {:metrix, "~> 0.2.0"} 32 | ] 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /triangle/mix.lock: -------------------------------------------------------------------------------- 1 | %{"logfmt": {:hex, :logfmt, "3.0.2"}, 2 | "metrix": {:hex, :metrix, "0.2.0"}} 3 | -------------------------------------------------------------------------------- /triangle/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /triangle/test/triangle_test.exs: -------------------------------------------------------------------------------- 1 | defmodule TriangleTest do 2 | use ExUnit.Case 3 | 4 | test "area" do 5 | assert Triangle.area(3, 5) == 7.5 6 | end 7 | 8 | test "default equilateral size" do 9 | assert Triangle.equilateral == {5, 5, 5} 10 | end 11 | end 12 | --------------------------------------------------------------------------------