lib/meilisearch-rails.rb in meilisearch-rails-0.4.1 vs lib/meilisearch-rails.rb in meilisearch-rails-0.5.0

- old
+ new

@@ -1,14 +1,14 @@ require 'meilisearch' -require 'meilisearch/version' -require 'meilisearch/utilities' -require 'meilisearch/errors' +require 'meilisearch/rails/version' +require 'meilisearch/rails/utilities' +require 'meilisearch/rails/errors' if defined? Rails begin - require 'meilisearch/railtie' + require 'meilisearch/rails/railtie' rescue LoadError end end begin @@ -18,909 +18,906 @@ end require 'logger' module MeiliSearch - autoload :Configuration, 'meilisearch/configuration' - extend Configuration + module Rails + autoload :Configuration, 'meilisearch/rails/configuration' + extend Configuration - autoload :Pagination, 'meilisearch/pagination' + autoload :Pagination, 'meilisearch/rails/pagination' - class << self - attr_reader :included_in + class << self + attr_reader :included_in - def included(klass) - @included_in ||= [] - @included_in << klass - @included_in.uniq! + def included(klass) + @included_in ||= [] + @included_in << klass + @included_in.uniq! - klass.class_eval do - extend ClassMethods - include InstanceMethods + klass.class_eval do + extend ClassMethods + include InstanceMethods + end end end - end - class IndexSettings - DEFAULT_BATCH_SIZE = 1000 + class IndexSettings + DEFAULT_BATCH_SIZE = 1000 - DEFAULT_PRIMARY_KEY = 'id'.freeze + DEFAULT_PRIMARY_KEY = 'id'.freeze - # MeiliSearch settings - OPTIONS = %i[ - searchableAttributes - filterableAttributes - displayedAttributes - distinctAttribute - synonyms - stopWords - rankingRules - attributesToHighlight - attributesToCrop - cropLength - ].freeze + # Meilisearch settings + OPTIONS = %i[ + searchableAttributes + filterableAttributes + sortableAttributes + displayedAttributes + distinctAttribute + synonyms + stopWords + rankingRules + attributesToHighlight + attributesToCrop + cropLength + ].freeze - OPTIONS.each do |option| - define_method option do |value| - instance_variable_set("@#{option}", value) + OPTIONS.each do |option| + define_method option do |value| + instance_variable_set("@#{option}", value) + end + + underscored_name = option.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase + alias_method underscored_name, option if underscored_name != option end - underscored_name = option.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase - alias_method underscored_name, option if underscored_name != option - end + def initialize(options, &block) + @options = options + instance_exec(&block) if block_given? + end - def initialize(options, &block) - @options = options - instance_exec(&block) if block_given? - end + def use_serializer(serializer) + @serializer = serializer + # instance_variable_set("@serializer", serializer) + end - def use_serializer(serializer) - @serializer = serializer - # instance_variable_set("@serializer", serializer) - end + def attribute(*names, &block) + raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1) - def attribute(*names, &block) - raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1) - - @attributes ||= {} - names.flatten.each do |name| - @attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) } + @attributes ||= {} + names.flatten.each do |name| + @attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) } + end end - end - alias attributes attribute + alias attributes attribute - def add_attribute(*names, &block) - raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1) + def add_attribute(*names, &block) + raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1) - @additional_attributes ||= {} - names.each do |name| - @additional_attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) } + @additional_attributes ||= {} + names.each do |name| + @additional_attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) } + end end - end - alias add_attributes add_attribute + alias add_attributes add_attribute - def mongoid?(document) - defined?(::Mongoid::Document) && document.class.include?(::Mongoid::Document) - end + def mongoid?(document) + defined?(::Mongoid::Document) && document.class.include?(::Mongoid::Document) + end - def sequel?(document) - defined?(::Sequel) && document.class < ::Sequel::Model - end + def sequel?(document) + defined?(::Sequel) && document.class < ::Sequel::Model + end - def active_record?(document) - !mongoid?(document) && !sequel?(document) - end + def active_record?(document) + !mongoid?(document) && !sequel?(document) + end - def get_default_attributes(document) - if mongoid?(document) - # work-around mongoid 2.4's unscoped method, not accepting a block - document.attributes - elsif sequel?(document) - document.to_hash - else - document.class.unscoped do + def get_default_attributes(document) + if mongoid?(document) + # work-around mongoid 2.4's unscoped method, not accepting a block document.attributes + elsif sequel?(document) + document.to_hash + else + document.class.unscoped do + document.attributes + end end end - end - def get_attribute_names(document) - get_attributes(document).keys - end + def get_attribute_names(document) + get_attributes(document).keys + end - def attributes_to_hash(attributes, document) - if attributes - attributes.to_h { |name, value| [name.to_s, value.call(document)] } - else - {} + def attributes_to_hash(attributes, document) + if attributes + attributes.to_h { |name, value| [name.to_s, value.call(document)] } + else + {} + end end - end - def get_attributes(document) - # If a serializer is set, we ignore attributes - # everything should be done via the serializer - if !@serializer.nil? - attributes = @serializer.new(document).attributes - elsif @attributes.blank? - attributes = get_default_attributes(document) + def get_attributes(document) + # If a serializer is set, we ignore attributes + # everything should be done via the serializer + if !@serializer.nil? + attributes = @serializer.new(document).attributes + elsif @attributes.blank? + attributes = get_default_attributes(document) # no `attribute ...` have been configured, use the default attributes of the model - elsif active_record?(document) + elsif active_record?(document) # at least 1 `attribute ...` has been configured, therefore use ONLY the one configured - document.class.unscoped do + document.class.unscoped do attributes = attributes_to_hash(@attributes, document) end - else - attributes = attributes_to_hash(@attributes, document) - end + else + attributes = attributes_to_hash(@attributes, document) + end - attributes.merge!(attributes_to_hash(@additional_attributes, document)) if @additional_attributes + attributes.merge!(attributes_to_hash(@additional_attributes, document)) if @additional_attributes - if @options[:sanitize] - sanitizer = begin - ::HTML::FullSanitizer.new - rescue NameError - # from rails 4.2 - ::Rails::Html::FullSanitizer.new + if @options[:sanitize] + attributes = sanitize_attributes(attributes) end - attributes = sanitize_attributes(attributes, sanitizer) - end - attributes = encode_attributes(attributes) if @options[:force_utf8_encoding] + attributes = encode_attributes(attributes) if @options[:force_utf8_encoding] - attributes - end + attributes + end - def sanitize_attributes(value, sanitizer) - case value - when String - sanitizer.sanitize(value) - when Hash - value.each { |key, val| value[key] = sanitize_attributes(val, sanitizer) } - when Array - value.map { |item| sanitize_attributes(item, sanitizer) } - else - value + def sanitize_attributes(value) + case value + when String + ActionView::Base.full_sanitizer.sanitize(value) + when Hash + value.each { |key, val| value[key] = sanitize_attributes(val) } + when Array + value.map { |item| sanitize_attributes(item) } + else + value + end end - end - def encode_attributes(value) - case value - when String - value.force_encoding('utf-8') - when Hash - value.each { |key, val| value[key] = encode_attributes(val) } - when Array - value.map { |x| encode_attributes(x) } - else - value + def encode_attributes(value) + case value + when String + value.force_encoding('utf-8') + when Hash + value.each { |key, val| value[key] = encode_attributes(val) } + when Array + value.map { |x| encode_attributes(x) } + else + value + end end - end - def get_setting(name) - instance_variable_get("@#{name}") - end + def get_setting(name) + instance_variable_get("@#{name}") + end - def to_settings - settings = {} - OPTIONS.each do |k| - v = get_setting(k) - settings[k] = v unless v.nil? + def to_settings + settings = {} + OPTIONS.each do |k| + v = get_setting(k) + settings[k] = v unless v.nil? + end + settings end - settings - end - def add_index(index_uid, options = {}, &block) - raise ArgumentError, 'No block given' unless block_given? - if options[:auto_index] || options[:auto_remove] - raise ArgumentError, 'Options auto_index and auto_remove cannot be set on nested indexes' + def add_index(index_uid, options = {}, &block) + raise ArgumentError, 'No block given' unless block_given? + if options[:auto_index] || options[:auto_remove] + raise ArgumentError, 'Options auto_index and auto_remove cannot be set on nested indexes' + end + + @additional_indexes ||= {} + options[:index_uid] = index_uid + @additional_indexes[options] = IndexSettings.new(options, &block) end - @additional_indexes ||= {} - options[:index_uid] = index_uid - @additional_indexes[options] = IndexSettings.new(options, &block) + def additional_indexes + @additional_indexes || {} + end end - def additional_indexes - @additional_indexes || {} + # Default queueing system + if defined?(::ActiveJob::Base) + # lazy load the ActiveJob class to ensure the + # queue is initialized before using it + autoload :MSJob, 'meilisearch/ms_job' end - end - # Default queueing system - if defined?(::ActiveJob::Base) - # lazy load the ActiveJob class to ensure the - # queue is initialized before using it - autoload :MSJob, 'meilisearch/ms_job' - end + # this class wraps an MeiliSearch::Index document ensuring all raised exceptions + # are correctly logged or thrown depending on the `raise_on_failure` option + class SafeIndex + def initialize(index_uid, raise_on_failure, options) + client = MeiliSearch::Rails.client + primary_key = options[:primary_key] || MeiliSearch::Rails::IndexSettings::DEFAULT_PRIMARY_KEY + client.create_index(index_uid, { primaryKey: primary_key }) + @index = client.index(index_uid) + @raise_on_failure = raise_on_failure.nil? || raise_on_failure + end - # this class wraps an MeiliSearch::Index document ensuring all raised exceptions - # are correctly logged or thrown depending on the `raise_on_failure` option - class SafeIndex - def initialize(index_uid, raise_on_failure, options) - client = MeiliSearch.client - primary_key = options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY - client.create_index(index_uid, { primaryKey: primary_key }) - @index = client.index(index_uid) - @raise_on_failure = raise_on_failure.nil? || raise_on_failure - end + ::MeiliSearch::Index.instance_methods(false).each do |m| + define_method(m) do |*args, &block| + if m == :update_settings + args[0].delete(:attributesToHighlight) if args[0][:attributesToHighlight] + args[0].delete(:attributesToCrop) if args[0][:attributesToCrop] + args[0].delete(:cropLength) if args[0][:cropLength] + end + SafeIndex.log_or_throw(m, @raise_on_failure) do + @index.send(m, *args, &block) + end + end + end - ::MeiliSearch::Index.instance_methods(false).each do |m| - define_method(m) do |*args, &block| - if m == :update_settings - args[0].delete(:attributesToHighlight) if args[0][:attributesToHighlight] - args[0].delete(:attributesToCrop) if args[0][:attributesToCrop] - args[0].delete(:cropLength) if args[0][:cropLength] + # special handling of wait_for_task to handle null task_id + def wait_for_task(task_uid) + return if task_uid.nil? && !@raise_on_failure # ok + + SafeIndex.log_or_throw(:wait_for_task, @raise_on_failure) do + @index.wait_for_task(task_uid) end - SafeIndex.log_or_throw(m, @raise_on_failure) do - @index.send(m, *args, &block) - end end - end - # special handling of wait_for_task to handle null task_id - def wait_for_task(task_uid) - return if task_uid.nil? && !@raise_on_failure # ok + # special handling of settings to avoid raising errors on 404 + def settings(*args) + SafeIndex.log_or_throw(:settings, @raise_on_failure) do + @index.settings(*args) + rescue ::MeiliSearch::ApiError => e + return {} if e.code == 404 # not fatal - SafeIndex.log_or_throw(:wait_for_task, @raise_on_failure) do - @index.wait_for_task(task_uid) + raise e + end end - end - # special handling of settings to avoid raising errors on 404 - def settings(*args) - SafeIndex.log_or_throw(:settings, @raise_on_failure) do - @index.settings(*args) + def self.log_or_throw(method, raise_on_failure, &block) + yield rescue ::MeiliSearch::ApiError => e - return {} if e.code == 404 # not fatal + raise e if raise_on_failure - raise e + # log the error + (::Rails.logger || Logger.new($stdout)).error("[meilisearch-rails] #{e.message}") + # return something + case method.to_s + when 'search' + # some attributes are required + { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetsDistribution' => {}, 'error' => e } + else + # empty answer + { 'error' => e } + end end end - def self.log_or_throw(method, raise_on_failure, &block) - yield - rescue ::MeiliSearch::ApiError => e - raise e if raise_on_failure + # these are the class methods added when MeiliSearch is included + module ClassMethods + def self.extended(base) + class << base + alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index + alias_method :reindex!, :ms_reindex! unless method_defined? :reindex! + alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents + alias_method :index!, :ms_index! unless method_defined? :index! + alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index! + alias_method :clear_index!, :ms_clear_index! unless method_defined? :clear_index! + alias_method :search, :ms_search unless method_defined? :search + alias_method :raw_search, :ms_raw_search unless method_defined? :raw_search + alias_method :index, :ms_index unless method_defined? :index + alias_method :index_uid, :ms_index_uid unless method_defined? :index_uid + alias_method :must_reindex?, :ms_must_reindex? unless method_defined? :must_reindex? + end - # log the error - (Rails.logger || Logger.new($stdout)).error("[meilisearch-rails] #{e.message}") - # return something - case method.to_s - when 'search' - # some attributes are required - { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetsDistribution' => {}, 'error' => e } - else - # empty answer - { 'error' => e } + base.cattr_accessor :meilisearch_options, :meilisearch_settings end - end - end - # these are the class methods added when MeiliSearch is included - module ClassMethods - def self.extended(base) - class << base - alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index - alias_method :reindex!, :ms_reindex! unless method_defined? :reindex! - alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents - alias_method :index!, :ms_index! unless method_defined? :index! - alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index! - alias_method :clear_index!, :ms_clear_index! unless method_defined? :clear_index! - alias_method :search, :ms_search unless method_defined? :search - alias_method :raw_search, :ms_raw_search unless method_defined? :raw_search - alias_method :index, :ms_index unless method_defined? :index - alias_method :index_uid, :ms_index_uid unless method_defined? :index_uid - alias_method :must_reindex?, :ms_must_reindex? unless method_defined? :must_reindex? - end + def meilisearch(options = {}, &block) + self.meilisearch_settings = IndexSettings.new(options, &block) + self.meilisearch_options = { + type: model_name.to_s.constantize, + per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1 + }.merge(options) - base.cattr_accessor :meilisearch_options, :meilisearch_settings - end + attr_accessor :formatted - def meilisearch(options = {}, &block) - self.meilisearch_settings = IndexSettings.new(options, &block) - self.meilisearch_options = { - type: model_name.to_s.constantize, - per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1 - }.merge(options) - - attr_accessor :formatted - - if options[:synchronous] == true - if defined?(::Sequel) && self < Sequel::Model - class_eval do - copy_after_validation = instance_method(:after_validation) - define_method(:after_validation) do |*args| - super(*args) - copy_after_validation.bind(self).call - ms_mark_synchronous + if options[:synchronous] == true + if defined?(::Sequel) && self < Sequel::Model + class_eval do + copy_after_validation = instance_method(:after_validation) + define_method(:after_validation) do |*args| + super(*args) + copy_after_validation.bind(self).call + ms_mark_synchronous + end end + elsif respond_to?(:after_validation) + after_validation :ms_mark_synchronous end - elsif respond_to?(:after_validation) - after_validation :ms_mark_synchronous end - end - if options[:enqueue] - raise ArgumentError, 'Cannot use a enqueue if the `synchronous` option if set' if options[:synchronous] + if options[:enqueue] + raise ArgumentError, 'Cannot use a enqueue if the `synchronous` option if set' if options[:synchronous] - proc = if options[:enqueue] == true - proc do |record, remove| - MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!') + proc = if options[:enqueue] == true + proc do |record, remove| + MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!') + end + elsif options[:enqueue].respond_to?(:call) + options[:enqueue] + elsif options[:enqueue].is_a?(Symbol) + proc { |record, remove| send(options[:enqueue], record, remove) } + else + raise ArgumentError, "Invalid `enqueue` option: #{options[:enqueue]}" end - elsif options[:enqueue].respond_to?(:call) - options[:enqueue] - elsif options[:enqueue].is_a?(Symbol) - proc { |record, remove| send(options[:enqueue], record, remove) } - else - raise ArgumentError, "Invalid `enqueue` option: #{options[:enqueue]}" - end - meilisearch_options[:enqueue] = proc do |record, remove| - proc.call(record, remove) unless ms_without_auto_index_scope + meilisearch_options[:enqueue] = proc do |record, remove| + proc.call(record, remove) unless ms_without_auto_index_scope + end end - end - unless options[:auto_index] == false - if defined?(::Sequel) && self < Sequel::Model - class_eval do - copy_after_validation = instance_method(:after_validation) - copy_before_save = instance_method(:before_save) + unless options[:auto_index] == false + if defined?(::Sequel) && self < Sequel::Model + class_eval do + copy_after_validation = instance_method(:after_validation) + copy_before_save = instance_method(:before_save) - define_method(:after_validation) do |*args| - super(*args) - copy_after_validation.bind(self).call - ms_mark_must_reindex - end - - define_method(:before_save) do |*args| - copy_before_save.bind(self).call - ms_mark_for_auto_indexing - super(*args) - end - - sequel_version = Gem::Version.new(Sequel.version) - if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0') - copy_after_commit = instance_method(:after_commit) - define_method(:after_commit) do |*args| + define_method(:after_validation) do |*args| super(*args) - copy_after_commit.bind(self).call - ms_perform_index_tasks + copy_after_validation.bind(self).call + ms_mark_must_reindex end - else - copy_after_save = instance_method(:after_save) - define_method(:after_save) do |*args| + + define_method(:before_save) do |*args| + copy_before_save.bind(self).call + ms_mark_for_auto_indexing super(*args) - copy_after_save.bind(self).call - db.after_commit do + end + + sequel_version = Gem::Version.new(Sequel.version) + if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0') + copy_after_commit = instance_method(:after_commit) + define_method(:after_commit) do |*args| + super(*args) + copy_after_commit.bind(self).call ms_perform_index_tasks end + else + copy_after_save = instance_method(:after_save) + define_method(:after_save) do |*args| + super(*args) + copy_after_save.bind(self).call + db.after_commit do + ms_perform_index_tasks + end + end end end + else + after_validation :ms_mark_must_reindex if respond_to?(:after_validation) + before_save :ms_mark_for_auto_indexing if respond_to?(:before_save) + if respond_to?(:after_commit) + after_commit :ms_perform_index_tasks + elsif respond_to?(:after_save) + after_save :ms_perform_index_tasks + end end - else - after_validation :ms_mark_must_reindex if respond_to?(:after_validation) - before_save :ms_mark_for_auto_indexing if respond_to?(:before_save) - if respond_to?(:after_commit) - after_commit :ms_perform_index_tasks - elsif respond_to?(:after_save) - after_save :ms_perform_index_tasks - end end - end - unless options[:auto_remove] == false - if defined?(::Sequel) && self < Sequel::Model - class_eval do - copy_after_destroy = instance_method(:after_destroy) + unless options[:auto_remove] == false + if defined?(::Sequel) && self < Sequel::Model + class_eval do + copy_after_destroy = instance_method(:after_destroy) - define_method(:after_destroy) do |*args| - copy_after_destroy.bind(self).call - ms_enqueue_remove_from_index!(ms_synchronous?) - super(*args) + define_method(:after_destroy) do |*args| + copy_after_destroy.bind(self).call + ms_enqueue_remove_from_index!(ms_synchronous?) + super(*args) + end end + elsif respond_to?(:after_destroy) + after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) } end - elsif respond_to?(:after_destroy) - after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) } end end - end - def ms_without_auto_index(&block) - self.ms_without_auto_index_scope = true - begin - yield - ensure - self.ms_without_auto_index_scope = false + def ms_without_auto_index(&block) + self.ms_without_auto_index_scope = true + begin + yield + ensure + self.ms_without_auto_index_scope = false + end end - end - def ms_without_auto_index_scope=(value) - Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value - end + def ms_without_auto_index_scope=(value) + Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value + end - def ms_without_auto_index_scope - Thread.current["ms_without_auto_index_scope_for_#{model_name}"] - end + def ms_without_auto_index_scope + Thread.current["ms_without_auto_index_scope_for_#{model_name}"] + end - def ms_reindex!(batch_size = MeiliSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false) - return if ms_without_auto_index_scope + def ms_reindex!(batch_size = MeiliSearch::Rails::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false) + return if ms_without_auto_index_scope - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) - index = ms_ensure_init(options, settings) - last_task = nil + index = ms_ensure_init(options, settings) + last_task = nil - ms_find_in_batches(batch_size) do |group| - if ms_conditional_index?(options) - # delete non-indexable documents - ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) } - index.delete_documents(ids.select(&:present?)) - # select only indexable documents - group = group.select { |d| ms_indexable?(d, options) } + ms_find_in_batches(batch_size) do |group| + if ms_conditional_index?(options) + # delete non-indexable documents + ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) } + index.delete_documents(ids.select(&:present?)) + # select only indexable documents + group = group.select { |d| ms_indexable?(d, options) } + end + documents = group.map do |d| + attributes = settings.get_attributes(d) + attributes = attributes.to_hash unless attributes.instance_of?(Hash) + attributes.merge ms_pk(options) => ms_primary_key_of(d, options) + end + last_task = index.add_documents(documents) end - documents = group.map do |d| - attributes = settings.get_attributes(d) - attributes = attributes.to_hash unless attributes.instance_of?(Hash) - attributes.merge ms_pk(options) => ms_primary_key_of(d, options) - end - last_task = index.add_documents(documents) + index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous]) end - index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous]) + nil end - nil - end - def ms_set_settings(synchronous = false) - ms_configurations.each do |options, settings| - if options[:primary_settings] && options[:inherit] - primary = options[:primary_settings].to_settings - final_settings = primary.merge(settings.to_settings) - else - final_settings = settings.to_settings - end + def ms_set_settings(synchronous = false) + ms_configurations.each do |options, settings| + if options[:primary_settings] && options[:inherit] + primary = options[:primary_settings].to_settings + final_settings = primary.merge(settings.to_settings) + else + final_settings = settings.to_settings + end - index = SafeIndex.new(ms_index_uid(options), true, options) - task = index.update_settings(final_settings) - index.wait_for_task(task['uid']) if synchronous + index = SafeIndex.new(ms_index_uid(options), true, options) + task = index.update_settings(final_settings) + index.wait_for_task(task['uid']) if synchronous + end end - end - def ms_index_documents(documents, synchronous = false) - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) + def ms_index_documents(documents, synchronous = false) + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) - index = ms_ensure_init(options, settings) - task = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) }) - index.wait_for_task(task['uid']) if synchronous || options[:synchronous] + index = ms_ensure_init(options, settings) + task = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) }) + index.wait_for_task(task['uid']) if synchronous || options[:synchronous] + end end - end - def ms_index!(document, synchronous = false) - return if ms_without_auto_index_scope + def ms_index!(document, synchronous = false) + return if ms_without_auto_index_scope - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) - primary_key = ms_primary_key_of(document, options) - index = ms_ensure_init(options, settings) - if ms_indexable?(document, options) - raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank? + primary_key = ms_primary_key_of(document, options) + index = ms_ensure_init(options, settings) + if ms_indexable?(document, options) + raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank? - doc = settings.get_attributes(document) - doc = doc.merge ms_pk(options) => primary_key + doc = settings.get_attributes(document) + doc = doc.merge ms_pk(options) => primary_key - if synchronous || options[:synchronous] - index.add_documents!(doc) - else - index.add_documents(doc) + if synchronous || options[:synchronous] + index.add_documents!(doc) + else + index.add_documents(doc) + end + elsif ms_conditional_index?(options) && primary_key.present? + # remove non-indexable documents + if synchronous || options[:synchronous] + index.delete_document!(primary_key) + else + index.delete_document(primary_key) + end end - elsif ms_conditional_index?(options) && primary_key.present? - # remove non-indexable documents + end + nil + end + + def ms_remove_from_index!(document, synchronous = false) + return if ms_without_auto_index_scope + + primary_key = ms_primary_key_of(document) + raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank? + + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) + + index = ms_ensure_init(options, settings) if synchronous || options[:synchronous] index.delete_document!(primary_key) else index.delete_document(primary_key) end end + nil end - nil - end - def ms_remove_from_index!(document, synchronous = false) - return if ms_without_auto_index_scope + def ms_clear_index!(synchronous = false) + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) - primary_key = ms_primary_key_of(document) - raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank? - - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) - - index = ms_ensure_init(options, settings) - if synchronous || options[:synchronous] - index.delete_document!(primary_key) - else - index.delete_document(primary_key) + index = ms_ensure_init(options, settings) + synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents + @ms_indexes[settings] = nil end + nil end - nil - end - def ms_clear_index!(synchronous = false) - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) + def ms_raw_search(q, params = {}) + index_uid = params.delete(:index) || params.delete('index') - index = ms_ensure_init(options, settings) - synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents - @ms_indexes[settings] = nil - end - nil - end + unless meilisearch_settings.get_setting(:attributesToHighlight).nil? + params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight) + end - def ms_raw_search(q, params = {}) - index_uid = params.delete(:index) || params.delete('index') + unless meilisearch_settings.get_setting(:attributesToCrop).nil? + params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop) - unless meilisearch_settings.get_setting(:attributesToHighlight).nil? - params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight) + unless meilisearch_settings.get_setting(:cropLength).nil? + params[:cropLength] = meilisearch_settings.get_setting(:cropLength) + end + end + + index = ms_index(index_uid) + index.search(q, params.to_h { |k, v| [k, v] }) end - unless meilisearch_settings.get_setting(:attributesToCrop).nil? - params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop) + module AdditionalMethods + def self.extended(base) + class << base + alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer + alias_method :facets_distribution, :ms_facets_distribution unless method_defined? :facets_distribution + end + end - unless meilisearch_settings.get_setting(:cropLength).nil? - params[:cropLength] = meilisearch_settings.get_setting(:cropLength) + def ms_raw_answer + @ms_json end - end - index = ms_index(index_uid) - index.search(q, params.to_h { |k, v| [k, v] }) - end - - module AdditionalMethods - def self.extended(base) - class << base - alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer - alias_method :facets_distribution, :ms_facets_distribution unless method_defined? :facets_distribution + def ms_facets_distribution + @ms_json['facetsDistribution'] end - end - def ms_raw_answer - @ms_json - end + private - def ms_facets_distribution - @ms_json['facetsDistribution'] + def ms_init_raw_answer(json) + @ms_json = json + end end - private + def ms_search(query, params = {}) + if MeiliSearch::Rails.configuration[:pagination_backend] - def ms_init_raw_answer(json) - @ms_json = json - end - end + page = params[:page].nil? ? params[:page] : params[:page].to_i + hits_per_page = params[:hitsPerPage].nil? ? params[:hitsPerPage] : params[:hitsPerPage].to_i - def ms_search(query, params = {}) - if MeiliSearch.configuration[:pagination_backend] + params.delete(:page) + params.delete(:hitsPerPage) + params[:limit] = 200 + end - page = params[:page].nil? ? params[:page] : params[:page].to_i - hits_per_page = params[:hitsPerPage].nil? ? params[:hitsPerPage] : params[:hitsPerPage].to_i + # Returns raw json hits as follows: + # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1, + # "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"} + json = ms_raw_search(query, params) - params.delete(:page) - params.delete(:hitsPerPage) - params[:limit] = 200 - end + # Returns the ids of the hits: 13 + hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] } - # Returns raw json hits as follows: - # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1, - # "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"} - json = ms_raw_search(query, params) + # condition_key gets the primary key of the document; looks for "id" on the options + condition_key = if defined?(::Mongoid::Document) && include?(::Mongoid::Document) + ms_primary_key_method.in + else + ms_primary_key_method + end - # Returns the ids of the hits: 13 - hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] } + # meilisearch_options[:type] refers to the Model name (e.g. Product) + # results_by_id creates a hash with the primaryKey of the document (id) as the key and doc itself as the value + # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil, + # description: "Puts even more features at your fingertips", release_date: nil>} + results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit| + ms_primary_key_of(hit) + end - # condition_key gets the primary key of the document; looks for "id" on the options - condition_key = if defined?(::Mongoid::Document) && include?(::Mongoid::Document) - ms_primary_key_method.in - else - ms_primary_key_method - end + results = json['hits'].map do |hit| + o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s] + if o + o.formatted = hit['_formatted'] + o + end + end.compact - # meilisearch_options[:type] refers to the Model name (e.g. Product) - # results_by_id creates a hash with the primaryKey of the document (id) as the key and doc itself as the value - # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil, - # description: "Puts even more features at your fingertips", release_date: nil>} - results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit| - ms_primary_key_of(hit) + total_hits = json['hits'].length + hits_per_page ||= 20 + page ||= 1 + + res = MeiliSearch::Rails::Pagination.create(results, total_hits, meilisearch_options.merge(page: page, per_page: hits_per_page)) + res.extend(AdditionalMethods) + res.send(:ms_init_raw_answer, json) + res end - results = json['hits'].map do |hit| - o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s] - if o - o.formatted = hit['_formatted'] - o + def ms_index(name = nil) + if name + ms_configurations.each do |o, s| + return ms_ensure_init(o, s) if o[:index_uid].to_s == name.to_s + end + raise ArgumentError, "Invalid index name: #{name}" end - end.compact + ms_ensure_init + end - total_hits = json['hits'].length - hits_per_page ||= 20 - page ||= 1 - - res = MeiliSearch::Pagination.create(results, total_hits, meilisearch_options.merge(page: page, per_page: hits_per_page)) - res.extend(AdditionalMethods) - res.send(:ms_init_raw_answer, json) - res - end - - def ms_index(name = nil) - if name - ms_configurations.each do |o, s| - return ms_ensure_init(o, s) if o[:index_uid].to_s == name.to_s - end - raise ArgumentError, "Invalid index name: #{name}" + def ms_index_uid(options = nil) + options ||= meilisearch_options + name = options[:index_uid] || model_name.to_s.gsub('::', '_') + name = "#{name}_#{::Rails.env}" if options[:per_environment] + name end - ms_ensure_init - end - def ms_index_uid(options = nil) - options ||= meilisearch_options - name = options[:index_uid] || model_name.to_s.gsub('::', '_') - name = "#{name}_#{Rails.env}" if options[:per_environment] - name - end + def ms_must_reindex?(document) + # use +ms_dirty?+ method if implemented + return document.send(:ms_dirty?) if document.respond_to?(:ms_dirty?) - def ms_must_reindex?(document) - # use +ms_dirty?+ method if implemented - return document.send(:ms_dirty?) if document.respond_to?(:ms_dirty?) + # Loop over each index to see if a attribute used in records has changed + ms_configurations.each do |options, settings| + next if ms_indexing_disabled?(options) + return true if ms_primary_key_changed?(document, options) - # Loop over each index to see if a attribute used in records has changed - ms_configurations.each do |options, settings| - next if ms_indexing_disabled?(options) - return true if ms_primary_key_changed?(document, options) - - settings.get_attribute_names(document).each do |k| - return true if ms_attribute_changed?(document, k) - # return true if !document.respond_to?(changed_method) || document.send(changed_method) - end - [options[:if], options[:unless]].each do |condition| - case condition - when nil - when String, Symbol - return true if ms_attribute_changed?(document, condition) - else - # if the :if, :unless condition is a anything else, - # we have no idea whether we should reindex or not - # let's always reindex then - return true + settings.get_attribute_names(document).each do |k| + return true if ms_attribute_changed?(document, k) + # return true if !document.respond_to?(changed_method) || document.send(changed_method) end + [options[:if], options[:unless]].each do |condition| + case condition + when nil + when String, Symbol + return true if ms_attribute_changed?(document, condition) + else + # if the :if, :unless condition is a anything else, + # we have no idea whether we should reindex or not + # let's always reindex then + return true + end + end end + + # By default, we don't reindex + false end - # By default, we don't reindex - false - end + protected - protected + def ms_ensure_init(options = nil, settings = nil, index_settings = nil) + raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil? - def ms_ensure_init(options = nil, settings = nil, index_settings = nil) - raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil? + @ms_indexes ||= {} - @ms_indexes ||= {} + options ||= meilisearch_options + settings ||= meilisearch_settings - options ||= meilisearch_options - settings ||= meilisearch_settings + return @ms_indexes[settings] if @ms_indexes[settings] - return @ms_indexes[settings] if @ms_indexes[settings] + @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options) - @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options) + current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist - current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist + index_settings ||= settings.to_settings + index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit] - index_settings ||= settings.to_settings - index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit] + options[:check_settings] = true if options[:check_settings].nil? - options[:check_settings] = true if options[:check_settings].nil? + if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings) + @ms_indexes[settings].update_settings(index_settings) + end - if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings) - @ms_indexes[settings].update_settings(index_settings) + @ms_indexes[settings] end - @ms_indexes[settings] - end + private - private + def ms_configurations + raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil? - def ms_configurations - raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil? + if @configurations.nil? + @configurations = {} + @configurations[meilisearch_options] = meilisearch_settings + meilisearch_settings.additional_indexes.each do |k, v| + @configurations[k] = v - if @configurations.nil? - @configurations = {} - @configurations[meilisearch_options] = meilisearch_settings - meilisearch_settings.additional_indexes.each do |k, v| - @configurations[k] = v + next unless v.additional_indexes.any? - next unless v.additional_indexes.any? - - v.additional_indexes.each do |options, index| - @configurations[options] = index + v.additional_indexes.each do |options, index| + @configurations[options] = index + end end end + @configurations end - @configurations - end - def ms_primary_key_method(options = nil) - options ||= meilisearch_options - options[:primary_key] || options[:id] || :id - end + def ms_primary_key_method(options = nil) + options ||= meilisearch_options + options[:primary_key] || options[:id] || :id + end - def ms_primary_key_of(doc, options = nil) - doc.send(ms_primary_key_method(options)).to_s - end + def ms_primary_key_of(doc, options = nil) + doc.send(ms_primary_key_method(options)).to_s + end - def ms_primary_key_changed?(doc, options = nil) - changed = ms_attribute_changed?(doc, ms_primary_key_method(options)) - changed.nil? ? false : changed - end + def ms_primary_key_changed?(doc, options = nil) + changed = ms_attribute_changed?(doc, ms_primary_key_method(options)) + changed.nil? ? false : changed + end - def ms_pk(options = nil) - options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY - end + def ms_pk(options = nil) + options[:primary_key] || MeiliSearch::Rails::IndexSettings::DEFAULT_PRIMARY_KEY + end - def meilisearch_settings_changed?(prev, current) - return true if prev.nil? + def meilisearch_settings_changed?(prev, current) + return true if prev.nil? - current.each do |k, v| - prev_v = prev[k.to_s] - if v.is_a?(Array) && prev_v.is_a?(Array) - # compare array of strings, avoiding symbols VS strings comparison - return true if v.map(&:to_s) != prev_v.map(&:to_s) - elsif prev_v != v - return true + current.each do |k, v| + prev_v = prev[k.to_s] + if v.is_a?(Array) && prev_v.is_a?(Array) + # compare array of strings, avoiding symbols VS strings comparison + return true if v.map(&:to_s) != prev_v.map(&:to_s) + elsif prev_v != v + return true + end end + false end - false - end - def ms_conditional_index?(options = nil) - options ||= meilisearch_options - options[:if].present? || options[:unless].present? - end + def ms_conditional_index?(options = nil) + options ||= meilisearch_options + options[:if].present? || options[:unless].present? + end - def ms_indexable?(document, options = nil) - options ||= meilisearch_options - if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if]) - unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless]) - if_passes && unless_passes - end + def ms_indexable?(document, options = nil) + options ||= meilisearch_options + if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if]) + unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless]) + if_passes && unless_passes + end - def ms_constraint_passes?(document, constraint) - case constraint - when Symbol - document.send(constraint) - when String - document.send(constraint.to_sym) - when Enumerable - # All constraints must pass - constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) } - else - unless constraint.respond_to?(:call) - raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})" - end + def ms_constraint_passes?(document, constraint) + case constraint + when Symbol + document.send(constraint) + when String + document.send(constraint.to_sym) + when Enumerable + # All constraints must pass + constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) } + else + unless constraint.respond_to?(:call) + raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})" + end - constraint.call(document) + constraint.call(document) + end end - end - def ms_indexing_disabled?(options = nil) - options ||= meilisearch_options - constraint = options[:disable_indexing] || options['disable_indexing'] - case constraint - when nil - return false - when true, false - return constraint - when String, Symbol - return send(constraint) - else - return constraint.call if constraint.respond_to?(:call) # Proc + def ms_indexing_disabled?(options = nil) + options ||= meilisearch_options + constraint = options[:disable_indexing] || options['disable_indexing'] + case constraint + when nil + return false + when true, false + return constraint + when String, Symbol + return send(constraint) + else + return constraint.call if constraint.respond_to?(:call) # Proc + end + raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})" end - raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})" - end - def ms_find_in_batches(batch_size, &block) - if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches) - find_in_batches(batch_size: batch_size, &block) - elsif defined?(::Sequel) && self < Sequel::Model - dataset.extension(:pagination).each_page(batch_size, &block) - else - # don't worry, mongoid has its own underlying cursor/streaming mechanism - items = [] - all.each do |item| - items << item - if (items.length % batch_size).zero? - yield items - items = [] + def ms_find_in_batches(batch_size, &block) + if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches) + find_in_batches(batch_size: batch_size, &block) + elsif defined?(::Sequel) && self < Sequel::Model + dataset.extension(:pagination).each_page(batch_size, &block) + else + # don't worry, mongoid has its own underlying cursor/streaming mechanism + items = [] + all.each do |item| + items << item + if (items.length % batch_size).zero? + yield items + items = [] + end end + yield items unless items.empty? end - yield items unless items.empty? end - end - def ms_attribute_changed?(document, attr_name) - if document.respond_to?("will_save_change_to_#{attr_name}?") - return document.send("will_save_change_to_#{attr_name}?") - end + def ms_attribute_changed?(document, attr_name) + if document.respond_to?("will_save_change_to_#{attr_name}?") + return document.send("will_save_change_to_#{attr_name}?") + end - # We don't know if the attribute has changed, so conservatively assume it has - true + # We don't know if the attribute has changed, so conservatively assume it has + true + end end - end - # these are the instance methods included - module InstanceMethods + # these are the instance methods included + module InstanceMethods - def self.included(base) - base.instance_eval do - alias_method :index!, :ms_index! unless method_defined? :index! - alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index! + def self.included(base) + base.instance_eval do + alias_method :index!, :ms_index! unless method_defined? :index! + alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index! + end end - end - def ms_index!(synchronous = false) - self.class.ms_index!(self, synchronous || ms_synchronous?) - end + def ms_index!(synchronous = false) + self.class.ms_index!(self, synchronous || ms_synchronous?) + end - def ms_remove_from_index!(synchronous = false) - self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?) - end + def ms_remove_from_index!(synchronous = false) + self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?) + end - def ms_enqueue_remove_from_index!(synchronous) - if meilisearch_options[:enqueue] - unless self.class.send(:ms_indexing_disabled?, meilisearch_options) - meilisearch_options[:enqueue].call(self, true) + def ms_enqueue_remove_from_index!(synchronous) + if meilisearch_options[:enqueue] + unless self.class.send(:ms_indexing_disabled?, meilisearch_options) + meilisearch_options[:enqueue].call(self, true) + end + else + ms_remove_from_index!(synchronous || ms_synchronous?) end - else - ms_remove_from_index!(synchronous || ms_synchronous?) end - end - def ms_enqueue_index!(synchronous) - if meilisearch_options[:enqueue] - unless self.class.send(:ms_indexing_disabled?, meilisearch_options) - meilisearch_options[:enqueue].call(self, false) + def ms_enqueue_index!(synchronous) + if meilisearch_options[:enqueue] + unless self.class.send(:ms_indexing_disabled?, meilisearch_options) + meilisearch_options[:enqueue].call(self, false) + end + else + ms_index!(synchronous) end - else - ms_index!(synchronous) end - end - def ms_synchronous? - @ms_synchronous - end + def ms_synchronous? + @ms_synchronous + end - private + private - def ms_mark_synchronous - @ms_synchronous = true - end + def ms_mark_synchronous + @ms_synchronous = true + end - def ms_mark_for_auto_indexing - @ms_auto_indexing = true - end + def ms_mark_for_auto_indexing + @ms_auto_indexing = true + end - def ms_mark_must_reindex - # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in - # a transaction, keep flag set until it is explicitly unset - @ms_must_reindex ||= - if defined?(::Sequel) && is_a?(Sequel::Model) - new? || self.class.ms_must_reindex?(self) - else - new_record? || self.class.ms_must_reindex?(self) - end - true - end + def ms_mark_must_reindex + # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in + # a transaction, keep flag set until it is explicitly unset + @ms_must_reindex ||= + if defined?(::Sequel) && is_a?(Sequel::Model) + new? || self.class.ms_must_reindex?(self) + else + new_record? || self.class.ms_must_reindex?(self) + end + true + end - def ms_perform_index_tasks - return if !@ms_auto_indexing || @ms_must_reindex == false + def ms_perform_index_tasks + return if !@ms_auto_indexing || @ms_must_reindex == false - ms_enqueue_index!(ms_synchronous?) - remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing) - remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous) - remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex) + ms_enqueue_index!(ms_synchronous?) + remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing) + remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous) + remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex) + end end end end