Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/hanami/cli/commands/app/db/utils/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def sql

def self.database_class(database_url)
database_scheme = URI(database_url).scheme
if database_scheme == "jdbc"
database_scheme = URI(database_url.sub("jdbc:", "")).scheme
end
DATABASE_CLASS_RESOLVER[database_scheme].call
end

Expand Down
15 changes: 11 additions & 4 deletions lib/hanami/cli/commands/app/db/utils/sqlite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,17 @@ def exec_load_command
# @api private
# @since 2.2.0
def name
# Sequel expects sqlite:// URIs to operate the same as file:// URIs: 2 slashes for
# a relative path, 3 for an absolute path. In the case of 2 slashes, the first part
# of the path is considered by Ruby's `URI` as the `#host`.
@name ||= "#{database_uri.host}#{database_uri.path}"
@name ||= if database_uri.scheme == "jdbc"
# For JDBC SQLite URIs like "jdbc:sqlite:db/app.sqlite3",
# we need to extract the path part after "jdbc:sqlite:"
# The standard URI.parse doesn't handle JDBC URIs well, so we remove the prefix manually
database_url.sub(%r{^jdbc:sqlite:}, "")
else
# Sequel expects sqlite:// URIs to operate the same as file:// URIs: 2 slashes for
# a relative path, 3 for an absolute path. In the case of 2 slashes, the first part
# of the path is considered by Ruby's `URI` as the `#host`.
"#{database_uri.host}#{database_uri.path}"
end
end

private
Expand Down
34 changes: 34 additions & 0 deletions spec/support/helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module RSpec
module Support
module Helpers
def sqlite_url(url, dir: nil)
url = sqlite_db_name(url, dir:)
if jruby?
# We need to ensure that the parent directory for the database exists, because JDBC driver
# won't create it
base_dir = File.dirname(url)
FileUtils.mkdir_p(base_dir)
"jdbc:sqlite:#{url}"
else
"sqlite://#{url}"
end
end

def sqlite_db_name(url, dir: nil)
# JDBC driver does not use Dir.current for building the path, so we need to construct
# the correct path ourselves
jruby? && dir ? File.join(dir, url) : url
end

def jruby?
RUBY_ENGINE == "jruby"
end
end
end
end

RSpec.configure do |config|
config.include RSpec::Support::Helpers
end
46 changes: 23 additions & 23 deletions spec/unit/hanami/cli/commands/app/db/create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def before_prepare

describe "sqlite" do
before do
ENV["DATABASE_URL"] = "sqlite://db/app.sqlite3"
ENV["DATABASE_URL"] = sqlite_url("db/app.sqlite3", dir: @dir)
end

it "creates the database" do
Expand All @@ -68,16 +68,16 @@ def before_prepare

expect { Hanami.app["db.gateway"] }.not_to raise_error

expect(output).to include "database db/app.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} created"
end

it "does not create the database if it already exists" do
FileUtils.mkdir(@dir.join("db"))
FileUtils.mkdir_p(@dir.join("db"))
FileUtils.touch(@dir.join("db", "app.sqlite3"))

command.call

expect(output).to include "database db/app.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} created"
end
end

Expand Down Expand Up @@ -142,8 +142,8 @@ def before_prepare

describe "sqlite" do
before do
ENV["DATABASE_URL"] = "sqlite://db/app.sqlite3"
ENV["MAIN__DATABASE_URL"] = "sqlite://db/main.sqlite3"
ENV["DATABASE_URL"] = sqlite_url("db/app.sqlite3", dir: @dir)
ENV["MAIN__DATABASE_URL"] = sqlite_url("db/main.sqlite3", dir: @dir)
end

it "creates each database" do
Expand All @@ -155,8 +155,8 @@ def before_prepare
expect { Hanami.app["db.gateway"] }.not_to raise_error
expect { Main::Slice["db.gateway"] }.not_to raise_error

expect(output).to include "database db/app.sqlite3 created"
expect(output).to include "database db/main.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} created"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} created"
end

it "creates the app database when given --app" do
Expand All @@ -167,8 +167,8 @@ def before_prepare

expect { Hanami.app["db.gateway"] }.not_to raise_error

expect(output).to include "database db/app.sqlite3 created"
expect(output).not_to include "db/main.sqlite3"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} created"
expect(output).not_to include sqlite_db_name("db/main.sqlite3", dir: @dir)
end

it "creates a slice database when given --slice" do
Expand All @@ -179,8 +179,8 @@ def before_prepare

expect { Main::Slice["db.gateway"] }.not_to raise_error

expect(output).to include "database db/main.sqlite3 created"
expect(output).not_to include "db/app.sqlite3"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} created"
expect(output).not_to include sqlite_db_name("db/app.sqlite3", dir: @dir)
end

it "prints errors for any create commands that fail and exits with non-zero status" do
Expand All @@ -197,18 +197,18 @@ def before_prepare
expect(Hanami.app.root.join("db", "app.sqlite3").exist?).to be false
expect(Hanami.app.root.join("db", "main.sqlite3").exist?).to be true

expect(output).to include "failed to create database db/app.sqlite3"
expect(output).to include "failed to create database #{sqlite_db_name("db/app.sqlite3", dir: @dir)}"
expect(output).to include "app-db-err"

expect(output).to include "database db/main.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} created"

expect(command).to have_received(:exit).with(2).once
end

context "app with gateways" do
def before_prepare
write "config/db/.keep", ""
ENV["DATABASE_URL__EXTRA"] = "sqlite://db/app_extra.sqlite3"
ENV["DATABASE_URL__EXTRA"] = sqlite_url("db/app_extra.sqlite3", dir: @dir)
end

it "creates the databases for all the app's gateways when given --app" do
Expand All @@ -217,8 +217,8 @@ def before_prepare
.and change { Hanami.app.root.join("db", "app_extra.sqlite3").exist? }.to(true)

expect(output).to include_in_order(
"database db/app.sqlite3 created",
"database db/app_extra.sqlite3 created"
"database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} created",
"database #{sqlite_db_name("db/app_extra.sqlite3", dir: @dir)} created"
)
end

Expand All @@ -227,15 +227,15 @@ def before_prepare
.to change { Hanami.app.root.join("db", "app_extra.sqlite3").exist? }.to(true)
.and not_change { Hanami.app.root.join("db", "app.sqlite3").exist? }.from(false)

expect(output).to include "database db/app_extra.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/app_extra.sqlite3", dir: @dir)} created"
expect(output).not_to include "database/app.sqlite3"
end
end

context "slice with gateways" do
def before_prepare
write "slices/main/config/db/.keep", ""
ENV["MAIN__DATABASE_URL__EXTRA"] = "sqlite://db/main_extra.sqlite3"
ENV["MAIN__DATABASE_URL__EXTRA"] = sqlite_url("db/main_extra.sqlite3", dir: @dir)
end

it "creates the databases for all the slices's gateways when given --slice" do
Expand All @@ -244,8 +244,8 @@ def before_prepare
.and change { Hanami.app.root.join("db", "main_extra.sqlite3").exist? }.to(true)

expect(output).to include_in_order(
"database db/main.sqlite3 created",
"database db/main_extra.sqlite3 created"
"database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} created",
"database #{sqlite_db_name("db/main_extra.sqlite3", dir: @dir)} created"
)
end

Expand All @@ -254,7 +254,7 @@ def before_prepare
.to change { Hanami.app.root.join("db", "main_extra.sqlite3").exist? }.to(true)
.and not_change { Hanami.app.root.join("db", "main.sqlite3").exist? }.from(false)

expect(output).to include "database db/main_extra.sqlite3 created"
expect(output).to include "database #{sqlite_db_name("db/main_extra.sqlite3", dir: @dir)} created"
expect(output).not_to include "database/main.sqlite3"
end
end
Expand Down Expand Up @@ -299,7 +299,7 @@ def before_prepare

describe "automatic test env execution" do
before do
ENV["DATABASE_URL"] = "sqlite://db/app.sqlite3"
ENV["DATABASE_URL"] = sqlite_url("db/app.sqlite3", dir: @dir)
end

around do |example|
Expand Down
56 changes: 28 additions & 28 deletions spec/unit/hanami/cli/commands/app/db/drop_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ def before_prepare

describe "sqlite" do
before do
ENV["DATABASE_URL"] = "sqlite://db/app.sqlite3"
ENV["MAIN__DATABASE_URL"] = "sqlite://db/main.sqlite3"
ENV["DATABASE_URL"] = sqlite_url("db/app.sqlite3", dir: @dir)
ENV["MAIN__DATABASE_URL"] = sqlite_url("db/main.sqlite3", dir: @dir)
end

it "drops each database" do
Expand All @@ -72,8 +72,8 @@ def before_prepare
.and change { File.exist?(@dir.join("db", "main.sqlite3")) }
.to false

expect(output).to include "database db/app.sqlite3 dropped"
expect(output).to include "database db/main.sqlite3 dropped"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} dropped"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped"

expect(command).not_to have_received(:exit)
end
Expand All @@ -90,8 +90,8 @@ def before_prepare

expect(File.exist?(@dir.join("db", "main.sqlite3"))).to be true

expect(output).to include "database db/app.sqlite3 dropped"
expect(output).not_to include "db/main.sqlite3"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} dropped"
expect(output).not_to include sqlite_db_name("db/main.sqlite3", dir: @dir)

expect(command).not_to have_received(:exit)
end
Expand All @@ -108,8 +108,8 @@ def before_prepare

expect(File.exist?(@dir.join("db", "app.sqlite3"))).to be true

expect(output).to include "database db/main.sqlite3 dropped"
expect(output).not_to include "db/app.sqlite3"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped"
expect(output).not_to include sqlite_db_name("db/app.sqlite3", dir: @dir)

expect(command).not_to have_received(:exit)
end
Expand All @@ -120,8 +120,8 @@ def before_prepare
expect(File.exist?(@dir.join("db", "app.sqlite3"))).to be false
expect(File.exist?(@dir.join("db", "main.sqlite3"))).to be false

expect(output).to include "database db/app.sqlite3 dropped"
expect(output).to include "database db/main.sqlite3 dropped"
expect(output).to include "database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} dropped"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped"

expect(command).not_to have_received(:exit)
end
Expand All @@ -132,18 +132,18 @@ def before_prepare

allow(File).to receive(:unlink).and_call_original
allow(File).to receive(:unlink)
.with(a_string_including("db/app.sqlite3"))
.with(a_string_including(sqlite_db_name("db/app.sqlite3", dir: @dir)))
.and_raise Errno::EACCES

command.call

expect(File.exist?(@dir.join("db", "app.sqlite3"))).to be true
expect(File.exist?(@dir.join("db", "main.sqlite3"))).to be false

expect(output).to include "failed to drop database db/app.sqlite3"
expect(output).to include "failed to drop database #{sqlite_db_name("db/app.sqlite3", dir: @dir)}"
expect(output).to include "Permission denied" # from Errno::EACCESS

expect(output).to include "database db/main.sqlite3 dropped"
expect(output).to include "database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped"

expect(command).to have_received(:exit).with(1).once
end
Expand All @@ -153,8 +153,8 @@ def before_prepare
write "config/db/.keep", ""
write "slices/main/config/db/.keep", ""

ENV["DATABASE_URL__EXTRA"] = "sqlite://db/app_extra.sqlite3"
ENV["MAIN__DATABASE_URL__EXTRA"] = "sqlite://db/main_extra.sqlite3"
ENV["DATABASE_URL__EXTRA"] = sqlite_url("db/app_extra.sqlite3", dir: @dir)
ENV["MAIN__DATABASE_URL__EXTRA"] = sqlite_url("db/main_extra.sqlite3", dir: @dir)
end

before do
Expand All @@ -170,10 +170,10 @@ def before_prepare
.and change { File.exist?(@dir.join("db", "main_extra.sqlite3")) }.to(false)

expect(output.strip).to eq(<<~TEXT.strip)
=> database db/app.sqlite3 dropped
=> database db/app_extra.sqlite3 dropped
=> database db/main.sqlite3 dropped
=> database db/main_extra.sqlite3 dropped
=> database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} dropped
=> database #{sqlite_db_name("db/app_extra.sqlite3", dir: @dir)} dropped
=> database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped
=> database #{sqlite_db_name("db/main_extra.sqlite3", dir: @dir)} dropped
TEXT

expect(command).not_to have_received(:exit)
Expand All @@ -183,7 +183,7 @@ def before_prepare
context "app with gateways" do
def before_prepare
write "config/db/.keep", ""
ENV["DATABASE_URL__EXTRA"] = "sqlite://db/app_extra.sqlite3"
ENV["DATABASE_URL__EXTRA"] = sqlite_url("db/app_extra.sqlite3", dir: @dir)
end

before do
Expand All @@ -197,8 +197,8 @@ def before_prepare
.and change { File.exist?(@dir.join("db", "app_extra.sqlite3")) }.to false

expect(output).to include_in_order(
"database db/app.sqlite3 dropped",
"database db/app_extra.sqlite3 dropped"
"database #{sqlite_db_name("db/app.sqlite3", dir: @dir)} dropped",
"database #{sqlite_db_name("db/app_extra.sqlite3", dir: @dir)} dropped"
)

expect(command).not_to have_received(:exit)
Expand All @@ -209,15 +209,15 @@ def before_prepare
.to change { File.exist?(@dir.join("db", "app_extra.sqlite3")) }.to(false)
.and not_change { File.exist?(@dir.join("db", "app.sqlite3")) }.from(true)

expect(output).to include "database db/app_extra.sqlite3 dropped"
expect(output).to include "database #{sqlite_db_name("db/app_extra.sqlite3", dir: @dir)} dropped"
expect(output).not_to include "database/app.sqlite3"
end
end

context "slice with gateways" do
def before_prepare
write "slices/main/config/db/.keep", ""
ENV["MAIN__DATABASE_URL__EXTRA"] = "sqlite://db/main_extra.sqlite3"
ENV["MAIN__DATABASE_URL__EXTRA"] = sqlite_url("db/main_extra.sqlite3", dir: @dir)
end

before do
Expand All @@ -231,8 +231,8 @@ def before_prepare
.and change { File.exist?(@dir.join("db", "main_extra.sqlite3")) }.to false

expect(output).to include_in_order(
"database db/main.sqlite3 dropped",
"database db/main_extra.sqlite3 dropped"
"database #{sqlite_db_name("db/main.sqlite3", dir: @dir)} dropped",
"database #{sqlite_db_name("db/main_extra.sqlite3", dir: @dir)} dropped"
)

expect(command).not_to have_received(:exit)
Expand All @@ -243,7 +243,7 @@ def before_prepare
.to change { File.exist?(@dir.join("db", "main_extra.sqlite3")) }.to(false)
.and not_change { File.exist?(@dir.join("db", "main.sqlite3")) }.from(true)

expect(output).to include "database db/main_extra.sqlite3 dropped"
expect(output).to include "database #{sqlite_db_name("db/main_extra.sqlite3", dir: @dir)} dropped"
expect(output).not_to include "database/main.sqlite3"

expect(command).not_to have_received(:exit)
Expand Down Expand Up @@ -448,7 +448,7 @@ def before_prepare

describe "automatic test env execution" do
before do
ENV["DATABASE_URL"] = "sqlite://db/app.sqlite3"
ENV["DATABASE_URL"] = sqlite_url("db/app.sqlite3", dir: @dir)
end

around do |example|
Expand Down
Loading
Loading