require "spec_helper" describe Mongoid::Persistable::Updatable do describe "#update_attribute" do context "when the field is aliased" do let(:person) do Person.create end context "when the setter is overridden" do before do person.update_attribute(:overridden_setter, "value") end it "updates the field" do expect(person.overridden_setter).to eq("value") end it "clears the dirty attributes" do expect(person).to_not be_changed end it "calls the overridden setter" do expect(person.instance_variable_get(:@override_called)).to be true end it "persists the changes" do expect(person.reload.overridden_setter).to eq("value") end end context "when setting via the field name" do before do person.update_attribute(:t, "testing") end it "updates the field" do expect(person.t).to eq("testing") end it "persists the changes" do expect(person.reload.t).to eq("testing") end end context "when setting via the field alias" do before do person.update_attribute(:test, "testing") end it "updates the field" do expect(person.t).to eq("testing") end it "persists the changes" do expect(person.reload.t).to eq("testing") end end end context "when setting an array field" do let(:person) do Person.create(aliases: []) end before do person.update_attribute(:aliases, person.aliases << "Bond") end it "sets the new value in the document" do expect(person.aliases).to eq([ "Bond" ]) end it "persists the changes" do expect(person.reload.aliases).to eq([ "Bond" ]) end end context "when setting a boolean field" do context "when the field is true" do let(:person) do Person.new(terms: true) end context "when setting to false" do before do person.update_attribute(:terms, false) end it "persists the document" do expect(person).to be_persisted end it "changes the attribute value" do expect(person.terms).to be false end it "persists the changes" do expect(person.reload.terms).to be false end end end end context "when provided a symbol attribute name" do let(:post) do Post.new end context "when the document is valid" do before do post.update_attribute(:title, "Testing") end it "sets the attribute" do expect(post.title).to eq("Testing") end it "saves the document" do expect(post).to be_persisted end end context "when updating to the same value" do before do post.update_attribute(:title, "Testing") end it "returns true" do expect(post.update_attribute(:title, "Testing")).to be true end end context "when the document is invalid" do before do post.update_attribute(:title, "$invalid") end it "sets the attribute" do expect(post.title).to eq("$invalid") end it "saves the document" do expect(post).to be_persisted end end context "when the document has been destroyed" do before do post.delete end it "raises an error" do expect { post.update_attribute(:title, "something") }.to raise_error end end end context "when provided a string attribute name" do let(:post) do Post.new end context "when the document is valid" do before do post.update_attribute("title", "Testing") end it "sets the attribute" do expect(post.title).to eq("Testing") end it "saves the document" do expect(post).to be_persisted end end context "when the document is invalid" do before do post.update_attribute("title", "$invalid") end it "sets the attribute" do expect(post.title).to eq("$invalid") end it "saves the document" do expect(post).to be_persisted end end end context "when persisting a localized field" do let!(:product) do Product.create(description: "The bomb") end before do I18n.enforce_available_locales = false ::I18n.locale = :de product.update_attribute(:description, "Die Bombe") end after do ::I18n.locale = :en end let(:attributes) do product.attributes["description"] end it "persists the en locale" do expect(attributes["en"]).to eq("The bomb") end it "persists the de locale" do expect(attributes["de"]).to eq("Die Bombe") end end context "when updating a deeply embedded document" do let!(:person) do Person.create end let!(:address) do person.addresses.create(street: "Winterfeldtstr") end let!(:location) do address.locations.create(name: "work") end let(:from_db) do Person.last.addresses.last.locations.last end before do from_db.update_attribute(:name, "home") end it "updates the attribute" do expect(from_db.name).to eq("home") end it "persists the changes" do expect(from_db.reload.name).to eq("home") end end end [:update_attributes, :update].each do |method| describe "##{method}" do context "when saving with a hash field with invalid keys" do let(:person) do Person.create end it "raises an error" do expect { person.update_attributes(map: { "bad.key" => "value" }) }.to raise_error(Mongo::Error::OperationFailure) end end context "when validation passes" do let(:person) do Person.create end let!(:saved) do person.update_attributes(pets: false) end let(:from_db) do Person.find(person.id) end it "returns true" do expect(saved).to be true end it "saves the attributes" do expect(from_db.pets).to be false end end context "when the document has been destroyed" do let(:person) do Person.create end it "raises an error" do expect { person.send(method, map: { "bad.key" => "value" }) }.to raise_error(Mongo::Error::OperationFailure) end end context "when validation passes" do let(:person) do Person.create end let!(:saved) do person.send(method, pets: false) end let(:from_db) do Person.find(person.id) end it "returns true" do expect(saved).to be true end it "saves the attributes" do expect(from_db.pets).to be false end end context "when the document has been destroyed" do let!(:person) do Person.create end before do person.delete end it "raises an error" do expect { person.send(method, title: "something") }.to raise_error end end context "when updating through a one-to-one relation" do let(:person) do Person.create! end let(:game) do Game.create(person: person) end before do person.send(method, ssn: "444-44-4444") game.person.send(method, ssn: "555-66-7777") end let(:from_db) do Person.find(person.id) end it "saves the attributes" do expect(person.ssn).to eq("555-66-7777") end end context "on a new record" do let(:person) do Person.new end before do person.send(method, pets: false, title: nil) end it "saves the new record" do expect(Person.find(person.id)).to_not be_nil end end context "when passing in a relation" do context "when providing an embedded child" do let!(:person) do Person.create end let!(:name) do person.create_name(first_name: "test", last_name: "user") end let(:new_name) do Name.new(first_name: "Rupert", last_name: "Parkes") end before do person.send(method, name: new_name) end it "updates the embedded document" do expect(person.name).to eq(new_name) end it "persists the changes" do expect(person.reload.name).to eq(new_name) end end context "when providing a parent to a referenced in" do let!(:person) do Person.create end let!(:post) do Post.create(title: "Testing") end context "when the relation has not yet been touched" do before do post.send(method, person: person) end it "sets the instance of the relation" do expect(person.posts).to eq([ post ]) end it "sets properly through method_missing" do expect(person.posts.to_a).to eq([ post ]) end it "persists the reference" do expect(person.posts(true)).to eq([ post ]) end end context "when the relation has been touched" do before do person.posts post.send(method, person: person) end it "sets the instance of the relation" do expect(person.posts).to eq([ post ]) end it "sets properly through method_missing" do expect(person.posts.to_a).to eq([ post ]) end it "persists the reference" do expect(person.posts(true)).to eq([ post ]) end end end end context "when in a deeply nested hierarchy" do let!(:person) do Person.new(title: "The Boss") end let!(:phone_number) do Phone.new(number: "123-456-7890") end let!(:country_code) do CountryCode.new(code: 1) end before do phone_number.country_code = country_code person.phone_numbers << phone_number person.save end it "sets the first level document" do expect(person.phone_numbers.first).to eq(phone_number) end it "sets the second level document" do expect(person.phone_numbers.first.country_code).to eq(country_code) end context "when updating the first level document" do let(:phone) do person.phone_numbers.first end before do phone.number = "098-765-4321" phone.send(method, number: "098-765-4321") end it "sets the new attributes" do expect(phone.number).to eq("098-765-4321") end context "when reloading the root" do let(:reloaded) do person.reload end it "saves the new attributes" do expect(reloaded.phone_numbers.first.number).to eq("098-765-4321") end end end end end end describe "#update!" do context "when a callback returns false" do let(:oscar) do Oscar.new end it "raises a callback error" do expect { oscar.update!(title: "The Grouch") }.to raise_error(Mongoid::Errors::Callback) end end end end