rails_simple_event_sourcing 1.0.6 → 1.0.8
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 +4 -4
- data/README.md +8 -19
- data/app/controllers/rails_simple_event_sourcing/events_controller.rb +17 -10
- data/app/helpers/rails_simple_event_sourcing/events_helper.rb +0 -21
- data/app/models/concerns/rails_simple_event_sourcing/read_only.rb +4 -0
- data/app/models/rails_simple_event_sourcing/event.rb +5 -1
- data/app/views/rails_simple_event_sourcing/events/_pagination.html.erb +5 -15
- data/app/views/rails_simple_event_sourcing/events/index.html.erb +1 -2
- data/app/views/rails_simple_event_sourcing/events/show.html.erb +1 -1
- data/config/routes.rb +2 -2
- data/lib/rails_simple_event_sourcing/engine.rb +1 -0
- data/lib/rails_simple_event_sourcing/paginator.rb +56 -6
- data/lib/rails_simple_event_sourcing/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3de0a8a9ff2169679636b2f07ecbeb3c665bfd6a7167f564029c343d508dbcc0
|
|
4
|
+
data.tar.gz: 136e1bb962428462dd0921cefa63746beefb6efab578f6c8a0e018709f10da1f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcd653bc3201aeb4cffca05df6f9819e3b8d9ee2eab08e60e35988438321bf6f6b15fb7d65d0940483f9087877f5f451645fc26f7e1fb31fb36e61a898760aee
|
|
7
|
+
data.tar.gz: 85449f552f258d5d2ccd43f238389769dafdf885d93beeb7f1e4f47280f1bbc9ab5acb8fa435e94a3ca9f0602af52677812f1c8dcffa85857277b977081f0fb7
|
data/README.md
CHANGED
|
@@ -88,6 +88,9 @@ RailsSimpleEventSourcing.configure do |config|
|
|
|
88
88
|
# When true, falls back to convention-based handler resolution
|
|
89
89
|
# When false, requires explicit registration of all handlers
|
|
90
90
|
config.use_naming_convention_fallback = true
|
|
91
|
+
|
|
92
|
+
# Number of events displayed per page in the Events Viewer (defaults to 25)
|
|
93
|
+
config.events_per_page = 50
|
|
91
94
|
end
|
|
92
95
|
```
|
|
93
96
|
|
|
@@ -545,11 +548,11 @@ Mount the engine in your `config/routes.rb`:
|
|
|
545
548
|
|
|
546
549
|
```ruby
|
|
547
550
|
Rails.application.routes.draw do
|
|
548
|
-
mount RailsSimpleEventSourcing::Engine, at: "/
|
|
551
|
+
mount RailsSimpleEventSourcing::Engine, at: "/events"
|
|
549
552
|
end
|
|
550
553
|
```
|
|
551
554
|
|
|
552
|
-
Then navigate to `/
|
|
555
|
+
Then navigate to `/events` in your browser to access the viewer.
|
|
553
556
|
|
|
554
557
|
**Password Protection:**
|
|
555
558
|
|
|
@@ -561,11 +564,11 @@ In production you will likely want to restrict access to the events viewer.
|
|
|
561
564
|
Rails.application.routes.draw do
|
|
562
565
|
mount Rack::Auth::Basic.new(
|
|
563
566
|
RailsSimpleEventSourcing::Engine,
|
|
564
|
-
"
|
|
567
|
+
"Events"
|
|
565
568
|
) { |username, password|
|
|
566
569
|
ActiveSupport::SecurityUtils.secure_compare(username, "admin") &
|
|
567
570
|
ActiveSupport::SecurityUtils.secure_compare(password, Rails.application.credentials.event_sourcing_password || "secret")
|
|
568
|
-
}, at: "/
|
|
571
|
+
}, at: "/events"
|
|
569
572
|
end
|
|
570
573
|
```
|
|
571
574
|
|
|
@@ -574,7 +577,7 @@ end
|
|
|
574
577
|
```ruby
|
|
575
578
|
Rails.application.routes.draw do
|
|
576
579
|
authenticate :user, ->(user) { user.admin? } do
|
|
577
|
-
mount RailsSimpleEventSourcing::Engine, at: "/
|
|
580
|
+
mount RailsSimpleEventSourcing::Engine, at: "/events"
|
|
578
581
|
end
|
|
579
582
|
end
|
|
580
583
|
```
|
|
@@ -587,16 +590,6 @@ end
|
|
|
587
590
|
- **Filtering** - Filter events by event type or aggregate type using dropdown selectors
|
|
588
591
|
- **Search** - Search by aggregate ID, or use `key:value` syntax to search within payload and metadata (e.g., `email:john@example.com`)
|
|
589
592
|
|
|
590
|
-
**Configuration:**
|
|
591
|
-
|
|
592
|
-
You can configure the number of events displayed per page (defaults to 25):
|
|
593
|
-
|
|
594
|
-
```ruby
|
|
595
|
-
RailsSimpleEventSourcing.configure do |config|
|
|
596
|
-
config.events_per_page = 50
|
|
597
|
-
end
|
|
598
|
-
```
|
|
599
|
-
|
|
600
593
|
### Model Configuration
|
|
601
594
|
|
|
602
595
|
Models that use event sourcing should include the `RailsSimpleEventSourcing::Events` module:
|
|
@@ -646,10 +639,6 @@ class Customer < ApplicationRecord
|
|
|
646
639
|
|
|
647
640
|
scope :active, -> { where(deleted_at: nil) }
|
|
648
641
|
scope :deleted, -> { where.not(deleted_at: nil) }
|
|
649
|
-
|
|
650
|
-
def soft_delete
|
|
651
|
-
update(deleted_at: Time.current)
|
|
652
|
-
end
|
|
653
642
|
end
|
|
654
643
|
|
|
655
644
|
# Event
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module RailsSimpleEventSourcing
|
|
4
4
|
class EventsController < ApplicationController
|
|
5
5
|
def index
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
paginate(
|
|
6
|
+
@event_types = event_types
|
|
7
|
+
@aggregates = aggregates
|
|
8
|
+
paginate(search_events)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def show
|
|
@@ -16,15 +16,21 @@ module RailsSimpleEventSourcing
|
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
def event_types
|
|
20
|
+
return Event.descendants.map(&:name).sort if Rails.env.production?
|
|
21
|
+
|
|
22
|
+
Event.distinct.pluck(:event_type).sort
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def aggregates
|
|
26
|
+
return Event.descendants.filter_map(&:aggregate_class).map(&:name).uniq.sort if Rails.env.production?
|
|
27
|
+
|
|
28
|
+
Event.where.not(eventable_type: nil).distinct.pluck(:eventable_type).sort
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
def search_events
|
|
25
|
-
scope = Event.order(created_at: :desc)
|
|
26
32
|
EventSearch.new(
|
|
27
|
-
scope
|
|
33
|
+
scope: Event.all,
|
|
28
34
|
event_type: params[:event_type],
|
|
29
35
|
aggregate: params[:aggregate],
|
|
30
36
|
query: params[:q]
|
|
@@ -34,8 +40,9 @@ module RailsSimpleEventSourcing
|
|
|
34
40
|
def paginate(scope)
|
|
35
41
|
@paginator = Paginator.new(
|
|
36
42
|
scope:,
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
per_page: RailsSimpleEventSourcing.config.events_per_page,
|
|
44
|
+
cursor: params[:after] || params[:before],
|
|
45
|
+
direction: params[:before].present? ? :prev : :next
|
|
39
46
|
)
|
|
40
47
|
end
|
|
41
48
|
|
|
@@ -2,26 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
module RailsSimpleEventSourcing
|
|
4
4
|
module EventsHelper
|
|
5
|
-
def pagination_window(current_page, total_pages, window: 2) # rubocop:disable Metrics/MethodLength
|
|
6
|
-
return [1] if total_pages <= 1
|
|
7
|
-
|
|
8
|
-
pages = []
|
|
9
|
-
left = [current_page - window, 1].max
|
|
10
|
-
right = [current_page + window, total_pages].min
|
|
11
|
-
|
|
12
|
-
if left > 1
|
|
13
|
-
pages << 1
|
|
14
|
-
pages << :gap if left > 2
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
(left..right).each { |p| pages << p }
|
|
18
|
-
|
|
19
|
-
if right < total_pages
|
|
20
|
-
pages << :gap if right < total_pages - 1
|
|
21
|
-
pages << total_pages
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
pages
|
|
25
|
-
end
|
|
26
5
|
end
|
|
27
6
|
end
|
|
@@ -14,10 +14,13 @@ module RailsSimpleEventSourcing
|
|
|
14
14
|
|
|
15
15
|
before_validation :setup_for_create, on: :create
|
|
16
16
|
before_save :persist_aggregate, if: :aggregate_defined?
|
|
17
|
+
after_create :disable_write_access!
|
|
17
18
|
|
|
18
19
|
def apply(aggregate)
|
|
19
20
|
payload.each do |key, value|
|
|
20
|
-
|
|
21
|
+
raise "Unknown attribute '#{key}' on #{aggregate.class}" unless aggregate.respond_to?("#{key}=")
|
|
22
|
+
|
|
23
|
+
aggregate.send("#{key}=", value)
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
|
|
@@ -67,6 +70,7 @@ module RailsSimpleEventSourcing
|
|
|
67
70
|
|
|
68
71
|
aggregate_repository.save!(@aggregate)
|
|
69
72
|
self.aggregate_id = @aggregate.id
|
|
73
|
+
@aggregate.disable_write_access!
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
def aggregate_repository
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
<% if @paginator.
|
|
1
|
+
<% if @paginator.prev? || @paginator.next? %>
|
|
2
2
|
<nav class="pagination">
|
|
3
|
-
<% if @paginator.
|
|
4
|
-
<%= link_to "Prev", events_path(
|
|
3
|
+
<% if @paginator.prev? %>
|
|
4
|
+
<%= link_to "Prev", events_path(before: @paginator.prev_cursor, q: params[:q], event_type: params[:event_type], aggregate: params[:aggregate]) %>
|
|
5
5
|
<% else %>
|
|
6
6
|
<span class="disabled">Prev</span>
|
|
7
7
|
<% end %>
|
|
8
8
|
|
|
9
|
-
<%
|
|
10
|
-
|
|
11
|
-
<span class="gap">…</span>
|
|
12
|
-
<% elsif page == @paginator.current_page %>
|
|
13
|
-
<span class="current"><%= page %></span>
|
|
14
|
-
<% else %>
|
|
15
|
-
<%= link_to page, events_path(page: page, q: params[:q], event_type: params[:event_type], aggregate: params[:aggregate]) %>
|
|
16
|
-
<% end %>
|
|
17
|
-
<% end %>
|
|
18
|
-
|
|
19
|
-
<% if @paginator.current_page < @paginator.total_pages %>
|
|
20
|
-
<%= link_to "Next", events_path(page: @paginator.current_page + 1, q: params[:q], event_type: params[:event_type], aggregate: params[:aggregate]) %>
|
|
9
|
+
<% if @paginator.next? %>
|
|
10
|
+
<%= link_to "Next", events_path(after: @paginator.next_cursor, q: params[:q], event_type: params[:event_type], aggregate: params[:aggregate]) %>
|
|
21
11
|
<% else %>
|
|
22
12
|
<span class="disabled">Next</span>
|
|
23
13
|
<% end %>
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<div class="card">
|
|
12
12
|
<div class="info-bar">
|
|
13
|
-
<span
|
|
14
|
-
<span>Page <%= @paginator.current_page %> of <%= @paginator.total_pages %></span>
|
|
13
|
+
<span>Events</span>
|
|
15
14
|
</div>
|
|
16
15
|
|
|
17
16
|
<table>
|
data/config/routes.rb
CHANGED
|
@@ -2,18 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
module RailsSimpleEventSourcing
|
|
4
4
|
class Paginator
|
|
5
|
-
attr_reader :
|
|
5
|
+
attr_reader :per_page
|
|
6
6
|
|
|
7
|
-
def initialize(scope:,
|
|
7
|
+
def initialize(scope:, per_page:, cursor: nil, direction: :next)
|
|
8
8
|
@scope = scope
|
|
9
9
|
@per_page = per_page
|
|
10
|
-
@
|
|
11
|
-
@
|
|
12
|
-
@current_page = (page.presence || 1).to_i.clamp(1, @total_pages)
|
|
10
|
+
@cursor = cursor&.to_i
|
|
11
|
+
@direction = direction
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def records
|
|
16
|
-
@
|
|
15
|
+
@records ||= fetch_records
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def next_cursor
|
|
19
|
+
records.last&.id
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def prev_cursor
|
|
23
|
+
records.first&.id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def next?
|
|
27
|
+
records
|
|
28
|
+
@has_next
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def prev?
|
|
32
|
+
records
|
|
33
|
+
@has_prev
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def fetch_records
|
|
39
|
+
if @cursor.nil?
|
|
40
|
+
fetch_first_page
|
|
41
|
+
elsif @direction == :prev
|
|
42
|
+
fetch_prev_page
|
|
43
|
+
else
|
|
44
|
+
fetch_next_page
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def fetch_first_page
|
|
49
|
+
rows = @scope.order(id: :desc).limit(@per_page + 1).to_a
|
|
50
|
+
@has_prev = false
|
|
51
|
+
@has_next = rows.size > @per_page
|
|
52
|
+
rows.first(@per_page)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fetch_next_page
|
|
56
|
+
rows = @scope.where(id: ...@cursor).order(id: :desc).limit(@per_page + 1).to_a
|
|
57
|
+
@has_prev = true
|
|
58
|
+
@has_next = rows.size > @per_page
|
|
59
|
+
rows.first(@per_page)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def fetch_prev_page
|
|
63
|
+
rows = @scope.where('id > ?', @cursor).order(id: :asc).limit(@per_page + 1).to_a
|
|
64
|
+
@has_next = true
|
|
65
|
+
@has_prev = rows.size > @per_page
|
|
66
|
+
rows.first(@per_page).reverse
|
|
17
67
|
end
|
|
18
68
|
end
|
|
19
69
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_simple_event_sourcing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damian Baćkowski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pg
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 7.1.2
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: concurrent-ruby
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.1'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.1'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rubocop
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|