Skip to content

Instantly share code, notes, and snippets.

@paulnsorensen
Last active September 5, 2019 19:05
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save paulnsorensen/4744475 to your computer and use it in GitHub Desktop.
Save paulnsorensen/4744475 to your computer and use it in GitHub Desktop.
UPDATE: I have way better code that accomplishes this now at [https://github.com/paulnsorensen/lifesaver] Module to ease associated models that are indexed by tire. Use this to trigger reindexing instead of having to touch all the files.
#
# Please go to https://github.com/paulnsorensen/lifesaver
#
class Foo < ActiveRecord::Base
has_many :bars
belongs_to :baz
include IndexingHandler
indexed_associations :bars, :baz
end
module IndexingHandler
extend ActiveSupport::Concern
included do
@indexing_suppressed = true if Rails.env == 'test'
after_save :enqueue_update_index, unless: :suppress_index?
after_destroy :enqueue_remove_index, unless: :suppress_index?
after_save :update_associated_indexes, unless: :suppress_index?
before_destroy :update_associated_indexes_for_destroy, unless: :suppress_index?
end
module ClassMethods
def indexed_associations(*args)
@association_names ||= []
if args.empty?
return @association_names
else
if args.last.is_a?(Hash)
@indexed_associations_options = args.last.clone
args.delete_at(args.size - 1)
end
@association_names << args.pop while !args.empty?
end
@association_names
end
def indexed_associations_options
@indexed_associations_options
end
end
def suppress_index?
@indexing_suppressed ? true : false
end
def suppress_index
@indexing_suppressed = true
end
def unsuppress_index
@indexing_suppressed = false
end
# if not using resque or any other asynchronous processing
# you could just use tire.update_index
def enqueue_update_index
if respond_to?(:tire)
Resque.enqueue(Indexer, self.class.name.underscore.to_sym, self.id, :update)
end
end
def enqueue_remove_index
if respond_to?(:tire)
Resque.enqueue(Indexer, self.class.name.underscore.to_sym, self.id, :remove)
end
end
def update_associated_indexes(for_destroy = false)
@triggered_save = true # this was the model that triggered the save
visited_models = {}
visited_models[self] = true
indexed_models = get_associated_indexed_models(visited_models, for_destroy)
indexed_models.each do |m|
unless m == self # we've already reindexed
m.enqueue_update_index
end
end
@triggered_save = false # reset method
true # so callbacks still run
end
def update_associated_indexes_for_destroy
update_associated_indexes(true)
end
def get_associated_indexed_models(visited_models, for_destroy = false)
indexed_models = Set.new
opts = self.class.indexed_associations_options
skip_associations = {}
if opts && opts[:only_on_save] && !@triggered_save
return indexed_models
end
if for_destroy
self.class.reflect_on_all_associations.each do |assoc|
if assoc.options[:dependent].present? # we will be touching it and triggering reindexing elsewhere
skip_associations[assoc.name.to_sym] = assoc
end
end
end
self.class.indexed_associations.each do |assoc|
next if skip_associations[assoc]
# TODO: find way to eliminate calling recursive relationships (if possible)
target = self.send(assoc)
unless target.nil?
if target.respond_to?(:each)
target.each do |t|
unless visited_models[t]
visited_models[t] = true
if t.respond_to?(:tire)
indexed_models << t
end
indexed_models |= t.get_associated_indexed_models(visited_models)
end
end
else # must be a single association then
unless visited_models[target]
visited_models[target] = true
if target.respond_to?(:tire)
indexed_models << target
end
indexed_models |= target.get_associated_indexed_models(visited_models)
end
end
end
end
indexed_models
end
end
# Resque job for indexing
class Indexer
@queue = :indexing_queue
def self.perform(class_name, id, action)
klass = class_name.to_s.classify.constantize
case action.to_sym
when :update
if klass.exists?(id)
klass.find(id).update_index
end
when :remove
klass.index.remove({document: klass.document_type, id: id})
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment