ruby_dci 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +13 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +19 -1
- data/README.md +12 -11
- data/lib/dci/accessor.rb +4 -0
- data/lib/dci/configuration.rb +10 -8
- data/lib/dci/context.rb +13 -5
- data/lib/dci/event_router.rb +4 -5
- data/lib/dci/null_transaction.rb +2 -0
- data/lib/dci/version.rb +3 -1
- data/lib/dci.rb +5 -1
- data/ruby_dci.gemspec +0 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a61a836ec90dab9c17d7f88cfd866ac24fd7e4ca
|
4
|
+
data.tar.gz: 892f6b68acb2b87b6ef653c6c19860b3eac95c37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f81879bf07b9d8d4c335f9e7c53783edaf19d0cc3e1f0b6abf5f26d11edd3f696bfacd017925778f9d7b7fb93abc3258af419e27481f7cb5d0689e8c7430277
|
7
|
+
data.tar.gz: 888c5768b1f5c9d35454173eb91103ce3ad40914cb39f99e22b4898190e3b084fd033f3703d16b5eb725f39cc79ad03fc9c3be6ae2b40cafb06b663d2b74c89b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.3.0 (2018-05-24)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- Renamed `DCI::Configuration` accessors. `event_routes` is now `routes`, `route_methods` is now `router`, `raise_in_event_router` is now `raise_in_router`.
|
6
|
+
- `events` is not an instance variable defined in the context anymore, but has the same access style like the `context` itself. In the role don't push events to `context.events`, but to `context_events`.
|
7
|
+
|
8
|
+
Bugfixes:
|
9
|
+
|
10
|
+
- Added specs
|
11
|
+
- Added `README.md`
|
12
|
+
- Added `CHANGELOG.md`
|
13
|
+
|
1
14
|
## 0.2.0 (2018-05-19)
|
2
15
|
|
3
16
|
Features:
|
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{ repo_name }" }
|
4
|
+
|
5
|
+
gem "coveralls", require: false
|
4
6
|
|
5
7
|
# Specify your gem's dependencies in ruby_dci.gemspec
|
6
8
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby_dci (0.
|
4
|
+
ruby_dci (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
coderay (1.1.2)
|
10
|
+
coveralls (0.8.21)
|
11
|
+
json (>= 1.8, < 3)
|
12
|
+
simplecov (~> 0.14.1)
|
13
|
+
term-ansicolor (~> 1.3)
|
14
|
+
thor (~> 0.19.4)
|
15
|
+
tins (~> 1.6)
|
10
16
|
diff-lcs (1.3)
|
17
|
+
docile (1.1.5)
|
18
|
+
json (2.1.0)
|
11
19
|
method_source (0.9.0)
|
12
20
|
pry (0.11.3)
|
13
21
|
coderay (~> 1.1.0)
|
@@ -26,12 +34,22 @@ GEM
|
|
26
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
27
35
|
rspec-support (~> 3.7.0)
|
28
36
|
rspec-support (3.7.1)
|
37
|
+
simplecov (0.14.1)
|
38
|
+
docile (~> 1.1.0)
|
39
|
+
json (>= 1.8, < 3)
|
40
|
+
simplecov-html (~> 0.10.0)
|
41
|
+
simplecov-html (0.10.2)
|
42
|
+
term-ansicolor (1.6.0)
|
43
|
+
tins (~> 1.0)
|
44
|
+
thor (0.19.4)
|
45
|
+
tins (1.16.3)
|
29
46
|
|
30
47
|
PLATFORMS
|
31
48
|
ruby
|
32
49
|
|
33
50
|
DEPENDENCIES
|
34
51
|
bundler (~> 1.16)
|
52
|
+
coveralls
|
35
53
|
pry (~> 0.11.3)
|
36
54
|
rake (~> 10.0)
|
37
55
|
rspec (~> 3.0)
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/egze/ruby_dci.svg?branch=master)](https://travis-ci.org/egze/ruby_dci)
|
2
|
+
[![Coverage Status](https://coveralls.io/repos/github/egze/ruby_dci/badge.svg?branch=master)](https://coveralls.io/github/egze/ruby_dci?branch=master)
|
2
3
|
|
3
4
|
# RubyDci
|
4
5
|
|
@@ -9,7 +10,7 @@ A classic DCI implementation for ruby with some extra sugar. I've been using DCI
|
|
9
10
|
Add this line to your application's Gemfile:
|
10
11
|
|
11
12
|
```ruby
|
12
|
-
gem 'ruby_dci'
|
13
|
+
gem 'ruby_dci', require: 'dci'
|
13
14
|
```
|
14
15
|
|
15
16
|
And then execute:
|
@@ -43,8 +44,8 @@ I configure the gem in `config/initializers/dci_configuration.rb`.
|
|
43
44
|
|
44
45
|
```ruby
|
45
46
|
DCI.configure do |config|
|
46
|
-
config.
|
47
|
-
config.
|
47
|
+
config.routes = Hash.new([])
|
48
|
+
config.router = EventRouter.new
|
48
49
|
config.transaction_class = ApplicationRecord
|
49
50
|
config.raise_in_event_router = !Rails.env.production?
|
50
51
|
config.on_exception_in_router = -> (exception) {}
|
@@ -55,7 +56,7 @@ end
|
|
55
56
|
|
56
57
|
Usually you want your code to run in a transaction. Either everything runs fine, or nothing is saved. This is done by wrapping the executed code in a transaction block. I use ActiveRecord, but you can use whatever you want. Your class just needs to implement a `transaction` method that takes a block. If you don't want any transactions, you can either skip `config.transaction_class` completely, or set it to `DCI::NullTransaction`.
|
57
58
|
|
58
|
-
### config.
|
59
|
+
### config.routes
|
59
60
|
|
60
61
|
This is your mapping of events that may happen in the context. Key is a class name, and the value is an array of method names. Example:
|
61
62
|
|
@@ -65,7 +66,7 @@ This is your mapping of events that may happen in the context. Key is a class na
|
|
65
66
|
}
|
66
67
|
```
|
67
68
|
|
68
|
-
The system will know that it needs to execute `send_product_added_notification` from `config.route_methods` for every event of class `DomainEvents::ProductAddedToCart`. If you don't have any actions that you need to perform after a transaction,
|
69
|
+
The system will know that it needs to execute `send_product_added_notification` from `config.route_methods` for every event of class `DomainEvents::ProductAddedToCart`. If you don't have any actions that you need to perform after a transaction, then just skip `config.event_routes` completely or set it to `Hash.new([])`.
|
69
70
|
|
70
71
|
I implement events as plain ruby Structs. Example:
|
71
72
|
|
@@ -79,12 +80,12 @@ end
|
|
79
80
|
|
80
81
|
Why do I do it like this? It makes it easier to add other callbacks later. I can do it in one place instead of searching through hundreds of files. Also makes testing easier.
|
81
82
|
|
82
|
-
### config.
|
83
|
+
### config.router
|
83
84
|
|
84
|
-
This is a class that implements the methods for the `config.
|
85
|
+
This is a class that implements the methods for the `config.routes` mapping. Example:
|
85
86
|
|
86
87
|
```ruby
|
87
|
-
class
|
88
|
+
class EventRouter
|
88
89
|
|
89
90
|
def send_product_added_notification(event)
|
90
91
|
AddedToCartNotificationJob.perform_later(id: event.product.id)
|
@@ -106,7 +107,7 @@ config.raise_in_event_router = !Rails.env.production?
|
|
106
107
|
In case there is an exception in the event router, you can provide a handler for the exception. It should be a lambda that receives an exception as a parameter. You can use it to log the exception. If you don't need any logging, just skip `config.on_exception_in_router` completely, or assign an empty lambda.
|
107
108
|
|
108
109
|
```ruby
|
109
|
-
config.
|
110
|
+
config.on_exception_in_router = -> (exception) { Rails.logger.error(exception) }
|
110
111
|
```
|
111
112
|
|
112
113
|
|
@@ -142,7 +143,7 @@ Couple of thigs to keep in mind:
|
|
142
143
|
|
143
144
|
### Role
|
144
145
|
|
145
|
-
In a Rails app I put my roles in `app/roles`. Roles are plain ruby modules. You define a role by including `DCI::Role`.
|
146
|
+
In a Rails app I put my roles in `app/roles`. Roles are plain ruby modules. You define a role by including `DCI::Role`. A role has access to the `context` and to the `context_events` methods. You can push events to `context_events` to process them after the transaction.
|
146
147
|
|
147
148
|
```ruby
|
148
149
|
module Customer
|
@@ -153,7 +154,7 @@ module Customer
|
|
153
154
|
# do your thing
|
154
155
|
|
155
156
|
# add event to the context
|
156
|
-
|
157
|
+
context_events << DomainEvents::ProductAddedToCart.new(product)
|
157
158
|
end
|
158
159
|
|
159
160
|
end
|
data/lib/dci/accessor.rb
CHANGED
data/lib/dci/configuration.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
module DCI
|
2
2
|
class Configuration
|
3
|
+
|
3
4
|
attr_accessor :transaction_class,
|
4
|
-
:
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
5
|
+
:routes,
|
6
|
+
:router,
|
7
|
+
:on_exception_in_router,
|
8
|
+
:raise_in_router
|
8
9
|
|
9
10
|
def initialize
|
10
11
|
@transaction_class = NullTransaction
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@on_exception_in_router = -> (exception) {
|
12
|
+
@routes = Hash.new([])
|
13
|
+
@router = nil
|
14
|
+
@raise_in_router = false
|
15
|
+
@on_exception_in_router = -> (exception) {}
|
15
16
|
end
|
17
|
+
|
16
18
|
end
|
17
19
|
end
|
data/lib/dci/context.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module DCI
|
2
|
-
|
3
2
|
module Context
|
4
3
|
|
5
4
|
include EventRouter
|
@@ -16,8 +15,10 @@ module DCI
|
|
16
15
|
|
17
16
|
def perform_in_transaction
|
18
17
|
old_context = context
|
19
|
-
|
18
|
+
old_context_events = context_events
|
19
|
+
|
20
20
|
self.context = self
|
21
|
+
self.context_events = []
|
21
22
|
|
22
23
|
res = nil
|
23
24
|
|
@@ -25,33 +26,40 @@ module DCI
|
|
25
26
|
res = call
|
26
27
|
end
|
27
28
|
|
28
|
-
route_events!(
|
29
|
+
route_events!(context_events)
|
29
30
|
res
|
30
31
|
ensure
|
31
32
|
self.context = old_context
|
32
|
-
self.
|
33
|
+
self.context_events = old_context_events
|
33
34
|
end
|
34
35
|
|
35
36
|
def context=(ctx)
|
36
37
|
Thread.current[:context] = ctx
|
37
38
|
end
|
38
39
|
|
40
|
+
def context_events=(ctx_events = [])
|
41
|
+
Thread.current[:context_events] = Array(ctx_events).compact.flatten
|
42
|
+
end
|
43
|
+
|
39
44
|
def call
|
40
45
|
raise NotImplementedError.new("implement me")
|
41
46
|
end
|
42
47
|
|
43
48
|
private
|
49
|
+
|
44
50
|
def init_context_events
|
45
51
|
[]
|
46
52
|
end
|
53
|
+
|
47
54
|
end
|
48
55
|
|
49
56
|
module ClassMethods
|
57
|
+
|
50
58
|
def call(*args)
|
51
59
|
new(*args).perform_in_transaction
|
52
60
|
end
|
61
|
+
|
53
62
|
end
|
54
63
|
|
55
64
|
end
|
56
|
-
|
57
65
|
end
|
data/lib/dci/event_router.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module DCI
|
2
|
-
|
3
2
|
module EventRouter
|
4
3
|
|
5
4
|
def route_events!(events)
|
@@ -7,10 +6,11 @@ module DCI
|
|
7
6
|
end
|
8
7
|
|
9
8
|
private
|
9
|
+
|
10
10
|
def route_event!(event)
|
11
|
-
DCI.configuration.
|
11
|
+
DCI.configuration.routes[event.class].each do |callback|
|
12
12
|
dispatch_catching_standard_errors do
|
13
|
-
DCI.configuration.
|
13
|
+
DCI.configuration.router.send(callback, event)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -21,10 +21,9 @@ module DCI
|
|
21
21
|
rescue StandardError => exception
|
22
22
|
DCI.configuration.on_exception_in_router.call(exception) rescue nil
|
23
23
|
|
24
|
-
raise exception if DCI.configuration.
|
24
|
+
raise exception if DCI.configuration.raise_in_router
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
29
|
-
|
30
29
|
end
|
data/lib/dci/null_transaction.rb
CHANGED
data/lib/dci/version.rb
CHANGED
data/lib/dci.rb
CHANGED
@@ -7,8 +7,11 @@ require "dci/context"
|
|
7
7
|
require "dci/role"
|
8
8
|
|
9
9
|
module DCI
|
10
|
+
|
10
11
|
class << self
|
11
|
-
|
12
|
+
|
13
|
+
#attr_accessor :configuration
|
14
|
+
|
12
15
|
end
|
13
16
|
|
14
17
|
def self.configuration
|
@@ -22,4 +25,5 @@ module DCI
|
|
22
25
|
def self.configure
|
23
26
|
yield(configuration)
|
24
27
|
end
|
28
|
+
|
25
29
|
end
|
data/ruby_dci.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_dci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aleksandr Lossenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -115,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
115
|
version: '0'
|
116
116
|
requirements: []
|
117
117
|
rubyforge_project:
|
118
|
-
rubygems_version: 2.
|
118
|
+
rubygems_version: 2.6.13
|
119
119
|
signing_key:
|
120
120
|
specification_version: 4
|
121
121
|
summary: Opinionated DCI implementation for ruby.
|