# frozen_string_literal: true require 'dry-container' require 'dry-validation' require 'attr-gather' require 'http' # create the container class MyContainer extend Dry::Container::Mixin register :fetch_post do |id:, **_attrs| res = HTTP.get("https://jsonplaceholder.typicode.com/posts/#{id}") post = JSON.parse(res.to_s, symbolize_names: true) { title: post[:title], user_id: post[:userId], body: post[:body] } end register :fetch_user_from_buggy_api do |*| { user: { email: 'invalidemail' } } end register :fetch_user do |user_id:, **_attrs| res = HTTP.get("https://jsonplaceholder.typicode.com/users/#{user_id}") user = JSON.parse(res.to_s, symbolize_names: true) { user: { name: user[:name], email: user[:email] } } end register :email_info do |user:, **_attrs| res = HTTP.get("https://api.trumail.io/v2/lookups/json?email=#{user[:email]}") info = JSON.parse(res.to_s, symbolize_names: true) { user: { email_info: { deliverable: info[:deliverable], free: info[:free] } } } end register :gravatar_image do |user:, **_attrs| require 'digest/md5' email_address = user[:email].downcase hash = Digest::MD5.hexdigest(email_address) image_url = "https://www.gravatar.com/avatar/#{hash}" { user: { gravatar: image_url } } end end # define a workflow class EnhanceUserProfile include Attr::Gather::Workflow # contains all the task implementations container MyContainer # perform a deep merge of the task outputs for the result aggregator :deep_merge # filter out invalid values using a Dry::Validation::Contract filter_with_contract do params do required(:user_id).filled(:integer) optional(:user).hash do optional(:name).filled(:string) optional(:email).filled(:string) optional(:gravatar).filled(:string) optional(:email_info).hash do optional(:deliverable).filled(:bool?) optional(:free).filled(:bool?) end end end end task :fetch_post do |t| t.depends_on = [] end task :fetch_user_from_buggy_api do |t| t.depends_on = [:fetch_post] end task :fetch_user do |t| t.depends_on = [:fetch_post] end # will run in parallel task :email_info do |t| t.depends_on = [:fetch_user] end # will run in parallel task :gravatar_image do |t| t.depends_on = [:fetch_user] end end # run the workflow enhancer = EnhanceUserProfile.new puts puts 'Runing workflow...' puts puts 'Result' puts '======' pp enhancer.call(id: 12).value! # fun fact: you can preview as svg! puts print 'Would you like to preview the workflow as SVG? (y/n): ' exit 0 unless gets.strip == 'y' begin enhancer.to_dot(preview: true) rescue SignalException puts 'Done!' rescue StandardError abort 'Could not render SVG, please make sure you have ' \ 'graphviz installed (brew install graphviz), then retry' end