Lots of advances on design view code, more testing required
This commit is contained in:
parent
dc28155aa3
commit
800c2b322c
|
@ -16,7 +16,7 @@ module CouchRest
|
|||
include CouchRest::Model::PropertyProtection
|
||||
include CouchRest::Model::Associations
|
||||
include CouchRest::Model::Validations
|
||||
include CouchRest::Model::Design
|
||||
include CouchRest::Model::Designs
|
||||
|
||||
def self.subclasses
|
||||
@subclasses ||= []
|
||||
|
|
|
@ -16,7 +16,7 @@ module CouchRest
|
|||
# end
|
||||
# end
|
||||
#
|
||||
module Design
|
||||
module Designs
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
|
@ -45,7 +45,7 @@ module CouchRest
|
|||
View.create(model, name, opts)
|
||||
model.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
||||
def self.#{name}(opts = {})
|
||||
CouchRest::Model::Design::View.new(self, opts, '#{name}')
|
||||
CouchRest::Model::Designs::View.new(self, opts, '#{name}')
|
||||
end
|
||||
EOS
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module CouchRest
|
||||
module Model
|
||||
module Design
|
||||
module Designs
|
||||
|
||||
#
|
||||
# A proxy class that allows view queries to be created using
|
||||
|
@ -42,15 +42,16 @@ module CouchRest
|
|||
# Inmediatly send a request to the database for all documents provided by the query.
|
||||
#
|
||||
def all(&block)
|
||||
include_docs.execute(&block)
|
||||
include_docs.rows.map{|r| r.doc}
|
||||
end
|
||||
|
||||
# Inmediatly send a request for the first result of the dataset. This will override
|
||||
# any limit set in the view previously.
|
||||
# Inmediatly send a request for the first result of the dataset.
|
||||
# This will override any limit set in the view previously.
|
||||
def first
|
||||
limit(1).include_docs.execute.first
|
||||
limit(1).all.first
|
||||
end
|
||||
|
||||
|
||||
def info
|
||||
|
||||
end
|
||||
|
@ -64,11 +65,16 @@ module CouchRest
|
|||
end
|
||||
|
||||
def rows
|
||||
@rows ||= execute['rows'].map{|v| ViewRow.new(v, model)}
|
||||
return @rows if @rows
|
||||
if execute && result['rows']
|
||||
@rows ||= result['rows'].map{|v| ViewRow.new(v, model)}
|
||||
else
|
||||
[ ]
|
||||
end
|
||||
end
|
||||
|
||||
def keys
|
||||
execute['rows'].map{|r| r.key}
|
||||
rows.map{|r| r.key}
|
||||
end
|
||||
|
||||
|
||||
|
@ -175,15 +181,35 @@ module CouchRest
|
|||
self.class.new(self, new_query)
|
||||
end
|
||||
|
||||
def database
|
||||
query[:database] || model.database
|
||||
end
|
||||
|
||||
# Used internally to ensure that docs are provided. Should not be used outside of
|
||||
# the view class under normal circumstances.
|
||||
def include_docs
|
||||
raise "Documents cannot be returned from a view that is prepared for a reduce" if query[:reduce]
|
||||
query.delete(:reduce)
|
||||
update_query(:include_docs => true)
|
||||
end
|
||||
|
||||
def execute(&block)
|
||||
self.result ||= model.view(name, query, &block)
|
||||
return self.result if result
|
||||
raise "Database must be defined in model or view!" if database.nil?
|
||||
retryable = true
|
||||
# Remove the reduce value if its not needed
|
||||
query.delete(:reduce) if !query[:reduce] && model.design_doc['views'][name.to_s]['reduce'].blank?
|
||||
begin
|
||||
self.result = model.design_doc.view_on(database, name, query, &block)
|
||||
rescue RestClient::ResourceNotFound => e
|
||||
if retryable
|
||||
model.save_design_doc(database)
|
||||
retryable = false
|
||||
retry
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Class Methods
|
||||
|
@ -208,7 +234,6 @@ module CouchRest
|
|||
# subsecuent index.
|
||||
#
|
||||
def create(model, name, opts = {})
|
||||
views = model.design_doc['views'] ||= {}
|
||||
|
||||
unless opts[:map]
|
||||
if opts[:by].nil? && name =~ /^by_(.+)/
|
||||
|
@ -221,17 +246,20 @@ module CouchRest
|
|||
|
||||
keys = opts[:by].map{|o| "doc['#{o}']"}
|
||||
emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
|
||||
opts[:map] =
|
||||
"function(doc) {" +
|
||||
" if (#{opts[:guards].join(' && ')}) {" +
|
||||
" emit(#{emit}, null);" +
|
||||
" }" +
|
||||
"}"
|
||||
opts[:guards] += keys.map{|k| "(#{k} != null)"}
|
||||
opts[:map] = <<-EOF
|
||||
function(doc) {
|
||||
if (#{opts[:guards].join(' && ')}) {
|
||||
emit(#{emit}, null);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
end
|
||||
|
||||
views[name.to_s] = {
|
||||
:map => opts[:map],
|
||||
:reduce => opts[:reduce] || false,
|
||||
model.design_doc['views'] ||= {}
|
||||
model.design_doc['views'][name.to_s] = {
|
||||
'map' => opts[:map],
|
||||
'reduce' => opts[:reduce]
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -245,21 +273,25 @@ module CouchRest
|
|||
attr_accessor :model
|
||||
def initialize(hash, model)
|
||||
self.model = model
|
||||
super(hash)
|
||||
replace(hash)
|
||||
end
|
||||
def id
|
||||
["id"]
|
||||
self["id"]
|
||||
end
|
||||
def key
|
||||
["key"]
|
||||
self["key"]
|
||||
end
|
||||
def value
|
||||
['value']
|
||||
self['value']
|
||||
end
|
||||
def raw_doc
|
||||
self['doc']
|
||||
end
|
||||
# Send a request for the linked document either using the "id" field's
|
||||
# value, or the ["value"]["_id"] used for linked documents.
|
||||
def doc
|
||||
doc_id = value['_id'] || self.id
|
||||
return model.create_from_database(self['doc']) if self['doc']
|
||||
doc_id = (value && value['_id']) ? value['_id'] : self.id
|
||||
model.get(doc_id)
|
||||
end
|
||||
end
|
|
@ -40,8 +40,8 @@ require "couchrest/model/class_proxy"
|
|||
require "couchrest/model/collection"
|
||||
require "couchrest/model/associations"
|
||||
require "couchrest/model/configuration"
|
||||
require "couchrest/model/design"
|
||||
require "couchrest/model/design/view"
|
||||
require "couchrest/model/designs"
|
||||
require "couchrest/model/designs/view"
|
||||
|
||||
# Monkey patches applied to couchrest
|
||||
require "couchrest/model/support/couchrest"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
require File.expand_path("../../../spec_helper", __FILE__)
|
||||
|
||||
class DesignViewModel < CouchRest::Model::Base
|
||||
use_database DB
|
||||
property :name
|
||||
property :title
|
||||
|
||||
design do
|
||||
view :by_name
|
||||
|
@ -11,7 +13,7 @@ end
|
|||
describe "Design View" do
|
||||
|
||||
before :each do
|
||||
@klass = CouchRest::Model::Design::View
|
||||
@klass = CouchRest::Model::Designs::View
|
||||
end
|
||||
|
||||
describe ".new" do
|
||||
|
@ -56,14 +58,28 @@ describe "Design View" do
|
|||
|
||||
describe ".create" do
|
||||
|
||||
before :all do
|
||||
before :each do
|
||||
@design_doc = {}
|
||||
DesignViewModel.stub!(:design_doc).and_return(@design_doc)
|
||||
end
|
||||
|
||||
it "should add a basic view" do
|
||||
@klass.create(DesignViewModel, 'test_view')
|
||||
@design_doc['test_view'].should_not be_nil
|
||||
@klass.create(DesignViewModel, 'test_view', :map => 'foo')
|
||||
@design_doc['views']['test_view'].should_not be_nil
|
||||
end
|
||||
|
||||
it "should auto generate mapping from name" do
|
||||
lambda { @klass.create(DesignViewModel, 'by_title') }.should_not raise_error
|
||||
str = @design_doc['views']['by_title']['map']
|
||||
str.should include("((doc['couchrest-type'] == 'DesignViewModel') && (doc['title'] != null))")
|
||||
str.should include("emit(doc['title'], null);")
|
||||
end
|
||||
|
||||
it "should auto generate mapping from name with and" do
|
||||
@klass.create(DesignViewModel, 'by_title_and_name')
|
||||
str = @design_doc['views']['by_title_and_name']['map']
|
||||
str.should include("(doc['title'] != null) && (doc['name'] != null)")
|
||||
str.should include("emit([doc['title'], doc['name']], null);")
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -110,6 +126,22 @@ describe "Design View" do
|
|||
|
||||
end
|
||||
|
||||
describe "index information" do
|
||||
it "should provide total_rows" do
|
||||
DesignViewModel.by_name.total_rows.should eql(5)
|
||||
end
|
||||
it "should provide total_rows" do
|
||||
DesignViewModel.by_name.total_rows.should eql(5)
|
||||
end
|
||||
it "should provide an offset" do
|
||||
DesignViewModel.by_name.offset.should eql(0)
|
||||
end
|
||||
it "should provide a set of keys" do
|
||||
DesignViewModel.by_name.limit(2).keys.should eql(["Judith", "Lorena"])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -13,19 +13,19 @@ describe "Design" do
|
|||
describe ".design" do
|
||||
|
||||
it "should instantiate a new DesignMapper" do
|
||||
CouchRest::Model::Design::DesignMapper.should_receive(:new).and_return(DesignModel)
|
||||
CouchRest::Model::Designs::DesignMapper.should_receive(:new).and_return(DesignModel)
|
||||
DesignModel.design() { }
|
||||
end
|
||||
|
||||
it "should instantiate a new DesignMapper with model" do
|
||||
CouchRest::Model::Design::DesignMapper.should_receive(:new).with(DesignModel).and_return(DesignModel)
|
||||
CouchRest::Model::Designs::DesignMapper.should_receive(:new).with(DesignModel).and_return(DesignModel)
|
||||
DesignModel.design() { }
|
||||
end
|
||||
|
||||
it "should allow methods to be called in mapper" do
|
||||
model = mock('Foo')
|
||||
model.should_receive(:foo)
|
||||
CouchRest::Model::Design::DesignMapper.stub!(:new).and_return(model)
|
||||
CouchRest::Model::Designs::DesignMapper.stub!(:new).and_return(model)
|
||||
DesignModel.design { foo }
|
||||
end
|
||||
|
||||
|
@ -39,7 +39,7 @@ describe "Design" do
|
|||
describe "DesignMapper" do
|
||||
|
||||
before :all do
|
||||
@klass = CouchRest::Model::Design::DesignMapper
|
||||
@klass = CouchRest::Model::Designs::DesignMapper
|
||||
end
|
||||
|
||||
it "should initialize and set model" do
|
||||
|
@ -54,20 +54,20 @@ describe "Design" do
|
|||
end
|
||||
|
||||
it "should call create method on view" do
|
||||
CouchRest::Model::Design::View.should_receive(:create).with(DesignModel, 'test', {})
|
||||
CouchRest::Model::Designs::View.should_receive(:create).with(DesignModel, 'test', {})
|
||||
@object.view('test')
|
||||
end
|
||||
|
||||
it "should create a method on parent model" do
|
||||
CouchRest::Model::Design::View.stub!(:create)
|
||||
CouchRest::Model::Designs::View.stub!(:create)
|
||||
@object.view('test_view')
|
||||
DesignModel.should respond_to(:test_view)
|
||||
end
|
||||
|
||||
it "should create a method that returns view instance" do
|
||||
CouchRest::Model::Design::View.stub!(:create)
|
||||
CouchRest::Model::Designs::View.stub!(:create)
|
||||
@object.view('test_view')
|
||||
CouchRest::Model::Design::View.should_receive(:new).with(DesignModel, {}, 'test_view').and_return(nil)
|
||||
CouchRest::Model::Designs::View.should_receive(:new).with(DesignModel, {}, 'test_view').and_return(nil)
|
||||
DesignModel.test_view
|
||||
end
|
||||
|
|
@ -32,7 +32,7 @@ RSpec.configure do |config|
|
|||
cr = TEST_SERVER
|
||||
test_dbs = cr.databases.select { |db| db =~ /^#{TESTDB}/ }
|
||||
test_dbs.each do |db|
|
||||
cr.database(db).delete! rescue nil
|
||||
#cr.database(db).delete! rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue