AddThis

Showing posts with label tutorial. Show all posts
Showing posts with label tutorial. Show all posts

Friday, October 21, 2016

Phoenix Scaffolding

Phoenix, Let's Build

Welcome to the second installment of our Phoenix walkthrough. Last time we set up Phoenix, our database and saw our default 'Welcome' page for our dummy library application.

This week we will use some more generators and create our first model, views, and controller. Since this will be a library application, the most obvious choice of our first model should be a `book`.

Generate Scaffold


# mix [generator name] [model name] [table name] [attribute name : attribute type]
mix phoenix.gen.html Book books title:string author:string description:text

# in rails we do
#   rails generate scaffold Book title:string author:string description:text

There are a few key differences between the two, although they are very similar:

  • mix: it's the rake/rails of the elixir world.
  • phoenix.gen.html: this is the generator to use. remember that generators are just ordinary elixir scripts. also notice the `html` suffix which uses the script to generate html (as opposed to json -> phoenix.gen.json).
  • Book: this is the model name. notice the upper case. all models are uppercased.
  • books: this is an interesting distinction vs the rails world. WE NAME THE TABLE OURSELVES.
    in rails the name of the table is automatically made to be the pluralized name of the model, like Book (model name) -> books (table name).
    but English is a quirky language and you cannot always just add `s` to the end of the word, like child to children. so there is some complication in how rails needs to figure out to pluralize you model name.
    this is the first example of some bloat that Phoenix does away with, instead of trying to figure out how to pluralize your model name, Phoenix just defers to you, the developer, to name the table.
  • attributes: this is exactly the same as rails. except in Phoenix, if we omit the type, it defaults to `string`.

➜  phoenix_library git:(master) mix phoenix.gen.html Book books title author description:text

* creating web/controllers/book_controller.ex
* creating web/templates/book/edit.html.eex
* creating web/templates/book/form.html.eex
* creating web/templates/book/index.html.eex
* creating web/templates/book/new.html.eex
* creating web/templates/book/show.html.eex
* creating web/views/book_view.ex
* creating test/controllers/book_controller_test.exs
* creating web/models/book.ex
* creating test/models/book_test.exs
* creating priv/repo/migrations/20161014160805_create_book.exs

Add the resource to your browser scope in web/router.ex:

    resources "/books", BookController

Remember to update your repository by running migrations:

    $ mix ecto.migrate

Look at all the nice things that are generated. This is very similar to scaffold generation. You can see theres:

  • controller: book_controller
  • templates: index, show, new, edit, form
  • views: book_view
  • model: book
  • tests: book_controller, book
  • migration
We will eventually tackle them all, but not right now. Let's go ahead and follow the prompts. Open up web/router.ex and add in the new route to our books resource. Notice this distinction from Rails also. In Rails, this is done for you.


  scope "/", PhoenixLibrary do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index

    # added this line here
    resources "/books", BookController
  end

Next run `rake db:migrate`...I mean `mix ecto.migrate`. :)


➜  phoenix_library git:(master) ✗ mix ecto.migrate
Compiling 9 files (.ex)
Generated phoenix_library app

04:42:58.436 [info]  == Running PhoenixLibrary.Repo.Migrations.CreateBook.change/0 forward

04:42:58.436 [info]  create table books

04:42:58.456 [info]  == Migrated in 0.0s

Now start the server and check out your changes!

➜  phoenix_library git:(master) ✗ mix phoenix.server
Compiling 8 files (.ex)
[info] Running PhoenixLibrary.Endpoint with Cowboy using http://localhost:4000
21 Oct 04:46:52 - info: compiled 6 files into 2 files, copied 3 in 2.1 sec

Don't forget to hit the new route -> http://localhost:4000/books. Alright, that's it for now. Next time we'll talk about the model and migration.

Monday, August 29, 2016

First Sip of Elixir and Beer

Trying out Elixir

I've always been meaning to try out some Elixir but wasn't until I chatted a bit with José Valim about it until my curiosity was really piqued. I love the idea of embracing failure and not worry about catching and trying to handle exceptional cases. After all, this is what exceptions are, exceptional things. Just let the process die and rely on the framework to spin another one up. Beautiful! Additionally, add in the fact that it's a functional language, really makes it shine. Pattern matching and data transformation are so nice. Today I'll talk a bit about how to get started with Elixir and a bit about building a command line program. I chose to solve the "99 bottles of beer" problem, which is essentially just to print out the lyrics to the 99 bottles of beer song.

First installation of Elixir and project setup.

  # install Elixir, mac specific instructions
  brew install Elixir
  
  # create a new project, I named it elixir_99_bottles
  mix new elixir_99_bottles

Now a bit of setup to turn this into a command line tool. First open up mix.exs.

# A defmodule is sort of like a `class` in the object oriented world.
# But since this is a functional language, this is really more like a
# collection of related functions.
defmodule Elixir99Bottles.Mixfile do
  use Mix.Project
  ...

  # add the escript line.  
  # this tells elixir that this is a command line tool.
  # and that to invoke it, the escript_config method must be called
  def project do
    [app: :elixir_99_bottles,
     version: "0.1.0",
     elixir: "~> 1.3",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     escript: escript_config,
     deps: deps()]
  end

  ...

  # Notice that it's defp - meaning its a private function.
  # Compare that with def, which means a public function.
  defp escript_config do
    [main_module: Elixir99Bottles]
  end

end

Next bit of magic, go to lib/elixir_99_bottles.ex. Here you need to add a special main method that will serve as the entry point.

defmodule Elixir99Bottles do

  # entry point
  def main(args) do
    # args is a list, so we grab the first element and "set" that into string_val
    # if there are no args, we set string_val to 99
    string_val = List.first(args) || "99"

    # we want an integer, so we parse it.
    # the return is actually a tuple, which is a nice set of numbers.  
    # kind of like how x and y coordinates on a map make a nice tuple.
    # a tuple is a finite list of things that all logically belong together
    # the return tuple here is actually the { parsed integer, remained after parsing }.
    # but since we are using base 10, all our integers will parse nicely
    # `start` will be set to the parsed integer
    {start, _} = Integer.parse(string_val)

    # loop over the entered `start` value down to 1, i will be the iterator
    for i <- start..1
      do 
        IO.puts format i
      end
  end
  
  # when the num argument is 1, call this function and return this interpolated string
  defp format(num) when num == 1 do
    line_one = "#{num} bottle of beer on the wall, #{num} bottle of beer.\n"
    line_two = "Go to the store and buy some more, 99 bottles of beer on the wall."
    line_one <> line_two
  end

  # when the num argument is more than 1, call this function and return this interpolated string
  defp format(num) when num > 1 do
    line_one = "#{num} bottles of beer on the wall, #{num} bottles of beer.\n"
    line_two = "Take one down and pass it around, #{num-1} bottles of beer on the wall.\n"
    line_one <> line_two
  end

end

Now lastly we add some tests. Open up test/elixir_99_bottles_test.exs.

defmodule Elixir99BottlesTest do
  use ExUnit.Case
  doctest Elixir99Bottles

  # this import allows us to capture and compare things printed to standard out
  import ExUnit.CaptureIO

  # first test is when we have an input of 1.
  # notice we call our class with a list of "1" and compare that with our output
  # """ -> this is a heredoc where you can create multiline strings.  
  # """ starts and ends the multiline string.
  test "1 line" do
    assert capture_io(fn ->
      Elixir99Bottles.main(["1"])
    end) == """
    1 bottle of beer on the wall, 1 bottle of beer.
    Go to the store and buy some more, 99 bottles of beer on the wall.
    """
  end

  # first test is when we have an input of 2.
  # notice we call our class with a list of "2" and compare that with our output
  test "more than 1 line" do
    assert capture_io(fn ->
      Elixir99Bottles.main(["2"])
    end) == """
    2 bottles of beer on the wall, 2 bottles of beer.
    Take one down and pass it around, 1 bottles of beer on the wall.\n
    1 bottle of beer on the wall, 1 bottle of beer.
    Go to the store and buy some more, 99 bottles of beer on the wall.
    """
  end
end

Now, time to run it all!

  # compile the code
  mix escript.build

  # run it.  defaults to 99
  ./elixir_99_bottles 

  # run it for 50 beers
  ./elixir_99_bottles 50

  # run the tests
  mix test

Boom! A very simple demonstration of using mix to create an Elixir project complete with tests. Check out the code here:
https://github.com/jkeam/elixir_99_bottles