jouba 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/Gemfile.lock +12 -4
- data/README.md +113 -0
- data/VERSION +1 -1
- data/jouba.gemspec +20 -13
- data/lib/jouba.rb +20 -46
- data/lib/jouba/aggregate.rb +51 -39
- data/lib/jouba/cache.rb +40 -0
- data/lib/jouba/event.rb +27 -7
- data/lib/jouba/key.rb +11 -0
- data/lib/jouba/store.rb +76 -0
- data/spec/integration/customer_spec.rb +83 -0
- data/spec/lib/jouba/aggregate_spec.rb +88 -47
- data/spec/lib/jouba/cache_spec.rb +0 -0
- data/spec/lib/jouba/event_spec.rb +74 -10
- data/spec/lib/jouba/key_spec.rb +32 -0
- data/spec/lib/jouba_spec.rb +36 -182
- data/spec/spec_helper.rb +0 -7
- metadata +28 -10
- data/README.rdoc +0 -19
- data/lib/jouba/exceptions.rb +0 -3
- data/lib/jouba/stores.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15d2afcf883fa6ef14ca062a882155bc9b86b63d
|
4
|
+
data.tar.gz: a3c5d35b6e894ec126a532ee4c66e99c08630c83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e68608356450234636279deced5b95ed888ea798e2e5e1de6fc8e80d0568e64fad53f01470e4575a15a2c3c811e2ade46cbf24bec5767c6541549a7ffc9a2c6a
|
7
|
+
data.tar.gz: e78325ceaf57706cb4ba9f97eead6fa09f17aae35283aea62ea1d4fbd0781748abaf06d0c10136db30d63c8a19198ef44a3b7a87ecb5ca8ea6b912cd8fcc195f
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -3,6 +3,7 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
addressable (2.3.6)
|
5
5
|
ast (2.0.0)
|
6
|
+
atomic (1.1.99)
|
6
7
|
builder (3.2.2)
|
7
8
|
coderay (1.1.0)
|
8
9
|
descendants_tracker (0.0.4)
|
@@ -20,7 +21,7 @@ GEM
|
|
20
21
|
multi_json (>= 1.7.5, < 2.0)
|
21
22
|
nokogiri (~> 1.6.3)
|
22
23
|
oauth2
|
23
|
-
hashie (3.
|
24
|
+
hashie (3.4.0)
|
24
25
|
highline (1.6.21)
|
25
26
|
jeweler (2.0.1)
|
26
27
|
builder
|
@@ -33,6 +34,11 @@ GEM
|
|
33
34
|
rdoc
|
34
35
|
json (1.8.1)
|
35
36
|
jwt (1.2.0)
|
37
|
+
locality-uuid (1.0.0)
|
38
|
+
atomic
|
39
|
+
macaddr
|
40
|
+
macaddr (1.7.1)
|
41
|
+
systemu (~> 2.6.2)
|
36
42
|
method_source (0.8.2)
|
37
43
|
mini_portile (0.6.1)
|
38
44
|
multi_json (1.10.1)
|
@@ -80,19 +86,21 @@ GEM
|
|
80
86
|
simplecov-html (~> 0.8.0)
|
81
87
|
simplecov-html (0.8.0)
|
82
88
|
slop (3.5.0)
|
89
|
+
systemu (2.6.4)
|
83
90
|
thread_safe (0.3.4)
|
84
|
-
wisper (1.
|
91
|
+
wisper (1.6.0)
|
85
92
|
|
86
93
|
PLATFORMS
|
87
94
|
ruby
|
88
95
|
|
89
96
|
DEPENDENCIES
|
90
97
|
bundler (~> 1.0)
|
91
|
-
hashie (~> 3.
|
98
|
+
hashie (~> 3.4.0)
|
92
99
|
jeweler (~> 2.0)
|
100
|
+
locality-uuid
|
93
101
|
pry (~> 0)
|
94
102
|
rdoc (~> 3.12)
|
95
103
|
rspec (~> 2.14)
|
96
104
|
rubocop (~> 0.23)
|
97
105
|
simplecov (~> 0)
|
98
|
-
wisper (~> 1.
|
106
|
+
wisper (~> 1.6.0)
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# jouba
|
2
|
+
|
3
|
+
|
4
|
+
```ruby
|
5
|
+
class Customer < Hashie::Dash
|
6
|
+
include Jouba::Aggregate.new(prefix: :on)
|
7
|
+
property :uuid
|
8
|
+
property :name
|
9
|
+
|
10
|
+
def self.create(attributes)
|
11
|
+
Customer.new(uuid: SecureRandom.uuid).tap do |customer|
|
12
|
+
customer.create(attributes.merge(uuid: customer.uuid))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(attributes)
|
17
|
+
emit(:created, attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def on_created(attributes)
|
23
|
+
update_attributes!(attributes)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Admin
|
28
|
+
include Jouba::Aggregate.new(prefix: :on) # include Wisper and Anima
|
29
|
+
|
30
|
+
def self.create(attributes)
|
31
|
+
Admin.new(uuid: SecureRandom.uuid).tap do |admin|
|
32
|
+
admin.create(attributes.merge(uuid: customer.uuid))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create(attributes)
|
37
|
+
emit(:created, attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def on_created(attributes)
|
43
|
+
update_attributes!(attributes)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
Jouba.emit('us.computer1.cpu', :idle, {value: 50})
|
49
|
+
Jouba.stream('us.computer1.cpu').since(1.month.ago).where({value: ->(v) { v >= 20 }})
|
50
|
+
|
51
|
+
Jouba.config.Cache = Jouba::Cache::Memory.new
|
52
|
+
require 'jouba/aggregate'
|
53
|
+
customer_params = { fname: 'foo', lname: 'bar' }
|
54
|
+
|
55
|
+
c = Customer.create(customer_params, true)
|
56
|
+
c.fname # => "foo"
|
57
|
+
c.uuid # => 123
|
58
|
+
c.to_key # => "Customer.123"
|
59
|
+
|
60
|
+
d = Customer.find(c.uuid)
|
61
|
+
|
62
|
+
```
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class Store < AR
|
66
|
+
set_table_name :events
|
67
|
+
|
68
|
+
scope :since, -> (time) { where('timestamp >= ?', time) }
|
69
|
+
|
70
|
+
def self.stream(key, params={})
|
71
|
+
where(params).where(key: key)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.track(key, serialized_event)
|
75
|
+
create serialized_event
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Jouba.config.Store = Store
|
80
|
+
|
81
|
+
class UserRepository < AR
|
82
|
+
set_table_name :users
|
83
|
+
# must have a key column
|
84
|
+
|
85
|
+
def on_created(attributes)
|
86
|
+
self.create(attributes)
|
87
|
+
end
|
88
|
+
alias :on_created
|
89
|
+
end
|
90
|
+
|
91
|
+
Wisper.subscribe(UserRepository.new, scope: :Customer)
|
92
|
+
```
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
## Todo
|
97
|
+
- write a how to in english :)
|
98
|
+
|
99
|
+
## Contributing to jouba
|
100
|
+
|
101
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
102
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
103
|
+
* Fork the project.
|
104
|
+
* Start a feature/bugfix branch.
|
105
|
+
* Commit and push until you are happy with your contribution.
|
106
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
107
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
108
|
+
|
109
|
+
## Copyright
|
110
|
+
|
111
|
+
Copyright (c) 2014 gregory. See LICENSE.txt for
|
112
|
+
further details.
|
113
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
1.0.0
|
data/jouba.gemspec
CHANGED
@@ -2,21 +2,21 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: jouba
|
5
|
+
# stub: jouba 1.0.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "jouba"
|
9
|
-
s.version = "
|
9
|
+
s.version = "1.0.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["gregory"]
|
14
|
-
s.date = "2015-
|
14
|
+
s.date = "2015-02-03"
|
15
15
|
s.description = "Jouba is a tool set for event sourcing: aggregate root, entities and storage"
|
16
16
|
s.email = "greg2502@gmail.com"
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"LICENSE.txt",
|
19
|
-
"README.
|
19
|
+
"README.md"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
22
|
".document",
|
@@ -25,18 +25,22 @@ Gem::Specification.new do |s|
|
|
25
25
|
"Gemfile",
|
26
26
|
"Gemfile.lock",
|
27
27
|
"LICENSE.txt",
|
28
|
-
"README.
|
28
|
+
"README.md",
|
29
29
|
"Rakefile",
|
30
30
|
"VERSION",
|
31
31
|
"jouba.gemspec",
|
32
32
|
"lib/jouba.rb",
|
33
33
|
"lib/jouba/aggregate.rb",
|
34
|
+
"lib/jouba/cache.rb",
|
34
35
|
"lib/jouba/event.rb",
|
35
|
-
"lib/jouba/
|
36
|
-
"lib/jouba/
|
36
|
+
"lib/jouba/key.rb",
|
37
|
+
"lib/jouba/store.rb",
|
37
38
|
"lib/jouba/version.rb",
|
39
|
+
"spec/integration/customer_spec.rb",
|
38
40
|
"spec/lib/jouba/aggregate_spec.rb",
|
41
|
+
"spec/lib/jouba/cache_spec.rb",
|
39
42
|
"spec/lib/jouba/event_spec.rb",
|
43
|
+
"spec/lib/jouba/key_spec.rb",
|
40
44
|
"spec/lib/jouba_spec.rb",
|
41
45
|
"spec/spec_helper.rb"
|
42
46
|
]
|
@@ -49,8 +53,9 @@ Gem::Specification.new do |s|
|
|
49
53
|
s.specification_version = 4
|
50
54
|
|
51
55
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
|
-
s.add_runtime_dependency(%q<hashie>, ["~> 3.
|
53
|
-
s.add_runtime_dependency(%q<wisper>, ["~> 1.
|
56
|
+
s.add_runtime_dependency(%q<hashie>, ["~> 3.4.0"])
|
57
|
+
s.add_runtime_dependency(%q<wisper>, ["~> 1.6.0"])
|
58
|
+
s.add_runtime_dependency(%q<locality-uuid>, [">= 0"])
|
54
59
|
s.add_development_dependency(%q<pry>, ["~> 0"])
|
55
60
|
s.add_development_dependency(%q<rspec>, ["~> 2.14"])
|
56
61
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
@@ -59,8 +64,9 @@ Gem::Specification.new do |s|
|
|
59
64
|
s.add_development_dependency(%q<simplecov>, ["~> 0"])
|
60
65
|
s.add_development_dependency(%q<rubocop>, ["~> 0.23"])
|
61
66
|
else
|
62
|
-
s.add_dependency(%q<hashie>, ["~> 3.
|
63
|
-
s.add_dependency(%q<wisper>, ["~> 1.
|
67
|
+
s.add_dependency(%q<hashie>, ["~> 3.4.0"])
|
68
|
+
s.add_dependency(%q<wisper>, ["~> 1.6.0"])
|
69
|
+
s.add_dependency(%q<locality-uuid>, [">= 0"])
|
64
70
|
s.add_dependency(%q<pry>, ["~> 0"])
|
65
71
|
s.add_dependency(%q<rspec>, ["~> 2.14"])
|
66
72
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
@@ -70,8 +76,9 @@ Gem::Specification.new do |s|
|
|
70
76
|
s.add_dependency(%q<rubocop>, ["~> 0.23"])
|
71
77
|
end
|
72
78
|
else
|
73
|
-
s.add_dependency(%q<hashie>, ["~> 3.
|
74
|
-
s.add_dependency(%q<wisper>, ["~> 1.
|
79
|
+
s.add_dependency(%q<hashie>, ["~> 3.4.0"])
|
80
|
+
s.add_dependency(%q<wisper>, ["~> 1.6.0"])
|
81
|
+
s.add_dependency(%q<locality-uuid>, [">= 0"])
|
75
82
|
s.add_dependency(%q<pry>, ["~> 0"])
|
76
83
|
s.add_dependency(%q<rspec>, ["~> 2.14"])
|
77
84
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
data/lib/jouba.rb
CHANGED
@@ -1,64 +1,38 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'forwardable'
|
1
3
|
require 'hashie'
|
2
|
-
require '
|
4
|
+
require 'locality-uuid'
|
3
5
|
|
4
6
|
require 'jouba/version'
|
5
|
-
require 'jouba/
|
7
|
+
require 'jouba/key'
|
6
8
|
require 'jouba/event'
|
7
|
-
require 'jouba/
|
8
|
-
require 'jouba/
|
9
|
+
require 'jouba/store'
|
10
|
+
require 'jouba/cache'
|
9
11
|
|
10
12
|
module Jouba
|
11
13
|
module_function
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def alias_store(alias_name, target)
|
20
|
-
stores[alias_name] = stores[target]
|
21
|
-
end
|
15
|
+
DEFAULTS = {
|
16
|
+
Event: Jouba::Event,
|
17
|
+
Key: Jouba::Key,
|
18
|
+
Cache: Cache::Null.new,
|
19
|
+
Store: Jouba::EventStore.new
|
20
|
+
}
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
class<<self
|
23
|
+
extend Forwardable
|
24
|
+
def_delegators :config, :Key, :Event, :Cache, :Store
|
25
25
|
end
|
26
26
|
|
27
27
|
def config
|
28
|
-
@config ||= Hashie::Mash.new { |
|
29
|
-
end
|
30
|
-
|
31
|
-
def find(aggregate_class, aggregate_id)
|
32
|
-
stores[:events].find(aggregate_class, aggregate_id)
|
33
|
-
end
|
34
|
-
|
35
|
-
def locked?(key)
|
36
|
-
stores[:lock].locked?(key)
|
37
|
-
end
|
38
|
-
|
39
|
-
def register_adapter(key, klass)
|
40
|
-
adapters_map[key] = klass
|
28
|
+
@config ||= Hashie::Mash.new { |h, k| h[k] = DEFAULTS[k] }
|
41
29
|
end
|
42
30
|
|
43
|
-
def
|
44
|
-
|
45
|
-
store_config = config.stores[name]
|
46
|
-
adapter = adapters_map[store_config.adapter]
|
47
|
-
stores[name] = adapter.new(store_config)
|
31
|
+
def emit(key , name , data)
|
32
|
+
config.Event.new(key: key, name: name, data: data).track
|
48
33
|
end
|
49
34
|
|
50
|
-
def
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def with_lock(key)
|
55
|
-
fail(LockException, "#{key} has been locked") if Jouba.locked?(key)
|
56
|
-
|
57
|
-
begin
|
58
|
-
stores[:lock].lock!(key)
|
59
|
-
yield
|
60
|
-
ensure
|
61
|
-
stores[:lock].unlock!(key)
|
62
|
-
end
|
35
|
+
def stream(key, params = {})
|
36
|
+
config.Event.stream(key, params)
|
63
37
|
end
|
64
38
|
end
|
data/lib/jouba/aggregate.rb
CHANGED
@@ -1,64 +1,76 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'jouba'
|
2
|
+
require 'jouba/cache'
|
3
|
+
require 'wisper'
|
4
4
|
|
5
|
-
|
5
|
+
module Jouba
|
6
|
+
class Aggregate < Module
|
7
|
+
attr_reader :options
|
6
8
|
|
7
|
-
def
|
8
|
-
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = options
|
11
|
+
tap do |mod|
|
12
|
+
mod.define_singleton_method :included do |object|
|
13
|
+
super(object)
|
14
|
+
after_included(object, mod)
|
15
|
+
end
|
16
|
+
end
|
9
17
|
end
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
|
19
|
+
def after_included(object, mod)
|
20
|
+
object.extend(ClassMethods)
|
21
|
+
object.send :include, InstanceMethods
|
22
|
+
object.send :include, Wisper::Publisher
|
23
|
+
object.define_singleton_method :__module_options__ do
|
24
|
+
mod.options
|
14
25
|
end
|
26
|
+
end
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
28
|
+
module InstanceMethods
|
29
|
+
def emit(name, *args)
|
30
|
+
event = Jouba.Event.new(key: to_key, name: name, data: args)
|
31
|
+
apply_event(event)
|
32
|
+
Jouba.Cache.refresh(to_key, self) { event.track }
|
33
|
+
publish(event.name, event.data)
|
34
|
+
end
|
20
35
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
36
|
+
def replay(event)
|
37
|
+
send __callback_method__(:"#{event.name}"), *event.data
|
25
38
|
end
|
39
|
+
alias_method :apply_event, :replay
|
26
40
|
|
27
|
-
def
|
28
|
-
|
41
|
+
def to_key
|
42
|
+
fail 'Please make sure there is a uuid first' unless respond_to?(:uuid) && !uuid.nil?
|
43
|
+
self.class.key_from_uuid(uuid)
|
29
44
|
end
|
30
45
|
|
31
46
|
private
|
32
47
|
|
33
|
-
def
|
34
|
-
|
48
|
+
def __callback_method__(name)
|
49
|
+
:"#{__callback_prefix__}#{name}"
|
35
50
|
end
|
36
|
-
end
|
37
51
|
|
38
|
-
|
39
|
-
|
52
|
+
def __callback_prefix__
|
53
|
+
options = self.class.__module_options__
|
54
|
+
options[:prefix].nil? ? '' : "#{options[:prefix]}_"
|
55
|
+
end
|
40
56
|
end
|
41
57
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
apply_events(event)
|
46
|
-
Jouba.commit(self, event) do
|
47
|
-
publish(event_name, args)
|
58
|
+
module ClassMethods
|
59
|
+
def replay(events)
|
60
|
+
new.tap { |aggregate| Array(events).each { |event| aggregate.replay(event) } }
|
48
61
|
end
|
49
|
-
end
|
50
62
|
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
def find(uuid)
|
64
|
+
key = key_from_uuid(uuid)
|
65
|
+
Jouba.Cache.fetch(key) { replay stream(uuid) }
|
54
66
|
end
|
55
|
-
end
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
|
68
|
+
def stream(uuid, params = {})
|
69
|
+
Jouba.Event.stream(key_from_uuid(uuid), params)
|
70
|
+
end
|
60
71
|
|
61
|
-
|
72
|
+
def key_from_uuid(uuid)
|
73
|
+
Jouba.Key.serialize(name, uuid) # => default "ClassName.id"
|
62
74
|
end
|
63
75
|
end
|
64
76
|
end
|