sandthorn_event_filter 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.
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