Phoenix in Action v4
Phoenix in Action v4
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
welcome
Thank you for purchasing the MEAP for Phoenix in Action. Elixir and Phoenix have
had an exciting past few years as the Elixir language and the Phoenix framework have
both started to take off and become legitimate players in the web development world.
There are now many companies that have staked their future business on the fact that
Phoenix sites built on Elixir are the future of web development.
The incredible processing speed of Elixir combined with its built-in support for
parallel and distributed programming (not to mention its fault-tolerance) really shine
when paired with the Phoenix framework. With Phoenix, web development with Elixir
is quick and enjoyable with speed of development and developer happiness major
selling points.
While Phoenix in Action will not teach you everything there is to know about
Elixir, it does have a chapter introducing you to (or refreshing you on) the basics of the
Elixir language. As more complex subjects come up during the book, there will likely
be a sidebar describing the new Elixir concept and definitely footnotes directing you to
where you can find more or deeper information regarding those features if you so
choose.
The flow of the chapters of Phoenix in Action will have you building a real-time
auction website step-by-step, first starting with business logic completely separate
from Phoenix and slowly adding more and more complex features and introducing
Phoenix-specific functionality along the way.
While Elixir and Phoenix are certainly production-ready, they are still relative
newcomers in the world of application development. Because of that, there are still
discussions around best practices and the most appropriate way to structure
applications and even where to introduce functionality. One of the best things about
Phoenix is that it stays out of your way, keeping most of its opinions to itself. But the
other side of that coin is that there can be many competing opinions. This book will
have its own opinion regarding how to structure our application, but there are other
ways to do it that are no less correct. But Phoenix in Action will encourage you to
make Phoenix only a single boundary to your real underlying application logic.
I'm excited about Elixir and Phoenix and what it will mean for the future of
application development, specifically for the web. I hope that you join me in this
excitement and provide feedback and comments during the development of Phoenix in
Action. Your involvement will be essential while I continue writing this book, so
please don't be shy in your interactions with me.
Many thanks,
—Geoffrey Lessel
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
brief contents
PART 1: AN INTRODUCTION
1 Ride the Phoenix
2 Intro to Elixir
3 A Little Phoenix Overview
PART 2: HE BASICS
4 Phoenix is Not Your Application—Just a Boundary
5 Elixir Application Structure
6 Bring in Phoenix
7 Being Persistent with a Database
8 Changes Are Coming
9 Transforming Data In Your Browser
10 Plugs, Assigns, and Dealing With Users
11 Accepting Auction Bids and Associating Records
PART 3: GETTING ADVANCED
12 Building an API
13 Real-time updates with channels
14 Testing
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
1
In this chapter, you will be introduced to the Elixir programming language and the
Phoenix web framework. We will be taking a look at the main advantages and
disadvantages a language and framework like Elixir and Phoenix provides. By the
end of the chapter, you’ll have a good overview of the technologies, why they
exist, and you’ll be ready to jump into learning the details and creating your own
web applications.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
2
a user’s interaction with the web site. Figure 1 shows how much more complex a
dynamic website may be over a static one.
Figure 1.1. Static websites vs. dynamic websites that frameworks help provide.
Arguably the biggest impact on the open-source web in the 2000’s was the release
and adoption of Ruby on Rails—a web framework written on the Ruby
programming language. This allowed developers to quickly and somewhat easily
get a dynamic web site up and running in very little time. The famous example
from that time was creating a blogging engine in 15 minutes. It allowed developers
great productivity and "Web 2.0" exploded.
From Ruby on Rails, many new web frameworks attempted to match or outdo
Ruby on Rails in terms of features and developer happiness. There became a go-to
web framework for each major programming language. In that vein Phoenix was
born.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
3
Language Framework
Elixir Phoenix
Ruby Ruby on Rails, Sinatra
Python Flask, Django
PHP Laravel, Symfony
Java Spring, Ninja
Javascript Meteor, Express, Sails
C# Nancy, ASP.NET Boilerplate
Perl Mojo, Catalyst
could be with a chat application where users can send and receive chat messages to
thousands of other users and you need to keep each client up-to-date when another
client’s user submits a new message. It could be something more complex like an
auction site that wants to provide users visiting the auction item’s page up-to-the
second information regarding the state of the bids. Maybe it’s a workplace
collaboration site where users are sharing files and even working on the same file
simultaneously. Or perhaps there is a real-time multiplayer game server that you
want to build and you need to ensure that all players have the same information at
the same time. There are many different situations in which real-time channels are
necessary, and many more in which it would be beneficial. Figure 7 illustrates a
simple situation in which bandwidth can be saved and the user experience
improved with pushing data to the user instead of requiring the user to pull the data
from the server.
Figure 1.2. Traditional "pull" refreshed require the user to initiate the request and the server to
return the entire web page again.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
5
Figure 1.3. A "Push" requests originates from the server and "pushes" new information to the
user which is typically only that which changed, greatly reducing payload and speeding up page
rendering.
Elixir can spawn and efficiently handle hundreds of thousands of processes without
blinking an eye (we go into a bit more detail in the section about Elixir below).
You’ve also read briefly about Phoenix’s concept of channels which allow real-
time communication. Phoenix actually spawns each channel into its own process so
that no one process can damage or take down any of the others — they are
beautifully isolated.
Using these processes and a channels, Phoenix can handle a couple hundred
thousand connections on a single server! Granted, kudos to you if your web
application ever has hundreds of thousands of simultaneous users wanting to
communicate in real time—but that gives you an idea of the power of Phoenix and
Channels running within processes.
We’ll get more into Channels (and build some into our application) in later
chapters.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
6
If your application needs to do any more than a small amount of computation in the
background (normally asynchronously), then it is a perfect fit for something built
on top of Elixir like Phoenix. It is trivially easy to kick off background,
asynchronous processes with Elixir.
This fits in perfectly with the real-time communication features of Phoenix. Once
the long-running process is complete, the server could send a message through the
Channel to the user’s client that the process is complete and either display the
results directly on the client or provide a link or further instructions to retrieve the
results at a later time.
1.2.3 Low Hardware Requirements / Low-cost Scaling
If you look at any hosting provider’s offerings, you’ll notice that most of the time,
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
7
adding more CPU power/cores costs less than adding more RAM. This fits
perfectly with the way Elixir works. Elixir automatically utilizes all the different
CPU cores available to it and takes up a relatively very small amount of RAM in
order to run. Compare this to a web framework and language like Ruby on Rails
which is RAM hungry.
Other web frameworks built on differing programming languages CAN scale, but if
you are looking for a low-cost way to scale quickly, then Elixir and Phoenix would
be a good choice. Pinterest explains in a blog post 1 that they moved one of their
systems from Java to Elixir.
So, we like Elixir and have seen some pretty big wins with it. The system that
manages rate limits for both the Pinterest API and Ads API is built in Elixir. Its 50
percent response time is around 500 microseconds with a 90 percent response time
of 800 microseconds. Yes, microseconds.
We’ve also seen an improvement in code clarity. We’re converting our
notifications system from Java to Elixir. The Java version used an Actor system
and weighed in at around 10,000 lines of code. The new Elixir system has shrunk
this to around 1000 lines. The Elixir based system is also faster and more
consistent than the Java one and runs on half the number of servers.
1.2.4 It’s Not All Roses
While the Elixir language and the Phoenix framework are awesome and are usually
what I reach for when starting new projects, it is not always the de-facto best
choice for the job. There are some areas in which Elixir doesn’t do as good a job as
a different alternative.
• Time to Productivity: If you are already productive in a different web framework
using a different programming language, it may be hard to justify the cost of
getting up to speed in something new like Elixir. If you are just getting started in
development, one consideration is how long it will take you to become productive
in your new chosen language. Some people I’ve spoken to believe it is harder to
learn functional programming than Object-oriented programming while others
swear it’s the other way around. Regardless, I believe that Elixir and Phoenix offer
enough reasons for you to take the plunge and learn the language regardless of
your past experience levels.
• Numbers: Elixir isn’t a number-crunching powerhouse. It will never match the
pure computational speed of something lower level like C or C++. I’d even go so
far to say that if you are looking to primarily crunch large numbers, even Python is
a better choice. Elixir can do it and do it well, but it won’t win in a head-to-head
competition in this area.
• Community: While the community behind Elixir, Phoenix, and Erlang is very
helpful and enthusiastic, it is not as large as a community for something like Ruby,
1
medium.com/@Pinterest_Engineering/introducing-new-open-source-tools-for-the-elixir-community-2f7bb0bb7d8c
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
8
Python, Java, or even PHP. Those languages have a deeper and longer history in
the web application world and it might be easier to find help with your project in
one of those worlds. However, more and more meetups, conferences, blogs, jobs,
etc. are arriving all the time for Elixir and I believe the future will only hold great
things for the Elixir community.
• Packages: Going along with the above comment regarding the size and age of the
community surrounding Elixir versus other languages, the amount of open-source
packages available to use in your project is smaller. As of the time of this writing,
there are currently just under 4,500 packages available on hex.pm, the package
manager source for Elixir. Contrast that to rubygems which has over 8,500
packages and PyPI (the Python Package Index) which has over 109,000. While it
may be harder to find something that does exactly what you used in previously
programming languages, more packages are being built all the time. It also means
there is space for YOU to create a new helpful package and help grow the
community.
• Deploying: In the original draft of the table of contents of this book, a chapter
regarding deploying your application was discussed. However, this is an area that
is still, in all honesty, in need of a single, "best" solution. Deploying an Elixir
application (including ones that utilize the Phoenix web framework) is pretty
tricky and involves multiple steps. Beyond that, those steps are still not quite
clearly defined nor have mature tools to help in the process. We’ll provide
resources later on in the book that will give you some guidance on how to learn
about deploying your application, but it isn’t exactly easy at the moment.
Phoenix provides a lot of things that help you add normally-complex features to
your web applications but it will not be the foundation of your application. While it
may be strange to read that in a book specifically about Phoenix, the truth is that
Phoenix derives its powers from the amazing Elixir programming language.
Elixir leverages the Erlang VM, known for running low-latency, distributed and
fault-tolerant systems, while also being successfully used in web development
and the embedded software domain.
-- http://elixir-lang.org
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
9
real-time messaging applications, the engineering team at the time of acquisition was made
up of only 35 people. Your application may not ever have 450 million users sending 70 million
messages per second (well over that these days), why not utilize the technology that would
allow you to easily do so if required?
1.3.1 Scalability
One of the amazing things about Elixir is its scalability. Elixir has the idea of
processes running on your machine. These aren’t processes like you may have
thought of them in the past — they aren’t threads. Elixir processes are extremely
light-weight (the default initial size is 233 words or 0.5kb) and run independent of
all the other running processes. We will be building a Phoenix application through
the course of this book and it will not be surprising to find that thousands of
concurrent processes will be running on your machine in order to allow our
application to run. These processes are FAST, independent of each other, have
unshared memory, and can easily send and receive messages to other processes on
distributed nodes (different machines potentially in different parts of the world).
1.3.2 Supervision trees
Elixir applications (including Phoenix apps and our own application) have
Supervisors and Workers. The Supervisors monitor the Workers and ensure that if
they go down for one reason or another, they are started right back up. This
provides an amazing backbone of fault-tolerance without much work on our part.
We can configure how we’d like our Supervisors to handle their Workers if
something does happen to them including shutting down and restarting any sibling
workers. Beyond that, Supervisors can supervise other Supervisors! The
supervision tree can get pretty large and complex but this complexity does not
necessarily mean our application is harder to comprehend.
Figure 1.5. Supervisors and workers
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
10
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
11
Figure 1.7. The Process tree can continue to grow as your application runs.
Figure 1.8. Soon your application can spawn thousands of Workers, each isolated from the
other.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
12
The benefits of these Processes and Supervisors are many, but one in particular is
that it makes created distributed systems much easier than previously possible with
other languages. You can deploy your code to multiple servers in multiple areas of
the world (or in the same room) and have them communicate with each other. Need
more power? You can bring up another server node and your previous nodes can
communicate with the new node with minimal configuration, passing off some of
the required work as it sees fit.
1.3.3 Erlang VM
These particular features are available to us because the backbone of Elixir is
Erlang. Erlang has been around for decades now, silently and dependently
providing services to many of the things you use on a daily basis. Erlang was
created with the telecommunications industry in mind. And what does a
telecommunication service require? High fault tolerance, distributability, live-
updating of software — things that a modern-day web application should also be
able to rely on. Elixir runs on top of the Erlang VM which is rock-solid and has
been for decades. You can even run Erlang functions and use Erlang modules
directly in your Elixir applications! While this book will not go into the details of
Erlang and its VM, there are plenty of resources available that cover Erlang.
1.3.4 Macro and metaprogramming support
While Elixir runs on the Erlang VM, it is itself written in Elixir. How is that
possible? It is another one of the great things about the Elixir language — macros.
Elixir is extensible via built-in support for macros which allow anyone to define
their own language features. You can build your own little language or DSL within
Elixir itself. As Figure 5 illustrates, 89% of the Elixir codebase is itself Elixir—the
rest is made up of Erlang and various shell files.
Figure 1.9. Breakdown of language types in Elixir’s source code (as of June 2017)
1.3.5 OTP
Finally, one feature of Elixir that cannot go unwritten is its ability to utilize OTP.
OTP stands for "Open Telecom Platform" but that name isn’t very relevant
anymore so you’ll almost always just see OTP. These are a set of functions,
modules, and standards that make using things such as Supervisors and Workers,
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
13
and the fault-tolerance that goes along with that possible. There are concepts such
as GenServer, GenStage, concurrency, distributed computation, load balancing,
and worker pools to just name a few. It’s been light-heartedly said that any
complex problem you are attempting to solve has likely already been solved in
OTP.
If half of Erlang’s greatness comes from its concurrency and distribution and
the other half comes from its error handling capabilities, then the OTP
framework is the third half of it.
-- http://learnyousomeerlang.com/what-is-otp
So with all that greatness backing Phoenix, what if you don’t know Elixir? That’s
OK. Chapter 2 will take you through the basics of the Elixir language and, if you
want to go even deeper, recommend some resources you can check out.
Don’t worry too much about the syntax — we’ll cover that later. This may not be
the best example of using the features of the language to their fullest potential, but
it will give you an idea of the code syntax and structure.
defmodule RaceCar do
defstruct [:tires, :power, :acceleration, :speed]
ferrari_tires = [
%Tire{location: :front_right, kind: :racing},
%Tire{location: :front_left, kind: :racing},
%Tire{location: :back_right, kind: :racing},
%Tire{location: :back_left, kind: :racing}
]
ferrari = %RaceCar{tires: ferrari_tires,
power: %Engine{model: "FR223"},
acceleration: 60,
speed: 0}
ferrari.speed
# => 0
RaceCar.accelerate(ferrari)
# => 60
ferrari.speed
# => 0
new_ferrari = RaceCar.accelerate(ferrari)
new_ferrari.speed
# => 60
ferrari.speed
# => 0
Look carefully — our ferrari variable does not get changed when we pass it to
the RaceCar.accelerate/1 2 function. We can run that line 1,000 times and we’d
get the same return value every time: an updated structure of the ferrari with a
new speed. But remember, our original ferrari doesn’t change in memory. We
have to capture that return value in order to use it later. What this kind of
programming provides is an elimination of side effects. We can run our function at
any time and be confident that it will always return the same value for the same
input — regardless of time of day, "global state", what order functions were called
in, etc.
2
This syntax means the accelerate function inside the RaceCar module that has argument arity (the number of function
arguments) of 1
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
15
Eliminating side effects, i.e. changes in state that do not depend on the function
inputs, can make it much easier to understand and predict the behavior of a
program, which is one of the key motivations for the development of functional
programming.
-- https://en.wikipedia.org/wiki/Functional_programming#Coding_styles
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
16
Figure 1.11. Functional programming generally involves having a module that contains
functions that act together for a purpose. The module holds no data (state) but only returns the
result of the function based on the data the user gives it.
1.5 Summary
In this chapter you learned:
• Dynamic websites can be a tricky problem to solve. Web frameworks cam along
to help make the solution easy and quick to implement. Phoenix delivers on this
promise and can get your dynamic (or static) website online with minimal fuss.
• Much of the power of Phoenix comes from the Elixir langauge. Elixir runs on the
Erlang virtual machine (VM) so you could say that much of the power of Elixir
comes from the Erlang VM.
• There are strong benefits to writing a web application with Phoenix over other
alternatives including fault-tolerance, speed, concurrency, real-time
communication, distribution, and potential hardware cost savings.
• A few key differences between object-oriented programming in languages such as
Ruby vs functional programming in a language like Elixir. While OO languages
try to model the domain in real-world object terms that abstract away the code
interface from the data that object holds, functional programs treat the data as
king. All the code of a program is built around explicitly manipulating the data in
an immutable way by passing the result of one function to the input of another.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
17
• Some of the areas in which Elixir/Phoenix may not be the best choice for your
particular application — like requirements for pure processing speed, number
crunching, huge communities, lots of readily-available packages, considering the
time it will take to become productive, and the current difficulty of deployment.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
18
Now that you’ve read a bit about the benefits Phoenix can bring to your web
application development, you’ll need to know Elixir to use it. Phoenix is a web
"framework" that is built on top of the Elixir language (which, as you’ve seen in
previous chapters, is itself built on the Erlang VM known as BEAM). So what if
you don’t know Elixir?
In this chapter you will get up to speed on Elixir so that you can start learning the
language and how to write in it. If you already know Elixir, it is likely you can skip
this chapter entirely and move on. If you don’t know Elixir or need a refresher, this
chapter will cover just enough Elixir to get you going. I’ll point you to more
resources at the end of the chapter if you’d like to dive deeper into the language
and all its features.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
19
Figure 2.1. A REPL takes user input, evaluates it, prints it out back to the user, and loops back
around to take user input again
IEx is basically going to act as our Elixir playground as we are getting started. But
don’t let that fool you — it is actually a pretty powerful tool even for the most
advanced Elixir users. Let’s fire it up and see what we get.
In order to start a new session, open up your terminal program and
type iex (or iex.bat in Windows). You’ll see something like:
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe]
[kernel-poll:false] [dtrace]
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
Note that there is a lot of debugging information displayed in the above and most
of those numbers and terms won’t matter to us during our usage of IEx. In fact,
some of it may look a little bit different on your computer. If it does, don’t worry
about it too much. The two most important things to take a look at are the
Erlang/OPT version (19 in my case) and the Elixir version (1.4.2) you are running.
If you ever are in need of assistance in online discussions, those two version
numbers will likely be important to help those who are assisting you.
Another thing to notice is that it provides you with two hints:
1. press Ctrl+C to exit
2. type h() ENTER for help
These are both pretty self-explanatory, but when the time comes to close
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
20
your iex session, press Ctrl+C on your keyboard. Once you do, you’ll see this:
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
For now, don’t worry about those options other than (c)ontinue. If you
pressed Ctrl+C by accident or now regret your decision, you can press c to continue
your iex session. If you would indeed like to quit and move on to other things, just
press Ctrl+C a second time or a. Those will dump you back out into your terminal.
The second thing to notice is the h() for help. You can follow along with Listing
2.1 by tying h() in your IEx session now.
iex(1)> h()
IEx.Helpers
Welcome to Interactive Elixir. You are currently seeing the documentation for
the module IEx.Helpers which provides many helpers to make Elixir's shell more
joyful to work with.
This message was triggered by invoking the helper h(), usually referred to as
h/0 (since it expects 0 arguments).
You can use the h/1 function to invoke the documentation for any Elixir module
or function:
iex> h Enum
iex> h Enum.map
iex> h Enum.reverse/1
You can also use the i/1 function to introspect any value you have in the
shell:
iex> i "hello"
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
21
Help for all of those functions can be consulted directly from the command line
using the h/1 helper itself. Try:
iex> h(v/0)
Whoa, that’s quite a bit of text that was just spit out! But don’t get overwhelmed —
all of it is very helpful. One of the great things about Elixir is its idea of
documentation being a first-class citizen along with your code. We’ll get into code
documentation in later chapters, but as a quick example, if you typed h(clear/0), it
would give you documentation on the clear/0 function. That documentation is
written just above the function declaration in the Elixir source code. The source
code itself has this documentation alongside the code that makes it run. Figure 2 is
a screen shot from the source code listing for clear/0. You can see that the lines
dedicated to documentation closely matches the lines of code for the function. This
is an example of a function with light documentation and the documentation is at
times extensive, sometimes covering dozens of lines with multiple built-in
examples. This makes reading Elixir source code files really easy. It also makes
discovering how to use new modules and functions really easy!
Figure 2.2. The source code AND built-in, in-line documentation for clear/0
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
22
Type Example(s)
Integer 34
Float 387.936
Boolean true / false
String "Phoenix in Action"
Charlist 'Phoenix in Action'
Atom :phoenix_in_action
List [34, 387.936, false]
Tuple {34, 387.936, false}
Map %{name: "Geoffrey", location: "Oceanside, CA"}
Since these are basic types in Elixir and we are not doing any manipulation of the
data in them, if we enter these in IEx—IEx will just echo back the result (which is
the data itself). You can follow along with Listing 2.2 by typing the same things in
your IEx session.
We will be covering each of this more in-depth in the rest of this chapter, so no
need to memorize them now.
iex(3)> 34
34
iex(4)> 387.936
387.936
iex(5)> true
true
iex(6)> "Phoenix in Action"
"Phoenix in Action"
iex(7)> 'Phoenix in Action'
'Phoenix in Action'
iex(8)> :phoenix_in_action
:phoenix_in_action
iex(9)> [34, 387.936, false]
[34, 387.936, false]
iex(10)> {34, 387.936, false}
{34, 387.936, false}
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
23
As Figure 3 illustrates, a named function is called by its name and you can provide
it arguments it expects:
iex(1)> Kernel.is_integer(3)
true
Here we’ve called the is_integer function inside the Kernel module.
iex(1)> Kernel.length([1, 2, 3])
3
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
24
And here we call the length function inside the Kernel module.
One thing to note is that Kernel functions are available in most situations without
explicitly typing Kernel. This makes these functions available to use almost
everywhere (we’ll discuss how you can make your module functions act in the
same way later in the book). For example, these two calls are the exact same as the
two above:
iex(1)> is_integer(3)
true
There are also some special forms that are available to use. These include +, -
, *, /, ==, !=, etc., and there are quite a few of them. These special forms can be
used everywhere and are actually function calls. You can call them a bit differently
though thanks to some syntatic sugar. 3
iex(1)> 1 + 3
4
iex(2)> Kernel.+(1, 3)
4
The above syntax is special for Erlang and we will not be using it much during our
time in Elixir. If you’d like to explore the different Kernel functions available to
you, take a look at the source code for the Kernel module. Trust me, it’s not as
scary as it sounds. In fact, the vast majority of the file is made up of documentation
and examples.
ANONYMOUS FUNCTIONS
Anonymous functions are functions that are not called by a name. They can exist
outside of a module unlike named functions. You can bind them to a variable name
which you can use to call the function later on (with a bit different syntax). One
way to think about an anonymous function is as a simple (or complex) piece of
data transformation that you may or may not need to repeat.
3
This is a good example of "prefix notation" vs "infix notation". Kernel.+(1,3) is the normal prefix notation, but Elixir
provides 1 + 3 with infix notation. Simply put, infix notation has the function call between the two arguments. There are
not very many functions that have infix notation in Elixir and you will rarely write one of your own. But sometimes it’s
nice to know the correct term for things!
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
25
For example, suppose you are making sandwiches at a deli. You need to repeat the
same process over and over again. If you needed to write the sequence in code, you
could write out each step every time an order came in. Or, you could store the same
standard steps in a function and just use that function every time an order came in.
Then, it wouldn’t matter how many orders you received—your code can handle the
extra business. In psuedocode, it might look like Listing 2.TODO.
That’s a lot of repeating ourselves by explicitly add ing every ingredient. Let’s take
these steps and slowly build an Elixir implementation using anonymous functions.
Figure 3 illustrates the pieces required to define an anonymous function.
Figure 2.4. Pieces of an anonymous function
them plate and ingredient. The function body is then indicated by a "stabby
arrow" → and contained between it and an end declaration. Our function body in
Listing 2.3 adds the new ingredient to the plate. Note that Elixir functions
implicitly return the result of the last line of the function body.
❶ We are using a List for our ingredient list. We cover Lists in detail later in this chapter.
Let’s now take our knowledge of anonymous functions and expand our psuedocode
above to be a little bit more functional.
❶ Don’t worry, you didn’t miss anything. new_plate/1 is currently not defined in our psuedocode. But
you can imagine what it might do.
Calling anonymous functions differs slightly than calling named functions that are
contained within a module. We must use the variable name we bound our function
to (add_ingredient for us Listing 2.TODO) followed by a dot and open and close
parentheses. Inside the parentheses you would put your values to pass to the
function.
add_ingredient.(plate, ingredient)
Most of the time, your functions will require multiple lines and can be written as
such. Whitespace such as newlines are generally not strictly enforced in Elixir.
iex(1)> add_ingredient = fn(plate, ingredient) ->
...(1)> plate ++ [ingredient]
...(1)> end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
27
We won’t be writing many anonymous functions like this in this book. There are
times where we WILL be writing them, such as when using them when performing
some action over a set of data, but we won’t be binding them to a variable very
often. An example of an anonymous function that isn’t saved for later is in Listing
2.4. Don’t worry too much about the syntax or what it is doing, just try to
recognize the anonymous function being used.
3. You can round a value by using the round/1 function. trunc/1 returns just the
Integer portion of a Float value.
iex(1)> round(1.6)
2
iex(2)> trunc(1.6)
1
BOOLEANS
Booleans simply represent a true or false value.
iex(1)> 1 == 5 ❶
false
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
28
iex(2)> is_integer(5)
true
STRINGS
As you’ve seen in the above examples, Strings are enclosed in double quotes ("a
string") and importantly not single quotes ('not a string') which are Charlists
(explained a bit later on in this chapter). Since Elixir supports UTF-8/Unicode by
default, strings are stored as a UTF-8 encoded binary where each character is
actually stored as its unicode bytes. Because of that, Elixir considers Strings a
binary type. Listing 2.5 gives a number of examples of working with Strings.
For longer, multi-line Strings (like documentation), it may be easier to define them
with """ (known as heredoc). Everything inside the opening and closing """ will be
retained, including spacing and line feeds.
iex(3)> haiku = """
...(3)> Build web apps for fun, profit
...(3)> Phoenix in Action
...(3)> Learn the things you need to know
...(3)> """
"Build web apps for fun, profit\nPhoenix in Action\nLearn the things you need to
know" ❶
Strings can also be interpolated with #{ }. Interpolation prints the String value of
the code or variable contained withing the curly braces.
iex(4)> subject = "Phoenix"
"Phoenix"
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
29
iex(1)> 'Geo'
'Geo'
iex(2)> i('Geo') ❶
Term
'Geo'
Data type
List ❷
Description
This is a list of integers that is printed as a sequence of characters
delimited by single quotes because all the integers in it represent valid
ASCII characters. Conventionally, such lists of integers are referred to as
"charlists" (more precisely, a charlist is a list of Unicode codepoints,
and ASCII is a subset of Unicode).
Raw representation
[71, 101, 111] ❸
Reference modules
List
Implemented protocols
IEx.Info, Collectable, Enumerable, Inspect, List.Chars, String.Chars
❶ We use the i/1 function to inspect information about the passed value ('Geo').
❷ Elixir sees this term ('Geo') as a List data type and not a String because we used single quotes.
❸ Elixir informs us that internally, our 'Geo' is represented by the list [71, 101, 111].
❹ So what happens if we use that raw data? What will Elixir echo back to us? Crucially, we give it a List
of integers and it gives us back a Charlist of 'Geo'.
If you look again at callout #4 from Listing 2.6, you can potentially imagine a
4
hexdocs.pm/elixir/String.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
30
confusing scenario. Let’s say that you have a function that returns a List containing
the results of three calculations which return three Integers (perhaps X, Y, Z
coordinates). You run the function and our iex session reports back to us that the
result is 'Geo' — it doesn’t look like a List at all, especially not one filled with
three integers. But it is (71, 101, and 111). In these cases, IEx is trying to be smart
and helpful about how to display the data it received but it turns out to be
confusing, especially to beginners. One thing to remember is that your data is still
there as you’d expect it to be (those three integers representing your X, Y, Z
coordinates) but IEx displays it in this way.
We won’t be using Charlists much at all in this book. In fact, as an Elixir
developer, I’ve still rarely had the need for a Charlist and not a String. They are
mostly a holdover from Erlang. However, this particular gotcha exists and so we
needed to discuss it before moving on. Speaking of moving on…
ATOMS
Atoms in Elixir are like Symbols in other languages. Atoms start with a colon (:),
are constants, and they are their own value. For example, :foo can never mean or
be more than :foo — it can’t be re-bound, it’s value (:foo) cannot change, and
what you see is what you get. When I initially came across Atoms, it seemed like
there should be more to them … but there isn’t. They really are that simple.
Atoms are used regularly in Elixir, especially in situations in which a function
returns a status along with a value. Let’s take, for example,
the Enum.fetch/2 function. It’s documentation states
Finds the element at the given index (zero-based).
This pattern will be often-repeated in Elixir. You will see it all over the place and
because of that, it will be a good idea to start writing a lot of your functions with
the same pattern.
One caveat to Atoms is that it is the only Elixir data type that is not garbage
collected. Without getting into the nitty-gritty of computer science, that means that
if you create enough Atoms, you can crash your system as it runs out of memory
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
31
trying to allocate too many Atoms. If you are handling user-inputed data, it is a
good idea to avoid using Atoms for those as an overload of them can be used an
attack vector against your server.
LISTS
You can think of a List in Elixir as like an Array from other languages or as simply
a list of items that may or may not be similar types. Depending on the language
you are most familiar with, it even has similar syntax. Practically, it is a list of
items contained in a single type (we will see in a moment that they are more than
that, though). Lists can contain any other Elixir types (even mixed types) and can
even contain references to other Lists.
iex(1)> list = [1, 2]
[1, 2]
iex(2)> [:numbers, "for example", list]
[:numbers, "for example", [1, 2]]
The interesting thing about Lists in Elixir is that they are linked lists. That means
that each item in the list has an internal pointer to the next item in the list (see
Figure 4). You will never interact, see, or really know anything about that internal
pointer — just trust that it is there. Practically, that means that a number of typical
operations are fast and memory-kind. It is efficient to add items to the beginning of
a List, but adding items to the end of the List can get slower as the List grows in
size. The advantages and disadvantages of linked lists are a deep computer science
topic that we won’t dive into, but if you’re interested in some addition reading on
the topic, Wikipedia actually has a nice summary. 5
Figure 2.5. Lists are linked meaning each entry contains a pointer to the next item’s memory
location.
5
en.wikipedia.org/wiki/Linked_list
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
32
We will do a number of things with Lists in our time in Elixir and you will often
hear about the "head" and "tail" of a List — so much so that there are built-in
functions and patterns we will use for both. The "head" of a List is, as you’ve
probably guessed by now, the first item in the List. The "tail" is everything in the
List that isn’t the head (in other words, the rest of the List).
Figure 2.6. Each List has a head and a tail. You can use | to separate the head from the tail.
❶ The | character is used to denote the break between the head and tail. I affectionately call it the
"guillotine" as it separates the head from the rest of the List.
The hd function returns the head of a List (but, as in all of Elixir, does not modify
the value sent to it).
iex(4)> hd(tail)
"in"
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
33
iex(5)> tail
["in", "Action"]
Internally, since Elixir uses linked lists, we can actually write out how Elixir "sees"
them (as a value acting as the head, followed by another List as the tail). The head
of this is List 1. The tail is itself a List comprising of 2 as its head and an empty
List as its tail.
iex(1)> [1 | [2 | []]]
[1, 2]
There are lots of powerful functions and great documentation online. If you want to
dive further in, check out Elixir’s documentation. 6
TUPLES
Tuples are stylistically similar to Lists but instead of being surrounded by square
brackets ([ and ]), Tuples in Elixir are surrounded by curly braces ({ and }). They
can also store a undetermined number of elements of differing types like Lists can.
Underneath the hood, however, Tuples are different. They are ordered and store
their data contiguously in memory. Therefore, accessing any element in the Tuple
is a constant-time operation (unlike Lists).
The Elixir documentation is so good, I’ll let it take over the next paragraph:
Tuples are not meant to be used as a “collection” type (which is also suggested
by the absence of an implementation of the Enumerable protocol for tuples):
6
hexdocs.pm/elixir/List.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
34
❶ We can use the Map.fetch/2 function to access a Map’s value for a given key.
A shorthand syntax to use when using Atoms in a Map as either the only keys or
the last-passed keys is the Atom name followed by a ':' and the the value for that
Atom’s key. For example, %{a: 1} is functionally the same as %{:a ⇒ 1} but is
shorter to write and, in my humble opinion, nicer to look at. However, we can’t use
this syntax if we follow it with keys of a different type. Elixir will complain to us
about this syntax and we see that complaint in Listing 2.8. Elixir doesn’t know
what to do with this type of syntax mixing but if the shorthand is used in the last-
passed keys, it is possible.
Listing 2.10. Using the alternate Atom key syntax for Maps
iex(4)> %{a: 1, b: 2, c: 3}
{:b => 2, :a => 1, :c => 3}
7
hexdocs.pm/elixir/Tuple.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
35
Apart from the Map.fetch/2 function used in Listing 2.7, there are other, potentially
more often-seen ways to access a key’s value in a Map. We can use
the map_name[key] shorthand to access a Map’s value for the specified key. This
works for Atom keys and String/Binary keys. In fact, it works with any of the key-
types in Elixir.
Another way to access Atom-based keys is by separating the variable name and the
key to be retrieved with a .. However, unlike the [key] style of access, this style of
access only works with Atom-based keys as we can see in the error provided in
Listing 2.9. Elixir specifically looked for the key :hello in our Map.
Listing 2.9 contains examples of both of those methods.
iex(3)> map["hello"]
:world
iex(4)> map.a
1
iex(5)> map.hello
** (KeyError) key :hello not found in: %{:a => 1, "hello" => :world}
We will be using Maps a lot in our time in Elixir and Phoenix, so take some time to
play with them in your IEx session. Also, read up on the power of Maps and some
of the included functions in the Map documentation. 8
2.1.4 Back to Modules and Named Functions
Of course, most of your code will not be simple anonymous functions. We will be
organizing and reusing large portions of your codebase. When we do that, we will
need to create modules and have functions live inside those modules. For
organizational sanity, functions that have things in common are grouped into
modules. For example, if you had a group of functions that did any number of
mathematical calculations, you may group those together in a Math module.
Now that we’ve taken a look at the module as a whole, let’s break down a few
interesting sections of the module.
All module definitions start with defmodule followed by the name of your module
8
hexdocs.pm/elixir/Map.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
36
and do. Module names are CamelCased and you can namespace modules as well
with dot notation (.). For example, if we had a subset of Math functions that dealt
with mortgages, we could create a Math.Financial.Mortgage module. For now
though, let’s keep our Math module simple.
defmodule Math do
# ...
end
A function inside a module is defined with def followed by the function name and
any arguments it accepts. For our add function, we are accepting two arguments
and assigning them variable names of aand b. Since Elixir implicitly returns the
result of executing the last line of the function, we don’t have to explicitly return
the value of adding a and b.
def add(a, b) do
a + b
end
There also exists a shorthand function definition. When your function does one
thing or is a one-liner, it can be helpful to use this shorthand version of the function
definition. The differences between the multi-line and one-line function definitions
are small, but there is an addition of a , after the argument collection (e.g. (a, b))
and a : after do. Also note the lack of end.
def subtract(a, b), do: a - b
Functions can take any number of arguments and if they take none, the parentheses
can be omitted entirely.
def one, do: 1
Elixir allows you to define multiple functions with the same name. How does it
know which one to actually execute? It does pattern matching on the function
signatures and uses the first one that matches. We go deeper into pattern matching
later in this chapter, but for now, think of it as just looking for the function
signature. A function signature is its name and the amount of arguments it accepts
(or it’s "arity").
For even_numbers, we have one function that expects two arguments and one
function that accepts zero or one arguments. Functions can have default argument
values and can be specified by the \\characters. We can call even_numbers with a
single number and have it find even numbers from 0 to the provided number or call
it with no arguments and have it default to returning even numbers from 0 to 10.
Stylistically, functions with the same name are grouped together.
Functions inside a module can call other functions inside the same module without
using the module prefix. If there is ever a cause for confusion over function names,
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
37
you can always use the full name of the function (Math.even_numbers) instead.
def even_numbers(min, max) do
Enum.filter(min..max, fn(x) -> rem(x, 2) == 0 end) ❶ ❷
end
def even_numbers(max \\ 10) do
even_numbers(0, max)
end
❶ min..max simply creates a Range of Integers from min to max. A Range is what it sounds like, a
collection of all numbers between and including two numbers.
❷ rem/2 returns the remainder of dividing the first argument by the second. rem(x, 2) equals 0 for
even numbers.
Functions that you need to call internally but you don’t want exposed to the outside
world can be defined as private. You do this by using defp instead of def when
defining the function.
defp internal_calculation(x), do: 42 + x
Listing 2.10 shows what our group of functions that deal with doing simple
calculations might looks like now.
defmodule Math do
def add(a, b) do
a + b
end
If you have a module name that is deeply namespaced and you don’t want to type
its long name every time, you can alias it and from there on use only the last
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
38
Figure 2.7. alias vs import. alias allows you to shorten the module name
while import allows you to forego it entirely.
IMPORT
You can further reduce typing and import the functions of a module into your
module as well (demonstrated in Listing 2.11). That will allow them to be called
without the module name prepended. If you import a function without any
arguments, all the public functions will be available. However, you can limit which
functions are brought in by providing an only argument and a List of which
functions and their arity you’d like to import. Listing 2.11
imports Math.Financial.Interest.rate/1. Once the functions are imported, we can
call them without using their full module names.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
39
defmodule MyMortgage do
import Math.Financial.Mortgage # contains calculate_amount/1
import Math.Financial.Interest, only: [rate: 1]
Why is that helpful? Why not just pass it in directly as the first argument? Let’s
look back at our Deli from earlier in the chapter in Listing 2.TODO.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
40
end
Look at all that re-binding of plate. Pretty ugly and repetitive, isn’t it? But you
will notice something about how we wrote our add_ingredient/2 function: the first
argument is expecting the current state of the plate. Not only that, our function
returns the new state of the plate after adding the ingredient. We can use the pipe
operator in Listing 2.TODO to clean up this code greatly.
Our plate variable is now gone! We have bound no variables in our process of
creating our masterpiece of a sandwich. Instead, we are just taking the result of one
function and letting the next function use it, passing the state of our plate down
through the assembly instructions for our sandwich. Finally,
since add_ingredient/2 returns the state of the new plate, that means that at the end
we return the plate implicitly to the customer.
When writing your functions, it is normally worth the extra time to consider
exactly how you want the data in your application to flow. With that in mind, you
can arrange the functions like we have above so that our Elixir code can read just
like the assembly instructions.
Get to know and love the pipe operator because you will see it all over the
place during your time in Elixir and you will be writing it a lot. And believe
me, you will love it.
2.2.2 Pattern Matching
Think back on your personal experiences with sandwiches. How do you recognize
a sandwich? By the way it smells? Perhaps. By the way it sounds? Probably not.
By the way it looks? Most likely, yes. How would you describe a sandwich?
Typically, they are made up of smaller building blocks such as bread and meat like
in our examples from the section on the Pipe Operator. But you don’t typically
recognize a sandwich by piecing together all the smaller parts and then deciding it
is a sandwich. Normally, you can recognize the pattern of those smaller objects
that create a sandwich.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
41
For example, you would typically make a turkey sandwich using some variation of
bread, condiment(s), meat, cheese, lettuce, tomato, and other desired toppings. You
can see each individual piece (and name them), but also see the whole at the same
time. That is because your brain is wired to recognize patterns. Elixir is also wired
to recognize patterns.
Suppose you wanted to verify that an object you’ve been given is a grilled cheese
sandwich? Someone hands it to you and says "here’s a grilled_cheese_sandwich!"
but you’re not sure if you can trust them. You would use what you know about
grilled cheese sandwiches and use pattern recognition. Bread, cheese, then bread
again? If so, then you’ve verified the pattern. The same can be done with Elixir.
iex> grilled_cheese_sandwich = [:bread, :cheese, :bread]
[:bread, :cheese, :bread]
iex> [:bread, :cheese, :bread] = grilled_cheese_sandwich
[:bread, :cheese, :bread]
Figure 2.8. Pattern matching compares the two sides of a call. If everything matches, it is a
successful match. If one side is different than the other, an error will be returned.
The first call actually set up our variable with a List of Atoms. The second call
does the pattern check. The fact that we did not receive an error on our second IEx
call verifies that the pattern we told it to expect on the left side of the = sign is
indeed the pattern on the right. What would have happened if the patterns were
different?
iex> [:bread, :cheese, :salami, :bread] = grilled_cheese_sandwich
** (MatchError) no match of right hand side value: [:bread, :cheese, :bread]
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
42
expect with variable placeholders and bind the unknown values to those
placeholders. Suppose we knew this strange sandwich-giver gave
us something between two pieces of bread, but we weren’t sure what it was. We
can verify what we know about the pattern and bind the unknown to a variable to
learn what it is in between those slices of bread.
iex> [:bread, mystery_meat, :bread] = unknown_sandwich
[:bread, :olive_loaf, :bread]
iex> mystery_meat
:olive_loaf
We can now use that mystery_meat variable later in our code (perhaps to decide
whether or not to actually eat the strange sandwich).
As previously stated about Atoms and Tuples, a pattern you will see a lot in Elixir
is {:ok, value} or {:error, reason}. This is an idiomatic pattern because it is easy
to match on in your code to decide whether to continue operation or present an
error to the user. Take, for example, the Map.fetch/2 function. It takes a map and a
key and, if it finds that key in the given map, it will return that key’s value as {:ok,
value}. If the given map does not have the provided key, it will return :error. We
can use that knowledge to search our memory to see if the mystery meat is one we
will eat.
iex> edible_meats = %{turkey: true, chicken: true, ham: false, olive_loaf: false}
iex> {:ok, edible} = Map.fetch(edible_meats, :turkey)
{:ok, true}
iex> edible
true
We will eat the turkey! Now let’s use our mystery_meat to see if we will eat that.
iex> {:ok, edible} = Map.fetch(edible_meats, mystery_meat)
{:ok, false}
iex> edible
false
So we’ll pass on the olive loaf sandwich. But what happens when we’re given a
meat we’ve never encountered—one for which we have no listing in our Map? We
expect to get an error, right?
iex> {:ok, edible} = Map.fetch(edible_meats, :head_cheese)
** (MatchError) no match of right hand side value: :error
Our pattern match failed because we expected {:ok, value} but instead got :error.
Awesome! Elixir can tell you when an unexpected pattern is used. Let’s write a
little function in a module using pattern matching that will help us decide whether
we will eat a mystery sandwich.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
43
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
44
defmodule Sandwich do
def accept?(sandwich) do
edible_meats = %{turkey: true, ham: false, chicken: true, olive_loaf: false}
[:bread, mystery_meat, :bread] = sandwich
We will often reach for the case statement to pattern match a variety of expected
results in order to correctly respond to the result of a function.
We would have expected an error in call 3 because we wanted to ensure we got the same
sandwich twice, no matter what the mystery_meat was. Instead, we re-
bound mystery_meat to :ham. How do we "pin" the value of a variable in pattern matching
like this? Well, with the pin operator ^! Using it, we can pin the value in a pattern match so
that we can match on that variables value instead of re-binding it with the right-side value.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
45
:turkey
This time, because we pinned our variable ^mystery_meat, we didn’t re-bind it but instead
attempted to match on its value. And since the right hand side of the pattern match didn’t
match the left, we got a MatchError. Finally, we verify that our mystery_meatvariable
wasn’t re-bound to a value of :ham and instead retains its original :turkey value.
Pattern matching Maps can be fun and very code-efficient. Here we look to pattern
match the key of :bass from our band variable which contains our Map of band
member names. If we do find a :basskey, we ask Elixir to bind the value of that
key to the variable of name. Finally, we verify that we indeed now have "Adam" as
the value of name straight from our band Map.
iex(7)> band = %{vocals: "Paul", guitar: "Dave", bass: "Adam", drums: "Larry"}
iex(8)> %{bass: name} = band
iex(9)> name
"Adam"
There are occasions in which you want to make sure a pattern is matched but don’t
really care about storing a value away for later use, nor care which value is
even in a particular position. For example, we’ve used the Map.fetch/2 function
quite often in this chapter and you’ll remember that it returns {:ok, value} on a
successful match. For the times where we don’t want to store that value away, we
can use the character to let Elixir know we expect something in that position, but
don’t really care what.
iex(1)> {:ok, _} = Map.fetch(%{b: 2}, :b)
{:ok, 2}
iex(2)> {_, _} = Map.fetch(%{c: 3}, :c)
{:ok, 3}
You can also prepend variable names with an underscore (_) in order to give the
value an identifier, but also let Elixir know that you don’t expect to use that
variable’s value anywhere in your code.
iex(1)> {:ok, _name} = Map.fetch(%{vocals: "Paul"}, :vocals)
Pattern matching is not only incredibly useful and fun to use, it is also something
that is used quite often in Elixir code. We will also be writing a number of
functions that use pattern matching in Phoenix. Take some time to play with some
pattern matching in Elixir.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
46
iex(1)> 1 + 5
6
While you won’t be using this helper function at all in your own modules, it can be
incredibly useful as you are either learning or experimenting with Elixir in an IEx
session, or even debugging your code trying to figure out exactly how things work.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
47
2.3.2 Books
• Elixir in Action by Sasa Juric
• The Little Elixir & OTP Guidebook by Benjamin Tan
2.3.3 Community
• #elixir-lang IRC channel on Freenode
• Elixir Slack community: elixir-slackin.herokuapp.com/
• ElixirForum: elixirforum.com/
There are many other resources available for the budding Elixir developer. Check
out the above resources and do some searches on your own to find your favorite.
2.4 Summary
In this chapter you learned
• To utilize Interactive Elixir (IEx) to practice, experiment, and learn Elixir in an
interactive REPL is an easy way to get to know the basics of the language.
• That asking for help from Elixir itself is relatively easy and very informative by
using the h/1 IEx helper function.
• Most of the basic types of Elixir including Integers, Floats, Booleans, Strings,
Atoms, Lists, Tuples, and Maps. You also learned about Charlists and how they
can potentially be confusing when working in and iex session.
• Creating named functions must be done inside a module while anonymous
functions can be bound to a variable for later use.
• Modules keep your code together into sensible groupings of functions.
• Elixir’s Pipe Operator (|>) is incredibly useful and it can make your code read
more like a sentence written regarding how your data will be transformed from
one function through the next or a bulleted list of transformations our data will go
through.
• To utilize pattern matching in your code to quickly verify the return value is in a
pattern you expect and to grab desired data directly from the structure of the return
value. Pattern matching is also used by Elixir to determine which function to
execute when multiple function definitions have the same name. The first one to
match gets executed.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
48
In the last chapter, you learned some of the basics of the Elixir programming
language. If it was your first introduction to the language, I hope that you spent
some time in an IEx session to play with different concepts and to see how things
work. If you haven’t, consider taking a few minutes to re-orient yourself with the
syntax of Elixir and how you can interact with it — we’ll be looking at and writing
a lot of Elixir from this point on.
In this chapter, we’ll be looking at an example Phoenix application — one that isn’t
too complex and, to be honest, doesn’t really do all that much. But as we will be
just taking a bird’s-eye view of the application, we don’t want complexity to
overshadow the basics of how a Phoenix application is pieced together. The
application we will be looking at is a simple blog engine — one that doesn’t look
pretty but allows the owner to create and edit blog posts and a visitor to read those
blog posts and to create comments on those posts.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
49
Figure 3.1. Our quick-tour application will be a very simple blogging application
In later chapters, we will be building a much more complex web application from
the ground up and dive deeper into the topics being introduced here. Think of it
like this: soon we will be making a custom-built mansion and doing so with our
hands and our tools and starting from a dirt foundation. But this chapter is more
like a tour of a simple house that is already built. Such a tour would introduce you
to the basic concepts of a house if you weren’t really familiar, but you wouldn’t be
able to build your own after the tour. The same goes for this chapter—you won’t be
able to jump in and build your own Phoenix application after reading it, but you’ll
have a better understanding of what we will be building in the future chapters.
In short, if you don’t understand the code itself in this chapter, that is fine. What I
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
50
hope you get out of this chapter is the basic understanding of the different pieces of
a Phoenix application and how they all fit together to take a visitor’s web request
from initial server request to rendered page.
The name of our application will be "Blog". Really creative, huh? It’s important
that you know the name of our application because almost every module defined in
our application will have Blog prepending it which makes it very easy to
namespace. Namespacing not only helps you keep track of your code, but it
provides an easy way to combine applications in the future if you ever need to
merge two applications into one or call one application from another. It also allows
for nice mental and physical code organization.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
51
fact will not really ever come into play, so don’t worry about remembering it or
noting it as important, but it is nice to know.
Figure 3.2. The steps a request takes from the user, through our Phoenix application, back to
the user
Figure TODO shows how our Phoenix application handles a web request to our
blog. Right now, all you see is an empty box that shows where Phoenix comes into
play in the process. In each section of this chapter, we will uncover an additional
step in the Phoenix process that handles the request from the previous step. We’ll
slowly reveal what is inside that Phoenix-shaped box between the point when a
user’s request hits our server and we send the user back the html required to render
a web page in their browser.
The code we will be reviewing is from an already-built application. A surprising
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
52
amount of it is unchanged from the code that Phoenix automatically supplies when
you start a new application, but some of it was added by me to add the blog-like
features we need. All of the code for this simple blog application can be found
at www.phoenixinaction.com/code/blog.
3.1.2 Endpoint
After the Cowboy server handles what it needs to do, our journey with Phoenix
begins and the first piece of Phoenix code that handles this request the Endpoint.
Figure 3.3. The Endpoint is where our data’s Phoenix journey begins
You may be interested to know that even though the Endpoint module in Listing
3.1 runs our Blog web application, nothing of substance in this file has been
written or even edited from what was created for me when I ran mix phx.new (the
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
53
terminal command to use to start a new Phoenix application; we’ll run that
ourselves in an upcoming chapter). The only thing changed from the original is
removing comments and some reformatting due to spacing issues.
defmodule BlogWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :blog ❶
plug Plug.Static, ❷
at: "/", from: :blog, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
if code_reloading? do
socket "/phoenix/live_reload/socket",
Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Plug.RequestId
plug Plug.Logger
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session,
store: :cookie,
key: "_blog_key",
signing_salt: "IU3pUWqa"
plug BlogWeb.Router
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
54
❶ Most of the major components in Phoenix will use another module, bringing its functions into our
module.
❷ The majority of the Endpoint is made up of plugs.
Almost everything you see in listing 3.1 begins with plug. plug is a macro that sets
up a Plug. 9 While we didn’t cover it in the Intro to Elixir chapter, a macro is
essentially a way to write code that writes code. In the Endpoint example,
each plug * line adds a specified Plug to the pipeline that the data from a request
flows through. The flow goes from the top of the file to the bottom of the file and
without any knowledge of what each of these plugs do, you can take a reasonable
guess just from their module names. In our time in Phoenix, we will eventually
write our own Plug module so don’t worry too much about getting a deep grasp
of Plug and its abilities for now.
plug Plug.Static,
at: "/", from: :blog, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
The first plug we encounter is Plug.Static. It’s job is to serve up static files in our
we application. It specifies that we’d like to serve them at /, don’t compress them,
and serve only files in the css, fonts, images, and js directories as well as the
specific files favicon.ico and robots.txt.
if code_reloading? do
socket "/phoenix/live_reload/socket",
Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
the server, and you can explicitly log your own information in there as well. From
there, we move into Plug.Parsers which sets up different file handlers. In this case,
you can see that we tell Phoenix we’d like to decode our JSON data with the
Poison package (which is included by default with Phoenix).
The next two plugs are mostly for Phoenix’s benefit under the hood and ones we
don’t need to concern ourselves with too much. The request data is sent
into Plug.MethodOverride which is a plug that overrides some browser requests so
that Phoenix can better handle them. 11 That is then fed into the Plug.Head plug
which is simply "A Plug to convert HEAD requests to GET requests." 12
The final two steps are setting up our session with Plug.Session which will handle
our session data like cookies that we can use to track a User from one request to the
next — important for serving up dynamic web pages that is customized for each
user. Finally, our data is then transferred to the BlogWeb.Router plug, which
ultimately routes the request to the correct handler within our application (and
which we will look at next).
One thing you’ll notice about each of these plugs is the small, focused amount of
work that each is doing. There is a whole Plug that generates a unique ID? That is
pretty focused — and is also a good pattern to follow. When we write our own
plug, we will attempt to do the same: keep the scope small and focused.
3.1.3 Router
The last plug the Endpoint sends the request data through before it is done with its
job is BlogWeb.Router. Now that the Endpoint has done its job, the Router is next in
line to handle the request. This reveals the next step inside our Phoenix box.
11
If you’d like to dive into the docs though, you can so here: hexdocs.pm/plug/Plug.MethodOverride.html
12
hexdocs.pm/plug/Plug.Head.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
56
Figure 3.4. After the Endpoint finishes setting up the request, it passes it to the Router
The Router’s job is to examine the user’s request along with any additional data the
Endpoint added along the way and decide which part of our application should
respond next. It does that by examining the path the user requested (in this
particular case, "/posts/4"), piping that request through appropriate "pipelines",
then sending it on to the appropriate handler.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
57
Figure 3.5. The router decides which controller to send the request to.
Listing 3.2 is a full look at the code in the Router. We’ll take a closer look at each
section following the Listing.
defmodule BlogWeb.Router do
use BlogWeb, :router
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
58
pipeline :browser do ❶
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
❶ Each pipeline sets up related plugs that define a specific set of functions relating to different kinds of
requests
❷ Scopes group related request paths and allows namespacing
Within our Router file (Listing 3.2), you can see that we again have a lot
of plug statements. However, these are different than the last in one major aspect:
these are being sent an Atom of a function name as their argument rather than a
module name like in Endpoint. We will get more in-depth about the differences
when we create our own plugs later in the book, but we can again surmise from the
names of the functions what each does.
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
59
The plugs themselves are grouped together into what’s known as a "pipeline". A
pipeline is just a group of plugs that are used for a particular purpose. In
this Router, we have two different pipelines set up: one for a typical browser
request and one for a typical api request. In the browser pipeline, we see that we
are accepting "html" requests, getting the session and the flash (which stores
temporary messages for the user such as success/info/warning messages), we set up
forgery protection for our forms, and finally put secure browser headers into the
data flowing through. However, in the api pipeline, most of that is gone. We only
accept "json" requests. And this makes sense — why get the flash and set up form
forgery protection when a typical API request doesn’t utilize either of those
features? Another important note is that both of these pipelines are set up by
default by creating a new Phoenix application—these pipelines haven’t changed at
all from the default.
This is a good example of the explicitness of Elixir and Phoenix. We can see in
these pipelines exactly what is included or excluded from each type of request.
Why waste the processing power and potential database calls in situations in which
the results of those things are never used?
scope "/", BlogWeb do
pipe_through :browser # Use the default browser stack
We have the pipelines set up, but we don’t use them until we get into
the scope blocks. The first scope, sscope "/", listens for requests that are at the
root level of our web page ("`/`"). Everything in its do`/`end block can be thought
of children of that root path. So the top-level request
of www.phoenixinaction.com/ would enter into this block. We can see that we first
tell Phoenix that we want to pipe these requests through the :browser pipeline we
set up previously. From there, if it is a GET request to /, then send it to
the BlogWeb.PageController 13 and the index function within that module.
Next we move to the resources call. That single call creates handlers for a full
complement of RESTful actions (index, new, create, show, edit, update, and delete)
at the /posts location. In other words, if a user visits our.blog/posts, it will forward
the request to the BlogWeb.PostController’s `index function. All of those seven
functions are forwarded to the BlogWeb.PostController. Since this resources call
has a do/end block, we can add more routes within it; so we add another resource
13
You’ll notice that the specific get line only contains a reference to PageController. Since this is nested under
the scope "/", BlogWeb call, Phoenix knows to also namespace the calls within it to be under BlogWeb and as such,
will prepend that before any module name.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
60
Finally, you can see we’ve also set up a scope for /api which is handled under
the BlogWeb.API namespace. Instead of piping the requests to /api through
the browser pipeline like we did for our / scope, we pipe this one through
the api pipeline we set up in the top of the file. Within our API, we have one
resource, posts, and it is handled by the BlogWeb.API.PostController. We’ve
limited access to only the index and show functions.
Notice how in both scopes we have a PostController. This could have caused
name collisions in other frameworks but remember that the scopes prepend our
controller modules with their own names. In our case, we have
a BlogWeb.PostController and a BlogWeb.API.PostController—two different
controller modules for handling different types of requests for blog posts in our
application.
As with the other sections of this chapter, don’t sweat the details. We’ll dive into
routes and resource in a later chapter and will get much further into the weeds.
If at any time you’d like to see what routes your Router is creating, you can run mix
phx.routes in the terminal from the root directory of your application. Listing 3.3
shows the output of our currently set-up routes.
14
It’s usually a good idea to limit the endpoints of your web application to only the ones you are currently using. Leaving
other ones open could lead to security concerns.
15
hexdocs.pm/phoenix/1.3.0/Phoenix.Router.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
61
We can see the route name (post_path, which we’ll cover the use of later), the
method the browser uses to access the route (GET), the path of the route in the
user’s request (/posts), and finally names of the module and function that handles
the request (BlogWeb.PostController; :index).
For our purposes from here on, let’s follow a GET request to /posts/:id. In a
RESTful application, that will typically be a request to see the page for a particular
blog post with the specified ID. We can see from listing 3.3 that that type of
request is forwarded on to the BlogWeb.PostController.show/2 function. Let’s head
into the controller next.
3.1.4 Controller
After the Router handles the initial part of the request, it is passed on to the
controller. This unveils the next step in our Phoenix black box.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
62
And guess what a Phoenix controller is? If you guessed another Plug, you win the
grand prize! The entire purpose of a controller is to gather and set up all the data
that the next steps will require in order to return a response to the requesting user
and decide what kind of response to send. That may or may not include getting data
from a database, an external API endpoint, or some sort of static file. A controller’s
functions are also called "actions". Since we are following a request to /posts/:id,
we’ll look specifically at the BlogWeb.PostController.show/2 function (the "show"
action), which handles the request.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
63
Figure 3.7. The controller makes sure everything is available for rendering the response.
defmodule BlogWeb.PostController do
use BlogWeb, :controller
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
64
alias Blog.App ❶
❶ We alias Blog.App so for the rest of the file, any call to App will actually go to Blog.App. It just
saves some typing.
I’ve commented out the details of all the functions except for show/2 so we can
focus on that one function and drill into it. But notice that we have all the different
RESTful actions accounted for: index, new, create, show, edit, update, and delete.
SHOW/2 FUNCTION DEFINITION DETAILS
The first detail we’ll look at in show/2 is the function definition: def show(conn,
%{"id" ⇒ id}) do. We can see that show is going to be passed two
parameters, conn and a Map. Every function in the controller expects the same by
default: a conn and a Map. The Map is actually a Map of the request parameters
from the user. While there could be a large number of parameters that get passed
into the request by the site visitor, the only one we care about in show is
the id param so we use pattern matching to pull out the id from the parameter Map.
If you’ll remember from our list of routes, /posts/:id is the path that gets us to this
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
65
function. The ":id" portion of that route specifies that whatever we put into that
position will be passed as the id in the params Map.
A few more examples of pattern matching the parameter Map in the other
functions:
• For index/2 and new/2, we don’t really care what params are passed in with the
request as it has no bearing on our response. So we ignore the params Map. We
signify the fact that we are ignoring that data by prepending the variable name
with an underscore (_params).
• For create/2, we want to capture all the data that is sent to us in a web form when
an author creates a Post for our blog. We expect all that data to come in via a
param named post and so we capture that data into a post_params variable for
later use (which we don’t see here as I’ve commented it out).
• edit/2 and delete/2 are like show/2 in that the only request parameters we care
about are the id of the Post to work with.
• Finally, update/2 requires both the id of the Post as well as the form data in the
"post" portion of the param Map. The id is required to know which Post to update
with the rest of the data passed in.
SHOW/2 FUNCTION BODY DETAILS
The first line inside our function is
post = App.get_post_with_comments!(id)
defmodule Blog.App do
import Ecto.Query, warn: false
alias Blog.Repo
# ...
def get_post_with_comments!(id) do ❶
get_post!(id)
|> Repo.preload(:comments)
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
66
# ...
end
❶ We can define functions that provide us with exactly what we need in the controller in order to set up
the view
❷ Short, one-line functions can be well suited to this function definition shorthand
In the first few lines of the file (listing 3.5), we set up the environment for the rest
of the file by importing and aliasing as needed. Don’t worry about the details of
these lines as we’ll go further in depth in later chapters. For now, know that Ecto is
the package Phoenix uses by default to be our database adapter. It provides
functions that make working with a database easier. The Repo is our "Repository"
that uses Ecto to make those database calls. Again, we’ll get deeper later.
In short, get_post_with_comments!/1 fetches the Post from the database. It does
that by looking it up from the id we passed it. It then then preloads all the
comments associated with it. If we want to display relevant comments, we need to
let the database know ahead of time. Not only is it more explicit, but it reduces
unneeded database calls further down the stack.
def show(conn, %{"id" => id}) do
post = App.get_post_with_comments!(id)
new_comment = App.change_comment(%App.Comment{})
render(conn, "show.html", post: post, new_comment: new_comment)
end
Let’s move back up to the controller. We take the Post fetched from the database
along with all its associated comments and store that into the post variable. On the
next line, we set up a new App.Comment. This may be a bit confusing because we are
using the function named App.change_comment/1 to do so, but since we are passing
it a freshly-initialized struct, it’s basically setting up what is known as a
"changeset" for the new comment. We bind that fresh comment into the
variable new_comment. A changeset allows us and the database to easily track
changes and any validations needed on the comment. We’ll go much deeper into
changesets later in the book and use them extensively.
Once we have those two variables, we head to the last line: render(conn,
"show.html", post: post, new_comment: new_comment). This calls
the render/3 function. The first parameter we pass it is conn, which you’ll notice
every function definition has and uses. What is conn? It’s the connection that we
have been passing from Plug to Plug up to now. It contains all the information
about the request and connection that we’ve captured and set up to this point —
through the Endpoint and the Router and now to here. It’s officially
a Plug.Conn struct and the render/3 function requires it.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
67
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
68
By default, the View module with the name corresponding to the controller module
name is the one that is called. In our application, BlogWeb.PostController will
render the "show.html" template specified in BlogWeb.PostView. Let’s peek into
our App.Web.PostView file in Listing 3.6.
defmodule Blog.Web.PostView do
use Blog.Web, :view ❶
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
69
Not a lot in there, is there? The majority of the functionality is handled on line 2:
use Blog.Web, :view. This brings a number of functions into our View
including render/3 which ultimately handles the request. You’ll notice that we
don’t define our own render function — it is defined inside that use call. This is
one of the strengths of Elixir — metaprogramming is still possible even though it is
a compiled language. The details of what actually happens there is beyond the
scope of this particular chapter, but know that everything we need to render our
templates is set up in that call.
We also have a lone function that we defined named date/1. Where is this used? In
the templates. We’ll see its usage in the next section. All it is doing is converting a
passed in Elixir Date to a String representation of it. Any function you define in
this view will be available to the templates rendered by it.
Since our controller passed in "show.html" as the template name in
the render/3 call, "show.html" is what the View will attempt to render. Let’s look
at our template.
3.1.6 Templates
We have come to the final step inside our Phoenix black box: the Template.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
70
The template is responsible for taking the data that we’ve been building up
throughout the different stops in Phoenix and using that to render something back
to the user that requested it. It doesn’t have to be HTML — it could just as easily
be JSON, XML, CSV, etc. In our case, we just want to render back good, old-
fashioned HMTL to be rendered by the user’s browser.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
71
Something very neat about the way Phoenix handles template files is that when
Phoenix compiles, it turns every template file into its own render/2 function inside
its view module. The return value of the function is the resulting HTML from the
template! This means that, yes, rendering is actually just another function and just
another way to transform data. No HTML is stored on disk or in memory and it is
fast.
As we look at the code for our template in listing 3.7, take note of a few things:
1. It is a mix of HTML and Elixir code. You’ll notice the file extension is .eex—it is
an Embedded Elixir file.
2. It is not ALL of the HTML that is ultimately rendered. Where’s
the <html> opener?
3. This won’t render the prettiest web page in the world, but it’s a start. :-)
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
72
<div>
<p><%= date(@post.inserted_at) %></p> ❷
<%= text_to_html(@post.body) %> ❸
</div>
<hr />
<h4>Comments</h4>
<div>
<%= for comment <- @post.comments do %>
<div>
<em><%= comment.name %> says:</em>
<%= text_to_html(comment.body) %>
</div>
<% end %>
</div>
<hr />
<h5>Post a comment</h5>
<%= form_for @new_comment, post_comment_path(@conn, :create, @post), fn f -> %> ❹
<div>
<label>
Name:<br />
<%= text_input f, :name %> ❺
</label>
</div>
<div>
<label>
Comment:<br />
<%= textarea f, :body %>
</label>
</div>
<%= submit "Submit Comment" %>
<% end %>
<hr />
<div>
<span><%= link "Back", to: post_path(@conn, :index) %></span> ❻
</div>
❶ We render the result of Elixir code by enclosing it in a <%= %> block. Everything else will just be HTML.
Since we passed our Post data into the render/3 call under the post key, it is available to us in this
template as @post. So here we render the Post title attribute.
❷ Here we use the date/1 function we defined in our PostView module.
❸ text_to_html/1 is a helper method provided by the Phoenix.HTML.Format module.
❹ Phoenix.HTML.Form provides the form_for/3 function that allows us to easily set up a form…
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
73
❺ …and provides helper methods to add all the form elements we need.
❻ link/2 renders a link to a specific path. Here, we use the post_path named path we saw in our
routes list from earlier in the chapter.
As with most of the other things in this chapter, don’t worry about understanding
all that is going on in here. We will dive into the details in later chapters. This is
just provided as an overview of what is in these files.
Figure 3.11. The portion inside the box is what was rendered from our template
The output of this EEx template is the boxed portion of our original HTML image
from the beginning of the chapter. So what about the rest of the HTML you’d
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
74
expect like the <html>, <head>, and <body> tags? Those are all provided in a file
known as a Layout. An app can have multiple layouts though typically only use a
handful at most. The layout defines the parts of the page/HTML that are the same
from one page to another like css/javascript includes, analytics embeds, or page
navigation. By default, "app.html" is the layout that is called by Phoenix. We won’t
look in there at this time.
3.3 Summary
In this chapter you learned
• A web request can be thought of as our Phoenix application receiving a chunk of
data as a request and then modifying and building portions of it as it passes
through our application and is formed into a response to the user.
• The Endpoint sets up the initial portions of the environment in which our data
transformation will live by using Plugs.
• The Router takes the request from the Endpoint and acts as a director of sorts,
taking the request and deciding where to send it next. As it does that, it does some
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
75
of its own data transformation in the form of Plugs, typically in a defined pipeline
that can be shared by multiple scopes.
• The appropriate Controller is called by the Router and handles taking the request
and getting all the data ready that will be needed to present back to the requesting
visitor. Things like fetching information from a database or setting up new structs
to be used in a form happen here.
• The Controller then calls the render/3 function of a View. The View can define
helper methods to use in a Template and also does the work of actually rendering
the appropriate Template.
• Finally, the Template renders the HTML response (for a HTML request) to the
user based on their request. In our example, this included the details of the blog
post along with a list of comments and a form to allow the user to submit their
own comment.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
76
Part I covered the basics of Elixir and Phoenix and got you up to speed regarding
basic syntax and the flow of a web request as it traveled from the requesting user,
through the Phoenix framework, and back to the user as a rendered web page. In
Part II, we will be diving into creating a full-featured web application from the
ground up. Our application will be a simple live auction site sort of like eBay. Our
feature list won’t be near as long as eBay’s, but there will be plenty of
opportunities for you to add your own features as we go along.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
77
logic of your application. In our case, Phoenix is the main one. Starting from the
beginning with the knowledge that your app might be used in multiple ways in the
future is always a good way to go.
Figure 4.1. Phoenix is just a border of your application
That’s really what is meant when it is said that "Phoenix is not your application." It
should be a part of making your application web accessible, but don’t mistake that
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
78
and the business logic of your application. Your underlying application could be
used through the command line or perhaps through embedded hardware. For
example, what if our live-auction application could be integrated with RFID-
enabled bidding devices that enabled real-time, in-person bidding on items with
just a tap? We could make this easier to implement in the future by not locking our
logic inside Phoenix.
Because of this, we will be building our application from the inside out. We will
start with creating the basic building blocks of the business logic in Elixir, and then
focus on the web portion of it with Phoenix. Along the way, we’ll be careful to
keep the database concerns separate from our logic and while we won’t be using a
real database yet, we’ll be using something that will act like a database while we
get up to speed. Figure 2 shows how the different parts of our application will work
by the end of this chapter.
Figure 4.2. The first building blocks of our Auction application. We want to keep the database
and public API separated.
While we won’t have a usable web interface for a while, our initial application will
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
79
be usable through IEx. It will be built step by step from defining what an auction
item looks like in our database, to defining a set of items to store in an in-memory,
fake "database", to finally being able to interact with those Items and our
"database". By the end of this chapter, you will be able to:
1. List all the Items in the database
2. Get a specific Item based on its ID in the database
3. Get a specific Item based on other defining attributes
There will be four initial pieces to our application:
1. An Auction.Item module that defines the data structure for our auction items.
2. An in-memory, fake "database" that we will use to store a list of items for us to
interact with.
3. A module (Auction.FakeRepo) that directly interacts with our database.
4. A module (Auction) that will provide a public API in order to get the data we
need.
4.1.1 Defining an Item
In this chapter we will create the first building block of our auction site: an auction
Item. The purpose of our Item will be to represent the data about the different
things up for bid on our site. Eventually, it will also house some other functions
that will help us deal with various aspects of maintaining our item.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
80
For now, we will create our first struct that will contain the structure of an Item in
our auction’s marketplace. Make a directory somewhere in your development
environment. We’ll use this directory as our playground of sorts while we start our
development.
Figure 4.4. Create a folder somewhere on your development machine
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
81
For our auction application, we’ll need to define what data an Item should expect
to contain. We can define data structures in Elixir inside our modules
using defstruct. In the case of an auction Item, let’s initially keep track of the ID,
a title, a description, and at what time and date the auction should end. In your
preferred text editor, create a file named auction.ex and key in the following:
defmodule Auction.Item do
defstruct [:id, :title, :description, :ends_at]
end
❶ The ~N you see here is what’s known as a sigil. Sigils start with a "~" and a letter. They represent
different things in Elixir, but this one is an easy way to create a NaiveDateTime. It is considered
"naive" because it doesn’t know about the concept of timezones.
For our implementation, though, we don’t want to include default values for our
data so let’s go with the first implementation. Let’s try out our new struct. In your
preferred terminal, navigate to your development directory and start up an IEx
session while also compiling our new source file. If we pass iex the -r flag and a
file name, it will compile that file and make it available in the session.
$ iex -r auction.ex ❶
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10]
[hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.5.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> %Auction.Item{}
%Auction.Item{description: nil, ends_at: nil, id: nil, title: nil}
❶ You can see all the different available flags for iex with iex --help.
You can see that we now have the first building block of an auction item for our
application. In Listing 4.1, let’s get back into your iex session and try adding some
data to our new struct.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
82
❶ In order to save our fingers some extra typing, we can alias Auction.Item so that we only have to
type Item from then on.
❷ We can modify information by using the special Map modifier of | as long as the key already exists in
the Map being modified.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
83
Figure 4.5. Our fake database will contain a static list of Items
Listing 4.2 Auction module to help retrieve data from the database
defmodule Auction do
alias Auction.{FakeRepo, Item} ❶
@repo FakeRepo
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
84
def list_items do
@repo.all(Item)
end
def get_item(id) do
@repo.get!(Item, id)
end
def get_item_by(attrs) do
@repo.get_by(Item, attrs)
end
end
We will shortly also define a module called Auction.FakeRepo that will hold our
data for this particular implementation. Instead of
calling Auction.FakeRepo directly in each function, we can set our preferred Repo
at the top of the file as a module attribute (@repo FakeRepo). This will make it
easier to change in the future when we move to an actual database implementation.
When that happens, instead of changing every function’s call
from Auction.FakeRepo, we only have to change this one line to point to the new
real repo. If you need to change repos again in the future, it will also be very easy
to make that change with one line.
Module attributes
Module attributes in Elixir can serve a few different functions, but here (@repo) it is used as a
form of a constant value. When the module is compiled, the value of the module attribute is
read and inserted into any code that is referencing the attribute. Since it is compiled, it cannot
be set or changed at run-time.
Module attributes can also be used as file annotations such as documentation or as
temporary storage. To read more about module attributes and their uses, check out the Elixir
guides page on the topic. 16
Another question you may have after looking through Listing 4.2 is why are we
passing Item as the first argument to each function in @repo? While we currently
only have Items in our database, we’ll soon have other things like bids or users that
we’ll need to store and retrieve. When we pass Item in as the first argument, it lets
the repo know which database table (set of data) we are requesting.
Listing 4.2 shows a good way to isolate your business logic from your database
calls. You’ll see files like this used as a kind of standard in Phoenix — a border
between two different parts of your app that contain completely differing domain
knowledge. In this case, as you’ll see below in listing 4.3, the function names we
16
Elixir guides page about module attributes: elixir-lang.org/getting-started/module-attributes.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
85
use aren’t very different for the Auction module and our FakeRepo module. This is
mostly a product of the fact that we aren’t actually using a real database yet but are
attempting to model one that the Ecto package will later provide for us.
Until we bring in Ecto to get us a real database and repo, let’s continue on with
listing 4.3 and add our fake repo. Our fake repo will act like a database in that it
will contain a list of Items that we want to have up for sale. Beyond that, we’d like
to have a way to get to that data and manipulate it if necessary. Our goal with this
particular module is to act as our in-memory store of information that we are
calling our "database". First, we’ll define the list of Items that exist in our database,
then we’ll create the functions necessary to handle the function calls we created in
Listing 4.2.
As in the first listings in this chapter, we will be adding this module into the same
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
86
file (auction.ex). Later in the chapter, we will break the modules out into separate
files and talk a bit about project file structure.
defmodule Auction.FakeRepo do
alias Auction.Item
@items [ ❶
%Item{
id: 1,
title: "My first item",
description: "A tasty item sure to please",
ends_at: ~N[2020-01-01 00:00:00]
},
%Item{
id: 2,
title: "WarGames BluRay",
description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35]
},
%Item{
id: 3,
title: "U2 - Achtung Baby on CD",
description: "The sound of 4 men chopping down The Joshua Tree",
ends_at: ~N[2018-11-05 03:12:29]
}
]
❷ The all/1 function will be called to return all the Item s in our "database". All it does is return
the @items module attribute…
❶ …which is a List of Items.
As before in Listing 4.2, we will be using a module attribute in order to store our
list of items as a constant that will be evaluated at compile-
time. Auction.FakeRepo.all/1 takes the list of items we provided in that attribute
and just returns it to the user. Note that we utilized pattern matching in our function
definition. This particular function will only be called if the first argument to
allis Item. We can then leave room for returning other types of data in the future.
You can imagine having all(User) and all(Bid) as our application grows.
What is a Repo?
You’ve seen the term Repo used enough by now that if you are not familiar with it, you are
really starting to wonder what it is. Very simply, the Repo (or repository) is a mapping to a data
store. In our case, the data store is our static @items module attribute but normally—and for
in the next chapter on—it maps to an actual database.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
87
The database is the component that actually stores the data. The Repo will be our
application’s gateway to the data inside the database. Our Repo will translate what we’d like
from the database from a defined API into "database speak". This is really neat because as
long as an adapter is available, our Repo can talk many different database "languages" but
the API for our application to talk to the Repo remains the same.
Now that we have a fake database with data, we can attempt to use it. Since we
wrote a function in Listing 4.2 to be a boundary between our application, let’s use
that. That way we can verify that the our modules are working well with each other
and also so we can get used to calling our boundary functions instead of working
directly with our repo. In Listing 4.4, we use this new fake database layer of our
application in iex. Whenever you modify a file and would like to have those
modifications inside an already-running session of IEx, you can run c
filename.ex to recompile it and redefine the module.
[Auction.FakeRepo, Auction.Item]
iex(6)> Auction.list_items
[%Auction.Item{description: "A tasty item sure to please",
ends_at: ~N[2020-01-01 00:00:00], id: 1, title: "My first item"},
%Auction.Item{description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35], id: 2, title: "WarGames BluRay"},
%Auction.Item{description: "The sound of 4 men chopping down The Joshua Tree",
ends_at: ~N[2018-11-05 03:12:29], id: 3, title: "U2 - Achtung Baby on CD"}]
Tada! We can now list all the items in our "database". Let’s make it just a little bit
more useful. We’ll definitely want to do at least two more things before moving
on:
1. Get a specific Item based on its id
2. Get a specific Item based on other identifying information such as its title or
description
Let’s build those two things out in our FakeRepo.
4.1.3 Getting an Item by id
When we want to get a specific Item, we will most of the time know the id of that
Item. For example, a webpage request might be something
like myawesomeauction.com/items/8746 and 8746would be the id of the Item
loaded. What we need our finder to do is to loop through the List of @items and
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
88
Thankfully, we can use Enum.find/2 for exactly this purpose. Enum.find/2 expects
two things to be passed in:
1. A collection of things to iterate over.
2. A function to call for each thing in the collection. The function should take one
argument and that will be the thing in the collection currently being examined.
def get!(Item, id) do
Enum.find(@items, fn(item) -> item.id === id end)
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
89
Enum.find/2 will loop through each item in the collection until it finds one for
which the passed function returns true. Our function simply compares the id of
the item currently being examined with the id passed into the get!/2 function
itself. Here it is in use (don’t forget to recompile auction.ex with c
"auction.ex" before trying to use our new function). We’ll try using it directly
with the FakeRepo and then again with our boundary Auction module.
iex> Auction.FakeRepo.get!(Auction.Item, 2)
%Auction.Item{description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35], id: 2, title: "WarGames BluRay"}
iex> Auction.get_item(2)
%Auction.Item{description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35], id: 2, title: "WarGames BluRay"}
As expected, get_item/1 returns the Item that has the id of 2, which is what we
asked it for.
4.1.4 Getting an Item by other information
While we will know the id of the Item to look up a lot of times, we still need to
account for getting an Item by other identifying information. For example, what if
a user searches for an Item by title? Handling user queries can be a complex
subject, but we can account for exact matches with our a function. We’ll
create get_by/2 to handle finding an Item by a Map of matching information.
Before we add any code, let’s think about what it is we want to accomplish with
this search.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
90
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
91
Figure 4.9. The flow chart get_by/2 uses to determine if it has found the Item
Now that we’ve decided on our search strategy, we can start adding some code.
Add the code in Listing 4.5 to your Auction.FakeRepo module.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
92
I understand what you might be thinking: "WHAT?!". This is the most complex-
looking function we’ve seen yet but don’t let the nesting scare you off. If we can
peel the layers back one at a time, it is actually not that hard of a function to grasp.
Before reading further, try to understand on your own what this does. The names of
the functions you haven’t seen yet should give you good clues as to what is going
on.
OK, got an idea (or just want to get on with it)? Let’s peel it back.
• You’ve already seen Enum.find/2 in use in
our Auction.FakeRepo.get!/2 function. This time, our function that examines
each item in our @items list is just a bit more complex.
• Enum.all?/2 is a lot like Enum.find/2 in that it takes a collection of things and a
function to run on each of them. The difference is that instead of returning the first
thing that returns true, it runs through the entire collection until either the passed
anonymous function returns false or every run of the function has returned true.
Put another way, Enum.all?/2 returns true if, when passed every thing in the
collection, the provided function returned true; or false if even one thing
examined returned false. It essentially asks if all of every run of the function
provided can be evaluated to `true`.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
93
• Into Enum.all?/2 we pass all the keys of the attrs Map passed in to get_by/2.
This allows us to search for one thing (like %{title: "WarGames BluRay"}) or
multiple things at the same time (like %{title: "WarGames BluRay", id: 2}).
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
94
Figure 4.11. We iterate through all our Items until we find a match
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
95
• For each of those keys in map, we see if the examined item has that key and if it
does, if it matches the map 's value for the same key.
iex> Auction.get_item_by(
...> %{description: "A tasty item sure to please", id: 1}
...> )
%Auction.Item{description: "A tasty item sure to please",
ends_at: ~N[2020-01-01 00:00:00], id: 1, title: "My first item"}
defmodule Auction.FakeRepo do
alias Auction.Item
@items [ ❶
%Item{
id: 1,
title: "My first item",
description: "A tasty item sure to please",
ends_at: ~N[2020-01-01 00:00:00]
},
%Item{
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
96
id: 2,
title: "WarGames BluRay",
description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35]
},
%Item{
id: 3,
title: "U2 - Achtung Baby on CD",
description: "The sound of 4 men chopping down The Joshua Tree",
ends_at: ~N[2018-11-05 03:12:29]
}
]
def all(Item) do
@items
end
4.3 Summary
In this chapter you learned
• Phoenix is not your application, but just one entry point into it.
17
Check Agents out. They are amazing. hexdocs.pm/elixir/Agent.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
97
• Your application can have many various entry points, such as the web, hardware,
the command line, and many others.
• Because of the possibilities of various entry points, it is a good idea (and standard
practice) to try to keep your business logic separated and isolated as much as
possible. Don’t let things like computation get mixed in with things like the
workings of the database.
• Some nested enumerators can be intimidating when you look at them, but
breaking them down into a flowchart can make them seem very approachable.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
98
Up until now, we’ve been writing and using Elixir code in an IEx session or, as in
the case of Chapter 4, in a single file. The majority of your Elixir and Phoenix
projects will not be organized like this, however. As you application grows in
complexity, it becomes imperative that some sort of organizational structure is
implemented to keep things manageable.
In this chapter, we will be covering the structure of a typical Elixir application.
Along the way, we’ll utilize the mix utility to automate a lot of the tasks that would
be non-trivial if done manually. Finally, we’ll also utilize the hex package manager
to bring in third-party tools and libraries into our application. All of these things
will help us get our application set up in the right way before we tackle using a real
database in Chapter 6.
Figure 5.1. The basic directory and file structure of an Elixir project
Along with the standard directories, you’ll also typically find a file named mix.exs.
This file can be considered to be the brain or mothership of your application. We’ll
be diving into this file in the next section.
While simple applications are typically structured in this way, we will be creating
multiple applications—one for the business logic that we’ve already been working
on and one for our Phoenix application. However, we’ll be creating another
skeleton application along with those two to tie them together. This kind of pattern
is called an "umbrella application".
5.1.1 Using mix to create a new application
You may be thinking, "Oh, great. Every time I want to start a new Elixir
application, I have to remember this standard structure." If you are indeed thinking
that, I’ve got some great news for you! You don’t have to remember that at all! All
you have to remember is mix. Like iex, the mix utility is installed for you when you
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
100
install Elixir. And, luckily for those of us who have to remember too many other
things, it has an excellent help system (again, just like IEx).
If all you remember is the command mix, you can figure out how to get the rest.
Listing 5.1 jumps directly into using the mix tool, even though we don’t know
anything else about it. Thankfully, it will tell us all the different things it can do for
us! I run mix in my terminal:
> mix
** (Mix) "mix" with no arguments must be executed in a directory with a mix.exs
file
Examples:
You can see that the second example tells us how to create a new Elixir project!
That sounds helpful. But what if we want to know more? You can see from Listing
5.1 that it even tells you how to get more help. If we enter mix help new it will give
us all the help documentation for how to use that specific task (Listing 5.2).
Listing 5.2 Using mix help new to learn about creating a new Elixir project
mix new
Creates a new Elixir project. It expects the path of the project as argument.
A project at the given PATH will be created. The application name and module
name will be retrieved from the path, unless --module or --app is given.
An --app option can be given in order to name the OTP application for the
project.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
101
A --module option can be given in order to name the modules in the generated
code skeleton.
## Examples
Is equivalent to:
You can see from Listing 5.2 that there are a number of options we can specify
when creating a new Elixir application. If we wanted to create a new Elixir
application, we’d definitely want to use this tool as the first step. mix new will
generate not only the standard Elixir application directory structure, but also give
you some files that are starting points for your application.
Before we create an application for our Auction backend, let’s use the tool on a
throwaway project just to see how it works. Since the first option to mix new is the
path of the project as well as the application name, you’ll need to avoid most
special characters. Like in variable names, we’ll need to avoid using dashes but can
use underscores. If you are like me and prefer dashes in your project directories
instead of underscores, we can specify an alternate name for the application itself
(with --app) that utilizes underscores instead. Imagine you want to create a
Facebook replacement named FriendFace 18 . In a temporary directory somewhere,
run the following (Listing 5.3):
18
Thanks to the show The I.T. Crowd for the name inspiration
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
102
cd friend-face
mix test
You can see that not only did the mix new command create our directory structure,
but also gave us a README, a .gitignore file for ignoring files in our git repo if we
were to create one, a config file, a skeleton module for our friend_face application,
and even a test file. You can peek into each of these files and see that they are not
just empty files. They are files that have actual use as they are and are very helpful
as you are getting started. If you follow the instructions at the end of Listing 5.3
and run mix test in your project directory, you’ll even discover that it already has
a passing test!
After you’re done exploring your next million-dollar idea application, feel free to
delete the directory and all the files it generated for us. We won’t need them.
5.1.2 Generating our Auction umbrella application
Now that we’ve discovered a little bit about how mix works, let’s create an
application for our Auction site. We will use the mix new command like before
except this time we’ll want to do it in a non-temporary directory you use for your
projects. The first thing we need to generate is the umbrella itself. We will then
create an Auction application inside that umbrella.
Creating an umbrella application is very simple: we pass the --umbrella flag to mix
new. Let’s call our umbrella structure auction_umbrella to make it recognizably an
umbrella structure. I get Listing 5.4 in my terminal when I run that command.
cd auction_umbrella
cd apps
mix new my_app
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
103
This kind of structure allows us to create sub-applications that are all contained
under the umbrella application. We don’t really need to modify anything it
generated at the moment, but the output does give us a good clue as to what we
should do next. All the sub-applications of an umbrella application are stored in
an apps directory (auction_umbrella/apps in this case). We can then cd into that
subdirectory and run mix new app_name to generate a sub-application.
We’ll name our application "Auction". Catchy, huh? Cd into
auction_umbrella/apps and type mix new auction --sup in your terminal. What
does the --sup do? It creates an application skeleton with a supervision tree in
mind. This won’t make much difference now, but will as we go on (we’ll finally
utilize that supervisor in Chapter 7). You should see output like Listing 5.5.
cd auction
mix test
The rest of the work we’ll be doing in this chapter will strictly be inside
the auction_web/apps/auction directory.
THE MAGIC MIX.EXS FILE
For any Elixir application, the mix.exs file is pretty magical. In it, you’ll define
things like the current version of your application, your application’s name, the
version of Elixir it runs on, any outside dependencies that your application requires
in order to run, and any additional applications or supervisors that also need to be
started when your application is started.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
104
If we take a peek inside the mix.exs file that our mix new task generated for us, you
can begin to see just how helpful that mix task is. Listing 5.6 shows what ours
looks like.
defmodule Auction.MixProject do
use Mix.Project
def project do
[
app: :auction,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
105
Most of the time, we will be writing .ex files as we want all the benefits of compiled code
(compiler optimizations, speed, etc.). .exs files, since they have to be interpreted, are slower
to run (since they have to go through parsing, tokenization, etc.). They are, however, a flexible
choice when you don’t require compilation. For example, the mix.exs file in an Elixir project
and all test code files are .exs files.
project
The project function (shown isolated in Listing 5.7) defines the top-level details of
your Elixir application including:
1. The app name as an atom (auction), the current version of the application (0.1.0)
2. The Elixir version(s) our app will run on (~> 1.6 19 )
3. Configuring the application so that if it goes down either by failure or successful
shutdown, other applications that your application started as dependencies will
also be shutdown (this is set as true in the production environment by the return
of the comparison of the current Mix environment to the :prod atom.)
4. A list of dependencies. This is expecting a list of tuples containing external
package names and version numbers but for simplicity sake, it is set up by default
to rely on a private method defined later named deps. We will cover
the deps function shortly.
def project do
[
app: :auction,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
There are actually many more options that can be set here for your application.
Elixir itself has a few more (which you can read about in it’s excellent
documentation page for Mix.Project 20). Other dependencies of your application
may have their own options that will need to be set here as well.
application
The application function (Listing 5.8) is a pretty simple one in terms of what is
19
The ~> means the following version number can be incremented by the last-given dot in the version. So in this case, we
could run versions 1.6 through 1.∞ but not versions before1.6 or 2.0 and later.
20
hexdocs.pm/mix/Mix.Project.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
106
generated, but the functionality it provides is big. In simple terms, it is what tells
the compiler that you’d like to generate an .app file for your application.
According to the documentation (which you can read by following the directions in
the comment about the function declaration # Run "mix help compile.app" to
learn about applications.):
An .app file is a file containing Erlang terms that defines your application. Mix
automatically generates this file based on your mix.exs configuration.
def application do
[
extra_applications: [:logger],
mod: {Auction.Application, []}
]
end
Since an Elixir application really compiles down to Erlang code to run on the
BEAM virtual machine, we need to somehow get our Elixir code into Erlang. This
function provides additional instructions to the compiler about how to do that. The
most used options are regarding additional, external applications that need to be
started along with your application. Any application name (in :atom form) you
provide to extra_applications will be ensured to start before your application so
that if your application depends on any of those functions, it will be ready to accept
commands when your application needs them. By default, Elixir’s built-
in Logger application is started up to provide logging functionality.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
107
Figure 5.2. All of your files, plus all the dependencies are compiled into files that can run on the
BEAM VM
Elixir has the ability to know if third-party applications your application depends
on need to be started up. If they do, they will be started automatically without you
having to tell Elixir to explicitly do so. If an application needs to be included in
extra_applications, the README for the library will let you know. If it doesn’t
mention the requirement, you can bet that it either is started automatically or it
doesn’t need to be started at all.
Finally, the mod key is an application callback. Any module you specify in this list
(along with a list of arguments which is empty) will be called when the main
application starts. The callback expects Auction.Application.start/2 to be
defined. This is indeed the case for us since we generated the application with the –
sup flag.
deps
The final function that we see in our generated mix.exs file is deps. This is where
you will list all the external applications, packages, and libraries your application
depends on. You can see in Listing 5.9 that we currently rely on no external
packages.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
108
The format used to specify dependencies is the package name, plus the version
number(s) accepted, in a tuple (like {:package_name, "~> 1.0"}). For the version
requirements, there are a few different options you can specify. For more details,
check the Version documentation. 21 We will be using deps extensively the
coming sections.
Other options
There are a handful more options and functions you can use in your Mixfile that
aren’t generated in a skeleton application nor are we specifying in ours. If you’d
like more information on these options, check out the
22
Mix.Project documentation.
We therefore should create three different files—one for each module. Most of the
time, our application/library code should go in the lib directory that mix new
already generated for us. So we’ll start there.
The mix new task created a file in the lib directory of our application
named auction.ex. In that file, paste all the code that made up our Auction module.
You’ll notice that mix new auctiongenerated that file already and created a hello
function too. It is safe to overwrite all the contents of the generated file with our
module code. Listing 5.10 contains that module.
defmodule Auction do
alias Auction.{FakeRepo, Item}
@repo FakeRepo
def list_items do
@repo.all(Item)
end
def get_item(id) do
@repo.get!(Item, id)
end
def get_item_by(attrs) do
@repo.get_by(Item, attrs)
end
end
For our Auction.Item module, we’ll need to create another new file. This time, a
file does not already exist for us to overwrite—we’ll need to create our own. While
you could create this file in the top level of the lib directory, it is standard practice
to match your directory structure to the namespacing of your module. What do I
mean by that? Item is namespaced underneath Auction in our module
name Auction.Item. Therefore, a good rule of thumb is to create
an auction subdirectory and have item.ex live in there.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
110
Paste the entirety of the Auction.Item module code info a file named
lib/auction/item.ex (or whatever you may have chosen). Listing 5.11 reflects
the contents of that file.
defmodule Auction.Item do
defstruct [:id, :name, :description, :ends_at]
end
Finally, we are left with our Auction.FakeRepo code. Like Auction.Item, we will
match the module namespacing with our directory structure. Paste the
Auction.FakeRepo module code from Chapter 4 into the file
lib/auction/fake_repo.ex, matching Listing 5.12. Note that another good rule of
thumb for file names is when you have a CamelCased module name, downcase the
name and use an underscore before the previously-capitalized letters. Therefore,
PhoenixInAction would become phoenix_in_action and FakeRepo becomes
fake_repo.
defmodule Auction.FakeRepo do
alias Auction.Item
@items [
%Item{
id: 1,
title: "My first item",
description: "A tasty item sure to please",
ends_at: ~N[2020-01-01 00:00:00]
},
%Item{
id: 2,
title: "WarGames BluRay",
description: "The best computer movie of all time, now on BluRay!",
ends_at: ~N[2018-10-15 13:39:35]
},
%Item{
id: 3,
title: "U2 - Achtung Baby on CD",
description: "The sound of 4 men chopping down The Joshua Tree",
ends_at: ~N[2018-11-05 03:12:29]
}
]
def all(Item) do
@items
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
111
But since Elixir is a compiled language, we’ll need to compile our application
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
112
The first time we run this, it will go through our entire application looking for .ex
files and compile them into something that can run on the Erlang VM (using
the mix.exs). You’ll notice that it compiled "3 files". Guess which three those
are—the three files we just created in the previous section! And since we got no
warnings during compilation, it tells us that it successfully generated our auction
application.
If you run mix compile again right away, you get no output from the compiler. This
is because Elixir’s compiler is smart enough to recognize that no changes have
been made since the last time we compiled and therefore nothing needs to be done!
In fact, it is smart enough to know not only that some files changed, but which files
changed. If a file didn’t change, it won’t be recompiled (unless it is affected by a
file that did change)! This will save you lots of time as you build up your app over
time.
RUNNING OUR AUCTION APPLICATION FOR THE FIRST TIME
So we now have a compiled application. How do we run it? At this point in time,
we have created no graphical interface to our application— only the code required
to play with our fake data through our API. Because of that, the only way we can
interact with our program at the moment is via IEx. However, we will be starting
our new IEx session a little differently than we have in the past. If you recall, in
Chapter 4, we would start IEx while requiring a specific file (our application code,
auction.ex). Now, we no longer have a single file. Instead, we have a Elixir Mix
application.
To start up an IEx session and require an entire Mix application to be brought in as
well, you can use the following command: iex -S mix.
> iex -S mix
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10]
[hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.5.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
113
If we had not already compiled our application, it would compile here before
starting IEx. But since we previously did compile it, it starts right up and includes
all the files of our Auction application. Listing 5.13 shows an example of testing
out the API we created.
It’s all working! We didn’t have to manually require any files, we didn’t have to
specify which files we were going to use, it all just worked! This is the "magic" of
an Elixir application. I say "magic" because when you move from using individual
source code files and getting caught in a mess of file requires and trying to get
everything working together to having something that just works right off the bat is
very refreshing. It again makes things very easy and is another example of how
Elixir makes developer happiness and productivity real concerns.
Hex’s front end (at hx.pm) or use the mix task mix hex.search PACKAGE. In Listing
5.14, you can see the output from using the mix task.
All of those packages listed above have something to do with the word "react".
"react" could be in the package name, in the description, or potentially other places
and they are ordered loosely by popularit
But sometimes, you do know what package you need and you just need to know
what the latest version number is. In those cases, I’ve found the mix task to be the
fastest way to retrieve that information. For example, we know we will need Ecto
in our application. Since we already know the package name, the only further piece
of information we require is the latest version number (unless we know of a
specific one already we want to depend on). In this case, let’s search for ecto with
the mix task. Listing 5.15 shows that search.
We can see in Listing 5.15 that the latest version is 2.2.7. Unless you have a
compelling reason to use an older version of a package, it is generally a good idea
to use the latest. So we’ll depend on at least version 2.2.7 in our application.
To specify this version in our deps function in mix.exs, we specify the package
name and the version requirements. To specify the exact version number of 2.2.7,
our deps function would look like Listing 5.16:
Listing 5.16 Specifying our first dependency
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
115
defp deps do
[
{:ecto, "2.2.7"}
]
end
Version examples
1) > 2.2.7 means give us any package as long as it is above version 2.2.7. This
does not include version 2.2.7 itself so in the case of Ecto as of writing, hex would not be
able to find a suitable version for the pckage.
2) >= 2.2.7 means give us any package equal to or above 2.2.7. This and the above
include any patch, minor, and potentially breaking major releases, so use this carefully.
3) < 3.0 means use any package found as long as it isn’t version 3.0 or above. This could
also mean that someone who comes along later (including you) would find that
version 0.0.4 can be used just as well as version 2.2.7. Use this with caution as well.
4) >= 2.2.7 and < 2.3.0 means any package that is from the major 2 minor 2 releases
as long as it is equal to or above 2.2.7.
5) ~> 2.2.7. Number 4 above is so common that a special symbol is used to denote this
kind of requirement. ~> MAJOR.MINOR.PATCH means use any version that is beyond the
PATCH version but not incrementing the MINOR version. ~> MAJOR.MINOR means take
any MINOR or PATCH version up to the next MAJOR version.
Regardless of the version requirements, hex will attempt to fetch the latest, most
recent package version that meets the requirement(s) specified. With that in mind,
let’s make sure to have hex include any bug fixes in the version 2.2 minor branch
by using the ~> 2.2.7 format. Listing 5.17 shows what your deps function should
look like.
defp deps do
[
{:ecto, "~> 2.2.7"}
]
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
116
Since we are using Postgres, modify your deps function in mix.exs to include the
dependency (postgrex) like in Listing 5.18. If you’d rather use a different
supported database for your application, be sure to use the correct adapter.
defp deps do
[
{:ecto, "~> 2.2.7"},
{:postgrex, "~> 0.13.3"}
]
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
117
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
118
Figure 5.5. hex manages the sometimes complex relationships between your apps'
dependencies
The output you see is Hex fetching those required packages and putting them into
the deps directory of your application. The next time you compile your application
(whether explicitly via mix compile or by starting your application like iex -S
mix), those dependencies will be compiled into your application
for your application code to take advantage of.
We’ll be diving more into the usage of Ecto in the next chapter, but to demonstrate
how we can use external dependencies with little fuss, let’s temporarily add
the UUID package to our applications dependencies. Add the dependency
requirement to your mix.exs file as in Listing 5.20.
defp deps do
[
{:ecto, "~> 2.2.7"},
{:postgrex, "~> 0.13.3"},
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
119
Be sure to also run mix deps.get again to fetch the newly added dependency. Now
let’s use it in our application’s environment via an IEx session. UUID is a package
that allows you to easily generate Universally Unique IDentifiers. We will simply
test it’s ability to do so within our own application’s environment in Listing 5.20
by using it’s uuid4/0 function to generate a version 4 UUID. We could even use it
to generate auction titles for us.
iex(1)> UUID.uuid4
"40a6eb21-4c85-46ac-a550-e18130187aee"
defp deps do
[
{:ecto, "~> 2.2.7"},
{:postgrex, "~> 0.13.3"}
]
end
Once you’ve done that, you can again run mix deps.get and it will update
your mix.lock file which keeps track of all your dependencies. But if you look in
your deps folder for your project, you will still see a directory/folder for uuid. If
you’d like to get rid of that, you can simply manually delete it from the directory or
you can use the mix deps.clean uuid command in your terminal. It will determine
that it is no longer needed and remove the package’s code from your hard drive.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
120
5.4 Summary
In this chapter you learned
• The majority of Elixir projects aren’t contained within a single file, but within
multiple files within an application structure.
• Use mix new to start a new Elixir project. That single task will generate the initial
folder structure and files to get you started.
• The mix.exs file contains important information regarding the configuration of
your application.
• Hex.pm is the Elixir package manager and has many helpful third-party modules
and applications to help you create your application. Your application’s package
dependencies are stated in the mix.exs file.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
121
Up to this point, the flashiest thing we have to look at for our Auction application
is IEx. While IEx does the job, is full-featured, and generally looks nice, the UI
isn’t going to raise millions of dollars in seed money for your auction start-up. In
order to get users on our site and bids being made on the Items in our database,
we’ll need to get a web interface so that they can interact with the data.
It’s time to bring in Phoenix.
The first thing we want to do with Phoenix (and what we’ll be covering in this
chapter) is for it to list the Items in our database. Creating, editing, and deleting the
Items and bidding on the Items will all come later. By the end of this chapter, you
should be able to get something like Figure 1 in your web browser.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
122
23
hexdocs.pm/phoenix/installation.html#phoenix
24
nodejs.org/en/download/
25
hexdocs.pm/phoenix/installation.html#node-js-5-0-0
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
123
These are some of the mix tasks we will be using while we are building our
Phoenix application. For now, though, we are keeping it incredibly simple.
mix phx.new.web
It expects the name of the otp app as the first argument and for the command to
be run inside your umbrella application's apps directory:
$ cd my_umbrella/apps
$ mix phx.new.web APP [--module MODULE] [--app APP]
## Examples
Is equivalent to:
Supports the same options as the phx.new task. See Mix.Tasks.Phx.New for
details.
The Phoenix/web portion of our application will not be using a database directly.
Why? Because we want to keep our domain logic separate from our web interface.
All the database interactions for our Items will be handled in the Auction
application we started in Chapter 5. Since that is the case, we will not need Ecto in
this application.
Figure 2 shows what your current umbrella application directory listing looks like.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
124
Figure 6.2. Our umbrella app directories before we create a Phoenix app
You will see a lot of output scroll by as the mix task generates skeleton files for
your application. Once those files are created, you are greeted by a prompt asking
if you’d like to Fetch and install dependencies? [Yn]. You can go ahead and
type Y (or just hit return) and it will fetch all the dependencies you’ll need for our
Phoenix application. Again, more text will scroll by as your dependencies are
fetched from hex.pm.
6.2.1 Running our server for the first time
Once your dependencies are fetched, you are finally greeted with some good news
(as in Listing 6.3).
$ cd auction_web
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
125
We do want to start our application, so let’s follow their suggestion and go into our
new auction_web directory and run the mix task to start our server (mix
phx.server). As soon as you do so, you’ll see yet more debug text scroll by as your
application is compiled and the server is started up. As soon as you see something
like Listing 6.4, you’re serving webpages!
Now for the moment of truth! Fire up your favorite web browser and navigate
to 0.0.0.0:4000. You should see something similar to Figure 1. You’re now serving
up web pages through Phoenix on your computer!
What is 0.0.0.0:4000?
You may be used to entering domain names into your web browser, but numbers and colons
may be foreign to you. So what does this address (0.0.0.0:4000) mean?
The address can be broken down into two sections:
1) The IP address
2) The port number
The IP address is the address of the computer that is hosting the content. When you enter a
domain name into your browser, that domain name is actually like an alias for an IP address
which your browser actually calls. 0.0.0.0 is the IP address of your local computer.
The port is like the tunnel that the connection uses when making requests. The Phoenix
web server listens for connections on port 4000, so we make our browser go there explicitly.
When you don’t enter a port number, a browser uses port 80 by default for unsecure http
traffic.
Put the two together, and you have IP_ADDRESS:PORT, or 0.0.0.0:4000 in our case.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
126
Figure 6.3. If everything has gone correctly, this is what you’ll see—a webpage being served up
on your local computer through Phoenix
In order to stop the server, you can hit CTRL-C twice in the terminal that is running
the server.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
127
If you have been following along with us in Chapters 4 and 5, you’ll have built an
Auction application that will contain all the business logic of our site. When we left
it, it was using a fake Repo to serve up hard-coded items. But the nice thing
about how we created it is that we don’t care what repo it uses or where it gets its
items or anything about its logic—we just want the items. This is that point where
having strong boundaries between your interface (like a Phoenix web application)
and your business logic has huge benefits. As long as our business logic provides
us an API to use to get the information we are after, let it decide where and how to
get the data. We want our web interface to be as naive as possible about the inner
workings of the business logic.
We did build a
simple API to get the items from our database:
Auction.list_items(). We can bring in that other application and use it in this
application. How can we do that? Specify it as a dependency in our mix.exs file!
The mix.exs file contains a function named deps that contains the list of
dependencies for our application. Right now, it contains the default dependencies
for a Phoenix application generated with the phx.new.web generator.
All we need to do is tell this web application about our Auction application. A
dependency declaration typically contains the name of the application required,
plus the version number(s) that would satisfy our dependency. But there are other
ways you can declare a dependency. One of those ways is via the path option to
direct it to a directory machine; another via the github option to specify a git repo
on Github. Instead of a version, we can tell it where to find the application we
depend on. We’ll use that to bring in our Auction application. Yet another option is
to tell it that it is available as another app within the same umbrella. This last
scenario is the one we have going on, so we’ll use the in_umbrella option.
Add {:auction, in_umbrella: true} to the list of dependencies (don’t forget the
comma at the end of the line above it if you are adding it to the end of the list). See
Listing 6.5 as a guide. Once we have added that dependency, we can now use
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
128
our Auction module in our new AuctionWeb Phoenix application inside the
umbrella.
defp deps do
[
{:phoenix, "~> 1.3.1"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:auction, in_umbrella: true} ❶
]
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
129
defmodule AuctionWeb.PageController do
use AuctionWeb, :controller
You can see that the only function defined in the controller right now is that index
function that the root route points to. In the index function, we’ll need to do two
things.
1. Get the list of Item s, binding it to a variable; and
2. Pass that list to the view template so we can render the Items.
A controller’s functions are just like any other Elixir functions definitions—you
can call external module’s functions, store the results of those in a variable, and
transform data. We wrote our Auction module’s public-facing API so that we can
easily get a list of Items with Auction.list_items/0. We’ll use that function and
capture the result. Our modified index function looks like Listing 6.7.
❶ Our addition
We now have the list of Item s in the items variable so step 1 is done. Step 2 is to
pass the list to the view template. The second line of the index function is a call
to Phoenix.Controller.render/2 and it takes two parameters: the conn which is
the Plug.Conn struct that is passed through the application from initial connection
through rendering the view, and the template name Phoenix should render for this
route ("index.html"). Figure 5 gives an overview of how the group of items moves
from a request from the controller, through the Auctionmodule, to
the Auction.FakeRepo module, and then back to the controller to be finally passed
off to the view/template.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
130
Figure 6.5. The flow of items from our fake repo to the view/template
If you are wondering why the generated code in the controller calls render directly
instead of its full name of Phoenix.Controller.render, take a look at line 2. It
reads use AuctionWeb, :controller. That line does a whole bunch of stuff behind
the scenes, but the thing we’re interested in is the fact that it calls import
Phoenix.Controller. That allows us to use any Phoenix.Controller function in our
module without having to type the full name.
Inside Phoenix.Controller, there is also a `render/3 which accepts a third
parameter: assigns (variables) to be passed through to the view. All we need to do
to utilize this other renderfunction is tack on our items list.
❶ Our addition.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
131
Now the view has access to our items through a @items variable.
THE TEMPLATE
The final step for rendering our list of Items in the browser is to actually utilize the
@items variable that we set up in the controller. If you’ll recall, the second
parameter of the Phoenix.Controller.render/3 function on the last line of index/2
is "index.html". That defines the template Phoenix will use to render the web page.
Phoenix will look for a file named "index.html" in the page (for PageController)
subdirectory of the templates directory. The full page of the template file will be
auction_umbrella/apps/auction_web/lib/auction_web/templates/page/index.htm
l.eex.
This file is an .eex file which means it will be preprocessed by Phoenix before the
final HTML is output. This means we can use some Elixir inside the template file
itself. Everything inside <%=and %> tags will be processed by Elixir and the result
rendered in its place. You can also enclose Elixir code within <% and %> if you
don’t need to render the results.
If you open up the index template file, you’ll see some straight HTML that
provides the "Welcome to Phoenix" banner and links to resources and help. We can
safely delete all this HTML and replace it with our own. For our purposes, a simple
unordered list (<ul>) will be enough to list our Items. We can open up the
unordered list, but we’ll still need to loop through our list of Items and output a list
item (<li>) for each entry.
There is an idiomatic way to render each item in a list like this—for. for is a
"comprehension". According to the docs, "comprehensions allow you to quickly
build a data structure from an enumerable". Figure 6 has a bit of a breakdown on
the portions of the for comprehension as we will be using it.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
132
The full documentation 26 contains some pretty neat examples and shows how
useful for can be, but we will use it pretty simply. Listing 6.9 shows how we can
use for to loop through the Items in @items and output a <li> tag for each one.
<ul>
<%= for item <- @items do %>
<li>
<strong><%= item.title %></strong>: <%= item.description %>
</li>
<% end %>
</ul>
This takes our collection of @items, takes each one at a time, and binds the
currently-considered item in item inside the do/end block. Inside that block, we
render an opening <li> tag, render the title of our item with <%= item.title %>
inside a <strong> HTML tag, then also render the description of our item with <%=
item.description %>. Remember, anything inside the <%= %> tags will be evaluated
as Elixir code and the result will be rendered. This allows us to get data from the
database, transform the data in any way necessary, then render what we need.
If you start your server back up (if you killed it earlier) with mix phx.server in the
top level of your auction umbrella application and then point your browser
26
hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
133
to 0.0.0.0:4000 again, you should see a list of your own like figure 7.
Figure 6.7. Listing Items from our Auction’s fake repo
You’ll notice that there are some things on the web page that we didn’t add. In
particular, there is a large "Phoenix Framework" banner image and some baseline
css/styles. If you remember back to Chapter 3, we briefly introduced the idea
behind the layout template and the page template. We edited the page template in
this chapter, but the layout template wraps every page template. The banner and the
rest of the wrapping HTML are contained in a "layout" template
named app.html.eex. We’re not going to cover it here, but if you’d like, go ahead
and take a peek inside that file to see how it works.
In the next chapter, we’ll bring in a real database and start persisting items into it.
6.4 Summary
In this chapter you learned
• mix phx.new generates a brand new Phoenix application
• mix phx.new.web generates a new Phoenix application inside an existing umbrella
application
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
134
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
135
Our Auction application is at a point where we have set up an API to query for
Items in a fake database and we now know how to bring external package
dependencies into our application. Last chapter, we finished the beginning phases
of readying our application for real database usage by bringing in Ecto and
Postgrex (or a different database adapter based on your needs).
PostgreSQL. There are other options available in Ecto if you’d rather use them
(like MySQL, MSSQL, etc.). Since Ecto is a package built to provide abstractions
away from SQL-like language, there may be database-specific gotchas for your
database of choice. Check the Ecto documentation to see if your database has any
specific issues. 27
2. Once the database is installed, you need to know the correct way to access it. This
involves the database address (typically localhost or 127.0.0.1) as well as the
port number (if it differs from the database’s default port).
3. Finally, you’ll need to have a database user and password either created
specifically for our Auction application or have one already available.
defp deps do
[
{:ecto, "~> 2.2.7"},
{:postgrex, "~> 0.13.3"}
]
end
27
github.com/elixir-ecto/ecto
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
137
Once you have ensured you have the correct dependencies listed, run mix
deps.get in your terminal to fetch the packages from hex.pm.
In order for Ecto to work with our database, we will need to give it some
information such as the database name, the username and password it can access
that database with, and the database adapter we will be using. Application
configuration like that is typically done in the config/ directory and more
specifically the config.exs file contained there.
You may (or may not) be surprised to learn that you already have
a auction_umbrella/apps/auction/config/config.exs file in your version of our
Auction application. How did it get there? Well, it was created along with the rest
of the standard files and directories for our application when we used the mix
new command-line script. If you open up that file, you’ll notice that almost nothing
is in the file apart from comments. However, those comments can be very helpful
as you learn what can be configured and how to go about not only configuring, but
also reading those configurations in your application.
For Ecto, the reading and utilizing parts of the config are already taken care of for
us—we just need to tell Ecto about our local database environment. Specifically,
there are three initial things we need to let Ecto know:
1. What Repo(s) we will have in our application.
2. How to access each of the Repos we specified in #1.
3. Minimal setup for the Repo.
As described in the comments in config.exs we do that with the config function.
There are two different versions of config—config/2 and config/3. We will be
using both. config/2 requires the name of our application (:auction) and key/value
pairs we’d like to configure.
For #1 above, we will tell our application that the name of our repo will be
Auction.Repo. Ecto can handle multiple repos simultaneously and for that reason it
expects a List as its value. Add Listing 7.2 to your config.exs file.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
138
Listing 7.2. Letting Ecto know which Repos we will have in our application
config/3 requires the name of our application (:auction), a key to store the
configuration under, and key/value pairs of configuration. We use config/3 when
we tell Ecto how our Auction.Reporepo will be able to communicate with our
database. Listing 7.3 shows the basic values that Ecto requires in config.exs.
The values above should be pretty self-explanatory and match up with what was
described in the prerequisites above. Plug in the values that work for your local
database. Also note that I am using PostgreSQL so the adapter for PostgreSQL is
Ecto.Adapters.Postgres. If you require a different database adapter, refer to Table
1.
Finally, we need to create a file that will house the code for our Repo. This is
super-simple as Ecto provides almost all the functionality you will need when you
use Ecto.Repo. All we need to do in our module is use Ecto.Repo and specify the
name of our application (:auction). In a file named
auction_umbrella/apps/auction/lib/auction/repo.ex, enter the code as found in
Listing 7.4. Naming the module Auction.Repo matches the Ecto configuration used
in Listing 7.2.
defmodule Auction.Repo do
use Ecto.Repo,
otp_app: :auction,
adapter: Ecto.Adapters.Postgres
end
knowledge it needs to start interacting with your system and application. The first
thing we’ll want Ecto to do for us is actually create the database that we specified
when we configured Auction.Repo in config.exs (i.e. database: "auction"). In
order to do that, we’ll use the mix tasks that Ecto provides. After you have set up
your database with a user as specified in your config file (i.e. username:
"postgres", password: "postgres"), and that user has the ability to create a
database, you can run the ecto.create mix task as in Listing 7.5.
If you received the above message when you ran that command, you are in
business! Your application can connect to the database and interact with it
successfully.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
140
we’ll delete the defstruct definition and instead use Ecto’s Ecto.Schema.schema/2
function. Ecto.Schema.schema/2 takes as arguments the table name the data will
live in, plus a list of field definitions (these will roughly match up with the columns
in our table) using Ecto.Schema.field/2. It will have a familiar feel, but we will
provide Ecto more information about our module’s data structure such as data type
and which table it can find all our information in. Listing 7.6 shows our
Auction.Item schema defined for Ecto.
defmodule Auction.Item do
use Ecto.Schema
schema "items" do
field :title, :string
field :description, :string
field :ends_at, :utc_datetime
timestamps()
end
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
141
defmodule Auction.Repo.Migrations.CreateItems do
use Ecto.Migration
def change do
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
142
end
end
Listing 7.9. Filling out the data for our table creation migration
defmodule Auction.Repo.Migrations.CreateItems do
use Ecto.Migration
def change do
create table("items") do
add :title, :string
add :description, :string
add :ends_at, :utc_datetime
timestamps()
end
end
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
143
end
Now that we have our migration filled out that describes to Ecto exactly what we’d
like to do in our migration, we can then use a Mix task to ask Ecto to actually run
the migration. Ecto provides a Mix task that lists the application’s migrations and
their current status (whether they are "up" or "down"). If you run mix
ecto.migrations (like in Listing 7.10), you will see that list.
Repo: Auction.Repo
We can see by the output that our new migration has not yet been run on the
database. mix ecto.migrate will migrate any migrations that haven’t yet been run.
Ours is the only one so far, so it will run it. When you run it, you will see some
output from Ecto describing exactly what it is doing to your database. This is the
output I saw when I ran the command.
> mix ecto.migrate
20:41:27.725 [info] == Running Auction.Repo.Migrations.CreateItems.change/0
forward
20:41:27.725 [info] create table items
20:41:27.809 [info] == Migrated in 0.0s
You just created a database table! Really easy and fast, huh? Beyond that, you
didn’t need to know a lick of SQL or the small intricate differences between how
one database does things vs another (like MySQL vs PostgreSQL). It just works.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
144
What if we made a mistake and we weren’t quite ready to migrate yet? Ecto
provides a mix task for that as well. You can run mix ecto.rollback to roll back
just the last migration either using its down/0 function explicitly provided in the
migration file, or by reversing the changes it did in the change/0 function. We have
no data in our database, so we can try out the feature without destroying any data
we need.
> mix ecto.rollback
20:47:29.418 [info] == Running Auction.Repo.Migrations.CreateItems.change/0
backward
20:47:29.418 [info] drop table items
20:47:29.466 [info] == Migrated in 0.0s
We can see that Ecto is smart enough to know that while we created an items table
when we migrated forward (or up), the reverse is to drop (or destroy)
the items table when we migrate backwards (or down). While mix
ecto.rollback is extremely helpful, it can be dangerous as well. If we had
thousands of rows of customer data in that table, it would all be gone now! Use
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
145
with care.
Note that ecto.migrate attempts to bring as many migrations up as possible
while ecto.rollback only rolls back one migration at a time.
But to continue with our Auction application, we’ll need that table to exist.
Run mix ecto.migrate again to re-create the table before moving on.
7.4.3 Pointing our API to the new Repo
When we initially created our Auction application, we created alongside it a fake
repo to allow us to create our API without having to worry about a real database. It
is now time to point our API to use our new database. We made it really easy to do
that by not referencing the repo directly, but through a module context
variable. lib/auction.ex is the file that provides our API and does the work of
talking with our repo. The only thing we need to change in that file right now is
which repo to point at. It currently reads:
@repo Auction.FakeRepo
That one line provided our module all the information it needed to become a
supervised Ecto worker. All we have to do is specify that we’d like to have it
supervised. To do so, add {Auction.Repo, []} to the children list in
lib/auction/application.ex. This provides our application with enough
information to know that we want that worker (in the list of workers) to be
supervised at startup. Right now it is the only worker, but we can add more to the
list as needed. Listing 7.11 is how your lib/auction/application.ex module
should look after adding our worker.
defmodule Auction.Application dO
use Application
iex(1)> Auction.list_items()
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
147
28
hexdocs.pm/ecto/Ecto.Repo.html
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
148
get_by/3 and get/3 have sister functions that end with a bang (e.g. get!/3). Those
versions of the functions raise an error if a row cannot be found based on the passed query
(instead of just returning nil).
The above three functions have sister functions that end with a bang (e.g. insert!/2) that
returns the struct/row directly (instead of in a tuple with :ok) if found or raises an error if not
found (instead of returning a tuple beginning with :error).
Each of the bang and non-bang versions have their uses depending on how you want your
application to react to data not found in the database.
def insert_item(attrs) do
Auction.Item
|> struct(attrs)
|> @repo.insert()
end
Since we are using the insert/2 function instead of the bang version (insert!/2),
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
149
{:ok,
%Auction.Item{__meta__: #Ecto.Schema.Metadata<:loaded, "items">,
description: "Computer games and thermonuclear war",
ends_at: ~N[2018-02-22 11:43:39], id: 1,
inserted_at: ~N[2018-01-20 16:43:10.657375], title: "Wargames BluRay",
updated_at: ~N[2018-01-20 16:43:10.657389]}}
iex(2)> item
%Auction.Item{__meta__: #Ecto.Schema.Metadata<:loaded, "items">,
description: "Computer games and thermonuclear war",
ends_at: ~N[2018-02-22 11:43:39], id: 1,
inserted_at: ~N[2018-01-20 16:43:10.657375], title: "Wargames BluRay",
updated_at: ~N[2018-01-20 16:43:10.657389]}
Listing 7.14 shows that Ecto provides debug output describing exactly what it is
doing with the database including the SQL it generated and submitted to the
database. It also shows the complete return value of {:ok, …} which we pattern
matched against with {:ok, item}. We now have the database version of the data
in the item variable which we can then use how we’d please.
7.5.2 Retrieving data
We already went over retrieving data in this chapter, but now that we have actual
data to retrieve, we can have a quick review. We have created three functions in
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
150
iex(3)> Auction.list_items
iex(4)> Auction.get_item(1)
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
151
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action
Licensed to Rodrigo Dominguez <[email protected]>
152
Figure 7.2. The web side of our application didn’t require any changes to see real database
data
7.6 Summary
In this chapter you learned
• Ecto provides a number of Mix tasks to make interacting with your database setup
simple
• Use Ecto’s Ecto.Schema.schema/2 function to let Ecto know how your data is
structured in the database
• Use Ecto’s Ecto.Migration module and Mix task to create and run migrations that
change the structure of your database programmatically
• Ecto provides functions for inserting, retrieving, and deleting data from the
database, though it is a good idea to write your own functions that utilize Ecto’s to
provide a clean border between concerns
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/phoenix-in-action