# frozen_string_literal: true
require "rom-sql"
require "hanami/utils"
module Hanami
# Hanami::Model migrations
module Model
require "hanami/model/error"
require "hanami/model/association"
require "hanami/model/migration"
# Define a migration
#
# It must define an up/down strategy to write schema changes (up) and to
# rollback them (down).
#
# We can use up and down blocks for custom strategies, or
# only one change block that automatically implements "down" strategy.
#
# @param blk [Proc] a block that defines up/down or change database migration
#
# @since 0.4.0
#
# @example Use up/down blocks
# Hanami::Model.migration do
# up do
# create_table :books do
# primary_key :id
# column :book, String
# end
# end
#
# down do
# drop_table :books
# end
# end
#
# @example Use change block
# Hanami::Model.migration do
# change do
# create_table :books do
# primary_key :id
# column :book, String
# end
# end
#
# # DOWN strategy is automatically generated
# end
def self.migration(&blk)
Migration.new(configuration.gateways[:default], &blk)
end
# SQL adapter
#
# @since 0.7.0
module Sql
require "hanami/model/sql/types"
require "hanami/model/sql/entity/schema"
# Returns a SQL fragment that references a database function by the given name
# This is useful for database migrations
#
# @param name [String,Symbol] the function name
# @return [String] the SQL fragment
#
# @since 0.7.0
#
# @example
# Hanami::Model.migration do
# up do
# execute 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
#
# create_table :source_files do
# column :id, 'uuid', primary_key: true, default: Hanami::Model::Sql.function(:uuid_generate_v4)
# # ...
# end
# end
#
# down do
# drop_table :source_files
# execute 'DROP EXTENSION "uuid-ossp"'
# end
# end
def self.function(name)
Sequel.function(name)
end
# Returns a literal SQL fragment for the given SQL fragment.
# This is useful for database migrations
#
# @param string [String] the SQL fragment
# @return [String] the literal SQL fragment
#
# @since 0.7.0
#
# @example
# Hanami::Model.migration do
# up do
# execute %{
# CREATE TYPE inventory_item AS (
# name text,
# supplier_id integer,
# price numeric
# );
# }
#
# create_table :items do
# column :item, 'inventory_item', default: Hanami::Model::Sql.literal("ROW('fuzzy dice', 42, 1.99)")
# # ...
# end
# end
#
# down do
# drop_table :items
# execute 'DROP TYPE inventory_item'
# end
# end
def self.literal(string)
Sequel.lit(string)
end
# Returns SQL fragment for ascending order for the given column
#
# @param column [Symbol] the column name
# @return [String] the SQL fragment
#
# @since 0.7.0
def self.asc(column)
Sequel.asc(column)
end
# Returns SQL fragment for descending order for the given column
#
# @param column [Symbol] the column name
# @return [String] the SQL fragment
#
# @since 0.7.0
def self.desc(column)
Sequel.desc(column)
end
end
Error.register(ROM::SQL::DatabaseError, DatabaseError)
Error.register(ROM::SQL::ConstraintError, ConstraintViolationError)
Error.register(ROM::SQL::NotNullConstraintError, NotNullConstraintViolationError)
Error.register(ROM::SQL::UniqueConstraintError, UniqueConstraintViolationError)
Error.register(ROM::SQL::CheckConstraintError, CheckConstraintViolationError)
Error.register(ROM::SQL::ForeignKeyConstraintError, ForeignKeyConstraintViolationError)
Error.register(ROM::SQL::UnknownDBTypeError, UnknownDatabaseTypeError)
Error.register(ROM::SQL::MissingPrimaryKeyError, MissingPrimaryKeyError)
Error.register(Java::JavaSql::SQLException, DatabaseError) if Utils.jruby?
end
end
Sequel.default_timezone = :utc
ROM.plugins do
adapter :sql do
register :mapping, Hanami::Model::Plugins::Mapping, type: :command
register :schema, Hanami::Model::Plugins::Schema, type: :command
register :timestamps, Hanami::Model::Plugins::Timestamps, type: :command
end
end