2016-12-20 17 views
1

Я общая функция управления, который я хотел бы быть повторно использованы между контроллерами:Где поставить общий код контроллера

def render_unprocessable_entity(conn, changeset) do 
    conn 
    |> put_status(:unprocessable_entity) 
    |> render(ExampleApp.ChangesetView, "error.json", changeset: changeset) 
    end 

Вопрос: где я могу поставить это? Я попытался поместить его в контроллеры/помощники/controller_helper.ex, и он говорит: undefined function put_status/2. Я не могу просто добавить use ExampleApp.Web, :controller в этот помощник, потому что он будет конфликтовать с существующим контроллером. Я мог бы использовать его как обычный модуль и использовать псевдоним, но это больше набирает ControllerHelper везде.

Возможно, я могу поместить его в web.ex? Но, может быть, я не должен делать этот файл слишком большим?

Каков наилучший способ СУБДАТЬ код?

ответ

1

Использование Kernel.SpecialForms.import/2 для импорта всех экспортированных (государственных) функций от требуемого модуля (от помощника в данном конкретном случае) без явного именования:

controller_helper.ex (или любого другого модуля)

defmodule My.ControllerHelper do 
    def put_status(param), do: IO.puts(param) # whatever 
end 

my_controller.ex (или любой другой модуль)

import My.ControllerHelper # ⇐ HERE !!! 

defmodule My.Controller do 
    use Waveia.Web, :controller 

    def render_unprocessable_entity(conn, changeset) do 
    conn 
    |> put_status(:unprocessable_entity) # ⇐ HERE !!! 
    |> render(...) 
    end 
    ... 
end 

Если вы хотите, чтобы иметь доступ из одного модуля для всех тех, кто с помощью его, вы можете переопределить __using__/2 обратного вызова, чтобы импортировать модуль, который звонит use Helper.

Пример этого был бы:

defmodule ExampleApp.ControllerHelper do 
    defmacro __using__(opts) do 
    quote do 
     defp render_unprocessable_entity(conn, changeset) do 
     conn 
     |> put_status(:unprocessable_entity) 
     |> render(ExampleApp.ChangesetView, "error.json", changeset: changeset) 
     end 
    end 
    end 
end 
+0

Извините, я не уточнил достаточно. Но я хотел использовать эту функцию в своем контроллере, но поместил ее в отдельный модуль, чтобы его можно было импортировать/использовать в других модулях. Я искал что-то вроде рубинового модуля в elixir, где вспомогательный модуль может иметь доступ ко всем существующим функциям, определенным в любом классе, включенном в модуль. Например, методы 'has_many' или' attribute' также могут использоваться в этих модулях. – randomor

+1

Если вы хотите получить доступ из одного модуля ко всем тем, кто его использует, вы можете переопределить ['__using__'] (http://elixir-lang.org/docs/stable/elixir/Kernel.html#use/2) callback для импорта модуля, который вызывает 'use Helper'. – mudasobwa

+0

Бинго! Я думаю, это именно то, что мне нужно. Я закончил с использованием '' 'defmodule ExampleApp.ControllerHelper сделать defmacro __using __ (ОПТС) делать цитаты делать defp render_unprocessable_entity (Conn, ревизия) у сопп |> put_status (: unprocessable_entity) |> Render (ExampleApp.ChangesetView , "error.json", changeet: changeset) конец конец конец конец '' 'вы бы добавили это в свой ответ, прежде чем я приму свой ответ? – randomor

1

Эти функции (put_status/2 и render/3) существуют в других модулях, которые импортируются, когда вы делаете use Waveia.Web, :controller

Вы можете сделать:

def render_unprocessable_entity(conn, changeset) do 
    conn 
    |> Plug.Conn.put_status(:unprocessable_entity) 
    |> Phoenix.Controller.render(ExampleApp.ChangesetView, "error.json", changeset: changeset) 
end 

Вы можете определить это в модуле, затем import модуль внутри controller в web.ex, однако следует отметить, что в любое время, когда вы импортируете функции в модуль, вы скрываете, где определен модуль. Это означает, что другим людям, работающим с базой кода, возможно, придется сделать некоторые копания вокруг кода, чтобы найти, где функция определена. Вместо этого я бы рекомендовал делать:

import MyModule, only: [render_unprocessable_entity: 2] 

Таким образом кто-то просмотрев код контроллера может увидеть, где именно функция render_unprocessable_entity/2 приходит.

+0

Спасибо! Это сработало! Это просто странно, я должен явно указать модуль 'put_status', который был деталью, которая скрыта в' use Phoenix.Controller'. Исходя из ruby, мне интересно, есть ли способ написать код так, как если бы код мог быть скопирован в/из моих контроллеров. – randomor