require File.expand_path(File.dirname(__FILE__) + '/spec_helper') class ResourceFoo include HashPersistent::Resource end class ResourceFooWithState include HashPersistent::Resource attr_accessor :dummy def ==(other) return self.class == other.class && self.dummy == other.dummy end end class ResourceFooOnlyUsedOnce include HashPersistent::Resource attr_accessor :dummy def ==(other) return self.class == other.class && self.dummy == other.dummy end end describe "A class that includes HashPersistent::Resource" do context "(class methods)" do it "should acquire a persist_to class method" do ResourceFoo.respond_to?(:persist_to).should be_true end end context "(persistence mechanism)" do it "should expect to persist to a hash-like store, with a string-like prefix" do ResourceFoo.persist_to({}, "_prefix") end it "should reject a non-hash-like store" do lambda {ResourceFoo.persist_to(1, "_prefix")}.should raise_error(ArgumentError) end it "should reject a non-string-like prefix" do lambda {ResourceFoo.persist_to({}, Dummy_NoStringRep.new)}.should raise_error(ArgumentError) end end context "(an instance that has not been saved)" do it "should maintain a key attribute" do ResourceFoo.new.respond_to?(:key).should be_true ResourceFoo.new.respond_to?(:key=).should be_true end it "should respond to a save method" do ResourceFoo.new.respond_to?(:save).should be_true end it "should respond to a delete method" do ResourceFoo.new.respond_to?(:delete).should be_true end it "should not save unless the key has been explictly set" do lambda{ResourceFoo.new.save}.should raise_error end it "should not delete unless the key has been explictly set" do lambda{ResourceFoo.new.delete}.should raise_error end it "should not complain when deleted, even after a key is set" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" lambda{resource.delete}.should_not raise_error end it "should not be findable via its key" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" ResourceFoo.find("fred").should == nil ResourceFoo.find("barney").should == nil end end context "(an instance that has been saved)" do it "should be findable via its key" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" resource.save ResourceFoo.find("fred").should_not be_nil end it "should correctly recover all of its state" do ResourceFooWithState.persist_to(Hash.new, "") resource = ResourceFooWithState.new resource.key = "fred" resource.dummy = ["something random", {}] resource.save resource_dup = resource.dup ResourceFooWithState.find("fred").should == resource_dup end it "should be deletable" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" resource.save lambda{resource.delete}.should_not raise_error end it "should save a new state when asked" do ResourceFooWithState.persist_to(Hash.new, "") resource = ResourceFooWithState.new resource.key = "fred" resource.dummy = ["something random", {}] resource.save resource_dup_1 = resource.dup resource.dummy = ["something else random", 1] resource.save resource_dup_2 = resource.dup ResourceFooWithState.find("fred").should_not == resource_dup_1 ResourceFooWithState.find("fred").should == resource_dup_2 end end context "(an instance that has been saved and then deleted)" do it "should not be findable via its key" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" resource.save resource.delete ResourceFoo.find("fred").should == nil end it "should not complain when deleted" do ResourceFoo.persist_to(Hash.new, "") resource = ResourceFoo.new resource.key = "fred" resource.save resource.delete lambda{resource.delete}.should_not raise_error end end context "(use of prefix to namespace keys)" do it "should (transparently) prepend the supplied prefix to the key of the saved instance" do store = Hash.new ResourceFoo.persist_to(store, "a_random_namespace::") resource = ResourceFoo.new resource.key = "fred" resource.save store.should_not have_key("fred") store.should have_key("a_random_namespace::fred") end it "should find instances when using a namespace, using only the key" do store = Hash.new ResourceFoo.persist_to(store, "a_random_namespace::") resource = ResourceFoo.new resource.key = "fred" resource.save ResourceFoo.find("fred").should_not == nil end it "should not find instances when mistakenly given a namespaced key" do store = Hash.new ResourceFoo.persist_to(store, "a_random_namespace::") resource = ResourceFoo.new resource.key = "fred" resource.save ResourceFoo.find("a_random_namespace::fred").should == nil end it "should correctly delete an instance from the store when using a prefix" do store = Hash.new ResourceFoo.persist_to(store, "a_random_namespace::") resource = ResourceFoo.new resource.key = "fred" resource.save resource.delete ResourceFoo.find("fred").should == nil end end context "(use of store)" do it "should correctly delete items from the store" do store = Hash.new ResourceFoo.persist_to(store, "") resource_1 = ResourceFoo.new resource_1.key = "fred" resource_1.save resource_2 = ResourceFoo.new resource_2.key = "barney" resource_2.save store.should_not == Hash.new resource_1.delete resource_2.delete store.should == Hash.new end end context "(use of restricted hash api)" do it "should use only the restricted hash api provided by moneta" do lambda { ResourceFooWithState.persist_to(Dummy_RestrictedHash.new, "prefix::") resource_1 = ResourceFooWithState.new resource_1.key = "fred" resource_1.dummy = ["barney"] resource_1.save ResourceFooWithState.find("fred").delete }.should_not raise_error end end context "(default store)" do it "should function with a vdefault store/prefix if none is supplied" do # This is implicitly tested elsewhere. Which makes this test documentation, I guess resource_1 = ResourceFooOnlyUsedOnce.new resource_1.key = "fred" resource_1.dummy = ["barney"] resource_1.save ResourceFooOnlyUsedOnce.find("fred").should == resource_1.dup lambda {ResourceFooOnlyUsedOnce.find("fred").delete}.should_not raise_error end end context "(multiple classes using module)" do # TODO: should check the callbacks here? Seems over the top... it "should not cross-contaminate classes that include the module" do store_1 = Hash.new ResourceFoo.persist_to(store_1, "") store_2 = Hash.new ResourceFooWithState.persist_to(store_2, "prefix::") store_1.should == Hash.new store_2.should == Hash.new resource_1 = ResourceFoo.new resource_1.key = "1" resource_1.save store_1.should_not == Hash.new store_2.should == Hash.new store_1.should have_key("1") store_1.should_not have_key("prefix::1") resource_1.delete store_1.should == Hash.new store_2.should == Hash.new resource_2 = ResourceFooWithState.new resource_2.key = "2" resource_2.save store_1.should == Hash.new store_2.should_not == Hash.new store_2.should have_key("prefix::2") store_2.should_not have_key("2") resource_2.delete store_1.should == Hash.new store_2.should == Hash.new end end context "(callbacks)" do it "should allow a class level callback to be set and retrieved for save events" do ResourceFoo.on_save.should == nil lambda{ResourceFoo.on_save do "thing" end}.should_not raise_error ResourceFoo.on_save.call.should == "thing" lambda{ResourceFoo.on_save(1)}.should raise_error end it "should allow a class level callback to be set and retrieved for delete events" do ResourceFoo.on_delete.should == nil lambda{ResourceFoo.on_delete do "thing" end}.should_not raise_error ResourceFoo.on_delete.call.should == "thing" lambda{ResourceFoo.on_delete(1)}.should raise_error end it "should yield an instance to the save callback when the instance is saved" do yielded_instance = nil ResourceFoo.on_save do |yielded| yielded_instance = yielded end instance = ResourceFoo.new instance.key = "1" instance.save yielded_instance.should_not == nil yielded_instance.should == instance end it "should yield an instance to the delete callback when the instance is deleted" do yielded_instance = nil ResourceFoo.on_delete do |yielded| yielded_instance = yielded end instance = ResourceFoo.new instance.key = "1" instance.save instance.delete yielded_instance.should_not == nil yielded_instance.should == instance end end end