From d012380b673561660601131b22a845bd8c5c8787 Mon Sep 17 00:00:00 2001 From: Peter Gumeson Date: Thu, 28 May 2009 22:42:30 -0700 Subject: [PATCH] Added helper for accessing the top level document. And more rails compatibility. --- lib/couchrest/more/casted_model.rb | 7 ++++ lib/couchrest/more/extended_document.rb | 13 ++++++ lib/couchrest/support/rails.rb | 9 +++++ spec/couchrest/more/casted_model_spec.rb | 50 ++++++++++++++++++++++++ spec/fixtures/more/person.rb | 1 + 5 files changed, 80 insertions(+) diff --git a/lib/couchrest/more/casted_model.rb b/lib/couchrest/more/casted_model.rb index 2811de3..6740263 100644 --- a/lib/couchrest/more/casted_model.rb +++ b/lib/couchrest/more/casted_model.rb @@ -27,6 +27,13 @@ module CouchRest super(key.to_s) end + # Gets a reference to the top level extended + # document that a model is saved inside of + def base_doc + raise "Cannot call base_doc on a model that is not yet casted by a document" unless @casted_by + @casted_by.base_doc + end + # False if the casted model has already # been saved in the containing document def new_model? diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index 273e204..dbc929c 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -110,6 +110,19 @@ module CouchRest self.class.properties end + # Gets a reference to the actual document in the DB + # Calls up to the next document if there is one, + # Otherwise we're at the top and we return self + def base_doc + return self if base_doc? + @casted_by.base_doc + end + + # Checks if we're the top document + def base_doc? + !@casted_by + end + # Takes a hash as argument, and applies the values by using writer methods # for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are # missing. In case of error, no attributes are changed. diff --git a/lib/couchrest/support/rails.rb b/lib/couchrest/support/rails.rb index 19374c0..eaf1650 100644 --- a/lib/couchrest/support/rails.rb +++ b/lib/couchrest/support/rails.rb @@ -21,8 +21,17 @@ CouchRest::Document.class_eval do super end alias_method :kind_of?, :is_a? + alias_method :to_param, :id end +CouchRest::CastedModel.class_eval do + # The to_param method is needed for rails to generate resourceful routes. + # In your controller, remember that it's actually the id of the document. + def id + base_doc.id + end + alias_method :to_param, :id +end require Pathname.new(File.dirname(__FILE__)).join('..', 'validation', 'validation_errors') diff --git a/spec/couchrest/more/casted_model_spec.rb b/spec/couchrest/more/casted_model_spec.rb index b493656..faf175d 100644 --- a/spec/couchrest/more/casted_model_spec.rb +++ b/spec/couchrest/more/casted_model_spec.rb @@ -5,6 +5,7 @@ require File.join(FIXTURE_PATH, 'more', 'card') require File.join(FIXTURE_PATH, 'more', 'cat') require File.join(FIXTURE_PATH, 'more', 'person') require File.join(FIXTURE_PATH, 'more', 'question') +require File.join(FIXTURE_PATH, 'more', 'course') class WithCastedModelMixin < Hash @@ -299,4 +300,53 @@ describe CouchRest::CastedModel do @cat.toys.first.new_model?.should be_true end end + + describe "calling base_doc from a nested casted model" do + before :each do + @course = Course.new(:title => 'Science 101') + @professor = Person.new(:name => 'Professor Plum') + @cat = Cat.new(:name => 'Scratchy') + @toy1 = CatToy.new + @toy2 = CatToy.new + @course.professor = @professor + @professor.pet = @cat + @cat.favorite_toy = @toy1 + @cat.toys << @toy2 + end + + it "should reference the top document for" do + @course.base_doc.should === @course + @professor.base_doc.should === @course + @cat.base_doc.should === @course + @toy1.base_doc.should === @course + @toy2.base_doc.should === @course + end + + it "should call setter on top document" do + @toy1.base_doc.title = 'Tom Foolery' + @course.title.should == 'Tom Foolery' + end + end + + describe "calling base_doc.save from a nested casted model" do + before :each do + reset_test_db! + @cat = Cat.new(:name => 'Snowball') + @toy = CatToy.new + @cat.favorite_toy = @toy + end + + it "should not save parent document when casted model is invalid" do + @toy.should_not be_valid + @toy.base_doc.save.should be_false + lambda{@toy.base_doc.save!}.should raise_error + end + + it "should save parent document when nested casted model is valid" do + @toy.name = "Mr Squeaks" + @toy.should be_valid + @toy.base_doc.save.should be_true + lambda{@toy.base_doc.save!}.should_not raise_error + end + end end diff --git a/spec/fixtures/more/person.rb b/spec/fixtures/more/person.rb index ddc1bfd..de9e72c 100644 --- a/spec/fixtures/more/person.rb +++ b/spec/fixtures/more/person.rb @@ -1,6 +1,7 @@ class Person < Hash include ::CouchRest::CastedModel property :name + property :pet, :cast_as => 'Cat' def last_name name.last