require "spec_helper" describe Mongoid::Association::Options do STORES_FOREIGN_KEY = [ Mongoid::Association::Referenced::HasAndBelongsToMany, Mongoid::Association::Referenced::BelongsTo ] [ Mongoid::Association::Embedded::EmbeddedIn, Mongoid::Association::Embedded::EmbedsMany, Mongoid::Association::Embedded::EmbedsOne, Mongoid::Association::Referenced::BelongsTo, Mongoid::Association::Referenced::HasMany, Mongoid::Association::Referenced::HasOne, Mongoid::Association::Referenced::HasAndBelongsToMany ].each do |association_class| context "when the association type is #{association_class}" do let(:class_left) do class ClassLeft; include Mongoid::Document; end ClassLeft end let(:class_right) do class ClassRight; include Mongoid::Document; end ClassRight end let(:association) do association_class.new(class_left, :name, options) end let(:options) do {} end describe 'the :as option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:as) do context 'when :as is in the options' do let(:options) do { as: :nameable } end it 'returns the :as value' do expect(association.as).to eq(:nameable) end end context 'when :as is not in the options' do it 'returns nil' do expect(association.as).to be_nil end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:as) do it 'returns nil' do expect(association.as).to be_nil end context 'when the option is provided' do let(:options) do { as: :nameable } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#autobuilding?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:autobuild) do context 'when :autobuild is in the options' do context 'when :autobuild is true' do let(:options) do { autobuild: true } end it 'returns true' do expect(association.autobuilding?).to be(true) end end context 'when :autobuild is false' do let(:options) do { autobuild: false } end it 'returns false' do expect(association.autobuilding?).to be(false) end end context 'when :autobuild is nil' do let(:options) do { autobuild: nil } end it 'returns false' do expect(association.autobuilding?).to be(false) end end end context 'when :autobuild is not in the options' do it 'returns false' do expect(association.autobuilding?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:autobuild) do it 'returns false' do expect(association.autobuilding?).to be(false) end context 'when the option is provided' do let(:options) do { autobuild: false } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#autosave?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:autosave) do context 'when :autosave is in the options' do context 'when :autosave is true' do let(:options) do { autosave: true } end it 'returns true' do expect(association.autosave?).to be(true) end end context 'when :autosave is false' do let(:options) do { autosave: false } end it 'returns false' do expect(association.autosave?).to be(false) end end context 'when :autosave is nil' do let(:options) do { autosave: nil } end it 'returns false' do expect(association.autosave?).to be(false) end end end context 'when :autosave is not in the options' do it 'returns false' do expect(association.autosave?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:autosave) do it 'returns false' do expect(association.autosave?).to be(false) end context 'when the option is provided' do let(:options) do { autosave: false } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :dependent option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:dependent) do context 'when :dependent is in the options' do let(:options) do { dependent: :delete_all } end it 'returns the :dependent value' do expect(association.dependent).to eq(options[:dependent]) end end context 'when :dependent is not in the options' do it 'returns nil' do expect(association.dependent).to be_nil end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:dependent) do it 'returns nil' do expect(association.dependent).to be_nil end context 'when the option is provided' do let(:options) do { dependent: :delete_all } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :foreign_key option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:foreign_key) do context 'when the :foreign_key option is specified' do let(:options) do { foreign_key: :some_field } end it 'returns the foreign_key as a String' do expect(association.foreign_key).to eq(options[:foreign_key].to_s) end end context 'when the association stores the foreign key', if: association_class::VALID_OPTIONS.include?(:foreign_key) && STORES_FOREIGN_KEY.include?(association_class) do context 'when :foreign_key option is not specified' do it 'returns the name followed by the foreign_key_suffix' do expect(association.foreign_key).to eq("name#{association.class::FOREIGN_KEY_SUFFIX}") end end end context 'when the association does not store the foreign key', if: association_class::VALID_OPTIONS.include?(:foreign_key) && !STORES_FOREIGN_KEY.include?(association_class) do context 'when :foreign_key option is not specified' do before do allow(association).to receive(:inverse).and_return(:other) end it 'returns the inverse name followed by the foreign_key_suffix' do expect(association.foreign_key).to eq("other#{association.class::FOREIGN_KEY_SUFFIX}") end end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:foreign_key) do it 'returns nil' do expect(association.dependent).to be_nil end context 'when the option is provided' do let(:options) do { foreign_key: :some_field } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :primary_key option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:primary_key) do context 'when the option is specified' do let(:options) do { primary_key: :other_id } end it 'returns the primary_key from the options as a String' do expect(association.primary_key).to eq(options[:primary_key].to_s) end end context 'when the option is not specified' do it 'returns the default primary key' do expect(association.primary_key).to eq(Mongoid::Association::Relatable::PRIMARY_KEY_DEFAULT) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:primary_key) do it 'returns nil' do expect(association.primary_key).to be_nil end context 'when the option is provided' do let(:options) do { primary_key: :other_id } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :after_add option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:after_add) do context 'when the :after_add option is specified' do let(:options) do { after_add: :method_name } end it 'retrieves the after_add method name from the association' do expect(association.get_callbacks(:after_add)).to eq(Array(options[:after_add])) end end context 'when the :after_add option is not specified' do it 'returns nil' do expect(association.get_callbacks(:after_add)).to be_empty end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:after_add) do it 'returns nil' do expect(association.get_callbacks(:after_add)).to be_empty end context 'when the option is provided' do let(:options) do { after_add: :method_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :after_remove option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:after_remove) do context 'when the :after_remove option is specified' do let(:options) do { after_remove: :method_name } end it 'retrieves the after_remove method name from the association' do expect(association.get_callbacks(:after_remove)).to eq(Array(options[:after_remove])) end end context 'when the :after_remove option is not specified' do it 'returns nil' do expect(association.get_callbacks(:after_remove)).to be_empty end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:after_remove) do it 'returns nil' do expect(association.get_callbacks(:after_remove)).to be_empty end context 'when the option is provided' do let(:options) do { after_remove: :method_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :before_add option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:before_add) do context 'when the :before_add option is specified' do let(:options) do { before_add: :method_name } end it 'retrieves the before_add method name from the association' do expect(association.get_callbacks(:before_add)).to eq(Array(options[:before_add])) end end context 'when the :before_add option is not specified' do it 'returns nil' do expect(association.get_callbacks(:before_add)).to be_empty end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:before_add) do it 'returns nil' do expect(association.get_callbacks(:before_add)).to be_empty end context 'when the option is provided' do let(:options) do { before_add: :method_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :before_remove option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:before_remove) do context 'when the :before_remove option is specified' do let(:options) do { before_remove: :method_name } end it 'retrieves the before_remove method name from the association' do expect(association.get_callbacks(:before_remove)).to eq(Array(options[:before_remove])) end end context 'when the :before_remove option is not specified' do it 'returns nil' do expect(association.get_callbacks(:before_remove)).to be_empty end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:before_remove) do it 'returns nil' do expect(association.get_callbacks(:before_remove)).to be_empty end context 'when the option is provided' do let(:options) do { before_remove: :method_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#indexed?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:index) do context 'when :index is in the options' do context 'when :index is true' do let(:options) do { index: true } end it 'returns true' do expect(association.indexed?).to be(true) end end context 'when :index is false' do let(:options) do { index: false } end it 'returns false' do expect(association.indexed?).to be(false) end end context 'when :index is nil' do let(:options) do { index: nil } end it 'returns false' do expect(association.indexed?).to be(false) end end end context 'when :index is not in the options' do it 'returns false' do expect(association.indexed?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:index) do it 'returns false' do expect(association.indexed?).to be(false) end context 'when the option is provided' do let(:options) do { index: true } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :order option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:order) do context 'when :order is in the options' do let(:options) do { order: :rating.desc } end it 'returns a Mongoid::Criteria::Queryable::Key' do expect(association.order).to be_a(Mongoid::Criteria::Queryable::Key) end end context 'when :order is not in the options' do it 'returns nil' do expect(association.order).to be_nil end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:order) do it 'returns nil' do expect(association.order).to be_nil end context 'when the option is provided' do let(:options) do { order: :rating.desc } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#polymorphic' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:polymorphic) do context 'when :polymorphic is in the options' do context 'when :polymorphic is true' do let(:options) do { polymorphic: true } end it 'returns true' do expect(association.polymorphic?).to be(true) end end context 'when :polymorphic is false' do let(:options) do { polymorphic: false } end it 'returns false' do expect(association.polymorphic?).to be(false) end end context 'when :polymorphic is nil' do let(:options) do { polymorphic: nil } end it 'returns false' do expect(association.polymorphic?).to be(false) end end end context 'when :polymorphic is not in the options' do it 'returns false' do expect(association.polymorphic?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:polymorphic) do it 'returns false' do expect(association.polymorphic?).to be(false) end context 'when the option is provided' do let(:options) do { polymorphic: true } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#cascading_callbacks?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:cascade_callbacks) do context 'when :cascade_callbacks is in the options' do context 'when :cascade_callbacks is true' do let(:options) do { cascade_callbacks: true } end it 'returns true' do expect(association.cascading_callbacks?).to be(true) end end context 'when :cascade_callbacks is false' do let(:options) do { cascade_callbacks: false } end it 'returns false' do expect(association.cascading_callbacks?).to be(false) end end context 'when :cascade_callbacks is nil' do let(:options) do { cascade_callbacks: nil } end it 'returns false' do expect(association.cascading_callbacks?).to be(false) end end end context 'when :cascade_callbacks is not in the options' do it 'returns false' do expect(association.cascading_callbacks?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:cascade_callbacks) do it 'returns false' do expect(association.cascading_callbacks?).to be(false) end context 'when the option is provided' do let(:options) do { cascade_callbacks: true } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#cyclic?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:cyclic) do context 'when :cyclic is in the options' do context 'when :cyclic is true' do let(:options) do { cyclic: true } end it 'returns true' do expect(association.cyclic?).to be(true) end end context 'when :cyclic is false' do let(:options) do { cyclic: false } end it 'returns false' do expect(association.cyclic?).to be(false) end end context 'when :cyclic is nil' do let(:options) do { cyclic: nil } end it 'returns false' do expect(association.cyclic?).to be(false) end end end context 'when :cyclic is not in the options' do it 'returns false' do expect(association.cyclic?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:cyclic) do it 'returns false' do expect(association.cyclic?).to be(false) end context 'when the option is provided' do let(:options) do { cyclic: true } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :store_as option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:store_as) do context 'when :store_as is in the options' do let(:options) do { store_as: :another_name } end it 'returns the :store_as value' do expect(association.store_as).to eq(options[:store_as].to_s) end end context 'when :store_as is not in the options' do it 'returns the name as a string' do expect(association.store_as).to eq("name") end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:store_as) do it 'returns nil' do expect(association.store_as).to be_nil end context 'when the option is provided' do let(:options) do { store_as: :another_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe 'the :class_name option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:class_name) do context 'when :class_name is in the options' do let(:options) do { class_name: :a_class } end it 'returns the :class_name value' do expect(association.class_name).to eq(:a_class) end end context 'when :class_name is not in the options' do it 'returns the name deduced from the association name' do expect(association.class_name).to eq(ActiveSupport::Inflector.classify(:name)) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:class_name) do it 'returns the name deduced from the association name' do expect(association.class_name).to eq(ActiveSupport::Inflector.classify(:name)) end context 'when the option is provided' do let(:options) do { class_name: :a_class } end it 'should support the option' do fail('All association types should support this option') end end end end describe '#counter_cached?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:counter_cache) do context 'when :counter_cache is in the options' do context 'when :counter_cache is true' do let(:options) do { counter_cache: true } end it 'returns true' do expect(association.counter_cached?).to be(true) end end context 'when :counter_cache is false' do let(:options) do { counter_cache: false } end it 'returns false' do expect(association.counter_cached?).to be(false) end end context 'when :counter_cache is nil' do let(:options) do { counter_cache: nil } end it 'returns false' do expect(association.counter_cached?).to be(false) end end end context 'when :counter_cache is not in the options' do it 'returns false' do expect(association.counter_cached?).to be(false) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:counter_cache) do it 'returns false' do expect(association.counter_cached?).to be(false) end context 'when the option is provided' do let(:options) do { counter_cache: :column_name } end it 'raises a Mongoid::Errors::InvalidRelationOption error' do expect { association }.to raise_exception(Mongoid::Errors::InvalidRelationOption) end end end end describe '#extension' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:extend) do context 'when a block is passed' do let!(:association) do association_class.new(class_left, :name, options) do; end end after do Object.send(:remove_const, :ClassLeft) end it 'defines an extension module' do expect(ClassLeft::ClassLeftNameRelationExtension).to be_a(Module) end it 'returns the extension' do expect(association.extension).to eq(ClassLeft::ClassLeftNameRelationExtension) end end context 'when a module name is passed' do before do class ClassLeft; end module ClassLeft::Handle; end end let(:options) do { extend: ClassLeft::Handle } end let!(:association) do association_class.new(class_left, :name, options) end it 'returns the extension' do expect(association.extension).to eq(ClassLeft::Handle) end end context 'when a block is not passed' do it 'does not define an extension module' do expect(defined?(ClassLeft::ClassLeftNameRelationExtension)).to be_nil end it 'returns nil' do expect(association.extension).to be_nil end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:extend) do it 'should support the option' do fail('All association types should support this option') end end end describe 'the :inverse_of option' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:inverse_of) do context 'when :inverse_of is in the options' do let(:options) do { inverse_of: :inverse_name } end it 'returns the :inverse_of value' do expect(association.inverse_of).to eq(:inverse_name) end end context 'when :inverse_of is not in the options' do it 'returns nil' do expect(association.inverse_of).to be_nil end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:inverse_of) do it 'returns nil' do expect(association.inverse_of).to be_nil end context 'when the option is provided' do let(:options) do { inverse_of: :inverse_name } end it 'should support the option' do fail('All association types should support this option') end end end end describe '#validate?' do context 'when the option is supported by the association type', if: association_class::VALID_OPTIONS.include?(:validate) do context 'when :validate is in the options' do context 'when :validate is true' do let(:options) do { validate: true } end it 'returns true' do expect(association.send(:validate?)).to be(true) end end context 'when :validate is false' do let(:options) do { validate: false } end it 'returns false' do expect(association.send(:validate?)).to be(false) end end context 'when :validate is nil' do let(:options) do { validate: nil } end it 'returns the validation default' do expect(association.send(:validate?)).to be(association.validation_default) end end end context 'when :validate is not in the options' do it 'returns the validation default' do expect(association.send(:validate?)).to be(association.validation_default) end end end context 'when the option is not supported by the association type', if: !association_class::VALID_OPTIONS.include?(:validate) do it 'returns false' do expect(association.send(:validate?)).to be(false) end context 'when the option is provided' do let(:options) do { validate: true } end it 'should support the option' do fail('All association types should support this option') end end end end end end end