Phoenix 1.3.0 版本已發布

由 Chris McCord 於 2017 年 7 月 28 日張貼


Phoenix 1.3.0 版本已經釋出!此版本的重點在於具備改善專案結構的程式碼產生器、首要雨傘專案支援,以及將 Phoenix 強加為 Elixir 應用程式更大規模網際網路介面的鷹架。我們也在 Phoenix.Controller 中包含新的 action_fallback 功能,讓你可以將網域中常見的資料結構轉換為有效的回應。實際上,這可以減少控制器程式碼,並提供單一的地方來處理重複的程式碼路徑。這對 JSON API 控制器 來說尤其實用。此外,1.3 版本還包含頻道連線通訊協定的 V2 版本,它可以解決特定訊息模式下的競爭條件,並改善序列化格式。

若有興趣深入了解變更和設計決策,請看看我的 LonestarElixir 主旨演講:https://www.youtube.com/watch?v=tMO28ar0lW8。請注意,演講中的目錄結構稍微過期,但所有想法仍然適用。

若要使用新的 phx.new 專案產生器,你可以使用以下指令安裝檔案

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

1.3.0 版本對所有產生器使用 phx. 前置字元。舊的產生器仍存在,目的是給社群與學習資源時間迎頭趕上。它們將於 1.4.0 版本中移除。

和以往一樣,我們有 升級指南,其中有從 1.2.x 專案進行移轉的詳細說明。

1.3.0 版本向後相容,因此升級可能只需要將 mix.exs 中的 :phoenix 相依項提升到 “~> 1.3”。對於想要採用新規範的人來說,升級指南將會逐步說明。在升級之前,值得看看主旨演講或探討以下概述的設計決策。

Phoenix 1.3 – 有意圖的設計

新的專案和程式碼產生器會吸取過去兩年的經驗教訓,並在學習過程中引導人們做出更好的設計決策。新的專案有 lib/my_app 目錄,用於存放商業邏輯,以及 lib/my_app_web 目錄,用於存放所有與 Phoenix 相關的網頁模組,它們是 Elixir 應用程式更大規模的網路介面。隨著新的專案結構而來的是新的 phx.gen.htmlphx.gen.json 產生器,它們採用將網頁介面與網域分開的目標。

內容

當您使用 phx.gen.html|json 產生 HTML 或 JSON 資源時,Phoenix 會產生位於 Context 中的程式碼。Context 是專用的模組,可公開與群組相關的功能。例如,每次呼叫 Elixir 的標準程式庫,無論是 Logger.info/1 還是 Stream.map/2,您都在存取不同的 context。在內部,Elixir 的記錄器由多個模組組成,例如 Logger.ConfigLogger.Backends,但我們從未直接與這些模組互動。我們稱 Logger 模組為 context,正因為它公開並群組所有記錄功能。

例如,若要產生「使用者」資源,我們將執行

$ mix phx.gen.html Accounts User users email:string:unique

請注意「帳戶」是一個新的必要的第一個參數。這是您的程式碼將存在其中的 context 模組,它執行應用程式中使用者帳戶的業務邏輯。它可能包含驗證和使用者註冊等功能。以下是所產生程式碼的一部分

# lib/my_app_web/controllers/user_controller.ex
defmodule MyAppWeb.UserController do
  ...
  alias MyApp.Accounts

  def index(conn, _params) do
    users = Accounts.list_users()
    render(conn, "index.html", users: users)
  end

  def create(conn, %{"user" => user_params}) do
    case Accounts.create_user(user_params) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "user created successfully.")
        |> redirect(to: user_path(conn, :show, user))
      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end
  ...
end


# lib/my_app/accounts/accounts.ex
defmodule MyApp.Accounts do
  alias MyApp.Accounts.User

  def list_users do
    Repo.all(User)
  end

  def create_user(attrs \\ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end
  ...
end

您還將在 lib/my_app/accounts/user.ex 內產生一個 Ecto 結構。請注意我們的控制器如何呼叫 API 界限以在系統中建立或擷取使用者。現在我們可以輕鬆地在其他控制器、在 Phoenix channels、在管理任務等中重複使用該邏輯。測試也變得更為直接,因為我們可以在不使用 Web 堆疊的情況下測試網域的內部和外部。

使用 context 進行設計,為您的應用程式發展奠定了堅實的基礎。使用離散的、定義完善的 API ,可公開您的系統意圖,讓您撰寫更具可維護性的應用程式,並包含可重複使用的程式碼。此外,我們只需瀏覽應用程式目錄結構,即可一窺應用程式的作用及其功能集。

lib
 my_app
    accounts
       accounts.ex
       user.ex
    sales
       manager.ex
       sales.ex
       ticket.ex
    repo.ex
 my_app.ex
 my_app_web
    channels
    controllers
    templates
    views
 my_app_web.ex

只要一瞥目錄結構,我們便可以看到此應用程式具有使用者帳戶系統,以及銷售系統。我們還可以推論,透過 sales.exaccounts.ex 模組,這些系統之間存在一個自然 API。我們獲得此見解 而不用查看任何程式碼列。相對應以前的 web/models,它並不顯示檔案之間的任何關聯,而大多反映了資料庫結構,並未提供有關它們如何實際與網域相關的見解

action_fallback

新的 action_fallback 功能允許您指定一個 plug,如果您的控制器動作無法傳回有效的 Plug.Conn{} 結構。然後 action fallback plug 的工作是在控制器動作之前接收連線,以及結果,並將其轉換為有效的 plug 回應。對於 JSON API 而言,這特別好,因為它消除了控制器之間的重複。例如,您的先前控制器可能看起來像這樣


def MyAppWeb.PageController do
  alias MyApp.CMS
  def show(conn, %{"id" => id}) do
    case CMS.get_page(id, conn.assigns.current_user) do
      {:ok, page} -> render(conn, "show.html", page: page)
      {:error, :not_found} ->
        conn
        |> put_status(404)
        |> render(MyAppWeb.ErrorView, :"404")
      {:error, :unauthorized} ->
        conn
        |> put_status(401)
        |> render(MyAppWeb.ErrorView, :"401")
    end
  end
end

這段程式碼本身沒問題,但問題在於我們的網域中常見的資料結構,例如 {:error, :not_found}{:error, :unauthorized} 必須在許多不同的控制器中重複處理。現在有一個透過 action fallback 的更佳方式。有了 1.3 版本,我們可以編寫

def MyAppWeb.PageController do
  alias MyApp.CMS

  action_fallback MyAppWeb.FallbackController

  def show(conn, %{"id" => id}) do
    with {:ok, page} <- CMS.get_page(id, conn.assigns.current_user) do
      render(conn, "show.html", page: page)
    end
  end
end


defmodule MyAppWeb.FallbackController do
  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> render(MyAppWeb.ErrorView, :"404")
  end

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(:unauthorized)
    |> render(MyAppWeb.ErrorView, :"401")
  end
end

請注意,我們的控制器現在可以使用 with 表示式符合正常的路徑。然後,我們可以指定一個 fallback 控制器在一個地方處理回應轉換。這對於程式碼清楚度和消除重複很有幫助。

我們對於這些變更及其在可維護性方面的長期回報感到興奮。我們也覺得它們將導致可共享的獨立程式庫,讓整個社群都能受益 - 在 Phoenix 相關專案中及之外。

如果你在升級方面有問題,請在 #elixir-lang irc 或 slack 上找到我們,我們會為你解決問題!

最後但也並非最不重要的一點,我想花一點時間感謝讓這個專案成為可能的公司。非常感謝 plataformatec 持續支援 Elixir 開發,以及感謝 DockYard 贊助 Phoenix。

編碼愉快! 🐥🔥

-Chris

完整變更紀錄

1.3.0-rc.3 (2017-07-24)

  • 擴充功能

    • [ChannelTest] 訂閱 connectUserSocket.id 來支援強制中斷測試
    • [Socket] 定義頻道路由時支援靜態 :assigns
    • [Channel] 加入纜線頻道纜線通訊協定的 V2,其中已解決競爭條件和壓縮資料負載
    • [phx.new] 使用新的 lib/my_applib/my_app_web 目錄結構
    • [phx.new] 使用新的 MyAppWeb 別名慣例表示網頁模組
    • [phx.gen.context] 不再用內容名稱為 Ecto 資料表加上前綴
  • JavaScript 協力廠商擴充功能

    • 使用 V2 頻道纜線通訊協定支援
  • JavaScript 協力廠商 bug 修正

    • 在伺服器頻道順利加入時,解決客戶端加入逾時發生的競爭條件

1.3.0-rc.2 (2017-05-15)

請參閱 1.2.x1.3.x 升級說明,讓現有的應用程式升級。

  • 擴充功能

    • [Generator] 新增新的 phx.newphx.new.webphx.new.ecto 專案產生器,它們有改進的應用程式結構和對遮蔽式應用程式的支援
    • [Generator] 新增新的 phx.gen.htmlphx.gen.json 資源產生器,它們有改善的 API 邊界隔離
    • [Controller] 新增 current_pathcurrent_url 來產生連線的路徑和網址
    • [Controller] 簡介 action_fallback 來註冊一個 plug 作為控制器動作的 fallback 來呼叫
    • [Controller] 在控制器中封裝例外情況以維護連線狀態
    • [頻道] 新增設定頻道事件紀錄的功能,選項為 :log_join:log_handle_in
    • [頻道] 警告未處理的 handle_info/2 訊息
    • [頻道] 頻道現在可以區分正常退出與應用程式重新啟動,使用戶端能進入錯誤模式,並在冷部署後重新連線。
    • [路由器] 編寫文件,說明 match 支援對應任何 HTTP 方法及特殊 :* 參數
    • [路由器] 使用路徑參數填入 conn.path_params
    • [ConnTest] 新增 redirected_params/1,傳回路由器對應的重定向網址中之命名參數
    • [摘要處理器] 新增 mix phx.digest.clean,以移除已編譯資源的舊版本
    • [phx.new] 在 phx.new 安裝器檔案中新增 Erlang 20 支援
  • 臭蟲修復

    • [控制器] 強化限制當地重新導向,防止任意網址重新導向
    • [控制器] 修復使用 clear_flash/1 時,快閃資料會保留的問題
  • 已過時

    • [產生器] 所有 phoenix.* mix 任務已被 phx.* 新任務取代,因此已過時
  • JavaScript 協力廠商擴充功能

    • 新增傳遞 encodedecode 函式至 Socket 建構函式的功能,用於自訂編碼與解碼傳出與傳入的訊息。
    • 偵測使用者端的逾時心跳,處理非正常連線中斷,加快 Socket 錯誤偵測的速度
    • 新增支援 AMD/RequireJS