akasha 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 01eaf484143fe6572ed3efe8338c2b2fb1251bf2
4
- data.tar.gz: 99b1cfb52b4cb781dd83b2c711d22cbb1ff27ace
3
+ metadata.gz: 1294447dab3ae4202502a232dcc069d06dcdbf5d
4
+ data.tar.gz: 296cfc35578c3f15a90d55f0f0ed1db018458541
5
5
  SHA512:
6
- metadata.gz: f67f3ffbe4d1e75c4c514cff8770f8d8aa95948bafc8241bd1161d27e660765d2de0036d6578e7be60a3cc05f8bd1899400ad24b4461cc85d2b5fd36095ec3a0
7
- data.tar.gz: 447548998189e251c818d4ab8dfb88b71b85e31aa5e70934d22fa6498b8703ba269ca331d71dde6c0f2936330b231c23dbfaf341b0533a78dabec0b98970e898
6
+ metadata.gz: 0b177423fce21ab067cee6c37f0d50e1d132056611791af3af17c56b2b8a2ba6ea20d76a11462e99459814b1ce24259ea86d01bc794818110da1ea49283fcbbc
7
+ data.tar.gz: 3cbe93b9aeafeca8b3541d01feaacfb6bdd0395bf3ed92d9be73a5ee7df3dfdb593deb8ad55e9d9647748f51a069ae881c9fd970aa473fc73f5087f63d012c3e
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1,49 @@
1
+ # Enables Travis to use their new container-based infrastructure
2
+ sudo: false
3
+
4
+ # Build for Ruby
5
+ language: ruby
6
+
7
+ # Enables caching for bundler
8
+ cache: bundler
9
+
10
+ # Passes arguments to bundle install (http://gembundler.com/man/bundle-install.1.html)
11
+ # bundler_args:
12
+
13
+ before_install:
14
+ - gem update --system
15
+ - gem update bundler
16
+
17
+ # Specify which ruby versions you wish to run your tests on, each version will be used
18
+ rvm:
19
+ - 2.2.9
20
+ - 2.3.6
21
+ - 2.4.3
22
+ - 2.5.0
23
+
24
+ # Define how to run your tests (defaults to `bundle exec rake` or `rake` depending on whether you have a `Gemfile`)
25
+ script: "bundle exec rake"
26
+
27
+ # Define tasks to be completed before and after tests run . Will allow folding of content on frontend
28
+ #before_script:
29
+ # - command_1
30
+ # - command_2
31
+ #
32
+ #after_script:
33
+ # - command_1
34
+ # - command_2
35
+
36
+ # Specify the recipients for email notification
37
+ #notifications:
38
+ # recipients:
39
+ # - email-address-1
40
+ # - email-address-2
41
+
42
+ # Disable email notifications
43
+ #notifications:
44
+ # disabled: true
45
+
46
+ # notifications:
47
+ # webhooks:
48
+ # urls:
49
+ # - https://webhooks.gitter.im/e/c6dbb9323007dfcf81df
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+
4
+ ## Version 0.1.0
5
+
6
+ * Cleaner syntax for adding events to changesets: `changeset.append(:it_happened, foo: 'bar')`.
7
+ * Support for command routing (`Akasha::CommandRouter`).
8
+
9
+
10
+ ## Version 0.0.1
11
+
12
+ Initial release, basic functionality.
@@ -1,12 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- akasha (0.0.1)
4
+ akasha (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ byebug (10.0.2)
10
+ coderay (1.1.2)
9
11
  diff-lcs (1.3)
12
+ method_source (0.9.0)
13
+ pry (0.11.3)
14
+ coderay (~> 1.1.0)
15
+ method_source (~> 0.9.0)
16
+ pry-byebug (3.6.0)
17
+ byebug (~> 10.0)
18
+ pry (~> 0.10)
10
19
  rake (10.5.0)
11
20
  rspec (3.7.0)
12
21
  rspec-core (~> 3.7.0)
@@ -29,6 +38,7 @@ PLATFORMS
29
38
  DEPENDENCIES
30
39
  akasha!
31
40
  bundler (~> 1.16)
41
+ pry-byebug
32
42
  rake (~> 10.0)
33
43
  rspec (~> 3.7)
34
44
  timecop
data/README.md CHANGED
@@ -20,31 +20,51 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ The code below uses Sinatra to demonstrate how to use the library in a web application.
24
+ This library makes no assumptions about any web framework, you can use it in any way you see fit.
25
+
23
26
  ```ruby
24
27
  require 'akasha'
25
-
28
+ require 'sinatra'
26
29
 
27
30
  class User < Akasha::Aggregate
28
- def sign_up(email, password)
29
- changeset << Akasha::Event.new(:user_signed_up, email: email, password: password)
31
+ def sign_up(email:, password:, admin: false, **)
32
+ changeset << Akasha::Event.new(:user_signed_up, email: email, password: password, admin: admin)
30
33
  end
31
34
 
32
- def on_user_signed_up(email:, password:, **_)
35
+ def on_user_signed_up(email:, password:, admin:, **)
33
36
  @email = email
34
37
  @password = password
38
+ @admin = admin
35
39
  end
36
40
  end
37
41
 
38
- def initialize
39
- repository = Akasha::Repository.new(Akasha::Storage::MemoryEventStore.new)
40
- Akasha::Aggregate.connect!(repository)
42
+
43
+ before do
44
+ @router = Akasha::CommandRouter.new
45
+
46
+ # Aggregates will load from and save to in-memory storage.
47
+ repository = Akasha::Repository.new(Akasha::Storage::MemoryEventStore.new)
48
+ Akasha::Aggregate.connect!(repository)
49
+
50
+ # This is how you link commands to aggregates.
51
+ @router.register_default_route(:sign_up, User)
52
+
53
+ # Nearly identital to the default handling above but we're setting the admin
54
+ # flag to demo custom command handling.
55
+ @router.register_route(:sign_up_admin) do |aggregate_id, **data|
56
+ user = User.find_or_create(aggregate_id)
57
+ user.sign_up(email: data[:email], password: data[:password], admin: true)
58
+ user.save!
59
+ end
41
60
  end
42
61
 
43
- def handle_sign_up_command(params)
44
- user = User.find_or_create(params[:id])
45
- user.email = params[:email]
46
- user.password = params[:password]
47
- user.save!
62
+ post '/users/:user_id' do # With CQRS client pass unique aggregate ids.
63
+ @router.route!(:sign_up,
64
+ params[:user_id],
65
+ email: params[:email],
66
+ password: params[:password])
67
+ 'OK'
48
68
  end
49
69
  ```
50
70
 
@@ -52,7 +72,7 @@ end
52
72
 
53
73
  ## Next steps
54
74
 
55
- - [ ] Command routing (default and user-defined)
75
+ - [x] Command routing (default and user-defined)
56
76
  - [ ] EventHandler (relying only on Eventstore)
57
77
  - [ ] HTTP Eventstore storage backend
58
78
  - [ ] Namespacing for events and aggregates
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
28
  spec.add_development_dependency 'rspec', '~> 3.7'
29
29
  spec.add_development_dependency 'timecop'
30
+ spec.add_development_dependency 'pry-byebug'
30
31
  end
@@ -1,4 +1,5 @@
1
1
  require 'akasha/event'
2
2
  require 'akasha/aggregate'
3
+ require 'akasha/command_router'
3
4
  require 'akasha/repository'
4
5
  require 'akasha/storage/memory_event_store'
@@ -8,7 +8,7 @@ module Akasha
8
8
  #
9
9
  # class User < Akasha::Aggregate
10
10
  # def sign_up(email, password)
11
- # changeset << Akasha::Event.new(:user_signed_up, email: email, password: password)
11
+ # changeset.append(:user_signed_up, email: email, password: password)
12
12
  # end
13
13
  #
14
14
  # def on_user_signed_up(email:, password:, **_)
@@ -38,7 +38,9 @@ module Akasha
38
38
  module InstanceMethods
39
39
  # Saves the aggregate.
40
40
  def save!
41
+ return if changeset.empty?
41
42
  self.class.repository.save_aggregate(self)
43
+ changeset.clear!
42
44
  end
43
45
  end
44
46
  end
@@ -10,8 +10,18 @@ module Akasha
10
10
  end
11
11
 
12
12
  # Adds an event to the changeset.
13
- def <<(event)
14
- @events << event
13
+ def append(event_name, **data)
14
+ @events << Akasha::Event.new(event_name, **data)
15
+ end
16
+
17
+ # Returns true if no changes recorded.
18
+ def empty?
19
+ @events.empty?
20
+ end
21
+
22
+ # Clears the changeset.
23
+ def clear!
24
+ @events = []
15
25
  end
16
26
  end
17
27
  end
@@ -0,0 +1,36 @@
1
+ require_relative 'command_router/default_handler'
2
+
3
+ module Akasha
4
+ # Routes commands to their handlers.
5
+ class CommandRouter
6
+ # Raised when no corresponding target can be found for a command.
7
+ NotFoundError = Class.new(RuntimeError)
8
+
9
+ def initialize
10
+ @routes = {}
11
+ end
12
+
13
+ # Registers a custom route, specifying either a lambda or a block.
14
+ # If both lambda and block are specified, lambda takes precedence.
15
+ def register_route(command, lambda = nil, &block)
16
+ callable = lambda || block
17
+ @routes[command] = callable
18
+ end
19
+
20
+ # Registers a default route, mapping a command to an aggregate class.
21
+ # As a result, when `#route!` is called for that command, the aggregate
22
+ # will be loaded from repository, the command will be sent to the object
23
+ # to invoke the object's method, and finally the aggregate will be saved.
24
+ def register_default_route(command, aggregate_class)
25
+ register_route(command, DefaultHandler.new(aggregate_class))
26
+ end
27
+
28
+ # Routes a command to the registered target.
29
+ # Raises NotFoundError if no corresponding target can be found.
30
+ def route!(command, aggregate_id, **data)
31
+ handler = @routes[command]
32
+ return handler.call(command, aggregate_id, **data) if handler
33
+ raise NotFoundError, "Target for command #{command.inspect} not found"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ module Akasha
2
+ class CommandRouter
3
+ # Default command handler.
4
+ # Works by loading aggregate from the repo by id,
5
+ # invoking its method `command`, passing all data,
6
+ # and saving changes to the aggregate in the end.
7
+ class DefaultHandler
8
+ def initialize(klass)
9
+ @klass = klass
10
+ end
11
+
12
+ def call(command, aggregate_id, **data)
13
+ aggregate = @klass.find_or_create(aggregate_id)
14
+ aggregate.public_send(command, **data)
15
+ aggregate.save!
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Akasha
2
- VERSION='0.0.1'
2
+ VERSION='0.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: akasha
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Bilski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-03 00:00:00.000000000 Z
11
+ date: 2018-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
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'
69
83
  description: A simple CQRS library for Ruby.
70
84
  email:
71
85
  - marcin@tooploox.com
@@ -74,8 +88,11 @@ extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
90
  - ".gitignore"
91
+ - ".rspec"
77
92
  - ".rubocop.yml"
78
93
  - ".ruby-version"
94
+ - ".travis.yml"
95
+ - CHANGELOG.md
79
96
  - Gemfile
80
97
  - Gemfile.lock
81
98
  - LICENSE.txt
@@ -88,6 +105,8 @@ files:
88
105
  - lib/akasha/aggregate.rb
89
106
  - lib/akasha/aggregate/syntax_helpers.rb
90
107
  - lib/akasha/changeset.rb
108
+ - lib/akasha/command_router.rb
109
+ - lib/akasha/command_router/default_handler.rb
91
110
  - lib/akasha/event.rb
92
111
  - lib/akasha/repository.rb
93
112
  - lib/akasha/storage/memory_event_store.rb