A loose collection of Ruby related code that currently has no specific place to live in.
HasAttributes Module
require 'edgycircle_toolbox/has_attributes'
class Dummy
include EdgycircleToolbox::HasAttributes
attributes :a, :b
end
dummy = Dummy.new({ a: 1, b: 2, c: 3 })
dummy.a # => 1
dummy.b # => 2
dummy.c # => NoMethodError
dummy.attributes # => [:a, :b]
CQRS CommandResult
Returning self
from add_error
, add_event
and set_command
allows us write concise code like return result.add_error(:authentication_error) unless authenticated?
.
require 'edgycircle_toolbox/cqrs/command_result'
command = :command_a
result = EdgycircleToolbox::CQRS::CommandResult.new(command)
result.add_error(:error) # => result
result.add_event(:event) # => result
result.set_command(:command_b) # => result
result.failure? # => true
CQRS Handler Module
A handler class has to implement call(command)
and accept a command instance as parameter.
require 'edgycircle_toolbox/cqrs/handler'
class DummyHandler
include EdgycircleToolbox::CQRS::Handler
def call(command)
# Business Logic
end
end
CQRS Command Module
A command class can have a schema and attributes. The schema can be used by the command bus to validate the data before a command is built. Currently the definitions are redundant, changing this could be an improvement made in the future. Every command has a default attribute id
and a schema to validate its presence and make sure it's a string.
The module uses dry-validation for the schema and its validation.
require 'edgycircle_toolbox/cqrs/command'
class DummyCommand
include EdgycircleToolbox::CQRS::Command
schema do
required(:title).filled(:str?)
end
attributes :title
end
CQRS Command Bus
Can build commands from a parameter hash if the data fits the commands schema. A submitted command is passed to the corresponding handler. Events from the command handler get published on the message bus. Both build
and submit
return a CommandResult
.
require 'edgycircle_toolbox/cqrs/command_bus'
command_bus = EdgycircleToolbox::CQRS::CommandBus.new
command_bus.register EnterTicketCommand, EnterTicketHandler
command_result = command_bus.build(parameter_hash)
command_result = command_bus.submit(command_result.command)
CQRS Message Bus
require 'edgycircle_toolbox/cqrs/message_bus'
class RandomEvent
end
class CommonEvent
end
EdgycircleToolbox::CQRS::MessageBus.subscribe([RandomEvent], ->(event) { puts 'Output 1' })
EdgycircleToolbox::CQRS::MessageBus.subscribe([RandomEvent, CommonEvent], ->(event) { puts 'Output 2' })
EdgycircleToolbox::CQRS::MessageBus.publish(RandomEvent.new)
# =>
# Output 1
# Output 2
EdgycircleToolbox::CQRS::MessageBus.publish(CommonEvent.new)
# =>
# Output 2
CQRS Model Collector
Can be used to collect data based on events.
require 'edgycircle_toolbox/cqrs/model_collector'
class RandomEvent
end
class CommonEvent
end
EdgycircleToolbox::CQRS::ModelCollector.register(RandomEvent, ->(event) { [1, 2] })
EdgycircleToolbox::CQRS::ModelCollector.register(RandomEvent, ->(event) { [3] })
EdgycircleToolbox::CQRS::ModelCollector.register(CommonEvent, ->(event) { [4] })
EdgycircleToolbox::CQRS::ModelCollector.for_events([
RandomEvent.new,
CommonEvent.new
])
# =>
# [1, 2, 3, 4]
Sonapi Resource Module
require 'edgycircle_toolbox/sonapi/resource'
class CommandResource
include EdgycircleToolbox::Sonapi::Resource
type "commands"
dynamic_attributes Proc.new { |object| object.attribute_names - [:id] }
parameter_filter Proc.new { |name, value| true }
end
class TicketResource
include EdgycircleToolbox::Sonapi::Resource
type "tickets"
attribute :estimate
end
Sonapi Dynamic Resource
require 'edgycircle_toolbox/sonapi/dynamic_resource'
class Dummy
end
class Tree
end
class DummyResource
include EdgycircleToolbox::Sonapi::Resource
type "dummies"
end
class TreeResource
include EdgycircleToolbox::Sonapi::Resource
type "trees"
end
EdgycircleToolbox::Sonapi::DynamicResource.register(Dummy, DummyResource)
EdgycircleToolbox::Sonapi::DynamicResource.register(Tree, TreeResource)
EdgycircleToolbox::Sonapi::DynamicResource.serialize([Dummy.new, Tree.new])
Sonapi Error Resource
The ErrorResource
can serialize errors conforming to the SerializableError
interface.
require 'edgycircle_toolbox/sonapi/error_resource'
EdgycircleToolbox::Sonapi::ErrorResource.serialize([error_a, error_b])
Sonapi SerializableError Module
An error class has to implement the title
, pointer
and detail
methods.
require 'edgycircle_toolbox/sonapi/serializable_error'
class EmailTakenError
include EdgycircleToolbox::Sonapi::SerializableError
def initialize(email)
@email = email
end
def title
"Email Taken Error"
end
def pointer
nil
end
def detail
"The email #{@email} is already taken, please use something different"
end
end
Sonapi ValidationError
require 'edgycircle_toolbox/sonapi/validation_error'
EdgycircleToolbox::Sonapi::ValidationError.new(:title, "Title is not long enough")