diff --git a/guides/solr_indexing.md b/guides/solr_indexing.md index 70d77a58..2d16a4b9 100644 --- a/guides/solr_indexing.md +++ b/guides/solr_indexing.md @@ -251,7 +251,7 @@ Alternately or additionally, we could use some of the concurrency built into tra ## Use with non Kithe::Model ActiveRecord classes? -While Kithe::Indexable was developed for use with `Kithe::Model` (your collections, works, and assets), it's implementation should be independent of it. (Although we aren't currently testing that). +While Kithe::Indexable was developed for use with `Kithe::Model` (your collections, works, and assets), it's implementation is independent of it, should work mixed into any ActiveRecord model, and is currently tested to do so. Just add `include Kithe::Indexable` to any ActiveRecord::Base class, and you should get all the indexing functionality documented above for any arbitrary ActiveRecord model. diff --git a/spec/indexing/indexable_spec.rb b/spec/indexing/indexable_spec.rb index f9e0a0c4..1c4e2286 100644 --- a/spec/indexing/indexable_spec.rb +++ b/spec/indexing/indexable_spec.rb @@ -13,227 +13,233 @@ Kithe.indexable_settings.solr_url = @original_solr_url end - temporary_class("TestWork") do - Class.new(Kithe::Work) do - self.kithe_indexable_mapper = Kithe::Indexer.new - self.kithe_indexable_auto_callbacks = false - end - end - - describe "update_index without auto-indexing" do - describe "with something that should be in index" do - it "sends solr update" do - stub_request(:post, @solr_update_url) + shared_examples "Kithe::Indexable" do + describe "update_index without auto-indexing" do + describe "with something that should be in index" do + it "sends solr update" do + stub_request(:post, @solr_update_url) - work = TestWork.create!(title: "test") - work.update_index + work = TestModelClass.create!(title: "test") + work.update_index - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| - JSON.parse(req.body) == [{"id" => [work.id],"model_name_ssi" => ["TestWork"]}] - } + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| + JSON.parse(req.body) == [{"id" => [work.id],"model_name_ssi" => [TestModelClass.name]}] + } + end end - end - describe "with deleted thing" do - it "sends delete to Solr" do - work = TestWork.create!(title: "test") + describe "with deleted thing" do + it "sends delete to Solr" do + work = TestModelClass.create!(title: "test") - stub_request(:post, @solr_update_url) + stub_request(:post, @solr_update_url) - work.destroy! - work.update_index + work.destroy! + work.update_index - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| - JSON.parse(req.body) == { "delete" => work.id } - } + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| + JSON.parse(req.body) == { "delete" => work.id } + } + end end - end - describe "with explicit mapper" do - let(:custom_mapper) { Kithe::Indexer.new } - let(:work) { TestWork.create!(title: "test") } + describe "with explicit mapper" do + let(:custom_mapper) { Kithe::Indexer.new } + let(:work) { TestModelClass.create!(title: "test") } - it "uses" do - stub_request(:post, @solr_update_url) + it "uses" do + stub_request(:post, @solr_update_url) - expect(custom_mapper).to receive(:process_with).with([work]) - expect(work.kithe_indexable_mapper).not_to receive(:process_with) + expect(custom_mapper).to receive(:process_with).with([work]) + expect(work.kithe_indexable_mapper).not_to receive(:process_with) - work.update_index(mapper: custom_mapper) + work.update_index(mapper: custom_mapper) + end end - end - describe "with explicit writer" do - let(:custom_writer) { double(:custom_writer) } - let(:work) { TestWork.create!(title: "test") } + describe "with explicit writer" do + let(:custom_writer) { double(:custom_writer) } + let(:work) { TestModelClass.create!(title: "test") } - it "uses" do - expect(custom_writer).to receive(:put) - work.update_index(writer: custom_writer) + it "uses" do + expect(custom_writer).to receive(:put) + work.update_index(writer: custom_writer) + end end - end - end + end - describe "auto-indexing" do - temporary_class("TestWork") do - Class.new(Kithe::Work) do - self.kithe_indexable_mapper = Kithe::Indexer.new + describe "auto-indexing" do + temporary_class("TestModelClass") do + Class.new(Kithe::Work) do + self.kithe_indexable_mapper = Kithe::Indexer.new + end end - end - it "adds and deletes automatically" do - stub_request(:post, @solr_update_url) - work = TestWork.create!(title: "test") - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| - JSON.parse(req.body) == [{"id" => [work.id],"model_name_ssi" => ["TestWork"]}] - } - - work.destroy! - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| - JSON.parse(req.body) == { "delete" => work.id } - } - end + it "adds and deletes automatically" do + stub_request(:post, @solr_update_url) + work = TestModelClass.create!(title: "test") + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| + JSON.parse(req.body) == [{"id" => [work.id],"model_name_ssi" => [TestModelClass.name]}] + } - describe "with global disable_callbacks" do - around do |example| - original = Kithe.indexable_settings.disable_callbacks - Kithe.indexable_settings.disable_callbacks = true - example.run - Kithe.indexable_settings.disable_callbacks = original + work.destroy! + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| + JSON.parse(req.body) == { "delete" => work.id } + } end - it "does not index" do - work = TestWork.new(title: "test") + describe "with global disable_callbacks" do + around do |example| + original = Kithe.indexable_settings.disable_callbacks + Kithe.indexable_settings.disable_callbacks = true + example.run + Kithe.indexable_settings.disable_callbacks = original + end + + it "does not index" do + work = TestModelClass.new(title: "test") - expect(work).not_to receive(:update_index) - work.save! + expect(work).not_to receive(:update_index) + work.save! - expect(WebMock).not_to have_requested(:post, @solr_update_url) + expect(WebMock).not_to have_requested(:post, @solr_update_url) + end end - end - - describe "index_with block" do - describe "with batching" do - # TODO should this turn off softCommits? do we need a way to specify in index_with - # whether to do commits on every update, commits at end, and soft/hard? yes. - # - thread_settings = nil - it "batches solr updates" do - stub_request(:post, @solr_update_url) - expect(Kithe.indexable_settings.writer_class_name.constantize).to receive(:new).and_call_original - Kithe::Indexable.index_with(batching: true) do - TestWork.create!(title: "test1") - TestWork.create!(title: "test2") + describe "index_with block" do + describe "with batching" do + # TODO should this turn off softCommits? do we need a way to specify in index_with + # whether to do commits on every update, commits at end, and soft/hard? yes. + # + thread_settings = nil + it "batches solr updates" do + stub_request(:post, @solr_update_url) + expect(Kithe.indexable_settings.writer_class_name.constantize).to receive(:new).and_call_original + + Kithe::Indexable.index_with(batching: true) do + TestModelClass.create!(title: "test1") + TestModelClass.create!(title: "test2") + + thread_settings = Kithe::Indexable::ThreadSettings.current + expect(thread_settings.writer).to be_present + expect(thread_settings.writer).to receive(:close).and_call_original + end - thread_settings = Kithe::Indexable::ThreadSettings.current - expect(thread_settings.writer).to be_present - expect(thread_settings.writer).to receive(:close).and_call_original + expect(WebMock).to have_requested(:post, @solr_update_url).once + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| JSON.parse(req.body).count == 2} end - expect(WebMock).to have_requested(:post, @solr_update_url).once - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| JSON.parse(req.body).count == 2} - end - - it "creates no writer if no updates happen" do - expect(Kithe.indexable_settings.writer_class_name.constantize).not_to receive(:new) - Kithe::Indexable.index_with(batching: true) do + it "creates no writer if no updates happen" do + expect(Kithe.indexable_settings.writer_class_name.constantize).not_to receive(:new) + Kithe::Indexable.index_with(batching: true) do + end end - end - it "respects non-default on_finish" do - stub_request(:post, @solr_update_url) - stub_request(:get, "#{@solr_url}/update/json?commit=true") - expect(Kithe.indexable_settings.writer_class_name.constantize).to receive(:new).and_call_original + it "respects non-default on_finish" do + stub_request(:post, @solr_update_url) + stub_request(:get, "#{@solr_url}/update/json?commit=true") + expect(Kithe.indexable_settings.writer_class_name.constantize).to receive(:new).and_call_original - Kithe::Indexable.index_with(batching: true, on_finish: ->(w){ w.flush; w.commit(commit: true) }) do - TestWork.create!(title: "test1") - TestWork.create!(title: "test2") + Kithe::Indexable.index_with(batching: true, on_finish: ->(w){ w.flush; w.commit(commit: true) }) do + TestModelClass.create!(title: "test1") + TestModelClass.create!(title: "test2") - thread_settings = Kithe::Indexable::ThreadSettings.current - expect(thread_settings.writer).to be_present - expect(thread_settings.writer).to receive(:flush).and_call_original - end + thread_settings = Kithe::Indexable::ThreadSettings.current + expect(thread_settings.writer).to be_present + expect(thread_settings.writer).to receive(:flush).and_call_original + end - expect(WebMock).to have_requested(:post, @solr_update_url).once - expect(WebMock).to have_requested(:post, @solr_update_url). - with { |req| JSON.parse(req.body).count == 2} - end + expect(WebMock).to have_requested(:post, @solr_update_url).once + expect(WebMock).to have_requested(:post, @solr_update_url). + with { |req| JSON.parse(req.body).count == 2} + end - it "does not call on-finish if no writer was needed" do - Kithe::Indexable.index_with(batching: true, on_finish: ->(w){ raise "should not be called" }) do + it "does not call on-finish if no writer was needed" do + Kithe::Indexable.index_with(batching: true, on_finish: ->(w){ raise "should not be called" }) do + end end end - end - describe "auto_callbacks" do - it "happen with no args" do - stub_request(:post, @solr_update_url) - Kithe::Indexable.index_with do - first = TestWork.new(title: "test1") - expect(first).to receive(:update_index).once.and_call_original - first.save! + describe "auto_callbacks" do + it "happen with no args" do + stub_request(:post, @solr_update_url) + Kithe::Indexable.index_with do + first = TestModelClass.new(title: "test1") + expect(first).to receive(:update_index).once.and_call_original + first.save! - TestWork.create!(title: "test2") - end + TestModelClass.create!(title: "test2") + end - expect(WebMock).to have_requested(:post, @solr_update_url).twice + expect(WebMock).to have_requested(:post, @solr_update_url).twice - expect(Thread.current[Kithe::Indexable::ThreadSettings::THREAD_CURRENT_KEY]).to be_nil - end + expect(Thread.current[Kithe::Indexable::ThreadSettings::THREAD_CURRENT_KEY]).to be_nil + end - it "can be disabled" do - stub_request(:post, @solr_update_url) + it "can be disabled" do + stub_request(:post, @solr_update_url) - Kithe::Indexable.index_with(disable_callbacks: true) do - first = TestWork.new(title: "test1") - expect(first).not_to receive(:update_index) + Kithe::Indexable.index_with(disable_callbacks: true) do + first = TestModelClass.new(title: "test1") + expect(first).not_to receive(:update_index) - TestWork.create!(title: "test2") - end + TestModelClass.create!(title: "test2") + end - expect(WebMock).not_to have_requested(:post, @solr_update_url) + expect(WebMock).not_to have_requested(:post, @solr_update_url) - expect(Thread.current[Kithe::Indexable::ThreadSettings::THREAD_CURRENT_KEY]).to be_nil + expect(Thread.current[Kithe::Indexable::ThreadSettings::THREAD_CURRENT_KEY]).to be_nil + end end - end - describe "specified writer" do - let(:array_writer) { writer = Traject::ArrayWriter.new } + describe "specified writer" do + let(:array_writer) { writer = Traject::ArrayWriter.new } - it "raises ArgumentError if also batching" do - expect { - Kithe::Indexable.index_with(batching: true, writer: array_writer) do - end - }.to raise_error(ArgumentError) - end + it "raises ArgumentError if also batching" do + expect { + Kithe::Indexable.index_with(batching: true, writer: array_writer) do + end + }.to raise_error(ArgumentError) + end - it "uses custom writer, by default without close" do - expect(array_writer).to receive(:put).twice - Kithe::Indexable.index_with(writer: array_writer) do - TestWork.create!(title: "test1") - TestWork.create!(title: "test2") + it "uses custom writer, by default without close" do + expect(array_writer).to receive(:put).twice + Kithe::Indexable.index_with(writer: array_writer) do + TestModelClass.create!(title: "test1") + TestModelClass.create!(title: "test2") + end end - end - it "uses on_finish if specified" do - writer = double("writer") + it "uses on_finish if specified" do + writer = double("writer") - allow(writer).to receive(:close) - expect(writer).to receive(:put).twice + allow(writer).to receive(:close) + expect(writer).to receive(:put).twice - Kithe::Indexable.index_with(writer: writer, on_finish: ->(w) { w.close }) do - TestWork.create!(title: "test1") - TestWork.create!(title: "test2") + Kithe::Indexable.index_with(writer: writer, on_finish: ->(w) { w.close }) do + TestModelClass.create!(title: "test1") + TestModelClass.create!(title: "test2") + end end end end end end + + describe "With a Kithe::Work" do + it_behaves_like "Kithe::Indexable" do + temporary_class("TestModelClass") do + Class.new(Kithe::Work) do + self.kithe_indexable_mapper = Kithe::Indexer.new + self.kithe_indexable_auto_callbacks = false + end + end + end + end end