sandthorn_event_filter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +49 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +60 -0
  8. data/Rakefile +13 -0
  9. data/lib/sandthorn_event_filter/event.rb +38 -0
  10. data/lib/sandthorn_event_filter/filter.rb +77 -0
  11. data/lib/sandthorn_event_filter/matchers/aggregate_type_matcher.rb +15 -0
  12. data/lib/sandthorn_event_filter/matchers/attribute_changed_matcher.rb +17 -0
  13. data/lib/sandthorn_event_filter/matchers/block_matcher.rb +15 -0
  14. data/lib/sandthorn_event_filter/matchers/event_name_matcher.rb +15 -0
  15. data/lib/sandthorn_event_filter/matchers/extract.rb +51 -0
  16. data/lib/sandthorn_event_filter/matchers/identity_matcher.rb +9 -0
  17. data/lib/sandthorn_event_filter/matchers/matcher.rb +20 -0
  18. data/lib/sandthorn_event_filter/matchers/matcher_collection.rb +36 -0
  19. data/lib/sandthorn_event_filter/matchers/not_matcher.rb +10 -0
  20. data/lib/sandthorn_event_filter/matchers/sequence_number_matcher.rb +16 -0
  21. data/lib/sandthorn_event_filter/version.rb +3 -0
  22. data/lib/sandthorn_event_filter.rb +7 -0
  23. data/sandthorn_event_filter.gemspec +32 -0
  24. data/spec/data/events.rb +28 -0
  25. data/spec/event_spec.rb +38 -0
  26. data/spec/filter_spec.rb +151 -0
  27. data/spec/matchers/attribute_changed_matcher_spec.rb +48 -0
  28. data/spec/matchers/block_matcher_spec.rb +26 -0
  29. data/spec/matchers/class_matcher_spec.rb +37 -0
  30. data/spec/matchers/event_name_matcher_spec.rb +36 -0
  31. data/spec/matchers/extract_spec.rb +60 -0
  32. data/spec/matchers/matcher_collection_spec.rb +47 -0
  33. data/spec/matchers/sequence_number_matcher_spec.rb +31 -0
  34. data/spec/spec_helper.rb +17 -0
  35. metadata +200 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1078458dda85cc9f4050925f006b34f2e1ee39b8
4
+ data.tar.gz: dacd493ed0495b299a8ee86a0c64caf9801a5592
5
+ SHA512:
6
+ metadata.gz: 8ea5d12b472ae9e4f81d6f921b4839e3f2392bfc5c458529f3b38fed03294ce06dd01cd3a83415217a5ad21a020c2141efc1f593fcfd81c788615ab9c7d13d2f
7
+ data.tar.gz: 72cd5e9767b0a2c50be2dcdd12619928470209b38c7fc33cd60038215c960d72ef127e3573d127d9e7e6fd002964176d1f6774012ab0b143b35fef94cdaec2d6
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+ /.idea/
12
+
13
+ ## Documentation cache and generated files:
14
+ /.yardoc/
15
+ /_yardoc/
16
+ /doc/
17
+ /rdoc/
18
+
19
+ ## Environment normalisation:
20
+ /.bundle/
21
+ /lib/bundler/man/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.1.1
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ # Specify your gem's dependencies in event_filter.gemspec
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sandthorn_event_filter (0.0.1)
5
+ hamster
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ autotest-standalone (4.5.11)
11
+ coderay (1.1.0)
12
+ diff-lcs (1.2.5)
13
+ dotenv (0.11.1)
14
+ dotenv-deployment (~> 0.0.2)
15
+ dotenv-deployment (0.0.2)
16
+ gem-release (0.7.3)
17
+ hamster (0.4.3)
18
+ method_source (0.8.2)
19
+ pry (0.10.0)
20
+ coderay (~> 1.1.0)
21
+ method_source (~> 0.8.1)
22
+ slop (~> 3.4)
23
+ rake (10.3.2)
24
+ rspec (3.0.0)
25
+ rspec-core (~> 3.0.0)
26
+ rspec-expectations (~> 3.0.0)
27
+ rspec-mocks (~> 3.0.0)
28
+ rspec-core (3.0.3)
29
+ rspec-support (~> 3.0.0)
30
+ rspec-expectations (3.0.3)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.0.0)
33
+ rspec-mocks (3.0.3)
34
+ rspec-support (~> 3.0.0)
35
+ rspec-support (3.0.3)
36
+ slop (3.6.0)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ autotest-standalone
43
+ bundler (~> 1.6)
44
+ dotenv
45
+ gem-release
46
+ pry
47
+ rake
48
+ rspec
49
+ sandthorn_event_filter!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jesper Josefsson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ [![Build Status](https://travis-ci.org/Sandthorn/sandthorn_event_filter.svg?branch=develop)](https://travis-ci.org/Sandthorn/sandthorn_event_filter)
2
+
3
+ # SandthornEventFilter
4
+
5
+ SandthornEventFilter is a library for creating composable event filters.
6
+ They are composable in the following senses:
7
+
8
+ - chained filters get boiled down to a single expression, meaning we can filter in just one pass
9
+ - filters are immutable and can be extended with further chaining
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'event_filter'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install event_filter
24
+
25
+ ## Usage
26
+
27
+ The basic way to use a Filter is to wrap an array of Events:
28
+
29
+ filter = SandthornEventFilter::Filter.new(my_events)
30
+
31
+ You can always call `.events` to get the filtered events:
32
+
33
+ # This filter is empty, and will return the original events
34
+ filter.events
35
+ # => [...]
36
+
37
+ You can also enumerate on the filter directly:
38
+
39
+ filter.each { |event| puts event.inspect }
40
+
41
+ `extract` returns a new filter that extracts the chosen events:
42
+
43
+ new_events = filter.extract(events: "new", types: MyAggregate, after_sequence_number: 100)
44
+ new_events.events
45
+ # => events that match the criteria
46
+
47
+ `remove` returns a new filter with removes the chosen events:
48
+
49
+ except_new = filter.remove(events: "new")
50
+ except_new.events
51
+ # => events except those with event name "new"
52
+
53
+ `select` and `reject` behave as you would expect, except they return a new filter instance.
54
+
55
+ You can also create a filter chain without supplying initial events, and apply that chain to any
56
+ array of events.
57
+
58
+ filter = Filter.new.reject(events: "new")
59
+ filter.apply(my_array_of_events)
60
+ # => [ ... array ... ]
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require 'sandthorn_event_filter'
3
+ require 'dotenv/tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
9
+
10
+ desc "Open an irb session preloaded with this library"
11
+ task :console do
12
+ sh "irb -rubygems -I lib -r sandthorn_event_filter.rb"
13
+ end
@@ -0,0 +1,38 @@
1
+ require 'delegate'
2
+ module SandthornEventFilter
3
+ class Event < SimpleDelegator
4
+ # Wraps raw event data and encapsulates knowledge about it
5
+ # Makes sure the event is in canonical form
6
+ # Delegates to the raw data hash, so you can still do Event.new(data)[:foo]
7
+
8
+ def attribute_changed?(attribute)
9
+ input_attributes = Array(attribute)
10
+ changed_attributes.any? do |attr|
11
+ input_attributes.include?(attr)
12
+ end
13
+ end
14
+
15
+ def changed_attributes
16
+ attribute_deltas.map do |delta|
17
+ delta[:attribute_name]
18
+ end
19
+ end
20
+
21
+ def attribute_deltas
22
+ self.fetch(:event_args, {}).fetch(:attribute_deltas, [])
23
+ end
24
+
25
+ def aggregate_type
26
+ self[:aggregate_type]
27
+ end
28
+
29
+ def name
30
+ self[:event_name]
31
+ end
32
+
33
+ class << self
34
+ alias_method :wrap, :new
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,77 @@
1
+ require 'sandthorn_event_filter/matchers/matcher_collection'
2
+ require 'sandthorn_event_filter/matchers/matcher'
3
+ require 'sandthorn_event_filter/matchers/extract'
4
+ require 'sandthorn_event_filter/matchers/attribute_changed_matcher'
5
+ require 'sandthorn_event_filter/matchers/not_matcher'
6
+ require 'sandthorn_event_filter/matchers/identity_matcher'
7
+ require 'sandthorn_event_filter/matchers/aggregate_type_matcher'
8
+ require 'sandthorn_event_filter/matchers/event_name_matcher'
9
+ require 'sandthorn_event_filter/matchers/block_matcher'
10
+ require 'sandthorn_event_filter/matchers/sequence_number_matcher'
11
+
12
+ module SandthornEventFilter
13
+ class Filter
14
+ # = Filter
15
+ # A composable event filter.
16
+ # Filters can be chained at will, but the actual filtering won't be done
17
+ # until a method is called that needs actual data.
18
+ #
19
+ # Filter methods (+remove+, +extract+, +select+, +reject+) return new instances of Filter, and do not affect
20
+ # the original filter.
21
+ #
22
+ # NOTE:
23
+ # This class expects well-formed events as input, and does nothing to transform events.
24
+ # TODO: throw error for malformed events? Or at least emit warning?
25
+ extend Forwardable
26
+ include Enumerable
27
+
28
+ def_delegators :events, :each, :last, :length, :empty?, :[]
29
+
30
+ attr_reader :matchers, :original_events
31
+
32
+ def initialize(events = [], matchers = Matchers::MatcherCollection.new)
33
+ @matchers = matchers
34
+ @original_events = events
35
+ end
36
+
37
+ def events
38
+ if @matchers.any?
39
+ @filtered_events ||= apply(original_events)
40
+ else
41
+ @original_events
42
+ end
43
+ end
44
+
45
+ def extract(*args)
46
+ matcher = Matchers::Extract.new(*args)
47
+ add_matcher(matcher)
48
+ end
49
+
50
+ # A remove matcher is a negated extract matcher
51
+ def remove(*args)
52
+ matcher = Matchers::NotMatcher.new(Matchers::Extract.new(*args))
53
+ add_matcher(matcher)
54
+ end
55
+
56
+ def select(&block)
57
+ matcher = Matchers::BlockMatcher.new(&block)
58
+ add_matcher(matcher)
59
+ end
60
+
61
+ def reject(&block)
62
+ matcher = Matchers::NotMatcher.new(Matchers::BlockMatcher.new(&block))
63
+ add_matcher(matcher)
64
+ end
65
+
66
+ def add_matcher(matcher)
67
+ # This works because .add returns a new MatcherCollection
68
+ new_matchers = @matchers.add(matcher)
69
+ self.class.new(original_events, new_matchers)
70
+ end
71
+
72
+ def apply(events)
73
+ @matchers.apply(events)
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,15 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class AggregateTypeMatcher
4
+
5
+ def initialize(types)
6
+ @types = Array(types)
7
+ end
8
+
9
+ def match?(event)
10
+ @types.any? { |type| event[:aggregate_type].to_s == type.to_s }
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class AttributeChangedMatcher < Matcher
4
+
5
+ attr_reader :attributes
6
+
7
+ def initialize(attributes)
8
+ @attributes = Array(attributes)
9
+ end
10
+
11
+ def match?(event)
12
+ SandthornEventFilter::Event.wrap(event).attribute_changed?(attributes)
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class BlockMatcher < Matcher
4
+
5
+ def initialize(&block)
6
+ @block = block
7
+ end
8
+
9
+ def match?(event)
10
+ @block.call(event)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class EventNameMatcher < Matcher
4
+
5
+ def initialize(types)
6
+ @types = Array(types)
7
+ end
8
+
9
+ def match?(event)
10
+ @types.any? { |type| event[:event_name].to_s == type.to_s }
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class Extract < Matcher
4
+ # = Extract
5
+ # Extract is basically an AND matcher.
6
+ # Given some matching criteria, it will construct submatchers,
7
+ # for example creating a ClassMatcher if provided with classes.
8
+ # Events match an Extract matcher if all of its submatchers match the event.
9
+ def match?(event)
10
+ submatchers_match?(event)
11
+ end
12
+
13
+ private
14
+
15
+ def submatchers_match?(event)
16
+ submatchers.all? { |submatcher| submatcher.match?(event) }
17
+ end
18
+
19
+ def submatchers
20
+ @submatchers ||= construct_submatchers
21
+ end
22
+
23
+ def construct_submatchers
24
+ matchers = []
25
+ add_class_matcher(matchers, options)
26
+ add_event_name_matcher(matchers, options)
27
+ add_changed_attributes_matcher(matchers, options)
28
+ matchers
29
+ end
30
+
31
+ def add_class_matcher(matchers, options)
32
+ if types = options[:types]
33
+ matchers << AggregateTypeMatcher.new(types)
34
+ end
35
+ end
36
+
37
+ def add_event_name_matcher(matchers, options)
38
+ if names = options[:events]
39
+ matchers << EventNameMatcher.new(names)
40
+ end
41
+ end
42
+
43
+ def add_changed_attributes_matcher(matchers, options)
44
+ if attributes = options[:changed_attributes]
45
+ matchers << AttributeChangedMatcher.new(attributes)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class IdentityMatcher < Matcher
4
+ def match?(event)
5
+ true
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class Matcher
4
+ # = Matcher
5
+ # Abstract class.
6
+ # Matches an event. Must implement a +match?+ method.
7
+ # Used to filter collections of events.
8
+ attr_reader :options
9
+
10
+ def initialize(options = {})
11
+ @options = options
12
+ end
13
+
14
+ def match?(event)
15
+ raise NotImplementedError, "subclasses should override"
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ require 'forwardable'
2
+ require 'hamster/list'
3
+
4
+ module SandthornEventFilter
5
+ module Matchers
6
+ class MatcherCollection
7
+ # = MatcherCollection
8
+ # A collection of Matchers which can be applied to a collection of events.
9
+ extend Forwardable
10
+ attr_reader :matchers
11
+ def_delegators :@matchers, :any?
12
+
13
+ def initialize(matchers = Hamster.list)
14
+ @matchers = matchers
15
+ end
16
+
17
+ # Returns all events that match all of the matchers in the collection
18
+ def apply(events)
19
+ events.select { |event| matchers.all? { |matcher| matcher.match?(event) } }
20
+ end
21
+
22
+ # Returns a new instance of MatcherCollection, with the new matchers added.
23
+ # Since @matchers is a persistent, immutable Hamster::List,
24
+ # adding all new matchers and sending the resulting list to the
25
+ # new instance is safe.
26
+ def add(input_matchers)
27
+ input_matchers = Array(input_matchers)
28
+ matchers = input_matchers.reduce(@matchers) do |matcher_collection, matcher|
29
+ matcher_collection.cons(matcher)
30
+ end
31
+ self.class.new(matchers)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ require 'delegate'
2
+ module SandthornEventFilter
3
+ module Matchers
4
+ class NotMatcher < SimpleDelegator
5
+ def match?(event)
6
+ !super
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module SandthornEventFilter
2
+ module Matchers
3
+ class SequenceNumberMatcher < Matcher
4
+
5
+ attr_reader :sequence_number
6
+
7
+ def initialize(sequence_number)
8
+ @sequence_number = sequence_number
9
+ end
10
+
11
+ def match?(event)
12
+ event[:sequence_number] > sequence_number
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module SandthornEventFilter
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "sandthorn_event_filter/version"
2
+ require "sandthorn_event_filter/event"
3
+ require "sandthorn_event_filter/filter"
4
+
5
+ module SandthornEventFilter
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sandthorn_event_filter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sandthorn_event_filter"
8
+ spec.version = SandthornEventFilter::VERSION
9
+ spec.authors = ["Jesper Josefsson"]
10
+ spec.email = ["jesper.josefsson@gmail.com"]
11
+ spec.summary = %q{Composable filters for Sandthorn Events}
12
+ spec.homepage = "https://github.com/Sandthorn"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ # added by upptec_create_gem
21
+ spec.add_development_dependency "pry"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "gem-release"
24
+ spec.add_development_dependency "autotest-standalone"
25
+ spec.add_development_dependency "dotenv"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.6"
28
+ spec.add_development_dependency "rake"
29
+
30
+ spec.add_runtime_dependency "hamster"
31
+
32
+ end
@@ -0,0 +1,28 @@
1
+ require 'time'
2
+
3
+ module SandthornEventFilter
4
+ module Fixtures
5
+ EVENTS =
6
+ [{:aggregate_type=>"SandthornProduct", :aggregate_version=>1, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>1, :event_name=>"new", :timestamp=>Time.parse('2014-04-08 12:22:09 UTC'), :event_args=>{:method_name=>"new", :method_args=>[{"name"=>"Morgan", "price"=>"666", "stock_status"=>"instock"}], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>nil, :new_value=>"Morgan"}, {:attribute_name=>"price", :old_value=>nil, :new_value=>"666"}, {:attribute_name=>"stock_status", :old_value=>nil, :new_value=>"instock"}, {:attribute_name=>"active", :old_value=>nil, :new_value=>true}, {:attribute_name=>"aggregate_id", :old_value=>nil, :new_value=>"da932773-dd06-43dc-8c0c-61837f009226"}]}},
7
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>2, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>2, :event_name=>"name_updated", :timestamp=>Time.parse('2014-04-08 12:22:17 UTC'), :event_args=>{:method_name=>"name_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan", :new_value=>"Morgan Hallgren"}]}},
8
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>3, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>3, :event_name=>"price_updated", :timestamp=>Time.parse('2014-04-08 12:22:19 UTC'), :event_args=>{:method_name=>"price_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"price", :old_value=>"666", :new_value=>"999"}]}},
9
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>4, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>4, :event_name=>"product_discontinued", :timestamp=>Time.parse('2014-04-08 12:22:20 UTC'), :event_args=>{:method_name=>"product_discontinued", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"stock_status", :old_value=>"instock", :new_value=>"discontinued"}]}},
10
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>5, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>5, :event_name=>"price_updated", :timestamp=>Time.parse('2014-04-08 13:35:29 UTC'), :event_args=>{:method_name=>"price_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"price", :old_value=>"999", :new_value=>"1000"}]}},
11
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>6, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>6, :event_name=>"price_updated", :timestamp=>Time.parse('2014-04-08 13:40:28 UTC'), :event_args=>{:method_name=>"price_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"price", :old_value=>"1000", :new_value=>"1001"}]}},
12
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>7, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>7, :event_name=>"product_on_sale", :timestamp=>Time.parse('2014-04-09 09:14:45 UTC'), :event_args=>{:method_name=>"product_on_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan Hallgren", :new_value=>"ON SALE!!!! Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>"1001", :new_value=>0}, {:attribute_name=>"on_sale", :old_value=>false, :new_value=>true}]}},
13
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>8, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>8, :event_name=>"removed_from_sale", :timestamp=>Time.parse('2014-04-09 09:15:03 UTC'), :event_args=>{:method_name=>"removed_from_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"ON SALE!!!! Morgan Hallgren", :new_value=>"Morgan Hallgren"}, {:attribute_name=>"on_sale", :old_value=>true, :new_value=>false}]}},
14
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>9, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>9, :event_name=>"price_updated", :timestamp=>Time.parse('2014-04-09 09:15:06 UTC'), :event_args=>{:method_name=>"price_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"price", :old_value=>0, :new_value=>100}]}},
15
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>10, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>10, :event_name=>"product_on_sale", :timestamp=>Time.parse('2014-04-09 09:15:11 UTC'), :event_args=>{:method_name=>"product_on_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan Hallgren", :new_value=>"ON SALE!!!! Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>100, :new_value=>80}, {:attribute_name=>"on_sale", :old_value=>false, :new_value=>true}]}},
16
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>11, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>11, :event_name=>"product_out_of_stock", :timestamp=>Time.parse('2014-04-09 19:45:38 UTC'), :event_args=>{:method_name=>"product_out_of_stock", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"stock_status", :old_value=>"discontinued", :new_value=>"outofstock"}]}},
17
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>12, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>12, :event_name=>"removed_from_sale", :timestamp=>Time.parse('2014-04-09 19:45:53 UTC'), :event_args=>{:method_name=>"removed_from_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"ON SALE!!!! Morgan Hallgren", :new_value=>"Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>80, :new_value=>100}, {:attribute_name=>"on_sale", :old_value=>true, :new_value=>false}]}},
18
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>13, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>13, :event_name=>"price_updated", :timestamp=>Time.parse('2014-04-10 14:03:28 UTC'), :event_args=>{:method_name=>"price_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"price", :old_value=>100, :new_value=>111}]}},
19
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>14, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>14, :event_name=>"stock_status_updated", :timestamp=>Time.parse('2014-04-10 14:03:28 UTC'), :event_args=>{:method_name=>"stock_status_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"stock_status", :old_value=>"outofstock", :new_value=>nil}]}},
20
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>15, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>15, :event_name=>"stock_status_updated", :timestamp=>Time.parse('2014-04-10 21:32:30 UTC'), :event_args=>{:method_name=>"stock_status_updated", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"stock_status", :old_value=>nil, :new_value=>"discontinued"}]}},
21
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>16, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>16, :event_name=>"product_on_sale", :timestamp=>Time.parse('2014-04-10 21:32:55 UTC'), :event_args=>{:method_name=>"product_on_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan Hallgren", :new_value=>"ON SALE!!!! Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>111, :new_value=>88}, {:attribute_name=>"on_sale", :old_value=>false, :new_value=>true}]}},
22
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>17, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>17, :event_name=>"removed_from_sale", :timestamp=>Time.parse('2014-04-10 21:32:56 UTC'), :event_args=>{:method_name=>"removed_from_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"ON SALE!!!! Morgan Hallgren", :new_value=>"Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>88, :new_value=>110}, {:attribute_name=>"on_sale", :old_value=>true, :new_value=>false}]}},
23
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>18, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>18, :event_name=>"product_on_sale", :timestamp=>Time.parse('2014-04-10 21:32:57 UTC'), :event_args=>{:method_name=>"product_on_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan Hallgren", :new_value=>"ON SALE!!!! Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>110, :new_value=>88}, {:attribute_name=>"on_sale", :old_value=>false, :new_value=>true}]}},
24
+ {:aggregate_type=>"SandthornProduct", :aggregate_version=>19, :aggregate_id=>"da932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>19, :event_name=>"removed_from_sale", :timestamp=>Time.parse('2014-04-10 21:32:58 UTC'), :event_args=>{:method_name=>"removed_from_sale", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"name", :old_value=>"ON SALE!!!! Morgan Hallgren", :new_value=>"Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>88, :new_value=>110}, {:attribute_name=>"on_sale", :old_value=>true, :new_value=>false}]}},
25
+ {:aggregate_type=>"SandthornTest", :aggregate_version=>1, :aggregate_id=>"xa932773-dd06-43dc-8c0c-61837f009226", :sequence_number=>20, :event_name=>"new", :timestamp=>Time.parse('2014-04-10 21:32:59 UTC'), :event_args=>{:attribute_deltas=>[{:attribute_name=>"name", :old_value=>"Morgan Hallgren", :new_value=>"ON SALE!!!! Morgan Hallgren"}, {:attribute_name=>"price", :old_value=>110, :new_value=>88}, {:attribute_name=>"on_sale", :old_value=>false, :new_value=>true}]}}
26
+ ]
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module SandthornEventFilter
4
+ describe Event do
5
+ let(:event) { Event.wrap(Fixtures::EVENTS.first) }
6
+ let(:deltas) do [
7
+ {:attribute_name => "name", :old_value => nil, :new_value => "Morgan"},
8
+ {:attribute_name => "price", :old_value => nil, :new_value => "666"},
9
+ {:attribute_name => "stock_status", :old_value => nil, :new_value => "instock"},
10
+ {:attribute_name => "active", :old_value => nil, :new_value => true},
11
+ {:attribute_name => "aggregate_id", :old_value => nil, :new_value => "da932773-dd06-43dc-8c0c-61837f009226"}]
12
+ end
13
+
14
+ describe "#attribute_changed?" do
15
+ context "when the attribute is included in the attribute deltas" do
16
+ it "should return true" do
17
+ deltas.map { |delta| delta[:attribute_name] }.each do |attribute|
18
+ expect(event.attribute_changed?(attribute)).to be_truthy
19
+ end
20
+ end
21
+ end
22
+
23
+ context "when the attribute is bogus" do
24
+ it "should return false" do
25
+ expect(event.attribute_changed?("foo")).to be_falsey
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#attribute_deltas" do
31
+ it "should return the deltas" do
32
+ expect(event.attribute_deltas).to eq(deltas)
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end