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