Open nelsonic opened 7 years ago
https://elixir-lang.org/getting-started/basic-operators.html
Elixir sorting order (data types):
number < atom < reference < function < port < pid < tuple < map < list < bitstring
https://elixir-lang.org/getting-started/pattern-matching.html
https://elixir-lang.org/getting-started/pattern-matching.html#pattern-matching-1
https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator
No function calls on the left-hand side of a pattern matching statement:
https://elixir-lang.org/getting-started/case-cond-and-if.html https://elixir-lang.org/getting-started/case-cond-and-if.html#cond
https://elixir-lang.org/getting-started/case-cond-and-if.html#if-and-unless
https://elixir-lang.org/getting-started/case-cond-and-if.html#doend-blocks
https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html
https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#binaries-and-bitstrings
https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#char-lists
https://elixir-lang.org/getting-started/keywords-and-maps.html
https://elixir-lang.org/getting-started/keywords-and-maps.html#maps Map update syntax only for existing keys:
Accessing (atom) keys using dot notation:
https://elixir-lang.org/getting-started/keywords-and-maps.html#nested-data-structures
nested map put_in
update syntax:
https://elixir-lang.org/getting-started/modules-and-functions.html
https://elixir-lang.org/getting-started/modules-and-functions.html#compilation
https://elixir-lang.org/getting-started/modules-and-functions.html#scripted-mode
https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
Using do:
syntax:
You may use do: for one-liners but always use do/end for functions spanning multiple lines.
https://elixir-lang.org/getting-started/modules-and-functions.html#function-capturing
https://elixir-lang.org/getting-started/modules-and-functions.html#default-arguments
Any expression is allowed to serve as a default value, but it wonβt be evaluated during the function definition. Every time the function is invoked and any of its default values have to be used, the expression for that default value will be evaluated:
Concat with various clauses:
Second Clause unreachable because first clause Always matches:
https://elixir-lang.org/getting-started/recursion.html https://elixir-lang.org/getting-started/recursion.html#loops-through-recursion
https://elixir-lang.org/getting-started/recursion.html#reduce-and-map-algorithms
Inline each clause:
Math.double_each
:
Enum.map
& Enum.reduce
:
https://elixir-lang.org/getting-started/enumerables-and-streams.html
https://elixir-lang.org/getting-started/enumerables-and-streams.html#enumerables
https://elixir-lang.org/getting-started/enumerables-and-streams.html#eager-vs-lazy
https://elixir-lang.org/getting-started/enumerables-and-streams.html#the-pipe-operator
https://elixir-lang.org/getting-started/enumerables-and-streams.html#streams "Streams are lazy, composable enumerables."
More stream goodness:
https://elixir-lang.org/getting-started/processes.html
https://elixir-lang.org/getting-started/processes.html#spawn
https://elixir-lang.org/getting-started/processes.html#send-and-receive
https://elixir-lang.org/getting-started/processes.html#links
https://elixir-lang.org/getting-started/processes.html#tasks
https://elixir-lang.org/getting-started/processes.html#state
Using Agent
for maintaining state:
https://elixir-lang.org/getting-started/io-and-the-file-system.html
https://elixir-lang.org/getting-started/io-and-the-file-system.html#the-io-module
https://elixir-lang.org/getting-started/io-and-the-file-system.html#the-file-module
https://elixir-lang.org/getting-started/io-and-the-file-system.html#the-path-module
https://elixir-lang.org/getting-started/io-and-the-file-system.html#processes-and-group-leaders
Of all IO devices, there is one that is special to each process: the group leader ...
https://elixir-lang.org/getting-started/io-and-the-file-system.html#iodata-and-chardata
https://elixir-lang.org/getting-started/alias-require-and-import.html
https://elixir-lang.org/getting-started/alias-require-and-import.html#alias
https://elixir-lang.org/getting-started/alias-require-and-import.html#require
https://elixir-lang.org/getting-started/alias-require-and-import.html#import
https://elixir-lang.org/getting-started/alias-require-and-import.html#use
https://elixir-lang.org/getting-started/alias-require-and-import.html#understanding-aliases
https://elixir-lang.org/getting-started/alias-require-and-import.html#module-nesting
https://elixir-lang.org/getting-started/alias-require-and-import.html#multi-aliasimportrequireuse
https://elixir-lang.org/getting-started/module-attributes.html
https://elixir-lang.org/getting-started/module-attributes.html#as-annotations
https://elixir-lang.org/getting-started/module-attributes.html#as-constants
https://elixir-lang.org/getting-started/module-attributes.html#as-temporary-storage
https://elixir-lang.org/getting-started/structs.html
https://elixir-lang.org/getting-started/structs.html#defining-structs
https://elixir-lang.org/getting-started/structs.html#accessing-and-updating-structs
https://elixir-lang.org/getting-started/structs.html#structs-are-bare-maps-underneath
https://elixir-lang.org/getting-started/structs.html#default-values-and-required-keys
https://elixir-lang.org/getting-started/protocols.html
Passing a data type that doesnβt implement the protocol raises an error:
https://elixir-lang.org/getting-started/protocols.html#protocols-and-structs
Implement Size.size
for a Struct:
https://elixir-lang.org/getting-started/protocols.html#implementing-any
Note: we set it to 0 (zero) in the implementation...)
https://elixir-lang.org/getting-started/protocols.html#fallback-to-any
https://elixir-lang.org/getting-started/protocols.html#built-in-protocols
https://elixir-lang.org/getting-started/protocols.html#protocol-consolidation
https://elixir-lang.org/getting-started/comprehensions.html
https://elixir-lang.org/getting-started/comprehensions.html#generators-and-filters
Show size of each image in "Pictures" directory:
dirs = ['/Users/Admin/Pictures']
for dir <- dirs,
file <- File.ls!(dir),
path = Path.join(dir, file),
File.regular?(path) do
IO.puts File.stat!(path).size
end
Simplification:
https://elixir-lang.org/getting-started/comprehensions.html#bitstring-generators
https://elixir-lang.org/getting-started/comprehensions.html#the-into-option
https://elixir-lang.org/getting-started/sigils.html
https://elixir-lang.org/getting-started/sigils.html#regular-expressions
https://elixir-lang.org/getting-started/sigils.html#strings-char-lists-and-word-lists-sigils
https://elixir-lang.org/getting-started/sigils.html#interpolation-and-escaping-in-sigils
Heredoc sigils:
https://elixir-lang.org/getting-started/sigils.html#custom-sigils
https://elixir-lang.org/getting-started/try-catch-and-rescue.html
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#errors
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#throws
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#exits
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#after
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#else
https://elixir-lang.org/getting-started/try-catch-and-rescue.html#variables-scope
https://elixir-lang.org/getting-started/typespecs-and-behaviours.html
https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#defining-custom-types
custom @type
is available outside the module definition, to make it private use @typep
:
https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#behaviours
https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#defining-behaviours
https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#adopting-behaviours
Ok. I get this. I'll need to use it in a project to remember it tho... π
https://elixir-lang.org/getting-started/erlang-libraries.html
https://elixir-lang.org/getting-started/erlang-libraries.html#the-binary-module
https://elixir-lang.org/getting-started/erlang-libraries.html#formatted-text-output
https://elixir-lang.org/getting-started/erlang-libraries.html#the-crypto-module
https://elixir-lang.org/getting-started/erlang-libraries.html#the-digraph-module
https://elixir-lang.org/getting-started/erlang-libraries.html#erlang-term-storage
https://elixir-lang.org/getting-started/erlang-libraries.html#the-math-module
https://elixir-lang.org/getting-started/erlang-libraries.html#the-queue-module
https://elixir-lang.org/getting-started/erlang-libraries.html#the-rand-module
https://elixir-lang.org/getting-started/erlang-libraries.html#the-zip-and-zlib-modules
didn't work, but when I zipped the contents of the CWD as file.zip
:
zip -r ../file.zip ./*
got: and finally:
https://elixir-lang.org/crash-course.html
https://elixir-lang.org/crash-course.html#running-code
https://elixir-lang.org/crash-course.html#elixir
https://elixir-lang.org/crash-course.html#notable-differences
https://elixir-lang.org/crash-course.html#variable-names
https://elixir-lang.org/crash-course.html#calling-functions
https://elixir-lang.org/crash-course.html#data-types
https://elixir-lang.org/crash-course.html#tuples
https://elixir-lang.org/crash-course.html#lists-and-binaries
https://elixir-lang.org/crash-course.html#keyword-list
https://elixir-lang.org/crash-course.html#maps
https://elixir-lang.org/crash-course.html#regular-expressions
https://elixir-lang.org/crash-course.html#modules
https://elixir-lang.org/crash-course.html#function-syntax
https://elixir-lang.org/crash-course.html#identifying-functions
https://elixir-lang.org/crash-course.html#default-values
https://elixir-lang.org/crash-course.html#anonymous-functions
I prefer the Erlang syntax for anon functions...
https://elixir-lang.org/crash-course.html#first-class-functions
https://elixir-lang.org/crash-course.html#partials-and-function-captures-in-elixir
https://elixir-lang.org/crash-course.html#control-flow
https://elixir-lang.org/crash-course.html#if
https://elixir-lang.org/crash-course.html#sending-and-receiving-messages
https://elixir-lang.org/crash-course.html#adding-elixir-to-existing-erlang-programs
Done. β
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#our-first-project
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#project-compilation
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#running-tests
(deliberately) Make tests fail:
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#environments
https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#exploring
https://elixir-lang.org/getting-started/mix-otp/agent.html
https://elixir-lang.org/getting-started/mix-otp/agent.html#the-trouble-with-state
https://elixir-lang.org/getting-started/mix-otp/agent.html#agents
Tests first:
Test fails (functionality not yet implemented):
Tests pass:
https://elixir-lang.org/getting-started/mix-otp/agent.html#test-setup-with-exunit-callbacks
using test context with %{bucket: bucket}
https://elixir-lang.org/getting-started/mix-otp/agent.html#other-agent-actions
Delete key TEST First:
delete
implemented (test passing):
https://elixir-lang.org/getting-started/mix-otp/agent.html#clientserver-in-agents
(just info. no code to write...)
https://elixir-lang.org/getting-started/mix-otp/genserver.html
Never convert user input into atoms as this can exhaust system memory ... π
https://elixir-lang.org/getting-started/mix-otp/genserver.html#our-first-genserver
https://elixir-lang.org/getting-started/mix-otp/genserver.html#testing-a-genserver
https://elixir-lang.org/getting-started/mix-otp/genserver.html#the-need-for-monitoring
https://elixir-lang.org/getting-started/mix-otp/genserver.html#call-cast-or-info Just reading. no code.
https://elixir-lang.org/getting-started/mix-otp/genserver.html#monitors-or-links
Onto the next one. π
https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html
https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#our-first-supervisor
https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#naming-processes
Failing Test:
BEFORE:
def handle_cast({:create, name}, {names, refs}) do
if Map.has_key?(names, name) do
{:noreply, {names, refs}}
else
{:ok, pid} = KV.Bucket.start_link([])
ref = Process.monitor(pid)
refs = Map.put(refs, ref, name)
names = Map.put(names, name, pid)
{:noreply, {names, refs}}
end
end
AFTER:
def handle_cast({:create, name}, {names, refs}) do
if Map.has_key?(names, name) do
{:noreply, {names, refs}}
else
{:ok, pid} = KV.BucketSupervisor.start_bucket()
ref = Process.monitor(pid)
refs = Map.put(refs, ref, name)
names = Map.put(names, name, pid)
{:noreply, {names, refs}}
end
end
Tests pass:
Temporary:
Test:
https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#supervision-trees
https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#observer
Applications:
create "shopping":
So far so good ... π
https://elixir-lang.org/getting-started/mix-otp/ets.html#ets-as-a-cache
Run tests with --trace
:
(passing intermittently!!)
One failure:
Add "bogus" bucket: Tests pass as expected.
Conclusion: Use "Registry" module:
https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html
got:
changed the deps to:
defp deps do
[
{:plug, "~> 1.0"}
]
end
See: https://github.com/elixir-lang/elixir-lang.github.com/issues/1019
run the tests in the umbrella project:
https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html#summing-up
https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html
https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html#echo-server
explanation of code ...
Telnet session:
https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html#tasks
apps/kv_server/lib/kv_server/application.ex
BEFORE
defmodule KVServer.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: KVServer.Worker.start_link(arg)
# {KVServer.Worker, arg},
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: KVServer.Supervisor]
Supervisor.start_link(children, opts)
end
end
AFTER:
defmodule KVServer.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
{Task, fn -> KVServer.accept(4040) end}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: KVServer.Supervisor]
Supervisor.start_link(children, opts)
end
end
Dual Telnet Connections:
Doesn't work because we need a new
process to respond ...
https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html#task-supervisor
:ok = :gen_tcp.controlling_process(client, pid)
run: PORT=4040 mix run --no-halt
https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html
https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html#doctests
Failing test:
Make it pass:
https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html#with
Start server, connect using Telnet and issue commands:
Re-write serve
to use with
:
BEFORE:
defp serve(socket) do
msg =
case read_line(socket) do
{:ok, data} ->
case KVServer.Command.parse(data) do
{:ok, command} ->
KVServer.Command.run(command)
{:error, _} = err ->
err
end
{:error, _} = err ->
err
end
write_line(socket, msg)
serve(socket)
end
AFTER:
defp serve(socket) do
msg =
with {:ok, data} <- read_line(socket),
{:ok, command} <- KVServer.Command.parse(data),
do: KVServer.Command.run(command)
write_line(socket, msg)
serve(socket)
end
https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html#running-commands
Implement integration tests:
This tutorial ends with failing tests ... π
https://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html
Got error:
Protocol 'inet_tcp': invalid node name: foo@nelson@local
I suspect it has something to do with the two @
symbols ... π
When I tried:
iex --sname foo@localhost
It appears to work:
Second instance of iex:
Spawn process in original instance of iex:
Ping the process:
We are going to use Tasks:
https://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html#asyncawait
iex --sname foo@computer-name -S mix
iex --sname bar@computer-name -S mix
Fails ...
Tests still fail after following the instructions:
However the ==> kv_server Excluding tags: [distributed: true]
is working:
At this point nothing is going to work ...
I think I'm going to have to take a few steps back ...
https://elixir-lang.org/getting-started/introduction.html
https://elixir-lang.org/getting-started/introduction.html#interactive-mode
https://elixir-lang.org/getting-started/introduction.html#running-scripts
https://elixir-lang.org/getting-started/basic-types.html
https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic
https://elixir-lang.org/getting-started/basic-types.html#booleans
https://elixir-lang.org/getting-started/basic-types.html#atoms Took a detour to read: https://www.quora.com/What-are-atoms-in-Erlang-programming-language but got sucked into reading: https://www.quora.com/What-is-something-that-needs-to-be-said (don't fall into the same trap! you will waste a whole pomodoro before you realise it...!)
https://elixir-lang.org/getting-started/basic-types.html#strings
byte_size
vs.String.length
:https://elixir-lang.org/getting-started/basic-types.html#anonymous-functions
https://elixir-lang.org/getting-started/basic-types.html#linked-lists
https://elixir-lang.org/getting-started/basic-types.html#tuples
https://elixir-lang.org/getting-started/basic-types.html#lists-or-tuples
Tuples as return values from built-in functions:
good insight at the end of this "chapter" about size vs length operations. size (_e.g:
byte_size/1
andtuple_size/1
are pre-computed values and are thus "cheap" operations_) whereas length has a linear (proportional to the length of the list/string etc.) time becauseString.length/1
needs to "traverse" the entire string to determine it's length. π