GithubHelp home page GithubHelp logo

vt-elixir / interactor Goto Github PK

View Code? Open in Web Editor NEW
11.0 11.0 5.0 35 KB

Interactor provides an opinionated interface for performing complex user interactions.

Home Page: https://hex.pm/packages/interactor

License: Other

Elixir 100.00%

interactor's People

Contributors

adambouchard avatar alanpeabody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

interactor's Issues

Generator

It would be nice to be able to do something like mix interactor.gen MyProject.CreateOrder and have it spit out a basic namespaced generator and an associated test.

Data Structure & Better APIs

Overview

This library has been great for building and maintaining large (API) applications in Elixir. However a few patterns have emerged that could be handled better.

Typical current use cases identified are:

  1. A simple create/update/delete of something.
  2. 1 + Afterward syncing to a socket.
  3. 1 (or 2) plus calling some async interactor in the background.
  4. Use multis for more complicated work flows

Current pain points are:

  • After callbacks often really only relevant to success cases, but have to define both
  • After callbacks sometimes need more then just the results
  • No good way of hooking interactors together
  • Lack of strict data structure gives me feeling of malaise.

Some Ideas

Lets look at Plug.Conn, Plug.Builder and Plug.Router for inspiration!

%Interaction{
  assigns: %{},
  results: nil,
  success: false,
}

defmodule MyApp.CreateComment do
  use Interactor.Builder

  interaction MyApp.Auth.authorize, :create_comment
  interaction :comment_changeset
  interaction :insert, assign_as: :comment #built in! # but how does it know what to insert is it always from "result"?
  interaction :sync_to_socket, async: true
  interaction MyApp.UpdatePostCount, async: true
  
  def comment_changeset(%Interaction{assigns: %{attributes: attrs}}}) do
    %Comment{}
    |> cast(attrs, [:title, :body])
    |> Comment.validate # if non Interaction is returned it is put into results?
  end

  # Comment now both is result and is added to assigns.
  def sync_to_socket(int = %{Interaction{results: %Comment{}, assigns: %Comment{}}}) do
    # Push to socket.
  end
end

defmodule CommentController do
  # ...
  def create(conn, params) do
    case Interactor.call(CreateComment, %{attributes: params["comment"]}) do
      %{success: true, results: comment} -> render(conn, :show, data: comment)
      %{success: false, results: changeset} -> render(conn, :errors, data: changeset)
    end
  end
  # ...
end

Breakdown of example

Idea is the same sort of pattern as plug. In particular you have an %Interaction{} data structure, then each Interactor really is just a function or module with call/2 function that accept the interaction and the opts and returns the Interaction.

All interaction chains are started with Interactor.call/2 (/3?) which creates the %Interaction{} and calls call/2.

With Interactor.Builder you get some convenience macros similar to plug builder. In particular you have the interaction macro which runs interactions in order as long as the previous interaction did not fail. In addition it gives you some options such as :assign_as and :async which provide simple patterns for common behavior. In addition when an interaction is not returned the return value becomes the result in the interaction.

Built in interaction functions :insert, :update and :transaction also handle standard Repo calls and updating the interaction for you.

Is this a good idea? A bad one?

Howdy @alanpeabody !

I'm wondering if you've drawn any conclusions, some two years later, with respect to "Is this a good idea? A bad one?". Are you still using the interactor pattern (and leveraging this library for that purpose)? I continue to value it as a way of making actions modularized/testable/focused/maintainable but wondered with things being so quiet around here whether you ditched it in favor of another philosophy or if you are continuing to use it despite having (at some point) some desires for improvement (#13). I haven't found any of those "pain points" to be particularly painful, but I'd be curious to hear your take now that a considerable amount of time has passed.

Hope you're well!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.