require File.dirname(__FILE__) + '/../../spec_helper' require File.join(FIXTURE_PATH, 'more', 'article') require File.join(FIXTURE_PATH, 'more', 'course') describe "ExtendedDocument views" do class Unattached < CouchRest::ExtendedDocument # Note: no use_database here property :title property :questions property :professor view_by :title end describe "a model with simple views and a default param" do before(:all) do Article.all.map{|a| a.destroy(true)} Article.database.bulk_delete written_at = Time.now - 24 * 3600 * 7 @titles = ["this and that", "also interesting", "more fun", "some junk"] @titles.each do |title| a = Article.new(:title => title) a.date = written_at a.save written_at += 24 * 3600 end end it "should have a design doc" do Article.design_doc["views"]["by_date"].should_not be_nil end it "should save the design doc" do Article.by_date #rescue nil doc = Article.database.get Article.design_doc.id doc['views']['by_date'].should_not be_nil end it "should return the matching raw view result" do view = Article.by_date :raw => true view['rows'].length.should == 4 end it "should not include non-Articles" do Article.database.save_doc({"date" => 1}) view = Article.by_date :raw => true view['rows'].length.should == 4 end it "should return the matching objects (with default argument :descending => true)" do articles = Article.by_date articles.collect{|a|a.title}.should == @titles.reverse end it "should allow you to override default args" do articles = Article.by_date :descending => false articles.collect{|a|a.title}.should == @titles end end describe "another model with a simple view" do before(:all) do reset_test_db! %w{aaa bbb ddd eee}.each do |title| Course.new(:title => title).save end end it "should make the design doc upon first query" do Course.by_title doc = Course.design_doc doc['views']['all']['map'].should include('Course') end it "should can query via view" do # register methods with method-missing, for local dispatch. method # missing lookup table, no heuristics. view = Course.view :by_title designed = Course.by_title view.should == designed end it "should get them" do rs = Course.by_title rs.length.should == 4 end it "should yield" do courses = [] Course.view(:by_title) do |course| courses << course end courses[0]["doc"]["title"].should =='aaa' end it "should yield with by_key method" do courses = [] Course.by_title do |course| courses << course end courses[0]["doc"]["title"].should =='aaa' end end describe "a ducktype view" do before(:all) do reset_test_db! @id = DB.save_doc({:dept => true})['id'] end it "should setup" do duck = Course.get(@id) # from a different db duck["dept"].should == true end it "should make the design doc" do @as = Course.by_dept @doc = Course.design_doc @doc["views"]["by_dept"]["map"].should_not include("couchrest") end it "should not look for class" do @as = Course.by_dept @as[0]['_id'].should == @id end end describe "a model class not tied to a database" do before(:all) do reset_test_db! @db = DB %w{aaa bbb ddd eee}.each do |title| u = Unattached.new(:title => title) u.database = @db u.save @first_id ||= u.id end end it "should barf on all if no database given" do lambda{Unattached.all}.should raise_error end it "should query all" do rs = Unattached.all :database=>@db rs.length.should == 4 end it "should barf on query if no database given" do lambda{Unattached.view :by_title}.should raise_error end it "should make the design doc upon first query" do Unattached.by_title :database=>@db doc = Unattached.design_doc doc['views']['all']['map'].should include('Unattached') end it "should merge query params" do rs = Unattached.by_title :database=>@db, :startkey=>"bbb", :endkey=>"eee" rs.length.should == 3 end it "should query via view" do view = Unattached.view :by_title, :database=>@db designed = Unattached.by_title :database=>@db view.should == designed end it "should yield" do things = [] Unattached.view(:by_title, :database=>@db) do |thing| things << thing end things[0]["doc"]["title"].should =='aaa' end it "should yield with by_key method" do things = [] Unattached.by_title(:database=>@db) do |thing| things << thing end things[0]["doc"]["title"].should =='aaa' end it "should barf on get if no database given" do lambda{Unattached.get("aaa")}.should raise_error end it "should get from specific database" do u = Unattached.get(@first_id, @db) u.title.should == "aaa" end it "should barf on first if no database given" do lambda{Unattached.first}.should raise_error end it "should get first" do u = Unattached.first :database=>@db u.title.should =~ /\A...\z/ end it "should barf on all_design_doc_versions if no database given" do lambda{Unattached.all_design_doc_versions}.should raise_error end it "should be able to cleanup the db/bump the revision number" do # if the previous specs were not run, the model_design_doc will be blank Unattached.use_database DB Unattached.view_by :questions Unattached.by_questions(:database => @db) original_revision = Unattached.model_design_doc(@db)['_rev'] Unattached.cleanup_design_docs!(@db) Unattached.model_design_doc(@db)['_rev'].should_not == original_revision end end describe "class proxy" do before(:all) do reset_test_db! # setup the class default doc to save the design doc Unattached.use_database DB @us = Unattached.on(DB) %w{aaa bbb ddd eee}.each do |title| u = @us.new(:title => title) u.save @first_id ||= u.id end end it "should query all" do rs = @us.all rs.length.should == 4 end it "should make the design doc upon first query" do @us.by_title doc = @us.design_doc doc['views']['all']['map'].should include('Unattached') end it "should merge query params" do rs = @us.by_title :startkey=>"bbb", :endkey=>"eee" rs.length.should == 3 end it "should query via view" do view = @us.view :by_title designed = @us.by_title view.should == designed end it "should yield" do things = [] @us.view(:by_title) do |thing| things << thing end things[0]["doc"]["title"].should =='aaa' end it "should yield with by_key method" do things = [] @us.by_title do |thing| things << thing end things[0]["doc"]["title"].should =='aaa' end it "should get from specific database" do u = @us.get(@first_id) u.title.should == "aaa" end it "should get first" do u = @us.first u.title.should =~ /\A...\z/ end it "should clean up design docs left around on specific database" do @us.by_title original_id = @us.model_design_doc['_rev'] Unattached.view_by :professor @us.by_professor @us.model_design_doc['_rev'].should_not == original_id end end describe "a model with a compound key view" do before(:all) do Article.by_user_id_and_date.each{|a| a.destroy(true)} Article.database.bulk_delete written_at = Time.now - 24 * 3600 * 7 @titles = ["uniq one", "even more interesting", "less fun", "not junk"] @user_ids = ["quentin", "aaron"] @titles.each_with_index do |title,i| u = i % 2 a = Article.new(:title => title, :user_id => @user_ids[u]) a.date = written_at a.save written_at += 24 * 3600 end end it "should create the design doc" do Article.by_user_id_and_date rescue nil doc = Article.design_doc doc['views']['by_date'].should_not be_nil end it "should sort correctly" do articles = Article.by_user_id_and_date articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin', 'quentin'] articles[1].title.should == 'not junk' end it "should be queryable with couchrest options" do articles = Article.by_user_id_and_date :limit => 1, :startkey => 'quentin' articles.length.should == 1 articles[0].title.should == "even more interesting" end end describe "with a custom view" do before(:all) do @titles = ["very uniq one", "even less interesting", "some fun", "really junk", "crazy bob"] @tags = ["cool", "lame"] @titles.each_with_index do |title,i| u = i % 2 a = Article.new(:title => title, :tags => [@tags[u]]) a.save end end it "should be available raw" do view = Article.by_tags :raw => true view['rows'].length.should == 5 end it "should be default to :reduce => false" do ars = Article.by_tags ars.first.tags.first.should == 'cool' end it "should be raw when reduce is true" do view = Article.by_tags :reduce => true, :group => true view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3 end end # TODO: moved to Design, delete describe "adding a view" do before(:each) do reset_test_db! Article.by_date @original_doc_rev = Article.model_design_doc['_rev'] @design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999" end it "should not create a design doc on view definition" do Article.view_by :created_at newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999" newdocs["rows"].length.should == @design_docs["rows"].length end it "should create a new version of the design document on view access" do ddocs = Article.all_design_doc_versions["rows"].length Article.view_by :updated_at Article.by_updated_at @original_doc_rev.should_not == Article.model_design_doc['_rev'] Article.design_doc["views"].keys.should include("by_updated_at") end end describe "with a collection" do before(:all) do reset_test_db! @titles = ["very uniq one", "really interesting", "some fun", "really awesome", "crazy bob", "this rocks", "super rad"] @titles.each_with_index do |title,i| a = Article.new(:title => title, :date => Date.today) a.save end end it "should return a proxy that looks like an array of 7 Article objects" do articles = Article.by_date :key => Date.today articles.class.should == Array articles.size.should == 7 end it "should get a subset of articles using paginate" do articles = Article.by_date :key => Date.today articles.paginate(:page => 1, :per_page => 3).size.should == 3 articles.paginate(:page => 2, :per_page => 3).size.should == 3 articles.paginate(:page => 3, :per_page => 3).size.should == 1 end it "should get all articles, a few at a time, using paginated each" do articles = Article.by_date :key => Date.today articles.paginated_each(:per_page => 3) do |a| a.should_not be_nil end end it "should provide a class method to access the collection directly" do articles = Article.collection_proxy_for('Article', 'by_date', :descending => true, :key => Date.today, :include_docs => true) articles.class.should == Array articles.size.should == 7 end it "should provide a class method for paginate" do articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 3 articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 3 articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 1 end it "should provide a class method for paginated_each" do options = { :design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :page => 1, :descending => true, :key => Date.today, :include_docs => true } Article.paginated_each(options) do |a| a.should_not be_nil end end it "should provide a class method to get a collection for a view" do class Article provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true end articles = Article.find_all_article_details(:key => Date.today) articles.class.should == Array articles.size.should == 7 end it "should raise an exception if design_doc is not provided" do lambda{Article.collection_proxy_for(nil, 'by_date')}.should raise_error lambda{Article.paginate(:view_name => 'by_date')}.should raise_error end it "should raise an exception if view_name is not provided" do lambda{Article.collection_proxy_for('Article', nil)}.should raise_error lambda{Article.paginate(:design_doc => 'Article')}.should raise_error end end end