replay 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .idea/*
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@replay
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in replay.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem "ruby-debug19", :platforms => :ruby_19
8
+ gem "minitest"
9
+ gem "mocha"
10
+ end
data/README ADDED
@@ -0,0 +1,27 @@
1
+ class Domain::Model
2
+ include Replay::Domain
3
+
4
+ def twitter_update
5
+ #do stuff
6
+ signal_event "twitter_updated", status
7
+ self.save
8
+ end
9
+
10
+ apply "twitter_updated" do |status|
11
+ model.twitter_status = status
12
+ end
13
+
14
+ end
15
+
16
+
17
+ #usage
18
+ model = Model.find(params[:model_id])
19
+ model.twitter_update
20
+
21
+ #listener
22
+ class TweetReport
23
+ include Replay::Projector
24
+ listen :twitter_updated do |model_id, status|
25
+ #update read model/report/whatever...
26
+ end
27
+ end
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ require "replay/version"
2
+ require 'active_support/concern'
3
+ require 'replay/unknown_event_error'
4
+ require 'replay/test_storage'
5
+ require 'replay/configuration'
6
+ require 'replay/event'
7
+ require 'replay/event_store'
8
+ require 'replay/active_record_event_store'
9
+ require 'replay/domain'
10
+ require 'replay/projector'
11
+
12
+ module Replay
13
+
14
+ end
@@ -0,0 +1,32 @@
1
+ if defined?(ActiveRecord)
2
+ module Replay
3
+ class ActiveRecordEvent < ::ActiveRecord::Base
4
+ set_table_name "replay_events"
5
+ serialize :arguments
6
+ end
7
+
8
+ #needs model id
9
+ class ActiveRecordEventLogEntry < ::ActiveRecord::Base
10
+ set_table_name "replay_event_log_entries"
11
+ serialize :data
12
+ end
13
+
14
+ class ActiveRecordEventStore
15
+ def store(event, model_id, *args)
16
+ ar_event = Replay::ActiveRecordEvent.new
17
+ ar_event.event = event
18
+ ar_event.model_id = model_id
19
+ ar_event.arguments = args
20
+ ar_event.save
21
+ end
22
+
23
+ def log_event(event, model_id, *args)
24
+ log_entry = Replay::ActiveRecordEventLogEntry.new
25
+ log_entry.event = event
26
+ log_entry.model_id = model_id
27
+ log_entry.data = args
28
+ log_entry.save
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ module Replay
2
+ class Configuration
3
+ attr_accessor :storage
4
+
5
+ def storage=(stores)
6
+ stores = [stores] unless stores.is_a?(Array)
7
+ @storage = stores
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module Replay::Domain
2
+ extend ActiveSupport::Concern
3
+ include Replay
4
+
5
+ included do
6
+ self.extend ClassMethods
7
+ self.event_blocks = {}
8
+ end
9
+
10
+ def signal_event(event, *args)
11
+ EventStore.handle_event(event, self.id, *args)
12
+ apply_event(event, *args)
13
+ end
14
+
15
+ def apply_event(event, *args)
16
+ event_block = event_blocks[event.to_sym]
17
+ raise UnknownEventError.new("#{event} is not a known event on class #{self.class.name}") unless event_block
18
+ EventStore.log_event(event, self.id, *args)
19
+ self.instance_exec(*args, &event_block)
20
+ end
21
+
22
+ def event_blocks
23
+ self.class.event_blocks
24
+ end
25
+
26
+ module ClassMethods
27
+ def apply(event, &block)
28
+ event_blocks[event.to_sym] = block
29
+ end
30
+
31
+ attr_accessor :event_blocks
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ module Replay
2
+ class Event
3
+ attr_accessor :event_type, :attributes
4
+
5
+ def initialize(event_type, *data)
6
+ @event_type = event_type
7
+ if data.first.kind_of? Hash
8
+ @attributes = HashWithIndifferentAccess.new data.first.dup
9
+ else
10
+ @attributes = HashWithIndifferentAccess.new({data.first => data.last})
11
+ end
12
+ end
13
+
14
+ def method_missing(method, *args)
15
+ method_root = method.to_s.gsub(/=$/, "")
16
+ if @attributes && @attributes.has_key?(method_root)
17
+ if method.to_s[/=$/]
18
+ @attributes[method_root] = args.first
19
+ else
20
+ @attributes[method]
21
+ end
22
+ else
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ module Replay
2
+ class EventStore
3
+ include Replay
4
+
5
+ class << self
6
+ attr_accessor :configuration
7
+ attr_accessor :listeners
8
+ attr_accessor :test_mode
9
+ attr_accessor :event_stream
10
+ end
11
+ self.listeners = {}
12
+ self.configuration = Replay::Configuration.new
13
+ self.event_stream = []
14
+
15
+ def self.configure(&block)
16
+ block.call(self.configuration)
17
+ end
18
+
19
+ def self.storage
20
+ configuration.storage
21
+ end
22
+
23
+ def self.add_listener(event, new_listener)
24
+ self.listeners[event] = [] unless self.listeners[event]
25
+ self.listeners[event] << new_listener
26
+ end
27
+
28
+ def self.clear_listeners
29
+ listeners = {}
30
+ end
31
+
32
+ def self.log_event(event, model_id, *args)
33
+ if configuration.storage
34
+ configuration.storage.each do |event_store_adapter|
35
+ event_store_adapter.log_event(event, model_id, *args)
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.handle_event(event, model_id, *args)
41
+ self.event_stream << Event.new(event, args.last) if self.test_mode
42
+
43
+ if configuration.storage
44
+ configuration.storage.each do |event_store_adapter|
45
+ event_store_adapter.store(event, model_id, *args)
46
+ end
47
+ end
48
+ if self.listeners[event]
49
+ self.listeners[event].each do |listener|
50
+ listener.handle_event(event, model_id, *args)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ module Replay::Projector
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ self.extend(ClassMethods)
6
+ self.listening_blocks = {}
7
+ end
8
+
9
+ module ClassMethods
10
+ def listen(event, &block)
11
+ listening_blocks[event] = [] unless listening_blocks[event]
12
+ listening_blocks[event] << block
13
+ Replay::EventStore.add_listener(event, self)
14
+ end
15
+
16
+ private
17
+ attr_accessor :listening_blocks
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Replay
2
+ class TestStorage
3
+ # To change this template use File | Settings | File Templates.
4
+ def store(event, id, *args)
5
+ true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ class UnknownEventError < RuntimeError
2
+ end
@@ -0,0 +1,3 @@
1
+ module Replay
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "replay/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "replay"
7
+ s.version = Replay::VERSION
8
+ s.authors = ["karmajunkie"]
9
+ s.email = ["keith.gaddis@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Replay supports event-sourced data models.}
12
+ s.description = %q{Replay supports event-sourced data models.}
13
+
14
+ s.rubyforge_project = "replay"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "activerecord"
23
+ s.add_development_dependency "sqlite3-ruby"
24
+ # s.add_runtime_dependency "rest-client"
25
+ s.add_runtime_dependency "activesupport"
26
+ end
@@ -0,0 +1,6 @@
1
+ $:<<"lib"
2
+ require 'active_record'
3
+ require 'minitest/autorun'
4
+ require 'replay'
5
+ require 'ruby-debug'
6
+ require 'mocha'
Binary file
@@ -0,0 +1,24 @@
1
+ require './test/spec_helper'
2
+
3
+ ::ActiveRecord::Base.establish_connection(
4
+ :adapter => "sqlite3",
5
+ :database => "test/test_events.sqlite3"
6
+ )
7
+ ::ActiveRecord::Base.connection.execute("create table if not exists replay_events(id primary key, event, model_id, arguments, created_at, updated_at)")
8
+
9
+ describe Replay::ActiveRecordEventStore do
10
+ before do
11
+ @store = Replay::ActiveRecordEventStore.new
12
+ end
13
+ def action
14
+ @store.store(:foo_event, 123, 'bar')
15
+ end
16
+ describe "#store" do
17
+ it "should create an event in the database" do
18
+ count = Replay::ActiveRecordEvent.count
19
+ action
20
+ (Replay::ActiveRecordEvent.count - count).must_equal 1
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ require './test/spec_helper'
2
+
3
+ describe Replay do
4
+ class ReplayTestClass
5
+ include Replay::Domain
6
+
7
+ attr_accessor :bar_value
8
+
9
+ def id
10
+ 1
11
+ end
12
+
13
+ def foo
14
+ signal_event :foo_happened, "bar"
15
+ end
16
+
17
+ def bad_foo
18
+ signal_event :no_foo_lovey
19
+ end
20
+
21
+ apply :foo_happened do |what|
22
+ self.bar_value = what
23
+ end
24
+ end
25
+
26
+ before do
27
+ @something = ReplayTestClass.new
28
+ end
29
+ describe "behavior with signalling" do
30
+ it "raises an error for unknown events" do
31
+ lambda{@something.bad_foo}.must_raise UnknownEventError
32
+ end
33
+
34
+ it "sends the event to the EventStore" do
35
+ Replay::EventStore.expects(:handle_event).with(:foo_happened, 1, 'bar')
36
+ @something.foo
37
+ end
38
+ end
39
+
40
+ describe "rebuilding" do
41
+
42
+ end
43
+
44
+ describe "applying an event" do
45
+ it "changes state" do
46
+ @something.foo
47
+ @something.bar_value.must_equal "bar"
48
+ end
49
+ end
50
+
51
+
52
+ end
53
+
@@ -0,0 +1,13 @@
1
+ require './test/spec_helper'
2
+
3
+ describe Replay::Event do
4
+ before do
5
+ @event = Replay::Event.new("shit_happened", :foo => :bar)
6
+ end
7
+
8
+ describe "method_missing" do
9
+ it "fakes the reader" do
10
+ @event.foo.must_equal :bar
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ require './test/spec_helper'
2
+
3
+ describe Replay::EventStore do
4
+ before do
5
+ Replay::EventStore.configure do |config|
6
+ config.storage = Replay::TestStorage.new
7
+ end
8
+ end
9
+ after do
10
+ Replay::EventStore.clear_listeners
11
+ end
12
+ describe "#handle_event" do
13
+ def action
14
+ Replay::EventStore.handle_event(:foo_event, 123, {:bar => "fly"})
15
+ end
16
+ it "stores the event" do
17
+ Replay::TestStorage.any_instance.expects(:store).with(:foo_event, 123, {:bar => "fly"} )
18
+ action
19
+ end
20
+ it "notifies listeners" do
21
+ listener = mock("listener") do
22
+ expects(:handle_event).with(:foo_event, 123, {:bar => 'fly'})
23
+ end
24
+ Replay::EventStore.add_listener(:foo_event, listener)
25
+ action
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ require './test/spec_helper'
2
+
3
+ describe Replay::Projector do
4
+ after do
5
+ Replay::EventStore.clear_listeners
6
+ end
7
+
8
+ describe "#listen" do
9
+ it "adds the class as a listener to the EventStore" do
10
+ class ProjectorExample
11
+ Replay::EventStore.expects(:add_listener).with(:foo_happened, self)
12
+
13
+ include Replay::Projector
14
+ listen :foo_happened do |model_id, arg|
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: replay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - karmajunkie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sqlite3-ruby
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Replay supports event-sourced data models.
63
+ email:
64
+ - keith.gaddis@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rvmrc
71
+ - Gemfile
72
+ - README
73
+ - Rakefile
74
+ - lib/replay.rb
75
+ - lib/replay/active_record_event_store.rb
76
+ - lib/replay/configuration.rb
77
+ - lib/replay/domain.rb
78
+ - lib/replay/event.rb
79
+ - lib/replay/event_store.rb
80
+ - lib/replay/projector.rb
81
+ - lib/replay/test_storage.rb
82
+ - lib/replay/unknown_event_error.rb
83
+ - lib/replay/version.rb
84
+ - replay.gemspec
85
+ - test/spec_helper.rb
86
+ - test/test_events.sqlite3
87
+ - test/unit/active_record_event_store_spec.rb
88
+ - test/unit/domain_spec.rb
89
+ - test/unit/event_spec.rb
90
+ - test/unit/event_store_spec.rb
91
+ - test/unit/projector_spec.rb
92
+ homepage: ''
93
+ licenses: []
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project: replay
112
+ rubygems_version: 1.8.24
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Replay supports event-sourced data models.
116
+ test_files:
117
+ - test/spec_helper.rb
118
+ - test/test_events.sqlite3
119
+ - test/unit/active_record_event_store_spec.rb
120
+ - test/unit/domain_spec.rb
121
+ - test/unit/event_spec.rb
122
+ - test/unit/event_store_spec.rb
123
+ - test/unit/projector_spec.rb