Class: ActiveRecord::Tenanted::DatabaseAdapters::SQLite

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/tenanted/database_adapters/sqlite.rb

Overview

TODO: This still feels to me like it’s not quite right. I think we could further refactor this by:

1. Moving tenant_databases and validate_tenant_name to BaseConfig, and subclassing it for
   each database
2. Moving create_database, drop_database, database_exist?, database_ready?,
   acquire_ready_lock, ensure_database_directory_exists, and database_path to the SQLite
   connection adapter, possibly into Rails
3. Moving test_workerize and path_for to be SQLite connection adapter class methods,
   possibly into Rails

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db_config) ⇒ SQLite

Returns a new instance of SQLite.



20
21
22
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 20

def initialize(db_config)
  @db_config = db_config
end

Instance Attribute Details

#db_configObject (readonly)

Returns the value of attribute db_config.



18
19
20
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 18

def db_config
  @db_config
end

Instance Method Details

#acquire_ready_lock(&block) ⇒ Object



63
64
65
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 63

def acquire_ready_lock(&block)
  ActiveRecord::Tenanted::Mutex::Ready.lock(database_path, &block)
end

#create_databaseObject



43
44
45
46
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 43

def create_database
  ensure_database_directory_exists
  FileUtils.touch(database_path)
end

#database_exist?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 55

def database_exist?
  File.exist?(database_path)
end

#database_pathObject



76
77
78
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 76

def database_path
  path_for(db_config.database)
end

#database_ready?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 59

def database_ready?
  File.exist?(database_path) && !ActiveRecord::Tenanted::Mutex::Ready.locked?(database_path)
end

#drop_databaseObject



48
49
50
51
52
53
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 48

def drop_database
  # Remove the SQLite database file and associated files
  FileUtils.rm_f(database_path)
  FileUtils.rm_f("#{database_path}-wal")  # Write-Ahead Logging file
  FileUtils.rm_f("#{database_path}-shm")  # Shared Memory file
end

#ensure_database_directory_existsObject



67
68
69
70
71
72
73
74
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 67

def ensure_database_directory_exists
  return unless database_path

  database_dir = File.dirname(database_path)
  unless File.directory?(database_dir)
    FileUtils.mkdir_p(database_dir)
  end
end

#path_for(database) ⇒ Object

A sqlite database path can be a file path or a URI (either relative or absolute). We can’t parse it as a standard URI in all circumstances, though, see sqlite.org/uri.html



103
104
105
106
107
108
109
110
111
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 103

def path_for(database)
  if database.start_with?("file:/")
    URI.parse(database).path
  elsif database.start_with?("file:")
    URI.parse(database.sub(/\?.*$/, "")).opaque
  else
    database
  end
end

#tenant_databasesObject



24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 24

def tenant_databases
  glob = path_for(db_config.database_for("*"))
  scanner = Regexp.new(path_for(db_config.database_for("(.+)")))

  Dir.glob(glob).filter_map do |path|
    result = path.scan(scanner).flatten.first
    if result.nil?
      Rails.logger.warn "ActiveRecord::Tenanted: Cannot parse tenant name from filename #{path.inspect}"
    end
    result
  end
end

#test_workerize(db, test_worker_id) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 80

def test_workerize(db, test_worker_id)
  test_worker_suffix = "_#{test_worker_id}"

  if db.start_with?("file:") && db.include?("?")
    db.sub(/(\?.*)$/, "#{test_worker_suffix}\\1")
  else
    # This check is needed because of https://github.com/rails/rails/pull/55769 adding
    # replicas to the parallelization setup by using `include_hidden: true` which pulls in
    # the BaseConfig. We don't want to double-suffix the database name.
    #
    # TODO: Ideally we should have finer-grained filtering of database configurations in Rails
    # (other than simply hidden or not-hidden).
    if db.end_with?(test_worker_suffix)
      db
    else
      db + test_worker_suffix
    end
  end
end

#validate_tenant_name(tenant_name) ⇒ Object



37
38
39
40
41
# File 'lib/active_record/tenanted/database_adapters/sqlite.rb', line 37

def validate_tenant_name(tenant_name)
  if tenant_name.match?(%r{[/'"`]})
    raise BadTenantNameError, "Tenant name contains an invalid character: #{tenant_name.inspect}"
  end
end