2016-12-07 4 views
2

У меня есть контроллер, где я хотел бы добавить идентификатор страницы facebook и токен доступа к моей базе данных, чтобы я мог отправлять сообщения от имени пользователя.Phoenix, Does conn redirect остановить всю дальнейшую обработку?

Для этого мне нужно сделать GET-вызов в facebook, а затем с токеном доступа, который мне нужно вставить в мою базу данных.

Это означает, что у меня есть вложенный оператор случай, потому что есть 2 точки ошибок, от facebook вызова и вставлять в репозиторий:

case response_from_facebook do 
    {:ok, token} -> 
     case Repo.insert(token) do 
      {:ok, _} -> ... redirect the user 
      {:error, _} -> ... show an error message 
    {:error, _} -> redirect and show an error 

Это некрасиво имо поэтому мне было интересно, если я сделал что-то вроде охранник, если он прекратит дальнейшую обработку:

token = case response_from_facebook do 
    {:ok, token} -> token 
    {error,_ } -> conn |> redirect(to: user_path(conn, :show, user_id)) 
end 

# Does it ever try to insert into the repo if an error occurs? 

Repo.update ... etc. 

Это сделает мой код понятнее, чтобы понять/читать, но, я не совсем уверен, что если бы перенаправлять предотвратить попытку вставить в мой репозиторий. Если этот «защитник» не является решением, как я могу предотвратить многопользовательский аргумент case, который может быть затруднен, чтобы читать?

+1

Ответ 'response_from_facebook' возвращает ошибку, которая легко сопоставима с образцом, отдельно от ошибок' Repo.insert' (можете ли вы опубликовать ошибки, которые она может вернуть?)? Если да, вы можете использовать новый макрос 'с' для упрощения этой части кода. – Dogbert

+0

@ Dogbert вы можете дать ответ/пример? Я посмотрел на макрос онлайн, но, похоже, ни один из примеров не показывает, как обрабатывается ошибка. Предполагаете ли вы присвоить результат «с»? Ошибка из facebook находится в форме '{: ok,% {" error "=>% {" message "=> message}}}', тогда как ошибка из ecto имеет форму '{: error, reason}'. поэтому они могут быть сопоставлены с образцом. Однако я не совсем уверен, как это должно произойти. (Должен ли я передать ошибку внутри оператора 'with'? Должен ли я назначить результат оператора' with' для дальнейшей обработки?) –

+0

Посмотрите пример с блоком 'else' внутри' with' здесь: http: // elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html#with/1. – Dogbert

ответ

1

уборщик способ сделать это:

defmodule SomeApp.FacebookController do 
    ... 

    def create(conn, params) do 
    get_from_facebook 
    |> process_facebook(conn) 
    end 

    defp get_from_facebook do 
    # this is a function you implement 
    end 

    defp process_facebook({:ok, token}, conn) do 
    Repo.insert(token) 
    |> process_insert(conn) 
    end 

    defp process_facebook({:error, error}, conn) do 
    conn 
    |> redirect_with_error(error) # this is a function you will write 
    end 

    defp process_insert({:ok, _}, conn) do 
    conn 
    |> redirect(to: user_path(conn, :show, user_id)) 
    end 

    defp process_insert({:error, error}, conn) do 
    conn 
    |> redirect_with_error(error) # this is a function you will write 
    end 

    ... 
end 

никаких побочных эффектов не может произойти с этим факторинга логики, и это намного «симпатичнее».

Еще лучше, если вы нарушите большинство этой логики в другое модуль:

defmodule SomeApp.FacebookCommand do 

    def call do 
    make_request 
    |> process_response 
    end 

    defp make_request do 
    # whatever you are doing to request from Facebook 
    end 

    defp process_response({:ok, token}) do 
    Repo.insert(token) 
    |> process_insert(conn) 
    end 

    defp process_response({:error, error}) do 
    {:error, error} 
    end 

    defp process_insert({:ok, record}) do 
    {:ok, record} 
    end 

    defp process_insert({:error, error}) do 
    {:error, error} 
    end 

end 

, а затем использовать его в контроллере:

defmodule SomeApp.FcebookController do 
    ... 

    def create(conn, params) do 
    case SomeApp.FacebookCommand.call do 
     {:ok, user} -> 
     conn 
     |> redirect(to: user_path(conn, :show, user.id)) 
     {:error, error} -> 
     # redirect with error 
    end 
    end 

    ... 
end 

Очевидно, что если вы обновляете существующего пользователя, то вы можете выполнить команду, где вы передаете существующего пользователя в call в качестве параметра. '

Пожалуйста, помните, что мой модуль и функция назвали сосать, потому что я точно не знаю, что вы делаете. Лучшее именование - необходимость.

+1

Это может быть только вопрос стиля, но старайтесь не использовать '|>', если не существует нескольких каналов. 'redirect_with_error (conn, error)' лучше, чем 'conn |> redirect_with_error (error)'. Кроме того, когда вы используете канал, вы должны использовать значение для запуска набора. 'token |> Repo.insert() |> process_insert (conn)' будет лучше, чем 'Repo.insert (токен) |> process_insert (conn)' –

+1

Я в основном согласен с вами. Тем не менее, мне нравится использовать оператор трубы, чтобы выявить рабочие потоки высокого уровня, поэтому я использовал его в случаях выше, где есть только один параметр. Тем не менее, некоторые из других стилистических проблем связаны с копированием и вставкой оригинальных идей Terence, представленных выше, т.е. 'Repo.insert (маркер)'. Спасибо за ввод. –