### Start Supervisor and Migrate Repos with Reactor.Process Source: https://hexdocs.pm/reactor_process/readme.html This example demonstrates how to use Reactor.Process to start a supervisor, define a step to fetch repository configurations, and then migrate each repository. It includes helper functions for database migration and setup. ```elixir defmodule StartAllReposReactor do use Reactor, extensions: [Reactor.Process] start_supervisor :supervisor step :all_repos do run fn _ -> Application.get_env(:my_app, :ecto_repos) end end map :migrate_all_repos do source result(:all_repos) step :migrate do argument :repo, element(:migrate_all_repos) run &migrate_repo/2 end start_child :start_child do supervisor result(:supervisor) child_spec element(:migrate_all_repos) end end return :supervisor defp migrate_repo(args, _context) do Ecto.Migrator.with_repo(args.repo, fn repo -> with :ok <- repo_create(repo) do Ecto.Migrator.run(repo, :up, all: true) {:ok, repo} end end) end defp repo_create(repo) do case repo.__adapter__().storage_up(repo.config()) do :ok -> :ok {:error, :already_up} -> :ok {:error, reason} -> {:error, reason} end end end Reactor.run!(StartAllReposReactor, %{directory: "./to_reverse"}) ``` -------------------------------- ### Example Reactor Process DSL Usage Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Demonstrates starting a supervisor and then counting its children within a Reactor process. This example shows how to define supervisor configurations and query their status. ```elixir start_link :supervisor do child_spec value({Supervisor, strategy: :one_for_one}) end count_children :children do supervisor result(:supervisor) end ``` -------------------------------- ### Install Igniter New and Reactor Source: https://hexdocs.pm/reactor/01-getting-started.html Install the Igniter New archive and create a new project with Reactor configured. ```bash mix archive.install hex igniter_new mix igniter.new reactor_tutorial --install reactor cd reactor_tutorial ``` -------------------------------- ### Test API Integration Examples Source: https://hexdocs.pm/reactor/api-orchestration.html Examples demonstrating how to run Reactors for basic HTTP client integration, rate limiting, and versioning. ```elixir iex -S mix ``` ```elixir # Test basic HTTP client integration {:ok, result} = Reactor.run(ApiClient, %{ base_url: "https://jsonplaceholder.typicode.com", user_id: "1" }) # Test rate limiting requests = [%{id: 1}, %{id: 2}, %{id: 3}] {:ok, results} = Reactor.run(RateLimitedApi, %{requests: requests}) # Test versioning {:ok, normalized} = Reactor.run(VersionedApi, %{ api_version: "v2", user_id: "123" }) ``` -------------------------------- ### Example Wait For Step Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html An example of using `wait_for` to ensure the `:create_user` step is finished before proceeding. This is a common pattern for managing task order. ```elixir wait_for :create_user ``` -------------------------------- ### Start Processes Source: https://hexdocs.pm/reactor_process/cheatsheet.html Initiate processes, either linked directly or as children under a supervisor. Handles cases where a process might already be started. ```elixir # Start and link a process start_link :worker do child_spec result(:worker_spec) end ``` ```elixir # Start child under supervisor start_child :server do supervisor input(:my_supervisor) child_spec result(:server_spec) end ``` ```elixir # Don't fail if already started start_child :idempotent_worker do supervisor input(:supervisor) child_spec result(:worker_spec) fail_on_already_started? false end ``` -------------------------------- ### Set up Reactor Project Source: https://hexdocs.pm/reactor/03-async-workflows.html Installs the Reactor library and sets up a new project. ```bash mix igniter.new reactor_tutorial --install reactor cd reactor_tutorial ``` -------------------------------- ### Starting a Child Process Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Adds a child specification to a supervisor and starts that child. Use `child_spec` to define the child's configuration. ```elixir start_link :supervisor do child_spec value({Supervisor, strategy: :one_for_one}) end ``` ```elixir start_child :worker do supervisor result(:supervisor) child_spec value({Agent, initial_value: 0}) end ``` -------------------------------- ### GET Request Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of making a GET request to fetch data from an API, including setting headers and error handling. ```APIDOC ## GET /users/octocat ### Description Fetches user data from the GitHub API. ### Method GET ### Endpoint https://api.github.com/users/octocat ### Parameters #### Headers - **accept** (string) - Required - Specifies the desired response format, e.g., "application/json". #### Request Body None ### Request Example ```elixir req_get :get_data do url value("https://api.github.com/users/octocat") headers value([accept: "application/json"]) http_errors value(:raise) end ``` ### Response #### Success Response (200) - **body** (JSON) - User data in JSON format. #### Response Example ```json { "login": "octocat", "id": 583231, "node_id": "MDQ6VXNlcjU4MzIzMQ==", "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4", "gravatar_id": "", "url": "https://api.github.com/users/octocat", "html_url": "https://github.com/octocat", "followers_url": "https://api.github.com/users/octocat/followers", "following_url": "https://api.github.com/users/octocat/following{/other_user}", "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", "organizations_url": "https://api.github.com/users/octocat/orgs", "repos_url": "https://api.github.com/users/octocat/repos", "events_url": "https://api.github.com/users/octocat/events{/privacy}", "received_events_url": "https://api.github.com/users/octocat/received_events", "type": "User", "site_admin": false } ``` ``` -------------------------------- ### Reactor.Process.Step.StartChild Source: https://hexdocs.pm/reactor_process/api-reference.html Adds a child specification to a supervisor and starts that child. ```APIDOC ## Reactor.Process.Step.StartChild ### Description Adds a child specification to a supervisor and starts that child. ### Method Not applicable (Step) ### Endpoint Not applicable (Step) ``` -------------------------------- ### Fetch GitHub Repository Description with Reactor.Req Source: https://hexdocs.pm/reactor_req/index.html This example demonstrates how to use Reactor.Req to make a GET request to the GitHub API to retrieve a repository's description. It defines a Reactor workflow with steps for constructing the URL, making the request, and extracting the description. ```elixir defmodule GetGitHubRepoDescription do use Reactor, extensions: [Reactor.Req] input :owner input :repo step :repo_url do argument :owner, input(:owner) argument :repo, input(:repo) run fn args -> URI.new("https://api.github.com/repos/#{args.owner}/#{args.repo}") end end req_get :get_repo do url result(:repo_url) headers value([accept: "application/vnd.github.v3+json"]) http_errors value(:raise) end step :get_description do argument :description, result(:get_repo, [:body, "description"]) run fn args -> {:ok, args.description} end end end Reactor.run!(GetGitHubRepoDescription, %{ owner: "ash-project", repo: "reactor_req" }) # => "A Reactor DSL extension for making HTTP requests with Req." ``` -------------------------------- ### Example Where Clause for File Existence Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html This example uses a `where` clause to ensure a step to read a file only runs if the file actually exists at the specified path. This prevents errors from attempting to read non-existent files. ```elixir step :read_file do argument :path, input(:path) run &File.read(&1.path) where &File.exists?(&1.path) end ``` -------------------------------- ### Install Reactor.Req Source: https://hexdocs.pm/reactor_req/cheatsheet.html Add the Reactor.Req dependency to your `mix.exs` file to include it in your project. ```elixir def deps do [ {:reactor_req, "~> 0.1.4"} ] end ``` -------------------------------- ### Authentication - Basic Auth Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of using Basic Authentication for a GET request. ```APIDOC ## GET /protected (Basic Auth) ### Description Accesses a protected resource using Basic Authentication. ### Method GET ### Endpoint https://api.example.com/protected ### Parameters #### Authentication - **auth** (tuple) - Required - Specifies Basic Authentication with username and password. - `{:basic, "user:password"}` ### Request Example ```elixir req_get :secure_get do url value("https://api.example.com/protected") auth value({:basic, "user:password"}) end ``` ### Response #### Success Response (200) - **body** (JSON) - Data from the protected resource. #### Response Example ```json { "message": "Access granted" } ``` ``` -------------------------------- ### start_steps Source: https://hexdocs.pm/reactor/Reactor.Executor.Async.html Starts as many of the provided asynchronous steps as possible, considering maximum concurrency and available work slots. ```APIDOC ## start_steps(reactor, state, steps, supervisor) ### Description Start as many of the provided steps as possible. Takes into account the maximum concurrency and available work slots. ### Parameters - **reactor** (Reactor.t()) - The current reactor state. - **state** (Reactor.Executor.State.t()) - The current executor state. - **steps** ([Reactor.Step.t()]) - A list of steps to start. - **supervisor** (Supervisor.supervisor()) - The supervisor to use for starting tasks, typically `{:via, PartitionSupervisor, {Reactor.TaskSupervisor, self()}}`. ### Returns - **{:continue | :recurse, Reactor.t(), Reactor.Executor.State.t()}** - If steps were started successfully, returns the next action, updated reactor, and executor state. - **{:error, any()}** - If an error occurred during step startup. ``` -------------------------------- ### Reactor Usage Examples Source: https://hexdocs.pm/reactor/Reactor.html Demonstrates how to construct and run a Reactor using both the Spark DSL and programmatically. ```APIDOC ## Reactor (reactor v1.0.1) Reactor is a dynamic, concurrent, dependency resolving saga orchestrator. ### Usage You can construct a reactor using the `Reactor` Spark DSL: ```elixir defmodule HelloWorldReactor do @moduledoc false use Reactor input :whom step :greet, Greeter do argument :whom, input(:whom) end return :greet end ``` ```iex iex> Reactor.run(HelloWorldReactor, %{whom: "Dear Reader"}) {:ok, "Hello, Dear Reader!"} ``` or you can build it programmatically: ```iex iex> reactor = Builder.new() ...> {:ok, reactor} = Builder.add_input(reactor, :whom) ...> {:ok, reactor} = Builder.add_step(reactor, :greet, Greeter, whom: {:input, :whom}) ...> {:ok, reactor} = Builder.return(reactor, :greet) ...> Reactor.run(reactor, %{whom: nil}) {:ok, "Hello, World!"} ``` ### Options * `:extensions` (list of module that adopts `Spark.Dsl.Extension`) - A list of DSL extensions to add to the `Spark.Dsl` * `:otp_app` (`atom/0`) - The otp_app to use for any application configurable options * `:fragments` (list of `module/0`) - Fragments to include in the `Spark.Dsl`. See the fragments guide for more. ``` -------------------------------- ### Run a Reactor with Inputs Source: https://hexdocs.pm/reactor/Reactor.html Execute a defined Reactor with specific inputs. This example demonstrates running the HelloWorldReactor. ```elixir iex> Reactor.run(HelloWorldReactor, %{whom: "Dear Reader"}) {:ok, "Hello, Dear Reader!"} ``` -------------------------------- ### Use Path Parameters Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of using path parameters in a URL, with options for specifying the style. ```elixir req_get :user_repos do url value("https://api.github.com/users/{owner}/repos") path_params value(%{owner: "ash-project"}) path_params_style value(:colon) # default end ``` -------------------------------- ### Setup Reactor Extension Source: https://hexdocs.pm/reactor_process/cheatsheet.html Configure your module to use Reactor with the Reactor.Process extension. ```elixir defmodule MyReactor do use Reactor, extensions: [Reactor.Process] # Your reactor steps here end ``` -------------------------------- ### DynamicSupervisor Support Source: https://hexdocs.pm/reactor_process/cheatsheet.html Integrate with DynamicSupervisor for starting and terminating children using their PIDs. ```elixir # Start under DynamicSupervisor start_child :dynamic_worker do supervisor input(:dynamic_sup) child_spec result(:worker_spec) module DynamicSupervisor end ``` ```elixir # Terminate by PID in DynamicSupervisor terminate_child :stop_dynamic do supervisor input(:dynamic_sup) child_id input(:worker_pid) # Use PID for DynamicSupervisor module DynamicSupervisor end ``` -------------------------------- ### Setup Performance Monitoring Source: https://hexdocs.pm/reactor/debugging-workflows.html Attach telemetry to monitor step performance and log slow steps. Configure a slow threshold in milliseconds. ```elixir defmodule PerformanceMonitor do def setup_performance_monitoring(slow_threshold_ms \ 100) do :telemetry.attach( "reactor-performance", [:reactor, :step, :run, :stop], &monitor_step_performance/4, %{threshold: slow_threshold_ms * 1_000_000} # Convert to nanoseconds ) end def monitor_step_performance(_event, measurements, metadata, config) do if measurements.duration > config.threshold do duration_ms = measurements.duration / 1_000_000 IO.puts("🐌 Slow step detected:") IO.puts(" Step: #{metadata.step.name}") IO.puts(" Duration: #{duration_ms}ms") IO.puts(" Module: #{metadata.step.impl}") end end end PerformanceMonitor.setup_performance_monitoring(50) # 50ms threshold ``` -------------------------------- ### Example Guard Usage for File Reading Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html This example demonstrates using a `guard` to check a cache before reading a file. If the content is found in the cache, it halts execution and returns the cached content; otherwise, it continues to read the file. ```elixir step :read_file_via_cache do argument :path, input(:path) run &File.read(&1.path) guard fn %{path: path}, %{cache: cache} -> case Cache.get(cache, path) do {:ok, content} -> {:halt, {:ok, content}} _ -> :cont end end end ``` -------------------------------- ### StartChild Source: https://hexdocs.pm/reactor_process/Reactor.Process.Step.StartChild.html Adds a child specification to a supervisor and starts that child. This function is analogous to `Supervisor.start_child/2`. ```APIDOC ## StartChild ### Description Adds a child specification to a supervisor and starts that child. See the documentation for `Supervisor.start_child/2` for more information. ### Arguments * `:supervisor` - Required. The supervisor to query. * `:child_spec` - Required. The child spec. ### Options * `:module` (`module/0`) - The module to use. Must export `count_children/1`. Defaults to `Supervisor`. * `:fail_on_already_present?` (`boolean/0`) - Whether the step should fail if the child spec is already present in the supervisor. Defaults to `true`. * `:fail_on_already_started?` (`boolean/0`) - Whether the step should fail if the start function returns an already started error. Defaults to `true`. * `:terminate_on_undo?` (`boolean/0`) - Whether to terminate the started process when the Reactor is undoing changes. Defaults to `true`. * `:termination_reason` (`term/0`) - The reason to give to the process when terminating it. Defaults to `:kill`. * `:termination_timeout` (`timeout/0`) - How long to wait for a process to terminate. Defaults to `5000`. ``` -------------------------------- ### __reactor.start_child Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Adds a child specification to a supervisor and starts that child. This function is a wrapper around `Supervisor.start_child/2`. ```APIDOC ## __reactor.start_child ### Description Adds a child specification to a supervisor and starts that child. See the documentation for `Supervisor.start_child/2` for more information. ### Arguments - `name` (atom) - Required - A unique name for the step. Used when choosing the return value of the Reactor and for arguments into other steps. ``` -------------------------------- ### Path Parameters Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of using path parameters to specify a user's owner for fetching repositories. ```APIDOC ## GET /users/{owner}/repos (Path Parameters) ### Description Retrieves a list of repositories for a specific GitHub user. ### Method GET ### Endpoint https://api.github.com/users/{owner}/repos ### Parameters #### Path Parameters - **owner** (string) - Required - The username of the repository owner. #### Request Body None ### Request Example ```elixir req_get :user_repos do url value("https://api.github.com/users/{owner}/repos") path_params value(%{owner: "ash-project"}) path_params_style value(:colon) # default end ``` ### Response #### Success Response (200) - **body** (JSON) - A list of repositories owned by the specified user. #### Response Example ```json [ { "name": "reactor-reactor", "full_name": "reactor/reactor-reactor", "owner": {"login": "reactor"} } ] ``` ``` -------------------------------- ### Request Authentication Methods Source: https://hexdocs.pm/reactor_req/cheatsheet.html Examples demonstrating basic authentication and bearer token authentication for secure requests. ```elixir # Basic auth req_get :secure_get do url value("https://api.example.com/protected") auth value({:basic, "user:password"}) end # Bearer token req_post :api_call do url value("https://api.example.com/data") auth value({:bearer, "jwt_token_here"}) end ``` -------------------------------- ### start_link Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Starts a process linked to the current Reactor process, with options for defining child specifications and execution conditions. ```APIDOC ## start_link ### Description Starts a process which is linked to the process running the Reactor. See the documentation for `Reactor.Process.Step.StartLink` for more information. ### Nested DSLs * wait_for * child_spec * guard * where ### Arguments #### `name` - **Type**: `atom` - **Default**: None - **Description**: A unique name for the step. Used when choosing the return value of the Reactor and for arguments into other steps. ### Examples ```elixir start_link :supervisor do child_spec {Supervisor, name: __MODULE__.Supervisor}) end input :initial_value start_link :agent do child_spec result(:value), transform: &{Agent, fn -> &1 end} end ``` ``` -------------------------------- ### Configure Retries and Timeouts Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of setting up automatic retries with delays and configuring various timeouts for a request. ```elixir req_get :resilient_request do url value("https://api.example.com/data") max_retries value(5) retry_delay value(1000) # 1 second retry_log_level value(:info) receive_timeout value(30_000) # 30 seconds pool_timeout value(10_000) # 10 seconds end ``` -------------------------------- ### init/2 Source: https://hexdocs.pm/reactor/Reactor.Executor.Hooks.html Runs the init hooks, collecting the new context. ```APIDOC ## init/2 ### Description Run the init hooks collecting the new context as it goes ### Function Signature ```elixir init(reactor, context) ``` ### Parameters - `reactor` (Reactor.t()): The reactor instance. - `context` (Reactor.context()): The initial context. ### Returns - `{:ok, Reactor.context()}`: The initialized context. - `{:error, any()}`: An error if hook execution fails. ``` -------------------------------- ### Perform a GET Request Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of making a GET request to a GitHub API endpoint, setting headers, and raising HTTP errors on failure. ```elixir req_get :get_data do url value("https://api.github.com/users/octocat") headers value([accept: "application/json"]) http_errors value(:raise) end ``` -------------------------------- ### Manage Process Dependencies Source: https://hexdocs.pm/reactor_process/cheatsheet.html Define dependencies between processes, ensuring supervisors are ready before starting children and enabling sequential startup. ```elixir # Wait for supervisor to be ready start_child :dependent_worker do supervisor result(:supervisor_start) child_spec result(:worker_spec) wait_for [:supervisor_start] end ``` ```elixir # Sequential process startup start_child :worker_a do supervisor input(:supervisor) child_spec result(:spec_a) end start_child :worker_b do supervisor input(:supervisor) child_spec result(:spec_b) wait_for [:worker_a] end ``` -------------------------------- ### Build a Reactor Programmatically Source: https://hexdocs.pm/reactor/Reactor.Builder.html This example demonstrates how to construct a Reactor program step-by-step using the Builder functions. It shows adding an input, a step with an argument, and specifying the return value. ```elixir reactor = Builder.new() {:ok, reactor} = Builder.add_input(reactor, :name) argument = Argument.from_input(:name) {:ok, reactor} = Builder.add_step(reactor, :greet, [argument]) {:ok, reactor} = Builder.return(reactor, :greet) ``` -------------------------------- ### Query Parameters Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of adding query parameters to a GET request for searching repositories. ```APIDOC ## GET /search/repositories (Query Parameters) ### Description Searches for GitHub repositories based on specified criteria. ### Method GET ### Endpoint https://api.github.com/search/repositories ### Parameters #### Query Parameters - **q** (string) - Required - The search query string. - **sort** (string) - Optional - The field to sort results by (e.g., "stars"). - **order** (string) - Optional - The sort order (e.g., "desc"). ### Request Example ```elixir req_get :search do url value("https://api.github.com/search/repositories") params value([ q: "reactor language:elixir", sort: "stars", order: "desc" ]) end ``` ### Response #### Success Response (200) - **body** (JSON) - Search results containing repositories. #### Response Example ```json { "total_count": 10, "items": [ { "name": "reactor-reactor", "full_name": "reactor/reactor-reactor", "owner": {"login": "reactor"}, "stargazers_count": 100 } ] } ``` ``` -------------------------------- ### Specify Query Parameters Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of adding query parameters to a GET request for searching repositories. ```elixir req_get :search do url value("https://api.github.com/search/repositories") params value([ q: "reactor language:elixir", sort: "stars", order: "desc" ]) end ``` -------------------------------- ### Reactor Get Process Context Callback Source: https://hexdocs.pm/reactor/Reactor.Middleware.html Called before starting an asynchronous step to retrieve context information for the new process, often used for tracing span information. ```elixir @callback get_process_context() :: any() ``` -------------------------------- ### Define Complete Workflow with Reactor Source: https://hexdocs.pm/reactor_process/cheatsheet.html Use this pattern to define a complete application workflow, including supervisor setup, child process specifications, and inter-process dependencies. Ensure child processes are started in the correct order. ```elixir defmodule MyAppReactor do use Reactor, extensions: [Reactor.Process] # Define child specs child_spec :database_spec do module {MyApp.Database, []} end child_spec :server_spec do module {MyApp.Server, [port: input(:port)]} end # Start supervisor first start_link :main_supervisor do child_spec {Supervisor, strategy: :one_for_one} end # Start database start_child :database do supervisor result(:main_supervisor) child_spec result(:database_spec) end # Start server after database start_child :server do supervisor result(:main_supervisor) child_spec result(:server_spec) wait_for [:database] end # Health monitoring count_children :monitor do supervisor result(:main_supervisor) end end ``` -------------------------------- ### Dynamic Step Creation Example Source: https://hexdocs.pm/reactor/concepts.html Demonstrates how a step can dynamically generate new steps during execution based on processed data. The `build_dynamic_steps/1` function should return a list of new steps. ```elixir def run(arguments, context, step) do # Process data and determine what steps to create new_steps = build_dynamic_steps(arguments.data) {:ok, result, new_steps} end ``` -------------------------------- ### Basic Reactor.File Setup Source: https://hexdocs.pm/reactor_file/cheatsheet.html Defines a basic Reactor workflow that reads a file and writes its content to another location. Ensure Reactor and Reactor.File are included in your project dependencies. ```elixir defmodule MyFileReactor do use Reactor, extensions: [Reactor.File] input :source_path input :dest_path read_file :content do path input(:source_path) end write_file :output do path input(:dest_path) content result(:content) end return :output end ``` -------------------------------- ### Reactor Input Helper Example Source: https://hexdocs.pm/reactor/Reactor.Dsl.Argument.html Shows how to use the `input` helper to bind a reactor's input to an argument within a step, enabling access to external data. ```elixir defmodule ExampleReactor do use Reactor input :name step :greet do # here: --------↓↓↓↓↓ argument :name, input(:name) run fn %{name: nil}, _, _ -> {:ok, "Hello, World!"} %{name: name}, _, _ -> {:ok, "Hello, #{name}!"} end end end ``` -------------------------------- ### Compensation Logic Examples Source: https://hexdocs.pm/reactor/architecture.html Provides examples of compensation logic based on different error types, including retries and fallback values. ```elixir def compensate(reason, arguments, context, step) do case reason do %RetryableError{} -> :retry # Try again %FallbackError{value: v} -> {:continue, v} # Use fallback _other -> :ok # Accept failure end end ``` -------------------------------- ### Testing Recursive Reactors in IEx Source: https://hexdocs.pm/reactor/05-recursive-execution.html Provides example commands to run and test the CountdownExample, AccumulatorExample, and SquareRootCalculator reactors within an IEx session. ```iex iex -S mix # Test the countdown example {:ok, result} = Reactor.run(CountdownExample, %{start_number: 5}) IO.inspect(result.message) # Should output: "Counted down from 5 to 0" # Test the accumulator {:ok, result} = Reactor.run(AccumulatorExample, %{target_score: 50}) IO.inspect(result.message) # Should output something like: "Reached 53 (target was 50)" # Test the square root calculator {:ok, result} = Reactor.run(SquareRootCalculator, %{number: 25}) IO.inspect(result.message) # Should output: "√25 ≈ 5.0" ``` -------------------------------- ### Perform HTTP GET Request Source: https://hexdocs.pm/reactor_req/dsl-reactor-req.html Executes an HTTP GET request using `Req.get/2`. Reactor passes options directly to `Req`, assuming `Req` handles validation. ```elixir req_get name ``` -------------------------------- ### Example Around Function for Database Transactions Source: https://hexdocs.pm/reactor/Reactor.Step.Around.html This example demonstrates how to use an around function to execute nested steps within an Ecto database transaction. It handles both successful results and errors by raising the reason. ```elixir def in_transaction(arguments, context, steps, callback) do MyApp.Repo.transaction(fn -> case callback.(arguments, context, steps) do {:ok, results} -> result {:error, reason} -> raise reason end end) end ``` -------------------------------- ### run(arguments, context, options) Source: https://hexdocs.pm/reactor/Reactor.Step.html Executes the primary logic of the step. This callback receives arguments, context, and options, and returns the result of the step's execution, which can be success, failure, retry, or halt. ```APIDOC ## run(arguments, context, options) ### Description Execute the step. This is the function that implements the behaviour you wish to execute. You will receive arguments as per the `t:Step.t` definition along with their corresponding values as a map and a copy of the current reactor context. ### Arguments * `arguments` - A map of arguments as per the `t:Step.t` definition we're called from. * `context` - The reactor context. * `options` - A keyword list of options provided to the step (if any). ### Return values * `{:ok, value}` the step completed successfully it returns the value in an ok tuple. * `{:ok, value, [step]}` the step completed successfully and wants to add new steps to the reactor. * `{:error, reason}` the if step failed, return an error tuple. * `:retry` or `{:retry, reason}` the step failed, but is retryable. You can optionally supply an error reason which will be used in the event that the step runs out of retries, otherwise a `Reactor.Error.RetriesExceededError` will be used. * `{:halt, reason}` terminate (or pause) reactor execution. If there are actively running steps the reactor will wait for them to finish and then return the incomplete state for later resumption. ### Signature ```elixir @callback run( arguments :: Reactor.inputs(), context :: Reactor.context(), options :: keyword() ) :: run_result() ``` ``` -------------------------------- ### Reactor.File.Step.Lstat Source: https://hexdocs.pm/reactor_file/Reactor.File.Step.Lstat.html A step that calls `File.lstat/2` to get file status information. ```APIDOC ## Reactor.File.Step.Lstat A step which calls `File.lstat/2`. ### Arguments * `:path` (String.t()) - Required. The path of the file to inspect. ### Options * `:time` - What format to return the file times in. See `File.stat/2` for more. Valid values are :universal, :local, :posix. The default value is `:posix`. ``` -------------------------------- ### reactor.io_read.wait_for Source: https://hexdocs.pm/reactor_file/dsl-reactor-file.html Waits for a named step to complete before starting the current one. ```APIDOC ## reactor.io_read.wait_for ### Description Wait for the named step to complete before allowing this one to start. Desugars to `argument :_, result(step_to_wait_for)`. ### Arguments * `names` (atom | list(atom)) - The name of the step to wait for. ### Options * `description` (String.t) - An optional description. ``` -------------------------------- ### Test Composed Reactor and Individual Reactors Source: https://hexdocs.pm/reactor/04-composition.html This example demonstrates how to run the composed OrderProcessingReactor with sample inputs and inspect its output. It also shows how to run individual sub-reactors like UserValidationReactor for isolated testing. ```elixir iex -S mix # Test the composed order processing {:ok, order} = Reactor.run(OrderProcessingReactor, %{ user_id: 123, product_id: 456, quantity: 2, amount: 99.99 }) IO.inspect(order.order_id) # Should output something like "order_123" # Test individual reactors too {:ok, user} = Reactor.run(UserValidationReactor, %{user_id: 123}) IO.inspect(user.name) # Should output "User 123" ``` -------------------------------- ### Running a Reactor Workflow Source: https://hexdocs.pm/reactor/payment-processing.html Demonstrates how to execute a Reactor workflow for payment processing. Includes examples for successful execution and graceful handling of workflow failures. ```elixir # Successful execution {:ok, confirmation} = Reactor.run(ECommerce.PaymentWorkflow, order_id: "order_123", payment_details: %{ card_number: "4111111111111111", exp_month: 12, exp_year: 2025, cvv: "123" }, customer_id: "customer_456" ) # Handle failures gracefully case Reactor.run(ECommerce.PaymentWorkflow, invalid_inputs) do {:ok, result} -> # Process successful order handle_successful_order(result) {:error, errors} -> # Log errors and notify customer Logger.error("Payment workflow failed: #{inspect(errors)}") notify_customer_of_failure(customer_id, errors) end ``` -------------------------------- ### reactor.io_binstream.wait_for Source: https://hexdocs.pm/reactor_file/dsl-reactor-file.html Waits for a named step to complete before starting the current one. ```APIDOC ## reactor.io_binstream.wait_for ### Description Wait for the named step to complete before allowing this one to start. Desugars to `argument :_, result(step_to_wait_for)`. ### Arguments * `names` (atom | list(atom)) - The name of the step to wait for. ### Options * `description` (String.t) - An optional description. ``` -------------------------------- ### Integrate Reactor Ecosystem Packages Source: https://hexdocs.pm/reactor/ecosystem.html This example shows how to use Reactor with extensions for file operations, HTTP requests, and process management. Ensure the respective ecosystem packages (reactor_req, reactor_file, reactor_process) are included in your project. ```elixir defmodule MyWorkflowReactor do use Reactor, extensions: [Reactor.Req, Reactor.File, Reactor.Process] input :file_path input :upload_url # File operations from reactor_file (DSL entities) file_read :process_file do path input(:file_path) end # HTTP requests from reactor_req (DSL entities) req_post :upload_data do url input(:upload_url) body result(:process_file) end # Process management from reactor_process (DSL entities) start_child :start_worker do supervisor value(MyApp.WorkerSupervisor) child_spec template({MyApp.Worker, result(:process_file)}) end return :upload_data end ``` -------------------------------- ### reactor.cp_r.wait_for Source: https://hexdocs.pm/reactor_file/dsl-reactor-file.html Wait for the named step to complete before allowing this one to start. ```APIDOC ## reactor.cp_r.wait_for wait_for names ### Description Wait for the named step to complete before allowing this one to start. Desugars to `argument :_, result(step_to_wait_for)` ### Arguments - **names** (atom | list(atom)) - The name of the step to wait for. ### Options - **description** (String.t) - An optional description. ``` -------------------------------- ### POST Request Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of making a POST request to create a new user, including sending JSON data and setting the content type. ```APIDOC ## POST /users ### Description Creates a new user with the provided name and email. ### Method POST ### Endpoint https://api.example.com/users ### Parameters #### Headers - **content_type** (string) - Required - Specifies the format of the request body, e.g., "application/json". #### Request Body - **name** (string) - Required - The name of the user. - **email** (string) - Required - The email address of the user. ### Request Example ```elixir req_post :create_user do url value("https://api.example.com/users") json value(%{name: "Marty", email: "marty@example.com"}) headers value([content_type: "application/json"]) end ``` ### Response #### Success Response (201) - **body** (JSON) - Details of the created user. #### Response Example ```json { "id": "123", "name": "Marty", "email": "marty@example.com" } ``` ``` -------------------------------- ### Reactor.Process.Middleware Source: https://hexdocs.pm/reactor_process/api-reference.html A Reactor middleware which records the pid of the original starting process. ```APIDOC ## Reactor.Process.Middleware ### Description A Reactor middleware which records the pid of the original starting process. ### Method Not applicable (Middleware) ### Endpoint Not applicable (Middleware) ``` -------------------------------- ### child_spec/1 Source: https://hexdocs.pm/reactor/Reactor.Executor.ConcurrencyTracker.html Returns a supervisor child specification for starting the ConcurrencyTracker module. ```APIDOC ## child_spec/1 ### Description Returns a specification to start this module under a supervisor. ### Method child_spec(init_arg) ### Parameters #### Path Parameters - **init_arg** - Required - Initialization argument for the module. ### Note See `Supervisor`. ``` -------------------------------- ### run(step, arguments, context) Source: https://hexdocs.pm/reactor/Reactor.Step.html Executes a step with the provided arguments and context. ```APIDOC ## run(step, arguments, context) ### Description Execute a step. ### Method `run` ### Parameters * **step** (t()) - The step to execute. * **arguments** (Reactor.inputs()) - The arguments to pass to the step. * **context** (Reactor.context()) - The reactor context. ### Return values * `run_result()` - The result of executing the step. ``` -------------------------------- ### Handle Process Start/Termination Failures Source: https://hexdocs.pm/reactor_process/cheatsheet.html Configure options to gracefully handle scenarios where processes are already present or not found during start or termination attempts. ```elixir # Handle already present scenarios start_child :maybe_start do supervisor input(:supervisor) child_spec result(:worker_spec) fail_on_already_present? false fail_on_already_started? false end ``` ```elixir # Handle missing children gracefully terminate_child :maybe_stop do supervisor input(:supervisor) child_id input(:worker_id) fail_on_not_found? false end ``` -------------------------------- ### DELETE Request Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of making a DELETE request to remove a user. ```APIDOC ## DELETE /users/{id} ### Description Removes a specific user from the system. ### Method DELETE ### Endpoint https://api.example.com/users/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the user to remove. #### Headers - **authorization** (string) - Required - Bearer token for authentication. ### Request Example ```elixir req_delete :remove_user do url result(:user_url) headers value([authorization: input(:auth_token)]) end ``` ### Response #### Success Response (204) - **body** - No content, indicating successful deletion. #### Response Example (No content) ``` -------------------------------- ### run/3 Source: https://hexdocs.pm/reactor/Reactor.Executor.Sync.html Try and run a step synchronously. ```APIDOC ## run/3 ### Description Try and run a step synchronously. ### Function Signature ```elixir run(reactor, state, step) ``` ### Parameters - `reactor` (Reactor.t()) - `state` (Reactor.Executor.State.t()) - `step` (Reactor.Step.t() | nil) ### Returns - `{:continue | :recurse | :halt | :undo, Reactor.t(), Reactor.Executor.State.t()}` ``` -------------------------------- ### init/1 Source: https://hexdocs.pm/reactor/Reactor.Middleware.html The init callback is called with the Reactor context during startup, providing an opportunity to modify the context or initialize non-reactor-managed resources. ```APIDOC ## init/1 ### Description The init callback will be called with the Reactor context when starting up. This gives you the opportunity to modify the context or to perform any initialisation of any non-reactor-managed resources (eg notifications). ### Callback Signature ```elixir @callback init(context()) :: {:ok, context()} | {:error, any()} ``` ``` -------------------------------- ### Reactor.Req.Dsl.Get Source: https://hexdocs.pm/reactor_req/api-reference.html Provides a DSL entity for making GET requests using Reactor.Req. ```APIDOC ## GET req_get ### Description Represents a GET request operation within the Reactor.Req DSL. ### Method GET ### Endpoint [Endpoint details not specified in source] ### Parameters [Parameter details not specified in source] ### Request Example [Request example not specified in source] ### Response [Response details not specified in source] ``` -------------------------------- ### reactor.rmdir.wait_for Source: https://hexdocs.pm/reactor_file/dsl-reactor-file.html Waits for a named step to complete before allowing the `rmdir` operation to start. ```APIDOC ## reactor.rmdir.wait_for names ### Description Wait for the named step to complete before allowing this one to start. Desugars to `argument :_, result(step_to_wait_for)`. ### Arguments * `names` (atom | list(atom)) - Required - The name of the step to wait for. ### Options * `description` (String.t) - Optional - An optional description. ``` -------------------------------- ### Async vs Sync Execution in Reactor Steps Source: https://hexdocs.pm/reactor/03-async-workflows.html Demonstrates how to configure steps for asynchronous (default) or synchronous execution. ```elixir # Async (default) - runs in a separate task step :fetch_data do async? true # This is the default run &fetch_from_api/1 end # Sync - runs in the main process step :critical_operation do async? false # Forces synchronous execution run &update_database/1 end ``` -------------------------------- ### child_spec Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Specifies a child specification for a supervisor, defining how a process should be started and managed. ```APIDOC ## child_spec ### Description Specifies a child spec as used by a supervisor. ### Arguments #### `source` - **Type**: `Reactor.Template.Element | Reactor.Template.Input | Reactor.Template.Result | Reactor.Template.Value | {module, keyword} | module` - **Default**: None - **Description**: The child spec. ### Options #### `description` - **Type**: `String.t` - **Default**: None - **Description**: An optional description for the child spec. #### `transform` - **Type**: `(any -> any) | module | nil` - **Default**: None - **Description**: An optional transformation function which can be used to modify the child spec before it is passed to the step. ### Examples ```elixir child_spec {Supervisor, name: __MODULE__.Supervisor} child_spec input(:initial_value) do transform fn initial_value -> fn -> initial_value end end end ``` ``` -------------------------------- ### __reactor.restart_child.wait_for Source: https://hexdocs.pm/reactor_process/dsl-reactor-process.html Waits for a named step to complete before allowing the current step to start. ```APIDOC ## __reactor.restart_child.wait_for ### Description Wait for the named step to complete before allowing this one to start. Desugars to `argument :_, result(step_to_wait_for)`. ### Arguments - `names` (atom | list(atom)) - Required - The name of the step to wait for. ### Options - `description` (String.t) - Optional - An optional description. ``` -------------------------------- ### Argument Resolution Examples Source: https://hexdocs.pm/reactor/architecture.html Illustrates various ways arguments can be resolved for step execution, including inputs, results from other steps, and literal values. ```elixir # Argument types and their resolution input(:name) # → reactor.context.private.inputs result(:step) # → reactor.intermediate_results result(:step, :key) # → get_in(intermediate_results, [:step, :key]) result(:step, [:key, :subkey]) # → get_in(intermediate_results, [:step, :key, :subkey]) value(literal) # → literal value ``` -------------------------------- ### Reactor and Step Context Structures Source: https://hexdocs.pm/reactor/architecture.html Illustrates the structure of base reactor context and enhanced step context, showing how user inputs and runtime information are organized. ```elixir %{ private: %{ inputs: %{user_id: 123}, composed_reactors: #{}, replace_arguments: nil } } ``` ```elixir %{ current_step: step, current_try: 0, retries_remaining: 5, concurrency_key: #Reference<...> } ``` -------------------------------- ### Request Body - Multipart Form Source: https://hexdocs.pm/reactor_req/cheatsheet.html Example of uploading a file using multipart/form-data. ```APIDOC ## POST /upload (Multipart Form) ### Description Uploads a file along with other data using `multipart/form-data` encoding. ### Method POST ### Endpoint https://api.example.com/upload ### Parameters #### Request Body - **form_multipart** (map) - Required - Data to be sent, including file uploads. - `file`: `{:file, "path/to/file.txt"}` - `description`: `"Time travel documentation"` ### Request Example ```elixir req_post :upload do url value("https://api.example.com/upload") form_multipart value([ file: {:file, "path/to/file.txt"}, description: "Time travel documentation" ]) end ``` ### Response #### Success Response (200) - **body** (JSON) - Confirmation of upload and file details. #### Response Example ```json { "status": "uploaded", "filename": "file.txt", "description": "Time travel documentation" } ``` ``` -------------------------------- ### Define Reactor Input Source: https://hexdocs.pm/reactor/reactor-cheatsheet.html Demonstrates how to define basic inputs, inputs with transformations (e.g., string to integer), and inputs with descriptions. ```elixir # Basic input input :name # Input with transformation input :age do transform &String.to_integer/1 end # Input with description input :email, description: "User's email address" ```