eventsimple 2.0.3 → 2.0.4

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: d72072b70e9962eb4e340513ea94a5f68bc0e9c3e80555a0ddf7ed188d990888
4
- data.tar.gz: d42eb9077fda2bd8cd450a1198314dfe6e06ea7aa450af2b2d2c21f9349ccbda
3
+ metadata.gz: ae9bbebb1de86056d5f59ce55f4b689acf234bc0edb22250a929bc50beab5e51
4
+ data.tar.gz: a65c7510e97468125924def2c029dfb675553e86380b243c08dcbc7666b082af
5
5
  SHA512:
6
- metadata.gz: 043c3b0f6383c2b25729a721986454398d6fabd0a51444b4e0f5f3a9664bdd562730942fd5549e09c00ca8cb2c051813657e667ce9be68fe93c90cc8a512768a
7
- data.tar.gz: dfefe25f7cd94473700ac4802e93234cef7d0b2d69621163b2f91de3380982bbe23df95735fa5ea4123e5b775db3fbd17ed24e91a58df73f9c3d371dea0c887e
6
+ metadata.gz: dfda660ee2398b239ace585e95047b2fec9dcd5d8056258513b76061707ec730a5d5fd6ba2d40ffb22406d6aa3d8688401579a4c2d309d61e1afc056b001439b
7
+ data.tar.gz: d23dbffcbc0229dc04f3b57b1755dfb107c1d32e7bbe684592cc0d737dd7c80a7970cb5fc157a5a3d57dca8d08466251e965dd02a30be28d1f1dd3b2d5536dfe
data/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 2.0.4 - 2025-12-16
10
+ ### Added
11
+ - Fixes an issue where entities with deleted events were not being rendered correctly.
12
+ - Display a diff showing actual vs expected values for mismatched attributes when the current entity state does not match the reprojection from events.
13
+
9
14
  ## 2.0.3 - 2025-12-12
10
15
  ### Changed
11
16
  - Fix false positive deprecation warnings when using Message as an optional attribute type.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- eventsimple (2.0.3)
4
+ eventsimple (2.0.4)
5
5
  concurrent-ruby (>= 1.2.3)
6
6
  dry-struct (>= 1.8.0)
7
7
  dry-types (>= 1.7.0)
@@ -24,6 +24,10 @@ module Eventsimple
24
24
  previous_index = @entity_event_history.find_index { |e| e.id == @selected_event.id } + 1
25
25
  @previous_event = @entity_event_history[previous_index]
26
26
 
27
+ # Check projection mismatch BEFORE calling changes (which modifies @entity via reproject)
28
+ @projection_mismatch = !@entity.projection_matches_events?
29
+ @projection_diff = projection_diff if @projection_mismatch
30
+
27
31
  @entity_changes = changes
28
32
  rescue StandardError => e
29
33
  @error_message = e.message
@@ -44,7 +48,7 @@ module Eventsimple
44
48
  end
45
49
 
46
50
  def current_attributes
47
- @current_attributes ||= @entity.reproject(at: @selected_event.created_at).attributes.except(
51
+ @current_attributes ||= @entity.reproject(skip_deleted: true, at: @selected_event.created_at).attributes.except(
48
52
  'lock_version',
49
53
  )
50
54
  end
@@ -52,12 +56,28 @@ module Eventsimple
52
56
  def previous_attributes
53
57
  @previous_attributes ||=
54
58
  if @previous_event
55
- @entity.reproject(at: @previous_event.created_at).attributes.except(
59
+ @entity.reproject(skip_deleted: true, at: @previous_event.created_at).attributes.except(
56
60
  'lock_version',
57
61
  )
58
62
  else
59
63
  @model_class.column_defaults
60
64
  end
61
65
  end
66
+
67
+ def projection_diff
68
+ actual_attributes = @model_class.unscoped.find(@entity.id).attributes.except('lock_version')
69
+ reprojected_attributes = @model_class.unscoped.find(@entity.id).reproject.attributes.except('lock_version')
70
+
71
+ actual_attributes.filter_map do |attr_name, actual_value|
72
+ reprojected_value = reprojected_attributes[attr_name]
73
+ next if actual_value == reprojected_value
74
+
75
+ {
76
+ attribute: attr_name,
77
+ actual: actual_value,
78
+ expected: reprojected_value,
79
+ }
80
+ end
81
+ end
62
82
  end
63
83
  end
@@ -1,6 +1,6 @@
1
- <div class="row align-items-start">
1
+ <div class="row g-4 align-items-start">
2
2
  <!-- Pane: Entity Details Start -->
3
- <div class="col g-4 col-sm-8">
3
+ <div class="col-12 col-sm-8">
4
4
  <h2><%= @model_name%>: <%= @aggregate_id %></h2>
5
5
  <hr/>
6
6
 
@@ -32,7 +32,7 @@
32
32
  <% @entity_changes.each do |property| %>
33
33
  <tr class="<%= property[:is_changed] ? 'table-info' : '' %>">
34
34
  <th scope="row"><%= property[:label] %></th>
35
- <td><code class="entity-property"><%= property[:historical_value].nil? ? '-' : property[:historical_value] %></code></th>
35
+ <td><code class="entity-property"><%= property[:historical_value].nil? ? '-' : property[:historical_value] %></code></td>
36
36
  <td><code class="entity-property"><%= property[:current_value].nil? ? '-' : property[:current_value] %></code></td>
37
37
  </tr>
38
38
  <% end %>
@@ -87,7 +87,7 @@
87
87
  <!-- Pane: Entity Details End -->
88
88
 
89
89
  <!-- Pane: Time Travel Start -->
90
- <div class="col g-4 col-sm-4">
90
+ <div class="col-12 col-sm-4">
91
91
  <h3>Time Travel</h3>
92
92
  <ul class="list-group">
93
93
  <% @entity_event_history.each_with_index do | event, index | %>
@@ -104,6 +104,43 @@
104
104
  </li>
105
105
  <% end %>
106
106
  </ul>
107
+
108
+ <% if @projection_mismatch %>
109
+ <!-- Projection Mismatch Warning Start -->
110
+ <div class="mt-4">
111
+ <div class="alert alert-warning" role="alert">
112
+ <h6 class="alert-heading d-flex align-items-center mb-2">
113
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
114
+ Projection Mismatch
115
+ </h6>
116
+ <p class="mb-2 small">
117
+ The current entity state does not match the reprojection from events.
118
+ This may indicate that the entity was modified outside of the event system.
119
+ </p>
120
+ <hr class="my-2">
121
+ <p class="mb-1 small fw-bold">Differences:</p>
122
+ <table class="table table-sm table-borderless mb-0 small">
123
+ <thead>
124
+ <tr>
125
+ <th>Attribute</th>
126
+ <th>Actual</th>
127
+ <th>Expected</th>
128
+ </tr>
129
+ </thead>
130
+ <tbody>
131
+ <% @projection_diff.each do |diff| %>
132
+ <tr>
133
+ <td><code><%= diff[:attribute] %></code></td>
134
+ <td class="text-danger"><%= diff[:actual].nil? ? '-' : diff[:actual] %></td>
135
+ <td class="text-success"><%= diff[:expected].nil? ? '-' : diff[:expected] %></td>
136
+ </tr>
137
+ <% end %>
138
+ </tbody>
139
+ </table>
140
+ </div>
141
+ </div>
142
+ <!-- Projection Mismatch Warning End -->
143
+ <% end %>
107
144
  </div>
108
145
  <!-- Pane: Time Travel End -->
109
146
  </div>
@@ -2,15 +2,17 @@
2
2
  <header class="container mb-4">
3
3
  <% if @model_name %>
4
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">
5
+ <div class="row g-2 align-items-center">
6
+ <div class="col-auto">
7
+ <label for="filters_keys_keys" class="col-form-label">Filter attribute</label>
8
+ </div>
9
+ <div class="col-auto">
8
10
  <%= select :filters_keys, :keys, @filters.keys, { selected: @filters.compact.keys&.first }, { class:"form-select", onchange: "document.getElementById('model-filter-value').name=`filters[${this.value}]`" } %>
9
11
  </div>
10
- <div class="form-group col-sm-5">
12
+ <div class="col-auto flex-grow-1" style="max-width: 300px;">
11
13
  <%= 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
14
  </div>
13
- <div class="form-group col-sm-2">
15
+ <div class="col-auto">
14
16
  <%= f.submit 'Search', class: 'btn btn-primary' %>
15
17
  </div>
16
18
  </div>
@@ -1,5 +1,5 @@
1
1
  <style>
2
- // Structure
2
+ /* Structure */
3
3
  body {
4
4
  min-height: 100vh;
5
5
  min-height: -webkit-fill-available;
@@ -30,7 +30,7 @@
30
30
  overflow-y: auto;
31
31
  }
32
32
 
33
- // Sidebar Nav
33
+ /* Sidebar Nav */
34
34
 
35
35
  .bi {
36
36
  vertical-align: -.125em;
@@ -90,10 +90,7 @@
90
90
  overflow-y: auto;
91
91
  }
92
92
 
93
- .fw-semibold { font-weight: 600; }
94
- .lh-tight { line-height: 1.25; }
95
-
96
- // Styles
93
+ /* Styles */
97
94
 
98
95
  code {
99
96
  color: deeppink;
@@ -8,20 +8,21 @@
8
8
  <%= csrf_meta_tags %>
9
9
  <%= csp_meta_tag %>
10
10
 
11
- <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css",
11
+ <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css",
12
12
  media: "all",
13
- integrity: 'sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65',
13
+ integrity: 'sha256-2FMn2Zx6PuH5tdBQDRNwrOo60ts5wWPC9R8jK67b3t4=',
14
14
  crossorigin: 'anonymous'
15
15
  %>
16
- <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css",
16
+ <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css",
17
17
  media: "all",
18
18
  crossorigin: 'anonymous'
19
19
  %>
20
- <%= javascript_include_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js",
21
- integrity: "sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V",
20
+ <%= javascript_include_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js",
21
+ integrity: "sha256-ew8UiV1pJH/YjpOEBInP1HxVvT/SfrCmwSoUzF9JIgc=",
22
22
  crossorigin: "anonymous"
23
23
  %>
24
- <%= javascript_include_tag "https://cdn.jsdelivr.net/npm/jquery@3.6.3/dist/jquery.min.js",
24
+ <%= javascript_include_tag "https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
25
+ integrity: "sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=",
25
26
  crossorigin: "anonymous"
26
27
  %>
27
28
 
@@ -51,7 +51,7 @@ module Eventsimple
51
51
 
52
52
  module InstanceMethods
53
53
  def projection_matches_events?
54
- reprojected = self.class.find(id).reproject
54
+ reprojected = self.class.unscoped.find(id).reproject
55
55
 
56
56
  attributes == reprojected.attributes
57
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eventsimple
4
- VERSION = '2.0.3'
4
+ VERSION = '2.0.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventsimple
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zulfiqar Ali