serradura / u-case

Represent use cases in a simple and powerful way while writing modular, expressive and sequentially logical code.
https://rubygems.org/gems/u-case
MIT License
527 stars 33 forks source link

Add `Micro::Case#Check` #87

Closed MatheusRich closed 3 years ago

MatheusRich commented 4 years ago

This PR adds the method Check on Micro::Case.

Micro::Case#Check

Check is a method that make simple to evaluate boolean conditions and turn them into Result objects.

Let's take this simple use case as an example. It should check if a given number is even and fails on odd numbers:

  class IsEven  < Micro::Case
    attributes :number

    def call!
      if number.even?
        Success(result: { number_is_even: true } )
      else
        Failure(result: { number_is_even: false } )
      end
    end
  end

We can simplify this conditional by using Check. Take a look:

  class IsEven < Micro::Case
    attributes :number

    def call!
      Check { number.even? }
    end
  end

Changing the result type

It's possible to add a type a custom result type:

  class IsEven < Micro::Case
    attributes :number

    def call!
      Check(:number_is_even) { number.even? }
    end
  end

Changing the result value

By default Check adds the return of the given block as the Result value. In the example above, the value would be the result of number.even?.

You can define a custom return value with the result: keyword argument:

  class IsEven < Micro::Case
    attributes :number

    def call!
      Check(:number_is_even, result: number) { number.even? }
    end
  end

In the example above, the result value will be number, rather than number.even?.

Check with private methods

Check really shines when using private methods to define steps in a use case:

  class IsEven < Micro::Case
    attributes :number

    def call!
      check_number_is_an_integer
        .then(method(:check_number_is_even))
    end

    private

    def check_number_is_an_integer
      Check(:number_is_integer) { number.is_a? Integer }
    end

    def check_number_is_even
      Check(:number_is_even) { number.even? }
    end
  end
serradura commented 4 years ago

@MatheusRich Thak you again for your contribution! 👏

As we talked on Telegram, follow the final "spec" for this feature

The expected output when the check receives a truthy result.

Check(result: true) # Success(:ok, result: { check: true })

Check(result: 1) # Success(:ok, result: { check: true })

The expected output when the check receives a falsey result

Check(result: nil) # Failure(:error, result: { check: false })

Check(result: false) # Failure(:error, result: { check: false })

Exposes a way to allow the definition of the success/failure data

Check(result: true, on: { success: {a: 1}, failure: {})
# Success(:ok, result: { a: 1 })

Custom types should be used as the result type and data

Check(:valid, result: true) # Success(:valid, result: { valid: true })

Check(:valid, result: false) # Failure(:valid, result: { valid: false })