### Initialize Extep Pipeline Source: https://context7.com/shore-gmbh/extep/llms.txt Creates a new Extep pipeline, optionally with an initial context. The pipeline starts with a status of :ok and an empty context if none is provided. ```elixir Extep.new() #=> %Extep{status: :ok, context: %{}, message: nil} Extep.new(%{user_id: 1, action: "subscribe"}) #=> %Extep{status: :ok, context: %{user_id: 1, action: "subscribe"}, message: nil} ``` -------------------------------- ### Extep.new/1 - Create Extep Struct with Context Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates creating a new Extep struct with an initial context map provided as an argument to `Extep.new/1`. This allows starting a pipeline with pre-populated data. ```elixir Extep.new(%{foo: "bar"}) #=> %Extep{status: :ok, context: %{foo: "bar"}, message: nil} ``` -------------------------------- ### Extep.new/0 - Create Empty Extep Struct Source: https://github.com/shore-gmbh/extep/blob/main/README.md Shows how to create a new, empty Extep struct using the `Extep.new/0` function. This is the starting point for building a new pipeline with an empty initial context. ```elixir Extep.new() #=> %Extep{status: :ok, context: %{}, message: nil} ``` -------------------------------- ### Install Extep Dependency in mix.exs Source: https://github.com/shore-gmbh/extep/blob/main/README.md This code snippet shows how to add the 'extep' library to your Elixir project's dependencies by modifying the `deps` function in your `mix.exs` file. It specifies the version constraint for the dependency. ```elixir def deps do [ {:extep, "~> 0.3.0"} ] end ``` -------------------------------- ### Retry HTTP request with Extep in Elixir Source: https://context7.com/shore-gmbh/extep/llms.txt The ResilientFetcher module defines fetch_with_retry/2 which uses Extep to manage retry attempts for HTTP GET calls. It retries up to a configurable max_attempts with incremental delays, returning either the successful response or a detailed error after exhausting attempts. ```Elixir defmodule ResilientFetcher do def fetch_with_retry(url, max_attempts \\ 3) do Extep.new(%{url: url, attempt: 1, max_attempts: max_attempts}) |> attempt_fetch() |> Extep.return(:response) end defp attempt_fetch(extep) do Extep.run(extep, :response, fn ctx -> case HTTP.get(ctx.url) do {:ok, response} -> {:ok, response} {:error, _reason} when ctx.attempt < ctx.max_attempts -> Process.sleep(1000 * ctx.attempt) {:ok, %{ctx | attempt: ctx.attempt + 1}} |> attempt_fetch() {:error, reason} -> {:error, "Failed after #{ctx.max_attempts} attempts: #{reason}"} end end) end end ``` -------------------------------- ### Basic Extep Pipeline Execution Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates a basic Elixir pipeline using Extep. It initializes a context with parameters, then sequentially runs validation, fetches user data asynchronously, fetches items asynchronously, and finally creates a subscription. The pipeline can halt or error at any step. ```elixir params = %{user_id: 1, plan: "super-power-plus"} Extep.new(%{params: params}) |> Extep.run(:params, &validate_params/1) |> Extep.async(:user, &fetch_user/1) |> Extep.async(:items, &fetch_items/1) |> Extep.return(&create_subscription/1, label_error: true) #=> {:ok, %{id: 123, object: "subscription", user_id: 1, items: [%{code: "item1"}, %{code: "item2"}]}} ``` -------------------------------- ### Extep.run/3 - Run Mutator Function and Store Result Source: https://github.com/shore-gmbh/extep/blob/main/README.md Shows how to use `Extep.run/3` to execute a mutator function and store its result under a specified key in the context. The function must return `{:ok, value}`, `{:halt, reason}`, or `{:error, reason}`. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:bar, fn ctx -> {:ok, ctx.foo + 1} end) #=> %Extep{status: :ok, context: %{foo: 1, bar: 2}, message: nil} ``` -------------------------------- ### Create subscription with validation and parallel fetching Source: https://context7.com/shore-gmbh/extep/llms.txt Implements a subscription creation workflow with parameter validation, concurrent data fetching, and proper error handling. Uses Extep's async operations for user, plan, and payment method lookups. Returns either the created subscription or detailed error information. ```elixir defmodule SubscriptionService do def create_subscription(params) do Extep.new(%{params: params}) |> Extep.run(:validated_params, &validate_params/1) |> Extep.async(:user, &fetch_user/1) |> Extep.async(:plan, &fetch_plan/1) |> Extep.async(:payment_method, &fetch_payment_method/1) |> Extep.run(:subscription, &create_subscription_record/1) |> Extep.run(&send_confirmation_email/1) |> Extep.return(:subscription, label_error: true) end defp validate_params(%{params: params}) do with :ok <- validate_required(params, [:user_id, :plan_id, :payment_method_id]), :ok <- validate_types(params) do {:ok, params} else {:error, reason} -> {:error, reason} end end defp fetch_user(%{validated_params: %{user_id: user_id}}) do case UserRepo.get(user_id) do nil -> {:error, "User not found"} user -> {:ok, user} end end defp fetch_plan(%{validated_params: %{plan_id: plan_id}}) do case PlanRepo.get(plan_id) do nil -> {:error, "Plan not found"} plan -> {:ok, plan} end end defp fetch_payment_method(%{validated_params: %{payment_method_id: pm_id}}) do case PaymentRepo.get(pm_id) do nil -> {:error, "Payment method not found"} pm -> {:ok, pm} end end defp create_subscription_record(%{user: user, plan: plan, payment_method: pm}) do attrs = %{ user_id: user.id, plan_id: plan.id, payment_method_id: pm.id, status: "active", started_at: DateTime.utc_now() } case SubscriptionRepo.insert(attrs) do {:ok, subscription} -> {:ok, subscription} {:error, changeset} -> {:error, format_changeset_errors(changeset)} end end defp send_confirmation_email(%{user: user, subscription: subscription}) do Email.send_subscription_confirmation(user.email, subscription) :ok end end ``` -------------------------------- ### Extep.return/2 - Return Value from Context Key Source: https://github.com/shore-gmbh/extep/blob/main/README.md Shows how `Extep.return/2` can be used to directly return a value stored under a specific key in the pipeline's context. This is useful when the final result is already computed and stored. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:bar, fn ctx -> {:ok, ctx.foo + 1} end) |> Extep.run(:baz, fn ctx -> {:ok, ctx.bar + 2} end) |> Extep.return(:bar) #=> {:ok, 2} ``` -------------------------------- ### Extep basic error handling with pipe operator Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates basic error handling using Extep's pipe operator pattern. Creates a new context, runs a user operation that returns an error tuple, and returns the foo value. Shows clean error message propagation without additional labeling. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:user, fn _ctx -> {:error, "User not found"} end) |> Extep.return(:foo) #=> {:error, "User not found"} ``` -------------------------------- ### Extep.async/3 - Run Mutator Function Asynchronously Source: https://github.com/shore-gmbh/extep/blob/main/README.md Shows how to run a mutator function asynchronously using `Extep.async/3`, storing the result under a key. Multiple asynchronous operations can be chained and then awaited using `Extep.await/1`. ```elixir Extep.new(%{foo: 1}) |> Extep.async(:bar, fn ctx -> {:ok, ctx.foo + 1} end) |> Extep.async(:baz, fn ctx -> {:ok, ctx.foo + 2} end) |> Extep.await() #=> %Extep{status: :ok, context: %{foo: 1, bar: 2, baz: 3}, message: nil} ``` -------------------------------- ### Extep.async/2 - Run Checker Function Asynchronously Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates running a checker function asynchronously using `Extep.async/2`. This allows multiple checks to run in parallel. The `Extep.await/1` function must be called later to process the results. ```elixir Extep.new(%{foo: 1}) |> Extep.async(&check_something/1) |> Extep.async(&check_another_thing/1) |> Extep.await() #=> %Extep{status: :ok, context: %{foo: 1}, message: nil} ``` -------------------------------- ### Parallel Data Fetching with Extep Source: https://context7.com/shore-gmbh/extep/llms.txt Demonstrates how to fetch data from multiple sources concurrently using Extep's async and await functionalities. Errors in one async task will halt the pipeline. Inputs are context maps, and outputs are Extep structures containing results. ```elixir Extep.new(%{user_id: 1}) |> Extep.async(:user, fn ctx -> # Simulates database query {:ok, %{id: ctx.user_id, name: "Alice", email: "alice@example.com"}} end) |> Extep.async(:subscriptions, fn ctx -> # Simulates API call {:ok, [%{id: 1, plan: "premium"}, %{id: 2, plan: "basic"}]} end) |> Extep.async(:payment_methods, fn ctx -> # Simulates external service call {:ok, [%{type: "card", last4: "4242"}]} end) |> Extep.await() #=> %Extep{status: :ok, context: %{ # user_id: 1, # user: %{id: 1, name: "Alice", email: "alice@example.com"}, # subscriptions: [%{id: 1, plan: "premium"}, %{id: 2, plan: "basic"}], # payment_methods: [%{type: "card", last4: "4242"}] # }, message: nil} # First error stops pipeline, later tasks ignored Extep.new(%{product_id: 999}) |> Extep.async(:product, fn ctx -> {:error, "Product #{ctx.product_id} not found"} end) |> Extep.async(:reviews, fn _ctx -> {:ok, [%{rating: 5, text: "Great!"}]} end) |> Extep.await() #=> %Extep{status: :error, context: %{product_id: 999}, # message: %{product: "Product 999 not found"}} ``` -------------------------------- ### Returning Final Results with Extep Source: https://context7.com/shore-gmbh/extep/llms.txt Explains how to extract final results from an Extep pipeline using `return/2` or `return/3`. Supports returning by context key, transformation function, and handling of labeled or simple error messages. ```elixir # Return by context key Extep.new(%{a: 1}) |> Extep.run(:b, fn ctx -> {:ok, ctx.a + 10} end) |> Extep.run(:c, fn ctx -> {:ok, ctx.a + ctx.b} end) |> Extep.return(:c) #=> {:ok, 12} # Return by transformation function Extep.new(%{first_name: "Alice", last_name: "Smith"}) |> Extep.return(fn ctx -> {:ok, "#{ctx.first_name} #{ctx.last_name}"} end) #=> {:ok, "Alice Smith"} # Clean error messages (default) Extep.new(%{user_id: nil}) |> Extep.run(:user, fn _ctx -> {:error, "User not found"} end) |> Extep.return(:user) #=> {:error, "User not found"} # Labeled error messages Extep.new(%{user_id: nil}) |> Extep.run(:user, fn _ctx -> {:error, "User not found"} end) |> Extep.run(:profile, fn _ctx -> {:error, "Profile missing"} end) |> Extep.return(:user, label_error: true) #=> {:error, %{user: "User not found"}} # Halt returns custom message directly Extep.new(%{job_status: "completed"}) |> Extep.run(fn ctx -> if ctx.job_status == "completed" do {:halt, {:cancel, "Job already completed"}} else :ok end end) |> Extep.return(:job_status) #=> {:cancel, "Job already completed"} ``` -------------------------------- ### Extep.return/2 - Return Final Result with Function Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates how `Extep.return/2` can be used to derive and return a final result from the pipeline's context using a provided function. This function receives the context and should return the desired final output. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:bar, fn ctx -> {:ok, ctx.foo + 1} end) |> Extep.return(fn ctx -> {:ok, ctx.bar + 2} end) #=> {:ok, 4} ``` -------------------------------- ### Awaiting Asynchronous Tasks with Timeout Source: https://context7.com/shore-gmbh/extep/llms.txt Shows how to wait for asynchronous operations to complete using `await/1` and `await/2` with default or custom timeouts. It also illustrates automatic awaiting when chaining operations. ```elixir # Default timeout (10 seconds) Extep.new(%{order_id: 123}) |> Extep.async(:inventory, fn _ctx -> Process.sleep(100) {:ok, %{in_stock: true, quantity: 50}} end) |> Extep.async(:pricing, fn _ctx -> Process.sleep(150) {:ok, %{price: 99.99, discount: 10}} end) |> Extep.await() #=> %Extep{status: :ok, context: %{ # order_id: 123, # inventory: %{in_stock: true, quantity: 50}, # pricing: %{price: 99.99, discount: 10} # }, message: nil} # Custom timeout Extep.new(%{user_id: 1}) |> Extep.async(:data, fn _ctx -> Process.sleep(5000) {:ok, "large dataset"} end) |> Extep.await(timeout: 30_000) #=> %Extep{status: :ok, context: %{user_id: 1, data: "large dataset"}, message: nil} # Automatic await on subsequent operations Extep.new(%{id: 1}) |> Extep.async(:user, fn _ctx -> {:ok, %{name: "Bob"}} end) |> Extep.run(:greeting, fn ctx -> {:ok, "Hello, #{ctx.user.name}"} end) # await() is called automatically before run/3 executes #=> %Extep{status: :ok, context: %{id: 1, user: %{name: "Bob"}, # greeting: "Hello, Bob"}, message: nil} ``` -------------------------------- ### Extep.run/2 - Run Checker Function Source: https://github.com/shore-gmbh/extep/blob/main/README.md Illustrates using `Extep.run/2` to execute a checker function within the pipeline. The function is expected to return `:ok` to continue, or `{:halt, reason}` or `{:error, reason}` to stop the pipeline. The result is not stored under a specific key. ```elixir Extep.new(%{foo: 1}) |> Extep.run(&check_something/1) #=> %Extep{status: :ok, context: %{foo: 1}, message: nil} ``` -------------------------------- ### Extep.await/1 - Await Asynchronous Tasks Source: https://github.com/shore-gmbh/extep/blob/main/README.md This snippet shows how to use `Extep.await/1` to wait for all previously initiated asynchronous tasks in the pipeline to complete. It ensures that all parallel operations have finished before proceeding. ```elixir Extep.new(%{foo: 1}) |> Extep.async(:bar, fn ctx -> {:ok, ctx.foo + 1} end) |> Extep.async(:baz, fn ctx -> {:ok, ctx.bar + 2} end) |> Extep.await() #=> %Extep{status: :ok, context: %{foo: 1, bar: 2, baz: 4}, message: nil} ``` -------------------------------- ### Asynchronous Context Mutation with Extep.async/3 Source: https://context7.com/shore-gmbh/extep/llms.txt Allows fetching and storing data concurrently within the pipeline. This function takes a key for the context, a function to fetch the data, and returns the result. It's useful for parallel I/O operations. ```elixir # Example demonstrating async context mutation (implementation details omitted for brevity) # Assuming fetch_user_data and fetch_permissions are functions that return {:ok, data} or {:error, reason} Extep.new(%{user_id: 1}) |> Extep.async(:user_data, fn ctx -> fetch_user_data(ctx.user_id) end) |> Extep.async(:permissions, fn ctx -> fetch_permissions(ctx.user_id) end) |> Extep.await() #=> Example successful result: # %Extep{status: :ok, context: %{user_id: 1, user_data: %{...}, permissions: [...]}, message: nil} # Example error result: # %Extep{status: :error, context: %{user_id: 1}, message: %{user_data: "Failed to fetch user data"}} ``` -------------------------------- ### Asynchronous Validation with Extep.async/2 and Extep.await/1 Source: https://context7.com/shore-gmbh/extep/llms.txt Enables concurrent execution of validation steps without blocking the pipeline. Multiple `async/2` calls can be chained, and `await/1` is used to collect results and determine the pipeline's final status. ```elixir # Multiple async validations Extep.new(%{user_id: 1, plan: "premium"}) |> Extep.async(fn ctx -> # Check user exists in database if user_exists?(ctx.user_id), do: :ok, else: {:error, "User not found"} end) |> Extep.async(fn ctx -> # Check plan is valid if valid_plan?(ctx.plan), do: :ok, else: {:error, "Invalid plan"} end) |> Extep.await() #=> %Extep{status: :ok, context: %{user_id: 1, plan: "premium"}, message: nil} # Async validation with error Extep.new(%{api_key: "invalid"}) |> Extep.async(fn ctx -> case validate_api_key(ctx.api_key) do :ok -> :ok :error -> {:error, "Invalid API key"} end end) |> Extep.await() #=> %Extep{status: :error, context: %{api_key: "invalid"}, # message: %{no_label: "Invalid API key"}} ``` -------------------------------- ### Extep named function error handling Source: https://github.com/shore-gmbh/extep/blob/main/README.md Shows error handling using a named function return_error_tuple/1. Demonstrates how named functions can be passed to Extep.run for error handling. When label_error is true, the function name becomes part of the error response for easier debugging. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:user, &return_error_tuple/1) |> Extep.return(:foo, label_error: true) #=> {:error, %{return_error_tuple: "error message"}} ``` -------------------------------- ### Error handling with graceful degradation Source: https://context7.com/shore-gmbh/extep/llms.txt Demonstrates graceful degradation by providing default values when optional data is missing. The workflow continues execution even when some data cannot be fetched, substituting with sensible defaults. Useful for non-critical data dependencies in report generation. ```elixir defmodule ReportGenerator do def generate(user_id) do Extep.new(%{user_id: user_id}) |> Extep.run(:user, &fetch_user/1) |> Extep.async(:recent_orders, &fetch_orders/1) |> Extep.async(:preferences, &fetch_preferences_or_defaults/1) |> Extep.return(&format_report/1) end defp fetch_preferences_or_defaults(ctx) do # Returns default preferences if user prefs don't exist case PreferenceRepo.get(ctx.user_id) do nil -> {:ok, default_preferences()} prefs -> {:ok, prefs} end end end ``` -------------------------------- ### Extep labeled error messages with label_error Source: https://github.com/shore-gmbh/extep/blob/main/README.md Demonstrates labeled error functionality for debugging complex pipelines. When label_error is true, error messages are wrapped in a map with the operation name as the key, making it easier to identify which step failed in multi-step processing pipelines. ```elixir Extep.new(%{foo: 1}) |> Extep.run(:user, fn _ctx -> {:error, "User not found"} end) |> Extep.return(:foo, label_error: true) #=> {:error, %{user: "User not found"}} ``` -------------------------------- ### Early exit with halt pattern Source: https://context7.com/shore-gmbh/extep/llms.txt Shows how to implement early termination of workflows when certain conditions are met. Useful for avoiding unnecessary processing when an order has already been processed. The halt mechanism prevents further step execution and immediately returns the specified result. ```elixir defmodule OrderProcessor do def process_order(order_id) do Extep.new(%{order_id: order_id}) |> Extep.run(:order, &fetch_order/1) |> Extep.run(&check_already_processed/1) |> Extep.run(:payment, &process_payment/1) |> Extep.run(:shipment, &create_shipment/1) |> Extep.return(:shipment) end defp check_already_processed(%{order: order}) do if order.status == "processed" do {:halt, {:ok, "Order already processed"}} else :ok end end end ``` -------------------------------- ### Synchronous Context Mutation with Extep.run/3 Source: https://context7.com/shore-gmbh/extep/llms.txt Performs synchronous operations that can mutate or add to the pipeline's context. It accepts a key to store the result and a function to compute the new context value. Errors in a step halt the pipeline. ```elixir # Successful pipeline with context updates Extep.new(%{price: 100}) |> Extep.run(:tax, fn ctx -> {:ok, ctx.price * 0.2} end) |> Extep.run(:total, fn ctx -> {:ok, ctx.price + ctx.tax} end) #=> %Extep{status: :ok, context: %{price: 100, tax: 20.0, total: 120.0}, message: nil} # Error stops subsequent steps Extep.new(%{user_id: nil}) |> Extep.run(:user, fn ctx -> if ctx.user_id, do: {:ok, %{id: ctx.user_id, name: "Alice"}}, else: {:error, "User ID required"} end) |> Extep.run(:settings, fn ctx -> {:ok, fetch_user_settings(ctx.user)} end) #=> %Extep{status: :error, context: %{user_id: nil}, # message: %{user: "User ID required"}} # Updating existing keys Extep.new(%{count: 1}) |> Extep.run(:count, fn ctx -> {:ok, ctx.count + 1} end) |> Extep.run(:count, fn ctx -> {:ok, ctx.count * 2} end) #=> %Extep{status: :ok, context: %{count: 4}, message: nil} ``` -------------------------------- ### Synchronous Validation with Extep.run/2 Source: https://context7.com/shore-gmbh/extep/llms.txt Executes a synchronous validation step without altering the context. It returns :ok if the validation passes, or an {:error, message} tuple if it fails. The pipeline can also be halted with a specific return value. ```elixir # Success case - context unchanged Extep.new(%{user_id: 1, email: "alice@example.com"}) |> Extep.run(fn ctx -> if ctx.email =~ ~r/@/, do: :ok, else: {:error, "Invalid email"} end) #=> %Extep{status: :ok, context: %{user_id: 1, email: "alice@example.com"}, message: nil} # Error case with named function defmodule Validators do def validate_user(ctx) do if Map.has_key?(ctx, :user_id), do: :ok, else: {:error, "Missing user_id"} end end Extep.new(%{email: "test@example.com"}) |> Extep.run(&Validators.validate_user/1) #=> %Extep{status: :error, context: %{email: "test@example.com"}, # message: %{validate_user: "Missing user_id"}} # Halt case - stops pipeline with custom message Extep.new(%{subscription_status: "cancelled"}) |> Extep.run(fn ctx -> if ctx.subscription_status == "cancelled" do {:halt, {:ok, "Subscription already cancelled"}} else :ok end end) |> Extep.return(:subscription_status) #=> {:ok, "Subscription already cancelled"} ``` -------------------------------- ### Collect multiple validation errors Source: https://context7.com/shore-gmbh/extep/llms.txt Implements form validation that collects all errors instead of stopping at the first failure. Each validation step appends errors to a shared error list in the context. After all validations, returns either the valid parameters or a comprehensive list of all validation failures. ```elixir defmodule FormValidator do def validate_form(params) do # Collect all validation errors Extep.new(%{params: params, errors: []}) |> validate_field(:email, &validate_email/1) |> validate_field(:password, &validate_password/1) |> validate_field(:age, &validate_age/1) |> Extep.return(fn ctx -> if Enum.empty?(ctx.errors) do {:ok, ctx.params} else {:error, ctx.errors} end end) end defp validate_field(extep, field, validator) do Extep.run(extep, fn ctx -> case validator.(ctx.params[field]) do :ok -> {:ok, ctx} {:error, msg} -> {:ok, %{ctx | errors: [{field, msg} | ctx.errors]}} end end) end end ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.