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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94a0e68b0a8935c5a7b34943bae5e8fa8c978f3d
4
- data.tar.gz: b09485abe09d0965c75fb8f5dfdc33ac05a23b87
3
+ metadata.gz: 15d2afcf883fa6ef14ca062a882155bc9b86b63d
4
+ data.tar.gz: a3c5d35b6e894ec126a532ee4c66e99c08630c83
5
5
  SHA512:
6
- metadata.gz: 9a6681a756a9d6d08d6087891c6d93a53102656e3d9ca61c393e089702dfd89e7229ecc167371e8a58de067874cd518f33e46ed29cd1b58a849f25b0f1c10c5d
7
- data.tar.gz: 7909d8679c12b9ffaee9778a96b59fa61116d2708501ed2659bff4f3f34e6d6c9b5e1b6f50090ff36c849f936b307b3ea4932d0ce61971a586464bb7335a7459
6
+ metadata.gz: e68608356450234636279deced5b95ed888ea798e2e5e1de6fc8e80d0568e64fad53f01470e4575a15a2c3c811e2ade46cbf24bec5767c6541549a7ffc9a2c6a
7
+ data.tar.gz: e78325ceaf57706cb4ba9f97eead6fa09f17aae35283aea62ea1d4fbd0781748abaf06d0c10136db30d63c8a19198ef44a3b7a87ecb5ca8ea6b912cd8fcc195f
data/Gemfile CHANGED
@@ -1,7 +1,8 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'hashie', '~> 3.3'
4
- gem 'wisper', '~>1.3'
3
+ gem 'hashie', '~> 3.4.0'
4
+ gem 'wisper', '~>1.6.0'
5
+ gem 'locality-uuid'
5
6
 
6
7
  group :development do
7
8
  gem 'pry', '~> 0'
@@ -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.3.2)
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.3.0)
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.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.3)
106
+ wisper (~> 1.6.0)
@@ -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
- 0.1.0
1
+ 1.0.0
@@ -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 0.1.0 ruby lib
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 = "0.1.0"
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-01-02"
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.rdoc"
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.rdoc",
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/exceptions.rb",
36
- "lib/jouba/stores.rb",
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.3"])
53
- s.add_runtime_dependency(%q<wisper>, ["~> 1.3"])
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.3"])
63
- s.add_dependency(%q<wisper>, ["~> 1.3"])
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.3"])
74
- s.add_dependency(%q<wisper>, ["~> 1.3"])
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"])
@@ -1,64 +1,38 @@
1
+ require 'pry'
2
+ require 'forwardable'
1
3
  require 'hashie'
2
- require 'wisper'
4
+ require 'locality-uuid'
3
5
 
4
6
  require 'jouba/version'
5
- require 'jouba/exceptions'
7
+ require 'jouba/key'
6
8
  require 'jouba/event'
7
- require 'jouba/aggregate'
8
- require 'jouba/stores'
9
+ require 'jouba/store'
10
+ require 'jouba/cache'
9
11
 
10
12
  module Jouba
11
13
  module_function
12
14
 
13
- def adapters_map
14
- @adapters_map ||= Hashie::Mash.new do |_, k|
15
- fail("Unknown adapter #{k}, valids are: #{@adapters_map.keys.join(' ')}")
16
- end
17
- end
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
- def commit(aggregate, event)
24
- yield if stores[:events].append_events(aggregate, event)
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 { |_, k| fail("Unknown key #{k}, please use configure to set it up") }
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 register_store(name)
44
- yield(config.stores!.send("#{name}!"))
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 stores
51
- @stores ||= Hashie::Mash.new { |_, k| fail("Unknown store #{k}, valids are: #{@stores.keys.join(' ')}") }
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
@@ -1,64 +1,76 @@
1
- module Jouba
2
- module Aggregate
3
- attr_reader :uuid
1
+ require 'jouba'
2
+ require 'jouba/cache'
3
+ require 'wisper'
4
4
 
5
- include Wisper::Publisher
5
+ module Jouba
6
+ class Aggregate < Module
7
+ attr_reader :options
6
8
 
7
- def self.included(target_class)
8
- target_class.extend ClassMethods
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
- module ClassMethods
12
- def find(id)
13
- Jouba.find(self, id)
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
- def build_from_events(uuid, events = [])
17
- new.tap do |aggregate|
18
- aggregate[:uuid] = uuid
19
- aggregate.apply_events(events)
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
- after_initialize_blocks.each do |block|
22
- block.call(aggregate)
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 after_initialize(&block)
28
- after_initialize_blocks.push(block)
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 after_initialize_blocks
34
- @after_initialize_blocks ||= []
48
+ def __callback_method__(name)
49
+ :"#{__callback_prefix__}#{name}"
35
50
  end
36
- end
37
51
 
38
- def uuid
39
- @uuid ||= SecureRandom.uuid
52
+ def __callback_prefix__
53
+ options = self.class.__module_options__
54
+ options[:prefix].nil? ? '' : "#{options[:prefix]}_"
55
+ end
40
56
  end
41
57
 
42
- def commit(event_name, args)
43
- event = Event.build(event_name, args)
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
- def commit_with_lock(event_name, args, lock_key)
52
- Jouba.with_lock(lock_key) do
53
- commit(event_name, args)
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
- def apply_events(events)
58
- [events].flatten.each do |event|
59
- next unless respond_to?(event.name.to_sym)
68
+ def stream(uuid, params = {})
69
+ Jouba.Event.stream(key_from_uuid(uuid), params)
70
+ end
60
71
 
61
- send(event.name.to_sym, event.data)
72
+ def key_from_uuid(uuid)
73
+ Jouba.Key.serialize(name, uuid) # => default "ClassName.id"
62
74
  end
63
75
  end
64
76
  end