├── .gitignore ├── CNAME ├── phoenix.jpg ├── riker.gif ├── img ├── close.png ├── next.png ├── prev.png ├── loading.gif └── demopage │ ├── favicon.png │ ├── image-1.jpg │ ├── image-2.jpg │ ├── image-3.jpg │ ├── image-4.jpg │ ├── image-5.jpg │ ├── image-6.jpg │ ├── thumb-1.jpg │ ├── thumb-2.jpg │ ├── thumb-3.jpg │ ├── thumb-4.jpg │ ├── thumb-5.jpg │ └── thumb-6.jpg ├── lesson-1.png ├── lesson-10.png ├── lesson-2.png ├── lesson-4.png ├── lesson-5.png ├── lesson-6.png ├── lesson-7.png ├── lesson-8.png ├── lesson-9.png ├── morpheus.png ├── og-image.png ├── microseconds.png ├── css ├── lightbox.css └── screen.css ├── index.html ├── js ├── lightbox.min.js ├── lightbox.min.map └── lightbox.js ├── style.css ├── 0.html ├── 11.html ├── 1.html ├── 2.html ├── 6.html ├── 4.html ├── 9.html ├── 5.html └── 8.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.DS_Store 3 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | phoenix.thefirehoseproject.com 2 | -------------------------------------------------------------------------------- /phoenix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/phoenix.jpg -------------------------------------------------------------------------------- /riker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/riker.gif -------------------------------------------------------------------------------- /img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/close.png -------------------------------------------------------------------------------- /img/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/next.png -------------------------------------------------------------------------------- /img/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/prev.png -------------------------------------------------------------------------------- /lesson-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-1.png -------------------------------------------------------------------------------- /lesson-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-10.png -------------------------------------------------------------------------------- /lesson-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-2.png -------------------------------------------------------------------------------- /lesson-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-4.png -------------------------------------------------------------------------------- /lesson-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-5.png -------------------------------------------------------------------------------- /lesson-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-6.png -------------------------------------------------------------------------------- /lesson-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-7.png -------------------------------------------------------------------------------- /lesson-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-8.png -------------------------------------------------------------------------------- /lesson-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/lesson-9.png -------------------------------------------------------------------------------- /morpheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/morpheus.png -------------------------------------------------------------------------------- /og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/og-image.png -------------------------------------------------------------------------------- /img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/loading.gif -------------------------------------------------------------------------------- /microseconds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/microseconds.png -------------------------------------------------------------------------------- /img/demopage/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/favicon.png -------------------------------------------------------------------------------- /img/demopage/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-1.jpg -------------------------------------------------------------------------------- /img/demopage/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-2.jpg -------------------------------------------------------------------------------- /img/demopage/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-3.jpg -------------------------------------------------------------------------------- /img/demopage/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-4.jpg -------------------------------------------------------------------------------- /img/demopage/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-5.jpg -------------------------------------------------------------------------------- /img/demopage/image-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/image-6.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-1.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-2.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-3.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-4.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-5.jpg -------------------------------------------------------------------------------- /img/demopage/thumb-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theFirehoseProject/phoenix-tutorial/HEAD/img/demopage/thumb-6.jpg -------------------------------------------------------------------------------- /css/lightbox.css: -------------------------------------------------------------------------------- 1 | /* Preload images */ 2 | body:after { 3 | content: url(../img/close.png) url(../img/loading.gif) url(../img/prev.png) url(../img/next.png); 4 | display: none; 5 | } 6 | 7 | .lightboxOverlay { 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | z-index: 9999; 12 | background-color: black; 13 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); 14 | opacity: 0.8; 15 | display: none; 16 | } 17 | 18 | .lightbox { 19 | position: absolute; 20 | left: 0; 21 | width: 100%; 22 | z-index: 10000; 23 | text-align: center; 24 | line-height: 0; 25 | font-weight: normal; 26 | } 27 | 28 | .lightbox .lb-image { 29 | display: block; 30 | height: auto; 31 | max-width: inherit; 32 | -webkit-border-radius: 3px; 33 | -moz-border-radius: 3px; 34 | -ms-border-radius: 3px; 35 | -o-border-radius: 3px; 36 | border-radius: 3px; 37 | } 38 | 39 | .lightbox a img { 40 | border: none; 41 | } 42 | 43 | .lb-outerContainer { 44 | position: relative; 45 | background-color: white; 46 | *zoom: 1; 47 | width: 250px; 48 | height: 250px; 49 | margin: 0 auto; 50 | -webkit-border-radius: 4px; 51 | -moz-border-radius: 4px; 52 | -ms-border-radius: 4px; 53 | -o-border-radius: 4px; 54 | border-radius: 4px; 55 | } 56 | 57 | .lb-outerContainer:after { 58 | content: ""; 59 | display: table; 60 | clear: both; 61 | } 62 | 63 | .lb-container { 64 | padding: 4px; 65 | } 66 | 67 | .lb-loader { 68 | position: absolute; 69 | top: 43%; 70 | left: 0; 71 | height: 25%; 72 | width: 100%; 73 | text-align: center; 74 | line-height: 0; 75 | } 76 | 77 | .lb-cancel { 78 | display: block; 79 | width: 32px; 80 | height: 32px; 81 | margin: 0 auto; 82 | background: url(../img/loading.gif) no-repeat; 83 | } 84 | 85 | .lb-nav { 86 | position: absolute; 87 | top: 0; 88 | left: 0; 89 | height: 100%; 90 | width: 100%; 91 | z-index: 10; 92 | } 93 | 94 | .lb-container > .nav { 95 | left: 0; 96 | } 97 | 98 | .lb-nav a { 99 | outline: none; 100 | background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='); 101 | } 102 | 103 | .lb-prev, .lb-next { 104 | height: 100%; 105 | cursor: pointer; 106 | display: block; 107 | } 108 | 109 | .lb-nav a.lb-prev { 110 | width: 34%; 111 | left: 0; 112 | float: left; 113 | background: url(../img/prev.png) left 48% no-repeat; 114 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 115 | opacity: 0; 116 | -webkit-transition: opacity 0.6s; 117 | -moz-transition: opacity 0.6s; 118 | -o-transition: opacity 0.6s; 119 | transition: opacity 0.6s; 120 | } 121 | 122 | .lb-nav a.lb-prev:hover { 123 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 124 | opacity: 1; 125 | } 126 | 127 | .lb-nav a.lb-next { 128 | width: 64%; 129 | right: 0; 130 | float: right; 131 | background: url(../img/next.png) right 48% no-repeat; 132 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 133 | opacity: 0; 134 | -webkit-transition: opacity 0.6s; 135 | -moz-transition: opacity 0.6s; 136 | -o-transition: opacity 0.6s; 137 | transition: opacity 0.6s; 138 | } 139 | 140 | .lb-nav a.lb-next:hover { 141 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 142 | opacity: 1; 143 | } 144 | 145 | .lb-dataContainer { 146 | margin: 0 auto; 147 | padding-top: 5px; 148 | *zoom: 1; 149 | width: 100%; 150 | -moz-border-radius-bottomleft: 4px; 151 | -webkit-border-bottom-left-radius: 4px; 152 | border-bottom-left-radius: 4px; 153 | -moz-border-radius-bottomright: 4px; 154 | -webkit-border-bottom-right-radius: 4px; 155 | border-bottom-right-radius: 4px; 156 | } 157 | 158 | .lb-dataContainer:after { 159 | content: ""; 160 | display: table; 161 | clear: both; 162 | } 163 | 164 | .lb-data { 165 | padding: 0 4px; 166 | color: #ccc; 167 | } 168 | 169 | .lb-data .lb-details { 170 | width: 85%; 171 | float: left; 172 | text-align: left; 173 | line-height: 1.1em; 174 | } 175 | 176 | .lb-data .lb-caption { 177 | font-size: 13px; 178 | font-weight: bold; 179 | line-height: 1em; 180 | } 181 | 182 | .lb-data .lb-number { 183 | display: block; 184 | clear: left; 185 | padding-bottom: 1em; 186 | font-size: 12px; 187 | color: #999999; 188 | } 189 | 190 | .lb-data .lb-close { 191 | display: block; 192 | float: right; 193 | width: 30px; 194 | height: 30px; 195 | background: url(../img/close.png) top right no-repeat; 196 | text-align: right; 197 | outline: none; 198 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); 199 | opacity: 0.7; 200 | -webkit-transition: opacity 0.2s; 201 | -moz-transition: opacity 0.2s; 202 | -o-transition: opacity 0.2s; 203 | transition: opacity 0.2s; 204 | } 205 | 206 | .lb-data .lb-close:hover { 207 | cursor: pointer; 208 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 209 | opacity: 1; 210 | } 211 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |49 | I imagine right now you're feeling a bit like Alice. Tumbling down the rabbit hole. I can see it in your eyes. You have the look of a man who accepts what he sees, because he is expecting to wake up. 50 |
51 | 52 | 53 |54 | Let me tell you why you're here. You're here because you know something. What you know you can't explain, but you feel it. You've felt it your entire life: that there's something wrong in the world. You don't know what it is, but it's there. Like a splinter in your mind, driving you mad. It is this feeling that brought you to me. 55 |
56 |
61 | 63 | After this, there is no turning back. 64 |
65 |The story ends, you navigate away from this page and you believe whatever you want to believe. 67 |
68 |88 | You stay in wonderland, and I'll show you how deep the rabbit hole goes. 89 |
90 |91 | Remember, all I'm offering is the truth. 92 |
93 | 94 | 95 | 96 | Let me show you how
124 | In this tutorial you're going to build a simple CRUD app using Elixir and the Phoenix Framework. It will support new, create, index, edit,update,destroy and a homepage.
125 |
128 | When served in a production environment, pages can be rendered in microseconds, rather than milliseconds. 129 |
130 | 131 | 132 |133 | This tutorial assumes you have experience building web applications using other languages and frameworks. 134 |
135 | 136 | 137 | 138 |139 | Sometimes I will make analogies to Ruby on Rails, for clarity, so if you are not familiar with it, things might get a little confusing. 140 |
141 | 142 | 143 | 144 |145 | Source code for both the Guide and the Completed Splurty app are both available on GitHub. 146 |
147 | 148 |149 | If you follow through to the end of the tutorial, you will have built a web application with Phoenix and Elixir. The application will show a user generated quote that is randomly selected and pulled from a Postgres database. The quote will tell you why you should learn Elixir and should look like this:
150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
158 |
159 | 145 | Congrats, you earned this: 146 |
147 | 148 |
150 |
151 | 157 | As you've noticed the Phoenix Framework is a web framework that is enjoyable to code in, and the Elixir language has Erlang's awesome performance and the BEAM virtual machine, all with syntax quite similar to ruby's. 158 |
159 | 160 |161 | Elixir is a new language, but I expect it to take off in the future. 162 |
163 | 164 | 165 |122 | The first step to build a web application with the Phoenix Web Framework is to install the tools you need to start coding. 123 | 124 |
125 | 126 |129 | Follow the Install Instructions from the Elixir-Lang homepage. This will install the Elixir and Erlang programming languages on your machine. 130 |
131 | 132 |133 | The homebrew instructions worked seamlessly for me. Once you have installed Elixir, you can verify that it is properly installed by running the following commands: 134 |
135 | 136 |$ elixir --version
137 | Elixir 1.0.4
138 |
139 | $ erl -version
140 | Erlang (SMP,ASYNC_THREADS,HIPE) (BEAM) emulator version 6.2
141 |
142 |
143 | Elixir provides a REPL, similar to IRB, called iex. Jump into iex by running the following command:
144 |
$ iex
147 |
148 | 149 | As you would expect you can run standard Elixir code here: 150 |
151 | 152 |$ iex
153 | Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
154 |
155 | Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help)
156 | iex(1)> a = 10 + 14
157 | 24
158 | iex(2)>
159 |
160 | 161 | To get out of iex, press CTRL+C twice in a row. 162 |
163 | 164 |165 | In the future, if you need to manage multiple versions of Elixir on the same machine, the exenv project, will do just that. For this tutorial though, we'll stick to using just the single version of Elixir that we just installed on your computer. 166 |
167 | 168 |169 | The syntax of Elixir is very similar to ruby. The key differences are: macros instead of meta-programming, no classes or instance variables and pattern matching support. 170 |
171 | 172 |173 | To get a feel for pattern matching in Elixir, read Pattern Matching in Elixir for Rubyists from DockYard, it covers the topic in an excellent manner. 174 |
175 | 176 | 177 |180 | Most text editors don't include syntax highlighting for Elixir source code by default. There are packages for Sublime, but I will show you how to add support for Elixir into Atom. If you haven't installed the atom text editor, you can get it here. 181 |
182 | 183 |
184 | Once you load atom, select Atom>Preferences... in the menu. Select the "packages" button, filter for the language-elixir. Now you can install it. This will automatically highlight files with the extension ex and exs as elixir source code.
185 |
190 | Right now, Postgres is the only database supported by Ecto, which is what we'll be using to connect to our database. This makes deciding which to use easy!
191 |
194 | If you don't have Postgres installed yet, follow the instructions to install it. 195 |
196 | 197 |198 | Then create an ecto user in Postgres: 199 |
200 | 201 |$ createuser ecto --pwprompt
202 |
203 | 204 | Enter a password that you will remember. 205 |
206 |207 | Then create the database for this project: 208 |
209 | 210 |$ createdb -Oecto -Eutf8 splurty_development
211 |
212 | 213 | Make sure you can login to your postgres database by running the following command: 214 |
215 | 216 |$ psql splurty_development --user ecto --password
217 |
218 | 219 | When it asks you for a password, enter it in the prompt. Once you get pulled into the postgres database prompt, press CTRL+D to exit. 220 |
221 | 222 |Navigate in the terminal to the directory where your different projects live and clone the phoenix source code: 224 |
225 | 226 |$ git clone https://github.com/phoenixframework/phoenix.git
227 |
228 | 229 | Navigate into the phoenix framework folder: 230 |
231 | 232 |cd phoenix
233 |
234 | 235 | This tutorial is geared to work with Phoenix version 0.8. As it's changing quite quickly, you'll want to make sure you're using that version. 236 |
237 | 238 |$ git checkout v0.8.0
239 |
240 |
241 |
242 | 245 | You need to be in the same directory as the phoenix project. You need to get the dependencies and compile the phoenix project before you go any further. 246 |
247 |$ mix do deps.get, compile
248 | 249 | Once that completes, run the following command: 250 |
251 | 252 |$ mix phoenix.new splurty ../splurty
253 |
254 |
255 | This command will generate a new phoenix web project called splurty in the splurty directory of the parent folder.
256 |
259 | This is the first time we're using the mix command. Mix is a program that is a hybrid of bundler and rake if you're familiar with ruby on rails. In short it allows us to run commands and manages our dependencies.
260 |
263 | If you've tried programming in scala, or any other functional programming language, you may have noticed that package management is usually overly complicated. Mix is very well designed and works as simply as rake and bundler. 264 |
265 | 266 |269 | Navigate to the directory that your splurty application is located in: 270 |
271 | 272 |$ cd ../splurty
273 |
274 | 275 | Fetch, install and compile your application's source code and dependencies by running this command: 276 |
277 | 278 |$ mix do deps.get, compile
279 |
280 | 281 | It should complete without showing you any error messages. Once it does you can start your server by running the following command: 282 |
283 |$ mix phoenix.server
284 |
285 | 286 | Navigate your browser to localhost:4000 and see your generated phoenix web application. Awesome! 287 |
288 | 289 |290 | Your application should look 291 | just like this at this step. 292 |
293 | 294 |
125 | Phoenix is a Model/View/Controller Web Framework and it has many parallels to Ruby on Rails. New projects have the root page displayed from the page controller's index action, but in this lesson we will override that. We will point it to a new controller, the quote controller and the homepage action.
126 |
129 | Pro-tip! 130 | One subtle difference you'll notice from rails is that in Phoenix, controller names tend to be in the singular rather than plural. 131 |
132 | 133 |134 | As of v 0.8 there aren't generators to create controllers yet, so we need to create them manually. 135 |
136 | 137 |
138 | In this lesson we'll setup a new controller, called the quote controller and setup an action called homepage.
139 |
144 | Quickly look at the file structure that was generated when we created a blank Phoenix web application. The bulk of the interesting code is in the folder called web, it's like the app folder in Rails. You'll see channels, controllers, models, templates, views.
145 |
148 | Pro-tip! If you're coming from rails, views is serving a role close to what helpers do, and templates are the templates for HTML, that are generally put in Ruby on Rails views.
149 |
154 | Build a new file in the folder web/controllers called quote_controller.ex, and have it be a blank controller. You can compare this to the page controller, as it's code is quite similar.
155 |
defmodule Splurty.QuoteController do
158 | use Phoenix.Controller
159 |
160 | plug :action
161 | end
162 | 164 | Save the file. 165 |
166 | 167 |
168 | Our goal now is to hook-up request to the root url of our application to go to the QuoteController and the homepage action (we'll hook the homepage action up in the controller a bit later).
169 |
172 | Run the following command to see which URLs are hooked up to which controllers and actions. 173 |
174 | 175 |$ mix phoenix.routes
176 |
177 | 178 | Right now it will say: 179 |
180 | 181 |page_path GET / Splurty.PageController.index/2
182 |
183 |
184 | 185 | Next, we want to change our routes so the root page goes to the QuoteController. 186 |
187 | 188 |
189 | To do this, edit web/router.ex to look like this:
190 |
defmodule Splurty.Router do
197 | use Phoenix.Router
198 |
199 | pipeline :browser do
200 | plug :accepts, ~w(html)
201 | plug :fetch_session
202 | plug :fetch_flash
203 | plug :protect_from_forgery
204 | end
205 |
206 | pipeline :api do
207 | plug :accepts, ~w(json)
208 | end
209 |
210 | scope "/", Splurty do
211 | pipe_through :browser # Use the default browser stack
212 |
213 | get "/", QuoteController, :homepage end
214 |
215 | # Other scopes may use custom stacks.
216 | # scope "/api", Splurty do
217 | # pipe_through :api
218 | # end
219 | end
220 | 224 | Save the file. 225 |
226 |227 | Then run the command to generate and display the routes table for our application 228 |
229 |$ mix phoenix.routes
230 |
231 | 232 | Now it will say: 233 |
234 | 235 |quote_path GET / Splurty.QuoteController.homepage/2
236 |
237 |
238 | 239 | This indicates we properly hooked up the routes to our new controller. 240 |
241 | 242 |
243 | We now need to add the homepage action into our controller.
244 |
245 | Edit web/controllers/quote_controller.ex to look like this:
246 |
defmodule Splurty.QuoteController do
250 | use Phoenix.Controller
251 |
252 | plug :action
253 |
254 | def homepage(conn, _params) do
255 | render conn, "homepage.html"
256 | end
257 | end
258 | 260 | Save the file. 261 |
262 | 263 |266 | We'll need to build the view file to support our QuoteController. 267 |
268 | 269 |
270 | Build a new file in the web/views directory called quote_view.ex and put the following content in it:
271 |
defmodule Splurty.QuoteView do
274 | use Splurty.View
275 | end
276 | 278 | Save the file. 279 |
280 | 281 |282 | We are very close to having the root of our web application changed. 283 |
284 | 285 |
288 | Create a new folder for quote templates in the application by running the following command:
289 |
$ mkdir web/templates/quote
292 |
293 |
294 | Then add a file to the web/templates/quote directory called homepage.html.eex with the following content:
295 |
Number of reasons I yolo everyday <%= 10 + 14 %>
299 | 301 | Save the file. 302 |
303 | 304 |305 | Return to your web browser. Refresh the page at localhost:4000 and the page should update. 306 |
307 | 308 |
309 | You'll notice html.eex is similar to html.erb files in ruby. This allows you to template out HTML, and will execute elixir code, and display it to the page between the <%= and %>.
310 |
315 | Your application should look 316 | just like this at this step. 317 |
318 | 319 |
120 | In this lesson we will add a detail page to display a single quote. We will do this in the quote controller and the show action.
121 |
124 | We want to link to the detail page from the index page. Add the link in the web/templates/quote/index.html.eex file:
125 |
<table>
130 | <thead>
131 | <tr>
132 | <th>Saying</th>
133 | <th>Author</th>
134 | </thead>
135 | <%= for q <- @quotes do %>
136 | <tr>
137 | <td>
138 | <a href="<%=quote_path(@conn, :show, q.id) %>">
139 | <%= q.saying %>
140 | </a> </td>
141 | <td>
142 | <%= q.author %>
143 | </td>
144 | </tr>
145 | <% end %>
146 | </table>
147 | 150 | Save the file. Navigate to localhost:4000/quotes and you will see the saying is now a link that will send the user to the detail page. 151 |
152 |153 | When you click the link you will see an error that indicates that we haven't built the show action in the quote controller yet. 154 |
155 | 156 |
157 | Edit web/controllers/quote_controller.ex to include the show action by adding the following method:
158 |
defmodule Splurty.QuoteController do
161 | use Phoenix.Controller
162 |
163 | alias Splurty.Router
164 | import Splurty.Router.Helpers
165 |
166 | plug :action
167 |
168 | def homepage(conn, _params) do
169 | render conn, "homepage.html"
170 | end
171 |
172 | def index(conn, _params) do
173 | conn
174 | |> assign(:quotes, Repo.all(Splurty.Quote))
175 | |> render("index.html")
176 | end
177 |
178 | def new(conn, _params) do
179 | render conn, "new.html"
180 | end
181 |
182 | def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
183 | q = %Splurty.Quote{saying: saying, author: author}
184 | Repo.insert(q)
185 |
186 | redirect conn, to: quote_path(conn, :index)
187 | end
188 |
189 | def show(conn, %{"id" => id}) do
190 | {id, _} = Integer.parse(id)
191 | conn
192 | |> assign(:quote, Repo.get(Splurty.Quote, id))
193 | |> render("show.html")
194 | end
195 | end
196 | 199 | Save the file. 200 |
201 | 202 |203 | 204 | The first thing this does is convert the id we extract from the parameters in the URL from a string to an integer. If we don't do this, Ecto will give us error messages about type errors. 205 |
206 | 207 | 208 |
209 | This also renders the show.html template and assigns the quote we load up from the Repo with the proper id into the @quote assignment.
210 |
213 | Refresh the page and you will see a different error message, which indicates we haven't build the template yet. 214 |
215 | 216 | 217 |
218 | Add the template file web/templates/quote/show.html.eex with the following dummy content within it:
219 |
hi
222 | 224 | Save the file and refresh the page. Excellent, the error goes away and are presented with the "hi" message. 225 |
226 |
227 | Add the saying and the author as headers on web/templates/quote/show.html.eex like this:
228 |
<h1>
232 | <%= @quote.saying %>
233 | </h1>
234 |
235 | <h2>
236 | <%= @quote.author %>
237 | </h2>
238 | 240 | Save the file and refresh the page. Awesome, we now see the details of the quote presented to us on the quote detail page. 241 |
242 | 243 | 244 |245 | Your application should look 246 | just like this at this step. 247 |
248 | 249 | 250 |
126 | In this lesson we will build a page to list out all the quotes in our application. We will use the QuoteController and index action.
127 |
130 | In advance we know we will build full CRUD functionality on the QuoteController, so we can setup the full set of RESTful, resourceful, routes all at once.
131 |
134 | To do this, edit web/router.ex and add the following line:
135 |
defmodule Splurty.Router do
137 | use Phoenix.Router
138 |
139 | pipeline :browser do
140 | plug :accepts, ~w(html)
141 | plug :fetch_session
142 | plug :fetch_flash
143 | plug :protect_from_forgery
144 | end
145 |
146 | pipeline :api do
147 | plug :accepts, ~w(json)
148 | end
149 |
150 | scope "/", Splurty do
151 | pipe_through :browser # Use the default browser stack
152 |
153 | get "/", QuoteController, :homepage
154 | resources "/quotes", QuoteController
155 | end
156 |
157 | # Other scopes may use custom stacks.
158 | # scope "/api", Splurty do
159 | # pipe_through :api
160 | # end
161 | end
162 | 165 | Save the file. 166 |
167 | 168 |169 | Whenever I change the routes file, I like to verify that my code changes did what I expected, so I will run the command to display the routes table: 170 |
171 | 172 |$ mix phoenix.routes
173 |
174 | 175 | Right now it will say: 176 |
177 |quote_path GET / Splurty.QuoteController.homepage/2
178 | quote_path GET /quotes Splurty.QuoteController.index/2
179 | quote_path GET /quotes/:id/edit Splurty.QuoteController.edit/2
180 | quote_path GET /quotes/new Splurty.QuoteController.new/2
181 | quote_path GET /quotes/:id Splurty.QuoteController.show/2
182 | quote_path POST /quotes Splurty.QuoteController.create/2
183 | quote_path PATCH /quotes/:id Splurty.QuoteController.update/2
184 | PUT /quotes/:id Splurty.QuoteController.update/2
185 | quote_path DELETE /quotes/:id Splurty.QuoteController.destroy/2
186 |
187 |
188 | 189 | The single-line code change hooked up the full set of RESTful routes on our QuoteController. 190 |
191 | 192 |193 | Restart your server. 194 |
195 | 196 |
197 | Navigate in your web browser to the URL that looks like: localhost:4000/quotes. You should see an error message explaining there is an undefined function: Splurty.QuoteController.index/2.
198 |
200 | This indicates we must add an index action to the QuoteController. Edit web/controllers/quote_controller.ex and add it:
201 |
defmodule Splurty.QuoteController do
205 | use Phoenix.Controller
206 |
207 | plug :action
208 |
209 | def homepage(conn, _params) do
210 | render conn, "homepage.html"
211 | end
212 |
213 | def index(conn, _params) do
214 | render conn, "index.html"
215 | end
216 | end
217 |
219 | Save the file and refresh the page. It will show you an error message that indicates the template index.html hasn't been created. This means we must create one.
220 |
223 | Create a file web/templates/quote/index.html.eex that contains the following text:
224 |
YOLO!
227 | 229 | Save the file and refresh the page. Awesome, we no longer see an error message, instead we see the text from our template file. 230 |
231 | 232 |233 | We want this page to list out each of the quotes in our database, but right now our database has no quotes in it. Let's jump into iex and add a couple quotes in our database so this page has data it can display. To start with spin up iex by running the following command: 234 |
235 | 236 |$ iex -S mix
237 |
238 |
239 | 240 | Store two quotes into the database, by running commands like this: 241 |
242 | 243 |> quote = %Splurty.Quote{saying: "They hate us, cause they ain't us.", author: "Dave Skylark"}
244 | > Repo.insert(quote)
245 |
246 |
247 | > quote2 = %Splurty.Quote{saying: "They're peanut butter and jealous.", author: "Dave Skylark"}
248 | > Repo.insert(quote2)
249 | 250 | Then press CTRL+C twice to exit iex. 251 |
252 | 253 |254 | Since we now have two quotes in our database our goal will be to list all of the quotes in our database on the index template. 255 |
256 | 257 |
258 | The first step is to load the quotes in our controller, and allow our template access the records.
259 | Edit the index action in web/controller/quote_controller.ex so the code looks like this:
260 |
defmodule Splurty.QuoteController do
263 | use Phoenix.Controller
264 |
265 | plug :action
266 |
267 | def homepage(conn, _params) do
268 | render conn, "homepage.html"
269 | end
270 |
271 | def index(conn, _params) do
272 | conn
273 | |> assign(:quotes, Repo.all(Splurty.Quote))
274 | |> render("index.html")
275 | end
276 | end
277 |
281 | This will render the index.html.eex template and give the template access to @quotes, which will be a list of all the quotes that exist in our database.
282 |
285 | Edit web/templates/quote/index.html.eex and replace it's contents with this, so it displays a table on the page, with a table row for each record in our database:
286 |
<table>
289 | <thead>
290 | <tr>
291 | <th>Saying</th>
292 | <th>Author</th>
293 | </thead>
294 | <%= for q <- @quotes do %>
295 | <tr>
296 | <td>
297 | <%= q.saying %>
298 | </td>
299 | <td>
300 | <%= q.author %>
301 | </td>
302 | </tr>
303 | <% end %>
304 |
305 | </table>
306 | 308 | Save the file and open localhost:4000/quotes in your browser. 309 |
310 | 311 |312 | Your application should look 313 | just like this at this step. 314 |
315 | 316 | 317 | 318 |
122 | We want our splurty application to splurt out un-expected quotes to the user from the database.
123 |
124 | This means the homepage action of the QuoteController should load up a quote randomly from the database, and then show the details for the quote. We will do that in this lesson.
125 |
128 | Before we keep coding, since you're staying up to date with cutting edge technologies, like Elixir and Phoenix we thought you may be interested in giving back and helping teach the next generation of web developers.... 129 |
130 | 131 | 132 |136 | Here's the gist: 137 |
138 |139 | Me and a few other developers are running theFirehoseProject - a 12 week mentor driven online coding program to train the next generation of web developers. 140 |
141 |142 | Our code mentors take a student under their wing for 12 weeks, pair program with them for one-hour per week and guide them towards their personal coding goal. 143 |
144 |145 | Throughout each week, students work on building several web applications (think Yelp and Udemy clones), solve real-world coding challenges (from FizzBuzz to LinkedLists), build a web app through TDD and join a group project to build a chess game as a team. 146 |
147 |148 | Our mentors are senior web devs from PayPal, Techstars companies, ex-Flickr, ex-bit.ly and get excited by seeing a beginner grow into a proficient developer who's able to solve complex CS challenges. 149 |
150 |151 | You can see what some of our students are saying about us here and find more info about what our mentors are doing here. 152 |
153 | 154 |155 | If you're interested email marco@thefirehoseproject and we'll give you more details! 156 |
157 | 158 |
161 | Ok, the plug is over, let's get back to coding.
162 |
163 | Ecto's query syntax is quite nice for simple queries. See an example here.
164 |
165 | Unfortunately it doesn't allow us to perform an ORDER BY RANDOM() from SQL easily.
166 |
167 | This means we'll need to craft a custom SQL query and load up a Splurty.Quote based on that.
168 |
171 | We will build a submodule within quote.ex that will allow us to put various queries inside it. In this module we can build a random method that will execute a SQL query to extract a random record and parse the result into a Splurty.Quote model. To do that, add the following lines of code to web/models/quote.ex:
172 |
defmodule Splurty.Quote do
175 | use Ecto.Model
176 |
177 | schema "quotes" do
178 | field :saying, :string
179 | field :author, :string
180 | end
181 |
182 | defmodule Queries do
183 | def random do
184 | query = Ecto.Adapters.Postgres.query(
185 | Repo,
186 | "SELECT id, saying, author from quotes ORDER BY RANDOM() LIMIT 1",
187 | [])
188 | %Postgrex.Result{rows: [row]} = query
189 | {id, saying, author} = row
190 | %Splurty.Quote{id: id, saying: saying, author: author}
191 | end
192 | end
193 | end
194 | 198 | Save the file. 199 |
200 |
201 | Now we can extract a random quote from our database by calling Splurty.Quote.Queries.random.
202 |
205 | Modify the homepage action within the QuoteController to hook this functionality up. The homepage action should load a random quote, assign it to @quote and render the show.html template. To do that edit web/controllers/quote_controller.ex to look like this:
206 |
defmodule Splurty.QuoteController do
209 | use Phoenix.Controller
210 |
211 | alias Splurty.Router
212 | import Splurty.Router.Helpers
213 |
214 | plug :action
215 |
216 | def homepage(conn, _params) do
217 | conn
218 | |> assign(:quote, Splurty.Quote.Queries.random)
219 | |> render("show.html") end
220 |
221 | def index(conn, _params) do
222 | conn
223 | |> assign(:quotes, Repo.all(Splurty.Quote))
224 | |> render("index.html")
225 | end
226 |
227 | def new(conn, _params) do
228 | render conn, "new.html"
229 | end
230 |
231 | def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
232 | q = %Splurty.Quote{saying: saying, author: author}
233 | Repo.insert(q)
234 |
235 | redirect conn, to: quote_path(conn, :index)
236 | end
237 |
238 | def show(conn, %{"id" => id}) do
239 | {id, _} = Integer.parse(id)
240 | conn
241 | |> assign(:quote, Repo.get(Splurty.Quote, id))
242 | |> render("show.html")
243 | end
244 |
245 | def edit(conn, %{"id" => id}) do
246 | {id, _} = Integer.parse(id)
247 | conn
248 | |> assign(:quote, Repo.get(Splurty.Quote, id))
249 | |> render("edit.html")
250 | end
251 |
252 |
253 | def update(conn, %{"id" => id, "quote" => %{"saying" => saying, "author" => author}}) do
254 | {id, _} = Integer.parse(id)
255 | q = Repo.get(Splurty.Quote, id)
256 | q = %{q | saying: saying, author: author }
257 | Repo.update(q)
258 | redirect conn, to: quote_path(conn, :show, q.id)
259 | end
260 |
261 | def destroy(conn, %{"id" => id}) do
262 | {id, _} = Integer.parse(id)
263 | q = Repo.get(Splurty.Quote, id)
264 | Repo.delete(q)
265 | redirect conn, to: quote_path(conn, :index)
266 | end
267 |
268 | end
269 | 272 | Save the file. 273 |
274 | 275 |When you navigate to localhost:4000, you'll notice a random quote is randomly shown. Sweet! 276 |
277 | 278 |
279 | We should cleanup after ourselves and delete the file homepage.html.eex, since we're no longer using it. Run this command:
280 |
$ rm web/templates/quote/homepage.html.eex
283 |
284 |
285 |
286 | 287 | Your application should look 288 | just like this at this step. 289 |
290 | 291 |121 | In order to allow the application to accept user generated quotes, we will need to build form where the user is prompted to enter details about the quote they want to add. We also will need to store the quote in the database when the form button is pressed. We will accomplish both in this lesson. 122 |
123 | 124 |127 | Right now if you navigate to localhost:4000/quotes/new, you'll see an error message indicating we need to add the new action to the controller. 128 |
129 | 130 |
131 | Edit web/controllers/quote_controller.ex and add the new action to it:
132 |
defmodule Splurty.QuoteController do
135 | use Phoenix.Controller
136 |
137 | plug :action
138 |
139 | def homepage(conn, _params) do
140 | render conn, "homepage.html"
141 | end
142 |
143 | def index(conn, _params) do
144 | conn
145 | |> assign(:quotes, Repo.all(Splurty.Quote))
146 | |> render("index.html")
147 | end
148 |
149 | def new(conn, _params) do
150 | render conn, "new.html"
151 | end
152 | end
153 | 155 | Save the file. 156 |
157 | 158 |
159 | Refresh the page. The error message will change, and it will say that the new.html template doesn't exist. This means we will need to create a file called new.html.eex. To start with create the web/templates/quote/new.html.eex file with the following content:
160 |
Hello!
163 | 166 | Save the file and refresh the page. We no longer are presented with an error message, instead we see the text: Hello! 167 |
168 | 169 |
170 | In this lesson we'll want to use routing helpers to build URLs in our controller. To do that, we'll want to include the routing helpers by adding the following lines of code to web/controllers/quote_controller.ex:
171 |
defmodule Splurty.QuoteController do
174 | use Phoenix.Controller
175 |
176 | alias Splurty.Router
177 | import Splurty.Router.Helpers
178 | plug :action
179 |
180 | def homepage(conn, _params) do
181 | render conn, "homepage.html"
182 | end
183 |
184 | def index(conn, _params) do
185 | conn
186 | |> assign(:quotes, Repo.all(Splurty.Quote))
187 | |> render("index.html")
188 | end
189 |
190 | def new(conn, _params) do
191 | render conn, "new.html"
192 | end
193 |
194 | end
195 | 198 | Save the file and restart the server. 199 |
200 | 201 | 202 | 203 |
204 | Next let's add the form to the view. Edit web/templates/quote/new.html.eex so it looks like this:
205 |
<h1>New Quote</h1>
208 | <form action="<%= quote_path(@conn, :create) %>" method="post">
209 | <div class="form-group">
210 | <label for="quote[saying]">saying</label>
211 | <input type="text" name="quote[saying]" class="form-control" />
212 |
213 | <label for="quote[author]">author</label>
214 | <input type="text" name="quote[author]" class="form-control" />
215 |
216 | </div>
217 | <button type="submit" class="btn btn-primary">Save</button>
218 | </form>
219 | 221 | Save the file and refresh the page. Awesome, the form appeared! 222 |
223 | 224 |227 | Next, we should plan to support the creation of a quote in the database. Navigate to localhost:4000/quotes/new, populate the form with data and press the submit button. 228 |
229 | 230 |231 | An error message is displayed, indicating problems with CSRF. This is because we're not passing along a valid CSRF token in the form we're submitting. 232 |
233 | 234 |
235 | Add the following helper method to web/view.ex, to allow us to get the CSRF token from the template:
236 |
defmodule Splurty.View do
239 | use Phoenix.View, root: "web/templates"
240 |
241 | # The quoted expression returned by this block is applied
242 | # to this module and all other views that use this module.
243 | using do
244 | quote do
245 | # Import common functionality
246 | import Splurty.Router.Helpers
247 |
248 | # Use Phoenix.HTML to import all HTML functions (forms, tags, etc)
249 | use Phoenix.HTML
250 | end
251 | end
252 |
253 | def csrf_token(conn) do
254 | Plug.Conn.get_session(conn, :csrf_token)
255 | end # Functions defined here are available to all other views/templates
256 | end
257 | 260 | Save the file. 261 |
262 |
263 | Add a hidden form field to pass through the CSRF token when the form is submitted by adding this line to the web/templates/quote/new.html.eex file:
264 |
<h1>New Quote</h1>
269 | <form action="<%= quote_path(@conn, :create) %>" method="post">
270 | <div class="form-group">
271 | <input type="hidden" name="csrf_token" value="<%= csrf_token(@conn) %>">
272 | <label for="quote[saying]">saying</label>
273 | <input type="text" name="quote[saying]" class="form-control" />
274 |
275 | <label for="quote[author]">author</label>
276 | <input type="text" name="quote[author]" class="form-control" />
277 |
278 | </div>
279 | <button type="submit" class="btn btn-primary">Save</button>
280 | </form>
281 | 285 | Save the file. 286 |
287 | 288 |289 | Navigate to localhost:4000/quotes/new and refresh the page. Then fill out the form and press the submit button. The error message changes, and indicates the problem now is that the controller action is undefined. This means we've properly dealt with CSRF. 290 |
291 | 292 | 293 | 294 |
297 | We'll need to add an action that will be triggered when someone pressed the submit button on the new form. Edit web/controllers/quote_controller.ex and add the create method, which will create a quote in the database and redirect the user to the index page:
298 |
defmodule Splurty.QuoteController do
301 | use Phoenix.Controller
302 |
303 | alias Splurty.Router
304 | import Splurty.Router.Helpers
305 |
306 | plug :action
307 |
308 | def homepage(conn, _params) do
309 | render conn, "homepage.html"
310 | end
311 |
312 | def index(conn, _params) do
313 | conn
314 | |> assign(:quotes, Repo.all(Splurty.Quote))
315 | |> render("index.html")
316 | end
317 |
318 | def new(conn, _params) do
319 | render conn, "new.html"
320 | end
321 |
322 | def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
323 | q = %Splurty.Quote{saying: saying, author: author}
324 | Repo.insert(q)
325 |
326 | redirect conn, to: quote_path(conn, :index)
327 | end
328 | end
329 | 332 | Save the file. Navigate to localhost:4000/quotes/new, fill out the form and press the submit button. You will be redirected to the index page and see the quote you just added displayed on the page. 333 |
334 | 335 |336 | Your application should look 337 | just like this at this step. 338 |
339 | 340 |121 | Our web application supports most of the standard CRUD functionality, but we haven't tackled deleting content from our database yet. We will do that in this lesson. 122 |
123 | 124 | 125 |
126 | From our web application we'll want to trigger a DELETE HTTP request to the quote_path in our application. Rather than doing this through submitting a form, we should trigger the HTTP request by clicking on a link.
127 |
128 | Unfortunately, right now Phoenix doesn't support links to non-GET requests out of the box. We can pull in a JavaScript library to do this for us called the RestfulizerJs.
129 |
134 | Add the RestfulizerJs to your Phoenix web application. To start with copy the jquery.restfulizer.js file into priv/static/js folder.
135 |
136 |
139 | We'll also want to include jQuery, so let's pull that into our application too. We should place this within the web/templates/layout/application.html.eex file, so the JavaScript is loaded on all pages of our web application.
140 |
142 | Pro-tip!
143 |
144 | The application.html.eex is like application.html.erb in rails.
145 |
148 | Add the following two lines to web/templates/layout/application.html.eex:
149 |
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
152 | <script src="/js/jquery.restfulizer.js"></script>
153 |
157 | In order to activate the RestfulizerJs plugin, and also tie in our CSRF token we'll need to add a bit of JavaScript code. Including this script tag to the web/templates/layout/application.html.eex will do the trick:
158 |
<script>
161 | $(function() {
162 | $(".rest").click(function(d) {
163 | $('form').submit(function(s) {
164 | var input = $("<input>")
165 | .attr("type", "hidden")
166 | .attr("name", "csrf_token").val("<%= csrf_token(@conn) %>");
167 | $(s.target).append($(input));
168 | });
169 | });
170 | $(".rest").restfulizer({});
171 | });
172 | </script>
173 | 176 | Save the file. 177 |
178 |
179 | We now can quickly perform POST, PUT and DELETE HTTP requests from links.
180 |
185 | Edit web/templates/quote/index.eex to add a link to delete the quote:
186 |
<table>
189 | <thead>
190 | <tr>
191 | <th>Saying</th>
192 | <th>Author</th>
193 | <th>Actions</th>
194 | </thead>
195 | <%= for q <- @quotes do %>
196 | <tr>
197 | <td>
198 | <a href="<%=quote_path(@conn, :show, q.id) %>">
199 | <%= q.saying %>
200 | </a>
201 | </td>
202 | <td>
203 | <%= q.author %>
204 | </td>
205 | <td>
206 | <a href="<%=quote_path(@conn, :edit, q.id) %>">
207 | Edit
208 | </a>
209 | |
210 | <a href="<%= quote_path(@conn, :destroy, q.id) %>" data-method="DELETE" class="rest">
211 | Destroy
212 | </a>
213 | </td>
214 | </tr>
215 | <% end %>
216 | </table>
217 | 220 | Save the file and refresh the index page. You should notice a destroy link got added. Sweet! 221 | 222 |
223 | 224 |
225 | In the code above, by giving the link the class of rest we connect it to the script tag we used above to configure the RestfulizerJs library. The data-method attribute tells RestfulizerJs to perform that type of an HTTP request when the link is clicked, in this case a DELETE request.
226 |
230 | When you press the destroy button you will be presented an error message, stating that we haven't implemented the action in the controller yet. Let's add the action in our controller! 231 |
232 | 233 |
234 | Add the method to web/controllers/quote_controller.ex so the file looks like this:
235 |
defmodule Splurty.QuoteController do
238 | use Phoenix.Controller
239 |
240 | alias Splurty.Router
241 | import Splurty.Router.Helpers
242 |
243 | plug :action
244 |
245 | def homepage(conn, _params) do
246 | render conn, "homepage.html"
247 | end
248 |
249 | def index(conn, _params) do
250 | conn
251 | |> assign(:quotes, Repo.all(Splurty.Quote))
252 | |> render("index.html")
253 | end
254 |
255 | def new(conn, _params) do
256 | render conn, "new.html"
257 | end
258 |
259 | def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
260 | q = %Splurty.Quote{saying: saying, author: author}
261 | Repo.insert(q)
262 |
263 | redirect conn, to: quote_path(conn, :index)
264 | end
265 |
266 | def show(conn, %{"id" => id}) do
267 | {id, _} = Integer.parse(id)
268 | conn
269 | |> assign(:quote, Repo.get(Splurty.Quote, id))
270 | |> render("show.html")
271 | end
272 |
273 | def edit(conn, %{"id" => id}) do
274 | {id, _} = Integer.parse(id)
275 | conn
276 | |> assign(:quote, Repo.get(Splurty.Quote, id))
277 | |> render("edit.html")
278 | end
279 |
280 |
281 | def update(conn, %{"id" => id, "quote" => %{"saying" => saying, "author" => author}}) do
282 | {id, _} = Integer.parse(id)
283 | q = Repo.get(Splurty.Quote, id)
284 | q = %{q | saying: saying, author: author }
285 | Repo.update(q)
286 | redirect conn, to: quote_path(conn, :show, q.id)
287 | end
288 |
289 | def destroy(conn, %{"id" => id}) do
290 | {id, _} = Integer.parse(id)
291 | q = Repo.get(Splurty.Quote, id)
292 | Repo.delete(q)
293 | redirect conn, to: quote_path(conn, :index)
294 | end
295 | end
296 | 298 | Save the file. 299 |
300 |
301 | This code uses the standard code we've been using to load up the Splurty.Quote model in the controller. We then delete the item from the repo in the same manner we used in Lesson 3. The user is redirected to the index of the QuoteController.
302 |
306 | Press the destroy button. Sweet! You will be redirected to the index page and the item you deleted will no longer be presented to you in the table. 307 |
308 | 309 | 310 | 311 |312 | Your application should look 313 | just like this at this step. 314 |
315 | 316 | 317 | 318 | 319 |