Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
88d63ed
Migrate from Rails to ROM Sequel migrations
christinach Dec 12, 2025
e9f9100
Load the lando env in spec_helper.
christinach Dec 16, 2025
f548d7d
Check if rom exists before accessing it.
christinach Dec 16, 2025
46c243f
Pass ALLSEARCH_ROM in rom_container
christinach Dec 16, 2025
7140ef5
Add missing text search configuration to migration
sandbergja Dec 16, 2025
66c3461
Add missing text search configuration unaccented_dict
christinach Dec 16, 2025
c495a68
Update migration syntax to support name_searchable and searchable
christinach Dec 16, 2025
8560f7f
Use tsvector type in rom migration
sandbergja Dec 16, 2025
d91c59c
Get name_searchable working
sandbergja Dec 16, 2025
097aacc
migration library staff reords - Use correct syntax for tsvector :nam…
christinach Dec 16, 2025
59a98c1
Address failing tests
sandbergja Dec 16, 2025
f909f32
Update syntax for database records searchable column
sandbergja Dec 16, 2025
651b375
Get all tests passing
sandbergja Dec 16, 2025
a3ee206
Use rom:migrate task in circleci
sandbergja Dec 16, 2025
0b15de2
Fix rubocop erros
christinach Dec 16, 2025
d5154a6
Extract Rails.application.config.rom fron CSVLoadingService#initialize
christinach Dec 17, 2025
4e4761b
Update rom_factory_spec with new specs
christinach Dec 17, 2025
fb66f56
Update best bet loading service spec to test
christinach Dec 17, 2025
4face39
Update rom_factory_spec and test failure when sequel_schema_migrations
christinach Dec 17, 2025
09e4865
Update rom_factory_spec and test
christinach Dec 17, 2025
28d4a76
Update rom:migrate rake task to point to the release_path
christinach Dec 17, 2025
8203d60
In the rom_migrate rake file clear the rails rake db:migrate task
christinach Dec 17, 2025
d0ae1e4
For the existing tables in the DB run rom:migrate_applied before runn…
christinach Dec 17, 2025
cd591f9
Rename migrate directory with rails migrations
christinach Dec 17, 2025
939c3b8
Update rake_migrations tasks to use the db namespace instead of the rom.
christinach Dec 17, 2025
22c16cf
Remove eval to fix security rubocop warning
christinach Dec 18, 2025
78a98f3
add a one time rake task to clear the old rails migrations and
christinach Dec 18, 2025
1e83fe3
Run one time migration to remove rails migrations and use rom
christinach Dec 18, 2025
e83b299
The deploy on the server is failing because the schema_migrations tab…
christinach Dec 18, 2025
afb6834
Change column type from varchar to bigint with explicit casting
christinach Dec 18, 2025
ce38594
Truncate schema_migrations table
christinach Dec 18, 2025
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
command: dockerize -wait tcp://localhost:5432 -timeout 1m
- run:
name: Database setup
command: bundle exec rake db:setup
command: RAILS_ENV=test bundle exec rake db:migrate
- ruby/install-deps
- ruby/rspec-test:
include: spec/**/*_spec.rb
Expand Down
2 changes: 1 addition & 1 deletion app/models/library_staff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LibraryStaff

attr_reader :query_terms

def initialize(query_terms:, rom: Rails.application.config.rom)
def initialize(query_terms:, rom: ALLSEARCH_ROM)
@query_terms = query_terms
@rom = rom
end
Expand Down
2 changes: 1 addition & 1 deletion app/repositories/repository_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class RepositoryFactory
define_singleton_method method_name do
return instance_variable_get("@#{method_name}") if instance_variable_defined?("@#{method_name}")

rom = Rails.application.config.rom
rom = RomFactory.new.require_rom!
instance_variable_set("@#{method_name}", new(rom).send(method_name))
end
end
Expand Down
9 changes: 8 additions & 1 deletion app/services/csv_loading_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CSVLoadingService
# rom_container: ALLSEARCH_ROM and remove the offending line
def initialize(logger: ALLSEARCH_LOGGER, rom_container: nil)
@logger = logger
@rom_container = rom_container || Rails.application.config&.rom || RomFactory.new.require_rom!
@rom_container = rom_container || rails_rom_container || RomFactory.new.require_rom!
end

def run
Expand All @@ -24,6 +24,13 @@ def run

attr_reader :csv, :logger, :rom_container

# :reek:UtilityFunction
def rails_rom_container
Rails.application.config.rom
rescue NoMethodError
nil
end

def fetch_data
contents = uri.open
@csv = CSV.new(contents)
Expand Down
5 changes: 3 additions & 2 deletions config/db_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

require_relative 'allsearch_configs'
require 'erb'
require 'sequel'
require 'yaml'
require 'sequel'

begin
db_config = ALLSEARCH_CONFIGS[:database]

DB = Sequel.postgres(db_config[:database], user: db_config[:username], password: db_config[:password],
host: db_config[:host], port: db_config[:port])
rescue StandardError
nil
DB = nil
end
1 change: 0 additions & 1 deletion config/initializers/rom_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# This initializer is responsible for making a ROM container available
# to the application

# Initialize rom config attribute to ensure it always exists
Rails.application.config.rom = nil

result = RomFactory.new.rom_if_available
Expand Down
15 changes: 15 additions & 0 deletions db/rom_migrate/20251212143343_create_best_bet_records.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

Sequel.migration do
change do
create_table(:best_bet_records) do
primary_key :id
String :title
String :description
String :url
column :search_terms, 'text[]'
Date :last_update
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
end
end
end
16 changes: 16 additions & 0 deletions db/rom_migrate/20251212150000_create_oauth_tokens.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Sequel.migration do
change do
create_table(:oauth_tokens) do
primary_key :id
String :service, null: false
String :endpoint, null: false
String :token, null: true
DateTime :expiration_time, null: true
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
end
add_index :oauth_tokens, :service, unique: true
add_index :oauth_tokens, :endpoint, unique: true
end
end
15 changes: 15 additions & 0 deletions db/rom_migrate/20251212150500_create_banners.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

Sequel.migration do
change do
create_table(:banners) do
primary_key :id
String :text, text: true, default: ''
Boolean :display_banner, default: false
Integer :alert_status, default: 1
Boolean :dismissible, default: true
Boolean :autoclear, default: false
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
end
end
end
29 changes: 29 additions & 0 deletions db/rom_migrate/20251212161222_create_library_database_records.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

Sequel.migration do
change do
create_table(:library_database_records) do
primary_key :id
bigint :libguides_id, null: false
String :name, null: false
String :description
column :alt_names, 'text[]'
String :alt_names_concat
String :url
String :friendly_url
column :subjects, 'text[]'
String :subjects_concat
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP

tsvector :searchable,
generated_always_as: Sequel.lit(
"setweight(to_tsvector('english', coalesce(name,'')), 'A') || " \
"setweight(to_tsvector('english', coalesce(alt_names_concat,'')), 'B') || " \
"setweight(to_tsvector('english', coalesce(description,'')), 'C') || " \
"setweight(to_tsvector('english', coalesce(subjects_concat,'')), 'D') ",
)
end
add_index :library_database_records, :searchable,
using: :gin, name: 'searchable_idx'
end
end
62 changes: 62 additions & 0 deletions db/rom_migrate/20251215120000_create_library_staff_records.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

Sequel.migration do
change do
execute "CREATE ExTENSION IF NOT EXISTS unaccent;"
execute <<~SQL
CREATE TEXT SEARCH CONFIGURATION unaccented_dict ( COPY = english );
ALTER TEXT SEARCH CONFIGURATION unaccented_dict ALTER MAPPING FOR hword, hword_part, word WITH unaccent, simple;
SQL
execute <<~SQL
CREATE TEXT SEARCH CONFIGURATION unaccented_simple_dict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION unaccented_simple_dict ALTER MAPPING FOR hword, hword_part, word WITH unaccent, simple;
SQL
create_table(:library_staff_records) do
primary_key :id
bigint :puid, null: false
String :netid, null: false
String :phone
String :name, null: false
String :last_name
String :first_name
String :middle_name
String :title, null: false
String :library_title, null: false
String :email, null: false
String :team
String :division
String :department
String :unit
String :office
String :building
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
String :areas_of_study
String :other_entities
String :my_scheduler_link
String :pronouns

tsvector :name_searchable,
generated_always_as: Sequel.function(
:to_tsvector,
'public.unaccented_simple_dict',
Sequel.lit("coalesce(name, '') || ' ' || coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, '')")
)

String :bio

tsvector :searchable,
generated_always_as: Sequel.function(
:to_tsvector,
'public.unaccented_dict',
Sequel.lit("coalesce(title, '') || ' ' || coalesce(email, '') || ' ' || coalesce(department, '') || ' ' || coalesce(office, '') || ' ' || coalesce(building, '') || ' ' || coalesce(team, '') || ' ' || coalesce(division, '') || ' ' || coalesce(unit, '') || ' ' || coalesce(areas_of_study, '') || ' ' || coalesce(bio, '') || ' ' || coalesce(other_entities, '')")
)

end

add_index :library_staff_records, :name_searchable,
using: :gin, name: 'staff_name_search_idx'

add_index :library_staff_records, :searchable,
using: :gin, name: 'staff_search_idx'
end
end
24 changes: 24 additions & 0 deletions db/rom_migrate/20251215121000_create_flipper_tables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

Sequel.migration do
change do
create_table(:flipper_features) do
primary_key :id
String :key, null: false
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
end

add_index :flipper_features, :key, unique: true

create_table(:flipper_gates) do
primary_key :id
String :feature_key, null: false
String :key, null: false
column :value, 'text'
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
end

add_index :flipper_gates, [:feature_key, :key, :value], unique: true, name: 'flipper_gates_feature_key_key_value_idx'
end
end
44 changes: 31 additions & 13 deletions init/rom_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'rom-sql'
require 'dry-monads'
require_relative 'environment'
require_relative '../config/db_connection'
require allsearch_path 'init/logger'
require allsearch_path 'app/relations/banner_relation'
require allsearch_path 'app/relations/best_bet_relation'
Expand All @@ -29,45 +30,62 @@ def database_if_available

def rom_if_available
db_connection
.bind { |connection| verify_database_ready(connection) }
.bind { |connection| verify_db_connection_is_ready(connection) }
.bind { |connection| verify_required_rom_tables(connection) }
.bind { |connection| rom_container(connection) }
end

private

def db_connection
return Success(DB) if defined?(DB) && DB

db_config = ALLSEARCH_CONFIGS[:database]
Success(Sequel.postgres(db_config[:database], user: db_config[:username], password: db_config[:password],
host: db_config[:host], port: db_config[:port]))
rescue StandardError => error
Failure(error)
end

def rom_container(db_connection)
rom_config = ROM::Configuration.new(:sql, db_connection)
rom_config.register_relation BannerRelation
rom_config.register_relation BestBetRelation
rom_config.register_relation LibraryDatabaseRelation
rom_config.register_relation LibraryStaffRelation
rom_config.register_relation OAuthTokenRelation
rom_config.default.use_logger ALLSEARCH_LOGGER
Success(ROM.container(rom_config))
end

# rubocop:disable Metrics/MethodLength
def verify_database_ready(connection)
required_tables = [:schema_migrations, :ar_internal_metadata]
def verify_required_rom_tables(connection)
# Check forschema_migrations table (created by ROM)
unless connection.table_exists?(:schema_migrations)
return Failure(StandardError.new(
'ROM migration tracking table does not exist. Run: bundle exec rake db:migrate'
))
end

# Check for required tables that ROM relations depend on
required_tables = [:best_bet_records, :oauth_tokens, :library_database_records, :library_staff_records, :banners]
missing_tables = required_tables.reject { |table| connection.table_exists?(table) }

if missing_tables.empty?
Success(connection)
else
Failure(StandardError.new(
"Database is missing tables: #{missing_tables.join(', ')}. Please run migrations/load the db structure."
"Required tables missing: #{missing_tables.join(', ')}. Run: bundle exec rake db:migrate"
))
end
rescue StandardError => error
Failure(error)
end
# rubocop:enable Metrics/MethodLength

def rom_container(db_connection)
rom_config = ROM::Configuration.new(:sql, db_connection)
rom_config.register_relation BannerRelation
rom_config.register_relation BestBetRelation
rom_config.register_relation LibraryDatabaseRelation
rom_config.register_relation LibraryStaffRelation
rom_config.register_relation OAuthTokenRelation
rom_config.default.use_logger ALLSEARCH_LOGGER
Success(ROM.container(rom_config))
def verify_db_connection_is_ready(connection)
Success(connection)
rescue StandardError => error
Failure(error)
end
end
17 changes: 17 additions & 0 deletions lib/capistrano/tasks/rom_migrate.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

# Override default Rails migrate step to run ROM/Sequel migrations instead
namespace :deploy do
Rake::Task['deploy:migrate'].clear if Rake::Task.task_defined?('deploy:migrate')

desc 'Run ROM/Sequel migrations'
task :migrate do
on roles(:db) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :rake, 'db:migrate_to_rom'
end
end
end
end
end
28 changes: 28 additions & 0 deletions lib/tasks/fix_sequences.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

namespace :rom do
desc 'Fix missing sequences for primary keys'
task :fix_sequences do
require_relative '../../init/rom_factory'

conn = RomFactory.new.database_if_available.value!

tables = [:best_bet_records, :oauth_tokens, :library_databases, :library_staff_documents, :banners]

tables.each do |table|
next unless conn.table_exists?(table)

sequence_name = "#{table}_id_seq"

conn.run("CREATE SEQUENCE IF NOT EXISTS #{sequence_name}")

conn.run("ALTER TABLE #{table} ALTER COLUMN id SET DEFAULT nextval('#{sequence_name}')")

conn.run("SELECT setval('#{sequence_name}', (SELECT COALESCE(MAX(id), 0) + 1 FROM #{table}))")

puts "Fixed sequence for #{table}"
end

puts 'All sequences fixed.'
end
end
Loading