Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Code Examples from the Programming Phoenix book #42

Merged
merged 34 commits into from
Mar 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a2f798d
adds update to controllers/video_controller.ex from ch 6 (p. 105)
nelsonic Feb 18, 2017
68adc2b
enforce categories reference in videos. p.109
nelsonic Feb 18, 2017
e2065b3
adds snapshot of rumbl app at end of chapter 7 (p.128)
nelsonic Feb 18, 2017
dffbb51
snapshot of rumbl code before adding integration tests p.134 (passing…
nelsonic Feb 19, 2017
a034625
adds comment to basic-test.exs for how to run a single test. thanks t…
nelsonic Feb 19, 2017
14bfa97
remove un-required _build files
nelsonic Feb 19, 2017
63efdf3
snapshot of rumbl code before testing for logged-in users on p.138
nelsonic Feb 19, 2017
e83b58f
[WiP] adds code @tag login_as: "max" (sadly test failing! see: https:…
nelsonic Feb 20, 2017
f11e96d
fix #38 test for logged-in user on p.139 still seeing #35 but at leas…
nelsonic Feb 26, 2017
fca2449
all tests passing on p.140 but see issue #40 so commented out redirec…
nelsonic Feb 26, 2017
cd0cb73
adds snapshot of "rumbl" code on p.147 (before Testing Views and Temp…
nelsonic Feb 26, 2017
30d637c
adds "rumbl" View tests from p.147 & p.148
nelsonic Feb 26, 2017
cb701c6
adds final user and category tests for rumbl. all pass on p.153 :rocket:
nelsonic Feb 27, 2017
147514c
fix merge conflicts from master
nelsonic Feb 27, 2017
69fcb56
p.159 code snapshot
nelsonic Mar 1, 2017
f2a17a2
p.163 code snapshot including web/static/js/player.js
nelsonic Mar 4, 2017
08f92dc
p.166 slugify_title and slugify working as expected also fixed https:…
nelsonic Mar 5, 2017
09e9092
p.166 adds |> slugify_title() to models/video.ex changeset
nelsonic Mar 5, 2017
0ab4581
p.167 adds defimpl for creating slugs "#{id}-#{slug}" using Elixir pr…
nelsonic Mar 5, 2017
c7075e1
adds .travis.yml file so we can track the build! see: https://github.…
nelsonic Mar 5, 2017
eebd057
use before_install instead of before_script see: http://stackoverflow…
nelsonic Mar 5, 2017
aaf3cda
give full path for "change directory" in .travis.yml see: https://tra…
nelsonic Mar 5, 2017
837db9e
one-liner for .travis.yml before_install script
nelsonic Mar 5, 2017
5860104
p.167 adds defimpl Phoenix.Param, for: Rumbl.Video as instructed at e…
nelsonic Mar 5, 2017
96c9f64
update changeset in web/models/vide.ex to avoid warning: `Ecto.Change…
nelsonic Mar 5, 2017
db72acb
add defimpl Phoenix.Param to_param back into web/models/video.ex for …
nelsonic Mar 5, 2017
27c4a11
adds field :slug, :string to web/models/video.ex as suggested by @dog…
nelsonic Mar 5, 2017
954fab8
adds clean_id for: https://github.com/dwyl/learn-phoenix-framework/is…
nelsonic Mar 5, 2017
873465a
p.171 everything works. tests pass. :rocket: https://github.com/dwyl/…
nelsonic Mar 5, 2017
0dc090e
update rumbl/priv/repo/seeds.exs in response to @shouston3 comment: h…
nelsonic Mar 5, 2017
b2e0da3
fix typo in rumbl/test/controllers/auth_test.exs "secrete" > "secret"…
nelsonic Mar 5, 2017
b0ea31b
remove IO.inspect from rumbl/web/controllers/video_controller.ex note…
nelsonic Mar 5, 2017
cfe5333
adds clarifying comment to player.js explaining that we prefer Elm as…
nelsonic Mar 6, 2017
7d222ef
adds validate_required check for web/models/user.ex thanks to @untra …
nelsonic Mar 6, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language: elixir
elixir:
- 1.3.4
before_install: cd /home/travis/build/dwyl/learn-phoenix-framework/rumbl && pwd
cache:
directories:
- _build
- deps
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,19 @@ _trying_ to make it **go _faster_**<sup>1</sup> ...
***Or***... _get_ the [**_best_ car**](http://www.cnbc.com/2015/08/27/teslas-p85d-is-the-best-car-consumer-reports-has-ever-tested.html) built for speed, safety and environmental friendliness and ***stop wasting time*** on the past!

![model-s-photo](https://cloud.githubusercontent.com/assets/194400/22628333/e8a107ee-ebc9-11e6-9140-6be11cdddd87.jpg "Tesla Model S") <br />
<small>_**Note**: in the case of Phoenix,
you're getting a Tesla Model S P100D for the "price" of a Ford Fiesta!
<small>_**Note**: with Phoenix,
you're getting a [Tesla Model S P100D](https://www.tesla.com/en_GB/blog/new-tesla-model-s-now-quickest-production-car-world) for the "price" of a Ford Fiesta!
A **logical** choice, **right**_?
</small>

Just like there is an _entire industry_ involved in "_tuning_" distinctly
"_average_" cars (_that weren't made for high performance!_)
there's a similar one for "_optimsing_" slow web applications.
there's a similar one for "_optimizing_" slow web applications.
Organizations end up spending _way_ more time and money
(_"engineering" consultants and server resources_!)
trying to make their "_old tech_" scale or serve more (_concurrent users_),
than they would simply making smarter tech choices.
trying to make their "_old tech_" scale or serve more (_concurrent_) users,
than they would simply making smarter tech choices
(_and avoiding ["sunk cost bias"](http://www.lifehack.org/articles/communication/how-the-sunk-cost-fallacy-makes-you-act-stupid.html)_).

<sup>1: car mod fails:
[ridelust.com/30-custom-cars-from-around-the-world](http://www.ridelust.com/30-custom-cars-from-around-the-world)
Expand Down
23 changes: 23 additions & 0 deletions examples/basic-test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# run this test using the following command:
# elixir -e "ExUnit.start()" -r basic-test.exs

defmodule MyTest do
use ExUnit.Case, async: true

setup do
# run some tedious setup code
:ok
end

test "pass" do
assert true
end

test "fail" do
refute false
end
end


# thanks to @shouston3 in
# https://github.com/dwyl/learn-phoenix-framework/issues/34#issuecomment-280930167
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ defmodule Rumbl.VideoController do

plug :scrub_params, "video" when action in [:create, :update]


plug :load_categories when action in [:new, :create, :edit, :update]

defp load_categories(conn, _) do
Expand Down
3 changes: 3 additions & 0 deletions rumbl/config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ config :rumbl, Rumbl.Repo,
database: "rumbl_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox

config :comeonin, :bcrypt_log_rounds, 4
config :comeonin, :pbkdf2_rounds, 1
28 changes: 28 additions & 0 deletions rumbl/lib/rumbl/permalink.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule Rumbl.Permalink do
@behaviour Ecto.Type

def type, do: :id

def cast(binary) when is_binary(binary) do
case Integer.parse(binary) do
{int, _} when int > 0 -> {:ok, int}
_ -> :error
end
end

def cast(integer) when is_integer(integer) do
{:ok, integer}
end

def cast(_) do
:error
end

def dump(integer) when is_integer(integer) do
{:ok, integer}
end

def load(integer) when is_integer(integer) do
{:ok, integer}
end
end
2 changes: 1 addition & 1 deletion rumbl/priv/repo/migrations/20170207074532_create_user.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Rumbl.Repo.Migrations.CreateUser do
add :username, :string, null: false
add :password_hash, :string

timestamps
timestamps()
end

create unique_index(:users, [:username])
Expand Down
1 change: 0 additions & 1 deletion rumbl/priv/repo/migrations/20170212214945_create_video.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ defmodule Rumbl.Repo.Migrations.CreateVideo do
timestamps()
end
create index(:videos, [:user_id])

end
end
13 changes: 13 additions & 0 deletions rumbl/priv/repo/migrations/20170218093549_create_category.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule Rumbl.Repo.Migrations.CreateCategory do
use Ecto.Migration

def change do
create table(:categories) do
add :name, :string, null: false

timestamps() # function call parenthesis not in book!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

end

create unique_index(:categories, [:name])
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Rumbl.Repo.Migrations.AddCategoryIdToVideo do
use Ecto.Migration

def change do
alter table(:videos) do
add :category_id, references(:categories)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Rumbl.Repo.Migrations.AddSlugToVideo do
use Ecto.Migration

def change do
alter table(:videos) do
add :slug, :string
end
end
end
8 changes: 8 additions & 0 deletions rumbl/priv/repo/seeds.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.

alias Rumbl.Repo
alias Rumbl.Category

for category <- ~w(Action Drama Romance Comedy Sci-fi) do
Repo.get_by(Category, name: category) ||
Repo.insert!(%Category{name: category})
end
83 changes: 83 additions & 0 deletions rumbl/test/controllers/auth_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
defmodule Rumb.AuthTest do
use Rumbl.ConnCase
alias Rumbl.Auth

setup %{conn: conn} do
conn =
conn
|> bypass_through(Rumbl.Router, :browser)
|> get("/")

{:ok, %{conn: conn}}
end

test "authenticate_user halts when no current_user exists", %{conn: conn} do
conn = Auth.authenticate_user(conn, [])
assert conn.halted
end

test "authenticate_user continues when the current_user exists",
%{conn: conn} do

conn =
conn
|> assign(:current_user, %Rumbl.User{})
|> Auth.authenticate_user([])
refute conn.halted
end

test "login puts the user in the session", %{conn: conn} do
login_conn =
conn
|> Auth.login(%Rumbl.User{id: 123})
|> send_resp(:ok, "")

next_conn = get(login_conn, "/")
assert get_session(next_conn, :user_id) == 123
end

test "logout drops the session", %{conn: conn} do
logout_conn =
conn
|> put_session(:user_id, 123)
|> Auth.logout()
|> send_resp(:ok, "")

next_conn = get(logout_conn, "/")
refute get_session(next_conn, :user_id)
end

test "call places user from session into assigns", %{conn: conn} do
user = insert_user()
conn =
conn
|> put_session(:user_id, user.id)
|> Auth.call(Repo)

assert conn.assigns.current_user.id == user.id
end

test "call with no session sets current_user assign to nil", %{conn: conn} do
conn = Auth.call(conn, Repo)
assert conn.assigns.current_user == nil
end

test "login with a valid username and pass", %{conn: conn} do
user = insert_user(username: "me", password: "secret")
{:ok, conn} =
Auth.login_by_username_and_pass(conn, "me", "secret", repo: Repo)

assert conn.assigns.current_user.id == user.id
end

test "login with a not found user", %{conn: conn} do
assert {:error, :not_found, _conn} =
Auth.login_by_username_and_pass(conn, "me", "secret", repo: Repo)
end

test "login with password missmatch", %{conn: conn} do
_ = insert_user(username: "me", password: "secret")
assert {:error, :unauthorized, _conn} =
Auth.login_by_username_and_pass(conn, "me", "wrong", repo: Repo)
end
end
2 changes: 1 addition & 1 deletion rumbl/test/controllers/page_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ defmodule Rumbl.PageControllerTest do

test "GET /", %{conn: conn} do
conn = get conn, "/"
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
assert html_response(conn, 200) =~ "Welcome to Rumbl.io"
end
end
117 changes: 70 additions & 47 deletions rumbl/test/controllers/video_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,66 +1,89 @@
defmodule Rumbl.VideoControllerTest do
use Rumbl.ConnCase

alias Rumbl.Video
@valid_attrs %{description: "some content", title: "some content", url: "some content"}
@invalid_attrs %{}

test "lists all entries on index", %{conn: conn} do
conn = get conn, video_path(conn, :index)
assert html_response(conn, 200) =~ "Listing videos"
setup %{conn: conn} = config do
if username = config[:login_as] do
user = insert_user(username: username)
conn = assign(conn, :current_user, user)
{:ok, conn: conn, user: user}
else
:ok
end
end

test "renders form for new resources", %{conn: conn} do
conn = get conn, video_path(conn, :new)
assert html_response(conn, 200) =~ "New video"
test "requires user authentication on all actions", %{conn: conn} do
Enum.each([
get(conn, video_path(conn, :new)),
get(conn, video_path(conn, :index)),
get(conn, video_path(conn, :show, "123")),
get(conn, video_path(conn, :edit, "123")),
put(conn, video_path(conn, :update, "123", %{})),
post(conn, video_path(conn, :create, %{})),
delete(conn, video_path(conn, :delete, "123")),
], fn conn ->
assert html_response(conn, 302)
assert conn.halted
end)
end

test "creates resource and redirects when data is valid", %{conn: conn} do
conn = post conn, video_path(conn, :create), video: @valid_attrs
assert redirected_to(conn) == video_path(conn, :index)
assert Repo.get_by(Video, @valid_attrs)
end
# setup do
# user = insert_user(username: "max")
# conn = assign(build_conn, :current_user, user)
# {:ok, conn: conn, user: user}
# end

test "does not create resource and renders errors when data is invalid", %{conn: conn} do
conn = post conn, video_path(conn, :create), video: @invalid_attrs
assert html_response(conn, 200) =~ "New video"
end
@tag login_as: "max"
test "lists all user's video on index", %{conn: conn, user: user} do
user_video = insert_video(user, title: "funny cats")
other_video = insert_video(insert_user(username: "other"),
title: "another video")

test "shows chosen resource", %{conn: conn} do
video = Repo.insert! %Video{}
conn = get conn, video_path(conn, :show, video)
assert html_response(conn, 200) =~ "Show video"
conn = get conn, video_path(conn, :index)
assert html_response(conn, 200) =~ ~r/Listing videos/
assert String.contains?(conn.resp_body, user_video.title)
refute String.contains?(conn.resp_body, other_video.title)
end

test "renders page not found when id is nonexistent", %{conn: conn} do
assert_error_sent 404, fn ->
get conn, video_path(conn, :show, -1)
end
end
alias Rumbl.Video
@valid_attrs %{url: "http://youtu.be", title: "vid", description: "a vid"}
@invalid_attrs %{title: "invalid"}

test "renders form for editing chosen resource", %{conn: conn} do
video = Repo.insert! %Video{}
conn = get conn, video_path(conn, :edit, video)
assert html_response(conn, 200) =~ "Edit video"
end
defp video_count(query), do: Repo.one(from v in query, select: count(v.id))

test "updates chosen resource and redirects when data is valid", %{conn: conn} do
video = Repo.insert! %Video{}
conn = put conn, video_path(conn, :update, video), video: @valid_attrs
assert redirected_to(conn) == video_path(conn, :show, video)
assert Repo.get_by(Video, @valid_attrs)
@tag login_as: "max"
test "creates user video and redirects", %{conn: conn, user: user} do
conn = post conn, video_path(conn, :create), video: @valid_attrs
# assert redirected_to(conn) = video_path(conn, :index) # see: https://github.com/dwyl/learn-phoenix-framework/issues/40
assert Repo.get_by!(Video, @valid_attrs).user_id == user.id
end

test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do
video = Repo.insert! %Video{}
conn = put conn, video_path(conn, :update, video), video: @invalid_attrs
assert html_response(conn, 200) =~ "Edit video"
@tag login_as: "max"
test "does not create video and renders errors when invalid", %{conn: conn} do
count_before = video_count(Video)
conn = post conn, video_path(conn, :create), video: @invalid_attrs
assert html_response(conn, 200) =~ "check the errors"
assert video_count(Video) == count_before
end

test "deletes chosen resource", %{conn: conn} do
video = Repo.insert! %Video{}
conn = delete conn, video_path(conn, :delete, video)
assert redirected_to(conn) == video_path(conn, :index)
refute Repo.get(Video, video.id)
@tag login_as: "max"
test "authorizes actions against access by other users",
%{user: owner, conn: conn} do

video = insert_video(owner, @valid_attrs)
non_owner = insert_user(username: "sneaky") # https://youtu.be/_YQpbzQ6gzs?t=2m48s
conn = assign(conn, :current_user, non_owner)

assert_error_sent :not_found, fn ->
get(conn, video_path(conn, :show, video))
end
assert_error_sent :not_found, fn ->
get(conn, video_path(conn, :edit, video))
end
assert_error_sent :not_found, fn ->
put(conn, video_path(conn, :update, video, video: @valid_attrs))
end
assert_error_sent :not_found, fn ->
delete(conn, video_path(conn, :delete, video))
end
end
end
Loading