Skip to content

Commit

Permalink
Update README (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
serradura committed Jul 20, 2020
1 parent 723b99c commit 714c6b6
Showing 1 changed file with 38 additions and 28 deletions.
66 changes: 38 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ class Multiply < Micro::Case
# 2. Define the method `call!` with its business logic
def call!

# 3. Wrap the use case result/output using the `Success()` or `Failure()` methods
# 3. Wrap the use case result/output using the `Success(result: *)` or `Failure(result: *)` methods
if a.is_a?(Numeric) && b.is_a?(Numeric)
Success(a * b)
Success result: { number: a * b }
else
Failure { '`a` and `b` attributes must be numeric' }
Failure result: { message: '`a` and `b` attributes must be numeric' }
end
end
end
Expand All @@ -126,22 +126,22 @@ end
result = Multiply.call(a: 2, b: 2)

result.success? # true
result.value # 4
result.data # { number: 4 }

# Failure result

bad_result = Multiply.call(a: 2, b: '2')

bad_result.failure? # true
bad_result.value # "`a` and `b` attributes must be numeric"
bad_result.data # { message: "`a` and `b` attributes must be numeric" }

#-----------------------------#
# Calling a use case instance #
#-----------------------------#

result = Multiply.new(a: 2, b: 3).call

result.value # 6
result.value # { number: 6 }

# Note:
# ----
Expand All @@ -156,11 +156,13 @@ result.value # 6
A `Micro::Case::Result` stores the use cases output data. These are their main methods:
- `#success?` returns true if is a successful result.
- `#failure?` returns true if is an unsuccessful result.
- `#value` the result value itself.
- `#data` the result data itself.
- `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
- `#on_success` or `#on_failure` are hook methods that help you define the application flow.
- `#on_success` or `#on_failure` are hook methods that help you to define the application flow.
- `#use_case` if is a failure result, the use case responsible for it will be accessible through this method. This feature is handy to handle a flow failure (this topic will be covered ahead).
- `#then` allows if the current result is a success, the `then` method will allow to applying a new use case for its value.
- `#then` this method will allow applying a new use case if the current result was a success. The idea of this feature is to allow the creation of dynamic flows.

> **Note:** for backward compatibility, you could use the `#value` method as an alias of `#data` method.
[⬆️ Back to Top](#table-of-contents-)

Expand All @@ -175,9 +177,13 @@ class Divide < Micro::Case
attributes :a, :b

def call!
invalid_attributes.empty? ? Success(a / b) : Failure(invalid_attributes)
rescue => e
Failure(e)
if invalid_attributes.empty?
Success result: { number: a / b }
else
Failure result: { invalid_attributes: invalid_attributes }
end
rescue => exception
Failure result: exception
end

private def invalid_attributes
Expand All @@ -190,7 +196,7 @@ end
result = Divide.call(a: 2, b: 2)

result.type # :ok
result.value # 1
result.data # { number: 1 }
result.success? # true
result.use_case # raises `Micro::Case::Error::InvalidAccessToTheUseCaseObject: only a failure result can access its own use case`

Expand All @@ -199,7 +205,7 @@ result.use_case # raises `Micro::Case::Error::InvalidAccessToTheUseCaseObject: o
bad_result = Divide.call(a: 2, b: '2')

bad_result.type # :error
bad_result.value # {"b"=>"2"}
bad_result.data # { invalid_attributes: { "b"=>"2" } }
bad_result.failure? # true
bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b="2", @__result=#<Micro::Case::Result:0x0000 @use_case=#<Divide:0x0000 ...>, @type=:error, @value={"b"=>"2"}, @success=false>

Expand All @@ -208,31 +214,33 @@ bad_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>"2"}, @a=2, @b
err_result = Divide.call(a: 2, b: 0)

err_result.type # :exception
err_result.value # <ZeroDivisionError: divided by 0>
err_result.data # { exception: <ZeroDivisionError: divided by 0> }
err_result.failure? # true
err_result.use_case # #<Divide:0x0000 @__attributes={"a"=>2, "b"=>0}, @a=2, @b=0, @__result=#<Micro::Case::Result:0x0000 @use_case=#<Divide:0x0000 ...>, @type=:exception, @value=#<ZeroDivisionError: divided by 0>, @success=false>

# Note:
# ----
# Any Exception instance which is wrapped by
# the Failure() method will receive `:exception` instead of the `:error` type.
# the Failure(result: *) method will receive `:exception` instead of the `:error` type.
```

[⬆️ Back to Top](#table-of-contents-)

#### How to define custom result types?

Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare a block to set their values.
Answer: Use a symbol as the argument of `Success()`, `Failure()` methods and declare the `result:` keyword to set the result data.

```ruby
class Multiply < Micro::Case
attributes :a, :b

def call!
return Success(a * b) if a.is_a?(Numeric) && b.is_a?(Numeric)

Failure(:invalid_data) do
attributes.reject { |_, input| input.is_a?(Numeric) }
if a.is_a?(Numeric) && b.is_a?(Numeric)
Success result: { number: a * b }
else
Failure :invalid_data, result: {
attributes: attributes.reject { |_, input| input.is_a?(Numeric) }
}
end
end
end
Expand All @@ -242,39 +250,41 @@ end
result = Multiply.call(a: 3, b: 2)

result.type # :ok
result.value # 6
result.data # { number: 6 }
result.success? # true

# Failure result

bad_result = Multiply.call(a: 3, b: '2')

bad_result.type # :invalid_data
bad_result.value # {"b"=>"2"}
bad_result.data # { attributes: {"b"=>"2"} }
bad_result.failure? # true
```

[⬆️ Back to Top](#table-of-contents-)

#### Is it possible to define a custom result type without a block?

Answer: Yes, it is. But only for failure results!
Answer: Yes, it is possible. But this will have special behavior because the result data will be a hash with the given type as the key and true as its value.

```ruby
class Multiply < Micro::Case
attributes :a, :b

def call!
return Failure(:invalid_data) unless a.is_a?(Numeric) && b.is_a?(Numeric)

Success(a * b)
if a.is_a?(Numeric) && b.is_a?(Numeric)
Success result: { number: a * b }
else
Failure(:invalid_data)
end
end
end

result = Multiply.call(a: 2, b: '2')

result.failure? # true
result.value # :invalid_data
result.data # { :invalid_data => true }
result.type # :invalid_data
result.use_case.attributes # {"a"=>2, "b"=>"2"}

Expand Down

0 comments on commit 714c6b6

Please sign in to comment.