Module: Copse::Database
- Defined in:
- lib/copse/database.rb
Overview
Decides whether Copse renames the development database, and to what.
Same shape as UrlOptions and for the same reason: the decisions are Rails-free so they can be tested without booting an application, and the railtie is only the wiring.
The suffix is derived from the checkout on disk rather than from the
environment Copse exports, so bin/rails db:prepare and bin/rails console
in a linked worktree reach the same database bin/dev does. A database that
only existed when Copse booted the app would be a database no rake task could
create.
Constant Summary collapse
- NAME_LIMIT =
A main worktree keeps its plain database name, so only a linked worktree is renamed. That is what makes this safe to add to an existing app: the database you already have is never the one that moves.
PostgreSQL silently truncates identifiers past 63 bytes and MySQL rejects them past 64, so the composed name is capped here rather than left to the server. The cap trims the suffix, never the app's own database name -- which can make two very long branch names share a database, exactly as the 63-character DNS label cap can make them share a hostname.
63- FILE_BACKED_ADAPTERS =
Adapters whose "database" is a path rather than a name on a shared server. A linked worktree is a separate directory, so these are already separate databases; renaming would only move the file inside the worktree.
%w[sqlite3 sqlite].freeze
Class Method Summary collapse
-
.apply(configurations, suffix:, env_name: "development") ⇒ Object
Renames every development database in an ActiveRecord::DatabaseConfigurations, in place.
-
.apply?(rails_env:) ⇒ Boolean
Development only.
-
.rename(database:, adapter:, suffix:) ⇒ Object
The renamed database, or nil when this configuration is left alone.
-
.suffix(root: Dir.pwd) ⇒ Object
The suffix for this checkout, or nil in a main worktree.
Class Method Details
.apply(configurations, suffix:, env_name: "development") ⇒ Object
Renames every development database in an ActiveRecord::DatabaseConfigurations, in place.
In place is the load-bearing part. Active Record's own
active_record.initialize_database initializer calls establish_connection
before this runs, so a replacement configuration object would be ignored
by the pool that already exists. Mutating the object the pool holds works
because the adapter is built from it lazily, at first checkout.
Returns the names that changed, for reporting.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/copse/database.rb', line 75 def self.apply(configurations, suffix:, env_name: "development") configurations.configs_for(env_name: env_name, include_hidden: true).filter_map do |config| # `database_tasks: false` is how an app says Rails does not own this # database -- a legacy or shared server it only reads. Copse does not get # to rename something the app cannot create. # # The key is read directly rather than through `database_tasks?`, which is # also false for every replica. A replica points at the same database as # its primary, so leaving it behind would send reads to another worktree's # data. next unless config.configuration_hash.fetch(:database_tasks, true) renamed = rename(database: config.database, adapter: config.adapter, suffix: suffix) next if renamed.nil? config._database = renamed renamed end end |
.apply?(rails_env:) ⇒ Boolean
Development only. The point of the rename is that several worktrees of one app can run at once; production has one, and a test database is already partitioned per worker by Rails itself.
37 38 39 |
# File 'lib/copse/database.rb', line 37 def self.apply?(rails_env:) rails_env.to_s == "development" end |
.rename(database:, adapter:, suffix:) ⇒ Object
The renamed database, or nil when this configuration is left alone.
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/copse/database.rb', line 54 def self.rename(database:, adapter:, suffix:) name = database.to_s return nil if suffix.to_s.empty? || name.empty? return nil if FILE_BACKED_ADAPTERS.include?(adapter.to_s) || name.include?("/") # Idempotent: the railtie can run again in the same process (a reload, a # second `db:prepare` in one rake invocation) and must not keep appending. return nil if name.end_with?("_#{suffix}") compose(name, suffix.to_s) end |
.suffix(root: Dir.pwd) ⇒ Object
The suffix for this checkout, or nil in a main worktree.
The checkout on disk is the only authority, never the exported
COPSE_DATABASE_SUFFIX -- which is deliberate rather than just simple. That
variable can outlive the session that set it: a shell opened from a linked
worktree's bin/dev, or a stale line in the app's own .env that
dotenv-rails loads. Reading it here would let either one rename a main
worktree's database, which is the one promise this feature makes.
49 50 51 |
# File 'lib/copse/database.rb', line 49 def self.suffix(root: Dir.pwd) Worktree.new(root).database_suffix end |