galetahub/redress

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Redress

[![Gem Version](https://badge.fury.io/rb/redress.svg)](http://badge.fury.io/rb/redress)
[![Code Climate](https://codeclimate.com/github/galetahub/redress/badges/gpa.svg)](https://codeclimate.com/github/galetahub/redress)
[![Build Status](https://galetahub.semaphoreci.com/badges/redress/branches/master.svg?style=shields&key=1c60c225-c1b2-4889-9887-18aacf2e6184)](https://galetahub.semaphoreci.com/projects/redress)
[![Coverage Status](https://coveralls.io/repos/github/galetahub/redress/badge.svg?branch=master)](https://coveralls.io/github/galetahub/redress?branch=master)

## Motivation (Command pattern)

The command pattern is sometimes called a service object, an operation, an action, and probably more names that I’m not aware of. Whatever the name we gave it, the purpose of such a pattern is rather simple: take a business action and put it behind an object with a simple interface.

<!-- Tocer[start]: Auto-generated, don't remove. -->

## Table of Contents

  - [Requirements](#requirements)
  - [Setup](#setup)
  - [Usage](#usage)
  - [Tests](#tests)
  - [Versioning](#versioning)
  - [Code of Conduct](#code-of-conduct)
  - [Contributions](#contributions)
  - [License](#license)
  - [History](#history)
  - [Credits](#credits)

<!-- Tocer[finish]: Auto-generated, don't remove. -->

## Requirements

0. [Ruby 2.3](https://www.ruby-lang.org)
1. dry-monads
2. dry-struct
3. hashie

### Optional

To support validations just add activemodel to your project

4. activemodel

## Setup

For an insecure install, type the following:

    gem install redress

Add the following to your Gemfile:

    gem "redress"

## Usage

### Forms

```ruby
# app/forms/application_form.rb
require 'redress/form'

class ApplicationForm < Redress::Form
end
```

Let's define simple form (Built-in Types https://dry-rb.org/gems/dry-types/built-in-types/):

```ruby
class SimpleForm < ApplicationForm
  mimic :user

  define_schema do
    attribute :nickname, Redress::Types::Strict::String.default('superman')
    attribute :name, Redress::Types::StrippedString
    attribute :email, Redress::Types::StrippedString
    attribute :name_with_email, String
    attribute :age, Redress::Types::Coercible::Integer
    attribute :terms_of_service, Redress::Types::Bool
  end

  validates :name, presence: true
  validates :email, presence: true

  def map_model(user)
    self.name_with_email = "#{user.name} <#{user.email}>"
  end
end
```

Form with default values (http://dry-rb.org/gems/dry-types/default-values/):

```ruby
require 'securerandom'

class CommentForm < Redress::Form
  define_schema do
    attribute(:id, Redress::Types::Coercible::String.default { SecureRandom.uuid })
    attribute(:content, Redress::Types::String)
  end

  validates :content, presence: true
end
```

Form with multiple comments:

```ruby
class CommentForm < Redress::Form
  define_schema do
    attribute :id, Redress::Types::Coercible::Integer
    attribute :content, Redress::Types::String
  end

  validates :content, presence: true
end

class OrderForm < Redress::Form
  mimic :order

  define_schema do
    attribute :title, Redress::Types::String
    attribute :comments, Redress::Types::Array.of(CommentForm)
  end
end
```

Form with context:

```ruby
class CommentForm < Redress::Form
  define_schema do
    attribute :id, Redress::Types::Coercible::Integer
    attribute :content, Redress::Types::String
  end

  validates :content, presence: true
  validate :unsure_order_state_waiting

  private

  def unsure_order_state_waiting
    context.order.state?(:waiting)
  end
end

CommentForm.new(content: 'Hi').with_context(order: order)
```

### Commands

```ruby
# app/commands/application_command.rb
require 'redress/command'

class ApplicationCommand < Redress::Command
end
```

Simple command for user registration:

```ruby
# app/commands/users/create_command.rb
module Users
  class CreateCommand < ApplicationCommand
    def initialize(form)
      @form = form
    end

    def call
      return Failure(@form) if @form.invalid?

      user = User.new(@form.attributes)

      if user.save
        Success(user)
      else
        Failure(user)
      end
    end
  end
end
```

### Controllers

```ruby
# app/controllers/users_controller.rb
class UsersController < Account::BaseController
  respond_to :json, only: :update

  def new
    @user_form = SimpleForm.new
  end

  def create
    @user_form = SimpleForm.from_params(params)

    Users::CreateCommand.call(@user_form) do |c|
      c.success { head status: 201 }
      c.failure { |form| render status: 422, json: { errors: form.errors } }
    end
  end
end

```

## Tests

To test, run:

    bundle exec rspec ./spec/

## Versioning

Read [Semantic Versioning](http://semver.org) for details. Briefly, it means:

- Major (X.y.z) - Incremented for any backwards incompatible public API changes.
- Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes.
- Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes.

## Code of Conduct

Please note that this project is released with a [CODE OF CONDUCT](CODE_OF_CONDUCT.md). By
participating in this project you agree to abide by its terms.

## Contributions

Read [CONTRIBUTING](CONTRIBUTING.md) for details.

## License

Copyright (c) 2017 [Fodojo LLC](https://www.fodojo.com/).
Read [LICENSE](LICENSE.md) for details.

## History

Read [CHANGES](CHANGES.md) for details.
Built with [Gemsmith](https://github.com/bkuhlmann/gemsmith).

## Credits

Developed by [Igor Galeta](https://www.linkedin.com/in/igor-galeta-585a9730/) at
[Fodojo LLC](https://www.fodojo.com/).