cdc-core 0.0.0 → 0.1.0

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.
@@ -0,0 +1,51 @@
1
+ module CDC
2
+ module Core
3
+ # Immutable metadata container for CDC domain objects.
4
+ #
5
+ # Metadata keys are normalized to frozen strings. Nested hashes and arrays
6
+ # are recursively converted into Ractor-shareable objects. Values that Ruby
7
+ # cannot make shareable are stored as frozen #inspect strings.
8
+ class EventMetadata
9
+ @data: untyped
10
+
11
+ # @return [Hash{String=>Object}] normalized metadata
12
+ attr_reader data: untyped
13
+
14
+ # Build metadata from a hash-like structure.
15
+ #
16
+ # @param data [Hash] metadata values
17
+ def initialize: (?::Hash[untyped, untyped] data) -> void
18
+
19
+ # Fetch a metadata value by string or symbol key.
20
+ #
21
+ # @param key [String, Symbol] metadata key
22
+ # @return [Object, nil]
23
+ def []: (untyped key) -> untyped
24
+
25
+ # Return the normalized Ractor-shareable hash.
26
+ #
27
+ # @return [Hash{String=>Object}]
28
+ def to_h: () -> untyped
29
+
30
+ private
31
+
32
+ # Recursively normalize and freeze a hash.
33
+ #
34
+ # @param hash [Hash]
35
+ # @return [Hash{String=>Object}]
36
+ def deep_shareable_hash: (untyped hash) -> untyped
37
+
38
+ # Normalize metadata keys to frozen strings.
39
+ #
40
+ # @param key [Object]
41
+ # @return [String]
42
+ def normalize_key: (untyped key) -> untyped
43
+
44
+ # Normalize a metadata value into a shareable representation.
45
+ #
46
+ # @param value [Object]
47
+ # @return [Object]
48
+ def normalize_value: (untyped value) -> untyped
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,68 @@
1
+ module CDC
2
+ module Core
3
+ # Predicate object used to decide whether a pipeline should process an event.
4
+ #
5
+ # Filters are composable with #& and #|. A filter only matches when its
6
+ # predicate returns true exactly, keeping accidental truthy values from
7
+ # silently passing events through a pipeline.
8
+ class Filter
9
+ @predicate: untyped
10
+
11
+ # Match every event.
12
+ #
13
+ # @return [Filter]
14
+ def self.all: () -> untyped
15
+
16
+ # Match events from a schema.
17
+ #
18
+ # @param name [#to_s] schema name
19
+ # @return [Filter]
20
+ def self.schema: (untyped name) -> untyped
21
+
22
+ # Match events from a table regardless of schema.
23
+ #
24
+ # @param name [#to_s] table name
25
+ # @return [Filter]
26
+ def self.table: (untyped name) -> untyped
27
+
28
+ # Match events from a fully qualified schema.table name.
29
+ #
30
+ # @param name [#to_s] qualified table name
31
+ # @return [Filter]
32
+ def self.qualified_table: (untyped name) -> untyped
33
+
34
+ # Match events by operation.
35
+ #
36
+ # @param operation [#to_sym] CDC operation
37
+ # @return [Filter]
38
+ def self.operation: (untyped operation) -> untyped
39
+
40
+ # Build a custom filter.
41
+ #
42
+ # @yieldparam event [ChangeEvent] event being tested
43
+ # @yieldreturn [Boolean] true to match the event
44
+ # @raise [ArgumentError] when no predicate block is provided
45
+ def initialize: () ?{ (?) -> untyped } -> void
46
+
47
+ # Whether this filter matches an event.
48
+ #
49
+ # @param event [ChangeEvent] event to test
50
+ # @return [Boolean]
51
+ def match?: (untyped event) -> untyped
52
+
53
+ alias =~ match?
54
+
55
+ # Compose this filter with another filter using logical AND.
56
+ #
57
+ # @param other [Filter] other filter
58
+ # @return [Filter]
59
+ def &: (untyped other) -> untyped
60
+
61
+ # Compose this filter with another filter using logical OR.
62
+ #
63
+ # @param other [Filter] other filter
64
+ # @return [Filter]
65
+ def |: (untyped other) -> untyped
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,44 @@
1
+ module CDC
2
+ module Core
3
+ # Canonical CDC operation names.
4
+ #
5
+ # Operations are represented as symbols to keep event objects small,
6
+ # immutable, and easy to compare. Use .normalize when accepting user input
7
+ # and .supported? when validating optional values.
8
+ module Operation
9
+ # Insert row operation.
10
+ INSERT: :insert
11
+
12
+ # Update row operation.
13
+ UPDATE: :update
14
+
15
+ # Delete row operation.
16
+ DELETE: :delete
17
+
18
+ # Truncate table operation.
19
+ TRUNCATE: :truncate
20
+
21
+ # Logical replication message operation.
22
+ MESSAGE: :message
23
+
24
+ # All operation names currently recognized by cdc-core.
25
+ SUPPORTED: untyped
26
+
27
+ # Minimal row-change operations used by the initial runtime surface.
28
+ MVP: untyped
29
+
30
+ # Convert an operation-like value into a supported operation symbol.
31
+ #
32
+ # @param operation [#to_sym] value to normalize
33
+ # @return [Symbol] one of SUPPORTED
34
+ # @raise [InvalidOperationError] when the operation is not supported
35
+ def self?.normalize: (untyped operation) -> untyped
36
+
37
+ # Check whether an operation-like value is supported.
38
+ #
39
+ # @param operation [#to_sym] value to check
40
+ # @return [Boolean] true when the value normalizes to a supported operation
41
+ def self?.supported?: (untyped operation) -> untyped
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ module CDC
2
+ module Core
3
+ # Connects filters with a processor to form an event-processing unit.
4
+ #
5
+ # A Pipeline first evaluates all filters. Matching events are handed to the
6
+ # processor, while filtered events produce skipped results. Processor errors
7
+ # are captured as failure results instead of escaping to the caller.
8
+ class Pipeline
9
+ @processor: untyped
10
+
11
+ @filters: untyped
12
+
13
+ # @return [#process] processor invoked for matching events
14
+ # @return [Array<Filter>] filters that must all match before processing
15
+ attr_reader processor: untyped
16
+
17
+ # @return [#process] processor invoked for matching events
18
+ # @return [Array<Filter>] filters that must all match before processing
19
+ attr_reader filters: untyped
20
+
21
+ # Build a pipeline.
22
+ #
23
+ # @param processor [#process] processor for matching events
24
+ # @param filters [Array<Filter>] filters applied before processing
25
+ def initialize: (processor: untyped, ?filters: untyped) -> void
26
+
27
+ # Process one event through the pipeline.
28
+ #
29
+ # @param event [ChangeEvent] event to process
30
+ # @return [ProcessorResult]
31
+ def process: (untyped event) -> untyped
32
+
33
+ # Process many events in order.
34
+ #
35
+ # @param events [Enumerable<ChangeEvent>] events to process
36
+ # @return [Array<ProcessorResult>]
37
+ def process_many: (untyped events) -> untyped
38
+
39
+ private
40
+
41
+ # Check whether every filter matches an event.
42
+ #
43
+ # @param event [ChangeEvent]
44
+ # @return [Boolean]
45
+ def matches?: (untyped event) -> untyped
46
+
47
+ # Normalize raw processor output into a ProcessorResult.
48
+ #
49
+ # @param result [Object] raw processor result
50
+ # @param event [ChangeEvent] processed event
51
+ # @return [ProcessorResult]
52
+ def normalize_result: (untyped result, untyped event) -> untyped
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ module CDC
2
+ module Core
3
+ # Base class for CDC event processors.
4
+ #
5
+ # Subclasses implement #process and may opt into Ractor-safe execution by
6
+ # calling .ractor_safe!. cdc-core itself does not schedule Ractors; it only
7
+ # records processor capabilities for runtime layers such as cdc-ractor.
8
+ class Processor
9
+ self.@ractor_safe: untyped
10
+
11
+ # Mark this processor class as safe to execute in Ractor-aware runtimes.
12
+ #
13
+ # @return [true]
14
+ def self.ractor_safe!: () -> untyped
15
+
16
+ # Whether this processor class has declared Ractor safety.
17
+ #
18
+ # @return [Boolean]
19
+ def self.ractor_safe?: () -> untyped
20
+
21
+ # Whether this processor instance is Ractor-safe.
22
+ #
23
+ # @return [Boolean]
24
+ def ractor_safe?: () -> untyped
25
+
26
+ # Process one event.
27
+ #
28
+ # Subclasses must override this method.
29
+ #
30
+ # @param _event [ChangeEvent] event to process
31
+ # @raise [NotImplementedError] when not implemented by a subclass
32
+ def process: (untyped _event) -> untyped
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ module CDC
2
+ module Core
3
+ # Result returned by processors and pipelines.
4
+ #
5
+ # ProcessorResult standardizes processor outcomes so callers can distinguish
6
+ # successful processing, skipped events, and failures without relying on
7
+ # processor-specific return values.
8
+ class ProcessorResult
9
+ @status: untyped
10
+
11
+ @event: untyped
12
+
13
+ @error: untyped
14
+
15
+ @metadata: untyped
16
+
17
+ # @return [Symbol] result status
18
+ # @return [ChangeEvent, nil] event associated with the result
19
+ # @return [Exception, nil] failure error, when status is :failure
20
+ # @return [EventMetadata] result metadata
21
+ attr_reader status: untyped
22
+
23
+ # @return [Symbol] result status
24
+ # @return [ChangeEvent, nil] event associated with the result
25
+ # @return [Exception, nil] failure error, when status is :failure
26
+ # @return [EventMetadata] result metadata
27
+ attr_reader event: untyped
28
+
29
+ # @return [Symbol] result status
30
+ # @return [ChangeEvent, nil] event associated with the result
31
+ # @return [Exception, nil] failure error, when status is :failure
32
+ # @return [EventMetadata] result metadata
33
+ attr_reader error: untyped
34
+
35
+ # @return [Symbol] result status
36
+ # @return [ChangeEvent, nil] event associated with the result
37
+ # @return [Exception, nil] failure error, when status is :failure
38
+ # @return [EventMetadata] result metadata
39
+ attr_reader metadata: untyped
40
+
41
+ # Build a successful result.
42
+ #
43
+ # @param event [ChangeEvent, nil] processed event
44
+ # @param metadata [Hash, EventMetadata] result metadata
45
+ # @return [ProcessorResult]
46
+ def self.success: (?untyped? event, ?metadata: ::Hash[untyped, untyped]) -> untyped
47
+
48
+ # Build a failure result.
49
+ #
50
+ # @param error [Exception] processor error
51
+ # @param event [ChangeEvent, nil] event being processed
52
+ # @param metadata [Hash, EventMetadata] result metadata
53
+ # @return [ProcessorResult]
54
+ def self.failure: (untyped error, ?event: untyped?, ?metadata: ::Hash[untyped, untyped]) -> untyped
55
+
56
+ # Build a skipped result.
57
+ #
58
+ # @param event [ChangeEvent, nil] skipped event
59
+ # @param metadata [Hash, EventMetadata] result metadata
60
+ # @return [ProcessorResult]
61
+ def self.skipped: (?untyped? event, ?metadata: ::Hash[untyped, untyped]) -> untyped
62
+
63
+ # Build a processor result with an explicit status.
64
+ #
65
+ # @param status [#to_sym] result status
66
+ # @param event [ChangeEvent, nil] associated event
67
+ # @param error [Exception, nil] associated failure
68
+ # @param metadata [Hash, EventMetadata] result metadata
69
+ def initialize: (untyped status, ?event: untyped?, ?error: untyped?, ?metadata: ::Hash[untyped, untyped]) -> void
70
+
71
+ # @return [Boolean] true when status is :success
72
+ def success?: () -> untyped
73
+
74
+ # @return [Boolean] true when status is :failure
75
+ def failure?: () -> untyped
76
+
77
+ # @return [Boolean] true when status is :skipped
78
+ def skipped?: () -> untyped
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,79 @@
1
+ module CDC
2
+ module Core
3
+ # Immutable group of change events committed in one transaction.
4
+ #
5
+ # TransactionEnvelope is useful when a downstream processor needs transaction
6
+ # boundaries instead of isolated row-level events. The contained events and
7
+ # metadata are Ractor-shareable when construction succeeds.
8
+ class TransactionEnvelope
9
+ @transaction_id: untyped
10
+
11
+ @events: untyped
12
+
13
+ @commit_lsn: untyped
14
+
15
+ @committed_at: untyped
16
+
17
+ @metadata: untyped
18
+
19
+ # @return [Object] transaction identifier
20
+ # @return [Array<ChangeEvent>] events committed by the transaction
21
+ # @return [String, nil] commit log sequence number
22
+ # @return [Time, nil] commit timestamp
23
+ # @return [EventMetadata] transaction metadata
24
+ attr_reader transaction_id: untyped
25
+
26
+ # @return [Object] transaction identifier
27
+ # @return [Array<ChangeEvent>] events committed by the transaction
28
+ # @return [String, nil] commit log sequence number
29
+ # @return [Time, nil] commit timestamp
30
+ # @return [EventMetadata] transaction metadata
31
+ attr_reader events: untyped
32
+
33
+ # @return [Object] transaction identifier
34
+ # @return [Array<ChangeEvent>] events committed by the transaction
35
+ # @return [String, nil] commit log sequence number
36
+ # @return [Time, nil] commit timestamp
37
+ # @return [EventMetadata] transaction metadata
38
+ attr_reader commit_lsn: untyped
39
+
40
+ # @return [Object] transaction identifier
41
+ # @return [Array<ChangeEvent>] events committed by the transaction
42
+ # @return [String, nil] commit log sequence number
43
+ # @return [Time, nil] commit timestamp
44
+ # @return [EventMetadata] transaction metadata
45
+ attr_reader committed_at: untyped
46
+
47
+ # @return [Object] transaction identifier
48
+ # @return [Array<ChangeEvent>] events committed by the transaction
49
+ # @return [String, nil] commit log sequence number
50
+ # @return [Time, nil] commit timestamp
51
+ # @return [EventMetadata] transaction metadata
52
+ attr_reader metadata: untyped
53
+
54
+ # Build a transaction envelope.
55
+ #
56
+ # @param transaction_id [Object] upstream transaction identifier
57
+ # @param events [Array<ChangeEvent>] committed events
58
+ # @param commit_lsn [#to_s, nil] commit log sequence number
59
+ # @param committed_at [Time, nil] commit timestamp
60
+ # @param metadata [Hash, EventMetadata] transaction metadata
61
+ def initialize: (transaction_id: untyped, events: untyped, ?commit_lsn: untyped?, ?committed_at: untyped?, ?metadata: ::Hash[untyped, untyped]) -> void
62
+
63
+ # Whether the envelope has no events.
64
+ #
65
+ # @return [Boolean]
66
+ def empty?: () -> untyped
67
+
68
+ # Number of events in the envelope.
69
+ #
70
+ # @return [Integer]
71
+ def size: () -> untyped
72
+
73
+ # Convert the transaction envelope into a Ractor-shareable hash.
74
+ #
75
+ # @return [Hash{String=>Object,nil}]
76
+ def to_h: () -> untyped
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,6 @@
1
+ module CDC
2
+ module Core
3
+ # Current gem version.
4
+ VERSION: "0.1.0"
5
+ end
6
+ end
data/sig/cdc/core.rbs CHANGED
@@ -1,6 +1,11 @@
1
- module Cdc
1
+ # Top-level namespace for Change Data Capture libraries.
2
+ module CDC
3
+ # Database-agnostic Change Data Capture domain primitives.
4
+ #
5
+ # CDC::Core intentionally contains only lightweight runtime abstractions:
6
+ # events, metadata, processors, filters, pipelines, and processor results.
7
+ # Transport, PostgreSQL protocol parsing, and value decoding live in sibling
8
+ # gems so this layer can remain independently useful.
2
9
  module Core
3
- VERSION: String
4
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
10
  end
6
11
  end
data/sig/cdc_core.rbs ADDED
File without changes
metadata CHANGED
@@ -1,15 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cdc-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken C. Demanawa
8
- bindir: exe
8
+ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
- description: Write a longer description or delete this line.
12
+ description: 'CDC Core provides immutable, Ractor-safe Change Data Capture domain
13
+ objects, processor contracts, filters, and pipeline primitives.
14
+
15
+ '
13
16
  email:
14
17
  - kenneth.c.demanawa@gmail.com
15
18
  executables: []
@@ -17,13 +20,36 @@ extensions: []
17
20
  extra_rdoc_files: []
18
21
  files:
19
22
  - CHANGELOG.md
20
- - CODE_OF_CONDUCT.md
21
23
  - LICENSE.txt
22
24
  - README.md
23
- - Rakefile
24
25
  - lib/cdc/core.rb
26
+ - lib/cdc/core/change_event.rb
27
+ - lib/cdc/core/column_change.rb
28
+ - lib/cdc/core/composite_processor.rb
29
+ - lib/cdc/core/errors.rb
30
+ - lib/cdc/core/event_metadata.rb
31
+ - lib/cdc/core/filter.rb
32
+ - lib/cdc/core/operation.rb
33
+ - lib/cdc/core/pipeline.rb
34
+ - lib/cdc/core/processor.rb
35
+ - lib/cdc/core/processor_result.rb
36
+ - lib/cdc/core/transaction_envelope.rb
25
37
  - lib/cdc/core/version.rb
38
+ - lib/cdc_core.rb
26
39
  - sig/cdc/core.rbs
40
+ - sig/cdc/core/change_event.rbs
41
+ - sig/cdc/core/column_change.rbs
42
+ - sig/cdc/core/composite_processor.rbs
43
+ - sig/cdc/core/errors.rbs
44
+ - sig/cdc/core/event_metadata.rbs
45
+ - sig/cdc/core/filter.rbs
46
+ - sig/cdc/core/operation.rbs
47
+ - sig/cdc/core/pipeline.rbs
48
+ - sig/cdc/core/processor.rbs
49
+ - sig/cdc/core/processor_result.rbs
50
+ - sig/cdc/core/transaction_envelope.rbs
51
+ - sig/cdc/core/version.rbs
52
+ - sig/cdc_core.rbs
27
53
  homepage: https://github.com/kanutocd/cdc-core
28
54
  licenses:
29
55
  - MIT
@@ -31,6 +57,7 @@ metadata:
31
57
  homepage_uri: https://github.com/kanutocd/cdc-core
32
58
  source_code_uri: https://github.com/kanutocd/cdc-core
33
59
  changelog_uri: https://github.com/kanutocd/cdc-core/blob/main/CHANGELOG.md
60
+ documentation_uri: https://kanutocd.github.io/cdc-core/
34
61
  rubygems_mfa_required: 'true'
35
62
  rdoc_options: []
36
63
  require_paths:
@@ -46,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
73
  - !ruby/object:Gem::Version
47
74
  version: '0'
48
75
  requirements: []
49
- rubygems_version: 4.0.10
76
+ rubygems_version: 3.6.9
50
77
  specification_version: 4
51
- summary: Write a short summary, because RubyGems requires one.
78
+ summary: Database-agnostic Change Data Capture domain primitives for Ruby.
52
79
  test_files: []
data/CODE_OF_CONDUCT.md DELETED
@@ -1,10 +0,0 @@
1
- # Code of Conduct
2
-
3
- "cdc-core" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
4
-
5
- * Participants will be tolerant of opposing views.
6
- * Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
- * When interpreting the words and actions of others, participants should always assume good intentions.
8
- * Behaviour which can be reasonably considered harassment will not be tolerated.
9
-
10
- If you have any concerns about behaviour within this project, please contact us at ["kenneth.c.demanawa@gmail.com"](mailto:"kenneth.c.demanawa@gmail.com").
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]