Module: Polyrun::Database::UrlBuilder

Defined in:
lib/polyrun/database/url_builder.rb,
lib/polyrun/database/url_builder/connection.rb,
lib/polyrun/database/url_builder/connection/infer.rb,
lib/polyrun/database/url_builder/template_prepare.rb,
lib/polyrun/database/url_builder/connection/url_builders.rb

Overview

Builds database URLs from polyrun.yml databases (spec2 §5.2) — no Liquid; use ENV fallbacks. Use adapter: or nested blocks: postgresql, mysql / mysql2 / trilogy, sqlserver / mssql, sqlite3 / sqlite, mongodb / mongo.

Defined Under Namespace

Modules: Connection, ConnectionInfer, ConnectionUrlBuilders

Class Method Summary collapse

Class Method Details

.env_exports_for_databases(databases_hash, shard_index:) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/polyrun/database/url_builder.rb', line 128

def env_exports_for_databases(databases_hash, shard_index:)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  out = {}
  primary_url = url_for_shard(dh, shard_index: shard_index)
  out["DATABASE_URL"] = primary_url
  out["TEST_DB_NAME"] = extract_db_name(primary_url)

  conns = dh["connections"] || dh[:connections] || []
  Array(conns).each do |c|
    name = (c["name"] || c[:name]).to_s
    next if name.empty?

    u = url_for_shard(dh, shard_index: shard_index, connection: name)
    key = (c["env_key"] || c[:env_key]).to_s.strip
    key = "DATABASE_URL_#{name.upcase.tr("-", "_")}" if key.empty?
    out[key] = u
  end
  out
end

.extract_db_name(url) ⇒ Object



148
149
150
151
152
153
154
155
# File 'lib/polyrun/database/url_builder.rb', line 148

def extract_db_name(url)
  s = url.to_s
  return s.sub(/\Asqlite3:/i, "") if s.match?(/\Asqlite3:/i)

  URI.parse(s).path.delete_prefix("/").split("?", 2).first
rescue URI::InvalidURIError
  nil
end

.postgres_url_for_database_name(databases_hash, database_name) ⇒ Object



17
18
19
# File 'lib/polyrun/database/url_builder.rb', line 17

def postgres_url_for_database_name(databases_hash, database_name)
  url_for_database_name(databases_hash, database_name)
end

.postgres_url_for_shard(databases_hash, shard_index:, connection: nil) ⇒ Object



21
22
23
# File 'lib/polyrun/database/url_builder.rb', line 21

def postgres_url_for_shard(databases_hash, shard_index:, connection: nil)
  url_for_shard(databases_hash, shard_index: shard_index, connection: connection)
end

.postgres_url_for_template(databases_hash) ⇒ Object



13
14
15
# File 'lib/polyrun/database/url_builder.rb', line 13

def postgres_url_for_template(databases_hash)
  url_for_template(databases_hash)
end

.shard_database_name(databases_hash, shard_index:, connection: nil) ⇒ Object



75
76
77
# File 'lib/polyrun/database/url_builder.rb', line 75

def shard_database_name(databases_hash, shard_index:, connection: nil)
  extract_db_name(url_for_shard(databases_hash, shard_index: shard_index, connection: connection))
end

.shard_database_plan(databases_hash, shard_index:) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/polyrun/database/url_builder.rb', line 91

def shard_database_plan(databases_hash, shard_index:)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  rows = []
  primary_shard = shard_database_name(dh, shard_index: shard_index, connection: nil)
  primary_tmpl = template_database_name_for(dh, connection: nil)
  if !primary_shard.empty? && !primary_tmpl.empty?
    rows << {new_db: primary_shard, template_db: primary_tmpl}
  end

  Array(dh["connections"] || dh[:connections]).each do |c|
    nm = (c["name"] || c[:name]).to_s
    next if nm.empty?

    sname = shard_database_name(dh, shard_index: shard_index, connection: nm)
    tname = template_database_name_for(dh, connection: nm)
    rows << {new_db: sname, template_db: tname} if !sname.empty? && !tname.empty?
  end
  rows
end

.template_database_name_for(databases_hash, connection: nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/polyrun/database/url_builder.rb', line 79

def template_database_name_for(databases_hash, connection: nil)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  if connection.nil?
    return (dh["template_db"] || dh[:template_db]).to_s
  end

  c = Array(dh["connections"] || dh[:connections]).find { |x| (x["name"] || x[:name]).to_s == connection.to_s }
  return "" unless c

  (c["template_db"] || c[:template_db] || dh["template_db"] || dh[:template_db]).to_s
end

.template_prepare_env(databases_hash) ⇒ Object

ENV overrides so bin/rails db:prepare runs once for multi-DB apps: each connection keeps its own migrations_paths (e.g. db/cache_migrate) instead of pointing DATABASE_URL at every template in turn.

Raises:



40
41
42
43
44
45
46
47
48
# File 'lib/polyrun/database/url_builder.rb', line 40

def template_prepare_env(databases_hash)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  pt = (dh["template_db"] || dh[:template_db]).to_s
  raise Polyrun::Error, "template_prepare_env: set databases.template_db" if pt.empty?

  out = {}
  out["DATABASE_URL"] = url_for_database_name(dh, pt)
  template_prepare_env_fill_connections!(dh, pt, out)
end

.template_prepare_env_fill_connections!(dh, primary_template, out) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/polyrun/database/url_builder/template_prepare.rb', line 6

def template_prepare_env_fill_connections!(dh, primary_template, out)
  Array(dh["connections"] || dh[:connections]).each do |c|
    nm = (c["name"] || c[:name]).to_s
    key = (c["env_key"] || c[:env_key]).to_s.strip
    key = "DATABASE_URL_#{nm.upcase.tr("-", "_")}" if key.empty? && !nm.empty?
    next if key.empty?

    tname = (c["template_db"] || c[:template_db]).to_s
    tname = primary_template if tname.empty?
    out[key] = url_for_database_name(dh, tname)
  end
  out
end

.template_prepare_env_shell_log(databases_hash) ⇒ Object



50
51
52
# File 'lib/polyrun/database/url_builder.rb', line 50

def template_prepare_env_shell_log(databases_hash)
  template_prepare_env(databases_hash).sort.map { |k, v| "#{k}=#{Shellwords.escape(v.to_s)}" }.join(" ")
end

.unique_template_migrate_urls(databases_hash) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/polyrun/database/url_builder.rb', line 54

def unique_template_migrate_urls(databases_hash)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  seen = {}
  out = []
  pt = (dh["template_db"] || dh[:template_db]).to_s
  unless pt.empty?
    out << url_for_database_name(dh, pt)
    seen[pt] = true
  end
  Array(dh["connections"] || dh[:connections]).each do |c|
    t = (c["template_db"] || c[:template_db]).to_s
    t = pt if t.empty?
    next if t.empty?
    next if seen[t]

    out << url_for_database_name(dh, t)
    seen[t] = true
  end
  out
end

.url_for_database_name(databases_hash, database_name) ⇒ Object



33
34
35
36
# File 'lib/polyrun/database/url_builder.rb', line 33

def url_for_database_name(databases_hash, database_name)
  conn = Connection.resolve_connection(databases_hash)
  Connection.build_database_url(database_name.to_s, conn)
end

.url_for_shard(databases_hash, shard_index:, connection: nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/polyrun/database/url_builder.rb', line 111

def url_for_shard(databases_hash, shard_index:, connection: nil)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  conn = Connection.resolve_connection(dh)
  pattern =
    if connection
      conns = dh["connections"] || dh[:connections] || []
      c = Array(conns).find { |x| (x["name"] || x[:name]).to_s == connection.to_s }
      (c && (c["shard_db_pattern"] || c[:shard_db_pattern])) || dh["shard_db_pattern"]
    else
      dh["shard_db_pattern"] || dh[:shard_db_pattern]
    end
  pattern ||= "app_test_%{shard}"

  dbname = pattern.to_s.gsub("%{shard}", Integer(shard_index).to_s).gsub("%<shard>d", format("%d", Integer(shard_index)))
  Connection.build_database_url(dbname, conn)
end

.url_for_template(databases_hash) ⇒ Object

Raises:



25
26
27
28
29
30
31
# File 'lib/polyrun/database/url_builder.rb', line 25

def url_for_template(databases_hash)
  dh = databases_hash.is_a?(Hash) ? databases_hash : {}
  dbname = dh["template_db"] || dh[:template_db]
  raise Polyrun::Error, "databases.template_db is required" if dbname.nil? || dbname.to_s.empty?

  url_for_database_name(dh, dbname.to_s)
end