eventsimple 1.2.0 → 1.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c32b996ad44adbf1ae144adaa855a70d23c68286ff64e3bc633f8af98517c1f6
4
- data.tar.gz: 8de726b86bb3fcd0b30c1f580168abfa069f7a9910fc72ce1a5f943da624fb6c
3
+ metadata.gz: 73a860de275fa7998ed8048ee53ac70ec2b7b5872456bd1c03422cb4218847cb
4
+ data.tar.gz: 0aee5f6d22f2630d1a513f3847441fb8e1f28399512f9046be161c65c4cb49f2
5
5
  SHA512:
6
- metadata.gz: d85d69406ceb52536ee95b74449fcca0fcb592b58dd8497288c60072945550df3824e5c2ef9e4ffd2b7ae7d32cbd00a023c6a5d7314b5fd55ca6132b5dde9904
7
- data.tar.gz: 8ccffafa78b30f253e1e47e90f747bfa3ddd240aba2269dd7952e48016fa58750f976b13afe7624318c2146e9447fe32c85516daca371d4b5d127a92dd896890
6
+ metadata.gz: a92575ecbabbbf572beb4c61c7f46fff241e339b737d224ee93bf78b24df6dbedeab28a8fa7721d3a213b833e5f799b0a31e76234b41f7c903ea208ff04db2f9
7
+ data.tar.gz: 358feb4234563044b7609e46130913c161cdd2fa555cbfba7be4faea1e5e3bf3e26b83f546da72ab46142b1de5097976e13dfec56b44105077e80e33609e1f8e
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 1.2.2 - 2023-11-29
10
+ ### Changed
11
+ - Add support for filtering events by aggregate model attributes.
12
+
13
+ ## 1.2.1 - 2023-11-18
14
+ ### Changed
15
+ - Allow timestamp data in events to override default timestamps update.
16
+ - This allows us to create temporal events for example in the case of snapshots.
17
+
9
18
  ## 1.2.0 - 2023-09-26
10
19
  ### Changed
11
20
  - Fix issue where the reactor worker might not work correctly for models not inheriting from ApplicationRecord.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- eventsimple (1.2.0)
4
+ eventsimple (1.2.2)
5
5
  dry-struct (~> 1.6)
6
6
  dry-types (~> 1.7)
7
7
  pg (~> 1.4)
@@ -89,7 +89,7 @@ GEM
89
89
  coderay (1.1.3)
90
90
  concurrent-ruby (1.2.2)
91
91
  crass (1.0.6)
92
- date (3.3.3)
92
+ date (3.3.4)
93
93
  diff-lcs (1.5.0)
94
94
  dry-core (1.0.1)
95
95
  concurrent-ruby (~> 1.0)
@@ -158,12 +158,12 @@ GEM
158
158
  mini_mime (1.1.5)
159
159
  minitest (5.20.0)
160
160
  nenv (0.3.0)
161
- net-imap (0.3.7)
161
+ net-imap (0.4.7)
162
162
  date
163
163
  net-protocol
164
164
  net-pop (0.1.2)
165
165
  net-protocol
166
- net-protocol (0.2.1)
166
+ net-protocol (0.2.2)
167
167
  timeout
168
168
  net-smtp (0.4.0)
169
169
  net-protocol
@@ -300,7 +300,7 @@ GEM
300
300
  lint_roller (~> 1.0)
301
301
  rubocop-rails (~> 2.20.2)
302
302
  thor (1.2.2)
303
- timeout (0.4.0)
303
+ timeout (0.4.1)
304
304
  treetop (1.6.12)
305
305
  polyglot (~> 0.3)
306
306
  tzinfo (2.0.6)
data/README.md CHANGED
@@ -176,8 +176,6 @@ module UserComponent
176
176
  def apply(user)
177
177
  user.canonical_id = data.canonical_id
178
178
  user.email = data.email
179
-
180
- user
181
179
  end
182
180
  end
183
181
  end
@@ -8,6 +8,10 @@ module Eventsimple
8
8
  @event_id = params[:e] || -1
9
9
  @tab_id = (params[:t] == 'event') ? 'event' : 'entity'
10
10
 
11
+ filter_columns = @model_class._filter_attributes
12
+ params_filters = params.permit(filters: {})[:filters] || {}
13
+ @filters = filter_columns.to_h { |column| [column, params_filters[column]] }
14
+
11
15
  primary_key = @model_class.event_class._aggregate_id
12
16
  @entity = @model_class.find_by!(primary_key => @aggregate_id)
13
17
  @entity_event_history = @entity.events.reverse
@@ -2,9 +2,44 @@ module Eventsimple
2
2
  class ModelsController < ApplicationController
3
3
  def show
4
4
  @model_name = params[:name]
5
+ model_class = event_classes.find { |d| d.name == @model_name }
5
6
 
6
- model_event_class = event_classes.find { |d| d.name == @model_name }.event_class
7
- @latest_entities = model_event_class.last(20).reverse
7
+ scope = apply_filter(model_class, model_class.event_class)
8
+
9
+ @latest_entities = scope.last(20).reverse
10
+
11
+ check_redirect_to_entity
12
+ end
13
+
14
+ private
15
+
16
+ def apply_filter(model_class, model_event_class)
17
+ filter_columns = model_class._filter_attributes
18
+
19
+ params_filters = params.permit(filters: {})[:filters] || {}
20
+ @filters = filter_columns.to_h { |column| [column, params_filters[column]] }
21
+
22
+ return model_event_class unless @filters.any?
23
+
24
+ aggregate_class_symbol = model_event_class._aggregate_klass.model_name.element.to_sym
25
+ model_event_class = model_event_class.joins(aggregate_class_symbol)
26
+ @filters.each do |key, value|
27
+ next if value.blank?
28
+ key = model_event_class._aggregate_id if key == :aggregate_id
29
+ model_event_class = model_event_class.where({ aggregate_class_symbol => { key => value } })
30
+ end
31
+
32
+ model_event_class
33
+ end
34
+
35
+ def check_redirect_to_entity
36
+ return unless @latest_entities.any?
37
+
38
+ first_aggregate_id = @latest_entities.first.aggregate_id
39
+
40
+ return unless @latest_entities.all? { |entity| entity.aggregate_id == first_aggregate_id }
41
+
42
+ redirect_to model_entity_path(@model_name, first_aggregate_id, filters: @filters)
8
43
  end
9
44
  end
10
45
  end
@@ -1,26 +1,20 @@
1
1
  <!-- Header Search Start -->
2
2
  <header class="container mb-4">
3
- <div class="row">
4
- <div class="col col-sm-3">
5
- <select id="form-search-model-name" class="form-select" aria-label="Model select" onchange="document.location.href=this.value" required>
6
- <option value="">Choose event source model</option>
7
- <% event_class_names.each do |klass| %>
8
- <% is_active = klass === @model_name ? 'selected' : '' %>
9
- <option value="<%= model_path(klass) %>" <%= is_active %>><%= klass %></option>
10
- <% end %>
11
- </select>
12
- </div>
13
-
14
- <% if @model_name %>
15
- <div class="col col-sm-7">
16
- <%= form_with url: model_entity_path(@model_name, ''), id: 'model-search' do |f| %>
17
- <%= f.search_field :event_id, class: 'form-control', placeholder: 'Canonical identifier', value: @aggregate_id, required: true, aria: { label: 'Entity canonical identifier' } %>
3
+ <% if @model_name %>
4
+ <%= form_with scope: :filters, url: model_path(@model_name), method: :get, id: 'model-filter' do |f| %>
5
+ <div class="row">
6
+ <label for="filters_keys[keys]" class="col-sm-2 col-form-label">Filter attribute</label>
7
+ <div class="form-group col-sm-3">
8
+ <%= select :filters_keys, :keys, @filters.keys, { selected: @filters.compact.keys&.first }, { class:"form-select", onchange: "document.getElementById('model-filter-value').name=`filters[${this.value}]`" } %>
18
9
  </div>
19
- <div class="col col-sm-2">
10
+ <div class="form-group col-sm-5">
11
+ <%= f.search_field @filters.compact.keys&.first || @filters.keys.first, id: 'model-filter-value', value: @filters.compact.values&.first, class:"form-control", placeholder: "Filter value" %>
12
+ </div>
13
+ <div class="form-group col-sm-2">
20
14
  <%= f.submit 'Search', class: 'btn btn-primary' %>
21
- <% end %>
15
+ </div>
22
16
  </div>
23
17
  <% end %>
24
- </div>
18
+ <% end %>
25
19
  </header>
26
20
  <!-- Header Search End -->
@@ -62,12 +62,6 @@
62
62
  query.set(param, value);
63
63
  link.attr("href",window.location.pathname + '?' + query.toString());
64
64
  });
65
-
66
- $('#model-search').on('submit', (event) => {
67
- event.preventDefault();
68
- const form = $(event.currentTarget);
69
- window.location = form.attr('action') + form.children('#event_id').val();
70
- });
71
65
  });
72
66
  </script>
73
67
 
@@ -2,7 +2,7 @@ module Eventsimple
2
2
  module Entity
3
3
  DEFAULT_IGNORE_PROPS = %w[id lock_version].freeze
4
4
 
5
- def event_driven_by(event_klass, aggregate_id:)
5
+ def event_driven_by(event_klass, aggregate_id:, filter_attributes: [])
6
6
  has_many :events, class_name: event_klass.name.to_s,
7
7
  foreign_key: :aggregate_id,
8
8
  primary_key: aggregate_id,
@@ -13,6 +13,9 @@ module Eventsimple
13
13
 
14
14
  class_attribute :ignored_for_projection, default: []
15
15
 
16
+ class_attribute :_filter_attributes
17
+ self._filter_attributes = [aggregate_id] | Array.wrap(filter_attributes)
18
+
16
19
  # disable automatic timestamp updates
17
20
  self.record_timestamps = false
18
21
 
@@ -35,8 +38,8 @@ module Eventsimple
35
38
  assign_attributes(self.class.column_defaults.except(*ignore_props))
36
39
 
37
40
  event_history.each do |event|
38
- event.apply(self)
39
41
  event.apply_timestamps(self)
42
+ event.apply(self)
40
43
  end
41
44
 
42
45
  self
@@ -86,8 +86,8 @@ module Eventsimple
86
86
 
87
87
  # Apply the transformation to the aggregate and save it.
88
88
  def apply_and_persist
89
- apply(aggregate)
90
89
  apply_timestamps(aggregate)
90
+ apply(aggregate)
91
91
 
92
92
  # Persist!
93
93
  aggregate.save!
@@ -45,3 +45,5 @@ RSpec.shared_examples 'an event in invalid state that is rescued' do
45
45
  end
46
46
  end
47
47
  end
48
+
49
+ RSpec::Matchers.define_negated_matcher(:not_change, :change)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eventsimple
4
- VERSION = '1.2.0'
4
+ VERSION = '1.2.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventsimple
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zulfiqar Ali
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-26 00:00:00.000000000 Z
11
+ date: 2023-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct