├── README.md ├── design-patterns └── REMAP_INCOMING_JSONS.md ├── introduction ├── CODE_QUALITY.md ├── ERRORS.md ├── FUNCTIONAL_PROGRAMMING.md ├── INTRODUCTION.md ├── LOGGING.md ├── WHY_ELIXIR.md └── WORKING_IN_A_TEAM.md ├── labs ├── calc │ ├── .gitignore │ ├── README.md │ ├── config │ │ └── config.exs │ ├── lib │ │ ├── calc.ex │ │ └── operation.ex │ ├── mix.exs │ └── test │ │ ├── calc_test.exs │ │ └── test_helper.exs └── ninety_nine │ ├── .formatter.exs │ ├── .gitignore │ ├── README.md │ ├── config │ └── config.exs │ ├── lib │ ├── ninety_nine.ex │ ├── p1.ex │ ├── p2.ex │ ├── p3.ex │ ├── p4.ex │ ├── p5.ex │ └── p6.ex │ ├── mix.exs │ └── test │ ├── ninety_nine_test.exs │ └── test_helper.exs └── references ├── docker └── DOCKER.md ├── elixir ├── ARCHITECTURE.md ├── CLOSURES.md ├── DEBUGGING.md ├── DOCUMENTATION.md ├── ELIXIR.md ├── ERROR_HANDLING.md ├── FUNCTION_APPLICATION.md ├── MONITORING.md ├── PATTERN-MATCHING.md ├── PIPES.md ├── SPECS.md ├── WITH.md └── images │ ├── atom-naming.png │ ├── module-naming.png │ └── variable-naming.png ├── git ├── GIT.md └── git-model.png ├── http ├── CURL.md ├── HTTP.md ├── REST.md └── http-headers-status-v3.png ├── linux ├── LINUX.md ├── REGEX.md ├── VIM.md └── vim.gif ├── markdown └── MARKDOWN.md ├── phoenix ├── CONN.md ├── CONTROLLERS.md ├── PHOENIX.md ├── PLUG.md └── ecto │ ├── ECTO.md │ ├── MIGRATIONS.md │ └── MULTI.md ├── postgres └── POSTGRES.md ├── semiotics └── SEMIOTICS.md └── typing ├── TYPING.md └── typing-fingers.png /README.md: -------------------------------------------------------------------------------- 1 | Elixir in a Team 2 | ================ 3 | 4 | > "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." — Martin Fowler 5 | 6 | Well, you just arrived in a company to work with Elixir, but wait, there are a 7 | lot of resources needed, behind coding! And the most important of all, is to 8 | learn to code for your team. People will need to read your code, typing is 9 | essential to leave your brain CPU resources focus on coding, instead of hunting 10 | letters, code-style, architecture, HTTP, git, linux, and much more. 11 | 12 | But the most important issue: you take some vacations, and your co-workers will 13 | open and read/mantain your code... oho... what happens now ? can they understand 14 | something ? can they compile ? can they change some part of the code with safety, 15 | or they become afraid of touching it ? It's all about coding for 16 | others, and once you understand that well, for sure your approach in coding will be 17 | the correct one, and your code will be a real aggregated value for your company. 18 | 19 | An excelent reference for Elixir is in [Elixir 20 | School](https://elixirschool.com/en/). I added in this repo a lot of info on how 21 | to write code for you team, enjoy. 22 | 23 | 24 | 25 | ## Introduction 26 | 27 | * [introduction](introduction/INTRODUCTION.md) - some theory to start 28 | * [why-elixir](introduction/WHY_ELIXIR.md) - good reasons to adopt 29 | * [working-in-a-team](introduction/TEAM.md) - what is the approach on learning 30 | how to code for people 31 | * [functional-programming](introduction/FUNCTIONAL_PROGRAMMING.md) - 32 | introduction and basic principles for functional programming 33 | 34 | 35 | ## References 36 | 37 | 38 | ### Elixir 39 | * [quick-reference](references/elixir/ELIXIR.md) - amazing quick reference. 40 | * [specs](references/elixir/SPECS.md) - type specifications for your functions. 41 | * [error-handling](references/elixir/ERROR_HANDLING.md) - many good strategies 42 | * [with](references/elixir/WITH.md) - pipelines using {:ok, res} and {:error, message} pattern matchings 43 | * [pipes](references/elixir/PIPES.md) - many options on how to use them 44 | * [debugging](references/elixir/DEBUGGING.md) - local and remote debugging techniques 45 | * [monitoring](references/elixir/MONITORING.md) - metrics collection, 46 | application monitoring, tracing and profiling, and exception monitoring. 47 | * [documentation](references/elixir/DOCUMENTATION.md) - writing documentation 48 | * [pattern-matching](references/elixir/PATTERN-MATCHING.md) - big advantages ! 49 | * TODO: [boilerplate](references/BOILERPLATE.md) - essential dependencies: ex_doc, dialyzer, sentry, docker, etc... 50 | * TODO: http://www.jeramysingleton.com/phoenix-templates-are-just-functions/ 51 | 52 | ### Phoenix / Ecto 53 | 54 | * [conn](references/phoenix/CONN.md) - http data structure 55 | * [controllers](references/phoenix/CONTROLLERS.md) - design patterns in 56 | controllers 57 | * [plug](references/phoenix/PLUG.md) - building components to http calls 58 | * [multi](references/phoenix/ecto/MULTI.md) - multi with examples on cross 59 | * [migrations](references/phoenix/ecto/MIGRATIONS.md) - migrations cheat sheet 60 | 61 | 62 | 63 | ### GIT 64 | * [git](references/git/GIT.md) - code repository to share code with a team 65 | 66 | ### Semiotics 67 | * [overview](references/semiotics/SEMIOTICS.md) - it's all is about 68 | communication 69 | 70 | ### Postgres 71 | * [postgres](references/postgres/POSTGRES.md) - Tutorials, tips, etc... 72 | 73 | ### Typing 74 | 75 | * [typing](references/typing/TYPING.md) - finger positions and links 76 | 77 | 78 | ### Linux 79 | * [linux](references/linux/LINUX.md) - linux 80 | * [regex](references/linux/REGEX.md) - regex tutorial and references 81 | * [vi](references/linux/VIM.md) - vi, the super hero editor 82 | 83 | ### HTTP 84 | * [http](references/http/HTTP.md) - hyper text transfer protocol introduction 85 | * [curl](references/http/CURL.md) - making http requests from the command line 86 | * [rest](references/http/REST.md) - basic and advanced concepts on building a 87 | REST API 88 | 89 | ### Deploy 90 | * TODO: [docker](references/docker/DOCKER.md) - building containers 91 | * TODO: [kubernetes](references/kubernetes/KUBERNETES.md) - sending your app to the 92 | cloud 93 | 94 | 95 | ## Labs 96 | 97 | * [calc](labs/calc) - simple calculations, using a datastructure to store every 98 | step 99 | * [ninety-nine](labs/ninety_nine) - different solutions for the 100 | [99-functional-problems](http://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/) 101 | -------------------------------------------------------------------------------- /design-patterns/REMAP_INCOMING_JSONS.md: -------------------------------------------------------------------------------- 1 | Remaping Incoming Jsons 2 | ======================= 3 | 4 | 5 | Sometimes you have to maintain your legacy API, but internally you would love 6 | to use beautiful and meaninful names. For that, an amazing solution is to add 7 | a small function plug in the incoming controller. Why not a complete plug in 8 | the router path ? because not all calls need to be remaped, and you also want 9 | to maintain your mapping solution in the place the incoming parameters are 10 | arriving, for the sake of design and also performance. Let's see the 11 | example below, on how to translate the JSON keys. 12 | 13 | In your web file, where phoenix automatically create the quote to introduce 14 | needed imports for the controller, add the translate_key function. I added 15 | some docs, but when doing meta-programming, we generally leave them away. 16 | 17 | ```elixir 18 | 19 | def controller do 20 | quote do 21 | use Phoenix.Controller, namespace: BackWeb 22 | import Plug.Conn 23 | import BackWeb.Router.Helpers 24 | import BackWeb.Gettext 25 | @doc """ 26 | Sometimes, the API call comes with a different key name on the attribute data structure. 27 | For example, http://my_server:4000/?lang , but on the db, the field 'language' is called 28 | key_lan. This functions is aimmed to translate when the data structure arrives in the 29 | schema file, inside contexts. To send out translations, we change in the 'view' render 30 | file, related to that json output. You will be happy to have all your params aligned 31 | with the database schema names :) 32 | 33 | iex> Back.Helpers.translate_key(%{a: 1, b: 2, c: 3}, :b, :z) 34 | %{a: 1, z: 2, c: 3} 35 | """ 36 | @spec translate_key(map(), String.t(), String.t()) :: map() 37 | def translate_key(attrs, source_key, target_key) do 38 | attrs 39 | |> Map.put(target_key, attrs[source_key]) 40 | |> Map.drop([source_key]) 41 | end 42 | @type controller_error :: nil | {:error, Ecto.Changeset.t()} 43 | end 44 | end 45 | 46 | ``` 47 | 48 | In your controller, add 49 | 50 | ``` 51 | plug :translate 52 | ``` 53 | 54 | On the end of your controller file, add: 55 | 56 | 57 | ```elixir 58 | def translate(%{params: params} = conn, _), do: 59 | %{conn | params: translate_key(params, "class", "type")} 60 | ``` 61 | 62 | Amazing, your controller can translate all the JSON calls like 63 | {"class":"some_data_here"} to {"type":"some_data_here"}. 64 | 65 | See how elegant, all the controller calls will received the transformed call, 66 | before they arrive in the actual controller function. 67 | 68 | 69 | -------------------------------------------------------------------------------- /introduction/CODE_QUALITY.md: -------------------------------------------------------------------------------- 1 | Code Quality 2 | ============ 3 | 4 | 5 | ## Follow a Style 6 | 7 | The codebase you’re working on needs to have a consistent style. If you’re part of a team then get together to establish a set of guidelines to work from. Simplify the effort by using existing styles in the community. Here are a few general points to keep in mind: 8 | 9 | Use informative names for variables, functions, and classes. There is no reason to be clever or sly by naming things with a small handful of characters. 10 | Don’t cram all your code within a couple of source files. Break your code into pieces where it makes sense. 11 | At the same time, don’t create functions that do 20 different things. Keep functions focused on a particular… function. 12 | Remain consistent with whitespace, naming, commenting, and the other rules you establish. 13 | 14 | 15 | 16 | ## 17 | 18 | 19 | * [quality-code-in-erlang](https://www.youtube.com/watch?v=CQyt9Vlkbis) - how to 20 | transform a big spagetti function in well and clearly diveded ones 21 | -------------------------------------------------------------------------------- /introduction/ERRORS.md: -------------------------------------------------------------------------------- 1 | Errors 2 | ====== 3 | 4 | 5 | Use LOGs only to detect software malfunction, all the business exceptions should be part of the main system, and audited. 6 | 7 | If {error, reason} is something you need to handle in your business logic you deal with it with pattern matching and you should only deal with the errors you care about. Because it is part of business logic you have to deal with is so the "complexity" is unavoidable. 8 | 9 | Don't catch errors and log anywhere except the top level of your code. It is better to bubble up. Error messages should ideally be tuples of atoms with as much information as possible so that you clearly know which sort of errors to handle and which to ignore. 10 | 11 | 12 | 13 | * actionable errors - i.e. expected errors. When those happen we need to handle them gracefully, so that our application can continue working correctly. A good example of this is invalid data provided by the user or data not present in the database. In general we want to handle this kind of errors by using tuple return values ({:ok, value} | {:error, reason}) - the consumer can match on the value and act on the result. 14 | 15 | * fatal errors - errors that place our application in undefined state - violate some system invariant, or make us reach a point of no return in some other way. A good example of this is a required config file not present or a network failing in the middle of transmitting a packet. With that kind of errors we usually want to crash and lean on the supervision system to recover - we handle all errors of this kind in a unified way. That’s exactly what the let it crash philosophy is about - it’s not about letting our application burn in case of any errors, but avoiding excessive code for handling errors that can’t be handled gracefully. You can read more about what the “let it crash” means in the excellent article by Fred Hebert of the “Learn You Some Erlang for Great Good” fame - “The Zen of Erlang”. 16 | 17 | -------------------------------------------------------------------------------- /introduction/FUNCTIONAL_PROGRAMMING.md: -------------------------------------------------------------------------------- 1 | Functional Programming 2 | ====================== 3 | 4 | ## Basic 5 | 6 | 7 | * https://medium.com/making-internets/functional-programming-elixir-pt-1-the-basics-bd3ce8d68f1b 8 | 9 | 10 | ## Advanced 11 | 12 | * [try-haskell](http://tryhaskell.org/) - because haskell is a pure functional 13 | programming, and maybe you come from OO world, you will need a wash brain... 14 | or even change your brain, and Haskell comes to save 15 | 16 | * [learn-you-a-haskell](http://learnyouahaskell.com) - amazing book to help you 17 | understand FP... a real wash-brain for OO devs... please, don't try to learn 18 | Scala and move slowly, unless you are fine to have a spaghetti paradigm in 19 | you code. 20 | 21 | * [future-learn](https://www.futurelearn.com/courses/functional-programming-haskell) 22 | - seems to be an excellent course 23 | 24 | * [the-most-adequate-guide](https://github.com/MostlyAdequate/mostly-adequate-guide) 25 | - if you would love to code FP in javascript this guide is a must. Also very 26 | good to understand the concepts. 27 | -------------------------------------------------------------------------------- /introduction/INTRODUCTION.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Even bad code can function. But if code isn’t clean, it can bring a development organization to its knees. Every year, countless hours and significant resources are lost because of poorly written code. But it doesn’t have to be that way. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /introduction/LOGGING.md: -------------------------------------------------------------------------------- 1 | Logging 2 | ======= 3 | 4 | 5 | * Diagnostic logging: has been discussed by others already and I won't belabor the point much more. I'll just say you should strongly think about what happens if there's a log failure? Do you care enough to throw an exception up through the app or manage it another way? This is an "it depends" but logging info level messages probably should be skipped. That is the diagnostic part of logging, the most important one and basically the one that developers could understand easier, as it is more part of their daily work routine. The diagnostic logging takes care of recording the events that happen during runtime ( method calls, input/outputs, HTTP calls, SQL executions ) 6 | 7 | * Audit logging: is a business requirement. Audit logging captures significant events in the system and are what management and the legal eagles are interested in. This is things like who signed off on something, who did what edits, etc. As a sysadmin or developer troubleshooting the system, you're probably only mildly interested in these. However, in many cases this kind of logging is absolutely part of the transaction and should fail the whole transaction if it can't be completed. The audit logging is responsible for recording more abstract, business logic events. Such events can be user actions (adding/editing/removal of content, transactions, access data) or other things that have either managerial value or, more importantly, legal value. Special attention on keeping the state on the central database, so you can officially track them for compliance, and also create code to react to business events, something that you couldn't achieve if you simply throw the information to an external text file. 8 | -------------------------------------------------------------------------------- /introduction/WHY_ELIXIR.md: -------------------------------------------------------------------------------- 1 | Why Elixir 2 | ========== 3 | 4 | We have been in San Francisco, and it's amazing how people there are moving from 5 | Rails to Elixir! Yeah!. See 6 | [how](https://medium.com/@elviovicosa/5-reasons-you-should-use-phoenix-instead-of-rails-in-your-next-project-504b4d83c48e) 7 | people are talking about it. 8 | 9 | 10 | 11 | 12 | ## Declarative, instead of Imperative 13 | 14 | Elixir is better for declarative programming, but you should pay attention to 15 | make your code declarative anyway. 16 | 17 | *Imperative programming*: telling the “machine” how to do something, and as a result what you want to happen will happen. 18 | 19 | *Declarative programming*: telling the “machine” what you would like to happen, and letting the computer figure out how to do it. 20 | 21 | 22 | 23 | ```javascript 24 | 'use strict' 25 | 26 | const sumAllMultiples = limit => { 27 | let sum = 0 28 | 29 | for(let i = 0; i < limit; i++) { 30 | if(i % 3 == 0 || i % 5 == 0) { 31 | sum += i; 32 | } 33 | } 34 | return sum; 35 | } 36 | 37 | console.log(sumAllMultiples(1000)); 38 | ``` 39 | 40 | 41 | The same solution in Elixir 42 | 43 | ```elixir 44 | 1..999 |> Enum.to_list 45 | |> Enum.filter(&(rem(&1, 3) == 0 or rem(&1, 5) == 0)) 46 | |> Enum.sum 47 | |> IO.puts 48 | ``` 49 | 50 | 51 | ## Microservices in-the-box 52 | Instead of creating a new docker for every microservices, consuming tons of 53 | memory/CPU, and relying on external libs like 54 | [this](https://github.com/Netflix/conductor) made by Netflix, and other gigants, 55 | you can have all the power of microservice distribution with a "Visual Studio" 56 | monolith experience, with all the necessary tools in the box. If you are 57 | building a startup, for sure microservice architecture is the 58 | [wrong](https://hackernoon.com/if-youre-thinking-about-microservices-for-an-mvp-you-re-probably-doing-it-wrong-6fef8341fce4) 59 | option, but anyway, you feel and believe that in 2 years your new app will have 60 | some thousand of users, and you would like to be ready. So in Elixir you can 61 | start with the most simplicity (more than Java!), and have an in-the-box super 62 | scalability. The [cowboy](https://ninenines.eu) web-server is a dream server, 63 | think that every request receives its own process, in parallel... and you are 64 | done, a super simple nano-service architeture, ready to receive 65 | [millions](http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/) 66 | of requests. See 67 | [here](http://tjheeta.github.io/2016/12/16/dawn-of-the-microlith-monoservices-microservices-with-elixir/) 68 | a deep comparison between the Elixir BEAM machine vs Standard Microservices 69 | architcture. 70 | 71 | 72 | ## Actor based concurrency model 73 | 74 | Instead of mutex, semaphores, and much more complexity, actor based model is 75 | language native and light, very light. Many 76 | [libs](http://berb.github.io/diploma-thesis/original/054_actors.html) today are 77 | adding actor based concurrency model, but really farway from the Beam Machine, 78 | behind the Elixir/Erlang binary files, where garbage collector is per process, 79 | etc... that build a soft real time system. 80 | 81 | 82 | ## Real time scalability 83 | 2 million users connectect in the same server, using web-sockets, with near zero 84 | CPU usage, as described 85 | [here](http://phoenixframework.org/blog/the-road-to-2-million-websocket-connections). 86 | 87 | 88 | ## Code clarity and size 89 | 90 | "We’ve also seen an improvement in code clarity. We’re converting our 91 | notifications system from Java to Elixir. The Java version used an Actor system 92 | and weighed in at around 10,000 lines of code. The new Elixir system has shrunk 93 | this to around 1000 lines." . See 94 | [here](https://pragtob.wordpress.com/2017/07/26/choosing-elixir-for-the-code-not-the-performance/) 95 | for more details. 96 | 97 | ## Web framework 98 | 99 | You can use phoenix, that is an amazing framework for web and database. Because 100 | functional programming is so powerfull, you will get a simple code with a lot of 101 | power. See [here](https://www.slant.co/topics/362/~best-backend-web-frameworks) 102 | the comparison. 103 | 104 | 105 | ## Future oriented 106 | 107 | Joe Armstrong, creator of Erlang, said something along the lines of: "In OO, if you ask a banana, you might receive a banana with the gorilla and the jungle". When we encapsulate data and behaviour in an object, we have no idea about what is going on there. Of course, one might argue that good OO design might solve this problem. I agree to a certain degree since normally you don't have control over third-party libraries. See [here](https://janjiss.com/the-way-of-modern-web/) for more details. 108 | 109 | 110 | 111 | 112 | 113 | 114 | ## Big and successful companies use Elixir 115 | See 116 | [here](https://codesync.global/media/successful-companies-using-elixir-and-erlang/) 117 | and [here](https://www.netguru.co/blog/10-companies-use-elixir) 118 | 119 | -------------------------------------------------------------------------------- /introduction/WORKING_IN_A_TEAM.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/introduction/WORKING_IN_A_TEAM.md -------------------------------------------------------------------------------- /labs/calc/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /labs/calc/README.md: -------------------------------------------------------------------------------- 1 | # Calc 2 | 3 | Simplest project, but focused on your reader (the programmer who will maintain 4 | this code, or you in the future) 5 | 6 | 7 | ### Problem Description 8 | Build a sum calculator for your fellow programmer, using ex_docs, specs, README, and all the stuff necessary to make a non mathematician to understand your software. 9 | 10 | ### Creative Solution 11 | What is the project name ? Sum ? if sum, this is the function name. Maybe "Calculator" ? If we want to add more calculations, it's fine! But if it's only sum ? Math ! If we call calculator for only one "sum", would confuse our fellow programmer ! Calculator for a project name can be too big... let's call it calc. 12 | 13 | What about the function name ? sum or add ? They are synonymous, but what is the better option ? After some research, see the tip below: 14 | 15 | They're pretty much the same. Addition is usually used to denote summation of 2 numbers, whereas summation is used for sequences of numbers. 16 | 17 | 'Sum' is also used to denote the result of a summation/addition. 18 | 19 | Summing is the result of adding n versions of a function or equation together, which is denoted using Σ. 20 | 21 | What about the variables ? x ? var1 ? but theses are not summing knowledge to our fellow programmer! Let's call number ? 22 | -------------------------------------------------------------------------------- /labs/calc/config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :calc, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:calc, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /labs/calc/lib/calc.ex: -------------------------------------------------------------------------------- 1 | defmodule Calc do 2 | @moduledoc """ 3 | Documentation for Calc. 4 | """ 5 | 6 | @typedoc "the term that will receive an addition" 7 | @type term1 :: integer | float 8 | 9 | @typedoc "the term that will be added over the first term" 10 | @type term2 :: integer | float 11 | 12 | @typedoc "result is the addition" 13 | @type result :: integer | float 14 | 15 | 16 | @typedoc "function that will in fact calculate between two terms" 17 | @type calc_operator :: fun() 18 | 19 | 20 | 21 | 22 | @doc """ 23 | Add two elemets, i.e. terms, one over another. 24 | 25 | ## Examples 26 | 27 | iex> Calc.add(1, 1) 28 | 3 29 | 30 | """ 31 | @spec add(term1, term2) :: result 32 | def add(term1, term2) do 33 | term1 + term2 34 | end 35 | 36 | 37 | def calculate(term1, term2, calc_operator) do 38 | %Operation{} 39 | |> Operation.set_name("jim") 40 | |> Operation.set_calc_operator(calc_operator) 41 | |> Operation.set_term1(term1) 42 | |> Operation.set_term2(term2) 43 | |> Operation.execute 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /labs/calc/lib/operation.ex: -------------------------------------------------------------------------------- 1 | defmodule Operation do 2 | @moduledoc """ 3 | Data strucutre to pipe a mathematical operation between two terms 4 | see [here](https://elixir-lang.org/getting-started/structs.html) for more details 5 | on how to deal with structs. 6 | """ 7 | 8 | @typedoc "Fields of an operation data structure" 9 | @type operation :: %Operation{ 10 | operation_type: String.t, 11 | term1: integer | float, 12 | term2: integer | float, 13 | result: integer | float, 14 | calc_operator: calc_operator 15 | } 16 | defstruct operation_type: nil, term1: nil, term2: nil, result: nil, calc_operator: nil 17 | 18 | @typedoc "the term that will receive an addition" 19 | @type term1 :: integer | float 20 | 21 | @typedoc "the term that will be added over the first term" 22 | @type term2 :: integer | float 23 | 24 | @typedoc "result is the addition" 25 | @type result :: integer | float 26 | 27 | @typedoc "function that will in fact make the calculation" 28 | @type calc_operator :: fun() 29 | 30 | 31 | 32 | 33 | @doc "add the operation operation_type in our data structure" 34 | @spec set_operation_type(operation, String.t) :: operation 35 | def set_operation_type(operation, operation_type), do: %{operation | operation_type: operation_type} 36 | 37 | @doc "add the first term" 38 | @spec set_term1(operation, term1) :: operation 39 | def set_term1(operation, term1), do: %{operation | term1: term1} 40 | 41 | @doc "add the term2" 42 | @spec set_term2(operation, term2) :: operation 43 | def set_term2(operation, term2), do: %{operation | term2: term2} 44 | 45 | @doc "setup the result" 46 | @spec set_result(operation, result) :: operation 47 | def set_result(operation, result) when is_integer(result), 48 | do: %{operation | result: result} 49 | 50 | @doc "setup the function that will calculate the result from both terms" 51 | @spec set_calc_operator(operation, calc_operator) :: operation 52 | def set_calc_operator(operation, calc_operator) when is_function(calc_operator), 53 | do: %{operation | calc_operator: calc_operator} 54 | 55 | 56 | @spec execute(operation) :: operation 57 | def execute(op), do: %{op | result: op.calc_operator.(op.term1, op.term2)} 58 | end 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /labs/calc/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Calc.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :calc, 7 | version: "0.1.0", 8 | elixir: "~> 1.5", 9 | start_permanent: Mix.env == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger] 18 | ] 19 | end 20 | 21 | # Run "mix help deps" to learn about dependencies. 22 | defp deps do 23 | [{:dialyxir, "~> 0.5", only: [:dev], runtime: false}] 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /labs/calc/test/calc_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CalcTest do 2 | use ExUnit.Case 3 | doctest Calc 4 | 5 | test "calculates using add operation" do 6 | assert Calc.add(3, 3) == 6 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /labs/calc/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /labs/ninety_nine/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /labs/ninety_nine/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | ninety_nine-*.tar 24 | 25 | -------------------------------------------------------------------------------- /labs/ninety_nine/README.md: -------------------------------------------------------------------------------- 1 | # NinetyNine 2 | 3 | **TODO: Add description** 4 | 5 | ## Installation 6 | 7 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 8 | by adding `ninety_nine` to your list of dependencies in `mix.exs`: 9 | 10 | ```elixir 11 | def deps do 12 | [ 13 | {:ninety_nine, "~> 0.1.0"} 14 | ] 15 | end 16 | ``` 17 | 18 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 19 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 20 | be found at [https://hexdocs.pm/ninety_nine](https://hexdocs.pm/ninety_nine). 21 | 22 | -------------------------------------------------------------------------------- /labs/ninety_nine/config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :ninety_nine, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:ninety_nine, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/ninety_nine.ex: -------------------------------------------------------------------------------- 1 | defmodule NinetyNine do 2 | end 3 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p1.ex: -------------------------------------------------------------------------------- 1 | defmodule P1 do 2 | @moduledoc """ 3 | Find the last element of a list. 4 | Example: 5 | my_last(x,[a,b,c,d]). 6 | x = d 7 | """ 8 | 9 | @doc "pre-defined function" 10 | def find_last1(list), do: List.last(list) 11 | 12 | @doc "using recursion" 13 | def find_last2([]), do: [] 14 | def find_last2([e]), do: e 15 | def find_last2([_|t]), do: find_last2(t) 16 | 17 | @doc "using pipe" 18 | def find_last3(list), do: 19 | list 20 | |> Enum.reverse 21 | |> List.first 22 | 23 | @doc "function composition" 24 | def find_last4(list), 25 | do: find_last_fun().(list) 26 | 27 | def find_last_fun(), do: 28 | fn list -> 29 | list |> Enum.reverse |> List.first 30 | end 31 | 32 | @doc "using index" 33 | def find_last5(list), do: 34 | list 35 | |> Enum.reverse 36 | |> Enum.at(0) 37 | 38 | @doc "using fold left" 39 | def find_last6(list), do: 40 | list 41 | |> List.foldl(0, fn(x, _) -> x end) 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p2.ex: -------------------------------------------------------------------------------- 1 | defmodule P2 do 2 | @moduledoc """ 3 | Find the last element of a list. 4 | Example: 5 | my_last(x,[a,b,c,d]). 6 | x = c 7 | """ 8 | 9 | @doc "using recursion" 10 | def find_last_but_one([]), do: [] 11 | def find_last_but_one([e, _]), do: e 12 | def find_last_but_one([_|t]), do: find_last_but_one(t) 13 | 14 | 15 | @doc "using pipes [drop the last, and get the last]" 16 | def find_last_but_one1(list), do: 17 | list 18 | |> Enum.drop(-1) 19 | |> List.last 20 | 21 | 22 | @doc "using pipes and index" 23 | def find_last_but_one2(list), do: 24 | list 25 | |> Enum.reverse 26 | |> Enum.at(1) 27 | 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p3.ex: -------------------------------------------------------------------------------- 1 | defmodule P3 do 2 | @moduledoc """ 3 | Find the K'th element of a list. 4 | The first element in the list is number 1. 5 | Example: 6 | ?- element_at(X,[a,b,c,d,e],3). 7 | X = c 8 | """ 9 | 10 | @doc "using pre-defined function [work with negative indexes also]" 11 | def element_at(list, index), do: Enum.at(list, index) 12 | 13 | @doc "using recursion [only with positive indexes]" 14 | def element_at1([], _), do: [] 15 | def element_at1([h|_], 1), do: h 16 | def element_at1([h|t], index), do: 17 | element_at1(t, index - 1) 18 | 19 | 20 | end 21 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p4.ex: -------------------------------------------------------------------------------- 1 | defmodule P4 do 2 | @moduledoc """ 3 | Find the number of elements of a list. 4 | """ 5 | 6 | @doc "pre-defined function" 7 | def count(list), do: Enum.count(list) 8 | 9 | 10 | 11 | @doc "using recursion" 12 | def count1([]), do: 0 13 | def count1([_|t]), do: 1 + count(t) 14 | 15 | 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p5.ex: -------------------------------------------------------------------------------- 1 | defmodule P5 do 2 | @moduledoc """ 3 | Reverse a list 4 | """ 5 | 6 | 7 | @doc "pre-defined function" 8 | def reverse(list), do: Enum.reverse(list) 9 | 10 | 11 | @doc "using recursion" 12 | def reverse1([]), do: [] 13 | def reverse1([h|t]), do: reverse(t) ++ [h] 14 | 15 | 16 | 17 | end 18 | -------------------------------------------------------------------------------- /labs/ninety_nine/lib/p6.ex: -------------------------------------------------------------------------------- 1 | defmodule P6 do 2 | @moduledoc """ 3 | Find out whether a list is a palindrome. 4 | """ 5 | 6 | 7 | def is_palindrome?(list), do: list == Enum.reverse(list) 8 | 9 | 10 | end 11 | -------------------------------------------------------------------------------- /labs/ninety_nine/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule NinetyNine.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :ninety_nine, 7 | version: "0.1.0", 8 | elixir: "~> 1.6", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger] 18 | ] 19 | end 20 | 21 | # Run "mix help deps" to learn about dependencies. 22 | defp deps do 23 | [ 24 | # {:dep_from_hexpm, "~> 0.3.0"}, 25 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}, 26 | ] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /labs/ninety_nine/test/ninety_nine_test.exs: -------------------------------------------------------------------------------- 1 | defmodule NinetyNineTest do 2 | use ExUnit.Case 3 | doctest NinetyNine 4 | 5 | test "greets the world" do 6 | assert NinetyNine.hello() == :world 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /labs/ninety_nine/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /references/docker/DOCKER.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/docker/DOCKER.md -------------------------------------------------------------------------------- /references/elixir/ARCHITECTURE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/elixir/ARCHITECTURE.md -------------------------------------------------------------------------------- /references/elixir/CLOSURES.md: -------------------------------------------------------------------------------- 1 | Closures 2 | ======== 3 | 4 | 5 | What closures add is the ability to give a subroutine attributes (or rather, add attributes to a reference to a subroutine). So you can write a subroutine that takes arguments and returns a subroutine that has those arguments as parameters. 6 | 7 | ```elixir 8 | closures = (0..2) 9 | |> Enum.map &( fn () -> IO.puts(&1) end) 10 | 11 | closures 12 | |> Enum.each &(&1.()) 13 | ``` 14 | 15 | 16 | * https://stackoverflow.com/questions/1305570/closures-why-are-they-so-useful 17 | * https://reprog.wordpress.com/2010/02/27/closures-finally-explained/ 18 | * http://www.perlmonks.org/?node=Why%20Closures%3F 19 | * https://www.electricmonk.nl/log/2011/05/20/closures-and-when-theyre-useful/ -> use case 20 | * https://www.quora.com/Why-are-closures-important-in-JavaScript 21 | * https://www.amberbit.com/blog/2015/6/14/closures-elixir-vs-ruby-vs-javascript/ 22 | 23 | * 24 | -------------------------------------------------------------------------------- /references/elixir/DEBUGGING.md: -------------------------------------------------------------------------------- 1 | Debugging 2 | ========= 3 | 4 | ## Local 5 | 6 | 7 | #### IO.inspect 8 | 9 | From elixir 1.4, you can use IO.inspect even during a pipe :) 10 | 11 | ```elixir 12 | [1, 2, 3] 13 | |> IO.inspect(label: "before") 14 | |> Enum.map(&(&1 * 2)) 15 | |> IO.inspect(label: "after") 16 | |> Enum.sum 17 | ``` 18 | 19 | #### Pry 20 | 21 | 22 | ```elixir 23 | require IEx 24 | IEx.pry() # <- add this where you want to stop 25 | ``` 26 | 27 | to restart: 28 | 29 | ``` 30 | respawn 31 | ``` 32 | 33 | 34 | 35 | #### Observer 36 | 37 | An insight tool for all kinds of metrics for a running BEAM node. 38 | 39 | 40 | 41 | ``` 42 | :observer.start() 43 | ``` 44 | 45 | 46 | * [pry-inspect](http://blog.plataformatec.com.br/2016/04/debugging-techniques-in-elixir-lang/) - debugging techniques in elixir 47 | * [observer](http://www.akitaonrails.com/2015/11/22/observing-processes-in-elixir-the-little-elixir-otp-guidebook) 48 | - observing processes in elixir - The Little Elixir & OTP Guidebook 49 | * [debuggin-phoenix](https://medium.com/@diamondgfx/debugging-phoenix-with-iex-pry-5417256e1d11) - debugging views, controllers, etc... 50 | * [tracing](https://zorbash.com/post/debugging-elixir-applications/) - tracing, 51 | EPMD, etc... 52 | 53 | 54 | 55 | 56 | ## Production 57 | 58 | #### Observer remotely 59 | 60 | ``` 61 | iex --name sally -S mix s 62 | epmd -names 63 | iex --name bob 64 | Node.connect(:"sally@Gregs-MacBook-Pro-2.wework.com") 65 | :observer.start() 66 | ``` 67 | * 68 | [remote-observer-scripts](https://github.com/chazsconi/connect-remote-elixir-docker) 69 | - This script allows you to start a local IEx session and connect it to a remotely running Docker node. 70 | 71 | * [recon](https://github.com/ferd/recon) - collection of functions and scripts to debug Erlang in production 72 | * [observer-cli](https://github.com/zhongwencool/observer_cli) - like the 73 | observer, but in a CLI environment 74 | * 75 | [observer-remotely](https://sgeos.github.io/elixir/erlang/observer/2016/09/16/elixir_erlang_running_otp_observer_remotely.html) 76 | - use the graphical observer connected to a remote node 77 | 78 | 79 | * [beam-without-epmd](https://www.erlang-solutions.com/blog/erlang-and-elixir-distribution-without-epmd.html) 80 | - Erlang (and Elixir) distribution without epmd 81 | -------------------------------------------------------------------------------- /references/elixir/DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | Documentation 2 | ============= 3 | 4 | An important part of writing code is making it easy to understand for other developers or your future self. 5 | 6 | In a perfect world, this is achieved through clear and concise code that shows the intent of the developer. There really is no better documentation than the code itself. 7 | 8 | However, comments and documentation are a critically important tool for learning. Newbies can’t be expected to pick up something new from the source code alone, and even experienced developers will spend a lot of their time referencing documentation in order to achieve their goals. 9 | 10 | Documentation in Elixir is a first-class citizen, and so there are a couple of tools that make writing and using documentation in Elixir particularly useful. 11 | 12 | 13 | 14 | * 15 | [writing-documentation](https://www.culttt.com/2016/10/19/writing-comments-documentation-elixir/) 16 | - details and examples on how to write documentations for modules, functions, 17 | etc.. 18 | -------------------------------------------------------------------------------- /references/elixir/ELIXIR.md: -------------------------------------------------------------------------------- 1 | # Elixir Quick Reference 2 | A quick reference for the Elixir programming Language and standard library. 3 | 4 | Elixir Website: http://elixir-lang.org/
5 | Elixir Documentation: http://elixir-lang.org/docs.html
6 | Elixir Source: https://github.com/elixir-lang/elixir
7 | Try Elixir Online: https://glot.io/new/elixir
8 | Elixir Regex editor/testor: http://www.elixre.uk/
9 | This Document: https://github.com/itsgreggreg/elixir_quick_reference
10 | 11 | 12 | 13 | # Table of Contents 14 | 15 | - [Basic Types](#basic-types) 16 | - [Integer](#integer) 17 | - [Float](#float) 18 | - [Atom](#atom) 19 | - [Boolean](#boolean) 20 | - [Nil](#nil) 21 | - [Binary](#binary) 22 | - [String](#string) 23 | - [Escape Sequences](#escape-sequences) 24 | - [Regular Expression](#regular-expression) 25 | - [Collection Types](#collection-types) 26 | - [List](#list) 27 | - [Charlist](#charlist) 28 | - [Tuple](#tuple) 29 | - [Keyword List](#keyword-list) 30 | - [Map](#map) 31 | - [Struct](#struct) 32 | - [Range](#range) 33 | - [Streams](#streams) 34 | - [Syntax](#syntax) 35 | - [Variables](#variables) 36 | - [Operators](#operators) 37 | - [Standard infix](#standard-infix) 38 | - [Standard prefix](#standard-prefix) 39 | - [= (match)](#-match) 40 | - [^ (pin)](#-pin) 41 | - [|> (pipe)](#-pipe) 42 | - [=~ (string match)](#-string-match) 43 | - [? (codepoint)](#-codepoint) 44 | - [& (capture)](#-capture) 45 | - [Ternary](#ternary) 46 | - [in](#in) 47 | - [Comments](#comments) 48 | - [Semicolons](#semicolons) 49 | - [Do, End](#do-end) 50 | - [Pattern Matching](#pattern-matching) 51 | - [Binaries](#binaries) 52 | - [Reserved Words](#reserved-words) 53 | - [Truthiness](#truthiness) 54 | - [Sorting](#sorting) 55 | - [Modules](#modules) 56 | - [Declaration](#declaration) 57 | - [Module Functions](#module-functions) 58 | - [Private Functions](#private-functions) 59 | - [Working with other modules](#working-with-other-modules) 60 | - [Attributes](#attributes) 61 | - [Documentation](#documentation) 62 | - [Introspection](#introspection) 63 | - [Errors](#errors) 64 | - [Raise](#raise) 65 | - [Custom Error](#custom-error) 66 | - [Rescue](#rescue) 67 | - [Control Flow](#control-flow) 68 | - [if/unless](#ifunless) 69 | - [case](#case) 70 | - [cond](#cond) 71 | - [throw/catch](#throwcatch) 72 | - [with](#with) 73 | - [Guards](#guards) 74 | - [Anonymous Functions](#anonymous-functions) 75 | - [Comprehensions](#comprehensions) 76 | - [Sigils](#sigils) 77 | - [Metaprogramming](#metaprogramming) 78 | - [Processes](#processes) 79 | - [Structs](#structs) 80 | - [Working with Files](#working-with-files) 81 | - [Erlang Interoperability](#erlang-interoperability) 82 | - [IEx](#iex) 83 | - [Mix](#mix) 84 | - [Applications](#applications) 85 | - [Tasks](#tasks) 86 | - [Tests](#tests) 87 | - [Style Guide](#style-guide) 88 | 89 | 90 | 91 | 92 | ## Basic Types 93 | 94 | ### Integer 95 | Can be specified in base 10, hex, or binary. All are stored as base 10. 96 | 97 | ```elixir 98 | > 1234567 == 1_234_567 # true 99 | > 0xcafe == 0xCAFE # true 100 | > 0b10101 == 65793 # true 101 | > 0xCafe == 0b1100101011111110 # true 102 | > 0xCafe == 51_966 # true 103 | > Integer.to_string(51_966, 16) == "CAFE" # true 104 | > Integer.to_string(0xcafe) == "51996" # true 105 | ``` 106 | 107 | ### Float 108 | 64bit double precision. Can be specified with an exponent. Cannot begin or end with `.`. 109 | 110 | ```elixir 111 | > 1.2345 112 | > 0.001 == 1.0e-3 # true 113 | > .001 # syntax error! 114 | ``` 115 | 116 | ### Atom 117 | Constants whose name is their value.
118 | Are named in this format: 119 | 120 | ![Atom Naming](https://rawgit.com/itsgreggreg/elixir_quick_reference/80954a5e9bd06d5a009857a97bfad640d3cbd31a/images/atom-naming.png)
121 | To use other characters you must quote the atom.
122 | TODO: Note which characters can be used when quoted.
123 | Stored in a global table once used and never de-allocated so avoid programmatic creation.
124 | 125 | ```elixir 126 | > :something 127 | > :_some_thing 128 | > :allowed? 129 | > :Some@Thing@12345 130 | > :"Üñîçødé and Spaces" 131 | > Atom.to_string(:Yay!) # "Yay!" 132 | > :123 # syntax error! 133 | ``` 134 | 135 | ### Boolean 136 | `true` and `false` are just syntactic sugar for `:true` and `:false` and not a special type. 137 | 138 | ```elixir 139 | > true == :true # true 140 | > false == :false # true 141 | > is_boolean(:true) # true 142 | > is_atom(false) # true 143 | > is_boolean(:True) # false! 144 | ``` 145 | 146 | ### Nil 147 | `nil` is syntactic sugar for `:nil` and is not a special type. 148 | 149 | ```elixir 150 | > nil == :nil # true 151 | > is_atom(nil) # true 152 | ``` 153 | 154 | ### Binary 155 | A binary is a sequence of bytes enclosed in `<< >>` and separated with `,`.
156 | By default each number is 8 bits though size can be specified with:
157 | `::size(n)`, `::n`, `::utf8`, `::utf16`, `::utf32` or `::float`
158 | If the number of bits in a binary is not divisible by 8, it is considered a bitstring.
159 | Binaries are concatenated with `<>`. 160 | ```elixir 161 | > <<0,1,2,3>> 162 | > <<100>> == <<100::size(8)>> # true 163 | > <<4::float>> == <<64, 16, 0, 0, 0, 0, 0, 0>> # true 164 | > <<65::utf32>> == <<0, 0, 0, 65>> # true 165 | > <<0::2, 1::2>> == <<1::4>> # true 166 | > <<1,2>> <> <<3,4>> == <<1,2,3,4>> # true 167 | > is_binary(<<1,2,3,4>>) # true 168 | > is_binary(<<1::size(4)>>) # false!, num of bits not devisible by 8 169 | > is_bitstring(<<1::size(4)>>) # true 170 | ``` 171 | 172 | ### String 173 | Strings are UTF-8 encoded binaries. They are enclosed in double quotes(`"`).
174 | They can span multiple lines and contain interpolations.
175 | Interpolations are enclosed in `#{}` and can contain any expression.
176 | Strings, being binaries, are concatenated with `<>`. 177 | 178 | ```elixir 179 | > "This is a string." 180 | > "☀★☂☻♞☯☭☢€→☎♫♎⇧☮♻⌘⌛☘☊♔♕♖☦♠♣♥♦♂♀" # no problem :) 181 | > "This is an #{ Atom.to_string(:interpolated) } string." 182 | > "Where is " <> "my other half?" 183 | > "multi\nline" == "multi 184 | line" # true 185 | > <<69,108,105,120,105,114>> == "Elixir" # true 186 | > String.length("🎩") # 1 187 | > byte_size("🎩") # 4 188 | > is_binary("any string") # true 189 | > String.valid?("こんにちは") # true 190 | > String.valid?("hello" <> <<255>>) # false! 191 | > String.valid?(<<4>>) # true 192 | > String.printable?(<<4>>) # false! 4 is a valid UTF-8 codepoint, but is not printable. 193 | ``` 194 | #### Escape Sequences 195 | characters | whitespace | control sequences 196 | --------------------------|---------------------|---------------------- 197 | `\"` – double quote | `\b` – backspace | `\a` – bell/alert 198 | `\'` – single quote | `\f` - form feed | `\d` - delete 199 | `\\` – single backslash | `\n` – newline | `\e` - escape 200 | | `\s` – space | `\r` – carriage return 201 | | `\t` - tab | `\0` - null byte 202 | | `\v` – vertical tab | 203 | 204 | `\x...` - character with hexadecimal representation.
205 | `\x{...}` - character with hexadecimal representation with one or more hexadecimal digits.
206 | ```elixir 207 | > "\x3f" == "?" # true 208 | > "\x{266B}" == "♫" # true 209 | > "\x{2660}" == "♠" # true 210 | ``` 211 | 212 | 213 | 214 | ### Regular Expression 215 | Inherited from Erlang's `re` module and are Perl compatible.
216 | Written literally with the `~r` [Sigil](#sigils) and can span multiple lines.
217 | Can have a number of modifiers specified directly after the pattern.
218 | Many functions take a captures option that limits captures. 219 | 220 | **Modifiers**: 221 | - `u` enables unicode specific patterns like \p and changes escapes like \w, \W, \s and friends to also match on unicode. It expects valid unicode strings to be given on match 222 | - `i` ignore case 223 | - `s` dot matches newlines and also set newline to anycrlf. 224 | - `m` ^ and $ match the start and end of each line; use \A and \z to match the end or start of the string 225 | - `x` whitespace characters are ignored except when escaped and `#` delimits comments 226 | - `f` forces the unanchored pattern to match before or at the first newline, though the matched text may continue over the newline 227 | r - inverts the “greediness” of the regexp 228 | 229 | **To override newline treatment start the pattern with**: 230 | - `(*CR)` carriage return 231 | - `(*LF)` line feed 232 | - `(*CRLF)` carriage return, followed by linefeed 233 | - `(*ANYCRLF)` any of the three above 234 | - `(*ANY)` all Unicode newline sequences 235 | 236 | ```elixir 237 | > Regex.compile!("caf[eé]") == ~r/caf[eé]/ # true 238 | > Regex.match?(~r/caf[eé]/, "café") # true 239 | > Regex.regex?(~r"caf[eé]") # true 240 | > Regex.regex?("caf[eé]") # false! string not compiled regex 241 | > Regex.run(~r/hat: (.*)/, "hat: 🎩", [capture: :all_but_first]) == ["🎩"] # true 242 | # Modifiers 243 | > Regex.match?(~r/mr. bojangles/i, "Mr. Bojangles") # true 244 | > Regex.compile!("mr. bojangles", "sxi") # ~r/mr. bojangles/sxi 245 | # Newline overrides 246 | > ~r/(*ANY)some\npattern/ 247 | ``` 248 | 249 | ## Collection Types 250 | 251 | ### List 252 | Simple linked lists that can be of any size and can have elements of any type.
253 | They are enclosed in `[ ]` and elements are comma separated.
254 | Concatenated with `++` and subtracted with `--`.
255 | Can be constructed with the cons operator `|`.
256 | Best for sequential access, fastest when elements are added and subtracted from the head.
257 | Instead of building a list by adding to its tail, add to the head and reverse the list.
258 | List implements the enumerable protocol so we use Enum for many common operations. 259 | 260 | ```elixir 261 | > [1, 2, 3.4, "a", "b", :c, [:d]] 262 | > [ 1 | [2 | [3]]] == [1, 2, 3] # true 263 | > [1, 2, 3.4] ++ ["a", "b", :c] # [1, 2, 3.4, "a", "b", :c] 264 | > [1, 2, 3.4, "a", "b", :c, [:d]] -- [2, "a", "c"] # [1, 3.4, "b", :c, [:d]] 265 | > hd [1, 2, 3] # 1 266 | > tl [1, 2, 3] # [2, 3] 267 | > length [:a, :b, :c, :d] # 4 268 | > Enum.reverse [:a, :b, :c] # [:c, :b, :a] 269 | > Enum.member? [:a, :b], :b # true 270 | > Enum.join [:a, :b], "_" # "a_b" 271 | > Enum.at [:a, :b, :c], 1 # :b 272 | ``` 273 | 274 | ### Charlist 275 | A [List](#list) of UTF-8 codepoints.
276 | Other than syntax they are exactly the same as Lists and are not a unique class.
277 | Can span multiple lines and are delimited with single quotes `'`.
278 | Have the same [Escape Sequences](#escape-sequences) as String.
279 | 280 | ```elixir 281 | > 'char list' 282 | > [108, 105, 115, 116] == 'list' # true 283 | > 'turbo' ++ 'pogo' # 'turbopogo' 284 | > 'char list' -- 'a l' # 'christ' 285 | > hd 'such list' == ?s # true 286 | > String.to_char_list "tacosalad" # 'tacosalad' 287 | > List.to_string 'frijoles' # "frijoles" 288 | > [?Y, ?e, ?a, ?h] == 'Yeah' # true 289 | ``` 290 | 291 | ### Tuple 292 | Can be of any size and have elements of any type.
293 | Elements are stored contiguously in memory.
294 | Enclosed in `{ }` and elements are comma separated.
295 | Fast for index-based access, slow for a large number of elements.
296 | 297 | ```elixir 298 | > { :a, 1, {:b}, [2]} 299 | > put_elem({:a}, 0, :b) # {:b} 300 | > elem({:a, :b, :c}, 1) # b 301 | > Tuple.delete_at({:a, :b, :c}, 1) # {:a, :c} 302 | > Tuple.insert_at({:a, :c}, 1, :b) # {:a, :b, :c} 303 | > Tuple.to_list({:a, :b, :c}) # [:a, :b, :c] 304 | ``` 305 | 306 | ### Keyword List 307 | A List of 2 element Tuples where each Tuple's first element is an Atom.
308 | This atom is refered to as the keyword, or key.
309 | Have a special concice syntax that omits the Tuple's brackets and places the key's colon on the right.
310 | Being Lists they: 311 | - are order as specified 312 | - can have duplicate elements and multiple elements with the same key 313 | - are fastest when accessed at the head. 314 | - are concatenated with `++` and subtracted with `--`.
315 | 316 | Elements can be accessed with `[:key]` notation. The first Element with a matching `:key` will be returned.
317 | 2 Keyword Lists are only equal if all elements are equal and in the same order. 318 | 319 | ```elixir 320 | # Full Syntax 321 | > [{:a, "one"}, {:b, 2}] 322 | # Concice Syntax 323 | > [a: "one", b: 2] 324 | > [a: 1] ++ [a: 2, b: 3] == [a: 1, a: 2, b: 3] # true 325 | > [a: 1, b: 2] == [b: 2, a: 1] # false! elements are in different order 326 | > [a: 1, a: 2][:a] == 1 # true 327 | > Keyword.keys([a: 1, b: 2]) # [:a, :b] 328 | > Keyword.get_values([a: 1, a: 2], :a) # [1, 2] 329 | > Keyword.keyword?([{:a,1}, {:b,2}]) # true 330 | > Keyword.keyword?([{:a,1}, {"b",2}]) # false! "b" is not an Atom 331 | > Keyword.delete([a: 1, b: 2], :a) # [b: 2] 332 | ``` 333 | 334 | ### Map 335 | Key - Value store where Keys and Values are of any type.
336 | Cannot have multiple values for the same key and are unordered.
337 | `Map`s are enclosed in `%{ }`, elements are comma seperated, and elemets have the form: key `=>` value.
338 | If all keys are `Atom`s, the `=>` can be omitted and the `Atom`'s `:` must be on the right.
339 | Values are accessed with `[key]` notation.
340 | Maps can be accessed with `.key` notation if key is an `Atom`.
341 | Maps can be updated by enclosing them in `%{}` and using the cons `|` operator.
342 | Maps can be of any size and are fastest for key based lookup. 343 | 344 | ```elixir 345 | > %{:a => 1, 1 => ["list"], [2,3,4] => {"a", "b"}} 346 | > %{:a => 1, :b => 2} == %{a: 1, b: 2} # true 347 | > %{a: "one", b: "two", a: 1} == %{a: 1, b: "two"} # true 348 | > %{a: "one", b: "two"} == %{b: "two", a: "one"} # true 349 | > %{a: "one", b: "two"}[:b] # "two" 350 | > %{a: "one", b: "two"}.b # "two" 351 | > %{a: "one", a: 1} == %{a: 1} # true 352 | > %{:a => "one", "a" => "two"}."a" == "two" # false! watchout 353 | > Map.keys( %{a: 1, b: 2} ) == [:a, :b] # true 354 | > %{ %{a: 1, b: 2, c: 3} | :a => 4, b: 5 } # %{a: 4, b: 5, c: 3} 355 | > Map.merge( %{a: 1, b: 2}, %{a: 4, c: 3} ) # %{a: 4, b: 2, c: 3} 356 | > Map.put( %{a: 1}, :b, 2 ) == %{a: 1, b: 2} # true 357 | > Kernel.get_in # TODO 358 | > Kernel.put_in # TODO 359 | ``` 360 | 361 | ### Struct 362 | Structs can be thought of as bare Maps with pre-defined keys, default values and where the keys must be atoms.
363 | Structs are defined at the top level of a Module and take the Module's name.
364 | Structs do not implement the Access or Enumerable protocol and can be considered bare Maps.
365 | Structs have a special field called `__struct__` that holds the name of the struct. 366 | 367 | ```elixir 368 | defmodule City do 369 | defstruct name: "New Orleans", founded: 1718 370 | end 371 | nola = %City{} 372 | chi = %City{name: "Chicago", founded: 1833} 373 | nola.name # "New Orleans" 374 | chi.founded # 1833 375 | nola.__struct__ # City 376 | ``` 377 | 378 | ### Range 379 | Used to specify the first and last elements of something.
380 | Just a Struct of type Range with a `first` field and a `last` field.
381 | Have a special `..` creation syntax but can also be created like any other struct. 382 | 383 | ```elixir 384 | > a = 5..10 385 | > b = Range.new(5, 10) 386 | > c = %Range{first: 5, last: 10} 387 | > Range.range?(c) # true 388 | > Enum.each(5..10, fn(n) -> n*n end) # prints all the squares of 5..10 389 | > Map.keys(5..10) # [:__struct__, :first, :last] 390 | > (5..10).first # 5 391 | ``` 392 | 393 | ### Streams 394 | Lazy enumerables.
395 | Are created out of enumerables with functions in the `Stream` module.
396 | Elements are not computed until a method from the `Enum` module is called on them.
397 | ```elixir 398 | > a = Stream.cycle 'abc' 399 | #Function<47.29647706/2 in Stream.unfold/2> # Infinate Stream created 400 | > Enum.take a, 10 # Enum.take computes the 10 elements 401 | 'abcabcabca' 402 | ``` 403 | 404 | With [Stream.unfold/2](http://elixir-lang.org/docs/stable/elixir/Stream.html#unfold/2) you can create an arbitrary stream. 405 | ```elixir 406 | > s = Stream.unfold( 5, 407 | fn 0 -> nil # returning nil halts the stream 408 | n -> {n, n-1} # return format {next-val, rest} 409 | end) 410 | > Enum.to_list(s) 411 | [5, 4, 3, 2, 1] 412 | ``` 413 | 414 | ## Syntax 415 | 416 | ### Variables 417 | Are declared and initialized upon use.
418 | Are named in the following format: 419 | 420 | ![variable naming](https://rawgit.com/itsgreggreg/elixir_quick_reference/50ffe1e533896247fbb6429b72f83cdbb1186a8a/images/variable-naming.png)
421 | Can hold any data structure and can be assigned more than once. 422 | 423 | ```elixir 424 | > something = :anything 425 | > something = ["a", "list", "of", "strings"] 426 | > _yeeHaw1234! = %{:a => :b} 427 | ``` 428 | 429 | ### Operators 430 | #### Standard infix 431 | - Equality `==`, `!=` 432 | - Strict equality `===` and `!==` do not coerce Floats to Integers. `1 === 1.0 #false` 433 | - Comparison `>`, `<`, `>=`, `<=` 434 | - Logic, short-circuiting `&&` and `||` 435 | - Boolean only Logic, short-circuiting `and` and `or`. (Only left side must be boolean) 436 | - Math `+`, `-`, `*`, `/` 437 | 438 | #### Standard prefix 439 | - Negation, any type `!`, `!1 == false` 440 | - Negation, boolean only `not`, `not is_atom(5) == true` 441 | 442 | #### = (match) 443 | left `=` right
444 | Performs a [Pattern Match](#pattern-matching). 445 | 446 | #### ^ (pin) 447 | Used to pin the value of a variable in the left side of a [Pattern Match](#pattern-matching). 448 | ```elixir 449 | a = "thirty hams" 450 | {b, ^a} = {:i_need, "thirty hams"} # `b` is set to `:i_need` 451 | {^a, {^a}} = {"thirty hams", {"thirty hams"}} # nothing is set, but the match succedes 452 | ``` 453 | 454 | #### |> (pipe) 455 | `|>`
456 | Takes the result of a statement on its left and passes as the first argument to the function on its right.
457 | The statement on the left can be on the preceeding line of code. 458 | ```elixir 459 | > [1,2,3] |> hd |> Integer.to_string |> IO.inspect # "1" 460 | # ⇣ doesn't work in iex 461 | hd([1,2,3]) 462 | |> Integer.to_string 463 | |> IO.inspect # "1" 464 | ``` 465 | 466 | #### =~ (string match) 467 | `=~`
468 | Takes a string on the left and on the right either a string or a regular expression.
469 | If the string on the right is a substring of left, `true` is returned.
470 | If the regular expression on the right matches the string on the left, `true` is returned.
471 | Otherwise `false` is returned. 472 | ```elixir 473 | > "abcd" =~ ~r/c(d)/ # true 474 | > "abcd" =~ ~r/e/ # false 475 | > "abcd" =~ "bc" # true 476 | > "abcd" =~ "ad" # false 477 | ``` 478 | 479 | #### ? (codepoint) 480 | `?`
481 | Returns the UTF-8 codepoint of the character immediately to its right.
482 | Can only take one character, accepts [Escape Sequences](#escape-sequences).
483 | **Remember** [Charlists](#charlist) are just lists of UTF-8 codepoints. 484 | ```elixir 485 | > ?a # 97 486 | > ?♫ # 9835 487 | > ?\s # 32 488 | > ?? # 63 489 | > [?♀, ?!] == '♀!' # true 490 | ``` 491 | 492 | #### & (capture) 493 | - TODO 494 | 495 | #### Ternary 496 | Elixir has no ternary operator. The same effect though can be achieved with the `if` macro. 497 | ```elixir 498 | > a = if true, do: "True!", else: "False!" 499 | > a == "True!" # true 500 | ``` 501 | 502 | #### in 503 | left `in` right.
504 | Used to check if the **enumerable** on the right contains the data structure on the left.
505 | Right hand side must implement the Enumerable Protocol. 506 | 507 | ```elixir 508 | > :b in [:a, :b, :c] # true 509 | > [:c] in [1,3,[:c]] # true 510 | > :ok in {:ok} # ERROR: protocol Enumerable not implemented for {:ok} 511 | ``` 512 | 513 | ### Comments 514 | `#` indicates that itself and anything after it until a new line is a comment. That is all. 515 | 516 | ### Semicolons 517 | Semicolons can be used to terminate statements but in practice are rarely if ever used.
518 | The only required usage is to put more than one statement on the same line. `a = 1; b = 2`
519 | This is considered bad style and placing them on seperate lines is much prefered. 520 | 521 | ### Do, End 522 | Blocks of code passed to macros start with `do` and end with `end`. 523 | ```elixir 524 | if true do 525 | "True!" 526 | end 527 | 528 | if true do "True!" end 529 | 530 | # inside a module 531 | def somefunc() do 532 | IO.puts "multi line" 533 | end 534 | 535 | if true do 536 | "True!" 537 | else 538 | "False!" 539 | end 540 | ``` 541 | 542 | You can pass the block as a single line and without `end` with some extra puctuation.
543 | ```elixir 544 | # ⇣ ⇣ ⇣ no end keyword 545 | if true, do: "True!" 546 | # ⇣ ⇣ ⇣ ⇣ ⇣ no end keyword 547 | if true, do: "True", else: "False!" 548 | # inside a module 549 | # ⇣ ⇣ ⇣ no end keyword 550 | def someFunc(), do: IO.puts "look ma, one line!" 551 | ``` 552 | 553 | Syntactic sugar for 554 | ```elixir 555 | if(true, [{:do, "True!"}, {:else, "False!"}]) 556 | def(someFunc(), [{:do, IO.puts "look ma, one line!"}]) 557 | ``` 558 | 559 | ### Pattern Matching 560 | A match has 2 main parts, a **left** side and a **right** side.
561 | ```elixir 562 | # ┌Left ┌Right 563 | # ┌───┴───┐ ┌───┴──────┐ 564 | {:one, x} = {:one, :two} 565 | # ┌Right 566 | # ┌───┴──────┐ 567 | case {:one, :two} do 568 | # ┌Left 569 | # ┌───┴───┐ 570 | {:one, x} -> IO.puts x 571 | # ┌Left 572 | _ -> IO.puts "no other match" 573 | end 574 | ``` 575 | 576 | The **right** side is a **data structure** of any kind.
577 | The **left** side attempts to **match** itself to the **data structure** on the right and **bind** any **variables** to **substructures**. 578 | 579 | The simplest **match** has a lone **variable** on the **left** and will **match** anything: 580 | ```elixir 581 | # in these examples `x` will be set to whatever is on the right 582 | x = 1 583 | x = [1,2,3,4] 584 | x = {:any, "structure", %{:whatso => :ever}} 585 | ``` 586 | 587 | But you can place the **variables** inside a **structure** so you can **capture** a **substructure**: 588 | ```elixir 589 | # `x` gets set to only the `substructure` it matches 590 | {:one, x} = {:one, :two} # `x` is set to `:two` 591 | [1,2,n,4] = [1,2,3,4] # `n` is set to `3` 592 | [:one, p] = [:one, {:apple, :orange}] # `p` is set to `{:apple, :orange}` 593 | ``` 594 | 595 | There is also a special `_` **variable** that works exactly like other **variables** but tells elixir, "Make sure something is here, but I don't care exactly what it is.": 596 | ```elixir 597 | # in all of these examples, `x` gets set to `:two` 598 | {_, x} = {:one, :two} 599 | {_, x} = {:three, :two} 600 | [_,_,x,_] = [1,{2},:two,3] 601 | ``` 602 | 603 | If you place a **variable** on the **right**, its **value** is used: 604 | ```elixir 605 | # ┌Same as writing {"twenty hams"} 606 | a = {"twenty hams"} ⇣ 607 | {:i_have, {b}} = {:i_have, a} # `b` is set to "twenty hams" 608 | ``` 609 | 610 | In the previous example you are telling elixir: I want to **match** a **structure** that is a **tuple**, and this **tuple's** first element is going to be the atom **:i_have**. This **tuple's** second element is going to be a **tuple**. This **second tuple** is going to have one element and whatever it is I want you to bind it to the variable **b**. 611 | 612 | If you want to use the **value** of a **variable** in your structure on the **left** you use the `^` operator: 613 | ```elixir 614 | a = "thirty hams" 615 | {b, ^a} = {:i_need, "thirty hams"} # `b` is set to `:i_need` 616 | {^a, {^a}} = {"thirty hams", {"thirty hams"}} # nothing is set, but the match succedes 617 | ``` 618 | 619 | #### Maps 620 | Individual keys can be matched in Maps like so: 621 | ```elixir 622 | nola = %{ name: "New Orleans", founded: 1718 } 623 | %{name: city_name} = nola # city_name now equals "New Orleans" 624 | %{name: _, founded: city_founded} = nola # Map must have both a name and a founded key 625 | ``` 626 | You can use the pin operator (^) to match on variables: 627 | ```elixir 628 | field = "founded" 629 | %{^field: city_founded} = nola # city_founded now equals 1718 630 | ``` 631 | 632 | #### Binaries 633 | - `<< size::8, rest::binary>> = <<3,0,25,1,1,2,1,6,4,3>>` 634 | - `<< data::size(size)-unit(16)-binary, rest::binary>> = rest` 635 | - TODO 636 | 637 | #### Ranges 638 | Ranges can be pattern matched if both their values are integers. 639 | ```elixir 640 | min..max = 20..5000 641 | min == 20 # true 642 | max == 5000 # true 643 | min..max == 1..10.0 # Thats an Argument Error 644 | ``` 645 | 646 | ### Reserved words 647 | These words are reserved for the language and cannot be used as variables, module or method names. 648 | 649 | `nil`, `true`, `false`, `__MODULE__`,`__FILE__`,`__DIR__`,`__ENV__`,`__CALLER__` 650 | 651 | ## Truthiness 652 | `:nil` and `:false` are falsy.
653 | `nil` and `false` are syntactic sugar for `:nil` and `:false`.
654 | Everything else is truthy. 655 | 656 | ```elixir 657 | > !!nil # false 658 | > !!0 # true 659 | > nil || :nil || false || :false || 1 # 1 660 | ``` 661 | 662 | ## Sorting 663 | Any types can be compared using comparison operators.
664 | `number < atom < reference < functions < port < pid < tuple < maps < list < bitstring` 665 | 666 | ```elixir 667 | 10000 < :five == true 668 | "something" > &div/2 == true 669 | ``` 670 | 671 | ## Modules 672 | Modules organize code under a namespace.
673 | They can be meta-programmed but are defined at compile time and cannot be changed after, only replaced .
674 | Named according to the following format:
675 | 676 | ![Module Naming](https://rawgit.com/itsgreggreg/elixir_quick_reference/75b339df6f6592dd123a8afad0449faae7bd36cc/images/module-naming.png) 677 | 678 | ### Declaration 679 | ```elixir 680 | defmodule MyModule do 681 | end 682 | ``` 683 | 684 | ### Module Functions 685 | Names must begin with `a-z`.
686 | Names can contain `a-Z`, `A-Z`, `0-9` and `_`.
687 | May end with `?` or `!`.
688 | 689 | ```elixir 690 | defmodule MyModule do 691 | def my_function do 692 | IO.puts("Hello from my function") 693 | end 694 | end 695 | ``` 696 | 697 | `def` is actually a macro, and like calling any macro, [do ... end](#do-end) can be written as a one liner: 698 | ```elixir 699 | defmodule MyModule do 700 | def my_function, do: IO.puts("Hello from my function") 701 | end 702 | ``` 703 | 704 | Inside of the defining module, functions may be called by name. Outside they must be called with the defining Module's name and a `.`. Eg: `IO.puts()` 705 | ```elixir 706 | defmodule MyModule do 707 | def function1 do 708 | IO.puts "func 1" 709 | end 710 | def function2 do 711 | function1 712 | IO.puts "funct 2" 713 | end 714 | end 715 | 716 | > MyModule.function2 717 | ``` 718 | 719 | Arguments are passed to functions positionally and can have default arguments.
720 | Arguments can be of any Type. 721 | ```elixir 722 | defmodule MyModule do 723 | # ⇣ indicates "earthlings" to be the default for who 724 | def greet(greeting, who \\ "earthlings") do 725 | IO.puts("#{greeting} #{who}") 726 | end 727 | end 728 | 729 | > MyModule.greet("'sup", "y'all?") # "'sup y'all?" 730 | > MyModule.greet("greetings") # "greetings earthlings" 731 | ``` 732 | 733 | Module functions can be defined multiple times to support different configurations of arguments. 734 | ```elixir 735 | defmodule MyModule do 736 | def greet() do 737 | greet("hello", "you") 738 | end 739 | def greet(greeting, who) do 740 | IO.puts("#{greeting} #{who}") 741 | end 742 | end 743 | 744 | > MyModule.printer("hello") # "hello" 745 | > MyModule.printer([1,2,3]) # [1,2,3] 746 | > MyModule.printer() # "nothing passed" 747 | ``` 748 | 749 | They can also be defined multiple times to Pattern Match on arguments passed. 750 | ```elixir 751 | def is_it_the_number_2?(2) do 752 | true 753 | end 754 | def is_it_the_number_2(value) do 755 | false 756 | end 757 | ``` 758 | 759 | You can ignore arguments with `_` and our previous example is better written as 760 | ```elixir 761 | def is_it_the_number_2?(2) do 762 | true 763 | end 764 | # ⇣ underscore ignores argument 765 | def is_it_the_number_2(_) do 766 | false 767 | end 768 | ``` 769 | 770 | Module function definitions can have [Guards](#guards). 771 | ```elixir 772 | def square(n) when is_number(n), do: n * n 773 | def square(_), do: raise "not a number" 774 | ``` 775 | 776 | ### Private Functions 777 | To make a function private to a module use `defp` instead of `def`. 778 | 779 | ```elixir 780 | defmodule ModA do 781 | defp hi, do: IO.puts "Hello from ModA" 782 | def say_hi, do: hi 783 | end 784 | ModA.say_hi 785 | # Hello from ModA 786 | ModA.hi 787 | # ** (UndefinedFunctionError) undefined function ModA.hi/0 ModA.hi() 788 | ``` 789 | 790 | ### Working with other modules 791 | Inside of a module you can use one of the 4 directives to interact with other modules. 792 | #### import 793 | `import SomeModule` brings all modules and macros of SomeModule into the enclosing module so you can use them un-namespaced
794 | `import` can take either an `only:` or `except:` list in which you specify functions and macros to include.
795 | Alternatively `import SomeModule, only:` can take `:functions` or `:macros` to specify only those. 796 | ```elixir 797 | def ModA do 798 | import ModB # All Functions and Macros in ModB 799 | import ModB, except: [destroy_planet: 1] # All Functions and Macros except destroy_planet/1 800 | import ModB, only: :functions # All functions, no macros 801 | import ModB, only: [say_hi: 0, fibonacci: 1] # Only the specified functions or macros 802 | end 803 | ``` 804 | 805 | #### require 806 | `require SomeModule` allows you to use macros of SomeModule. It also makes sure that SomeModule is compiled before the enclosing module. 807 | 808 | #### use 809 | `use SomeModule` first __requires SomeModule__ and then calls the macro SomeModule.\_\_using\_\_. It is often used to perform setup for metaprogramming. 810 | 811 | #### alias 812 | `alias SomeVery.Long.ModuleName, as: SVLMN` is used simply to shorten a module name to cut down on typing. 813 | 814 | ### Attributes 815 | Pieces of data that can be thought of as metadata or constants for a module.
816 | They are inlined by the compiler and cannot be changed at runtime.
817 | They can be set multiple times and the value used will be the value set when the function is defined. 818 | 819 | ```elixir 820 | defmodule ModA do 821 | @name "April" 822 | def first, do: @name 823 | @name "O'Neal" 824 | def last, do: @name 825 | end 826 | ``` 827 | 828 | TODO: 829 | - @external_resource 830 | - Better explanation of attributes in relation to metaprogramming 831 | 832 | ### Documentation 833 | Elixir has documentation built in and you can document your modules and functions with Attributes.
834 | `@moduledoc` describes your module.
835 | `@doc` describes module functions. 836 | ```elixir 837 | defmodule MathUtils do 838 | @moduledoc """ 839 | Random math related functions 840 | """ 841 | 842 | @doc "Squares the given number." 843 | def square(n), do: n*n 844 | end 845 | ``` 846 | 847 | ### Introspection 848 | - `__info__(:functions)` 849 | 850 | ## Errors 851 | Only to be used in exceptional circumstances, not for control flow.
852 | Built in errors are listed in the docs: http://elixir-lang.org/docs/stable/elixir/ArgumentError.html . They're on the left. 853 | 854 | ### Raise 855 | `raise` a runtime error: 856 | ```elixir 857 | > raise "not fabulous enough" 858 | ** (RuntimeError) not fabulous enough 859 | ``` 860 | 861 | `raise` a different error: 862 | ```elixir 863 | > raise ArgumentError, "I'm done. We're talking in circles." 864 | #** (ArgumentError) I'm done. We're talking in circles. 865 | ``` 866 | 867 | Some errors take specific options, and you must read the source to find them: 868 | ```elixir 869 | > raise KeyError, key: "to car", term: "pocket" 870 | #** (KeyError) key "to car" not found in: "pocket" 871 | ``` 872 | 873 | ### Custom Error 874 | ``` elixir 875 | defmodule LandWarWithRussiaError do 876 | defexception message: "Never." 877 | end 878 | > raise LandWarWithRussiaError 879 | #** (LandWarWithRussiaError) Never. 880 | ``` 881 | 882 | ### Rescue 883 | ```elixir 884 | try do 885 | if false do 886 | raise "are we there yet?" 887 | else 888 | raise ArgumentError, "I'll pull this car around!" 889 | end 890 | rescue 891 | e in RuntimeError -> IO.puts "No!" 892 | e in ArgumentError -> IO.puts "That's it we're going home!" 893 | end 894 | ``` 895 | 896 | **Remember** There are much better ways to control flow in Elixir than raising/rescuing errors.
897 | Errors should be reserved for truly exceptional situations. 898 | 899 | 900 | ## Control Flow 901 | ### if/unless 902 | ```elixir 903 | if :something_truthy do 904 | IO.puts "something truthy happened" 905 | else 906 | IO.puts "false or nil happened" 907 | end 908 | 909 | unless :something_truthy do 910 | IO.puts "nil or false happened" 911 | else 912 | IO.puts "something truthy happened" 913 | end 914 | ``` 915 | 916 | ### case 917 | `case` let's you pattern match on a value.
918 | If no cases match it throws a `MatchError`. 919 | ```elixir 920 | case 137 do 921 | "137" -> IO.puts "I require 137 the number." 922 | 137 -> IO.puts "Ahh much better." 923 | 138 -> 924 | IO.puts "Blocks can start on the next line as well." 925 | end 926 | ``` 927 | 928 | Like all pattern matches, `_` will match anything and can be used as a catchall: 929 | ```elixir 930 | case {:ok, "everything went to plan"} do 931 | {:ok, message} -> IO.puts message 932 | {:error, message} -> IO.puts "ERROR!: #{message}" 933 | # ⇣catchall, otherwise you'll get an error if nothing matches 934 | _ -> IO.puts "I match everything else!" 935 | end 936 | ``` 937 | 938 | You can have [guards](#guards) on your cases: 939 | ```elixir 940 | case 1_349 do 941 | n when is_integer n -> IO.puts "you gave me an integer" 942 | n when is_binary n -> IO.puts "you gave me a binary" 943 | _ -> IO.puts "you gave me neither an integer nor binary" 944 | end 945 | ``` 946 | 947 | ### cond 948 | `cond` takes one or more conditions and runs the first truthy one it finds.
949 | Often used where imperative languages would use elseif.
950 | If no statements evaluate to true it throws a `MatchError`. 951 | ```elixir 952 | cond do 953 | false -> IO.puts "I will never run" 954 | true -> IO.puts "I will always run" 955 | 1235 -> IO.puts "I would run if that dang true wasn't on top of me." 956 | end 957 | ``` 958 | 959 | `true` is often used as a catch all: 960 | ```elixir 961 | guess = 12 962 | cond do 963 | guess == 10 -> IO.puts "You guessed 10!" 964 | guess == 46 -> IO.puts "You guessed 46!" 965 | true -> 966 | IO.puts "I give up." 967 | end 968 | ``` 969 | ### throw/catch 970 | Inside of a `try` block you can `throw` any data type and pattern match on it inside a `catch` block.
971 | `catch` blocks work the same as [case](#case) blocks.
972 | `after` blocks will always run, throw or no throw. 973 | ```elixir 974 | try do 975 | IO.puts "Inside a try block" 976 | throw [:hey, "Reggie"] 977 | IO.puts "if there is a throw before me, I'll never run." 978 | catch 979 | x when is_number(x) -> IO.puts "!!A number was thrown." 980 | [:hey, name] -> IO.puts "!!Hey was thrown to #{name}." 981 | _ -> IO.puts "Something else was thrown." 982 | after 983 | IO.puts "I run regardless of a throw." 984 | end 985 | ``` 986 | 987 | ### with 988 | Takes a series of pattern matches, runs them in order, if all pattern matches succeed it returns its `do` block. If a pattern match doesn't succeed, the non-matching value is returned and `with` is exited.
989 | You can supply an `else` block that essentially functions as a `case` block for an uncussessful match.
990 | Variables assigned in a `with` block do not leak into outer scope.
991 | A comma must come after every match. 992 | 993 | ```elixir 994 | nums = [8,13,44] 995 | # ┌left arrow ┌comma 996 | # match left | match right | 997 | # ┌───┴────┐ ⇣ ┌───────┴─────────┐⇣ 998 | with {:ok, num} <- Enum.fetch(nums, 2), 999 | "44" <- Integer.to_string(num), 1000 | do: "it was 44" 1001 | 1002 | # Paterns can take guards 1003 | with a when is_nil(a) <- nil, 1004 | do: "Accepts guards" 1005 | else 1006 | _ -> "Does not accept guards" 1007 | 1008 | # From the docs 1009 | opts = %{width: 10, height: 15} 1010 | with {:ok, width} <- Map.fetch(opts, :width), 1011 | {:ok, height} <- Map.fetch(opts, :height), 1012 | do: {:ok, width * height} 1013 | # returns {:ok, 150} 1014 | 1015 | opts = %{width: 10} 1016 | with {:ok, width} <- Map.fetch(opts, :width), 1017 | {:ok, height} <- Map.fetch(opts, :height), 1018 | do: {:ok, width * height} 1019 | # returns :error as that's what Map.fetch returns when a key is not present. 1020 | # ┌─ Or you can catch the error in an else block 1021 | else 1022 | :error -> "A key wasn't found!" 1023 | 1024 | ``` 1025 | 1026 | ## Guards 1027 | TODO: how to use guards.
1028 | Errors generated in guards are swallowed and simply cause the guard to fail.
1029 | Functions and operators allowed in guard clauses: 1030 | 1031 | | = | logic | math | type_checking | map | tuple |list |binary | Kernel 1032 | |-----------|-------|-------|---------------|-----------|------------|---------|-----------|-------- 1033 | |== | or | + | is_atom | map_size()|elem() |a `in` b |bit_size() | node() 1034 | |!= | and | - | is_binary | |tuple_size()|hd() |byte_size()| self() 1035 | |=== | not | * | is_bitstring | | |tl() | | 1036 | |!== | | / | is_boolean | | |length() | | 1037 | |\> | |abs() | is_exception ||||| 1038 | |\< | |div() | is_float ||||| 1039 | |\<= | |float()| is_function ||||| 1040 | |\>= | |rem() | is_integer ||||| 1041 | | | |round()| is_nil ||||| 1042 | | | |trunc()| is_list ||||| 1043 | | | | | is_number ||||| 1044 | | | | | is_pid ||||| 1045 | | | | | is_port ||||| 1046 | | | | | is_reference ||||| 1047 | | | | | is_tuple ||||| 1048 | 1049 | **Notice** `!`, `&&` and `||` are not allowed in guard clauses. 1050 | 1051 | ## Anonymous Functions 1052 | Closures in which the captured variables are set at definition.
1053 | Are essentially [case](#case) blocks you can pass around.
1054 | Take the general form: 1055 | ```elixir 1056 | fn (1, 2, 3) -> IO.inspect 1*2*3 1057 | (a, b, c) -> IO.inspect [a,b,c] 1058 | end 1059 | ``` 1060 | Are called with the `.` operator. 1061 | ```elixir 1062 | > add_2 = fn num -> num + 2 end 1063 | # ⇣called with a dot 1064 | > add_2.(127) == 129 1065 | true 1066 | > three = 3 1067 | > add_3 = fn num -> num + three end 1068 | > add_3.(262) == 265 1069 | true 1070 | # ⇣is our function compromised? 1071 | > three = 7 1072 | # ⇣no, `three` is still 3 in the function 1073 | > add_3.(262) == 265 1074 | true 1075 | ``` 1076 | 1077 | Like case blocks, they can have multiple matches, matches can have guards, and do not leak scope: 1078 | ```elixir 1079 | > support_method = fn ("suspenders") -> IO.puts "Hey big spender." 1080 | ("belt") -> IO.puts "Who is the real hero?" 1081 | a when is_list a -> Enum.each(a, support_method) 1082 | _ -> IO.puts "What is this wizzardry?" 1083 | end 1084 | > support_method.(["belt", "suspenders"]) 1085 | "Who is the real hero?" 1086 | "Hey big spender." 1087 | > peanut_butter = "smooth" 1088 | # ⇣immediately call 1089 | > fn () -> peanut_butter = "chunky" end.() 1090 | > peanut_butter == "smooth" 1091 | true # scope was not leaked 1092 | ``` 1093 | 1094 | They **cannot** have multiple arities (which in case statements wouldn't make sense): 1095 | ```elixir 1096 | > fn (a) -> "One" 1097 | (a, b) -> "Two" 1098 | end 1099 | # That's a compile error 1100 | ``` 1101 | 1102 | ## Comprehensions 1103 | Loop over any enumerable or bitstring and build another.
1104 | By default they build lists.
1105 | `generators`, `filters` and `into:` are comma separated. `do` follows [Do, End](#do-end) rules.
1106 | Basic form: 1107 | ```elixir 1108 | # ┌for ┌Generator ┌Block 1109 | # ⇣ ┌────────┴────────┐┌──────┴──────┐ 1110 | > for num <- [1, 2, 3, 4], do: num * num 1111 | [1, 4, 9, 16] 1112 | ``` 1113 | 1114 | They can have filters: 1115 | ```elixir 1116 | # ┌Filter 1117 | # ┌────────┴───────┐ 1118 | > for num <- [1, 2, 3, 4], rem(num, 2) == 0, do: num * num 1119 | [4, 16] 1120 | ``` 1121 | 1122 | Multiple generators: 1123 | ```elixir 1124 | # ┌Generator ┌Generator 1125 | # ┌──────┴─────┐┌─────────┴───────┐ 1126 | > for num <- [1,2,3], str <- ["a", "b"], do: "#{num}#{str}" 1127 | ["1a", "1b", "2a", "2b", "3a", "3b"] 1128 | ``` 1129 | 1130 | Multiple filters: 1131 | ```elixir 1132 | # ┌Filter ┌Filter 1133 | # ┌─────┴───┐┌──────┴────┐ 1134 | > for num <- [1,2,3], str <- ["a", "b"], num !== 3, str !== "a", do: "#{num}#{str}" 1135 | ["1b", "2b"] 1136 | ``` 1137 | 1138 | You can pattern match in generators: 1139 | ```elixir 1140 | > for {_, age} <- %{doug: 4, lucy: 6, ralf: 10}, do: age 1141 | [4, 6, 10] 1142 | ``` 1143 | 1144 | You can build a different data structure with `into`: 1145 | ```elixir 1146 | # ┌Into a map 1147 | # ┌────┴────┐ 1148 | > for num <- [1, 2, 3, 4], into: %{}, do: {num, num*num} 1149 | %{1 => 1, 2 => 4, 3 => 9, 4 => 16} 1150 | # ┌Into a string 1151 | # ┌────┴───┐ 1152 | > for num <- [1, 2, 3, 4], into: "", do: "the square of #{num} is #{num * num}. " 1153 | "the square of 1 is 1. the square of 2 is 4. the square of 3 is 9. the square of 4 is 16. " 1154 | ``` 1155 | 1156 | Enumerating binaries is a breeze though they have a special syntax: 1157 | ```elixir 1158 | # ┌Binary generator 1159 | # ┌────────────┴─────────────────┐ 1160 | > for <> >>, do: byte 1161 | [255, 12, 55, 89] 1162 | # ┌Pattern Match on bit size 1163 | # ┌───┴───┐ 1164 | > for <> >>, do: bit 1165 | [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0] 1166 | 1167 | > pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>> 1168 | # ⇣Short hand for bit-size 1169 | > for <>, do: {r, g, b} 1170 | [{213,45,132},{64,76,32},{76,0,0},{234,32,15}] 1171 | ``` 1172 | 1173 | Work well with streams: 1174 | ```elixir 1175 | # Grabs 5 lines of input and upcases it 1176 | stream = IO.stream(:stdio, :line) 1177 | for line <- Enum.take(stream, 5), into: stream do 1178 | String.upcase(line) 1179 | end 1180 | ``` 1181 | 1182 | ## Sigils 1183 | Sigils create structures out of text passed to them.
1184 | They take the general form `~type| content |m` and can be delimited by `{}`, `[]`, `()`, `//`, `||`, `""`, or `''`.
1185 | Built in sigils: 1186 | 1187 | Creates | With Interpolation | Without Interpolation 1188 | ------------------|:------------------:|:---------------------:| 1189 | String | s | S 1190 | Charlist | c | C 1191 | List of words | w | W 1192 | Regular Expression| r | R 1193 | 1194 | ```elixir 1195 | > a = "Yay!" 1196 | > ~s|Quotes #{a} 'used' "willy nilly.| # "Quotes Yay! 'used' \"willy nilly." 1197 | > ~S{Not "interpolated" #{a}} # "Not \"interpolated\" \#{a}" 1198 | > ~c[charlist "with" 'quotes' #{a}] # 'charlist "with" \'quotes\' Yay!' 1199 | > ~w/a list of words #{a}/ # ["a", "list", "of", "words", "Yay!"] 1200 | > ~W"a list of words #{a}" # ["a", "list", "of", "words", "\#{a}"] 1201 | ``` 1202 | 1203 | The `~w` and `~W` sigils can take modifier to specify which type of value to create, `a`, `c`, or `s` for `atom` `charlist` or `string`. 1204 | 1205 | ```elixir 1206 | > ~w|alpha bravo charlie|a # [:alpha, :bravo, :charlie] 1207 | > ~w|alpha bravo charlie|c # ['alpha', 'bravo', 'charlie'] 1208 | > ~w|alpha bravo charlie|s # ["alpha", "bravo", "charlie"] 1209 | ``` 1210 | 1211 | You can also define custom Sigils: 1212 | ```elixir 1213 | defmodule MySigil do 1214 | def sigil_i(string, 'd') do 1215 | for num <- String.split(string), do: String.to_integer(num) * 2 1216 | end 1217 | def sigil_i(string, _opts) do 1218 | for num <- String.split(string), do: String.to_integer(num) 1219 | end 1220 | end 1221 | > import MySigil 1222 | > ~i|1 2 3 4 5| # [1, 2, 3, 4, 5] 1223 | > ~i|1 2 3 4 5|d # [2, 4, 6, 8, 10] 1224 | ``` 1225 | 1226 | ## Metaprogramming 1227 | - macros 1228 | - quote 1229 | - unquote 1230 | - var! 1231 | 1232 | ## Processes 1233 | 1234 | ## Structs 1235 | - %ModuleName{} 1236 | - implement Map behaviour 1237 | - Pattern matching on structs 1238 | - @derive 1239 | 1240 | ## Working with Files 1241 | 1242 | ## Erlang Interoperability 1243 | Erlang modules can be called by prepnding them with a colon. 1244 | ```elixir 1245 | :crypto.hash(:sha, "Elixir is the beez knees") 1246 | |> :crypto.bytes_to_integer 1247 | |> Integer.to_string(16) # "62A3326DEDE3EE38C9C85ED6EC87FD888A130D24" 1248 | ``` 1249 | 1250 | ## IEx 1251 | Interactive Elixir. An Elixir REPL that comes with your Elixir install
1252 | 1253 | ### Running 1254 | run `iex` from a command prompt to enter iex.
1255 | `iex some_file.ex` will compile ./some_file.ex and load it into iex.
1256 | 1257 | ### Using 1258 | iex has built in tab completion. Press tab at an empty prompt to see all functions available. 1259 | 1260 | #### Some useful functions 1261 | - h/0 : Documentation for IEx helpers 1262 | - h/1 : Documentation for a Module or Function 1263 | - i/1 : Inforamtion about a value 1264 | - c/1 : Compile and load a file 1265 | - r/1 : Reload a module 1266 | 1267 | TODO:
1268 | - `-S mix` 1269 | 1270 | ## Mix 1271 | Task runner, build tool, testing harness.
1272 | Run `mix help` from the command line for more details.
1273 | 1274 | ### Applications 1275 | `mix new PATH` generates the boilerplate for an elixir application.
1276 | Run `mix help new` for more details. 1277 | 1278 | ### Tasks 1279 | Mix tasks are modules under the `Mix.Task` namespace that `use Mix.Task` and implement a `run/1` function.
1280 | If you want your task to show up when `mix help` is run you must include a `@shortdoc`.
1281 | `@moduledoc` gets displayed when `mix help sometask` is run. 1282 | A base mix task file looks like: 1283 | ```elixir 1284 | defmodule Mix.Tasks.MyTask do 1285 | use Mix.Task 1286 | @shortdoc "Boilerplate for a task" 1287 | @moduledoc """ 1288 | This task just echos out the options passed to it. 1289 | """ 1290 | def run(opts) do 1291 | IO.inspect(opts) 1292 | end 1293 | end 1294 | ``` 1295 | 1296 | Elixir has a built in option parser that you should use for such a task:
1297 | http://elixir-lang.org/docs/stable/elixir/OptionParser.html 1298 | 1299 | ## Tests 1300 | Testing is built into the elixir platform via [ExUnit](http://elixir-lang.org/docs/stable/ex_unit/).
1301 | You can define tests in two ways.
1302 | If you are in a Mix Application, you put tests in files under the `test` directory.
1303 | These tests will be run with `mix test`.
1304 | You can also place tests at the end of any elixir file and they will be run on compilation.
1305 | Test boilerplate looks like: 1306 | ```elixir 1307 | # ⇣ not needed if in a mix project, setup for you in test_helper.exs 1308 | ExUnit.start 1309 | 1310 | defmodule MyModuleTest do 1311 | use ExUnit.Case 1312 | 1313 | test "the truth" do 1314 | assert 1 + 1 == 2 1315 | end 1316 | end 1317 | ``` 1318 | 1319 | ### Debugging in the context of a test 1320 | 1321 | 1. First add to the top of your test file (before `defmodule`): 1322 | ```elixir 1323 | require IEx 1324 | ``` 1325 | 1326 | 2. Inside your test, where you want to break into IEx, add: 1327 | ``` 1328 | IEx.pry 1329 | ``` 1330 | 1331 | 3. Run your tests with this invocation: 1332 | ```elixir 1333 | iex -S mix test --trace 1334 | ``` 1335 | The `--trace` prevents iex from timing out after you've broken into pry. 1336 | 1337 | 1338 | ## Style Guide 1339 | - Module, record, protocol and behaviour names start with an initial cap and are BumpyCase. 1340 | - Ex: MyModule 1341 | - All other identifiers start with a lowercase letter or an underscore and are snake_case. 1342 | - Ex: in_the_air_tonight 1343 | - For indentation use 2 spaces. No Tabs. 1344 | 1345 |
1346 | **Notes** 1347 | - Regex images generated here: http://jex.im/regulex/ 1348 | -------------------------------------------------------------------------------- /references/elixir/ERROR_HANDLING.md: -------------------------------------------------------------------------------- 1 | Error Handling 2 | ============== 3 | Although more common to return the {:error, reason} tuple, Elixir supports exceptions and in this lesson we’ll look at how to handle errors and the different mechanisms available to us. 4 | 5 | In general the convention in Elixir is to create a function (example/1) which returns {:ok, result} and {:error, reason} and a separate function (example!/1) that returns the unwrapped result or raises an error. 6 | 7 | The Elixir way is to use the pipe and `with` where each is more suitable for the task. So if you need to write a sequence of function calls where some or all are expected to return an error, `with` would be the most appropriate tool to use. 8 | 9 | One key difference between a pipeline and `with` is that the latter stops evaluation as soon as it encounters the first failed match. Whereas with a pipeline you'd have to wrap all constituent functions to pass the error from a previous step down to the next one. I see it as a downside to trying to use the pipe in places where it is more important to explicitly handle errors. 10 | 11 | The pipe operator is great when all functions are acting on a consistent piece of data. It falls apart when we introduce variability. That's where with comes in. with is a lot like a |> except that it allows you to match each intermediary result. 12 | 13 | 14 | If you are building a library, see 15 | [this](http://michal.muskala.eu/2017/02/10/error-handling-in-elixir-libraries.html) 16 | article. 17 | 18 | Anyway, it's important to differentiate between two kinds of error: 19 | 20 | *actionable errors* - i.e. expected errors. When those happen we need to handle them gracefully, so that our application can continue working correctly. A good example of this is invalid data provided by the user or data not present in the database. In general we want to handle this kind of errors by using tuple return values ({:ok, value} | {:error, reason}) - the consumer can match on the value and act on the result. 21 | 22 | *fatal errors* - errors that place our application in undefined state - violate some system invariant, or make us reach a point of no return in some other way. A good example of this is a required config file not present or a network failing in the middle of transmitting a packet. With that kind of errors we usually want to crash and lean on the supervision system to recover - we handle all errors of this kind in a unified way. That’s exactly what the let it crash philosophy is about - it’s not about letting our application burn in case of any errors, but avoiding excessive code for handling errors that can’t be handled gracefully. You can read more about what the “let it crash” means in the excellent article by Fred Hebert of the “Learn You Some Erlang for Great Good” fame - “The Zen of Erlang”. 23 | 24 | 25 | 26 | ## Basics 27 | 28 | Two ways for achieving the same result. The first is to use the pipe |> and for each function catch the error and propagate it down. The error has to be always returned with the same pattern, like {:error, reason} 29 | 30 | ```elixir 31 | connect 32 | |> receive_image 33 | |> resize 34 | |> rotate 35 | |> save 36 | ``` 37 | 38 | If receive_image returns an error, resize needs to handle it 39 | 40 | 41 | ```elixir 42 | def resize({:error, _}=error), do:error 43 | ``` 44 | 45 | and to propagate it down through the pipe. So rotate needs to do the same, etc... 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ```elixir 54 | defmodule Test do 55 | 56 | @doc """ 57 | Inserts the given JSON into the `data` field of `file`. 58 | """ 59 | def insert(file, data) do 60 | # Read the contents of the file 61 | file 62 | |> File.read() 63 | |> rewrite(file, data) 64 | |> to_user_message() 65 | |> IO.puts 66 | end 67 | 68 | defp rewrite({:ok, content}, file, data) do 69 | content 70 | |> Poison.decode() 71 | |> write_decoded_content(file, data) 72 | end 73 | defp rewrite({:error, reason}, _, _) do 74 | {:error, {:on_read, reason}} 75 | end 76 | 77 | defp write_decoded_content({:ok, decoded_content}, file, data) do 78 | data 79 | |> Poison.encode() 80 | |> write_encoded_data(file, decoded_content) 81 | end 82 | defp write_decoded_content({:error, :invalid}, _, _) do 83 | {:error, {:on_decode_invalid, ""}} 84 | end 85 | defp write_decoded_content({:error, :invalid, reason}, _, _) do 86 | {:error, {:on_decode, reason}} 87 | end 88 | 89 | defp write_encoded_data({:ok, encoded_data}, file, decoded_content) do 90 | # Prepare the updated data for insertion 91 | # and encode the updated data 92 | decoded_content 93 | |> Map.update("data", encoded_data, fn _ -> encoded_data end) 94 | |> Poison.encode() 95 | |> write_encoded_final_data(file) 96 | end 97 | defp write_encoded_data({:error, {:invalid, reason}}, _, _) do 98 | {:error, {:on_encode_data, reason}} 99 | end 100 | 101 | defp write_encoded_final_data({:ok, encoded_final_data}, file) do 102 | file 103 | |> File.write(encoded_final_data) 104 | |> report_on_file_write() 105 | end 106 | defp write_encoded_final_data({:error, {:invalid, reason}}, _) do 107 | {:error, {:on_encode_final_data, reason}} 108 | end 109 | 110 | defp report_on_file_write(:ok), 111 | do: :ok 112 | defp report_on_file_write({:error, reason}), 113 | do: {:error, {:on_file_write, reason}} 114 | 115 | defp to_user_message(:ok), 116 | do: "Successfully updated file!" 117 | defp to_user_message({:error, {source, reason}}), 118 | do: to_error_msg source, reason 119 | 120 | defp to_error_message(source, reason) do 121 | case source do 122 | :on_read -> 123 | "Could not read file because #{reason}!" 124 | :on_decode_invalid -> 125 | "Could not parse file to JSON!" 126 | :on_decode -> 127 | "Could not parse file to JSON because #{reason}!" 128 | :on_encode_data -> 129 | "Could not encode given JSON because #{reason}!" 130 | :on_encode_final_data -> 131 | "Could not encode updated JSON because #{reason}!" 132 | :on_file_write -> 133 | "Could not write to file because #{reason}!" 134 | end 135 | end 136 | end 137 | ``` 138 | 139 | 140 | 141 | So if you need to write a sequence of function calls where some or all are expected to return an error, `with` would be the most appropriate tool to use. 142 | 143 | ```elixir 144 | with {:ok, result1} <- square(a), 145 | {:ok, result2} <- half(result1), 146 | {:ok, result3} <- triple(result2), 147 | {:ok, result4} <- do_even_more_stuff(result3) 148 | do 149 | IO.puts “Success! the result is #{result4}” 150 | else 151 | {:error, error} -> IO.puts “Oops, something went wrong: #{error}” 152 | end 153 | ``` 154 | 155 | Now, the cool thing about with is that when one of the assignments fails, it exits returning the value that failed the pattern match. 156 | 157 | Using throw and catch is good for the newbies Ruby guys only, it consumes a lot of resources and the code becomes unreadable. So avoid it. 158 | 159 | 160 | ## Advanced 161 | 162 | * https://medium.com/elixir-magic/making-error-handling-a-breeze-with-with-operator-in-elixir-1-2-93d611a878e 163 | * https://elixirschool.com/en/lessons/advanced/error-handling/ 164 | * https://elixir-lang.org/getting-started/try-catch-and-rescue.html 165 | * https://dzone.com/articles/error-handling-strategies 166 | * http://michal.muskala.eu/2017/02/10/error-handling-in-elixir-libraries.html 167 | 168 | -------------------------------------------------------------------------------- /references/elixir/FUNCTION_APPLICATION.md: -------------------------------------------------------------------------------- 1 | Function Application 2 | ==================== 3 | 4 | Especially given that the function signature of a function in Elixir includes its arity, it seems somewhat unlikely that Elixir would support currying and/or partial function application. But a little bit of hacking turned up a nice surprise. While Elixir may not support currying it does support partial function application which seems to be the main reason to concern one’s self with currying anyway. 5 | 6 | 7 | ### Partial Function Application With Lambdas 8 | 9 | ```elixir 10 | multiply = &(&1 * &2) 11 | times_two = &(multiply.(&1, 2)) 12 | ``` 13 | 14 | 15 | ### Partial Function Application With Module 16 | 17 | 18 | ```elixir 19 | 20 | defmodule PaTest do 21 | def multiply(n, m), do: n * m 22 | 23 | def times_two, do: multiply(2, m) 24 | end 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /references/elixir/MONITORING.md: -------------------------------------------------------------------------------- 1 | Monitoring 2 | ========== 3 | 4 | 5 | ## Metric Collection 6 | 7 | * [exometer](https://github.com/Feuerlabs/exometer) - Ulf Wiger and Magnus 8 | Feuer’s Exometer is the defacto Erlang instrumentation package. It can 9 | record metrics, store and report them. 10 | 11 | * [elixometer](https://github.com/pinterest/elixometer) - Pinterest’s Elixometer is a light Elixir wrapper around Exometer that makes defining/updating metrics easier, plus has a nice @timed annotation to easily time a function’s execution time 12 | 13 | * [wombat](https://www.erlang-solutions.com/products/wombat-oam.html) - A full-featured operations and maintenance framework, Wombat is commercial software available via Erlang Solutions. 14 | 15 | 16 | 17 | ## Application Monitoring 18 | 19 | 20 | * app signal 21 | * scout 22 | * new relic 23 | 24 | 25 | ## Tracing and Profiling 26 | 27 | * [recon](http://ferd.github.io/recon/recon_trace.html) - Part of the previously mentioned Recon application, Recon Trace is an Erlang module that allows for safe tracing of functions in a production environment. 28 | 29 | 30 | * Tap 31 | * Erlyberly 32 | * REdbug 33 | * DBG 34 | 35 | 36 | ## Exception Monitoring 37 | 38 | * HoneyBadger 39 | * Sentry 40 | * Rollbar 41 | 42 | -------------------------------------------------------------------------------- /references/elixir/PATTERN-MATCHING.md: -------------------------------------------------------------------------------- 1 | Pattern Matching 2 | ================ 3 | 4 | 5 | 6 | ```elixir 7 | def userselect( user ) do 8 | cond do 9 | Map.has_key?( user, :id ) -> 10 | sel_user = User |> Repo.get( user.id ) 11 | 12 | case sel_user do 13 | nil -> { :error, "Something went wrong!" } 14 | _ -> { :ok, sel_user } 15 | end 16 | Map.has_key?( user, :email ) -> 17 | sel_user = User |> Repo.get_by( email: user.email ) 18 | 19 | case sel_user do 20 | nil -> { :error, "Something went wrong!" } 21 | _ -> { :ok, sel_user } 22 | end 23 | Map.has_key?( user, :username ) -> 24 | sel_user = User |> Repo.get_by( username: user.username ) 25 | 26 | case sel_user do 27 | nil -> { :error, "Something went wrong!" } 28 | _ -> { :ok, sel_user } 29 | end 30 | true -> 31 | { :error, "It's immposible to select a user using this parameter." } 32 | end 33 | end 34 | ``` 35 | 36 | after using pattern matching: 37 | 38 | 39 | ```elixir 40 | 41 | def userselect(%{id: id}), do: User |> Repo.get(id) |> sel_user() 42 | def userselect(%{email: email}), do: User |> Repo.get(email) |> sel_user() 43 | def userselect(%{username: username}), do: User |> Repo.get(username) |> sel_user() 44 | def userselect(_), do: sel_user(nil) 45 | 46 | defp sel_user(nil), do: {:error, "Something went wrong"} 47 | defp sel_user(user), do: {:ok, user} 48 | ``` 49 | 50 | 51 | 52 | ## Advanced 53 | 54 | * 55 | [5-things-to-remember](https://blog.carbonfive.com/2017/10/19/pattern-matching-in-elixir-five-things-to-remember/) 56 | - see importants concepts around pattern matching 57 | -------------------------------------------------------------------------------- /references/elixir/PIPES.md: -------------------------------------------------------------------------------- 1 | Pipes 2 | ===== 3 | 4 | 5 | 6 | ### Bang functions 7 | 8 | Sometimes, to make your pipe flow, you can't get a {:ok, res} from a function in the pipe, so below a recipe 9 | to make it a `bang` function: 10 | 11 | ```elixir 12 | @spec foo(any) :: {:ok, Foo.t} | {:error, String.t} 13 | def foo(bar), do: bar |> do_the_magic 14 | 15 | @spec foo!(any) :: Foo.t | no_return 16 | def foo!(bar) do 17 | case foo(bar) do 18 | {:ok, result} -> result 19 | {:error, _} -> raise FooError 20 | end 21 | end 22 | ``` 23 | 24 | 25 | 26 | 27 | ### Anonymous functions 28 | 29 | ```elixir 30 | def md5(str) do 31 | str 32 | |> (fn(s) -> :erlang.md5(s) end).() 33 | |> Base.encode16(case: :lower) 34 | end 35 | ``` 36 | 37 | 38 | or alternatively: 39 | 40 | 41 | ```elixir 42 | def md5(str) do 43 | str 44 | |> (&(:erlang.md5(&1))).() 45 | |> Base.encode16(case: :lower) 46 | end 47 | ``` 48 | -------------------------------------------------------------------------------- /references/elixir/SPECS.md: -------------------------------------------------------------------------------- 1 | Specs 2 | ===== 3 | 4 | Firstly, it acts as documentation. It’s much easier to read and understand code if you know what the types are. Even the best named arguments and functions can be ambiguous when you are trying to get to grips with new code. Function specifications and custom types make it explicitly clear as to what is going on, and tools like ExDoc can take advantage of your specifications to show this kind of detail in the documentation that is produced from your code. 5 | 6 | Secondly, you can use Dialyzer, which is an Erlang static analysis tool to find discrepancies or possible bugs in your code. In Elixir we can use dialyxir to make it easier to work with Dialyzer. Whilst using Dialyzer does not guarantee that you will find all bugs and errors in your code, you are sure to get some benefit from writing specs and using a static analysis tool. 7 | 8 | 9 | 10 | ## Basics 11 | 12 | * [intro](https://elixirschool.com/en/lessons/advanced/typespec/) good introduction on specs. 13 | * [what-are-typespecs](https://www.culttt.com/2016/10/26/specifications-types-elixir/) 14 | - why and how to use type and specs 15 | 16 | Elixir comes with a notation for declaring types, as you can see some examples 17 | below: 18 | 19 | ``` 20 | type :: any() # the top type, the set of all terms 21 | | none() # the bottom type, contains no terms 22 | | atom() 23 | | map() # any map 24 | | pid() # process identifier 25 | | port() 26 | | reference() 27 | | struct() # any struct 28 | | tuple() # tuple of any size 29 | 30 | ## Numbers 31 | | float() 32 | | integer() 33 | | neg_integer() # ..., -3, -2, -1 34 | | non_neg_integer() # 0, 1, 2, 3, ... 35 | | pos_integer() # 1, 2, 3, ... 36 | 37 | ## Lists 38 | | list(type) # proper list ([]-terminated) 39 | | nonempty_list(type) # non-empty proper list 40 | | maybe_improper_list(type1, type2) # proper or improper list 41 | | nonempty_improper_list(type1, type2) # improper list 42 | | nonempty_maybe_improper_list(type1, type2) # non-empty proper or improper list 43 | ``` 44 | 45 | 46 | Using specs, you will always know what type of data is getting into the 47 | function, and also, what type of data will be on the output. An amazing tool 48 | called dialyzer will make several tests automatically, to check the type 49 | consistency and much more. 50 | 51 | ```elixir 52 | defmodule Calc do 53 | @moduledoc """ 54 | Calculator module. 55 | """ 56 | 57 | @typedoc "the term that will receive an addition" 58 | @type term1 :: integer | float 59 | 60 | @typedoc "the term that will be added over the first term" 61 | @type term2 :: integer | float 62 | 63 | @typedoc "result from the both terms addition" 64 | @type result :: integer | float 65 | 66 | @doc """ 67 | Add two elemets, i.e. terms, one over another. 68 | 69 | ## Examples 70 | 71 | iex> Calc.add(1, 1) 72 | 3 73 | 74 | """ 75 | @spec add(term1, term2) :: result 76 | def add(term1, term2) do 77 | term1 + term2 78 | end 79 | end 80 | ``` 81 | 82 | 83 | ## Advanced 84 | 85 | * [official-documentation](https://hexdocs.pm/elixir/typespecs.html) - original 86 | elixir docs 87 | -------------------------------------------------------------------------------- /references/elixir/WITH.md: -------------------------------------------------------------------------------- 1 | With 2 | ==== 3 | 4 | 5 | ## Basics 6 | 7 | We start with using the with keyword and the first assignment on the first line. I wanted to make that pretty and indent it underneath with, but that’s not OK. 8 | 9 | We are pattern matching {:ok, width} with Map.fetch(opts, :width). This uses a backwards arrow operator which is a little strange. There’s probably a good explanation about why not pattern match on an =. Whatever the reason, it helps me think about each step of my work because it’s different. 10 | 11 | Put another way, that first line fetches :width from the opts map. It expects a tuple starting with :ok, and whatever width is in the map (10 in our case). If that doesn’t match, the else code is called. More on that in a moment. 12 | 13 | Notice a comma at the end of the first pattern match/operation. We then go to the next operation, and keep going as long as we’d like, pattern matching and collecting values as we go. These values are available to me in subsequent calls or my do block. Did you notice that block? In this case, it returns {:ok, width * height}. If all went well, we get the area of a square. If not, the else code is written. 14 | 15 | Now, the else statement works like a case. Something didn’t work, let’s pattern match what that was. In this case, we expect :error only, so we use :error -> {:error, :wrong_data}. If things could go wrong inconsistently, you could write many pattern matching entries here. The syntax for the else is like a case, meaning no commas and forward arrows. 16 | 17 | with recapped: 18 | 19 | * start the with operations on the same line as with 20 | * use backward arrows ( < -) for the operations 21 | * use commas to separate operations 22 | * pattern match the happy path from each operation 23 | * create a do block to handle what happens when everything goes write 24 | * create an else block to handle what happens when something goes wrong 25 | * leave the else block out if you can handle the error response directly 26 | * use case syntax for the else block (forward arrows ( - >) and no commas) 27 | 28 | 29 | 30 | ```elixir 31 | opts = %{width: 10} 32 | with {:ok, width} <- Map.fetch(opts, :width), 33 | {:ok, height} <- Map.fetch(opts, :height) do 34 | {:ok, width * height} 35 | else 36 | :error -> 37 | {:error, :wrong_data} 38 | end 39 | ``` 40 | 41 | Below is an example that we will rewrite with more flexibility, using `with` 42 | 43 | ```elixir 44 | defmodule User do 45 | defstruct name: nil, dob: nil 46 | 47 | def create(params) do 48 | %User{} 49 | |> parse_dob(params["dob"]) 50 | |> parse_name(params["name"]) 51 | end 52 | 53 | defp parse_dob(user, nil), do: {:error, "dob is required"} 54 | defp parse_dob(user, dob) when is_integer(dob), do: %{user | dob: dob} 55 | defp parse_dob(_user, _invalid), do: {:error "dob must be an integer"} 56 | 57 | defp parse_name(_user, {:error, _} = err), do: err 58 | defp parse_name(user, nil), do: {:error, "name is required"} 59 | defp parse_name(user, ""), do: parse_name(user, nil) 60 | defp parse_name(user, name), do: %{user | name: name} 61 | 62 | end 63 | ``` 64 | 65 | The problem with this approach is that every function in the chain needs to handle the case where any function before it returned an error. It's clumsy, both because it isn't pretty and because it isn't flexible. Any new return type that we introduced has to be handled by all functions in the chain. 66 | 67 | The pipe operator is great when all functions are acting on a consistent piece of data. It falls apart when we introduce variability. That's where with comes in. with is a lot like a |> except that it allows you to match each intermediary result. 68 | 69 | Let's rewrite our code: 70 | 71 | ```elixir 72 | defmodule User do 73 | defstruct name: nil, dob: nil 74 | def create(params) do 75 | with {:ok, dob} <- parse_dob(params["dob"]), 76 | {:ok, name} <- parse_name(params["name"]) 77 | do 78 | %User{dob: dob, name: name} 79 | else 80 | # nil -> {:error, ...} an example that we can match here too 81 | err -> err 82 | end 83 | end 84 | 85 | defp parse_dob(nil), do: {:error, "dob is required"} 86 | defp parse_dob(dob) when is_integer(dob), do: {:ok, dob} 87 | defp parse_dob(_invalid), do: {:error "dob must be an integer"} 88 | 89 | defp parse_name(nil), do: {:error, "name is required"} 90 | defp parse_name(""), do: parse_name(nil) 91 | defp parse_name(name), do: {:ok, name} 92 | end 93 | ``` 94 | 95 | 96 | 97 | Instead of this: 98 | 99 | ```elixir 100 | defp serve(socket) do 101 | msg = 102 | case read_line(socket) do 103 | {:ok, data} -> 104 | case KVServer.Command.parse(data) do 105 | {:ok, command} -> 106 | KVServer.Command.run(command) 107 | {:error, _} = err -> 108 | err 109 | end 110 | {:error, _} = err -> 111 | err 112 | end 113 | 114 | write_line(socket, msg) 115 | serve(socket) 116 | end 117 | ``` 118 | 119 | See how beautiful is this: 120 | 121 | ```elixir 122 | defp serve(socket) do 123 | msg = 124 | with {:ok, data} <- read_line(socket), 125 | {:ok, command} <- KVServer.Command.parse(data), 126 | do: KVServer.Command.run(command) 127 | 128 | write_line(socket, msg) 129 | serve(socket) 130 | end 131 | ``` 132 | 133 | 134 | But because the `msg` is unexpected, we should cover all possible returns: 135 | 136 | ```elixir 137 | defp serve(socket) do 138 | msg = 139 | with {:ok, data} <- read_line(socket), 140 | {:ok, command} <- KVServer.Command.parse(data), 141 | do: KVServer.Command.run(command) 142 | 143 | case msg do 144 | {:ok, value} -> # The whole chain succeeded 145 | write_line(socket, msg) 146 | 147 | {:error, :read_line, error} -> # Bailed out in `read_line/1` 148 | Log.error "Bad read_line/1 from socket #{inspect(socket)}" 149 | 150 | {:error, :command_parse, error} -> 151 | Log.error "Can't parse response #{inspect(data)}" 152 | do_something_with(error) 153 | 154 | unexpected -> 155 | Log.error "Got unexpected value #{inspect(unexpected)}" 156 | end 157 | 158 | serve(socket) 159 | end 160 | ``` 161 | 162 | 163 | but from Elixir 1.3 164 | 165 | ```elixir 166 | defp serve(socket) do 167 | with {:ok, data} <- read_line(socket), 168 | {:ok, command} <- KVServer.Command.parse(data), 169 | {:ok, msg} <- KVServer.Command.run(command) do 170 | write_line(socket, msg) 171 | else 172 | {:error, :read_line, error} -> # Bailed out in `read_line/1` 173 | Log.error "Bad read_line/1 from socket #{inspect(socket)}" 174 | 175 | {:error, :command_parse, error} -> # Bailed in ...Command.parse/1 176 | Log.error "Can't parse response #{inspect(data)}" 177 | do_something_with(error) 178 | 179 | unexpected -> 180 | Log.error "Got unexpected value #{inspect(unexpected)}" 181 | end 182 | 183 | serve(socket) 184 | end 185 | ``` 186 | 187 | 188 | 189 | 190 | 191 | ## Advanced 192 | 193 | See our `error handling` section for more examples. 194 | 195 | * https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html 196 | * http://openmymind.net/Elixirs-With-Statement/ 197 | * http://relistan.com/elixir-thoughts-on-the-with-statement/ 198 | * http://learningelixir.joekain.com/learning-elixir-with/ 199 | -------------------------------------------------------------------------------- /references/elixir/images/atom-naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/elixir/images/atom-naming.png -------------------------------------------------------------------------------- /references/elixir/images/module-naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/elixir/images/module-naming.png -------------------------------------------------------------------------------- /references/elixir/images/variable-naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/elixir/images/variable-naming.png -------------------------------------------------------------------------------- /references/git/GIT.md: -------------------------------------------------------------------------------- 1 | Git 2 | === 3 | 4 | Read carefully [this](http://nvie.com/posts/a-successful-git-branching-model) article. Below, the main 5 | git flow for a large team. For small teams, using 'master' as the only origin 6 | should be okay, see 7 | [here](https://www.atlassian.com/git/tutorials/comparing-workflows) for more details. 8 | 9 | 10 | ![](git-model.png) 11 | 12 | 13 | 14 | #### To see all your commits 15 | 16 | ``` 17 | git log 18 | ``` 19 | 20 | #### See if there are modifications for the actual commit 21 | 22 | ``` 23 | git status 24 | ``` 25 | 26 | #### To move to a previous commit (see the key in git log before) 27 | 28 | In git log, we found the key 40cfc393698673f506a6a9a2d22dd01ed56a8183 29 | as for the commit we want to move 30 | 31 | ``` 32 | git checkout 40cfc393698673f506a6a9a2d22dd01ed56a8183 33 | ``` 34 | 35 | To move back 36 | 37 | ``` 38 | git checkout my_branch_name_here 39 | ``` 40 | 41 | 42 | #### To get last modifications from the remote server 43 | 44 | It automatically merge 45 | 46 | ``` 47 | git pull 48 | ``` 49 | 50 | But, if there will be conflicts, use a 'git mergetool' or a GUI 51 | like [gitkraken](https://www.gitkraken.com) to visually edit (believe me, 52 | it's easier with a graphical tool) 53 | 54 | #### To create a new branch 55 | 56 | To understand branches and how it works, see 57 | [here](http://nvie.com/posts/a-successful-git-branching-model/) 58 | 59 | ``` 60 | git checkout -b the_name_of_my_new_branch_here 61 | ``` 62 | 63 | 64 | #### To see remote git addresses 65 | 66 | ``` 67 | git remote -v 68 | ``` 69 | 70 | 71 | #### To add files to be tracked in the actual commit 72 | 73 | You can add the file, or the complete directory recursivelly using '.' 74 | 75 | 76 | ``` 77 | git add . 78 | ``` 79 | 80 | #### To commit your files that you added to the track [with the command above] 81 | 82 | Write interesting messages on what you acomplished in this commit for your 83 | fellow co-workers. It will really help. Avoid: fix, fix1, fix2, add feature, 84 | etc... The convention is to use the infinitive verb format, like: 85 | 86 | * add query for clients 87 | * fix bug [#2323] 88 | * refactor bank service modules 89 | 90 | 91 | ``` 92 | git commit -m "my message here" 93 | ``` 94 | 95 | 96 | #### Push your code to the desired remote branch. 97 | 98 | Supose you are working directly on 'master' branch: 99 | 100 | ``` 101 | git push origin master 102 | ``` 103 | 104 | 105 | -------------------------------------------------------------------------------- /references/git/git-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/git/git-model.png -------------------------------------------------------------------------------- /references/http/CURL.md: -------------------------------------------------------------------------------- 1 | Curl 2 | ==== 3 | 4 | 5 | ### Options 6 | 7 | ``` 8 | -o # --output: write to file 9 | -u user:pass # --user: Authentication 10 | -v # --verbose 11 | -vv # Even more verbose 12 | -I # --head: headers only 13 | ``` 14 | 15 | 16 | ### Request 17 | 18 | ``` 19 | -X POST # --request 20 | ``` 21 | 22 | 23 | ### Data 24 | 25 | ``` 26 | -d 'data' # --data: HTTP post data, URL encoded (eg, status="Hello") 27 | -d @file # --data via file 28 | -G # --get: send -d data via get 29 | ``` 30 | 31 | ### Headers 32 | 33 | ``` 34 | -A # --user-agent 35 | -b name=val # --cookie 36 | -b FILE # --cookie 37 | -H "X-Foo: y" # --header 38 | --compressed # use deflate/gzip 39 | ``` 40 | 41 | ### SSL 42 | 43 | ``` 44 | --cacert 45 | --capath 46 | -E, --ecrt # --ecrt: Client cert file 47 | --cert-type # der/pem/eng 48 | -k, --insecure # for self-signed certs 49 | ``` 50 | 51 | ### Examples 52 | 53 | ``` 54 | # Post data: 55 | curl -d password=x http://x.com/y 56 | 57 | # Auth/data: 58 | curl -u user:pass -d status="Hello" http://twitter.com/statuses/update.xml 59 | 60 | # multipart file upload 61 | curl -v -include --form key1=value1 --form upload=@localfilename URL 62 | 63 | # save output to a file 64 | curl -o mygettext.html http://www.gnu.org/software/gettext/manual/gettext.html 65 | 66 | # upload file to a ftp server 67 | curl -u ftpuser:ftppass -T myfile.txt ftp://ftp.testserver.com 68 | 69 | # get HTTP header 70 | curl -I http://domain.com 71 | 72 | ``` 73 | 74 | 75 | ## Advanced 76 | 77 | * https://www.thegeekstuff.com/2012/04/curl-examples/ 78 | * https://curl.haxx.se/docs/httpscripting.html 79 | -------------------------------------------------------------------------------- /references/http/HTTP.md: -------------------------------------------------------------------------------- 1 | HTTP 2 | ==== 3 | 4 | ## Basics 5 | 6 | 7 | HTTP is the protocol used to fetch data from web servers. It is a very simple 8 | protocol that is built upon TCP/IP. The protocol also allows information to get 9 | sent to the server from the client using a few different methods, as will be 10 | shown here. 11 | 12 | HTTP is plain ASCII text lines being sent by the client to a server to request a 13 | particular action, and then the server replies a few text lines before the 14 | actual requested content is sent to the client. 15 | 16 | The client, curl, sends a HTTP request. The request contains a method (like GET, 17 | POST, HEAD etc), a number of request headers and sometimes a request body. The 18 | HTTP server responds with a status line (indicating if things went well), 19 | response headers and most often also a response body. The "body" part is the 20 | plain data you requested, like the actual HTML or the image etc. 21 | 22 | To see the protocol, with time tracing: 23 | 24 | ``` 25 | curl --trace-ascii dump.txt --trace-time http://example.com/ 26 | ``` 27 | 28 | See below the possible codes HTTPs requests can answer you: 29 | 30 | ![](http-headers-status-v3.png) 31 | 32 | 33 | ## Advanced 34 | 35 | * https://curl.haxx.se/docs/httpscripting.html#The_HTTP_Protocol 36 | -------------------------------------------------------------------------------- /references/http/REST.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/http/REST.md -------------------------------------------------------------------------------- /references/http/http-headers-status-v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/http/http-headers-status-v3.png -------------------------------------------------------------------------------- /references/linux/LINUX.md: -------------------------------------------------------------------------------- 1 | Linux 2 | ===== 3 | 4 | * [conquering-the-command-line](http://conqueringthecommandline.com/book) - one 5 | of the best books, a must to all linux devs. 6 | 7 | * [the-art-of-the-command-line](https://github.com/jlevy/the-art-of-command-line) 8 | - see this link, and you are done. 9 | 10 | * [awesome-shell](https://github.com/alebcay/awesome-shell) - many important 11 | tools 12 | -------------------------------------------------------------------------------- /references/linux/REGEX.md: -------------------------------------------------------------------------------- 1 | Regex 2 | ===== 3 | 4 | 5 | ## Basics 6 | 7 | * [tutorial](https://regexone.com) - tutorial for beginers 8 | * [quick-cheat-sheet](https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285) - learn by examples. I suggest to apply this examples using the tool in the link below 9 | * [on-line-creation](https://regexr.com) - amazing resource to learn regex and create your patterns online 10 | 11 | 12 | 13 | ## Advanced 14 | 15 | * [grouping](https://www.regular-expressions.info/refcapture.html) - Using 16 | parethesis to create groups 17 | -------------------------------------------------------------------------------- /references/linux/VIM.md: -------------------------------------------------------------------------------- 1 | Vim 2 | === 3 | 4 | 5 | ## Basics 6 | 7 | * http://i.imgur.com/YLInLlY.png 8 | * http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html 9 | 10 | 11 | 12 | ## Advanced 13 | 14 | * 15 | -------------------------------------------------------------------------------- /references/linux/vim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/linux/vim.gif -------------------------------------------------------------------------------- /references/markdown/MARKDOWN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/markdown/MARKDOWN.md -------------------------------------------------------------------------------- /references/phoenix/CONN.md: -------------------------------------------------------------------------------- 1 | Conn 2 | ==== 3 | 4 | ### Request 5 | 6 | ``` 7 | conn.host # → "example.com" 8 | conn.method # → "GET" 9 | conn.path_info # → ["posts", "1"] 10 | conn.request_path # → "/posts/1" 11 | conn.query_string # → "utm_source=twitter" 12 | conn.port # → 80 13 | conn.scheme # → :http 14 | conn.peer # → { {127, 0, 0, 1}, 12345 } 15 | conn.remote_ip # → { 151, 236, 219, 228 } 16 | conn.req_headers # → [{"content-type", "text/plain"}] 17 | conn |> get_req_header("content-type") 18 | # → ["text/plain"] 19 | ``` 20 | 21 | ### Response 22 | 23 | ``` 24 | conn.resp_body # → "..." 25 | conn.resp_charset # → "utf-8" 26 | conn.resp_cookies # → ... 27 | conn.resp_headers # → ... 28 | conn.status # → ... 29 | ``` 30 | 31 | ### Update 32 | 33 | ``` 34 | conn 35 | |> put_req_header("accept", "application/json") 36 | ``` 37 | 38 | ### Views 39 | 40 | ``` 41 | # Phoenix.Controller 42 | conn 43 | |> render("index.html") 44 | |> render("index.html", hello: "world") 45 | |> render(MyApp.ErrorView, "404.html") 46 | |> put_layout(:foo) 47 | |> put_layout(false) 48 | |> put_view(ErrorView) 49 | |> put_secure_browser_headers() 50 | 51 | # prevent clickjacking, nosniff, and xss protection 52 | # x-frame-options, x-content-type-options, x-xss-protection 53 | |> put_new_view(ErrorView) # if not set yet 54 | |> put_new_layout(:foo) 55 | layout(conn) 56 | ``` 57 | 58 | ### Sending Reponses 59 | 60 | ``` 61 | # Plug.Conn 62 | conn 63 | |> html("...") 64 | |> json(%{ message: "Hello" }) 65 | |> text("Hello") 66 | |> redirect(to: "/foo") 67 | |> redirect(external: "http://www.google.com/") 68 | |> halt() 69 | |> put_resp_content_type("text/plain") 70 | |> put_resp_cookie("abc", "def") 71 | |> put_resp_header("X-Delivered-By", "myapp") 72 | |> put_status(202) 73 | |> put_status(:not_found) 74 | |> put_private(:plug_foo, "...") # reserved for libraries 75 | |> send_resp(201, "") 76 | ``` 77 | 78 | ### Accepts 79 | 80 | ``` 81 | plug :accepts, ["html", "json"] 82 | conn |> accepts(["html", "json"]) 83 | get_format(conn) # → "html" 84 | conn.accepts 85 | ``` 86 | 87 | 88 | ### Assigns 89 | 90 | ``` 91 | conn.assigns[:hello] 92 | conn |> assign(:user_id, 100) 93 | conn = async_assign(conn, :location, fn -> geoip_lookup() end) 94 | await_assign(conn, :location) 95 | ``` 96 | 97 | ### Session 98 | 99 | ``` 100 | conn = fetch_session(conn) # or plug :fetch_session 101 | 102 | conn = put_session(conn, :message, "new stuff we just set in the session") 103 | get_session(conn, :message) 104 |  105 | conn = clear_session(conn) 106 | conn 107 | |> put_flash(:info, "Success") 108 | |> put_flash(:error, "Oh no") 109 | ``` 110 | 111 | 112 | -------------------------------------------------------------------------------- /references/phoenix/CONTROLLERS.md: -------------------------------------------------------------------------------- 1 | Controllers 2 | =========== 3 | 4 | 5 | https://sites.google.com/s/17j6onZdglX42WS4e-D5x_e_GqxE4UqB0/p/1eLjZA0T8hzixiYL93u9eHbWqGtigNvIi/preview?authuser=0 6 | -------------------------------------------------------------------------------- /references/phoenix/PHOENIX.md: -------------------------------------------------------------------------------- 1 | Phoenix 2 | ======= 3 | 4 | 5 | Quick start: 6 | 7 | ``` 8 | # Install Phoenix 9 | mix local.hex 10 | mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez 11 | 12 | # Create a new project 13 | mix phx.new hello 14 | 15 | # Start the application 16 | mix phx.server 17 | 18 | ``` 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /references/phoenix/PLUG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/phoenix/PLUG.md -------------------------------------------------------------------------------- /references/phoenix/ecto/ECTO.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/phoenix/ecto/ECTO.md -------------------------------------------------------------------------------- /references/phoenix/ecto/MIGRATIONS.md: -------------------------------------------------------------------------------- 1 | Ecto Migrations 2 | =============== 3 | 4 | 5 | ### Creating 6 | 7 | ``` 8 | mix ecto.gen.migration update_posts_table 9 | creating priv/repo/migrations/20160602085927_update_posts_table.exs 10 | ··· 11 | $ mix ecto.migrate 12 | $ mix ecto.rollback 13 | ``` 14 | 15 | ### Creating tables 16 | 17 | ``` 18 | create table(:documents) do 19 | add :title, :string 20 | add :title, :string, size: 40 21 | add :title, :string, default: "Hello" 22 | add :title, :string, default: fragment("now()") 23 | add :title, :string, null: false 24 | add :body, :text 25 | add :age, :integer 26 | add :price, :float 27 | add :price, :float, precision: 10, scale: 2 28 | add :published_at, :datetime 29 | add :group_id, references(:groups) 30 | add :object, :json 31 | 32 | timestamps # inserted_at and updated_at 33 | end 34 | 35 | create_if_not_exists table(:documents) do: ... end 36 | ``` 37 | 38 | ### Execute SQL 39 | 40 | ``` 41 | execute "UPDATE posts SET published_at = NULL" 42 | execute create: "posts", capped: true, size: 1024 43 | ``` 44 | 45 | ### Other operations 46 | 47 | ``` 48 | alter table(:posts) do 49 | add :summary, :text 50 | modify :title, :text 51 | remove :views 52 | end 53 | rename table(:posts), :title, to: :summary 54 | rename table(:posts), to: table(:new_posts) 55 | drop table(:documents) 56 | drop_if_exists table(:documents) 57 | table(:documents) 58 | table(:weather, prefix: :north_america) 59 | ``` 60 | 61 | ### Indexes 62 | 63 | ``` 64 | create index(:posts, [:slug], concurrently: true) 65 | create unique_index(:posts, [:slug]) 66 | drop index(:posts, [:name]) 67 | ``` 68 | 69 | ## Advanced 70 | 71 | * http://devdocs.io/phoenix/ecto/ecto.migration 72 | * https://hexdocs.pm/ecto/Ecto.Migration.html 73 | -------------------------------------------------------------------------------- /references/phoenix/ecto/MULTI.md: -------------------------------------------------------------------------------- 1 | Ecto Multi 2 | ========== 3 | 4 | ## Basics 5 | 6 | The module below is responsible for file creation transaction which consists of three operations: 7 | 8 | Storing attachment by calling File.Attachment.store which basically copies the file into the proper place and returns the path. 9 | Inserting the DB record with the path received from the previous step. 10 | Run some processing task asynchronously passing the struct from the previous step. 11 | You may ask why am I using Ecto.Multi when I have only one database query? Because if any of the steps returns an error it prevents the multi from executing next steps. 12 | 13 | 14 | ```elixir 15 | defmodule MyApp.File.Create do 16 | alias Ecto.Multi 17 | alias MyApp.{Repo, File} 18 | 19 | def call(attachment, user) do 20 | case transaction(attachment, user) |> Repo.transaction do 21 | {:ok, %{insert: file}} -> {:ok, file} 22 | {:error, _, reason, _} -> {:error, reason} 23 | end 24 | end 25 | 26 | defp transaction(attachment, user) do 27 | Multi.new 28 | |> Multi.run(:store, fn _ -> File.Attachment.store(attachment) end) 29 | |> Multi.run(:insert, &insert(&1.store, user)) 30 | |> Multi.run(:process, &process(&1.insert)) 31 | end 32 | 33 | defp insert(path, user) do 34 | %File{} 35 | |> File.changeset(%{path: path, owner_id: user.id}) 36 | |> Repo.insert 37 | end 38 | 39 | defp process(file) do 40 | Task.start_link(fn -> File.Process.call(file) end) 41 | end 42 | end 43 | ``` 44 | 45 | Inside the create function, we’re creating a new Multi struct with Ecto.Multi.new. The important thing here is we’re creating a data type that stores a local log of every step along the way. This is the all-together-now approach to the code. 46 | 47 | This is a nice way to setup a changeset, adding some defaults more simply. A basic Customer changeset with whatever parameters I know about, and setting up the wallet association. A lot of power, and little fuss. Win. 48 | 49 | Now we’re on to some custom code with run (twice, actually). We can write any function we’d like. We still label every step, we always label every step. The key is to use the right return value: {:ok,value} or {:error, value}. The more I build with Elixir, the more I love this interface. It makes it easier to manage error handling code with tuples like this. 50 | 51 | Finally, we call transaction. This also uses the same tuple return code and the same labeling convention. (works only with Postgres) 52 | 53 | 54 | ```elixir 55 | defmodule Bank.CustomerRegistration do 56 | use Bank.Model 57 | 58 | def create(username, email, password) do 59 | Ecto.Multi.new 60 | |> Ecto.Multi.insert(:customer, Customer.build(%{username: username, email: email})) 61 | |> Ecto.Multi.run(:account, fn _ -> 62 | Auth.register(%{email: email, password: password}) 63 | end) 64 | |> Ecto.Multi.run(:update, fn %{customer: customer, account: account} -> 65 | Ecto.Changeset.change(customer, auth_account_id: account.id) 66 | |> Repo.update 67 | end) 68 | |> Repo.transaction() 69 | end 70 | end 71 | 72 | # in another module 73 | def build(%{username: username} = params) do 74 | changeset(%Customer{}, params) 75 | |> put_assoc(:wallet, Ledger.Account.build_wallet("Wallet: #{username}")) 76 | end 77 | 78 | ``` 79 | 80 | First, let’s tackle the entry_transaction function. The Ecto.Multi module has functions available that correspond with Ecto.Repo functions like insert, delete, and update (to name just a few). However, these accept a new second argument that is identified in the docs as name. The name argument lets you name a particular portion of the grouped transactions in order to pattern match on it later. In my example above, I’ve simply named them after the record that is being dealt with. We’ll come back to this later when we check out our enter_doorfunction. Note that these have to be unique within the transaction grouping. 81 | 82 | Each step of the transaction passes the Multi.new result down through the chain until eventually, after building up our transaction a piece at a time, we tell our Repo to actually make the calls. 83 | 84 | If at any point in the steps a changeset has errors or the database call itself fails for one reason or another, none of the database calls will be persisted. The particular call that fails the transaction will be returned in a tuple with the form of {:error, name_of_call, failed_value,changes_that_succeeded}. The failed_value will contain the changeset errors (if using a changeset) or any other error values the call returned. changes_that_succeeded will contain the results of any previous operations that were successful. However, as the docs state, 85 | 86 | any successful operations would have been rolled back 87 | 88 | because the transaction itself failed. 89 | 90 | This is now where we can take a look at the enter_door function. If the transaction fails, we’ve already seen how it will return the {:error, ...} tuple for us to deal with how we’d like. If it succeeds, it will return a tuple with {:ok, map}. In map, we can access the success values of each of the individual operations from the value of what we named that particular operation. So in our example the :entry key in map would correspond with the result of the operation: 91 | 92 | 93 | ```elixir 94 | defmodule Guard do 95 | def enter_door(person, door) do 96 | case entry_transaction(person, door) do 97 | {:ok, %{entry: entry, log: log, person: person}} -> 98 | Logger.debug("Success on all three!") 99 | {:error, :log, _failed_value, _changes_successful} -> 100 | Logger.debug("Failed to save Log") 101 | {:error, :person, _failed_value, _changes_successful} -> 102 | Logger.debug("Failed to save Person") 103 | {:error, :entry, _failed_value, _changes_successful} -> 104 | Logger.debug("Failed to save Entry") 105 | end 106 | end 107 | 108 | def entry_transaction(person, door) do 109 | Multi.new 110 | |> Multi.insert(:entry, Entry.changeset(%Entry{}, %{door_id: door.id, person_id: person.id}}) 111 | |> Multi.update(:person, Person.increase_entry_count_changeset(person)) 112 | |> Multi.insert(:log, Log.changeset(%Log{}, %{text, "entry"})) 113 | |> Repo.transaction() 114 | end 115 | end 116 | ``` 117 | 118 | 119 | 120 | Another thing we can do with Ecto.Multi is utilize the run/3 function to execute arbitrary code within which we will have the result of any previously-run and successful operations. Let’s take a look at an example. 121 | 122 | Pretend we want to ensure that a Person doesn’t enter a Door more than 10 times. Something like that would normally (and probably should still) be done inside a changeset for Person, but in our case, let’s utilize run/3 to check whether our Person now has more than 10 entries: 123 | 124 | Ecto.Multi.run expects to receive a tuple containing either {:ok, message} or {:error,message} in order to determine whether to continue the transaction. 125 | 126 | We can see above that we pattern match on the Person which has the result of successfully running the update operation above it. So if before the operation the Person had 10 entries, after the successful update, it would have 11 and trigger the failure. The error message would be passed on into the pattern-matched failure in the case statement. 127 | 128 | ```elixir 129 | defmodule Guard do 130 | alias Ecto.Multi 131 | 132 | def enter_door(person, door) do 133 | case entry_transaction(person, door) do 134 | {:ok, %{entry: entry, log: log, person: person}} -> 135 | Logger.debug("Success on all four!") 136 | {:error, :allowed, _failed_value, _changes_successful} -> 137 | Logger.debug("Failed to pass allowed check") 138 | # ... other pattern matched failures 139 | end 140 | end 141 | 142 | def entry_transaction(person, door) do 143 | Multi.new 144 | |> Multi.insert(:entry, Entry.changeset(%Entry{}, %{door_id: door.id, person_id: person.id}}) 145 | |> Multi.update(:person, Person.increase_entry_count_changeset(person)) 146 | |> Mutli.run(:allowed, fn(%{person: person}) -> # NEW CODE 147 | if person.entry_count > 10 do # 148 | {:error, "Person entered more than 10 times"} # 149 | else # 150 | {:ok, "continue"} # 151 | end # 152 | end) # 153 | |> Multi.insert(:log, Log.changeset(%Log{}, %{text, "entry"})) 154 | |> Repo.transaction() 155 | end 156 | end 157 | ``` 158 | 159 | 160 | Note that probably, you will be using multi for a inter context communication. 161 | Specially if your context is a process flow. This is something beautiful about 162 | contexts, you can have static and granulated ones, and aftewards, isolate your 163 | flows in new contexts. 164 | 165 | Read the [context](https://hexdocs.pm/phoenix/contexts.html) manual, and see: 166 | Supose you have a CMS and Account granulated contexts, and a REGISTRATION flow, 167 | so instead of using a high coupled query, create a registration context to 168 | handle them: 169 | 170 | 171 | ``` 172 | defmodule Hello.UserRegistration do 173 | alias Ecto.Multi 174 | alias Hello.{Accounts, CMS} 175 | 176 | def register_user(params) do 177 | Multi.new() 178 | |> Multi.run(:user, fn _ -> Accounts.create_user(params) end) 179 | |> Multi.run(:author, fn %{user: user} -> 180 | {:ok, CMS.ensure_author_exists(user)} 181 | end) 182 | |> Repo.transaction() 183 | end 184 | end 185 | 186 | ``` 187 | 188 | 189 | 190 | ## Advanced 191 | 192 | 193 | *Oficial Documentation* 194 | https://hexdocs.pm/ecto/Ecto.Multi.html 195 | 196 | 197 | 198 | *Blogs* 199 | 200 | * http://blog.danielberkompas.com/2016/09/27/ecto-multi-services.html 201 | * https://hexdocs.pm/ecto/Ecto.Multi.html 202 | * https://medium.com/heresy-dev/a-brief-guide-to-ecto-multi-9c8ea0c729f0 203 | * https://blog.danielberkompas.com/2016/09/27/ecto-multi-services/ 204 | -------------------------------------------------------------------------------- /references/postgres/POSTGRES.md: -------------------------------------------------------------------------------- 1 | Postgres 2 | ======== 3 | 4 | 5 | ## Tutorials 6 | 7 | * [chart-io](https://chartio.com/learn/sql/introduction/) - learn interactivelly 8 | all you need to be a SQL ninja. 9 | * [postgres-exercises](https://pgexercises.com) - excelent interactive 10 | excercies. 11 | -------------------------------------------------------------------------------- /references/semiotics/SEMIOTICS.md: -------------------------------------------------------------------------------- 1 | Semiotics 2 | ========= 3 | 4 | Why semiotics here ? 5 | TODO: communication, art, reader, writer, interpretation, 6 | 7 | ## Basics 8 | 9 | 10 | #### Definition 11 | 12 | Defining Semiotics 13 | Have you ever thought about why certain words mean what they do? In an age when 'mouse' can mean a little rodent or a computer device, it's easy to question exactly what words mean and how we make sense of them. This question of meaning is at the heart of semiotics. 14 | 15 | Semiotics is the study of sign systems. It explores how words and other signs make meaning. In semiotics, a sign is anything that stands in for something other than itself. This lesson focuses primarily on linguistic signs. 16 | 17 | The word 'semiotics' dates back to ancient Greece, but its use in modern linguistics was propelled in the 19th century with the research of Ferdinand de Saussure. Saussure was a Swiss linguist who contributed greatly to the study of semiotics, also sometimes referred to as semiology. 18 | 19 | Scholars of modern linguistics understand that words do not have innate meanings. That is, when we say the word 'rabbit', it is not because those sounds or letter symbols have anything to do with the qualities of a small, furry herbivore. In fact, the word, sounds, and letters are all unrelated to the creature we call rabbit, except that humans have assigned a value to them. 20 | 21 | 22 | #### The code 23 | 24 | The code is a fabric woven from signs. It is open and interpretable, but it must be viewed as a coherent whole. It creates its Model Reader, and it is more than the sum of the author's words and the reader's meaning. The code is essentially "a lazy machine that demands the bold cooperation of the reader to fill in a whole series of gaps" of unsaid or already said missing elements (based on translation of Eco, 1985, 29). 25 | 26 | By its coherence, a programming code may reduce the possibility of arriving at certain interpretations. Within the system of linguistic conventions, if someone writes "Mary is eating a ....... right now", we deduce that the missing word will be a noun, and that this noun will certainly not be "truck". The reader – or receiver – must exercise semiotic judgment, meaning that "in order to 'understand' the meaning of a code, especially if it is indirect, the receiver has to apply processes of interpretive cooperation" (based on translation of Eco, 1988, 71). 27 | 28 | 29 | ### Types of programmers 30 | 31 | Remember that when you are coding, you are writing a code for someone else that 32 | is not you. It seems obvious, but 99% of people has a bias to think that the 33 | other is seeing what they are seeing, and it's absolutely not truth. 34 | 35 | **THE EMPIRICAL PROGRAMMER** 36 | 37 | The empirical reader is the "concrete subject of acts of [textual] cooperation"; he "deduces a model image of something that has previously been verified as an act of utterance and which is textually present as an utterance." (translation of Eco, 1985, 80-81). Briefly, he is the one who views the text pragmatically. 38 | 39 | For example, some readers of Foucault's Pendulum, a novel by Umberto Eco, have made a project of tracing the main character's path through the streets of Paris. They actually recognized a bar described in the story; however, the bar was in fact an invention of the author. 40 | 41 | ** THE MODEL PROGRAMMER ** 42 | 43 | Although the code is a cloth woven from signs and gaps, the Model Reader, using his encyclopaedia, has the ability to fill in the gaps to the best of his knowledge, using his social baggage, his encyclopaedia and cultural conventions. The author has in fact foreseen a Model Reader who is able to cooperate in the code's actualisation in a specific manner, and who is also "able to deal interpretively with the code in the same way as the author deals generatively [in producing the code, that is]" (Eco, 1979, 7). 44 | 45 | This Model Reader is created by the code, but he is not the one who has the only right interpretation. A code may foresee a Model Reader who is capable of trying out several interpretations where he is confronted with several fabula or possible worlds. The Model Reader, in essence, is "a textually established set of felicity conditions [...] to be met in order to have a macro-speech act (such as a code is) fully actualized" (Eco, 1979, 11). The Model Reader actualises the meaning of everything that the textual strategy intends to say. 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ## Advanced 56 | 57 | * [what-is-semiotics](https://study.com/academy/lesson/what-is-semiotics-definition-examples.html) 58 | - definition in details 59 | * [textual-coperation](http://www.signosemio.com/eco/textual-cooperation.asp) - Umberto Eco's theory of textual cooperation gives the reader an essential role in the process of making meaning 60 | 61 | -------------------------------------------------------------------------------- /references/typing/TYPING.md: -------------------------------------------------------------------------------- 1 | Typing 2 | ====== 3 | 4 | ## Basics 5 | 6 | All the theoretical knowledge to learn typing is to know which finger goes to 7 | each key. The rest is training and dicipline to keep the right finger over the 8 | right key. 9 | 10 | 11 | ![](typing-fingers.png) 12 | 13 | ## Advanced 14 | 15 | * [typing-cat](http://thetypingcat.com) - seems to be the best option. 16 | 17 | * [keybr](https://www.keybr.com) - good exercises but without a clear 18 | methodology. 19 | 20 | * [typing-club](https://www.typingclub.com) - this is a more formal course, with 21 | videos, etc... good for children. 22 | 23 | -------------------------------------------------------------------------------- /references/typing/typing-fingers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henry-hz/elixir-in-a-team/92c00818ae96ff2889427b914769b521915754ad/references/typing/typing-fingers.png --------------------------------------------------------------------------------