Skip to content

tstaetter/nanites

Repository files navigation

RSpec

Nanites - tiny command pattern framework for Ruby

Installation

Add this line to your application's Gemfile:

gem 'nanites', github: 'tstaetter/nanites', branch: 'main'

And then execute:

$ bundle install

When available on rubygems.org, you can install it yourself as:

$ gem install nanites

TODO: Push gem to rubygems when initial release is ready

Usage

Commands

Using the commands is pretty straight forward (see specs/support for more examples).

class MyCommand < Nanites::Commands::Command
  def execute(**params)
    # your code here
    if all_went_well
      success success_payload
    else
      error error_payload
    end
  end
end

# Be sure to only use the class method #execute, it ensures save execution
result = MyCommand.execute some_payload

puts result.option if result.success?
# => Nanites::Some @value=<success payload>

Result values are always wrapped in an Nanites::Option object which either is a Nanites::Some indicating some return value is available or Nanites::None for no value.

This is done in order to not having the hassle to deal with nil values. This approach is inspired by the Option type in Rust.

Compounds

Compounds can be used to combine several commands. A little example:

cmd1 = SomeUsefulCommand.new payload
cmd2 = SomeAnalyticsCommand.new payload

compound = Nanites::Commands::Compound.new cmd1, cmd2

context = compound.execute
# => 'context' is a hash containing the execution results of each command with the commands ID as key

Specialized compounds

There are some specials compounds as well, e.g. FirstSomeCompound returning the first result option which is a Some, or MatchSomeCompound returning only results of commands returning Some options.

For a full list of special compounds see lib/nanites/compounds. Implementing your own specialized compound is as easy as the following (taken from lib/nanites/compounds/match_some_compound.rb):

class MatchSomeCompound < Compound
  def initialize(*nanites)
    super

    @filter = ->(result) { result.option.some? }
  end
end

The magic happens here in the line @filter = ->(result) { result.option.some? } defining lambda checking the result. The filter is applied within the parent class execute method, getting passed each command result.

Some and None

Some and None are both descendants of Option. Each call of Nanites::Commands::Command#execute will return either a Some or None, indicating that the call returns some value, or no value resp.

When using those values, calling None#value will always return nil. If you need to have an error raised, use None#value!.

Taking the example from the above code, the behaviour is as follows:

puts result.option.value
# => will return some value if result.success? is true, nil otherwise

puts result.option.value!
# => will return some value if result.success? is true, raise a Nanites::Errors::ValueError otherwise

Development

rake spec to run the tests.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/tstaetter/nanites.