My Transaction model generates an array of unique ticket numbers based on quantity (an integer column) with the following controller logic:
@transaction.quantity.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }
However, this only ensures that the numbers WITHIN the array are unique.
I need a database validation that checks ALL Transaction.ticket_numbers arrays to ensure that every value (ticket number) is unique across ALL arrays.
Here is my Transaction table within schema.rb:
create_table "transactions", force: :cascade do |t|
t.string "payee"
t.integer "quantity"
t.decimal "debt", precision: 8, scale: 2
t.string "email"
t.string "ministry"
t.integer "status"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "ticket_numbers", default: [], array: true
end
Transaction model from transaction.rb:
require 'csv'
class Transaction < ApplicationRecord
belongs_to :user
validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, :on => [:create, :update] }
validates :payee, presence: true
validates :quantity, numericality: { greater_than_or_equal_to: 1 }
validates :debt, numericality: { greater_than_or_equal_to: 0 }
def self.to_csv
attributes = %w{payee email ministry quantity debt status ticket_numbers}
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |transaction|
csv << attributes.map{ |attr| transaction.send(attr) }
end
end
end
end
Transaction controller:
class TransactionsController < ApplicationController
before_action :load_transaction, only: [:edit, :update, :destroy]
def create
@transaction = Transaction.new(transaction_params)
if @transaction.save
@transaction.quantity.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }
end
@user = @transaction.user
@transactions = @user.transactions
respond_to do |format|
if @transaction.save && @transaction.status == 1
UserMailer.payment_confirmation(@transaction).deliver_later
format.html { redirect_to user_url(@user), notice:'Transaction added & ticket sent' }
format.json { render json: @user, status: :created, location: @user }
elsif @transaction.save && @transaction.status != 1
format.html { redirect_to user_url(@user), notice:'Transaction added' }
format.json { render json: @user, status: :created, location: @user }
else
format.html { render 'users/show' }
format.json { render json: @transaction.errors, status: :unprocessable_entity }
end
end
end
def edit
@user = current_user
end
def update
update_ticket_numbers
respond_to do |format|
if @transaction.update_attributes(transaction_params) && @transaction.status == 1
UserMailer.payment_confirmation(@transaction).deliver_later
format.html { redirect_to user_url(current_user), notice: 'Transaction info updated & confirmation email sent to payee' }
format.json { render json: current_user, status: :created, location: current_user }
elsif @transaction.update_attributes(transaction_params) && @transaction.status != 1
format.html { redirect_to user_url(current_user), notice: 'Transaction info updated' }
format.json { render json: current_user, status: :created, location: current_user }
else
format.html { render :edit }
format.json { render json: @transaction.errors, status: :unprocessable_entity }
end
end
end
def destroy
@transaction.destroy
redirect_to user_url(current_user), notice: 'Transaction deleted'
end
private
def load_transaction
@transaction = Transaction.find(params[:id])
end
def transaction_params
params.require(:transaction).permit(:payee, :email, :ministry, :debt,
:quantity, :status, :user_id, :ticket_numbers)
end
def update_ticket_numbers
if @transaction.update_attributes(transaction_params)
if @transaction.ticket_numbers.length < @transaction.quantity
i = @transaction.quantity - @transaction.ticket_numbers.length
i.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }
elsif @transaction.ticket_numbers.length > @transaction.quantity
i = @transaction.ticket_numbers.length - @transaction.quantity
i.times { @transaction.ticket_numbers.pop }
end
end
end
end
Ticketand implement the constraint with a unique index.