README.md in u-service-0.6.0 vs README.md in u-service-0.7.0

- old
+ new

@@ -4,14 +4,30 @@ [![Test Coverage](https://api.codeclimate.com/v1/badges/a30b18528a317435c2ee/test_coverage)](https://codeclimate.com/github/serradura/u-service/test_coverage) μ-service (Micro::Service) ========================== -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/micro/service`. To experiment with that code, run `bin/console` for an interactive prompt. +Create simple and powerful service objects. -TODO: Delete this and the text above, and describe your gem +- [μ-service (Micro::Service)](#%ce%bc-service-microservice) + - [Required Ruby version](#required-ruby-version) + - [Installation](#installation) + - [Usage](#usage) + - [How to create a basic Service Object?](#how-to-create-a-basic-service-object) + - [How to use the Service Object result hooks?](#how-to-use-the-service-object-result-hooks) + - [How to create a pipeline of Service Objects?](#how-to-create-a-pipeline-of-service-objects) + - [What is a strict Service Object?](#what-is-a-strict-service-object) + - [How to validate Service Object attributes?](#how-to-validate-service-object-attributes) + - [Development](#development) + - [Contributing](#contributing) + - [License](#license) + - [Code of Conduct](#code-of-conduct) +## Required Ruby version + +> \>= 2.2.0 + ## Installation Add this line to your application's Gemfile: ```ruby @@ -26,10 +42,206 @@ $ gem install u-service ## Usage -TODO: Write usage instructions here +### How to create a basic Service Object? + +```ruby +class Multiply < Micro::Service::Base + attributes :a, :b + + def call! + if a.is_a?(Numeric) && b.is_a?(Numeric) + Success(a * b) + else + Failure(:invalid_data) + end + end +end + +#====================# +# Calling a service # +#====================# + +result = Multiply.call(a: 2, b: 2) +p result.success? # true +p result.value # 4 + +#----------------------------# +# Calling a service instance # +#----------------------------# + +result = Multiply.new(a: 2, b: 3).call +p result.success? # true +p result.value # 6 + +#===========================# +# Verify the result failure # +#===========================# + +result = Multiply.call(a: '2', b: 2) +p result.success? # false +p result.failure? # true +p result.value # :invalid_data +``` + +### How to use the Service Object result hooks? + +```ruby +class Double < Micro::Service::Base + attributes :number + + def call! + return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric) + return Failure(:lte_zero) { 'the number must be greater than 0' } if number <= 0 + + Success(number * number) + end +end + +#================================# +# Printing the output if success # +#================================# + +Double + .call(number: 3) + .on_success { |number| p number } + .on_failure(:invalid) { |msg| raise TypeError, msg } + .on_failure(:lte_zero) { |msg| raise ArgumentError, msg } + +# The output when is a success: +# 9 + +#=============================# +# Raising an error if failure # +#=============================# + +Double + .call(number: -1) + .on_success { |number| p number } + .on_failure(:invalid) { |msg| raise TypeError, msg } + .on_failure(:lte_zero) { |msg| raise ArgumentError, msg } + +# The output (raised an error) when is a failure: +# ArgumentError (the number must be greater than 0) +``` + +### How to create a pipeline of Service Objects? + +```ruby +module Steps + class ConvertToNumbers < Micro::Service::Base + attribute :relation + + def call! + if relation.all? { |value| String(value) =~ /\d+/ } + Success(numbers: relation.map(&:to_i)) + else + Failure('relation must contain only numbers') + end + end + end + + class Add2 < Micro::Service::Strict + attribute :numbers + + def call! + Success(numbers.map { |number| number + 2 }) + end + end + + class Double < Micro::Service::Strict + attribute :numbers + + def call! + Success(numbers.map { |number| number * number }) + end + end +end + +Add2ToAllNumbers = Micro::Service::Pipeline[ + Steps::ConvertToNumbers, + Steps::Add2 +] + +DoubleAllNumbers = Micro::Service::Pipeline[ + Steps::ConvertToNumbers, + Steps::Double +] + +result = Add2ToAllNumbers.call(relation: %w[1 1 2 2 3 4]) + +p result.success? # true +p result.value # [3, 3, 4, 4, 5, 6] + +DoubleAllNumbers + .call(relation: %w[1 1 b 2 3 4]) + .on_failure { |message| p message } # "relation must contain only numbers" +``` + +### What is a strict Service Object? + +A: Is a service object which will require all keywords (attributes) on its initialization. + +```ruby +class Double < Micro::Service::Strict + attribute :numbers + + def call! + Success(numbers.map { |number| number * number }) + end +end + +Double.call({}) + +# The output (raised an error): +# ArgumentError (missing keyword: :numbers) +``` + +### How to validate Service Object attributes? + +Note: To do this your application must have the (activemodel >= 3.2)(https://rubygems.org/gems/activemodel) as a dependency. + +```ruby +# +# By default, if your project has the activemodel +# any kind of service attribute can be validated. +# +class Multiply < Micro::Service::Base + attribute :a + attribute :b + validates :a, :b, presence: true, numericality: true + + def call! + return Success(number: a * b) if valid? + + Failure(errors: self.errors) + end +end + +# +# But if do you want an automatic way to fail +# your services if there is some invalid data. +# You can use: +require 'micro/service/with_validation' + +# Using this approach, you can rewrite the previous sample with fewer lines of code. + +class Multiply < Micro::Service::WithValidation + attribute :a + attribute :b + validates :a, :b, presence: true, numericality: true + + def call! + Success(number: a * b) + end +end + +# Note: +# There is a strict variation for Micro::Service::WithValidation +# Use Micro::Service::Strict::Validation if do you want this behavior. +``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.