diff --git a/.rubocop.yml b/.rubocop.yml index 42659ba..6358661 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,18 @@ +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://docs.rubocop.org/rubocop/configuration + inherit_from: .rubocop_todo.yml +inherit_mode: + merge: + - Exclude AllCops: NewCops: disable diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bffb860..2ea6c40 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -18,7 +18,6 @@ Metrics/AbcSize: # AllowedMethods: refine Metrics/BlockLength: Exclude: - - '**/*.gemspec' - 'spec/event_sourcery/postgres/config_spec.rb' - 'spec/event_sourcery/postgres/event_store_spec.rb' - 'spec/event_sourcery/postgres/optimised_event_poll_waiter_spec.rb' @@ -51,63 +50,7 @@ Metrics/ParameterLists: - 'lib/event_sourcery/postgres/event_store.rb' - 'spec/support/event_helpers.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyleForLeadingUnderscores. -# SupportedStylesForLeadingUnderscores: disallowed, required, optional -Naming/MemoizedInstanceVariableName: - Exclude: - - 'lib/event_sourcery/postgres/reactor.rb' - - 'spec/event_sourcery/postgres/reactor_spec.rb' - -# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. -# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to -Naming/MethodParameterName: - Exclude: - - 'spec/event_sourcery/postgres/reactor_spec.rb' - -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. -# SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 -Naming/VariableNumber: - Exclude: - - 'spec/event_sourcery/postgres/reactor_spec.rb' - -# Configuration parameters: AllowedConstants. -Style/Documentation: - Exclude: - - 'spec/**/*' - - 'test/**/*' - - 'lib/event_sourcery/postgres.rb' - - 'lib/event_sourcery/postgres/config.rb' - - 'lib/event_sourcery/postgres/event_store.rb' - - 'lib/event_sourcery/postgres/projector.rb' - - 'lib/event_sourcery/postgres/queue_with_interval_callback.rb' - - 'lib/event_sourcery/postgres/reactor.rb' - - 'lib/event_sourcery/postgres/schema.rb' - - 'lib/event_sourcery/postgres/table_owner.rb' - # Configuration parameters: AllowedVariables. Style/GlobalVars: Exclude: - 'spec/support/db_helpers.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedReceivers. -# AllowedReceivers: Thread.current -Style/HashEachMethods: - Exclude: - - 'lib/event_sourcery/postgres/table_owner.rb' - -# Configuration parameters: AllowedMethods. -# AllowedMethods: respond_to_missing? -Style/OptionalBooleanParameter: - Exclude: - - 'lib/event_sourcery/postgres/queue_with_interval_callback.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. -# AllowedMethods: present?, blank?, presence, try, try! -Style/SafeNavigation: - Exclude: - - 'lib/event_sourcery/postgres/optimised_event_poll_waiter.rb' - - 'lib/event_sourcery/postgres/reactor.rb' diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5ed0c..ae6f5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -[Unreleased]: https://github.com/envato/event_sourcery-postgres/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/envato/event_sourcery-postgres/compare/v1.0.1...HEAD + +## [1.0.1] - 2026-01-17 + +### Changed + +- Resolve issues as identified by RuboCop ([#85]). + +[1.0.0]: https://github.com/envato/event_sourcery-postgres/compare/v1.0.0...v1.0.1 +[#85]: https://github.com/envato/event_sourcery-postgres/pull/85 ## [1.0.0] - 2025-12-28 diff --git a/README.md b/README.md index 1fd866d..686294a 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ ## Development Status -EventSourcery is currently being used in production by multiple apps but we -haven't finalized the API yet and things are still moving rapidly. Until we -release a 1.0 things may change without first being deprecated. +EventSourcery::Postgres is in production use at [Envato](http://envato.com). ## Installation diff --git a/event_sourcery-postgres.gemspec b/event_sourcery-postgres.gemspec index cb6ab21..4883ea7 100644 --- a/event_sourcery-postgres.gemspec +++ b/event_sourcery-postgres.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| 'allowed_push_host' => 'https://rubygems.org', 'bug_tracker_uri' => "#{spec.homepage}/issues", 'changelog_uri' => "#{spec.homepage}/blob/HEAD/CHANGELOG.md", - 'documentation_uri' => "https://www.rubydoc.info/gems/event_sourcery-postgres/#{spec.version}", + 'documentation_uri' => "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}", 'source_code_uri' => "#{spec.homepage}/tree/v#{spec.version}" } diff --git a/lib/event_sourcery/postgres.rb b/lib/event_sourcery/postgres.rb index 9505a1f..be54989 100644 --- a/lib/event_sourcery/postgres.rb +++ b/lib/event_sourcery/postgres.rb @@ -17,6 +17,7 @@ require 'event_sourcery/postgres/tracker' module EventSourcery + # PostgreSQL adapter for EventSourcery providing event store and projection capabilities. module Postgres def self.configure yield config diff --git a/lib/event_sourcery/postgres/config.rb b/lib/event_sourcery/postgres/config.rb index b086af9..121f106 100644 --- a/lib/event_sourcery/postgres/config.rb +++ b/lib/event_sourcery/postgres/config.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Configuration settings for the PostgreSQL event store and projections. class Config attr_accessor :lock_table_to_guarantee_linear_sequence_id_growth, :write_events_function_name, diff --git a/lib/event_sourcery/postgres/event_store.rb b/lib/event_sourcery/postgres/event_store.rb index 36d19e9..c05e263 100644 --- a/lib/event_sourcery/postgres/event_store.rb +++ b/lib/event_sourcery/postgres/event_store.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # PostgreSQL implementation of an event store for persisting and retrieving domain events. class EventStore include EventSourcery::EventStore::EachByRange diff --git a/lib/event_sourcery/postgres/optimised_event_poll_waiter.rb b/lib/event_sourcery/postgres/optimised_event_poll_waiter.rb index c574b0a..c9377c4 100644 --- a/lib/event_sourcery/postgres/optimised_event_poll_waiter.rb +++ b/lib/event_sourcery/postgres/optimised_event_poll_waiter.rb @@ -54,7 +54,7 @@ def start_async(after_listen: nil) after_listen_callback = if after_listen proc do after_listen.call - @after_listen.call if @after_listen + @after_listen&.call end else @after_listen diff --git a/lib/event_sourcery/postgres/projector.rb b/lib/event_sourcery/postgres/projector.rb index 5d85a3c..5c58620 100644 --- a/lib/event_sourcery/postgres/projector.rb +++ b/lib/event_sourcery/postgres/projector.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Mixin providing projection capabilities for processing events into read models. module Projector def self.included(base) base.include(EventProcessing::EventStreamProcessor) @@ -17,6 +18,7 @@ class << self end end + # Instance methods for projector event processing and tracking. module InstanceMethods def initialize(tracker: EventSourcery::Postgres.config.event_tracker, db_connection: EventSourcery::Postgres.config.projections_database, diff --git a/lib/event_sourcery/postgres/queue_with_interval_callback.rb b/lib/event_sourcery/postgres/queue_with_interval_callback.rb index 8a02f24..1cd0abb 100644 --- a/lib/event_sourcery/postgres/queue_with_interval_callback.rb +++ b/lib/event_sourcery/postgres/queue_with_interval_callback.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Queue that invokes a callback at regular intervals when no items are available. class QueueWithIntervalCallback < ::Queue attr_accessor :callback @@ -16,7 +17,7 @@ def initialize( super() end - def pop(non_block_without_callback = false) + def pop(non_block_without_callback = false) # rubocop:disable Style/OptionalBooleanParameter return super if non_block_without_callback pop_with_interval_callback diff --git a/lib/event_sourcery/postgres/reactor.rb b/lib/event_sourcery/postgres/reactor.rb index 9a04aa4..604b7ce 100644 --- a/lib/event_sourcery/postgres/reactor.rb +++ b/lib/event_sourcery/postgres/reactor.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Mixin providing reactor capabilities for processing events and emitting new events in response. module Reactor UndeclaredEventEmissionError = Class.new(StandardError) @@ -12,6 +13,7 @@ def self.included(base) base.include(InstanceMethods) end + # Class methods for declaring and querying emitted event types. module ClassMethods # Assign the types of events this reactor can emit. # @@ -22,7 +24,7 @@ def emits_events(*event_types) # @return [Array] an array of the types of events this reactor can emit def emit_events - @emits_event_types ||= [] + @emits_event_types ||= [] # rubocop:disable Naming/MemoizedInstanceVariableName end # This will tell you if this reactor emits any type of event. @@ -41,6 +43,7 @@ def emits_event?(event_type) end end + # Instance methods for reactor initialisation and event emission. module InstanceMethods def initialize(tracker: EventSourcery::Postgres.config.event_tracker, db_connection: EventSourcery::Postgres.config.projections_database, @@ -75,7 +78,7 @@ def emit_event(event_or_hash, &block) end def invoke_action_and_emit_event(event, action) - action.call(event.body) if action + action&.call(event.body) event_sink.sink(event) end end diff --git a/lib/event_sourcery/postgres/schema.rb b/lib/event_sourcery/postgres/schema.rb index 6effc07..6ff65bf 100644 --- a/lib/event_sourcery/postgres/schema.rb +++ b/lib/event_sourcery/postgres/schema.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Schema management for creating event store and projector tables in PostgreSQL. module Schema module_function diff --git a/lib/event_sourcery/postgres/table_owner.rb b/lib/event_sourcery/postgres/table_owner.rb index 73334ab..11b226d 100644 --- a/lib/event_sourcery/postgres/table_owner.rb +++ b/lib/event_sourcery/postgres/table_owner.rb @@ -2,6 +2,7 @@ module EventSourcery module Postgres + # Mixin providing table management capabilities for projectors and reactors. module TableOwner DefaultTableError = Class.new(StandardError) NoSuchTableError = Class.new(StandardError) @@ -10,6 +11,7 @@ def self.prepended(base) base.extend(ClassMethods) end + # Class methods for defining and managing database tables. module ClassMethods # Hash of the tables and their corresponding blocks. # @@ -38,7 +40,7 @@ def setup # Reset by dropping each table. def reset - self.class.tables.keys.each do |table_name| + self.class.tables.each_key do |table_name| prefixed_name = table_name_prefixed(table_name) @db_connection.drop_table(prefixed_name, cascade: true) if @db_connection.table_exists?(prefixed_name) end @@ -49,7 +51,7 @@ def reset # This will truncate all the tables and reset the tracker back to 0, # done as a transaction. def truncate - self.class.tables.each do |table_name, _| + self.class.tables.each_key do |table_name| @db_connection.transaction do prefixed_name = table_name_prefixed(table_name) @db_connection[prefixed_name].truncate diff --git a/lib/event_sourcery/postgres/version.rb b/lib/event_sourcery/postgres/version.rb index 2fd4dc7..a80d114 100644 --- a/lib/event_sourcery/postgres/version.rb +++ b/lib/event_sourcery/postgres/version.rb @@ -2,6 +2,6 @@ module EventSourcery module Postgres - VERSION = '1.0.0' + VERSION = '1.0.1' end end diff --git a/spec/event_sourcery/postgres/reactor_spec.rb b/spec/event_sourcery/postgres/reactor_spec.rb index 5390424..3facec1 100644 --- a/spec/event_sourcery/postgres/reactor_spec.rb +++ b/spec/event_sourcery/postgres/reactor_spec.rb @@ -144,7 +144,7 @@ end context 'with a reactor that emits events' do - let(:event_1) do + let(:event1) do TermsAccepted.new( id: 1, aggregate_id: aggregate_id, @@ -152,20 +152,20 @@ correlation_id: SecureRandom.uuid ) end - let(:event_2) do + let(:event2) do EchoEvent.new( id: 2, aggregate_id: aggregate_id, - body: event_1.body, - correlation_id: event_1.correlation_id, - causation_id: event_1.uuid + body: event1.body, + correlation_id: event1.correlation_id, + causation_id: event1.uuid ) end - let(:event_3) { TermsAccepted.new(id: 3, aggregate_id: aggregate_id, body: { time: Time.now }) } - let(:event_4) { TermsAccepted.new(id: 4, aggregate_id: aggregate_id, body: { time: Time.now }) } - let(:event_5) { TermsAccepted.new(id: 5, aggregate_id: aggregate_id, body: { time: Time.now }) } - let(:event_6) { EchoEvent.new(id: 6, aggregate_id: aggregate_id, body: event_3.body, causation_id: event_3.uuid) } - let(:events) { [event_1, event_2, event_3, event_4] } + let(:event3) { TermsAccepted.new(id: 3, aggregate_id: aggregate_id, body: { time: Time.now }) } + let(:event4) { TermsAccepted.new(id: 4, aggregate_id: aggregate_id, body: { time: Time.now }) } + let(:event5) { TermsAccepted.new(id: 5, aggregate_id: aggregate_id, body: { time: Time.now }) } + let(:event6) { EchoEvent.new(id: 6, aggregate_id: aggregate_id, body: event3.body, causation_id: event3.uuid) } + let(:events) { [event1, event2, event3, event4] } let(:action_stub_class) do Class.new do def self.action(id) @@ -173,7 +173,7 @@ def self.action(id) end def self.actioned - @actions ||= [] + @actioned ||= [] end end end @@ -203,8 +203,8 @@ def event_count event_source.get_next_from(0, limit: 100).count end - def latest_events(n = 1) - event_source.get_next_from(0, limit: 100)[-n..] + def latest_events(index = 1) + event_source.get_next_from(0, limit: 100)[-index..] end context "when the event emitted doesn't take actions" do @@ -221,20 +221,20 @@ def latest_events(n = 1) end it 'processes the events as usual' do - [event_1, event_2, event_3, event_4, event_5].each do |event| + [event1, event2, event3, event4, event5].each do |event| reactor.process(event) end expect(event_count).to eq 8 end it 'stores the event causation id' do - reactor.process(event_1) - expect(latest_events(1).first.causation_id).to eq event_1.uuid + reactor.process(event1) + expect(latest_events(1).first.causation_id).to eq event1.uuid end it 'stores the event correlation id' do - reactor.process(event_1) - expect(latest_events(1).first.correlation_id).to eq event_1.correlation_id + reactor.process(event1) + expect(latest_events(1).first.correlation_id).to eq event1.correlation_id end end @@ -252,7 +252,7 @@ def latest_events(n = 1) end it 'raises an error' do - expect { reactor.process(event_1) }.to raise_error(EventSourcery::EventProcessingError) + expect { reactor.process(event1) }.to raise_error(EventSourcery::EventProcessingError) end end @@ -273,18 +273,18 @@ def latest_events(n = 1) end it 'can manupulate the event body as part of the action' do - reactor.process(event_1) + reactor.process(event1) expect(latest_events(1).first.body['token']).to eq 'secret-identifier' end it 'stores the event causation id' do - reactor.process(event_1) - expect(latest_events(1).first.causation_id).to eq event_1.uuid + reactor.process(event1) + expect(latest_events(1).first.causation_id).to eq event1.uuid end it 'stores the event correlation id' do - reactor.process(event_1) - expect(latest_events(1).first.correlation_id).to eq event_1.correlation_id + reactor.process(event1) + expect(latest_events(1).first.correlation_id).to eq event1.correlation_id end end end