Project

redress

0.0
Low commit activity in last 3 years
A long-lived project that still receives updates
Build maintainable Ruby apps
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

>= 1.4.0
>= 1.4.0
>= 1.5.0
>= 0
 Project Readme

Redress

Gem Version Code Climate Build Status Coverage Status

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.

Table of Contents

  • Requirements
  • Setup
  • Usage
  • Tests
  • Versioning
  • Code of Conduct
  • Contributions
  • License
  • History
  • Credits

Requirements

  1. Ruby 2.3
  2. dry-monads
  3. dry-struct
  4. hashie

Optional

To support validations just add activemodel to your project

  1. activemodel

Setup

For an insecure install, type the following:

gem install redress

Add the following to your Gemfile:

gem "redress"

Usage

Forms

# 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/):

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/):

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:

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:

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

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

class ApplicationCommand < Redress::Command
end

Simple command for user registration:

# 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

# 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 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. By participating in this project you agree to abide by its terms.

Contributions

Read CONTRIBUTING for details.

License

Copyright (c) 2017 Fodojo LLC. Read LICENSE for details.

History

Read CHANGES for details. Built with Gemsmith.

Credits

Developed by Igor Galeta at Fodojo LLC.