require "spec_helper" describe "Single Table Inheritance" do let(:taggable) { TaggableModel.new(name: "taggable model") } let(:inheriting_model) { InheritingTaggableModel.new(name: "Inheriting Taggable Model") } let(:altered_inheriting) { AlteredInheritingTaggableModel.new(name: "Altered Inheriting Model") } 1.upto(4) do |n| let(:"inheriting_#{n}") { InheritingTaggableModel.new(name: "Inheriting Model #{n}") } end let(:student) { Student.create! } describe "tag contexts" do it "should pass on to STI-inherited models" do expect(inheriting_model).to respond_to(:tag_list, :skill_list, :language_list) expect(altered_inheriting).to respond_to(:tag_list, :skill_list, :language_list) end it "should pass on to altered STI models" do expect(altered_inheriting).to respond_to(:part_list) end end context "matching contexts" do before do inheriting_1.offering_list = "one, two" inheriting_1.need_list = "one, two" inheriting_1.save! inheriting_2.need_list = "one, two" inheriting_2.save! inheriting_3.offering_list = "one, two" inheriting_3.save! inheriting_4.tag_list = "one, two, three, four" inheriting_4.save! taggable.need_list = "one, two" taggable.save! end it "should find objects with tags of matching contexts" do expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to include(inheriting_2) expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_3) expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_4) expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(taggable) expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(inheriting_2) expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_3) expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_4) expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(taggable) end it "should not include the object itself in the list of related objects with tags of matching contexts" do expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_1) expect(inheriting_1.find_matching_contexts_for(InheritingTaggableModel, :offerings, :needs)).to_not include(inheriting_1) expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_1) end end context "find related tags" do before do inheriting_1.tag_list = "one, two" inheriting_1.save inheriting_2.tag_list = "three, four" inheriting_2.save inheriting_3.tag_list = "one, four" inheriting_3.save taggable.tag_list = "one, two, three, four" taggable.save end it "should find related objects based on tag names on context" do expect(inheriting_1.find_related_tags).to include(inheriting_3) expect(inheriting_1.find_related_tags).to_not include(inheriting_2) expect(inheriting_1.find_related_tags).to_not include(taggable) expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(inheriting_3) expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_2) expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(taggable) end it "should not include the object itself in the list of related objects" do expect(inheriting_1.find_related_tags).to_not include(inheriting_1) expect(inheriting_1.find_related_tags_for(InheritingTaggableModel)).to_not include(inheriting_1) expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_1) end end describe "tag list" do before do @inherited_same = InheritingTaggableModel.new(name: "inherited same") @inherited_different = AlteredInheritingTaggableModel.new(name: "inherited different") end # TODO, shared example it "should be able to save tags for inherited models" do inheriting_model.tag_list = "bob, kelso" inheriting_model.save expect(InheritingTaggableModel.tagged_with("bob").first).to eq(inheriting_model) end it "should find STI tagged models on the superclass" do inheriting_model.tag_list = "bob, kelso" inheriting_model.save expect(TaggableModel.tagged_with("bob").first).to eq(inheriting_model) end it "should be able to add on contexts only to some subclasses" do altered_inheriting.part_list = "fork, spoon" altered_inheriting.save expect(InheritingTaggableModel.tagged_with("fork", on: :parts)).to be_empty expect(AlteredInheritingTaggableModel.tagged_with("fork", on: :parts).first).to eq(altered_inheriting) end it "should have different tag_counts_on for inherited models" do inheriting_model.tag_list = "bob, kelso" inheriting_model.save! altered_inheriting.tag_list = "fork, spoon" altered_inheriting.save! expect(InheritingTaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso]) expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[fork spoon]) expect(TaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso fork spoon]) end it "should have different tags_on for inherited models" do inheriting_model.tag_list = "bob, kelso" inheriting_model.save! altered_inheriting.tag_list = "fork, spoon" altered_inheriting.save! expect(InheritingTaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso]) expect(AlteredInheritingTaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[fork spoon]) expect(TaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso fork spoon]) end it "should store same tag without validation conflict" do taggable.tag_list = "one" taggable.save! inheriting_model.tag_list = "one" inheriting_model.save! inheriting_model.update! name: "foo" end it "should only join with taggable table to check type for inherited models" do expect(TaggableModel.tag_counts_on(:tags).to_sql).to_not match(/INNER JOIN taggable_models ON/) expect(InheritingTaggableModel.tag_counts_on(:tags).to_sql).to match(/INNER JOIN taggable_models ON/) end end describe "ownership" do it "should have taggings" do student.tag(taggable, with: "ruby,scheme", on: :tags) expect(student.owned_taggings.count).to eq(2) end it "should have tags" do student.tag(taggable, with: "ruby,scheme", on: :tags) expect(student.owned_tags.count).to eq(2) end it "should return tags for the inheriting tagger" do student.tag(taggable, with: "ruby, scheme", on: :tags) expect(taggable.tags_from(student)).to eq(%w[ruby scheme]) end it "returns all owner tags on the taggable" do student.tag(taggable, with: "ruby, scheme", on: :tags) student.tag(taggable, with: "skill_one", on: :skills) student.tag(taggable, with: "english, spanish", on: :language) expect(taggable.owner_tags(student).count).to eq(5) expect(taggable.owner_tags(student).sort == %w[english ruby scheme skill_one spanish]) end it "returns owner tags on the tagger" do student.tag(taggable, with: "ruby, scheme", on: :tags) expect(taggable.owner_tags_on(student, :tags).count).to eq(2) end it "returns owner tags on the taggable for an array of contexts" do student.tag(taggable, with: "ruby, scheme", on: :tags) student.tag(taggable, with: "skill_one, skill_two", on: :skills) expect(taggable.owner_tags_on(student, [:tags, :skills]).count).to eq(4) expect(taggable.owner_tags_on(student, [:tags, :skills]).sort == %w[ruby scheme skill_one skill_two]) end it "should scope objects returned by tagged_with by owners" do student.tag(taggable, with: "ruby, scheme", on: :tags) expect(TaggableModel.tagged_with(%w[ruby scheme], owned_by: student).count).to eq(1) end end describe "a subclass of Tag" do let(:company) { Company.new(name: "Dewey, Cheatham & Howe") } let(:user) { User.create! } subject { Market.create! name: "finance" } it "sets STI type through string list" do company.market_list = "law, accounting" company.save! expect(Market.count).to eq(2) end it "does not interfere with a normal Tag context on the same model" do company.location_list = "cambridge" company.save! expect(MakeTaggable::Tag.where(name: "cambridge")).to_not be_empty end end end