lib/active_record/connection_adapters/oracle_enhanced/procedures.rb
# frozen_string_literal: true
require "active_support"
module ActiveRecord # :nodoc:
# Custom create, update, delete methods functionality.
#
# Example:
#
# class Employee < ActiveRecord::Base
# include ActiveRecord::OracleEnhancedProcedures
#
# set_create_method do
# plsql.employees_pkg.create_employee(
# :p_first_name => first_name,
# :p_last_name => last_name,
# :p_employee_id => nil
# )[:p_employee_id]
# end
#
# set_update_method do
# plsql.employees_pkg.update_employee(
# :p_employee_id => id,
# :p_first_name => first_name,
# :p_last_name => last_name
# )
# end
#
# set_delete_method do
# plsql.employees_pkg.delete_employee(
# :p_employee_id => id
# )
# end
# end
#
module OracleEnhancedProcedures # :nodoc:
module ClassMethods
# Specify custom create method which should be used instead of Rails generated INSERT statement.
# Provided block should return ID of new record.
# Example:
# set_create_method do
# plsql.employees_pkg.create_employee(
# :p_first_name => first_name,
# :p_last_name => last_name,
# :p_employee_id => nil
# )[:p_employee_id]
# end
def set_create_method(&block)
self.custom_create_method = block
end
# Specify custom update method which should be used instead of Rails generated UPDATE statement.
# Example:
# set_update_method do
# plsql.employees_pkg.update_employee(
# :p_employee_id => id,
# :p_first_name => first_name,
# :p_last_name => last_name
# )
# end
def set_update_method(&block)
self.custom_update_method = block
end
# Specify custom delete method which should be used instead of Rails generated DELETE statement.
# Example:
# set_delete_method do
# plsql.employees_pkg.delete_employee(
# :p_employee_id => id
# )
# end
def set_delete_method(&block)
self.custom_delete_method = block
end
end
def self.included(base)
base.class_eval do
extend ClassMethods
class_attribute :custom_create_method
class_attribute :custom_update_method
class_attribute :custom_delete_method
end
end
def destroy # :nodoc:
# check if class has custom delete method
if self.class.custom_delete_method
# wrap destroy in transaction
with_transaction_returning_status do
# run before/after callbacks defined in model
run_callbacks(:destroy) { destroy_using_custom_method }
end
else
super
end
end
private
# Creates a record with custom create method
# and returns its id.
def _create_record
# check if class has custom create method
if self.class.custom_create_method
# run before/after callbacks defined in model
run_callbacks(:create) do
# timestamp
if self.record_timestamps
current_time = current_time_from_proper_timezone
all_timestamp_attributes_in_model.each do |column|
if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil?
write_attribute(column.to_s, current_time)
end
end
end
# run
create_using_custom_method
end
else
super
end
end
def create_using_custom_method
log_custom_method("custom create method", "#{self.class.name} Create") do
self.id = instance_eval(&self.class.custom_create_method)
end
@new_record = false
# Starting from ActiveRecord 3.0.3 @persisted is used instead of @new_record
@persisted = true
id
end
# Updates the associated record with custom update method
# Returns the number of affected rows.
def _update_record(attribute_names = @attributes.keys)
# check if class has custom update method
if self.class.custom_update_method
# run before/after callbacks defined in model
run_callbacks(:update) do
# timestamp
if should_record_timestamps?
current_time = current_time_from_proper_timezone
timestamp_attributes_for_update_in_model.each do |column|
column = column.to_s
next if will_save_change_to_attribute?(column)
write_attribute(column, current_time)
end
end
# update just dirty attributes
if partial_updates?
# Serialized attributes should always be written in case they've been
# changed in place.
update_using_custom_method(changed | (attributes.keys & self.class.columns.select { |column| column.is_a?(Type::Serialized) }))
else
update_using_custom_method(attributes.keys)
end
end
else
super
end
end
def update_using_custom_method(attribute_names)
return 0 if attribute_names.empty?
log_custom_method("custom update method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Update") do
instance_eval(&self.class.custom_update_method)
end
1
end
# Deletes the record in the database with custom delete method
# and freezes this instance to reflect that no changes should
# be made (since they can't be persisted).
def destroy_using_custom_method
unless new_record? || @destroyed
log_custom_method("custom delete method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Destroy") do
instance_eval(&self.class.custom_delete_method)
end
end
@destroyed = true
freeze
end
def log_custom_method(*args, &block)
self.class.connection.send(:log, *args, &block)
end
alias_method :update_record, :_update_record if private_method_defined?(:_update_record)
alias_method :create_record, :_create_record if private_method_defined?(:_create_record)
end
end