require_relative "spec_helper" require_relative "../lib/data_broker" class TestValueObject include DataBroker.value_object values do attribute :id, Integer attribute :parent_id, Integer end belongs_to :parent has_many :friends delegate :name, :to => :parent delegate :recipes, :to => :parent, :return_on_nil => [] end class TestValueObjectWithType include DataBroker.value_object values do attribute :type, String end end describe DataBroker::ValueObject do describe ".new" do it "creates a new object" do obj = TestValueObject.new expect(obj).to be_a(TestValueObject) end it "accepts attributes in the constructor" do obj = TestValueObject.new(:id => 1, :parent_id => 2) expect(obj.id).to eq(1) expect(obj.parent_id).to eq(2) end end describe "#with" do it "creates a copy of the object with updated attributes" do obj1 = TestValueObject.new(:id => 1, :parent_id => 2) obj2 = obj1.with(:id => 2) expect(obj1).to_not eq(obj2) expect(obj2.id).to eq(2) expect(obj2.parent_id).to eq(2) end end describe "#type" do it "adds a type method that returns the class name if type is not defined" do obj = TestValueObject.new expect(obj.type).to eq("TestValueObject") end it "uses provided type if one already exists" do obj1 = TestValueObjectWithType.new obj2 = TestValueObjectWithType.new(:type => :foo) expect(obj1.type).to be_nil expect(obj2.type).to eq("foo") end end describe ".belongs_to" do it "adds a lookup method" do obj = TestValueObject.new expect(obj).to respond_to(:parent) end it "figures out what mapper and column to use, and uses them" do obj = TestValueObject.new(:parent_id => 1) mapper = double("ParentMapper") allow(obj).to receive(:constantize).with("ParentMapper") { mapper } expect(mapper).to receive(:find).with(1) obj.parent end it "memoizes the results" do obj = TestValueObject.new(:parent_id => 1) mapper = double("ParentMapper", :find => :foo) allow(obj).to receive(:constantize).with("ParentMapper") { mapper } 2.times { obj.parent } expect(mapper).to have_received(:find).once end end describe ".has_many" do context "lookup method" do it "adds a lookup method" do obj = TestValueObject.new expect(obj).to respond_to(:friends) end it "figures out what mapper and via to use, and uses them" do obj = TestValueObject.new(:id => 1) mapper = double("FriendMapper") allow(obj).to receive(:constantize).with("FriendMapper") { mapper } expect(mapper).to receive(:where).with(:test_value_object_id => 1) obj.friends end it "memoizes results" do obj = TestValueObject.new(:id => 1) mapper = double("FriendMapper", :where => :foo) allow(obj).to receive(:constantize).with("FriendMapper") { mapper } 2.times { obj.friends } expect(mapper).to have_received(:where).once end end context "id lookup method" do it "add an id lookup method" do obj = TestValueObject.new expect(obj).to respond_to(:friend_ids) end it "gets the ids of objects found via the lookup method" do obj = TestValueObject.new(:id => 1) mapper = double("FriendMapper") allow(obj).to receive(:constantize).with("FriendMapper") { mapper } expect(mapper).to receive(:where).with(:test_value_object_id => 1) { [double(:id => 1), double(:id => 2)] } expect(obj.friend_ids).to eq([1,2]) end it "memoizes results" do obj = TestValueObject.new(:id => 1) results = [double(:id => 1), double(:id => 2)] mapper = double("FriendMapper", :where => results) allow(obj).to receive(:constantize).with("FriendMapper") { mapper } 2.times { obj.friend_ids } expect(mapper).to have_received(:where).once end end end describe ".delegate" do it "creates a delegated method" do obj = TestValueObject.new expect(obj).to respond_to(:parent_name) end it "sends the message to the delegated object" do obj = TestValueObject.new parent = double(:name => "Bobby") allow(obj).to receive(:parent) { parent } expect(obj.parent_name).to eq("Bobby") end context "with no return_on_nil value specified" do it "returns nil if the delegated object is nil" do obj = TestValueObject.new allow(obj).to receive(:parent) { nil } expect(obj.parent_name).to be_nil end end context "with a return_on_nil value specified" do it "returns the specified value if delegated object is nil" do obj = TestValueObject.new allow(obj).to receive(:parent) { nil } expect(obj.parent_recipes).to be_empty end end it "memoizes the results" do obj = TestValueObject.new allow(obj).to receive(:parent) { double(:name => true) } 2.times { obj.parent_name } expect(obj).to have_received(:parent).once end end describe "#==" do it "considers objects with the same attributes to be equivalent" do obj1 = TestValueObject.new(:id => 1, :parent_id => 2) obj2 = TestValueObject.new(:id => 1, :parent_id => 2) expect(obj1).to eq(obj2) end it "considers objects with non-matching attributes to not be equivalent" do obj1 = TestValueObject.new(:id => 1, :parent_id => 2) obj2 = TestValueObject.new(:id => 2, :parent_id => 2) expect(obj1).to_not eq(obj2) end end end