lib/has_reindexable_associations.rb
require 'has_reindexable_associations/version'
require 'rails/all'
require 'searchkick'
# To avoid full model reindex, one can't just call reindex on a scope.
# One must call reindex_async on each associated record.
# Usage:
# MyClass < ActiveRecord::Base
# include HasReindexableAssociations
# belongs_to :some_association
# has_many :some_associations
# has_reindexable_associations :some_association, :some_associations
module HasReindexableAssociations
extend ActiveSupport::Concern
class_methods do
cattr_writer :reindexable_associations
# Set this to `true` before any seeds or imports to reindex associations later on your own like this:
# MyClass.reindexable_associations_skip = true
# MyClass.import(...)
# MyClass.reindex
# MyClass.reindexable_associations_skip = false
# MyClass.reindexable_associations.each { |association| MyClass.send(association).model.reindex }
cattr_accessor :reindexable_associations_skip
def has_reindexable_associations(*args)
# @@ is shared between classes via concern, not what we want
# @@reindexable_associations = args
class_variable_set :@@reindexable_associations, args
end
def reindexable_associations
# @@reindexable_associations || []
class_variable_get :@@reindexable_associations
rescue NameError
[]
end
end
included do
after_commit :reindex_associations
private
def reindex_associations
self.class.reindexable_associations.each do |reindexable_association|
reindex_associations_process(reindexable_association)
end
end
def reindex_associations_process(reindexable_association)
record_or_records = reindex_associations_scope(reindexable_association)
if record_or_records.respond_to?(:to_a)
reindex_associations_process_records(record_or_records, reindexable_association)
else
reindex_associations_process_record(record_or_records, reindexable_association)
end
end
def reindex_associations_scope(reindexable_association)
send(reindexable_association) # record_or_records
rescue NoMethodError
raise NoMethodError.new("foreign key '#{reindexable_association}' is not defined, verify has_reindexable_associations options")
end
def reindex_associations_process_records(records, reindexable_association)
unless self.class.reindexable_associations_skip
records.to_a.each do |record|
reindex_associations_process_record(record, reindexable_association)
end
end
end
def reindex_associations_process_record(record, reindexable_association)
reindex_association(record)
reindex_associations_process_old_record(reindexable_association)
end
def reindex_association(record)
record.reindex_async
end
def reindex_associations_find_old_polymorphic_association(reindexable_association)
key, type = reindex_associations_describe_polymorphic_association(reindexable_association)
old_klass = reindex_associations_describe_old_polymorphic_association(key, type)
return unless old_klass && previous_changes[key] && previous_changes[key].first
old_klass.where(id: previous_changes[key].first).first # old_record
end
def reindex_associations_process_old_record(reindexable_association)
old_record = reindex_associations_find_old_polymorphic_association(reindexable_association)
reindex_association(old_record) if old_record
end
def reindex_associations_describe_polymorphic_association(reindexable_association)
key = self.class.reflect_on_association(reindexable_association).try(:foreign_key)
type = self.class.reflect_on_association(reindexable_association).try(:foreign_type)
[key, type]
end
def reindex_associations_describe_old_polymorphic_association(key, type)
if (previous_changes[key] && previous_changes[key].first) || (previous_changes[type] && previous_changes[type].first)
old_klass = previous_changes[type].first rescue NameError
send(type).constantize if old_klass == NameError # old_klass
end
rescue NameError
end
end
end