whodunit-chronicles 0.3.0 → 0.4.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -226
  3. data/LICENSE +1 -1
  4. data/README.md +96 -599
  5. data/exe/whodunit-chronicles +6 -0
  6. data/lib/whodunit/chronicles/chronicler.rb +62 -0
  7. data/lib/whodunit/chronicles/cli.rb +131 -0
  8. data/lib/whodunit/chronicles/errors.rb +7 -33
  9. data/lib/whodunit/chronicles/ledger.rb +69 -0
  10. data/lib/whodunit/chronicles/ledger_entry.rb +143 -0
  11. data/lib/whodunit/chronicles/ledger_factory.rb +66 -0
  12. data/lib/whodunit/chronicles/ledgers/file_ledger.rb +56 -0
  13. data/lib/whodunit/chronicles/ledgers/memory_ledger.rb +29 -0
  14. data/lib/whodunit/chronicles/ledgers/sqlite_ledger.rb +172 -0
  15. data/lib/whodunit/chronicles/version.rb +2 -1
  16. data/lib/whodunit/chronicles.rb +12 -65
  17. data/lib/whodunit-chronicles.rb +0 -1
  18. data/sig/whodunit/chronicles/chronicler.rbs +14 -0
  19. data/sig/whodunit/chronicles/cli.rbs +17 -0
  20. data/sig/whodunit/chronicles/errors.rbs +15 -0
  21. data/sig/whodunit/chronicles/ledger.rbs +13 -0
  22. data/sig/whodunit/chronicles/ledger_entry.rbs +62 -0
  23. data/sig/whodunit/chronicles/ledger_factory.rbs +14 -0
  24. data/sig/whodunit/chronicles/ledgers/file_ledger.rbs +14 -0
  25. data/sig/whodunit/chronicles/ledgers/memory_ledger.rbs +12 -0
  26. data/sig/whodunit/chronicles/ledgers/sqlite_ledger.rbs +30 -0
  27. data/sig/whodunit/chronicles.rbs +5 -0
  28. metadata +40 -326
  29. data/.codeclimate.yml +0 -50
  30. data/.rubocop.yml +0 -93
  31. data/.yardopts +0 -14
  32. data/CODE_OF_CONDUCT.md +0 -132
  33. data/CONTRIBUTING.md +0 -186
  34. data/Rakefile +0 -18
  35. data/docker/mysql/init.sql +0 -33
  36. data/docker/postgres/init.sql +0 -40
  37. data/docker-compose.yml +0 -138
  38. data/examples/images/campaign-performance-analytics.png +0 -0
  39. data/examples/images/candidate-journey-analytics.png +0 -0
  40. data/examples/images/recruitment-funnel-analytics.png +0 -0
  41. data/lib/.gitkeep +0 -0
  42. data/lib/whodunit/chronicles/adapter_loader.rb +0 -69
  43. data/lib/whodunit/chronicles/adapters/mysql.rb +0 -261
  44. data/lib/whodunit/chronicles/adapters/postgresql.rb +0 -278
  45. data/lib/whodunit/chronicles/change_event.rb +0 -201
  46. data/lib/whodunit/chronicles/composite_processor.rb +0 -86
  47. data/lib/whodunit/chronicles/configuration.rb +0 -112
  48. data/lib/whodunit/chronicles/connection.rb +0 -88
  49. data/lib/whodunit/chronicles/persistence.rb +0 -129
  50. data/lib/whodunit/chronicles/processor.rb +0 -127
  51. data/lib/whodunit/chronicles/service.rb +0 -207
  52. data/lib/whodunit/chronicles/stream_adapter.rb +0 -91
  53. data/lib/whodunit/chronicles/table.rb +0 -120
  54. data/whodunit-chronicles.gemspec +0 -79
data/CODE_OF_CONDUCT.md DELETED
@@ -1,132 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our
6
- community a harassment-free experience for everyone, regardless of age, body
7
- size, visible or invisible disability, ethnicity, sex characteristics, gender
8
- identity and expression, level of experience, education, socio-economic status,
9
- nationality, personal appearance, race, caste, color, religion, or sexual
10
- identity and orientation.
11
-
12
- We pledge to act and interact in ways that contribute to an open, welcoming,
13
- diverse, inclusive, and healthy community.
14
-
15
- ## Our Standards
16
-
17
- Examples of behavior that contributes to a positive environment for our
18
- community include:
19
-
20
- * Demonstrating empathy and kindness toward other people
21
- * Being respectful of differing opinions, viewpoints, and experiences
22
- * Giving and gracefully accepting constructive feedback
23
- * Accepting responsibility and apologizing to those affected by our mistakes,
24
- and learning from the experience
25
- * Focusing on what is best not just for us as individuals, but for the overall
26
- community
27
-
28
- Examples of unacceptable behavior include:
29
-
30
- * The use of sexualized language or imagery, and sexual attention or advances of
31
- any kind
32
- * Trolling, insulting or derogatory comments, and personal or political attacks
33
- * Public or private harassment
34
- * Publishing others' private information, such as a physical or email address,
35
- without their explicit permission
36
- * Other conduct which could reasonably be considered inappropriate in a
37
- professional setting
38
-
39
- ## Enforcement Responsibilities
40
-
41
- Community leaders are responsible for clarifying and enforcing our standards of
42
- acceptable behavior and will take appropriate and fair corrective action in
43
- response to any behavior that they deem inappropriate, threatening, offensive,
44
- or harmful.
45
-
46
- Community leaders have the right and responsibility to remove, edit, or reject
47
- comments, commits, code, wiki edits, issues, and other contributions that are
48
- not aligned to this Code of Conduct, and will communicate reasons for moderation
49
- decisions when appropriate.
50
-
51
- ## Scope
52
-
53
- This Code of Conduct applies within all community spaces, and also applies when
54
- an individual is officially representing the community in public spaces.
55
- Examples of representing our community include using an official email address,
56
- posting via an official social media account, or acting as an appointed
57
- representative at an online or offline event.
58
-
59
- ## Enforcement
60
-
61
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
- reported to the community leaders responsible for enforcement at
63
- [INSERT CONTACT METHOD].
64
- All complaints will be reviewed and investigated promptly and fairly.
65
-
66
- All community leaders are obligated to respect the privacy and security of the
67
- reporter of any incident.
68
-
69
- ## Enforcement Guidelines
70
-
71
- Community leaders will follow these Community Impact Guidelines in determining
72
- the consequences for any action they deem in violation of this Code of Conduct:
73
-
74
- ### 1. Correction
75
-
76
- **Community Impact**: Use of inappropriate language or other behavior deemed
77
- unprofessional or unwelcome in the community.
78
-
79
- **Consequence**: A private, written warning from community leaders, providing
80
- clarity around the nature of the violation and an explanation of why the
81
- behavior was inappropriate. A public apology may be requested.
82
-
83
- ### 2. Warning
84
-
85
- **Community Impact**: A violation through a single incident or series of
86
- actions.
87
-
88
- **Consequence**: A warning with consequences for continued behavior. No
89
- interaction with the people involved, including unsolicited interaction with
90
- those enforcing the Code of Conduct, for a specified period of time. This
91
- includes avoiding interactions in community spaces as well as external channels
92
- like social media. Violating these terms may lead to a temporary or permanent
93
- ban.
94
-
95
- ### 3. Temporary Ban
96
-
97
- **Community Impact**: A serious violation of community standards, including
98
- sustained inappropriate behavior.
99
-
100
- **Consequence**: A temporary ban from any sort of interaction or public
101
- communication with the community for a specified period of time. No public or
102
- private interaction with the people involved, including unsolicited interaction
103
- with those enforcing the Code of Conduct, is allowed during this period.
104
- Violating these terms may lead to a permanent ban.
105
-
106
- ### 4. Permanent Ban
107
-
108
- **Community Impact**: Demonstrating a pattern of violation of community
109
- standards, including sustained inappropriate behavior, harassment of an
110
- individual, or aggression toward or disparagement of classes of individuals.
111
-
112
- **Consequence**: A permanent ban from any sort of public interaction within the
113
- community.
114
-
115
- ## Attribution
116
-
117
- This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
- version 2.1, available at
119
- [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
-
121
- Community Impact Guidelines were inspired by
122
- [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
-
124
- For answers to common questions about this code of conduct, see the FAQ at
125
- [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
- [https://www.contributor-covenant.org/translations][translations].
127
-
128
- [homepage]: https://www.contributor-covenant.org
129
- [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
- [Mozilla CoC]: https://github.com/mozilla/diversity
131
- [FAQ]: https://www.contributor-covenant.org/faq
132
- [translations]: https://www.contributor-covenant.org/translations
data/CONTRIBUTING.md DELETED
@@ -1,186 +0,0 @@
1
- # Contributing to Whodunit Chronicles
2
-
3
- Thank you for your interest in contributing! This document walks you through getting a working development environment set up, running the test suite, and submitting a pull request.
4
-
5
- ---
6
-
7
- ## Table of Contents
8
-
9
- 1. [Prerequisites](#prerequisites)
10
- 2. [Setting Up Your Environment](#setting-up-your-environment)
11
- 3. [Database Setup](#database-setup)
12
- 4. [Running the Tests](#running-the-tests)
13
- 5. [Code Style](#code-style)
14
- 6. [Opening a Pull Request](#opening-a-pull-request)
15
- 7. [Contributing Custom Processors](#contributing-custom-processors)
16
-
17
- ---
18
-
19
- ## Prerequisites
20
-
21
- | Tool | Minimum Version | Notes |
22
- |------|----------------|-------|
23
- | Ruby | 3.1.0 | `rbenv` or `asdf` recommended |
24
- | Docker | 24+ | For running test databases |
25
- | Docker Compose | v2 plugin | Bundled with Docker Desktop |
26
- | Git | Any recent | |
27
-
28
- ---
29
-
30
- ## Setting Up Your Environment
31
-
32
- ```bash
33
- git clone https://github.com/kanutocd/whodunit-chronicles.git
34
- cd whodunit-chronicles
35
-
36
- # Install dependencies
37
- bundle install
38
-
39
- # Verify the console works
40
- bundle exec bin/console
41
- ```
42
-
43
- ---
44
-
45
- ## Database Setup
46
-
47
- Chronicles streams directly from PostgreSQL logical replication and MySQL/MariaDB binary logs. The test suite needs real database processes — no SQLite or in-memory substitute.
48
-
49
- **Start all test databases with Docker Compose:**
50
-
51
- ```bash
52
- docker compose up -d
53
- ```
54
-
55
- This starts:
56
-
57
- | Service | Port | Purpose |
58
- |---------|------|---------|
59
- | PostgreSQL 16 (wal_level=logical) | 5432 | Source DB for pg tests |
60
- | PostgreSQL 16 (audit store) | 5433 | Audit DB for pg tests |
61
- | MySQL 8.0 (binlog ROW format) | 3306 | Source DB for MySQL tests |
62
- | MySQL 8.0 (audit store) | 3307 | Audit DB for MySQL tests |
63
- | MariaDB 11 (binlog ROW format) | 3308 | Source DB for MariaDB tests |
64
-
65
- Wait for all services to be healthy before running tests:
66
-
67
- ```bash
68
- docker compose ps # all should show "(healthy)"
69
- ```
70
-
71
- ### PostgreSQL — enabling logical replication
72
-
73
- The Docker image is pre-configured (`wal_level=logical`). If you're using a system Postgres instead, add to `postgresql.conf`:
74
-
75
- ```
76
- wal_level = logical
77
- max_replication_slots = 10
78
- max_wal_senders = 10
79
- ```
80
-
81
- Then restart Postgres and grant the replication role to your user:
82
-
83
- ```sql
84
- ALTER ROLE your_user WITH REPLICATION;
85
- ```
86
-
87
- ### MySQL — enabling binary logging
88
-
89
- The Docker image launches with `--binlog-format=ROW --binlog-row-image=FULL`. If using a system MySQL, add to `my.cnf`:
90
-
91
- ```ini
92
- [mysqld]
93
- server-id = 1
94
- log-bin = mysql-bin
95
- binlog-format = ROW
96
- binlog-row-image = FULL
97
- ```
98
-
99
- ---
100
-
101
- ## Running the Tests
102
-
103
- ```bash
104
- # Full suite
105
- bundle exec rake test
106
-
107
- # Single file
108
- bundle exec ruby test/whodunit/chronicles/composite_processor_test.rb
109
-
110
- # With coverage report
111
- bundle exec rake test
112
- open coverage/index.html
113
-
114
- # Code style
115
- bundle exec rubocop
116
-
117
- # Security scan
118
- bundle exec bundler-audit check --update
119
- bundle exec brakeman
120
- ```
121
-
122
- ### Environment variables for test databases
123
-
124
- The test suite reads these environment variables (defaults match the Docker Compose setup):
125
-
126
- | Variable | Default |
127
- |----------|---------|
128
- | `CHRONICLES_PG_URL` | `postgresql://chronicles:chronicles@localhost/chronicles_test` |
129
- | `CHRONICLES_PG_AUDIT_URL` | `postgresql://chronicles:chronicles@localhost:5433/chronicles_audit_test` |
130
- | `CHRONICLES_MYSQL_URL` | `mysql://chronicles:chronicles@localhost:3306/chronicles_test` |
131
- | `CHRONICLES_MYSQL_AUDIT_URL` | `mysql://chronicles:chronicles@localhost:3307/chronicles_audit_test` |
132
-
133
- ---
134
-
135
- ## Code Style
136
-
137
- - All files must have `# frozen_string_literal: true`
138
- - Public methods require YARD documentation
139
- - Follow the existing RuboCop configuration (`.rubocop.yml`)
140
- - Tests live in `test/` and use Minitest
141
-
142
- ---
143
-
144
- ## Opening a Pull Request
145
-
146
- 1. Fork the repository and create a feature branch:
147
- ```bash
148
- git checkout -b feature/my-improvement
149
- ```
150
-
151
- 2. Make your changes with tests. Coverage should not drop below 90%.
152
-
153
- 3. Run the full quality check before pushing:
154
- ```bash
155
- bundle exec rake test
156
- bundle exec rubocop
157
- bundle exec bundler-audit check --update
158
- ```
159
-
160
- 4. Push and open a PR with:
161
- - A clear description of what changed and why
162
- - Any migration steps if you changed configuration
163
- - A note on which databases you tested against
164
-
165
- ---
166
-
167
- ## Contributing Custom Processors
168
-
169
- The best contributions are custom processors for real-world domains. Here are domains we'd love to see:
170
-
171
- - **E-commerce** — order tracking, inventory changes, cart events
172
- - **Financial services** — transaction monitoring, compliance audit trails
173
- - **Healthcare** — patient record changes, HIPAA-relevant audit events
174
- - **SaaS** — feature flag changes, subscription events, seat management
175
- - **Education** — student progress, grade changes, enrollment events
176
-
177
- A good processor contribution includes:
178
-
179
- 1. The processor class in `lib/whodunit/chronicles/processors/`
180
- 2. Tests in `test/whodunit/chronicles/processors/`
181
- 3. A usage example in `examples/`
182
- 4. An entry in `CHANGELOG.md`
183
-
184
- ---
185
-
186
- Questions? Open an issue or start a discussion on GitHub.
data/Rakefile DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rake/testtask'
5
- require 'rubocop/rake_task'
6
-
7
- Rake::TestTask.new(:test) do |t|
8
- t.libs << 'test'
9
- t.libs << 'lib'
10
- t.test_files = FileList['test/**/*_test.rb']
11
- end
12
-
13
- RuboCop::RakeTask.new
14
-
15
- desc 'Run all checks (rubocop, tests)'
16
- task check: %i[rubocop test]
17
-
18
- task default: :check
@@ -1,33 +0,0 @@
1
- -- docker/mysql/init.sql
2
- -- Run once when the MySQL/MariaDB container is first created.
3
-
4
- USE chronicles_test;
5
-
6
- CREATE TABLE IF NOT EXISTS users (
7
- id INT AUTO_INCREMENT PRIMARY KEY,
8
- name VARCHAR(255) NOT NULL,
9
- email VARCHAR(255) NOT NULL UNIQUE,
10
- creator_id INT DEFAULT NULL,
11
- updater_id INT DEFAULT NULL,
12
- deleter_id INT DEFAULT NULL,
13
- created_at DATETIME(6) NOT NULL DEFAULT NOW(6),
14
- updated_at DATETIME(6) NOT NULL DEFAULT NOW(6) ON UPDATE NOW(6)
15
- );
16
-
17
- CREATE TABLE IF NOT EXISTS posts (
18
- id INT AUTO_INCREMENT PRIMARY KEY,
19
- user_id INT NOT NULL,
20
- title VARCHAR(500) NOT NULL,
21
- body TEXT,
22
- status VARCHAR(50) DEFAULT 'draft',
23
- creator_id INT DEFAULT NULL,
24
- updater_id INT DEFAULT NULL,
25
- deleter_id INT DEFAULT NULL,
26
- created_at DATETIME(6) NOT NULL DEFAULT NOW(6),
27
- updated_at DATETIME(6) NOT NULL DEFAULT NOW(6) ON UPDATE NOW(6),
28
- FOREIGN KEY (user_id) REFERENCES users (id)
29
- );
30
-
31
- -- Grant replication privileges to the chronicles user
32
- GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'chronicles'@'%';
33
- FLUSH PRIVILEGES;
@@ -1,40 +0,0 @@
1
- -- docker/postgres/init.sql
2
- -- Run once when the container is first created.
3
- -- Sets up the replication role and test tables used by the test suite.
4
-
5
- -- Allow the chronicles user to create replication slots and use logical replication
6
- ALTER ROLE chronicles WITH REPLICATION;
7
-
8
- -- Grant superuser for test convenience (never do this in production)
9
- -- Remove this line and grant only necessary permissions for a tighter setup.
10
- ALTER ROLE chronicles WITH SUPERUSER;
11
-
12
- -- Create the test tables Chronicles will stream from
13
- \c chronicles_test
14
-
15
- CREATE TABLE IF NOT EXISTS users (
16
- id SERIAL PRIMARY KEY,
17
- name VARCHAR(255) NOT NULL,
18
- email VARCHAR(255) NOT NULL UNIQUE,
19
- creator_id INTEGER,
20
- updater_id INTEGER,
21
- deleter_id INTEGER,
22
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
23
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
24
- );
25
-
26
- CREATE TABLE IF NOT EXISTS posts (
27
- id SERIAL PRIMARY KEY,
28
- user_id INTEGER NOT NULL REFERENCES users(id),
29
- title VARCHAR(500) NOT NULL,
30
- body TEXT,
31
- status VARCHAR(50) DEFAULT 'draft',
32
- creator_id INTEGER,
33
- updater_id INTEGER,
34
- deleter_id INTEGER,
35
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
36
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
37
- );
38
-
39
- -- Publication used by the Chronicles PostgreSQL adapter
40
- CREATE PUBLICATION chronicles_test_pub FOR TABLE users, posts;
data/docker-compose.yml DELETED
@@ -1,138 +0,0 @@
1
- name: whodunit-chronicles
2
-
3
- # ──────────────────────────────────────────────────────────────────────────────
4
- # Local development + CI test databases
5
- #
6
- # Quickstart:
7
- # docker compose up -d
8
- # bundle exec rake test
9
- #
10
- # Stop + wipe volumes:
11
- # docker compose down -v
12
- # ──────────────────────────────────────────────────────────────────────────────
13
-
14
- services:
15
- # ── PostgreSQL (logical replication enabled) ─────────────────────────────
16
- postgres:
17
- image: postgres:16-alpine
18
- environment:
19
- POSTGRES_USER: chronicles
20
- POSTGRES_PASSWORD: chronicles
21
- POSTGRES_DB: chronicles_test
22
- ports:
23
- - "${POSTGRES_PORT:-5432}:5432"
24
- command: >
25
- postgres
26
- -c wal_level=logical
27
- -c max_replication_slots=10
28
- -c max_wal_senders=10
29
- -c log_replication_commands=on
30
- volumes:
31
- - pg_data:/var/lib/postgresql/data
32
- - ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
33
- healthcheck:
34
- test: ["CMD-SHELL", "pg_isready -U chronicles -d chronicles_test"]
35
- interval: 5s
36
- timeout: 5s
37
- retries: 10
38
- start_period: 10s
39
-
40
- # Separate audit database (mirrors production split)
41
- postgres_audit:
42
- image: postgres:16-alpine
43
- environment:
44
- POSTGRES_USER: chronicles
45
- POSTGRES_PASSWORD: chronicles
46
- POSTGRES_DB: chronicles_audit_test
47
- ports:
48
- - "${POSTGRES_AUDIT_PORT:-5433}:5432"
49
- volumes:
50
- - pg_audit_data:/var/lib/postgresql/data
51
- healthcheck:
52
- test: ["CMD-SHELL", "pg_isready -U chronicles -d chronicles_audit_test"]
53
- interval: 5s
54
- timeout: 5s
55
- retries: 10
56
- start_period: 10s
57
-
58
- # ── MySQL (binary logging enabled) ───────────────────────────────────────
59
- mysql:
60
- image: mysql:8.0
61
- environment:
62
- MYSQL_ROOT_PASSWORD: chronicles_root
63
- MYSQL_USER: chronicles
64
- MYSQL_PASSWORD: chronicles
65
- MYSQL_DATABASE: chronicles_test
66
- ports:
67
- - "3306:3306"
68
- command: >
69
- mysqld
70
- --server-id=1
71
- --log-bin=mysql-bin
72
- --binlog-format=ROW
73
- --binlog-row-image=FULL
74
- --expire-logs-days=1
75
- --gtid-mode=ON
76
- --enforce-gtid-consistency=ON
77
- volumes:
78
- - mysql_data:/var/lib/mysql
79
- - ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
80
- healthcheck:
81
- test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "chronicles", "-pchronicles"]
82
- interval: 5s
83
- timeout: 5s
84
- retries: 15
85
- start_period: 30s
86
-
87
- # Separate audit database for MySQL
88
- mysql_audit:
89
- image: mysql:8.0
90
- environment:
91
- MYSQL_ROOT_PASSWORD: chronicles_root
92
- MYSQL_USER: chronicles
93
- MYSQL_PASSWORD: chronicles
94
- MYSQL_DATABASE: chronicles_audit_test
95
- ports:
96
- - "3307:3306"
97
- volumes:
98
- - mysql_audit_data:/var/lib/mysql
99
- healthcheck:
100
- test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "chronicles", "-pchronicles"]
101
- interval: 5s
102
- timeout: 5s
103
- retries: 15
104
- start_period: 30s
105
-
106
- # ── MariaDB (binary logging enabled) ─────────────────────────────────────
107
- mariadb:
108
- image: mariadb:11
109
- environment:
110
- MARIADB_ROOT_PASSWORD: chronicles_root
111
- MARIADB_USER: chronicles
112
- MARIADB_PASSWORD: chronicles
113
- MARIADB_DATABASE: chronicles_test
114
- ports:
115
- - "3308:3306"
116
- command: >
117
- mariadbd
118
- --server-id=2
119
- --log-bin=mariadb-bin
120
- --binlog-format=ROW
121
- --binlog-row-image=FULL
122
- --expire-logs-days=1
123
- volumes:
124
- - mariadb_data:/var/lib/mysql
125
- - ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
126
- healthcheck:
127
- test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
128
- interval: 5s
129
- timeout: 5s
130
- retries: 15
131
- start_period: 30s
132
-
133
- volumes:
134
- pg_data:
135
- pg_audit_data:
136
- mysql_data:
137
- mysql_audit_data:
138
- mariadb_data:
data/lib/.gitkeep DELETED
File without changes
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Whodunit
4
- module Chronicles
5
- # Lazily loads the correct database adapter gem at runtime.
6
- #
7
- # This avoids forcing all users to install both `pg` and `trilogy`
8
- # regardless of which database they actually use.
9
- #
10
- # @example
11
- # adapter = AdapterLoader.load(:postgresql)
12
- # adapter = AdapterLoader.load(:mysql)
13
- # adapter = AdapterLoader.load(:mariadb)
14
- module AdapterLoader
15
- # Map of adapter type symbols to their required gem and class path.
16
- ADAPTER_REGISTRY = {
17
- postgresql: {
18
- gem: 'pg',
19
- require: 'whodunit/chronicles/adapters/postgresql',
20
- class: 'Whodunit::Chronicles::Adapters::PostgreSQL',
21
- hint: "Add `gem 'pg', '~> 1.5'` to your Gemfile.",
22
- },
23
- mysql: {
24
- gem: 'trilogy',
25
- require: 'whodunit/chronicles/adapters/mysql',
26
- class: 'Whodunit::Chronicles::Adapters::MySQL',
27
- hint: "Add `gem 'trilogy', '~> 2.9'` to your Gemfile.",
28
- },
29
- mariadb: {
30
- gem: 'trilogy',
31
- require: 'whodunit/chronicles/adapters/mysql',
32
- class: 'Whodunit::Chronicles::Adapters::MySQL',
33
- hint: "Add `gem 'trilogy', '~> 2.9'` to your Gemfile.",
34
- },
35
- }.freeze
36
-
37
- # Load and instantiate an adapter by type.
38
- #
39
- # @param type [Symbol] one of :postgresql, :mysql, :mariadb
40
- # @param options [Hash] options forwarded to the adapter constructor
41
- # @return [Adapters::Base] the instantiated adapter
42
- # @raise [Whodunit::Chronicles::ConfigurationError] for unknown adapter types
43
- # @raise [Whodunit::Chronicles::AdapterLoadError] when the required gem is missing
44
- def self.load(type, **)
45
- config = ADAPTER_REGISTRY[type.to_sym]
46
-
47
- unless config
48
- known = ADAPTER_REGISTRY.keys.map(&:inspect).join(', ')
49
- raise ConfigurationError,
50
- "Unknown adapter type #{type.inspect}. Known adapters: #{known}"
51
- end
52
-
53
- load_gem!(config)
54
- require config[:require]
55
- Object.const_get(config[:class]).new(**)
56
- rescue LoadError => e
57
- raise AdapterLoadError,
58
- "Could not load the '#{config[:gem]}' gem required for the " \
59
- "#{type} adapter.\n#{config[:hint]}\nOriginal error: #{e.message}"
60
- end
61
-
62
- # @api private
63
- def self.load_gem!(config)
64
- require config[:gem]
65
- end
66
- private_class_method :load_gem!
67
- end
68
- end
69
- end