Working on new Design and View support
This commit is contained in:
parent
1ccb96bf56
commit
f5f473c75c
42
lib/couchrest/model/design.rb
Normal file
42
lib/couchrest/model/design.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
#### NOTE Work in progress! Not yet used!
|
||||||
|
|
||||||
|
module CouchRest
|
||||||
|
module Model
|
||||||
|
|
||||||
|
# A design block in CouchRest Model groups together the functionality of CouchDB's
|
||||||
|
# design documents in a simple block definition.
|
||||||
|
#
|
||||||
|
# class Person < CouchRest::Model::Base
|
||||||
|
# property :name
|
||||||
|
# timestamps!
|
||||||
|
#
|
||||||
|
# design do
|
||||||
|
# view :by_name
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
module Design
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
|
||||||
|
def design(*args, &block)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
module DesignMethods
|
||||||
|
|
||||||
|
|
||||||
|
def view(*args)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
191
lib/couchrest/model/design/view.rb
Normal file
191
lib/couchrest/model/design/view.rb
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
module CouchRest
|
||||||
|
module Model
|
||||||
|
module Design
|
||||||
|
|
||||||
|
#
|
||||||
|
# ### NOTE Work in progress! Not yet used! ###
|
||||||
|
#
|
||||||
|
# A proxy class that allows view queries to be created using
|
||||||
|
# chained method calls. After each call a new instance of the method
|
||||||
|
# is created based on the original in a similar fashion to ruby's sequel
|
||||||
|
# library, or Rails 3's Arel.
|
||||||
|
#
|
||||||
|
# CouchDB views have inherent limitations, so joins and filters as used in
|
||||||
|
# a normal relational database are not possible. At least not yet!
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
class View
|
||||||
|
|
||||||
|
attr_accessor :query, :design, :database, :name
|
||||||
|
|
||||||
|
# Initialize a new View object. This method should not be called from outside CouchRest Model.
|
||||||
|
def initialize(parent, new_query = {}, name = nil)
|
||||||
|
if parent.is_a? Base
|
||||||
|
raise "Name must be provided for view to be initialized" if name.nil?
|
||||||
|
@name = name
|
||||||
|
@database = parent.database
|
||||||
|
@query = { :reduce => false }
|
||||||
|
elsif parent.is_a? View
|
||||||
|
@database = parent.database
|
||||||
|
@query = parent.query.dup
|
||||||
|
else
|
||||||
|
raise "View cannot be initialized without a parent Model or View"
|
||||||
|
end
|
||||||
|
@query.update(new_query)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# == View Execution Methods
|
||||||
|
#
|
||||||
|
# Send a request to the CouchDB database using the current query values.
|
||||||
|
|
||||||
|
# Inmediatly send a request to the database for all documents provided by the query.
|
||||||
|
#
|
||||||
|
def all(&block)
|
||||||
|
args = include_docs.query
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inmediatly send a request for the first result of the dataset. This will override
|
||||||
|
# any limit set in the view previously.
|
||||||
|
def first(&block)
|
||||||
|
args = limit(1).include_docs.query
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def info
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_rows
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def rows
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# == View Filter Methods
|
||||||
|
#
|
||||||
|
# View filters return an copy of the view instance with the query
|
||||||
|
# modified appropriatly. Errors will be raised if the methods
|
||||||
|
# are combined in an incorrect fashion.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# Find all entries in the index whose key matches the value provided.
|
||||||
|
#
|
||||||
|
# Cannot be used when the +#startkey+ or +#endkey+ have been set.
|
||||||
|
def key(value)
|
||||||
|
raise "View#key cannot be used when startkey or endkey have been set" unless query[:startkey].nil? && query[:endkey].nil?
|
||||||
|
update_query(:key => value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find all index keys that start with the value provided. May or may not be used in
|
||||||
|
# conjunction with the +endkey+ option.
|
||||||
|
#
|
||||||
|
# When the +#descending+ option is used (not the default), the start and end keys should
|
||||||
|
# be reversed.
|
||||||
|
#
|
||||||
|
# Cannot be used if the key has been set.
|
||||||
|
def startkey(value)
|
||||||
|
raise "View#startkey cannot be used when key has been set" unless query[:key].nil?
|
||||||
|
update_query(:startkey => value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# The result set should start from the position of the provided document.
|
||||||
|
# The value may be provided as an object that responds to the +#id+ call
|
||||||
|
# or a string.
|
||||||
|
def startkey_doc(value)
|
||||||
|
update_query(:startkey_docid => value.is_a?(String) ? value : value.id
|
||||||
|
end
|
||||||
|
|
||||||
|
# The opposite of +#startkey+, finds all index entries whose key is before the value specified.
|
||||||
|
#
|
||||||
|
# See the +#startkey+ method for more details and the +#inclusive_end+ option.
|
||||||
|
def endkey(value)
|
||||||
|
raise "View#endkey cannot be used when key has been set" unless query[:key].nil?
|
||||||
|
update_query(:endkey => value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# The result set should end at the position of the provided document.
|
||||||
|
# The value may be provided as an object that responds to the +#id+ call
|
||||||
|
# or a string.
|
||||||
|
def endkey_doc(value)
|
||||||
|
update_query(:endkey_docid => value.is_a?(String) ? value : value.id
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# The results should be provided in descending order.
|
||||||
|
#
|
||||||
|
# Descending is false by default, this method will enable it and cannot be undone.
|
||||||
|
def descending
|
||||||
|
update_query(:descending => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Limit the result set to the value supplied.
|
||||||
|
def limit(value)
|
||||||
|
update_query(:limit => value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Skip the number of entries in the index specified by value. This would be
|
||||||
|
# the equivilent of an offset in SQL.
|
||||||
|
#
|
||||||
|
# The CouchDB documentation states that the skip option should not be used
|
||||||
|
# with large data sets as it is inefficient. Use the +startkey_doc+ method
|
||||||
|
# instead to skip ranges efficiently.
|
||||||
|
def skip(value = 0)
|
||||||
|
update_query(:skip => value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use the reduce function on the view. If none is available this method will fail.
|
||||||
|
def reduce
|
||||||
|
update_query(:reduce => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Control whether the reduce function reduces to a set of distinct keys or to a single
|
||||||
|
# result row.
|
||||||
|
#
|
||||||
|
# By default the value is false, and can only be set when the view's +#reduce+ option
|
||||||
|
# has been set.
|
||||||
|
def group
|
||||||
|
raise "View#reduce must have been set before grouping is permitted" unless query[:reduce]
|
||||||
|
update_query(:group => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_level(value)
|
||||||
|
raise "View#reduce and View#group must have been set before group_level is called" unless query[:reduce] && query[:group]
|
||||||
|
update_query(:group_level => value.to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def update_query(new_query = {})
|
||||||
|
self.class.new(self, new_query)
|
||||||
|
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]
|
||||||
|
update_query(:include_docs => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def execute(&block)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,190 +0,0 @@
|
||||||
|
|
||||||
#### NOTE Work in progress! Not yet used!
|
|
||||||
|
|
||||||
module CouchRest
|
|
||||||
module Model
|
|
||||||
|
|
||||||
# A proxy class that allows view queries to be created using
|
|
||||||
# chained method calls. After each call a new instance of the method
|
|
||||||
# is created based on the original in a similar fashion to ruby's sequel
|
|
||||||
# library, or Rails 3's Arel.
|
|
||||||
#
|
|
||||||
# CouchDB views have inherent limitations, so joins and filters as used in
|
|
||||||
# a normal relational database are not possible. At least not yet!
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
class View
|
|
||||||
|
|
||||||
attr_accessor :query, :design, :database, :name
|
|
||||||
|
|
||||||
# Initialize a new View object. This method should not be called from outside CouchRest Model.
|
|
||||||
def initialize(parent, new_query = {}, name = nil)
|
|
||||||
if parent.is_a? Base
|
|
||||||
raise "Name must be provided for view to be initialized" if name.nil?
|
|
||||||
@name = name
|
|
||||||
@database = parent.database
|
|
||||||
@query = { :reduce => false }
|
|
||||||
elsif parent.is_a? View
|
|
||||||
@database = parent.database
|
|
||||||
@query = parent.query.dup
|
|
||||||
else
|
|
||||||
raise "View cannot be initialized without a parent Model or View"
|
|
||||||
end
|
|
||||||
@query.update(new_query)
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# == View Execution Methods
|
|
||||||
#
|
|
||||||
# Send a request to the CouchDB database using the current query values.
|
|
||||||
|
|
||||||
# Inmediatly send a request to the database for all documents provided by the query.
|
|
||||||
#
|
|
||||||
def all(&block)
|
|
||||||
args = include_docs.query
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Inmediatly send a request for the first result of the dataset. This will override
|
|
||||||
# any limit set in the view previously.
|
|
||||||
def first(&block)
|
|
||||||
args = limit(1).include_docs.query
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def info
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_rows
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def rows
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# == View Filter Methods
|
|
||||||
#
|
|
||||||
# View filters return an copy of the view instance with the query
|
|
||||||
# modified appropriatly. Errors will be raised if the methods
|
|
||||||
# are combined in an incorrect fashion.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
# Find all entries in the index whose key matches the value provided.
|
|
||||||
#
|
|
||||||
# Cannot be used when the +#startkey+ or +#endkey+ have been set.
|
|
||||||
def key(value)
|
|
||||||
raise "View#key cannot be used when startkey or endkey have been set" unless query[:startkey].nil? && query[:endkey].nil?
|
|
||||||
update_query(:key => value)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find all index keys that start with the value provided. May or may not be used in
|
|
||||||
# conjunction with the +endkey+ option.
|
|
||||||
#
|
|
||||||
# When the +#descending+ option is used (not the default), the start and end keys should
|
|
||||||
# be reversed.
|
|
||||||
#
|
|
||||||
# Cannot be used if the key has been set.
|
|
||||||
def startkey(value)
|
|
||||||
raise "View#startkey cannot be used when key has been set" unless query[:key].nil?
|
|
||||||
update_query(:startkey => value)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The result set should start from the position of the provided document.
|
|
||||||
# The value may be provided as an object that responds to the +#id+ call
|
|
||||||
# or a string.
|
|
||||||
def startkey_doc(value)
|
|
||||||
update_query(:startkey_docid => value.is_a?(String) ? value : value.id
|
|
||||||
end
|
|
||||||
|
|
||||||
# The opposite of +#startkey+, finds all index entries whose key is before the value specified.
|
|
||||||
#
|
|
||||||
# See the +#startkey+ method for more details and the +#inclusive_end+ option.
|
|
||||||
def endkey(value)
|
|
||||||
raise "View#endkey cannot be used when key has been set" unless query[:key].nil?
|
|
||||||
update_query(:endkey => value)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The result set should end at the position of the provided document.
|
|
||||||
# The value may be provided as an object that responds to the +#id+ call
|
|
||||||
# or a string.
|
|
||||||
def endkey_doc(value)
|
|
||||||
update_query(:endkey_docid => value.is_a?(String) ? value : value.id
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# The results should be provided in descending order.
|
|
||||||
#
|
|
||||||
# Descending is false by default, this method will enable it and cannot be undone.
|
|
||||||
def descending
|
|
||||||
update_query(:descending => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Limit the result set to the value supplied.
|
|
||||||
def limit(value)
|
|
||||||
update_query(:limit => value)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Skip the number of entries in the index specified by value. This would be
|
|
||||||
# the equivilent of an offset in SQL.
|
|
||||||
#
|
|
||||||
# The CouchDB documentation states that the skip option should not be used
|
|
||||||
# with large data sets as it is inefficient. Use the +startkey_doc+ method
|
|
||||||
# instead to skip ranges efficiently.
|
|
||||||
def skip(value = 0)
|
|
||||||
update_query(:skip => value)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Use the reduce function on the view. If none is available this method will fail.
|
|
||||||
def reduce
|
|
||||||
update_query(:reduce => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Control whether the reduce function reduces to a set of distinct keys or to a single
|
|
||||||
# result row.
|
|
||||||
#
|
|
||||||
# By default the value is false, and can only be set when the view's +#reduce+ option
|
|
||||||
# has been set.
|
|
||||||
def group
|
|
||||||
raise "View#reduce must have been set before grouping is permitted" unless query[:reduce]
|
|
||||||
update_query(:group => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def group_level(value)
|
|
||||||
raise "View#reduce and View#group must have been set before group_level is called" unless query[:reduce] && query[:group]
|
|
||||||
update_query(:group_level => value.to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def update_query(new_query = {})
|
|
||||||
self.class.new(self, new_query)
|
|
||||||
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]
|
|
||||||
update_query(:include_docs => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def execute(&block)
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
9
spec/couchrest/design/view_spec.rb
Normal file
9
spec/couchrest/design/view_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require File.expand_path("../../spec_helper", __FILE__)
|
||||||
|
|
||||||
|
|
||||||
|
describe "Design View" do
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue