README.md in attr-gather-1.2.0 vs README.md in attr-gather-1.2.1
- old
+ new
@@ -1,11 +1,149 @@
# attr-gather
-[![Actions Status](https://github.com/ianks/attr-gather/workflows/.github/workflows/ruby.yml/badge.svg)](https://github.com/ianks/attr-gather/actions)
+![Actions Status](https://github.com/ianks/attr-gather/workflows/Build%20+%20Test%20+%20Lint/badge.svg)
+[![Maintainability](https://api.codeclimate.com/v1/badges/b825a3bc37ad6a76e005/maintainability)](https://codeclimate.com/github/ianks/attr-gather/maintainability)
-A gem for creating simple workflows to enhance entities with extra attributes.
-At a high level, `attr-gather` provides a process to sync attributes from many
-sources (third party APIs, legacy databases, etc).
+A gem for creating workflows that "enhance" entities with extra attributes. At a high level, [attr-gather](https://github.com/ianks/attr-gather) provides a process to fetch information from many data sources (such as third party APIs, legacy databases, etc.) in a fully parallelized fashion.
+
+## Usage
+
+### Defining your workflow
+
+```ruby
+# define a workflow
+class EnhanceProfile
+ include Attr::Gather::Workflow
+
+ # contains all the task implementations
+ container TasksContainer
+
+ # filter out invalid data using a Dry::Validation::Contract
+ # anything that doesn't match this schema will be filtered out
+ 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
+
+ # each task returns a hash of data that will be merged into the result
+ task :fetch_post do |t|
+ t.depends_on = []
+ end
+
+ # will run in parallel
+ task :fetch_user do |t|
+ t.depends_on = [:fetch_post]
+ end
+
+ # will run in parallel
+ task :fetch_email_info do |t|
+ t.depends_on = [:fetch_user]
+ end
+end
+```
+
+### Defining some tasks
+
+```ruby
+class PostFetcher
+ def call(attrs)
+ res = HTTP.get("https://jsonplaceholder.typicode.com/posts/#{attrs[:id]}")
+ post = JSON.parse(res.to_s, symbolize_names: true)
+
+ { title: post[:title], user_id: post[:userId], body: post[:body] }
+ end
+end
+```
+
+```ruby
+class UserFetcher
+ # will have access to the PostFetcher attributes here
+ def call(attrs)
+ res = HTTP.get("https://jsonplaceholder.typicode.com/users/#{attrs[:user_id]}")
+ user = JSON.parse(res.to_s, symbolize_names: true)
+
+ { user: { name: user[:name], email: user[:email] } }
+ end
+end
+```
+
+```ruby
+class EmailInfoFetcher
+ # will have access to the PostFetcher attributes here
+ def call(user:)
+ res = HTTP.timeout(3).get("https://api.trumail.io/v2/lookups/json?email=#{user[:email]}")
+ info = JSON.parse(res.to_s, symbolize_names: true)
+
+ # will deep merge with the final result
+ { user: { email_info: { deliverable: info[:deliverable], free: info[:free] } } }
+ end
+end
+```
+
+### Registering your tasks
+
+```ruby
+class MyContainer
+ extend Dry::Container::Mixin
+
+ register :fetch_post, PostFetcher
+ register :fetch_user, UserFetcher
+ register :fetch_email_info, EmailInfoFetcher
+end
+```
+
+### Run it!
+
+```ruby
+enhancer = EnhanceUserProfile.new
+enhancer.call(id: 12).value!
+```
+
+And this is the result...
+
+```ruby
+{
+ :id => 12,
+ :user_id => 2,
+ :user => {
+ :email => "Shanna@melissa.tv",
+ :name => "Ervin Howell",
+ :email_info => { :deliverable => true, :free => true },
+ :gravatar => "https://www.gravatar.com/avatar/241af7d19a0a7438794aef21e4e19b79"
+ }
+}
+```
+
+You can even preview it as an SVG!
+
+```ruby
+enhancer.to_dot(preview: true) # requires graphviz (brew install graphviz)
+```
+
+## Features
+
+- Offers DSL for defining workflows and merging the results from each task
+- Execution engine optimally parallelizes the execution of the workflow dependency graph using [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) Promises
+- Very easy to unit test
+- Ability to filter out bad/junky data using [dry-validation](https://dry-rb.org/gems/dry-validation) contracts
+
+## What are the main difference between this Ruby project and similar ones?
+
+- Operates on a single entity rather than a list, so easily adoptable in existing systems
+- Focuses on the "fetching" and filtering of data solely, and not transformation or storage
+- Focuses on having a clean PORO interface to make testing simple
+- Provides a declarative interface for merging results from many sources (APIs, legacy databases, etc.) which allows for prioritization
## Links
- [API Documentation](https://www.rubydoc.info/gems/attr-gather)