lieutenant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4b4bd4305e270917371083e9ad6138b7a45f6473
4
+ data.tar.gz: 5bd3c11a3945578b07e45226dc95efdd4388ba3a
5
+ SHA512:
6
+ metadata.gz: 11e225acbc456c2e8093eb467a1537df64c98400686d295a1ecfbd1c96f2d7463f896cc1c2363d38b8d37700a30db74f7cb731c4b967d11562a1ad0e461b9f30
7
+ data.tar.gz: 95136b8ad7cd14e41d4bc2eb7b2a3c8e4a76f2fad74b684574ef76d63e962422585377d2b044eb6ee1d7ec813269c5970c6ba4888ad57cf57276ffea4704e467
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ Metrics/LineLength:
5
+ Max: 120
6
+
7
+ Metrics/ClassLength:
8
+ Max: 150
9
+
10
+ Metrics/MethodLength:
11
+ Max: 15
12
+
13
+ Metrics/BlockLength:
14
+ Enabled: false
15
+
16
+ Style/RegexpLiteral:
17
+ Enabled: false
18
+
19
+ Layout/IndentArray:
20
+ Enabled: false
21
+
22
+ Layout/IndentHash:
23
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.16.0.pre.2
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ gemspec
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # **Lieutenant**
2
+
3
+ ## **CQRS/ES Toolkit to command them all**
4
+
5
+ Lieutenant is a toolkit that implements various of the components of Command & Query Responsability Segregation (CQRS) and Event Sourcing (ES). It means that your application can get rid of the "current" state of the entities you choose and store all the *changes* that led them to it.
6
+
7
+ This gem aims to be most independent as possible of your tecnological choices, it means that it should work with Rails, Sinatra, pure Rack apps or whatever you want.
8
+
9
+ If you are not familiarized, you may check this references:
10
+
11
+ - [CQRS Journey](https://msdn.microsoft.com/en-us/library/jj554200.aspx)
12
+ - [crqs.nu](http://cqrs.nu/)
13
+ - [Event Sourcing, by Martin Fowler](https://martinfowler.com/eaaDev/EventSourcing.html)
14
+ - [CQRS Documents, by Greg Young](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf)
15
+ - [Choosing an architecture, from TrustBK](https://blog.trustbk.com/choosing-an-architecture-85750e1e5a03)
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'lieutenant'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install lieutenant
32
+
33
+ ## Usage
34
+
35
+ By now, Lieutenant offer the components listed below. With each one, there's a description and example usage. If you cannot understand it, feel free to open an issue. Or if you think that it's not sufficient to other people, pull requests are welcome!
36
+
37
+ - [Commands](#commands)
38
+ - [Command Sender](#command-sender)
39
+ - [Command Handlers](#command-handlers)
40
+ - [Aggregate Repositories](#aggregate-repositories)
41
+ - [Aggregates](#aggregates)
42
+ - [Events](#events)
43
+ - [Event Store](#event-store)
44
+ - [Event Bus](#event-bus)
45
+
46
+ ### Commands
47
+
48
+ TODO
49
+
50
+
51
+ ### Command Sender
52
+
53
+ TODO
54
+
55
+
56
+ ### Command Handlers
57
+
58
+ TODO
59
+
60
+
61
+ ### Aggregate Repositories
62
+
63
+ TODO
64
+
65
+
66
+ ### Aggregates
67
+
68
+ TODO
69
+
70
+
71
+ ### Events
72
+
73
+ TODO
74
+
75
+
76
+ ### Event Store
77
+
78
+ TODO
79
+
80
+
81
+ ### Event Bus
82
+
83
+ TODO
84
+
85
+
86
+ ## Roadmap
87
+
88
+ In order to give some directions to the development of this gem, the roadmap below presents in a large picture of the plans to the future (more or less ordered).
89
+
90
+ - Projections
91
+ - Better documentation
92
+ - Command filters
93
+ - Command retry policies
94
+ - Sagas
95
+ - More implementations of event store
96
+ - More implementations of event bus
97
+
98
+ ## Development
99
+
100
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
101
+
102
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
103
+
104
+ You can also use `bundle exec rake lint` to be sure that your code follows our policies. We currently use [rubocop](https://github.com/bbatsov/rubocop) and [reek](https://github.com/troessner/reek).
105
+
106
+ ## Contributing
107
+
108
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gabteles/lieutenant.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+ require 'reek/rake/task'
7
+ require 'yard'
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+ RuboCop::RakeTask.new(:rubocop) do |t|
11
+ t.options = ['--display-cop-names']
12
+ end
13
+ Reek::Rake::Task.new do |t|
14
+ t.fail_on_error = false
15
+ end
16
+ YARD::Rake::YardocTask.new
17
+
18
+ task default: :spec
19
+ task lint: %i[rubocop reek]
20
+ task fulltest: %i[spec lint]
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'lieutenant'
6
+ require 'pry'
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Representation of an aggregate root
5
+ module Aggregate
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ # Define common class methods to aggregates
11
+ module ClassMethods
12
+ def load_from_history(id, history)
13
+ allocate.send(:load_from_history, id, history)
14
+ end
15
+
16
+ def on(*event_classes, &handler)
17
+ event_classes.each do |event_class|
18
+ unless event_class < Event
19
+ raise(Lieutenant::Exception, "Expected #{event_class} to include Lieutenant::Event")
20
+ end
21
+
22
+ handlers[event_class] = handlers[event_class].push(handler)
23
+ end
24
+ end
25
+
26
+ def handlers_for(event_class)
27
+ handlers[event_class]
28
+ end
29
+
30
+ private
31
+
32
+ def handlers
33
+ @handlers ||= Hash.new { [] }
34
+ end
35
+ end
36
+
37
+ attr_reader :id
38
+ attr_reader :uncommitted_events
39
+ attr_reader :version
40
+
41
+ def mark_as_committed
42
+ self.version += uncommitted_events.size
43
+ uncommitted_events.clear
44
+ end
45
+
46
+ protected
47
+
48
+ def apply(event_class, **params)
49
+ event = event_class.with(**params)
50
+ internal_apply(event)
51
+ uncommitted_events << event
52
+ end
53
+
54
+ attr_writer :version
55
+
56
+ private
57
+
58
+ def setup(id)
59
+ @id = id
60
+ @uncommitted_events = []
61
+ @version = -1
62
+ end
63
+
64
+ def load_from_history(id, history)
65
+ setup(id)
66
+
67
+ history.each do |event|
68
+ internal_apply(event)
69
+ self.version += 1
70
+ end
71
+
72
+ self
73
+ end
74
+
75
+ def internal_apply(event)
76
+ raise(Lieutenant::Exception, "Invalid event: #{event.inspect}") unless event.valid?
77
+ self.class.handlers_for(event.class).each { |handler| instance_exec(event, &handler) }
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Manages the repository logic to persist and retrieve aggregates
5
+ class AggregateRepository
6
+ def initialize(store)
7
+ @store = store
8
+ end
9
+
10
+ def unit_of_work
11
+ AggregateRepositoryUnit.new(store)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :store
17
+
18
+ # Represents one unit of work of the repository to grant independence
19
+ # between multiple concurrent commands being handled
20
+ class AggregateRepositoryUnit
21
+ def initialize(store)
22
+ @aggregates = {}
23
+ @store = store
24
+ end
25
+
26
+ def add_aggregate(aggregate)
27
+ aggregates[aggregate.id] = aggregate
28
+ end
29
+
30
+ def load_aggregate(aggregate_type, aggregate_id)
31
+ aggregates[aggregate_id] ||= begin
32
+ history = store.event_stream_for(aggregate_id)
33
+ aggregate_type.load_from_history(aggregate_id, history)
34
+ end
35
+ end
36
+
37
+ def execute
38
+ yield(self)
39
+ commit
40
+ ensure
41
+ clean
42
+ end
43
+
44
+ private
45
+
46
+ def commit
47
+ return if aggregates.empty?
48
+
49
+ store.transaction do
50
+ aggregates.each_value(&method(:commit_aggregate))
51
+ end
52
+ end
53
+
54
+ def clean
55
+ aggregates.clear
56
+ end
57
+
58
+ attr_reader :aggregates
59
+ attr_reader :store
60
+
61
+ # :reek:FeatureEnvy
62
+ def commit_aggregate(aggregate)
63
+ store.save_events(aggregate.id, aggregate.uncommitted_events, aggregate.version)
64
+ aggregate.mark_as_committed
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Syntax helper to define commands
5
+ module Command
6
+ def self.included(base)
7
+ base.include(Message)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Command handler helper. Allows clean syntax to register handlers:
5
+ #
6
+ # module FooCommandHandler
7
+ # include Lieutenant::CommandHandler
8
+ #
9
+ # on(BarCommand) do |repository, command|
10
+ # # ...
11
+ # end
12
+ # end
13
+ module CommandHandler
14
+ def self.included(base)
15
+ base.extend(self)
16
+ end
17
+
18
+ # :reek:UtilityFunction
19
+ def on(command_class, &block)
20
+ Lieutenant.config.command_sender.register(command_class, block)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Command bus dispatch commands to the appropriate handler and manages the repository commit/clean
5
+ class CommandSender
6
+ def initialize(aggregate_repository)
7
+ @aggregate_repository = aggregate_repository
8
+ @handlers = {}
9
+ end
10
+
11
+ def register(command_class, handler)
12
+ raise(Lieutenant::Exception, "Handler for #{command_class} already registered") if handlers.key?(command_class)
13
+ handlers[command_class] = handler
14
+ end
15
+
16
+ def dispatch(command)
17
+ handler = handler_for(command.class)
18
+ # TODO: Filters
19
+ raise(Lieutenant::Exception, "Invalid command: #{command.inspect}") unless command.valid?
20
+ aggregate_repository.unit_of_work.execute { |repository| handler.call(repository, command) }
21
+ # rescue Exception::ConcurrencyConflict
22
+ # TODO: implement command retry policy
23
+ end
24
+
25
+ alias call dispatch
26
+
27
+ private
28
+
29
+ attr_reader :aggregate_repository
30
+ attr_reader :handlers
31
+
32
+ def handler_for(command_class)
33
+ handlers.fetch(command_class) do
34
+ raise(Exception::NoRegisteredHandler, "No registered handler for #{command_class}")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Manages configuration
5
+ class Config
6
+ # :reek:BooleanParameter
7
+ def event_bus(implementation = false)
8
+ return @event_bus = implementation if implementation
9
+ @event_bus ||= EventBus::InMemory.new
10
+ end
11
+
12
+ # :reek:BooleanParameter
13
+ def event_store(implementation = false)
14
+ return @event_store_implementation = implementation if implementation
15
+ @event_store ||= EventStore.new(@event_store_implementation, event_bus)
16
+ end
17
+
18
+ def aggregate_repository
19
+ @aggregate_repository ||= AggregateRepository.new(event_store)
20
+ end
21
+
22
+ def command_sender
23
+ @command_sender ||= CommandSender.new(aggregate_repository)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # The basic interface to register the aggregates events
5
+ module Event
6
+ def self.included(base)
7
+ base.include(Message)
8
+ end
9
+
10
+ attr_reader :aggregate_id, :sequence_number
11
+
12
+ def prepare(aggregate_id, sequence_number)
13
+ @aggregate_id = aggregate_id
14
+ @sequence_number = sequence_number
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ module EventBus
5
+ # Memory implementation of the event bus. Publishes and notifies on the same memory space.
6
+ class InMemory
7
+ def initialize
8
+ @handlers = Hash.new { [] }
9
+ end
10
+
11
+ def subscribe(*event_classes, &handler)
12
+ event_classes.each do |event_class|
13
+ handlers[event_class] = handlers[event_class].push(handler)
14
+ end
15
+ end
16
+
17
+ def publish(event)
18
+ block = CALL_HANDLER_WITH_EVENT[event]
19
+ handlers[:all].each(&block)
20
+ handlers[event.class].each(&block)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :handlers
26
+
27
+ CALL_HANDLER_WITH_EVENT = ->(event) { ->(handler) { handler.call(event) } }
28
+ private_constant :CALL_HANDLER_WITH_EVENT
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Publishes and receives messages from the aggregates updates
5
+ module EventBus
6
+ autoload :InMemory, 'lieutenant/event_bus/in_memory'
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ class EventStore
5
+ # Memory implementation of the event store. Stores events while the application is running
6
+ class InMemory
7
+ def initialize
8
+ @store = {}
9
+ end
10
+
11
+ def persist(events)
12
+ events.each { |event| (store[event.aggregate_id] ||= []).push(event) }
13
+ end
14
+
15
+ def event_stream_for(aggregate_id)
16
+ events = store[aggregate_id]
17
+ return nil unless events
18
+ Enumerator.new { |yielder| events.each(&yielder.method(:<<)) }
19
+ end
20
+
21
+ def aggregate_sequence_number(aggregate_id)
22
+ return -1 unless store.key?(aggregate_id)
23
+ store[aggregate_id].last.sequence_number
24
+ end
25
+
26
+ def transaction
27
+ # In memory event store currently does not support transactions.
28
+ yield
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :store
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Event stores handles pushing and pulling events from the event store.
5
+ class EventStore
6
+ autoload :InMemory, 'lieutenant/event_store/in_memory'
7
+
8
+ def initialize(store, event_bus)
9
+ @store = store
10
+ @event_bus = event_bus
11
+ end
12
+
13
+ def save_events(aggregate_id, events, expected_version)
14
+ raise(Exception::ConcurrencyConflict) if store.aggregate_sequence_number(aggregate_id) != expected_version
15
+
16
+ PREPARE_EVENTS[aggregate_id, events, expected_version].tap do |final_events|
17
+ store.persist(final_events)
18
+ final_events.each(&event_bus.method(:publish))
19
+ end
20
+ end
21
+
22
+ def event_stream_for(aggregate_id)
23
+ store.event_stream_for(aggregate_id) || raise(Exception::AggregateNotFound, aggregate_id)
24
+ end
25
+
26
+ def transaction(&blk)
27
+ store.transaction(&blk)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :store
33
+ attr_reader :event_bus
34
+
35
+ PREPARE_EVENTS = lambda do |aggregate_id, events, sequence_number|
36
+ events.lazy.with_index.map do |event, idx|
37
+ event.prepare(aggregate_id, sequence_number + idx + 1)
38
+ event
39
+ end
40
+ end
41
+
42
+ private_constant :PREPARE_EVENTS
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ class Exception
5
+ # Raised when eventes associated with an aggregate id are not found
6
+ class AggregateNotFound < Lieutenant::Exception
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ class Exception
5
+ # Raised when expected version of the agreggate does not match the real one
6
+ # when saving events
7
+ class ConcurrencyConflict < Lieutenant::Exception
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ class Exception
5
+ # Raised when a handler to a command is not registered
6
+ class NoRegisteredHandler < Lieutenant::Exception
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Generic class to all Lieutenant exceptions. Anything is
5
+ # rescue-able with Lieutenant::Exception
6
+ class Exception < StandardError
7
+ autoload :AggregateNotFound, 'lieutenant/exception/aggregate_not_found'
8
+ autoload :ConcurrencyConflict, 'lieutenant/exception/concurrency_conflict'
9
+ autoload :NoRegisteredHandler, 'lieutenant/exception/no_registered_handler'
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ # Helper to define messages with validation
5
+ module Message
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ base.include(ActiveModel::Validations)
9
+ end
10
+
11
+ # Define common class methods to commands
12
+ module ClassMethods
13
+ def with(params)
14
+ new.tap do |command|
15
+ params.each_pair do |key, value|
16
+ begin
17
+ command.send("#{key}=", value)
18
+ rescue NoMethodError # rubocop:disable Lint/HandleExceptions
19
+ # DO NOTHING
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lieutenant
4
+ VERSION = '0.1.0'
5
+ end
data/lib/lieutenant.rb ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'active_model'
5
+
6
+ # Lieutenant namespace
7
+ module Lieutenant
8
+ autoload :Aggregate, 'lieutenant/aggregate'
9
+ autoload :AggregateRepository, 'lieutenant/aggregate_repository'
10
+ autoload :Command, 'lieutenant/command'
11
+ autoload :CommandHandler, 'lieutenant/command_handler'
12
+ autoload :CommandSender, 'lieutenant/command_sender'
13
+ autoload :Config, 'lieutenant/config'
14
+ autoload :Event, 'lieutenant/event'
15
+ autoload :EventBus, 'lieutenant/event_bus'
16
+ autoload :EventStore, 'lieutenant/event_store'
17
+ autoload :Exception, 'lieutenant/exception'
18
+ autoload :Message, 'lieutenant/message'
19
+ autoload :VERSION, 'lieutenant/version'
20
+
21
+ module_function
22
+
23
+ @config = Config.new
24
+
25
+ def config
26
+ block_given? ? yield(@config) : @config
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'lieutenant/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'lieutenant'
10
+ spec.version = Lieutenant::VERSION
11
+ spec.authors = ['Gabriel Teles']
12
+ spec.email = ['gab.teles@hotmail.com']
13
+
14
+ spec.summary = 'CQRS/ES Toolkit to command them all'
15
+ spec.homepage = 'https://github.com/gabteles/lieutenant'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'reek'
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'rubocop'
30
+ spec.add_development_dependency 'simplecov'
31
+ spec.add_development_dependency 'yard'
32
+ spec.add_dependency 'activemodel'
33
+ end
metadata ADDED
@@ -0,0 +1,197 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lieutenant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Teles
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: reek
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activemodel
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description:
140
+ email:
141
+ - gab.teles@hotmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - ".rubocop.yml"
149
+ - ".travis.yml"
150
+ - Gemfile
151
+ - README.md
152
+ - Rakefile
153
+ - bin/console
154
+ - bin/setup
155
+ - lib/lieutenant.rb
156
+ - lib/lieutenant/aggregate.rb
157
+ - lib/lieutenant/aggregate_repository.rb
158
+ - lib/lieutenant/command.rb
159
+ - lib/lieutenant/command_handler.rb
160
+ - lib/lieutenant/command_sender.rb
161
+ - lib/lieutenant/config.rb
162
+ - lib/lieutenant/event.rb
163
+ - lib/lieutenant/event_bus.rb
164
+ - lib/lieutenant/event_bus/in_memory.rb
165
+ - lib/lieutenant/event_store.rb
166
+ - lib/lieutenant/event_store/in_memory.rb
167
+ - lib/lieutenant/exception.rb
168
+ - lib/lieutenant/exception/aggregate_not_found.rb
169
+ - lib/lieutenant/exception/concurrency_conflict.rb
170
+ - lib/lieutenant/exception/no_registered_handler.rb
171
+ - lib/lieutenant/message.rb
172
+ - lib/lieutenant/version.rb
173
+ - lieutenant.gemspec
174
+ homepage: https://github.com/gabteles/lieutenant
175
+ licenses: []
176
+ metadata: {}
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubyforge_project:
193
+ rubygems_version: 2.6.11
194
+ signing_key:
195
+ specification_version: 4
196
+ summary: CQRS/ES Toolkit to command them all
197
+ test_files: []