mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +01:00
(quality) replacement of the gem NotifyWith by internal code
This commit is contained in:
parent
10de4fcf5e
commit
54dbd5ef4d
2
Gemfile
2
Gemfile
@ -103,8 +103,6 @@ gem 'elasticsearch-persistence', '~> 5'
|
||||
gem 'elasticsearch-rails', '~> 5'
|
||||
gem 'faraday', '~> 0.17'
|
||||
|
||||
gem 'notify_with'
|
||||
|
||||
gem 'pundit'
|
||||
|
||||
gem 'oj'
|
||||
|
@ -236,10 +236,6 @@ GEM
|
||||
nokogiri (1.13.10)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
notify_with (0.0.2)
|
||||
jbuilder (~> 2.0)
|
||||
rails (>= 4.2.0)
|
||||
responders (~> 2.0)
|
||||
oauth2 (1.4.4)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
@ -531,7 +527,6 @@ DEPENDENCIES
|
||||
message_format
|
||||
mini_magick
|
||||
minitest-reporters
|
||||
notify_with
|
||||
oj
|
||||
omniauth (~> 1.9.2)
|
||||
omniauth-oauth2
|
||||
|
@ -3,8 +3,8 @@
|
||||
# API Controller for resources of type Notification
|
||||
# Notifications are scoped by user
|
||||
class API::NotificationsController < API::ApiController
|
||||
include NotifyWith::NotificationsApi
|
||||
before_action :authenticate_user!
|
||||
before_action :set_notification, only: :update
|
||||
|
||||
# notifications can have anything attached, so we won't eager load the whole database
|
||||
around_action :skip_bullet, if: -> { defined?(Bullet) }
|
||||
@ -14,7 +14,10 @@ class API::NotificationsController < API::ApiController
|
||||
|
||||
def index
|
||||
loop do
|
||||
@notifications = current_user.notifications.includes(:attached_object).page(params[:page]).per(NOTIFICATIONS_PER_PAGE).order('created_at DESC')
|
||||
@notifications = current_user.notifications
|
||||
.includes(:attached_object)
|
||||
.page(params[:page])
|
||||
.per(NOTIFICATIONS_PER_PAGE).order('created_at DESC')
|
||||
# we delete obsolete notifications on first access
|
||||
break unless delete_obsoletes(@notifications)
|
||||
end
|
||||
@ -27,7 +30,11 @@ class API::NotificationsController < API::ApiController
|
||||
|
||||
def last_unread
|
||||
loop do
|
||||
@notifications = current_user.notifications.includes(:attached_object).where(is_read: false).limit(3).order('created_at DESC')
|
||||
@notifications = current_user.notifications
|
||||
.includes(:attached_object)
|
||||
.where(is_read: false)
|
||||
.limit(3)
|
||||
.order('created_at DESC')
|
||||
# we delete obsolete notifications on first access
|
||||
break unless delete_obsoletes(@notifications)
|
||||
end
|
||||
@ -49,8 +56,22 @@ class API::NotificationsController < API::ApiController
|
||||
render :index
|
||||
end
|
||||
|
||||
def update
|
||||
@notification.mark_as_read
|
||||
render :show
|
||||
end
|
||||
|
||||
def update_all
|
||||
current_user.notifications.where(is_read: false).find_each(&:mark_as_read)
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_notification
|
||||
@notification = current_user.notifications.find(params[:id])
|
||||
end
|
||||
|
||||
def delete_obsoletes(notifications)
|
||||
cleaned = false
|
||||
notifications.each do |n|
|
||||
|
@ -1,11 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Handle most of the emails sent by the platform. Triggered by notifications
|
||||
class NotificationsMailer < NotifyWith::NotificationsMailer
|
||||
default from: ->(*) { Setting.get('email_from') }
|
||||
layout 'notifications_mailer'
|
||||
|
||||
helper :application
|
||||
class NotificationsMailer < BaseMailer
|
||||
after_action :mark_notification_as_send
|
||||
|
||||
def send_mail_by(notification)
|
||||
@notification = notification
|
||||
@ -13,14 +10,14 @@ class NotificationsMailer < NotifyWith::NotificationsMailer
|
||||
@attached_object = notification.attached_object
|
||||
|
||||
unless respond_to?(notification.notification_type)
|
||||
class_eval %{
|
||||
def #{notification.notification_type}
|
||||
mail to: @recipient.email,
|
||||
subject: t('notifications_mailer.#{notification.notification_type}.subject'),
|
||||
template_name: '#{notification.notification_type}',
|
||||
content_type: 'text/html'
|
||||
end
|
||||
}, __FILE__, __LINE__ - 7
|
||||
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
||||
def #{notification.notification_type} # def notify_admin_when_project_published
|
||||
mail to: @recipient.email, # mail to: @recipient.email,
|
||||
subject: t('notifications_mailer.#{notification.notification_type}.subject'), # subject: t('notifications_mailer.notify_admin_when_project_published.subject'),
|
||||
template_name: '#{notification.notification_type}', # template_name: 'notify_admin_when_project_published',
|
||||
content_type: 'text/html' # content_type: 'text/html'
|
||||
end # end
|
||||
RUBY
|
||||
end
|
||||
|
||||
send(notification.notification_type)
|
||||
@ -28,10 +25,6 @@ class NotificationsMailer < NotifyWith::NotificationsMailer
|
||||
Rails.logger.error "[NotificationsMailer] notification cannot be sent: #{e}"
|
||||
end
|
||||
|
||||
def helpers
|
||||
ActionController::Base.helpers
|
||||
end
|
||||
|
||||
def notify_user_when_invoice_ready
|
||||
attachments[@attached_object.filename] = File.read(@attached_object.file)
|
||||
mail(to: @recipient.email,
|
||||
@ -59,4 +52,10 @@ class NotificationsMailer < NotifyWith::NotificationsMailer
|
||||
subject: t('notifications_mailer.notify_member_create_reservation.subject'),
|
||||
template_name: 'notify_member_create_reservation')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mark_notification_as_send
|
||||
@notification.mark_as_send unless @notification.is_send
|
||||
end
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
# Abuse is a report made by a visitor (not especially a logged user) who has signaled a content that seems abusive to his eyes.
|
||||
# It is currently used with projects.
|
||||
class Abuse < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
belongs_to :signaled, polymorphic: true
|
||||
|
||||
@ -11,7 +11,6 @@ class Abuse < ApplicationRecord
|
||||
|
||||
validates :first_name, :last_name, :email, :message, presence: true
|
||||
|
||||
|
||||
private
|
||||
|
||||
def notify_admins_abuse_reported
|
||||
|
15
app/models/concerns/notification_attached_object.rb
Normal file
15
app/models/concerns/notification_attached_object.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Concern use to delete a notification if its attached object is detroyed
|
||||
module NotificationAttachedObject
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
after_destroy :clean_notification
|
||||
|
||||
def clean_notification
|
||||
Notification.where('attached_object_id = :id and attached_object_type = :type', { id: id, type: self.class.to_s })
|
||||
.destroy_all
|
||||
end
|
||||
end
|
||||
end
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Event is an happening organized by the Fablab about a general topic, which does not involve Machines or trainings member's skills.
|
||||
class Event < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
include ApplicationHelper
|
||||
|
||||
has_one :event_image, as: :viewable, dependent: :destroy
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Invoice correspond to a single purchase made by an user. This purchase is linked to one or many invoice_items
|
||||
class Invoice < PaymentDocument
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
require 'fileutils'
|
||||
scope :only_invoice, -> { where(type: nil) }
|
||||
|
||||
|
@ -2,7 +2,36 @@
|
||||
|
||||
# Notification is an in-system alert that is shown to a specific user until it is marked as read.
|
||||
class Notification < ApplicationRecord
|
||||
include NotifyWith::Notification
|
||||
belongs_to :notification_type
|
||||
belongs_to :receiver, polymorphic: true
|
||||
belongs_to :attached_object, polymorphic: true
|
||||
|
||||
validates :receiver_id,
|
||||
:receiver_type,
|
||||
:attached_object_id,
|
||||
:attached_object_type,
|
||||
:notification_type_id,
|
||||
presence: true
|
||||
|
||||
def notification_type
|
||||
NotificationType.find(notification_type_id).name
|
||||
end
|
||||
|
||||
def mark_as_read
|
||||
update(is_read: true)
|
||||
end
|
||||
|
||||
def mark_as_send
|
||||
update(is_send: true)
|
||||
end
|
||||
|
||||
def deliver_now
|
||||
NotificationsMailer.send_mail_by(self).deliver_now if save
|
||||
end
|
||||
|
||||
def deliver_later
|
||||
NotificationsMailer.send_mail_by(self).deliver_later if save
|
||||
end
|
||||
|
||||
def get_meta_data(key)
|
||||
meta_data.try(:[], key.to_s)
|
||||
|
@ -1,84 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# NotificationType defines the different types of Notification.
|
||||
class NotificationType
|
||||
include NotifyWith::NotificationType
|
||||
|
||||
# DANGER: dont remove a notification type!!!
|
||||
notification_type_names %w[
|
||||
notify_admin_when_project_published
|
||||
notify_project_collaborator_to_valid
|
||||
notify_project_author_when_collaborator_valid
|
||||
notify_user_training_valid
|
||||
notify_member_subscribed_plan
|
||||
notify_member_create_reservation
|
||||
notify_member_subscribed_plan_is_changed
|
||||
notify_admin_member_create_reservation
|
||||
notify_member_slot_is_modified
|
||||
notify_admin_slot_is_modified
|
||||
notify_admin_when_user_is_created
|
||||
notify_admin_subscribed_plan
|
||||
notify_user_when_invoice_ready
|
||||
notify_member_subscription_will_expire_in_7_days
|
||||
notify_member_subscription_is_expired
|
||||
notify_admin_subscription_will_expire_in_7_days
|
||||
notify_admin_subscription_is_expired
|
||||
notify_admin_subscription_canceled
|
||||
notify_member_subscription_canceled
|
||||
notify_user_when_avoir_ready
|
||||
notify_member_slot_is_canceled
|
||||
notify_admin_slot_is_canceled
|
||||
notify_partner_subscribed_plan
|
||||
notify_member_subscription_extended
|
||||
notify_admin_subscription_extended
|
||||
notify_admin_user_group_changed
|
||||
notify_user_user_group_changed
|
||||
notify_admin_when_user_is_imported
|
||||
notify_user_profile_complete
|
||||
notify_user_auth_migration
|
||||
notify_admin_user_merged
|
||||
notify_admin_profile_complete
|
||||
notify_admin_abuse_reported
|
||||
notify_admin_invoicing_changed
|
||||
notify_user_wallet_is_credited
|
||||
notify_admin_user_wallet_is_credited
|
||||
notify_admin_export_complete
|
||||
notify_member_about_coupon
|
||||
notify_member_reservation_reminder
|
||||
notify_admin_free_disk_space
|
||||
notify_admin_close_period_reminder
|
||||
notify_admin_archive_complete
|
||||
notify_privacy_policy_changed
|
||||
notify_admin_import_complete
|
||||
notify_admin_refund_created
|
||||
notify_admins_role_update
|
||||
notify_user_role_update
|
||||
notify_admin_objects_stripe_sync
|
||||
notify_user_when_payment_schedule_ready
|
||||
notify_admin_payment_schedule_failed
|
||||
notify_member_payment_schedule_failed
|
||||
notify_admin_payment_schedule_check_deadline
|
||||
notify_admin_payment_schedule_transfer_deadline
|
||||
notify_admin_payment_schedule_error
|
||||
notify_member_payment_schedule_error
|
||||
notify_admin_payment_schedule_gateway_canceled
|
||||
notify_member_payment_schedule_gateway_canceled
|
||||
notify_admin_user_proof_of_identity_files_created
|
||||
notify_admin_user_proof_of_identity_files_updated
|
||||
notify_user_is_validated
|
||||
notify_user_is_invalidated
|
||||
notify_user_proof_of_identity_refusal
|
||||
notify_admin_user_proof_of_identity_refusal
|
||||
notify_user_order_is_ready
|
||||
notify_user_order_is_canceled
|
||||
notify_user_order_is_refunded
|
||||
notify_admin_low_stock_threshold
|
||||
notify_admin_training_auto_cancelled
|
||||
notify_member_training_auto_cancelled
|
||||
notify_member_training_authorization_expired
|
||||
notify_member_training_invalidated
|
||||
]
|
||||
# deprecated:
|
||||
# - notify_member_subscribed_plan_is_changed
|
||||
# - notify_admin_invoicing_changed
|
||||
class NotificationType < ApplicationRecord
|
||||
has_many :notifications, dependent: :destroy
|
||||
validates :name, uniqueness: true, presence: true
|
||||
end
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# OfferDay provides a way for admins to extend the subscription of a member for free.
|
||||
class OfferDay < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
has_many :invoice_items, as: :object, dependent: :destroy
|
||||
belongs_to :subscription
|
||||
@ -32,5 +32,4 @@ class OfferDay < ApplicationRecord
|
||||
attached_object: subscription,
|
||||
meta_data: meta_data
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -4,7 +4,7 @@
|
||||
# It can describe the steps taken by the fab-user to build his object, provide photos, description, attached CAO files, etc.
|
||||
class Project < ApplicationRecord
|
||||
include AASM
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
include OpenlabSync
|
||||
include PgSearch::Model
|
||||
|
||||
|
@ -3,14 +3,14 @@
|
||||
# ProjectUser is the relation table between a Project and an User.
|
||||
# Users are collaborators to a Project, with write access if they have confirmed their participation.
|
||||
class ProjectUser < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
||||
before_create :generate_valid_token
|
||||
after_commit :notify_project_collaborator_to_valid, on: :create
|
||||
after_update :notify_project_author_when_collaborator_valid, if: :saved_change_to_is_valid?
|
||||
after_commit :notify_project_collaborator_to_valid, on: :create
|
||||
|
||||
private
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Slots are for Machine, Space and Training reservations.
|
||||
# Tickets are for Event reservations.
|
||||
class Reservation < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
include ICalendarConcern
|
||||
|
||||
belongs_to :statistic_profile
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Slots duration are defined globally by Setting.get('slot_duration') but can be
|
||||
# overridden per availability.
|
||||
class Slot < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
has_many :slots_reservations, dependent: :destroy
|
||||
has_many :reservations, through: :slots_reservations
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Stores trainings validated per user (non validated trainings are only recorded in reservations)
|
||||
class StatisticProfileTraining < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
belongs_to :statistic_profile
|
||||
belongs_to :training
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Subscription is an active or archived subscription of an User to a Plan
|
||||
class Subscription < ApplicationRecord
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
include NotificationAttachedObject
|
||||
|
||||
belongs_to :plan
|
||||
belongs_to :statistic_profile
|
||||
|
@ -3,9 +3,7 @@
|
||||
# User is a physical or moral person with its authentication parameters
|
||||
# It is linked to the Profile model with hold information about this person (like address, name, etc.)
|
||||
class User < ApplicationRecord
|
||||
include NotifyWith::NotificationReceiver
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
|
||||
include NotificationAttachedObject
|
||||
include SingleSignOnConcern
|
||||
include UserRoleConcern
|
||||
include UserRessourcesConcern
|
||||
@ -52,6 +50,8 @@ class User < ApplicationRecord
|
||||
has_many :proof_of_identity_files, dependent: :destroy
|
||||
has_many :proof_of_identity_refusals, dependent: :destroy
|
||||
|
||||
has_many :notifications, as: :receiver, dependent: :destroy
|
||||
|
||||
# fix for create admin user
|
||||
before_save do
|
||||
email&.downcase!
|
||||
|
@ -3,6 +3,7 @@
|
||||
# track of all transactions payed using the given wallet
|
||||
class WalletTransaction < ApplicationRecord
|
||||
include AmountConcern
|
||||
include NotificationAttachedObject
|
||||
|
||||
belongs_to :invoicing_profile
|
||||
belongs_to :wallet
|
||||
|
@ -3,17 +3,14 @@
|
||||
# send notification to one or several receiver with a type, an attached object and an optional meta data
|
||||
class NotificationCenter
|
||||
def self.call(type: nil, receiver: nil, attached_object: nil, meta_data: {})
|
||||
if receiver.respond_to?(:each)
|
||||
receiver.each do |user|
|
||||
Notification.new(meta_data: meta_data)
|
||||
.send_notification(type: type, attached_object: attached_object)
|
||||
.to(user)
|
||||
.deliver_later
|
||||
end
|
||||
else
|
||||
Notification.new(meta_data: meta_data)
|
||||
.send_notification(type: type, attached_object: attached_object)
|
||||
.to(receiver)
|
||||
receiver = [receiver] unless receiver.respond_to?(:each)
|
||||
receiver.each do |user|
|
||||
Notification.new(
|
||||
meta_data: meta_data,
|
||||
attached_object: attached_object,
|
||||
receiver: user,
|
||||
notification_type: NotificationType.find_by(name: type)
|
||||
)
|
||||
.deliver_later
|
||||
end
|
||||
end
|
||||
|
@ -1 +0,0 @@
|
||||
NotifyWith.mailer = NotificationsMailer
|
105
db/migrate/20230126160900_create_notification_types.rb
Normal file
105
db/migrate/20230126160900_create_notification_types.rb
Normal file
@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Creates Notification Types table to record every notification type (previously
|
||||
# stored in an Array), and records the existing types.
|
||||
|
||||
# This migration is linked to the abandon of the NotifyWith gem. Notification Types
|
||||
# will now be store in database and we will manage ourself the Notification system.
|
||||
class CreateNotificationTypes < ActiveRecord::Migration[5.2]
|
||||
NAMES = %w[
|
||||
notify_admin_when_project_published
|
||||
notify_project_collaborator_to_valid
|
||||
notify_project_author_when_collaborator_valid
|
||||
notify_user_training_valid
|
||||
notify_member_subscribed_plan
|
||||
notify_member_create_reservation
|
||||
notify_member_subscribed_plan_is_changed
|
||||
notify_admin_member_create_reservation
|
||||
notify_member_slot_is_modified
|
||||
notify_admin_slot_is_modified
|
||||
notify_admin_when_user_is_created
|
||||
notify_admin_subscribed_plan
|
||||
notify_user_when_invoice_ready
|
||||
notify_member_subscription_will_expire_in_7_days
|
||||
notify_member_subscription_is_expired
|
||||
notify_admin_subscription_will_expire_in_7_days
|
||||
notify_admin_subscription_is_expired
|
||||
notify_admin_subscription_canceled
|
||||
notify_member_subscription_canceled
|
||||
notify_user_when_avoir_ready
|
||||
notify_member_slot_is_canceled
|
||||
notify_admin_slot_is_canceled
|
||||
notify_partner_subscribed_plan
|
||||
notify_member_subscription_extended
|
||||
notify_admin_subscription_extended
|
||||
notify_admin_user_group_changed
|
||||
notify_user_user_group_changed
|
||||
notify_admin_when_user_is_imported
|
||||
notify_user_profile_complete
|
||||
notify_user_auth_migration
|
||||
notify_admin_user_merged
|
||||
notify_admin_profile_complete
|
||||
notify_admin_abuse_reported
|
||||
notify_admin_invoicing_changed
|
||||
notify_user_wallet_is_credited
|
||||
notify_admin_user_wallet_is_credited
|
||||
notify_admin_export_complete
|
||||
notify_member_about_coupon
|
||||
notify_member_reservation_reminder
|
||||
notify_admin_free_disk_space
|
||||
notify_admin_close_period_reminder
|
||||
notify_admin_archive_complete
|
||||
notify_privacy_policy_changed
|
||||
notify_admin_import_complete
|
||||
notify_admin_refund_created
|
||||
notify_admins_role_update
|
||||
notify_user_role_update
|
||||
notify_admin_objects_stripe_sync
|
||||
notify_user_when_payment_schedule_ready
|
||||
notify_admin_payment_schedule_failed
|
||||
notify_member_payment_schedule_failed
|
||||
notify_admin_payment_schedule_check_deadline
|
||||
notify_admin_payment_schedule_transfer_deadline
|
||||
notify_admin_payment_schedule_error
|
||||
notify_member_payment_schedule_error
|
||||
notify_admin_payment_schedule_gateway_canceled
|
||||
notify_member_payment_schedule_gateway_canceled
|
||||
notify_admin_user_proof_of_identity_files_created
|
||||
notify_admin_user_proof_of_identity_files_updated
|
||||
notify_user_is_validated
|
||||
notify_user_is_invalidated
|
||||
notify_user_proof_of_identity_refusal
|
||||
notify_admin_user_proof_of_identity_refusal
|
||||
notify_user_order_is_ready
|
||||
notify_user_order_is_canceled
|
||||
notify_user_order_is_refunded
|
||||
notify_admin_low_stock_threshold
|
||||
notify_admin_training_auto_cancelled
|
||||
].freeze
|
||||
|
||||
def up
|
||||
create_table :notification_types do |t|
|
||||
t.string :name, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :notification_types, :name, unique: true
|
||||
|
||||
# Records previous notification types
|
||||
# Index start at 1. This is required due to previous functionning of the NotifyWith gem
|
||||
NAMES.each.with_index(1) do |type, index|
|
||||
NotificationType.create!(id: index, name: type)
|
||||
end
|
||||
|
||||
last_id = NotificationType.order(:id).last.id
|
||||
execute "SELECT setval('public.notification_types_id_seq', #{last_id})"
|
||||
|
||||
add_foreign_key :notifications, :notification_types
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key :notifications, :notification_types
|
||||
drop_table :notification_types
|
||||
end
|
||||
end
|
@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2023_01_31_104958) do
|
||||
ActiveRecord::Schema.define(version: 2023_01_26_160900) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "fuzzystrmatch"
|
||||
|
Loading…
Reference in New Issue
Block a user