lib/sequenced/acts_as_sequenced.rb
require "active_support/core_ext/hash/slice"
require "active_support/core_ext/class/attribute_accessors"
module Sequenced
module ActsAsSequenced
DEFAULT_OPTIONS = {
column: :sequential_id,
start_at: 1
}.freeze
SequencedColumnExists = Class.new(StandardError)
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# Public: Defines ActiveRecord callbacks to set a sequential ID scoped
# on a specific class.
#
# Can be called multiple times to add hooks for different column names.
#
# options - The Hash of options for configuration:
# :scope - The Symbol representing the columm on which the
# sequential ID should be scoped (default: nil)
# :column - The Symbol representing the column that stores the
# sequential ID (default: :sequential_id)
# :start_at - The Integer value at which the sequence should
# start (default: 1)
# :skip - Skips the sequential ID generation when the lambda
# expression evaluates to nil. Gets passed the
# model object
#
# Examples
#
# class Answer < ActiveRecord::Base
# belongs_to :question
# acts_as_sequenced :scope => :question_id
# end
#
# Returns nothing.
def acts_as_sequenced(options = {})
unless defined?(sequenced_options)
include Sequenced::ActsAsSequenced::InstanceMethods
mattr_accessor :sequenced_options, instance_accessor: false
self.sequenced_options = []
before_save :set_sequential_ids
end
options = DEFAULT_OPTIONS.merge(options)
column_name = options[:column]
if sequenced_options.any? { |options| options[:column] == column_name }
raise(SequencedColumnExists, <<-MSG.squish)
Tried to set #{column_name} as sequenced but there was already a
definition here. Did you accidentally call acts_as_sequenced
multiple times on the same column?
MSG
else
sequenced_options << options
end
end
end
module InstanceMethods
def set_sequential_ids
self.class.base_class.sequenced_options.each do |options|
Sequenced::Generator.new(self, options).set
end
end
end
end
end