lib/couchrest/model/designs/view.rb in couchrest_model-2.1.0.rc1 vs lib/couchrest/model/designs/view.rb in couchrest_model-2.2.0.beta1
- old
+ new
@@ -3,14 +3,14 @@
module Designs
#
# 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
+ # 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
+ # CouchDB views have inherent limitations, so joins and filters as used in
# a normal relational database are not possible.
#
class View
include Enumerable
@@ -50,11 +50,11 @@
# == View Execution Methods
#
# Request to the CouchDB database using the current query values.
-
+
# Return each row wrapped in a ViewRow object. Unlike the raw
# CouchDB request, this will provide an empty array if there
# are no results.
def rows
return @rows if @rows
@@ -63,11 +63,11 @@
yield ViewRow.new(row, model, use_database)
end
else
if execute && result['rows']
@rows ||= result['rows'].map{|v| ViewRow.new(v, model, use_database)}
- else
+ else
[ ]
end
end
end
@@ -79,11 +79,11 @@
include_docs!
docs(&block)
end
# Provide all the documents from the view. If the view has not been
- # prepared with the +include_docs+ option, each document will be
+ # prepared with the +include_docs+ option, each document will be
# loaded individually.
def docs
if block_given?
rows do |row|
yield row.doc
@@ -91,21 +91,21 @@
else
@docs ||= rows.map{|r| r.doc}
end
end
- # If another request has been made on the view, this will return
+ # If another request has been made on the view, this will return
# the first document in the set. If not, a new query object will be
- # generated with a limit of 1 so that only the first document is
+ # generated with a limit of 1 so that only the first document is
# loaded.
def first
result ? all.first : limit(1).all.first
end
# Same as first but will order the view in descending order. This
- # does not however reverse the search keys or the offset, so if you
- # are using a +startkey+ and +endkey+ you might end up with
+ # does not however reverse the search keys or the offset, so if you
+ # are using a +startkey+ and +endkey+ you might end up with
# unexpected results.
#
# If in doubt, don't use this method!
#
def last
@@ -125,11 +125,11 @@
# unexpected results if your reduce method does not calculate
# the total number of documents in a result set.
#
# Trying to use this method with the group option will raise an error.
#
- # If no reduce function is defined, a query will be performed
+ # If no reduce function is defined, a query will be performed
# to return the total number of rows, this is the equivalant of:
#
# view.limit(0).total_rows
#
def count
@@ -140,11 +140,11 @@
else
limit(0).total_rows
end
end
- # Check to see if the array of documents is empty. This *will*
+ # Check to see if the array of documents is empty. This *will*
# perform the query and return all documents ready to use, if you don't
# want to load anything, use +#total_rows+ or +#count+ instead.
def empty?
all.empty?
end
@@ -179,11 +179,11 @@
#
# Model.all(:raw => true, :limit => 0)['total_rows']
#
# In this example, the raw option will be ignored, and the total rows
# will still be accessible.
- #
+ #
def [](value)
execute[value]
end
# No yet implemented. Eventually this will provide a raw hash
@@ -192,12 +192,12 @@
raise "Not yet implemented"
end
# == View Filter Methods
- #
- # View filters return a copy of the view instance with the query
+ #
+ # View filters return a 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.
@@ -206,23 +206,23 @@
def key(value)
raise "View#key cannot be used when startkey or endkey have been set" unless query[:keys].nil? && query[:startkey].nil? && query[:endkey].nil?
update_query(:key => value)
end
- # Find all index keys that start with the value provided. May or may
+ # 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
+ # When the +#descending+ option is used (not the default), the start
# and end keys should be reversed, as per the CouchDB API.
#
# 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? && query[:keys].nil?
update_query(:startkey => value)
end
- # The result set should start from the position of the provided document.
+ # 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
@@ -235,23 +235,23 @@
def endkey(value)
raise "View#endkey cannot be used when key has been set" unless query[:key].nil? && query[:keys].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+
+ # 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
# Keys is a special CouchDB option that will cause the view request to be POSTed
- # including an array of keys. Only documents with the matching keys will be
- # returned. This is much faster than sending multiple requests for a set
+ # including an array of keys. Only documents with the matching keys will be
+ # returned. This is much faster than sending multiple requests for a set
# non-consecutive documents.
#
- # If no values are provided, this method will act as a wrapper around
+ # If no values are provided, this method will act as a wrapper around
# the rows result set, providing an array of keys.
def keys(*keys)
if keys.empty?
rows.map{|r| r.key}
else
@@ -300,20 +300,20 @@
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
+ # 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
- # Will set the level the grouping should be performed to. As per the
+ # Will set the level the grouping should be performed to. As per the
# CouchDB API, it only makes sense when the index key is an array.
- #
+ #
# This will automatically set the group option.
def group_level(value)
group.update_query(:group_level => value.to_i)
end
@@ -323,11 +323,11 @@
### Special View Filter Methods
# Allow the results of a query to be provided "stale". Setting to 'ok'
# will disable all view updates for the query.
- # When 'update_after' is provided the index will be update after the
+ # When 'update_after' is provided the index will be update after the
# result has been returned.
def stale(value)
unless (['ok', 'update_after'].include?(value.to_s))
raise "View#stale can only be set with 'ok' or 'update_after'."
end
@@ -443,21 +443,21 @@
def define_and_create(design_doc, name, opts = {})
define(design_doc, name, opts)
create_model_methods(design_doc, name, opts)
end
- # Simplified view definition. A new view will be added to the
+ # Simplified view definition. A new view will be added to the
# provided design document using the name and options.
#
- # If the view name starts with "by_" and +:by+ is not provided in
+ # If the view name starts with "by_" and +:by+ is not provided in
# the options, the new view's map method will be interpreted and
# generated automatically. For example:
#
# View.define(Meeting, design, "by_date_and_name")
#
- # Will create a view that searches by the date and name properties.
- # Explicity setting the attributes to use is possible using the
+ # Will create a view that searches by the date and name properties.
+ # Explicity setting the attributes to use is possible using the
# +:by+ option. For example:
#
# View.define(Meeting, design, "by_date_and_name", :by => [:date, :firstname, :lastname])
#
# The view name is the same, but three keys would be used in the
@@ -489,21 +489,26 @@
if opts[:by].nil? && name.to_s =~ /^by_(.+)/
opts[:by] = $1.split(/_and_/)
end
raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil?
+ # convert emit symbols to properties
+ opts[:emit] = "doc['#{opts[:emit]}']" if opts[:emit].is_a?(Symbol)
+ opts[:emit] = "[" + opts[:emit].map { |i| i.is_a?(Symbol) ? "doc['#{i}']" : i }.join(', ') + "]" if opts[:emit].is_a?(Array)
+
opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
opts[:guards] ||= []
opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.model_type_value}')"
keys = opts[:by].map{|o| "doc['#{o}']"}
- emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
+ emit_keys = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
+ emit_value = opts[:emit] || 1;
opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil]
opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank]
opts[:map] = <<-EOF
function(doc) {
if (#{opts[:guards].join(' && ')}) {
- emit(#{emit}, 1);
+ emit(#{emit_keys}, #{emit_value});
}
}
EOF
if opts[:reduce].nil?
# Use built-in sum function by default