activerecord-postgresql-branched 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 39dd95c063dde3c85a789ac3d6b5a0775bd0c95e39b7e68f2a2c33f9d50b645f
4
+ data.tar.gz: 2eaf350fe45a3cbb60aa16c2024c51af3e219ac99f8bc3e02e46fef4c68af2ec
5
+ SHA512:
6
+ metadata.gz: 5b9b1813347f21ce285180b6ac2e9f3a88e1e752ec9521fc615dcd7ee8392310b3ac81c60875cdc4861b6c86db0497c0274d8a405e070e38ae6ac2450ec28592
7
+ data.tar.gz: 0fa9c515e10ff4a8cdf21537b5b271198f8b804df8e14fba9ea06afdb830052423b7c014781476fd6600da8188009a89298381aa888ead775ce8bf57aaf6129f
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial release.
6
+
7
+ - PostgreSQL adapter that isolates each git branch in its own Postgres schema
8
+ - Shadow rule: automatically copies public tables before branch-local DDL
9
+ - Clean `schema.rb` output with no branch schema references
10
+ - `schema_migrations` and `ar_internal_metadata` isolated per branch
11
+ - Schema names truncated to 63 bytes with collision-safe hashing
12
+ - Rake tasks: `db:branch:reset`, `discard`, `list`, `diff`, `prune`
13
+ - Branch override via `branch_override` config or `PGBRANCH` env var
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carl Dawson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # activerecord-postgresql-branched
2
+
3
+ Database isolation for parallel development. Each git branch (or agent) gets its own Postgres schema. Migrations run in isolation. Nobody steps on anyone else's work.
4
+
5
+ Built for teams running multiple AI coding agents in parallel, each on its own worktree, all sharing one database.
6
+
7
+ ## The problem
8
+
9
+ You have three agents working simultaneously in three worktrees:
10
+
11
+ - `agent-0` adds a `payments` table and alters `users`
12
+ - `agent-1` adds a `bio` column to `users`
13
+ - `agent-2` drops a legacy table and adds indexes
14
+
15
+ They share one dev database. Every migration collides. Every agent breaks every other agent. You spend more time untangling the database than reviewing the code.
16
+
17
+ This adapter makes branching the database as cheap as branching the code.
18
+
19
+ ## Installation
20
+
21
+ ```ruby
22
+ # Gemfile
23
+ gem 'activerecord-postgresql-branched'
24
+ ```
25
+
26
+ ```yaml
27
+ # config/database.yml
28
+ development:
29
+ adapter: postgresql_branched
30
+ database: myapp_development
31
+ ```
32
+
33
+ That's it. No Postgres extensions, no environment variables, no initializers.
34
+
35
+ ## How it works
36
+
37
+ On connection, the adapter reads the current git branch, creates a dedicated Postgres schema for it, and sets `search_path`:
38
+
39
+ ```
40
+ git branch: feature/payments
41
+ schema: branch_feature_payments
42
+ search_path: branch_feature_payments, public
43
+ ```
44
+
45
+ New tables go into the branch schema. Queries against existing tables fall through to `public` via standard Postgres name resolution.
46
+
47
+ When a migration modifies a table that exists in `public`, the adapter shadows it first -- copies the table and its data into the branch schema, then applies the DDL to the copy. The public table is never touched.
48
+
49
+ On the primary branch (`main` by default), the adapter stands aside entirely. Migrations land in `public` as normal. When `public` advances, every branch sees the changes immediately via `search_path` fallthrough.
50
+
51
+ ## Agentic workflows
52
+
53
+ Give each agent its own branch identity:
54
+
55
+ ```yaml
56
+ # config/database.yml
57
+ development:
58
+ adapter: postgresql_branched
59
+ database: myapp_development
60
+ branch_override: <%= ENV.fetch("AGENT_BRANCH", nil) %>
61
+ ```
62
+
63
+ ```bash
64
+ # Launch agents with isolated schemas
65
+ AGENT_BRANCH=agent-0 bundle exec rails ...
66
+ AGENT_BRANCH=agent-1 bundle exec rails ...
67
+ AGENT_BRANCH=agent-2 bundle exec rails ...
68
+ ```
69
+
70
+ Or in a worktree-per-agent setup, each worktree is on its own git branch and the adapter picks it up automatically. No configuration needed beyond the adapter name.
71
+
72
+ ### Cleanup
73
+
74
+ After agents finish:
75
+
76
+ ```bash
77
+ rails db:branch:prune
78
+ ```
79
+
80
+ Compares branch schemas against `git branch --list` and drops any that no longer have a corresponding local branch. One command, all stale schemas gone.
81
+
82
+ For specific branches:
83
+
84
+ ```bash
85
+ rails db:branch:discard BRANCH=agent-0
86
+ ```
87
+
88
+ ## Rake tasks
89
+
90
+ ```bash
91
+ rails db:branch:reset # drop and recreate current branch schema
92
+ rails db:branch:discard # drop current branch schema (or BRANCH=name)
93
+ rails db:branch:list # list all branch schemas and their sizes
94
+ rails db:branch:diff # show objects in this branch vs public
95
+ rails db:branch:prune # drop schemas for branches no longer in git
96
+ ```
97
+
98
+ ## Configuration
99
+
100
+ ```yaml
101
+ development:
102
+ adapter: postgresql_branched
103
+ database: myapp_development
104
+ primary_branch: main # default, can be 'master', 'trunk', etc.
105
+ branch_override: agent-0 # bypass git, set branch explicitly
106
+ ```
107
+
108
+ The `PGBRANCH` or `BRANCH` environment variables also work for explicit branch selection.
109
+
110
+ ## Rebasing
111
+
112
+ ```bash
113
+ git fetch && git rebase origin/main
114
+ rails db:branch:reset
115
+ rails db:migrate
116
+ ```
117
+
118
+ `db:branch:reset` drops the branch schema. `search_path` falls through to the updated `public`. Re-running `db:migrate` reapplies your branch's migrations on top of the new baseline.
119
+
120
+ ## The merge story
121
+
122
+ The adapter does not merge. Git does.
123
+
124
+ 1. Agent writes migrations on its branch
125
+ 2. Adapter isolates them in a branch schema automatically
126
+ 3. PR merged into `main`
127
+ 4. Team pulls `main`, runs `db:migrate`
128
+ 5. Adapter stands aside (primary branch), migrations land in `public`
129
+ 6. `schema.rb` updated and committed
130
+ 7. All active branch schemas see updated `public` via fallthrough
131
+
132
+ ## schema.rb
133
+
134
+ `db:schema:dump` presents a unified view of the current branch as if everything lived in `public`:
135
+
136
+ - Branch-local tables appear without the `branch_` prefix
137
+ - Shadowed tables show the branch version
138
+ - Public tables the branch hasn't touched are included as normal
139
+ - No schema references, no branch artifacts
140
+
141
+ The diff for a schema change looks exactly as it always has.
142
+
143
+ ## Limitations
144
+
145
+ - **Rails + Postgres only** -- uses Postgres schemas and `search_path`
146
+ - **Dev only** -- production and staging should use the standard `postgresql` adapter
147
+ - **No `schema_search_path` in database.yml** -- it will conflict with the adapter
148
+ - **Non-ActiveRecord DDL** -- raw SQL outside of migrations bypasses the shadow rule
149
+ - **Sequences on shadowed tables** -- `rename_table` on a shadowed table with serial columns works, but the sequence keeps its original name
@@ -0,0 +1,66 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module Branched
5
+ class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
6
+ ADAPTER_NAME = "PostgreSQL Branched"
7
+
8
+ SHADOW_BEFORE = %i[
9
+ add_column
10
+ remove_column
11
+ rename_column
12
+ change_column
13
+ change_column_default
14
+ change_column_null
15
+ change_column_comment
16
+ change_table_comment
17
+ add_index
18
+ remove_index
19
+ rename_index
20
+ drop_table
21
+ change_table
22
+ bulk_change_table
23
+ ].freeze
24
+
25
+ def initialize(...)
26
+ super
27
+ @branch_manager = BranchManager.new(self, @config)
28
+ @shadow = Shadow.new(self, @branch_manager.branch_schema) unless @branch_manager.primary_branch?
29
+ end
30
+
31
+ def configure_connection
32
+ super
33
+ @branch_manager.activate
34
+ end
35
+
36
+ SHADOW_BEFORE.each do |method|
37
+ define_method(method) do |table_name, *args, **kwargs, &block|
38
+ @shadow&.call(table_name)
39
+ super(table_name, *args, **kwargs, &block)
40
+ end
41
+ end
42
+
43
+ # rename_table needs special handling: the shadow table's sequences
44
+ # live in public, but Rails' rename_table tries to rename them using
45
+ # the branch schema. The table and index renames succeed before the
46
+ # sequence rename fails, so we rescue the sequence error.
47
+ def rename_table(table_name, new_name, **options)
48
+ @shadow&.call(table_name)
49
+ super
50
+ rescue ActiveRecord::StatementInvalid => e
51
+ raise if @branch_manager.primary_branch?
52
+ raise unless e.cause.is_a?(PG::UndefinedTable)
53
+ end
54
+
55
+ attr_reader :branch_manager
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ ActiveRecord::ConnectionAdapters.register(
63
+ "postgresql_branched",
64
+ "ActiveRecord::ConnectionAdapters::PostgreSQL::Branched::Adapter",
65
+ "activerecord-postgresql-branched"
66
+ )
@@ -0,0 +1,161 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module Branched
5
+ class BranchManager
6
+ attr_reader :branch, :branch_schema
7
+
8
+ def initialize(connection, config)
9
+ @connection = connection
10
+ @config = config
11
+ @branch = resolve_branch
12
+ @branch_schema = self.class.sanitise(@branch)
13
+ end
14
+
15
+ def activate
16
+ return if primary_branch?
17
+
18
+ ensure_schema
19
+ set_search_path
20
+ shadow_migration_tables
21
+ end
22
+
23
+ def primary_branch?
24
+ @branch == primary_branch_name
25
+ end
26
+
27
+ def reset
28
+ drop_schema
29
+ ensure_schema
30
+ set_search_path
31
+ end
32
+
33
+ def discard(branch_name = @branch)
34
+ schema = self.class.sanitise(branch_name)
35
+
36
+ if schema == self.class.sanitise(primary_branch_name)
37
+ raise "Cannot discard the primary branch schema"
38
+ end
39
+
40
+ @connection.execute("DROP SCHEMA IF EXISTS #{quote(schema)} CASCADE")
41
+ end
42
+
43
+ def list
44
+ @connection.select_rows(<<~SQL)
45
+ SELECT s.schema_name,
46
+ COALESCE(pg_size_pretty(sum(pg_total_relation_size(
47
+ quote_ident(t.table_schema) || '.' || quote_ident(t.table_name)
48
+ ))), '0 bytes') AS size
49
+ FROM information_schema.schemata s
50
+ LEFT JOIN information_schema.tables t
51
+ ON t.table_schema = s.schema_name AND t.table_type = 'BASE TABLE'
52
+ WHERE s.schema_name LIKE 'branch_%'
53
+ GROUP BY s.schema_name
54
+ ORDER BY s.schema_name
55
+ SQL
56
+ end
57
+
58
+ def diff
59
+ return [] if primary_branch?
60
+
61
+ @connection.select_values(<<~SQL)
62
+ SELECT table_name FROM information_schema.tables
63
+ WHERE table_schema = #{@connection.quote(@branch_schema)}
64
+ AND table_type = 'BASE TABLE'
65
+ ORDER BY table_name
66
+ SQL
67
+ end
68
+
69
+ MAX_SCHEMA_LENGTH = 63
70
+ PREFIX = "branch_"
71
+
72
+ def self.sanitise(branch)
73
+ slug = branch.downcase.gsub(/[\/\-\.]/, "_").gsub(/[^a-z0-9_]/, "")
74
+ schema = PREFIX + slug
75
+
76
+ return schema if schema.bytesize <= MAX_SCHEMA_LENGTH
77
+
78
+ # Truncate and append a short hash to avoid collisions
79
+ hash = Digest::SHA256.hexdigest(slug)[0, 8]
80
+ max_slug = MAX_SCHEMA_LENGTH - PREFIX.bytesize - 9 # 9 = underscore + 8 char hash
81
+ PREFIX + slug[0, max_slug] + "_" + hash
82
+ end
83
+
84
+ def prune(keep: nil)
85
+ active_schemas = if keep
86
+ Array(keep).map { |b| self.class.sanitise(b) }.to_set
87
+ else
88
+ git_branches = `git branch --list 2>/dev/null`.lines.map { |l| l.strip.delete_prefix("* ") }
89
+ if git_branches.empty?
90
+ raise "No git branches found. Pass branch names explicitly: prune(keep: ['main', 'feature/x'])"
91
+ end
92
+ git_branches.map { |b| self.class.sanitise(b) }.to_set
93
+ end
94
+
95
+ all_branch_schemas = @connection.select_values(<<~SQL)
96
+ SELECT schema_name FROM information_schema.schemata
97
+ WHERE schema_name LIKE 'branch_%'
98
+ SQL
99
+
100
+ stale = all_branch_schemas.reject { |s| active_schemas.include?(s) }
101
+ stale.each do |schema|
102
+ @connection.execute("DROP SCHEMA IF EXISTS #{quote(schema)} CASCADE")
103
+ end
104
+ stale
105
+ end
106
+
107
+ def self.resolve_branch_name(config)
108
+ config[:branch_override]&.to_s ||
109
+ ENV["BRANCH"] ||
110
+ ENV["PGBRANCH"] ||
111
+ git_branch
112
+ end
113
+
114
+ private
115
+
116
+ def resolve_branch
117
+ name = self.class.resolve_branch_name(@config)
118
+
119
+ if name.nil? || name.empty?
120
+ raise "Could not determine git branch. " \
121
+ "Set branch_override in database.yml or the PGBRANCH environment variable."
122
+ end
123
+
124
+ name
125
+ end
126
+
127
+ def primary_branch_name
128
+ (@config[:primary_branch] || "main").to_s
129
+ end
130
+
131
+ def ensure_schema
132
+ @connection.execute("CREATE SCHEMA IF NOT EXISTS #{quote(@branch_schema)}")
133
+ end
134
+
135
+ def drop_schema
136
+ @connection.execute("DROP SCHEMA IF EXISTS #{quote(@branch_schema)} CASCADE")
137
+ end
138
+
139
+ def set_search_path
140
+ @connection.schema_search_path = "#{@branch_schema}, public"
141
+ end
142
+
143
+ def shadow_migration_tables
144
+ shadow = Shadow.new(@connection, @branch_schema)
145
+ shadow.call(ActiveRecord::Base.schema_migrations_table_name)
146
+ shadow.call(ActiveRecord::Base.internal_metadata_table_name)
147
+ end
148
+
149
+ def quote(identifier)
150
+ @connection.quote_column_name(identifier)
151
+ end
152
+
153
+ def self.git_branch
154
+ result = `git branch --show-current 2>/dev/null`.strip
155
+ result.empty? ? nil : result
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,96 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module Branched
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ namespace :db do
8
+ namespace :branch do
9
+ desc "Drop and recreate the current branch schema"
10
+ task reset: :load_config do
11
+ manager = branch_manager
12
+
13
+ if manager.primary_branch?
14
+ puts "On primary branch (#{manager.branch}), nothing to reset."
15
+ next
16
+ end
17
+
18
+ manager.reset
19
+ puts "Reset branch schema #{manager.branch_schema}. Run db:migrate to reapply branch migrations."
20
+ end
21
+
22
+ desc "Drop the current branch schema entirely"
23
+ task discard: :load_config do
24
+ manager = branch_manager
25
+ branch = ENV["BRANCH"] || manager.branch
26
+ schema = BranchManager.sanitise(branch)
27
+
28
+ manager.discard(branch)
29
+ puts "Discarded branch schema #{schema}."
30
+ rescue => e
31
+ puts e.message
32
+ end
33
+
34
+ desc "List all branch schemas and their sizes"
35
+ task list: :load_config do
36
+ rows = branch_manager.list
37
+
38
+ if rows.empty?
39
+ puts "No branch schemas found."
40
+ else
41
+ puts "Branch schemas:"
42
+ rows.each { |name, size| puts " #{name} (#{size})" }
43
+ end
44
+ end
45
+
46
+ desc "Drop schemas for branches that no longer exist in git"
47
+ task prune: :load_config do
48
+ pruned = branch_manager.prune
49
+
50
+ if pruned.empty?
51
+ puts "No stale branch schemas found."
52
+ else
53
+ puts "Pruned #{pruned.size} stale branch schema#{"s" if pruned.size > 1}:"
54
+ pruned.each { |s| puts " #{s}" }
55
+ end
56
+ end
57
+
58
+ desc "Show objects in the current branch schema vs public"
59
+ task diff: :load_config do
60
+ manager = branch_manager
61
+
62
+ if manager.primary_branch?
63
+ puts "On primary branch, no diff."
64
+ next
65
+ end
66
+
67
+ tables = manager.diff
68
+
69
+ if tables.empty?
70
+ puts "No branch-local objects in #{manager.branch_schema}."
71
+ else
72
+ puts "Branch-local objects in #{manager.branch_schema}:"
73
+ tables.each { |t| puts " #{t}" }
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def branch_manager
80
+ connection = ActiveRecord::Base.lease_connection
81
+ BranchManager.new(connection, connection.instance_variable_get(:@config))
82
+ end
83
+ end
84
+
85
+ initializer "postgresql_branched.schema_dumper" do
86
+ ActiveSupport.on_load(:active_record) do
87
+ ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(
88
+ ActiveRecord::ConnectionAdapters::PostgreSQL::Branched::SchemaDumperExtension
89
+ )
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,65 @@
1
+ require "stringio"
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module Branched
7
+ module SchemaDumperExtension
8
+ private
9
+
10
+ def on_branch?
11
+ @connection.respond_to?(:branch_manager) &&
12
+ @connection.branch_manager &&
13
+ !@connection.branch_manager.primary_branch?
14
+ end
15
+
16
+ def initialize(connection, options = {})
17
+ super
18
+ @dump_schemas = ["public"] if on_branch?
19
+ end
20
+
21
+ def schemas(stream)
22
+ return if on_branch?
23
+ super
24
+ end
25
+
26
+ def tables(stream)
27
+ return super unless on_branch?
28
+
29
+ table_names = @connection.tables.uniq.sort
30
+ table_names.reject! { |t| ignored?(t) }
31
+
32
+ table_names.each_with_index do |table_name, index|
33
+ table(table_name, stream)
34
+ stream.puts if index < table_names.size - 1
35
+ end
36
+
37
+ if @connection.supports_foreign_keys?
38
+ fk_stream = StringIO.new
39
+ table_names.each { |tbl| foreign_keys(tbl, fk_stream) }
40
+ fk_string = fk_stream.string
41
+ if fk_string.length > 0
42
+ stream.puts
43
+ stream.print fk_string
44
+ end
45
+ end
46
+ end
47
+
48
+ def types(stream)
49
+ return super unless on_branch?
50
+
51
+ enums = @connection.enum_types
52
+ if enums.any?
53
+ stream.puts " # Custom types defined in this database."
54
+ stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
55
+ enums.sort.each do |name, values|
56
+ stream.puts " create_enum #{name.inspect}, #{values.inspect}"
57
+ end
58
+ stream.puts
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,51 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module Branched
5
+ class Shadow
6
+ def initialize(connection, branch_schema)
7
+ @connection = connection
8
+ @branch_schema = branch_schema
9
+ end
10
+
11
+ def call(table_name)
12
+ table = table_name.to_s
13
+ return unless exists_in_public?(table)
14
+ return if already_shadowed?(table)
15
+
16
+ create_shadow(table)
17
+ end
18
+
19
+ private
20
+
21
+ def exists_in_public?(table)
22
+ @connection.select_value(<<~SQL) == 1
23
+ SELECT 1 FROM information_schema.tables
24
+ WHERE table_schema = 'public' AND table_name = #{@connection.quote(table)}
25
+ SQL
26
+ end
27
+
28
+ def already_shadowed?(table)
29
+ @connection.select_value(<<~SQL) == 1
30
+ SELECT 1 FROM information_schema.tables
31
+ WHERE table_schema = #{@connection.quote(@branch_schema)}
32
+ AND table_name = #{@connection.quote(table)}
33
+ SQL
34
+ end
35
+
36
+ def create_shadow(table)
37
+ quoted_table = @connection.quote_column_name(table)
38
+ @connection.execute(<<~SQL)
39
+ CREATE TABLE #{@branch_schema}.#{quoted_table}
40
+ (LIKE public.#{quoted_table} INCLUDING ALL)
41
+ SQL
42
+ @connection.execute(<<~SQL)
43
+ INSERT INTO #{@branch_schema}.#{quoted_table}
44
+ SELECT * FROM public.#{quoted_table}
45
+ SQL
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+ require "digest/sha2"
2
+ require "set"
3
+ require "active_record/connection_adapters/postgresql_adapter"
4
+ require "active_record/connection_adapters/postgresql/branched/branch_manager"
5
+ require "active_record/connection_adapters/postgresql/branched/shadow"
6
+ require "active_record/connection_adapters/postgresql/branched/schema_dumper"
7
+ require "active_record/connection_adapters/postgresql/branched/adapter"
8
+ require "active_record/connection_adapters/postgresql/branched/railtie" if defined?(Rails::Railtie)
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-postgresql-branched
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Carl Dawson
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: railties
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: pg
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ description: A Rails database adapter that gives each git branch its own Postgres
55
+ schema. Migrations run in isolation. Nobody steps on anyone else's work.
56
+ executables: []
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - CHANGELOG.md
61
+ - LICENSE
62
+ - README.md
63
+ - lib/active_record/connection_adapters/postgresql/branched/adapter.rb
64
+ - lib/active_record/connection_adapters/postgresql/branched/branch_manager.rb
65
+ - lib/active_record/connection_adapters/postgresql/branched/railtie.rb
66
+ - lib/active_record/connection_adapters/postgresql/branched/schema_dumper.rb
67
+ - lib/active_record/connection_adapters/postgresql/branched/shadow.rb
68
+ - lib/activerecord-postgresql-branched.rb
69
+ homepage: https://github.com/carldaws/activerecord-postgresql-branched
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ source_code_uri: https://github.com/carldaws/activerecord-postgresql-branched
74
+ changelog_uri: https://github.com/carldaws/activerecord-postgresql-branched/blob/main/CHANGELOG.md
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 4.0.6
90
+ specification_version: 4
91
+ summary: Branch-aware PostgreSQL adapter for ActiveRecord
92
+ test_files: []