
Coba : Phoenix-chat .fly.dev
Tutorial langkah demi langkah untuk membangun, menguji dan menggunakan aplikasi obrolan di Phoenix!
page_controller_test.exsuser_socket.jsexcoveralls sebagai Ketergantungan (Pengembangan) ke mix.exscoveralls.jsonPresence untuk melacak siapa yang online Aplikasi obrolan adalah "Hello World" dari contoh waktu nyata.
Sayangnya, sebagian besar contoh aplikasi menunjukkan beberapa dasar dan kemudian abaikan sisanya ...? ️
Jadi pemula sering tersesat atau bingung tentang apa yang harus mereka lakukan atau pelajari selanjutnya !
Sangat sedikit tutorial yang mempertimbangkan pengujian, penyebaran, dokumentasi atau " peningkatan " lainnya yang semuanya merupakan bagian dari " dunia nyata " membangun dan menjalankan aplikasi; Jadi itu adalah topik yang akan kami bahas untuk " mengisi celah ".
Kami menulis tutorial ini untuk menjadi cara termudah untuk mempelajari Phoenix , Ecto dan Channels dengan contoh praktis yang dapat diikuti siapa pun .
Ini adalah contoh/tutorial yang kami harapkan ketika kami belajar Elixir , Phoenix ... jika Anda merasa berguna, tolong terima kasih!
Tutorial langkah demi langkah sederhana yang menunjukkan kepada Anda cara:
mix phx.new chat )Fly.io sehingga Anda dapat menunjukkan kepada orang -orang kreasi Anda!Awalnya , kami dengan sengaja melewatkan file konfigurasi dan " internal Phoenix " karena Anda ( pemula ) tidak perlu tahu tentang mereka untuk memulai . Tapi jangan khawatir, kita akan kembali ke mereka saat dibutuhkan . Kami menyukai " tepat waktu " ( saat Anda membutuhkannya ) belajar karena segera jelas dan praktis mengapa kami belajar sesuatu.
Contoh ini untuk pemula lengkap sebagai aplikasi " Phoenix pertama saya ".
Kami mencoba berasumsi sesedikit mungkin, tetapi jika Anda pikir kami " melewatkan satu langkah " atau Anda merasa " macet " dengan alasan apa pun, atau memiliki pertanyaan ( terkait dengan contoh ini ), buka masalah di GitHub!
Baik komunitas @Dwyl dan Phoenix ramah pemula , jadi jangan takut/pemalu.
Juga, dengan mengajukan pertanyaan, Anda membantu semua orang yang terjebak atau mungkin terjebak dengan hal yang sama !
Instruksi ini menunjukkan kepada Anda cara membuat aplikasi obrolan dari awal .
brew install elixirCatatan : Jika Anda sudah menginstal
Elixirdi Mac Anda, dan hanya ingin meningkatkan ke versi terbaru, jalankan:brew upgrade elixir
mix archive.install hex phx_new Pengetahuan Sintaks Elixir Dasar akan membantu,
Silakan lihat: Dwyl/ Learn-Elixir
Pengetahuan JavaScript dasar menguntungkan ( tetapi tidak penting karena kode "front-end" cukup mendasar dan terarah dengan baik ). Lihat: Dwyl/JavaScript-the-Good-Parts-Notes
Periksa Anda memiliki versi Elixir terbaru ( jalankan perintah berikut di terminal Anda ):
elixir -vAnda harus melihat sesuatu seperti:
Erlang/OTP 25 [erts-13.1.1] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Elixir 1.14.1 (compiled with Erlang/OTP 25)Periksa Anda memiliki versi terbaru Phoenix :
mix phx.new -vAnda harus melihat:
Phoenix installer v1.7.0-rc.2Catatan : Jika versi
PhoenixAnda lebih baru , jangan ragu untuk memperbarui dokumen ini! Kami mencoba yang terbaik untuk memperbaruinya ... tetapi kontribusi Anda selalu diterima!
Dalam tutorial ini, kami menggunakan Phoenix 1.7-RC2, kandidat rilis kedua untuk
Phoenix 1.7. Pada saat penulisan, jika Anda menginstal Phoenix, versi stabil terbaru bukanv1.7. Untuk menggunakan versi ini, ikuti panduan resmi (jangan khawatir, itu hanya menjalankan satu perintah!)-> https://www.phoenixframework.org/blog/phoenix-1.7-releasedNamun, jika Anda membaca ini setelah dirilis,
v1.7akan diinstal untuk Anda, dan Anda akan melihatPhoenix installer v1.7.0di terminal Anda.
Konfirmasi PostgreSQL sedang berjalan ( sehingga aplikasi dapat menyimpan pesan obrolan ) menjalankan perintah berikut:
lsof -i :5432Anda harus melihat output yang mirip dengan yang berikut:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
postgres 529 Nelson 5u IPv6 0xbc5d729e529f062b 0t0 TCP localhost:postgresql (LISTEN)
postgres 529 Nelson 6u IPv4 0xbc5d729e55a89a13 0t0 TCP localhost:postgresql (LISTEN) Ini memberi tahu kami bahwa PostgreSQL adalah " Mendengarkan " di port TCP 5432 ( port default )
Jika perintah lsof tidak menghasilkan hasil apa pun di terminal Anda, jalankan:
pg_isreadyItu harus mencetak berikut:
/tmp:5432 - accepting connectionsDengan semua "pemeriksaan pra-penerbangan" yang dilakukan, mari kita terbang !
Sebelum Anda mencoba membangun aplikasi obrolan dari awal, klon dan menjalankan versi kerja yang sudah selesai untuk mendapatkan gambaran tentang apa yang diharapkan.
Di terminal Anda jalankan perintah berikut untuk mengkloning repo:
git clone [email protected]:dwyl/phoenix-chat-example.git Ubah ke direktori phoenix-chat-example dan instal kedua dependensi Elixir dan Node.js dengan perintah ini:
cd phoenix-chat-example
mix setupJalankan aplikasi Phoenix dengan perintah:
mix phx.serverJika Anda membuka aplikasi LocalHost: 4000 di dua browser web lagi, Anda dapat melihat pesan obrolan yang ditampilkan di semuanya segera setelah Anda menekan tombol Enter :

Sekarang setelah Anda mengkonfirmasi bahwa aplikasi obrolan Phoenix yang sudah selesai berfungsi di mesin Anda, saatnya untuk membangunnya dari awal!
Ubah Direktori:
cd ..Dan mulailah membangun!
Dalam program terminal Anda di LocalHost Anda, ketikkan perintah berikut untuk membuat aplikasi:
mix phx.new chat --no-mailer --no-dashboard --no-gettext Itu akan membuat struktur direktori dan file proyek.
Kami menjalankan perintah
mix phx.newdengan argumen--no-mailer--no-dashboard--no-gettextkarena kami tidak ingin proyek kami menghasilkan file mailer, untuk menyertakanPhoenix.LiveDashboarddan menghasilkan filegettext(untuki18n).
Ketika diminta untuk " mengambil dan menginstal dependensi ? [Yn]",
Ketik Y di terminal Anda, diikuti oleh kunci enter ( return ).
Anda harus melihat: 
Ubah direktori ke direktori chat dengan menjalankan perintah yang disarankan:
cd chatSekarang jalankan perintah berikut:
mix setupCatatan : Pada titik ini sudah ada "aplikasi" itu hanya tidak melakukan apa pun (belum) ...
Anda dapat menjalankanmix phx.serverdi terminal Anda - jangan khawatir jika Anda melihat kesalahan
Pesan, ini karena kami belum membuat database kami.
Kami akan mengurusnya di Langkah 6!
Untuk saat ini, buka http: // localhost: 4000 di browser Anda
Dan Anda akan melihat beranda "Welcome to Phoenix"default:

Matikan server Phoenix di terminal Anda dengan perintah CTRL + C.
Di jendela terminal Anda, jalankan perintah berikut:
mix test
Anda harus melihat output yang mirip dengan yang berikut:
Generated chat app
.....
Finished in 0.02 seconds (0.02s async, 0.00s sync)
5 tests, 0 failures
Randomized with seed 84184Sekarang kami telah mengkonfirmasi bahwa semuanya berfungsi (semua tes lulus), mari kita lanjutkan ke bagian yang menarik !
Hasilkan saluran (WebSocket) yang akan digunakan di aplikasi obrolan:
mix phx.gen.channel RoomJika Anda diminta untuk mengkonfirmasi pemasangan soket handler baru tipe
ydan tekan tombol[Enter].
Ini akan membuat tiga file :
* creating lib/chat_web/channels/room_channel.ex
* creating test/chat_web/channels/room_channel_test.exs
* creating test/support/channel_case.exSelain membuat dua file lagi :
* creating lib/chat_web/channels/user_socket.ex
* creating assets/js/user_socket.js File room_channel.ex menangani pesan yang menerima/mengirim pesan dan tes interaksi room_channel_test.exs dengan saluran. Kami akan fokus pada file socket yang dibuat setelahnya. ( Jangan khawatir tentang ini, kita akan melihat file uji pada langkah 14 di bawah ini !)
Kami diberitahu bahwa kami perlu memperbarui sepotong kode di aplikasi kami:
Add the socket handler to your ` lib/chat_web/endpoint.ex ` , for example:
socket " /socket " , ChatWeb.UserSocket,
websocket: true,
longpoll: false
For the front-end integration, you need to import the ` user_socket.js `
in your ` assets/js/app.js ` file:
import " ./user_socket.js " Generator meminta kami untuk mengimpor kode klien di frontend. Mari kita lakukan nanti. Untuk saat ini, buka file lib/chat_web/endpoint.ex dan ikuti instruksi.
Setelah ini, buka file yang disebut /lib/chat_web/channels/user_socket.ex
dan ubah garis:
channel "room:*" , ChatWeb.RoomChannelke:
channel "room:lobby" , ChatWeb.RoomChannelPeriksa perubahan di sini.
Ini akan memastikan bahwa pesan apa pun yang dikirim ke "room:lobby" dialihkan ke RoomChannel kami.
"room.* Sebelumnya berarti bahwa setiap subtopik di dalam "room" dialihkan. Tapi untuk saat ini, mari kita sempit menjadi hanya satu subtopik?
Untuk detail lebih lanjut tentang saluran Phoenix, ( kami sangat merekomendasikan Anda ) Baca: https://hexdocs.pm/phoenix/channels.html
Buka /lib/chat_web/controllers/page_html/home.html.heex file
dan copy-paste ( atau ketik ) kode berikut:
<!-- The list of messages will appear here: -->
< div class =" mt-[4rem] " >
< ul id =" msg-list " phx-update =" append " class =" pa-1 " > </ ul >
</ div >
< footer class =" bg-slate-800 p-2 h-[3rem] fixed bottom-0 w-full flex justify-center " >
< div class =" w-full flex flex-row items-center text-gray-700 focus:outline-none font-normal " >
< input type =" text " id =" name " placeholder =" Name " required
class =" grow-0 w-1/6 px-1.5 py-1.5 " />
< input type =" text " id =" msg " placeholder =" Your message " required
class =" grow w-2/3 mx-1 px-2 py-1.5 " />
< button id =" send " class =" text-white bold rounded px-3 py-1.5 w-fit
transition-colors duration-150 bg-sky-500 hover:bg-sky-600 " >
Send
</ button >
</ div >
</ footer > Ini adalah bentuk dasar yang akan kita gunakan untuk memasukkan pesan obrolan.
Kelas-kelasnya misalnya w-full dan items-center adalah kelas TailwindCSS untuk menata formulir.
Phoenix menyertakan Tailwind secara default sehingga Anda bisa naik dan berjalan dengan aplikasi/ide/"MVP" Anda!
Jika Anda baru di
Tailwind, silakan lihat: dwyl/ learn-tailwindJika Anda memiliki pertanyaan tentang salah satu kelas
Tailwindyang digunakan, silakan habiskan 2 menit googling atau mencari dokumen resmi (luar biasa!): Tailwindcss.com/docs dan kemudian jika Anda masih macet, silakan buka masalah.
File template home.html.heex Anda akan terlihat seperti ini: /lib/chat_web/controllers/page_html/home.html.heex
Buka file lib/chat_web/components/layouts/root.html.heex dan temukan tag <body> . Ganti konten <body> dengan kode berikut:
< body class =" bg-white antialiased min-h-screen flex flex-col " >
< header class =" bg-slate-800 w-full h-[4rem] top-0 fixed flex flex-col justify-center z-10 " >
< div class =" flex flex-row justify-center items-center " >
< h1 class =" w-4/5 md:text-3xl text-center font-mono text-white " >
Phoenix Chat Example
</ h1 >
</ div >
</ header >
< %= @inner_content % >
</ body > File template root.html.heex Anda akan terlihat seperti ini: /lib/chat_web/components/layouts/root.html.heex
Pada akhir langkah ini, jika Anda menjalankan Phoenix Server mix phx.server , dan lihat aplikasi di browser Anda, itu akan terlihat seperti ini:

Jadi sudah mulai terlihat seperti aplikasi obrolan dasar. Sayangnya, karena kami mengubah salinan home.html.heex page_controller_test.exs kami sekarang gagal:
Jalankan perintah:
mix test 1) test GET / (ChatWeb.PageControllerTest)
test/chat_web/controllers/page_controller_test.exs:4
Assertion with =~ failed
code: assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
Untungnya ini mudah diperbaiki.
page_controller_test.exs Buka file test/chat_web/controllers/page_controller_test.exs dan ganti baris:
assert html_response ( conn , 200 ) =~ "Peace of mind from prototype to production"Dengan:
assert html_response ( conn , 200 ) =~ "Phoenix Chat Example"Sekarang jika Anda menjalankan tes lagi, mereka akan lulus:
mix test
Output sampel:
........
Finished in 0.1 seconds (0.09s async, 0.06s sync)
8 tests, 0 failures
Randomized with seed 275786
Open assets/js/app.js , Uncomment dan Ubah Garis:
import socket from "./user_socket.js" Dengan garis tanpa komentar , aplikasi kami akan mengimpor file socket.js yang akan memberi kami fungsionalitas websocket.
Kemudian tambahkan kode javascript berikut ("klien") ke bagian bawah file:
/* Message list code */
const ul = document . getElementById ( 'msg-list' ) ; // list of messages.
const name = document . getElementById ( 'name' ) ; // name of message sender
const msg = document . getElementById ( 'msg' ) ; // message input field
const send = document . getElementById ( 'send' ) ; // send button
const channel = socket . channel ( 'room:lobby' , { } ) ; // connect to chat "room"
channel . join ( ) ; // join the channel.
// Listening to 'shout' events
channel . on ( 'shout' , function ( payload ) {
render_message ( payload )
} ) ;
// Send the message to the server on "shout" channel
function sendMessage ( ) {
channel . push ( 'shout' , {
name : name . value || "guest" , // get value of "name" of person sending the message. Set guest as default
message : msg . value , // get message text (value) from msg input field.
inserted_at : new Date ( ) // date + time of when the message was sent
} ) ;
msg . value = '' ; // reset the message input field for next message.
window . scrollTo ( 0 , document . documentElement . scrollHeight ) // scroll to the end of the page on send
}
// Render the message with Tailwind styles
function render_message ( payload ) {
const li = document . createElement ( "li" ) ; // create new list item DOM element
// Message HTML with Tailwind CSS Classes for layout/style:
li . innerHTML = `
<div class="flex flex-row w-[95%] mx-2 border-b-[1px] border-slate-300 py-2">
<div class="text-left w-1/5 font-semibold text-slate-800 break-words">
${ payload . name }
<div class="text-xs mr-1">
<span class="font-thin"> ${ formatDate ( payload . inserted_at ) } </span>
<span> ${ formatTime ( payload . inserted_at ) } </span>
</div>
</div>
<div class="flex w-3/5 mx-1 grow">
${ payload . message }
</div>
</div>
`
// Append to list
ul . appendChild ( li ) ;
}
// Listen for the [Enter] keypress event to send a message:
msg . addEventListener ( 'keypress' , function ( event ) {
if ( event . key === `Enter` && msg . value . length > 0 ) { // don't sent empty msg.
sendMessage ( )
}
} ) ;
// On "Send" button press
send . addEventListener ( 'click' , function ( event ) {
if ( msg . value . length > 0 ) { // don't sent empty msg.
sendMessage ( )
}
} ) ;
// Date formatting
function formatDate ( datetime ) {
const m = new Date ( datetime ) ;
return m . getUTCFullYear ( ) + "/"
+ ( "0" + ( m . getUTCMonth ( ) + 1 ) ) . slice ( - 2 ) + "/"
+ ( "0" + m . getUTCDate ( ) ) . slice ( - 2 ) ;
}
// Time formatting
function formatTime ( datetime ) {
const m = new Date ( datetime ) ;
return ( "0" + m . getUTCHours ( ) ) . slice ( - 2 ) + ":"
+ ( "0" + m . getUTCMinutes ( ) ) . slice ( - 2 ) + ":"
+ ( "0" + m . getUTCSeconds ( ) ) . slice ( - 2 ) ;
}Luangkan waktu sejenak untuk membaca kode JavaScript dan konfirmasikan pemahaman Anda tentang apa yang dilakukannya.
Semoga komentar in-line ini jelas, tetapi jika ada yang tidak jelas, tolong tanyakan!
Pada titik ini file app.js Anda akan terlihat seperti ini: /assets/js/app.js
user_socket.js Secara default saluran Phoenix (klien) akan berlangganan ruang generik: "topic:subtopic" . Karena kami tidak akan menggunakan ini, kami dapat menghindari melihat kesalahan "unable to join: unmatched topic" di browser/konsol kami dengan hanya mengomentari beberapa baris dalam file user_socket.js . Buka file di editor Anda dan temukan baris berikut:
let channel = socket . channel ( "room:42" , { } )
channel . join ( )
. receive ( "ok" , resp => { console . log ( "Joined successfully" , resp ) } )
. receive ( "error" , resp => { console . log ( "Unable to join" , resp ) } )Komentari baris sehingga mereka tidak akan dieksekusi:
//let channel = socket.channel("room:42", {})
//channel.join()
// .receive("ok", resp => { console.log("Joined successfully", resp) })
// .receive("error", resp => { console.log("Unable to join", resp) }) user_socket.js Anda sekarang akan terlihat seperti ini: /assets/js/user_socket.js
Jika Anda kemudian memutuskan untuk merapikan aplikasi obrolan Anda, Anda dapat
deletebaris yang dikomentari ini dari file.
Kami hanya menyimpannya untuk referensi cara bergabung dengan saluran dan menerima pesan.
Jika Anda menjalankan aplikasi, cobalah untuk mengisi bidang name dan message dan klik Enter (atau tekan Send ).
Pesannya akan muncul di jendela yang berbeda!

Dengan ini selesai, kita bisa melanjutkan.
Jika kami tidak ingin menyimpan riwayat obrolan, kami hanya dapat menggunakan aplikasi ini dan kami akan selesai!
Bahkan, ini bisa menjadi " fitur penggunaan "/" fitur " untuk memiliki obrolan " fana " tanpa riwayat apa pun ... lihat: http://www.psstchat.com/.
Tetapi kami berasumsi bahwa sebagian besar aplikasi obrolan menyimpan sejarah sehingga orang -orang
newyang bergabung dengan "saluran" dapat melihat sejarah dan orang -orang yang secara singkat "absen" dapat "mengejar" sejarah.
Jalankan perintah berikut di terminal Anda:
mix phx.gen.schema Message messages name:string message:stringAnda akan melihat output berikut:
* creating lib/chat/message.ex
* creating priv/repo/migrations/20230203114114_create_messages.exs
Remember to update your repository by running migrations:
$ mix ecto.migrateMari kita hancurkan perintah itu untuk kejelasan:
mix phx.gen.schema - Perintah campuran untuk membuat skema baru (tabel database)Message - Nama tunggal untuk direkam dalam "koleksi" pesan kamimessages - Nama koleksi ( atau tabel database )name:string - Nama orang yang mengirim pesan, disimpan sebagai string .message:string - Pesan yang dikirim oleh orang tersebut, juga disimpan sebagai string . Baris creating lib/chat/message.ex membuat "skema" untuk tabel database pesan kami.
Selain itu file migrasi dibuat, misalnya: creating priv/repo/migrations/20230203114114_create_messages.exs " Migrasi " sebenarnya membuat tabel database dalam database kami.
Di terminal Anda jalankan perintah berikut untuk membuat tabel messages :
mix ecto.migrateUntuk konteks kami merekomendasikan membaca: hexdocs.pm/ecto_sql/ ecto.migration .html
Anda harus melihat yang berikut di terminal Anda:
11:42:10.130 [info] == Running 20230203114114 Chat.Repo.Migrations.CreateMessages.change/0 forward
11:42:10.137 [info] create table messages
11:42:10.144 [info] == Migrated 20230203114114 in 0.0s Jika Anda membuka PostgreSQL GUI ( misalnya: PGADMIN ) Anda akan melihat bahwa tabel pesan telah dibuat di database chat_dev :

Anda dapat melihat skema tabel dengan " klik kanan " ( ctrl + click pada Mac ) pada tabel messages dan memilih "properti":

Catatan : Untuk bagian 7, 8, dan 9 kita akan menyempurnakan bagaimana kode kita "menangani" berbagai peristiwa yang dapat terjadi di aplikasi obrolan kita.
Phoenix mengabstraksikan banyak logika yang mendasari pesan dalam komunikasi proses Elixir (untuk info lebih lanjut tentang bagaimana proses elixir berkomunikasi, baca di sini).
Di Phoenix, acara/pesan yang dikirim dari klien secara otomatis dialihkan ke fungsi penangan yang sesuai berdasarkan nama acara, membuat penanganan pesan mulus dan mudah!.
Buka file lib/chat_web/channels/room_channel.ex dan di dalam fungsi def handle_in("shout", payload, socket) do menambahkan baris berikut:
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert Sehingga fungsi Anda berakhir seperti ini:
def handle_in ( "shout" , payload , socket ) do
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert
broadcast socket , "shout" , payload
{ :noreply , socket }
end Jika Anda memperhatikan sebelumnya, dalam file assets/js/app.js kami, kami menggunakan fungsi sendMessage() untuk mendorong pesan kami ke server pada acara "Shout".
Phoenix merutekan pesan ke fungsi server-sisi handle_in("shout", payload, socket) karena nama acara cocok dengan 'teriakan'.
Dalam fungsi ini, kami menangani muatan (yang merupakan teks pesan dan data lainnya) dan memasukkannya ke dalam database kami. Rapi!
Buka file lib/chat/message.ex dan impor Ecto.Query :
defmodule Chat.Message do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query # add Ecto.Query
Kemudian tambahkan fungsi baru ke dalamnya:
def get_messages ( limit \ 20 ) do
Chat.Message
|> limit ( ^ limit )
|> order_by ( desc: :inserted_at )
|> Chat.Repo . all ( )
end Fungsi ini menerima limit parameter tunggal untuk hanya mengembalikan jumlah catatan tetap/maksimum. Ini menggunakan all fungsi ECTO untuk mengambil semua catatan dari database. Message adalah nama skema/tabel yang ingin kami dapatkan catatan, dan batas adalah jumlah maksimum catatan untuk diambil.
Di file /lib/chat_web/channels/room_channel.ex membuat fungsi baru:
@ impl true
def handle_info ( :after_join , socket ) do
Chat.Message . get_messages ( )
|> Enum . reverse ( ) # revers to display the latest message at the bottom of the page
|> Enum . each ( fn msg -> push ( socket , "shout" , % {
name: msg . name ,
message: msg . message ,
inserted_at: msg . inserted_at ,
} ) end )
{ :noreply , socket } # :noreply
end dan di bagian atas file memperbarui fungsi join ke yang berikut:
def join ( "room:lobby" , payload , socket ) do
if authorized? ( payload ) do
send ( self ( ) , :after_join )
{ :ok , socket }
else
{ :error , % { reason: "unauthorized" } }
end
endCATATAN : Seperti bagian 7, Phoenix tahu untuk memanggil fungsi ini ketika server mengirim pesan internal
:after_joinmelalui proses saluran.Fungsi
join/3kami dilib/chat_web/channels/room_channel.exmengirimkannya:after_join messageke proses saluran ketika klien berhasil terhubung ke topik"room:lobby".
Mulai server Phoenix ( jika belum berjalan ):
mix phx.serverCatatan : Dibutuhkan beberapa detik untuk dikompilasi .
Di terminal Anda, Anda harus melihat:
[info] Running ChatWeb.Endpoint with cowboy 2.8.0 at 0.0.0.0:4000 (http)
[info] Access ChatWeb.Endpoint at http://localhost:4000
webpack is watching the files… Ini memberi tahu kami bahwa kode kami dikompilasi ( seperti yang diharapkan ) dan aplikasi obrolan berjalan di TCP Port 4000 !
Buka aplikasi web obrolan di dua windows browser terpisah : http: // localhost: 4000
( Jika mesin Anda hanya memiliki satu browser, coba gunakan tab "penyamaran" )
Anda harus dapat mengirim pesan di antara dua jendela browser: 
Selamat! Anda memiliki aplikasi obrolan yang berfungsi ( dasar ) yang ditulis di Phoenix!
Sejarah obrolan (pesan) disimpan !
Ini berarti Anda dapat menyegarkan browser atau bergabung di browser yang berbeda dan Anda masih akan melihat sejarahnya!
Pengujian otomatis adalah salah satu cara terbaik untuk memastikan keandalan dalam aplikasi web Anda.
Catatan : Jika Anda benar-benar baru dalam pengujian otomatis atau "pengembangan yang didorong oleh tes" ("TDD"), kami sarankan membaca/mengikuti tutorial "Dasar": github.com/dwyl/ belajar-tdd
Pengujian di Phoenix cepat ( tes dijalankan secara paralel! ) Dan mudah untuk memulai! Kerangka pengujian ExUnit adalah built-in sehingga tidak ada "keputusan/debat" tentang kerangka kerja atau gaya mana yang akan digunakan.
Jika Anda belum pernah melihat atau menulis tes dengan ExUnit , jangan takut, sintaks harus terbiasa jika Anda telah menulis segala jenis tes otomatis di masa lalu.
Setiap kali Anda membuat aplikasi Phoenix baru atau menambahkan fitur baru ( seperti saluran ), Phoenix menghasilkan tes baru untuk Anda.
Kami menjalankan tes menggunakan perintah mix test :
... ... ..
Finished in 0.1 seconds ( 0.05 s async , 0.06 s sync )
8 tests , 0 failures
Randomized with seed 157426Dalam hal ini tidak satu pun dari tes ini gagal. ( 8 tes, 0 kegagalan )
Perlu mengambil waktu sejenak ( atau selama yang Anda butuhkan !) Untuk memahami apa yang terjadi dalam file /room_channel_test.exs . Buka jika Anda belum melakukannya, baca deskripsi & kode tes.
Untuk sedikit konteks, kami merekomendasikan membaca: https://hexdocs.pm/phoenix/ test_channels .html
Mari kita lihat tes pertama di /test/chat_web/channels/room_channel_test.exs:
test "ping replies with status ok" , % { socket: socket } do
ref = push socket , "ping" , % { "hello" => "there" }
assert_reply ref , :ok , % { "hello" => "there" }
end Tes mendapatkan socket dari fungsi setup ( pada baris 6 file ) dan menetapkan hasil memanggil fungsi push ke variabel ref push hanya mendorong pesan ( peta %{"hello" => "there"} ) pada socket ke topik "ping" .
Klausul fungsi handle_in yang menangani topik "ping" :
def handle_in ( "ping" , payload , socket ) do
{ :reply , { :ok , payload } , socket }
end Cukup balas muatan yang Anda kirimkan, oleh karena itu dalam pengujian kami, kami dapat menggunakan assert_reply makro untuk menyatakan bahwa ref itu sama dengan :ok, %{"hello" => "there"}
Catatan : Jika Anda memiliki pertanyaan atau perlu bantuan memahami tes lain, buka masalah di GitHub, kami dengan senang hati memperluas ini lebih lanjut!
( Kami hanya berusaha untuk menjaga tutorial ini cukup "singkat" sehingga pemula tidak "kewalahan" oleh apa pun ...)
Seringkali kita dapat belajar banyak tentang aplikasi ( atau API ) dari membaca tes dan melihat di mana "celah" dalam pengujian.
Untungnya kami dapat mencapai ini hanya dengan beberapa langkah:
excoveralls sebagai Ketergantungan (Pengembangan) ke mix.exs Buka file mix.exs Anda dan temukan fungsi "DEPS":
defp deps do
Tambahkan koma ke akhir baris terakhir, lalu tambahkan baris berikut ke akhir daftar:
{ :excoveralls , "~> 0.15.2" , only: [ :test , :dev ] } # tracking test coverage Selain itu, temukan bagian def project do ( ke atas mix.exs ) dan tambahkan baris berikut ke daftar:
test_coverage : [ tool : E xCoveralls ] ,
preferred_cli_env: [
coveralls : :test ,
"coveralls.detail": :test ,
"coveralls.post": :test ,
"coveralls.html": :test
] Kemudian , instal ketergantungan pada excoveralls yang baru saja kami tambahkan ke mix.exs :
mix deps.getAnda harus melihat:
Resolving Hex dependencies...
Dependency resolution completed:
* Getting excoveralls (Hex package)
... etc.coveralls.json Di "root" ( Direktori Dasar ) dari proyek obrolan, buat file baru yang disebut coveralls.json dan copy-paste berikut:
{
"coverage_options" : {
"minimum_coverage" : 100
},
"skip_files" : [
" test/ " ,
" lib/chat/application.ex " ,
" lib/chat_web.ex " ,
" lib/chat_web/telemetry.ex " ,
" lib/chat_web/components/core_components.ex " ,
" lib/chat_web/channels/user_socket.ex "
]
}
File ini cukup mendasar, ini menginstruksikan aplikasi coveralls untuk memerlukan minimum_coverage 100% ( yaitu semuanya diuji 1 ) dan mengabaikan file dalam test/ direktori untuk pemeriksaan cakupan. Kami juga mengabaikan file seperti application.ex , telemetry.ex , core_components.ex dan user_socket.ex karena mereka tidak relevan untuk fungsionalitas proyek kami.
1 Kami percaya bahwa menginvestasikan sedikit waktu di muka untuk menulis tes untuk semua kode kami layak untuk memiliki lebih sedikit bug nanti.
Bug mahal , tes murah dan kepercayaan diri / keandalan tidak ternilai harganya .
Untuk menjalankan tes dengan cakupan, copy-paste perintah berikut ke terminal Anda:
MIX_ENV = test mix do coveralls . json
Untuk penggunaan windows:
$ env :MIX_ENV = "test" ; mix do coveralls . json
Anda harus melihat:
Randomized with seed 527109
----------------
COV FILE LINES RELEVANT MISSED
100.0% lib/chat.ex 9 0 0
100.0% lib/chat/message.ex 26 4 0
100.0% lib/chat/repo.ex 5 0 0
70.0% lib/chat_web/channels/room_channel.ex 46 10 3
100.0% lib/chat_web/components/layouts.ex 5 0 0
100.0% lib/chat_web/controllers/error_html.ex 19 1 0
100.0% lib/chat_web/controllers/error_json.ex 15 1 0
100.0% lib/chat_web/controllers/page_controller 9 1 0
100.0% lib/chat_web/controllers/page_html.ex 5 0 0
100.0% lib/chat_web/endpoint.ex 49 0 0
66.7% lib/chat_web/router.ex 27 3 1
[TOTAL] 80.0%
----------------
Seperti yang dapat kita se di sini, hanya 80% baris kode di /lib yang "dibahas" oleh tes yang telah kita tulis.
Untuk melihat liputan di browser web, jalankan berikut:
MIX_ENV = test mix coveralls . html ; open cover / excoveralls . html Ini akan membuka Laporan Cakupan (HTML) di browser web default Anda:

Buka file test/chat_web/channels/room_channel_test.exs dan tambahkan tes berikut:
test ":after_join sends all existing messages" , % { socket: socket } do
# insert a new message to send in the :after_join
payload = % { name: "Alex" , message: "test" }
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert ( )
{ :ok , _ , socket2 } = ChatWeb.UserSocket
|> socket ( "person_id" , % { some: :assign } )
|> subscribe_and_join ( ChatWeb.RoomChannel , "room:lobby" )
assert socket2 . join_ref != socket . join_ref
end Akhirnya, di dalam lib/chat_web/router.ex , komentar sepotong kode berikut.
pipeline :api do
plug :accepts , [ "json" ]
end Karena kami tidak menggunakan ini :api dalam proyek ini, tidak perlu mengujinya.
Sekarang saat Anda menjalankan MIX_ENV=test mix do coveralls.json Anda harus melihat:
Randomized with seed 15920
----------------
COV FILE LINES RELEVANT MISSED
100.0% lib/chat.ex 9 0 0
100.0% lib/chat/message.ex 26 4 0
100.0% lib/chat/repo.ex 5 0 0
100.0% lib/chat_web/channels/room_channel.ex 46 10 0
100.0% lib/chat_web/components/layouts.ex 5 0 0
100.0% lib/chat_web/controllers/error_html.ex 19 1 0
100.0% lib/chat_web/controllers/error_json.ex 15 1 0
100.0% lib/chat_web/controllers/page_controller 9 1 0
100.0% lib/chat_web/controllers/page_html.ex 5 0 0
100.0% lib/chat_web/endpoint.ex 49 0 0
100.0% lib/chat_web/router.ex 27 2 0
[TOTAL] 100.0%
----------------
Tes ini hanya membuat pesan sebelum subscribe_and_join sehingga ada pesan dalam database untuk dikirim ke clien mana pun yang bergabung dengan obrolan.
Dengan begitu :after_join memiliki setidaknya satu pesan dan Enum.each akan dipanggil setidaknya sekali.
Dengan itu aplikasi kami sepenuhnya diuji!
Kami dapat memperluas proyek ini untuk mendukung otentikasi dasar. Jika Anda ingin memahami bagaimana otentikasi diimplementasikan dengan cara yang mudah/cepat , lihat: auth.md
Presence untuk melacak siapa yang online Salah satu keuntungan besar menggunakan Phoenix adalah Anda dapat dengan mudah melacak proses dan saluran.
Ini membuka jalan untuk dengan mudah menunjukkan siapa yang online atau tidak!
Jika Anda tertarik untuk mengembangkan fitur ini, kami telah membuat panduan di presence.md hanya untuk Anda! ?
Integrasi berkelanjutan memungkinkan Anda mengotomatiskan menjalankan tes untuk memeriksa/mengonfirmasi bahwa aplikasi Anda berfungsi seperti yang diharapkan ( sebelum digunakan ). Ini mencegah secara tidak sengaja " melanggar " aplikasi Anda.
Untungnya langkah -langkahnya cukup sederhana.
Sebagai contoh ci.yml , lihat:
.github/workflows/ci.yml
Penyebaran untuk terbang.io membutuhkan beberapa menit, kami sarankan mengikuti panduan resmi: fly.io/docs/elixir/ Getting-started
Setelah Anda digunakan, Anda akan dapat melihat/menggunakan aplikasi Anda di peramban web/seluler apa pun.
misalnya: phoenix-chat .fly.dev/

Jika Anda menemukan contoh ini berguna, silakan ️ Repositori GitHub sehingga kami ( dan orang lain ) tahu Anda menyukainya!
Jika Anda ingin mempelajari lebih lanjut phoenix dan keajaiban LiveView , pertimbangkan untuk membaca tutorial pemula kami: github.com/dwyl/ phoenix-liveview-counter-tutorial
Untuk versi aplikasi obrolan menggunakan Liveview Anda dapat membaca repositori berikut: github.com/dwyl/ phoenix-liveview-cat-example
Terima kasih telah belajar dengan kami! ☀️
Repo ini terinspirasi oleh contoh obrolan sederhana @chrismccord: https://github.com/chrismccord/phoenix_chat_example ❤️
Pada saat penulisan contoh Chris terakhir diperbarui pada 20 Feb 2018 dan menggunakan Phoenix 1.3 Lihat: Masalah/40.
Ada beberapa perbedaan (perubahan perubahan) antara Phoenix 1.3 dan 1.6 ( versi terbaru ).
Tutorial kami menggunakan Phoenix 1.6.2 (terbaru pada Oktober 2021). Harapan kami adalah bahwa dengan menulis ( dan memelihara ) tutorial fokus pemula langkah demi langkah kami berkontribusi pada komunitas Elixir/Phoenix tanpa menumpuk PR pada repo Chris.