samflores-couchrest 0.2.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +10 -34
- data/Rakefile +5 -2
- data/bin/couchdir +20 -0
- data/examples/model/example.rb +13 -19
- data/examples/word_count/word_count.rb +24 -3
- data/examples/word_count/word_count_query.rb +6 -7
- data/lib/couchrest/core/database.rb +49 -126
- data/lib/couchrest/core/document.rb +25 -58
- data/lib/couchrest/core/model.rb +612 -0
- data/lib/couchrest/core/server.rb +10 -47
- data/lib/couchrest/core/validations.rb +328 -0
- data/lib/couchrest/monkeypatches.rb +0 -95
- data/lib/couchrest.rb +10 -57
- data/spec/couchrest/core/database_spec.rb +68 -183
- data/spec/couchrest/core/design_spec.rb +1 -1
- data/spec/couchrest/core/document_spec.rb +104 -285
- data/spec/couchrest/core/model_spec.rb +855 -0
- data/spec/couchrest/helpers/pager_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -13
- metadata +17 -83
- data/examples/word_count/word_count_views.rb +0 -26
- data/lib/couchrest/core/response.rb +0 -16
- data/lib/couchrest/mixins/attachments.rb +0 -31
- data/lib/couchrest/mixins/callbacks.rb +0 -483
- data/lib/couchrest/mixins/design_doc.rb +0 -64
- data/lib/couchrest/mixins/document_queries.rb +0 -48
- data/lib/couchrest/mixins/extended_attachments.rb +0 -68
- data/lib/couchrest/mixins/extended_document_mixins.rb +0 -6
- data/lib/couchrest/mixins/properties.rb +0 -125
- data/lib/couchrest/mixins/validation.rb +0 -234
- data/lib/couchrest/mixins/views.rb +0 -168
- data/lib/couchrest/mixins.rb +0 -4
- data/lib/couchrest/more/casted_model.rb +0 -28
- data/lib/couchrest/more/extended_document.rb +0 -217
- data/lib/couchrest/more/property.rb +0 -40
- data/lib/couchrest/support/blank.rb +0 -42
- data/lib/couchrest/support/class.rb +0 -191
- data/lib/couchrest/validation/auto_validate.rb +0 -163
- data/lib/couchrest/validation/contextual_validators.rb +0 -78
- data/lib/couchrest/validation/validation_errors.rb +0 -118
- data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
- data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -99
- data/lib/couchrest/validation/validators/format_validator.rb +0 -117
- data/lib/couchrest/validation/validators/formats/email.rb +0 -66
- data/lib/couchrest/validation/validators/formats/url.rb +0 -43
- data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
- data/lib/couchrest/validation/validators/length_validator.rb +0 -134
- data/lib/couchrest/validation/validators/method_validator.rb +0 -89
- data/lib/couchrest/validation/validators/numeric_validator.rb +0 -104
- data/lib/couchrest/validation/validators/required_field_validator.rb +0 -109
- data/spec/couchrest/core/server_spec.rb +0 -35
- data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -40
- data/spec/couchrest/more/casted_model_spec.rb +0 -98
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -130
- data/spec/couchrest/more/extended_doc_spec.rb +0 -509
- data/spec/couchrest/more/extended_doc_view_spec.rb +0 -207
- data/spec/couchrest/more/property_spec.rb +0 -130
- data/spec/couchrest/support/class_spec.rb +0 -59
- data/spec/fixtures/more/article.rb +0 -34
- data/spec/fixtures/more/card.rb +0 -20
- data/spec/fixtures/more/course.rb +0 -14
- data/spec/fixtures/more/event.rb +0 -6
- data/spec/fixtures/more/invoice.rb +0 -17
- data/spec/fixtures/more/person.rb +0 -8
- data/spec/fixtures/more/question.rb +0 -6
- data/spec/fixtures/more/service.rb +0 -12
@@ -1,125 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '..', 'more', 'property')
|
2
|
-
|
3
|
-
module CouchRest
|
4
|
-
module Mixins
|
5
|
-
module Properties
|
6
|
-
|
7
|
-
class IncludeError < StandardError; end
|
8
|
-
|
9
|
-
def self.included(base)
|
10
|
-
base.cattr_accessor(:properties)
|
11
|
-
base.class_eval <<-EOS, __FILE__, __LINE__
|
12
|
-
@@properties = []
|
13
|
-
EOS
|
14
|
-
base.extend(ClassMethods)
|
15
|
-
raise CouchRest::Mixins::Properties::IncludeError, "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (base.new.respond_to?(:[]) && base.new.respond_to?(:[]=))
|
16
|
-
end
|
17
|
-
|
18
|
-
def apply_defaults
|
19
|
-
return unless self.respond_to?(:new_document?) && new_document?
|
20
|
-
return unless self.class.respond_to?(:properties)
|
21
|
-
return if self.class.properties.empty?
|
22
|
-
# TODO: cache the default object
|
23
|
-
self.class.properties.each do |property|
|
24
|
-
key = property.name.to_s
|
25
|
-
# let's make sure we have a default and we can assign the value
|
26
|
-
if property.default && (self.respond_to?("#{key}=") || self.key?(key))
|
27
|
-
if property.default.class == Proc
|
28
|
-
self[key] = property.default.call
|
29
|
-
else
|
30
|
-
self[key] = Marshal.load(Marshal.dump(property.default))
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def cast_keys
|
37
|
-
return unless self.class.properties
|
38
|
-
self.class.properties.each do |property|
|
39
|
-
next unless property.casted
|
40
|
-
key = self.has_key?(property.name) ? property.name : property.name.to_sym
|
41
|
-
target = property.type
|
42
|
-
if target.is_a?(Array)
|
43
|
-
next unless self[key]
|
44
|
-
klass = ::CouchRest.constantize(target[0])
|
45
|
-
self[property.name] = self[key].collect do |value|
|
46
|
-
# Auto parse Time objects
|
47
|
-
obj = ( (property.init_method == 'new') && klass == Time) ? Time.parse(value) : klass.send(property.init_method, value)
|
48
|
-
obj.casted_by = self if obj.respond_to?(:casted_by)
|
49
|
-
obj
|
50
|
-
end
|
51
|
-
else
|
52
|
-
# Auto parse Time objects
|
53
|
-
self[property.name] = if ((property.init_method == 'new') && target == 'Time')
|
54
|
-
self[key].is_a?(String) ? Time.parse(self[key].dup) : self[key]
|
55
|
-
else
|
56
|
-
# Let people use :send as a Time parse arg
|
57
|
-
klass = ::CouchRest.constantize(target)
|
58
|
-
# I'm not convince we should or should not create a new instance if we are casting a doc/extended doc without default value and nothing was passed
|
59
|
-
# unless (property.casted &&
|
60
|
-
# (klass.superclass == CouchRest::ExtendedDocument || klass.superclass == CouchRest::Document) &&
|
61
|
-
# (self[key].nil? || property.default.nil?))
|
62
|
-
klass.send(property.init_method, self[key])
|
63
|
-
#end
|
64
|
-
end
|
65
|
-
self[property.name].casted_by = self if self[property.name].respond_to?(:casted_by)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
module ClassMethods
|
71
|
-
|
72
|
-
def property(name, options={})
|
73
|
-
define_property(name, options) unless self.properties.map{|p| p.name}.include?(name.to_s)
|
74
|
-
end
|
75
|
-
|
76
|
-
protected
|
77
|
-
|
78
|
-
# This is not a thread safe operation, if you have to set new properties at runtime
|
79
|
-
# make sure to use a mutex.
|
80
|
-
def define_property(name, options={})
|
81
|
-
# check if this property is going to casted
|
82
|
-
options[:casted] = options[:cast_as] ? options[:cast_as] : false
|
83
|
-
property = CouchRest::Property.new(name, (options.delete(:cast_as) || options.delete(:type)), options)
|
84
|
-
create_property_getter(property)
|
85
|
-
create_property_setter(property) unless property.read_only == true
|
86
|
-
properties << property
|
87
|
-
end
|
88
|
-
|
89
|
-
# defines the getter for the property (and optional aliases)
|
90
|
-
def create_property_getter(property)
|
91
|
-
# meth = property.name
|
92
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
93
|
-
def #{property.name}
|
94
|
-
self['#{property.name}']
|
95
|
-
end
|
96
|
-
EOS
|
97
|
-
|
98
|
-
if property.alias
|
99
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
100
|
-
alias #{property.alias.to_sym} #{property.name.to_sym}
|
101
|
-
EOS
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# defines the setter for the property (and optional aliases)
|
106
|
-
def create_property_setter(property)
|
107
|
-
meth = property.name
|
108
|
-
class_eval <<-EOS
|
109
|
-
def #{meth}=(value)
|
110
|
-
self['#{meth}'] = value
|
111
|
-
end
|
112
|
-
EOS
|
113
|
-
|
114
|
-
if property.alias
|
115
|
-
class_eval <<-EOS
|
116
|
-
alias #{property.alias.to_sym}= #{meth.to_sym}=
|
117
|
-
EOS
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
end # module ClassMethods
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
@@ -1,234 +0,0 @@
|
|
1
|
-
# Extracted from dm-validations 0.9.10
|
2
|
-
#
|
3
|
-
# Copyright (c) 2007 Guy van den Berg
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
# a copy of this software and associated documentation files (the
|
7
|
-
# "Software"), to deal in the Software without restriction, including
|
8
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
# the following conditions:
|
12
|
-
#
|
13
|
-
# The above copyright notice and this permission notice shall be
|
14
|
-
# included in all copies or substantial portions of the Software.
|
15
|
-
#
|
16
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
-
|
24
|
-
class Object
|
25
|
-
def validatable?
|
26
|
-
false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
require 'pathname'
|
31
|
-
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
32
|
-
|
33
|
-
dir = File.join(Pathname(__FILE__).dirname.expand_path, '..', 'validation')
|
34
|
-
|
35
|
-
require File.join(dir, 'validation_errors')
|
36
|
-
require File.join(dir, 'contextual_validators')
|
37
|
-
require File.join(dir, 'auto_validate')
|
38
|
-
|
39
|
-
require File.join(dir, 'validators', 'generic_validator')
|
40
|
-
require File.join(dir, 'validators', 'required_field_validator')
|
41
|
-
require File.join(dir, 'validators', 'absent_field_validator')
|
42
|
-
require File.join(dir, 'validators', 'format_validator')
|
43
|
-
require File.join(dir, 'validators', 'length_validator')
|
44
|
-
require File.join(dir, 'validators', 'numeric_validator')
|
45
|
-
require File.join(dir, 'validators', 'method_validator')
|
46
|
-
require File.join(dir, 'validators', 'confirmation_validator')
|
47
|
-
|
48
|
-
module CouchRest
|
49
|
-
module Validation
|
50
|
-
|
51
|
-
def self.included(base)
|
52
|
-
base.cattr_accessor(:auto_validation)
|
53
|
-
base.class_eval <<-EOS, __FILE__, __LINE__
|
54
|
-
# Turn off auto validation by default
|
55
|
-
@@auto_validation = false
|
56
|
-
|
57
|
-
# Force the auto validation for the class properties
|
58
|
-
# This feature is still not fully ported over,
|
59
|
-
# test are lacking, so please use with caution
|
60
|
-
def self.auto_validate!
|
61
|
-
self.auto_validation = true
|
62
|
-
end
|
63
|
-
EOS
|
64
|
-
|
65
|
-
base.extend(ClassMethods)
|
66
|
-
base.class_eval <<-EOS, __FILE__, __LINE__
|
67
|
-
if method_defined?(:_run_save_callbacks)
|
68
|
-
save_callback :before, :check_validations
|
69
|
-
end
|
70
|
-
EOS
|
71
|
-
base.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
72
|
-
def self.define_property(name, options={})
|
73
|
-
super
|
74
|
-
auto_generate_validations(properties.last)
|
75
|
-
autovalidation_check = true
|
76
|
-
end
|
77
|
-
RUBY_EVAL
|
78
|
-
end
|
79
|
-
|
80
|
-
# Ensures the object is valid for the context provided, and otherwise
|
81
|
-
# throws :halt and returns false.
|
82
|
-
#
|
83
|
-
def check_validations(context = :default)
|
84
|
-
throw(:halt, false) unless context.nil? || valid?(context)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Return the ValidationErrors
|
88
|
-
#
|
89
|
-
def errors
|
90
|
-
@errors ||= ValidationErrors.new
|
91
|
-
end
|
92
|
-
|
93
|
-
# Mark this resource as validatable. When we validate associations of a
|
94
|
-
# resource we can check if they respond to validatable? before trying to
|
95
|
-
# recursivly validate them
|
96
|
-
#
|
97
|
-
def validatable?
|
98
|
-
true
|
99
|
-
end
|
100
|
-
|
101
|
-
# Alias for valid?(:default)
|
102
|
-
#
|
103
|
-
def valid_for_default?
|
104
|
-
valid?(:default)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Check if a resource is valid in a given context
|
108
|
-
#
|
109
|
-
def valid?(context = :default)
|
110
|
-
self.class.validators.execute(context, self)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Begin a recursive walk of the model checking validity
|
114
|
-
#
|
115
|
-
def all_valid?(context = :default)
|
116
|
-
recursive_valid?(self, context, true)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Do recursive validity checking
|
120
|
-
#
|
121
|
-
def recursive_valid?(target, context, state)
|
122
|
-
valid = state
|
123
|
-
target.instance_variables.each do |ivar|
|
124
|
-
ivar_value = target.instance_variable_get(ivar)
|
125
|
-
if ivar_value.validatable?
|
126
|
-
valid = valid && recursive_valid?(ivar_value, context, valid)
|
127
|
-
elsif ivar_value.respond_to?(:each)
|
128
|
-
ivar_value.each do |item|
|
129
|
-
if item.validatable?
|
130
|
-
valid = valid && recursive_valid?(item, context, valid)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
return valid && target.valid?
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
def validation_property_value(name)
|
140
|
-
self.respond_to?(name, true) ? self.send(name) : nil
|
141
|
-
end
|
142
|
-
|
143
|
-
# Get the corresponding Object property, if it exists.
|
144
|
-
def validation_property(field_name)
|
145
|
-
properties.find{|p| p.name == field_name}
|
146
|
-
end
|
147
|
-
|
148
|
-
module ClassMethods
|
149
|
-
include CouchRest::Validation::ValidatesPresent
|
150
|
-
include CouchRest::Validation::ValidatesAbsent
|
151
|
-
include CouchRest::Validation::ValidatesIsConfirmed
|
152
|
-
# include CouchRest::Validation::ValidatesIsPrimitive
|
153
|
-
# include CouchRest::Validation::ValidatesIsAccepted
|
154
|
-
include CouchRest::Validation::ValidatesFormat
|
155
|
-
include CouchRest::Validation::ValidatesLength
|
156
|
-
# include CouchRest::Validation::ValidatesWithin
|
157
|
-
include CouchRest::Validation::ValidatesIsNumber
|
158
|
-
include CouchRest::Validation::ValidatesWithMethod
|
159
|
-
# include CouchRest::Validation::ValidatesWithBlock
|
160
|
-
# include CouchRest::Validation::ValidatesIsUnique
|
161
|
-
include CouchRest::Validation::AutoValidate
|
162
|
-
|
163
|
-
# Return the set of contextual validators or create a new one
|
164
|
-
#
|
165
|
-
def validators
|
166
|
-
@validations ||= ContextualValidators.new
|
167
|
-
end
|
168
|
-
|
169
|
-
# Clean up the argument list and return a opts hash, including the
|
170
|
-
# merging of any default opts. Set the context to default if none is
|
171
|
-
# provided. Also allow :context to be aliased to :on, :when & group
|
172
|
-
#
|
173
|
-
def opts_from_validator_args(args, defaults = nil)
|
174
|
-
opts = args.last.kind_of?(Hash) ? args.pop : {}
|
175
|
-
context = :default
|
176
|
-
context = opts[:context] if opts.has_key?(:context)
|
177
|
-
context = opts.delete(:on) if opts.has_key?(:on)
|
178
|
-
context = opts.delete(:when) if opts.has_key?(:when)
|
179
|
-
context = opts.delete(:group) if opts.has_key?(:group)
|
180
|
-
opts[:context] = context
|
181
|
-
opts.merge!(defaults) unless defaults.nil?
|
182
|
-
opts
|
183
|
-
end
|
184
|
-
|
185
|
-
# Given a new context create an instance method of
|
186
|
-
# valid_for_<context>? which simply calls valid?(context)
|
187
|
-
# if it does not already exist
|
188
|
-
#
|
189
|
-
def create_context_instance_methods(context)
|
190
|
-
name = "valid_for_#{context.to_s}?" # valid_for_signup?
|
191
|
-
if !self.instance_methods.include?(name)
|
192
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
193
|
-
def #{name} # def valid_for_signup?
|
194
|
-
valid?('#{context.to_s}'.to_sym) # valid?('signup'.to_sym)
|
195
|
-
end # end
|
196
|
-
EOS
|
197
|
-
end
|
198
|
-
|
199
|
-
all = "all_valid_for_#{context.to_s}?" # all_valid_for_signup?
|
200
|
-
if !self.instance_methods.include?(all)
|
201
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
202
|
-
def #{all} # def all_valid_for_signup?
|
203
|
-
all_valid?('#{context.to_s}'.to_sym) # all_valid?('signup'.to_sym)
|
204
|
-
end # end
|
205
|
-
EOS
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Create a new validator of the given klazz and push it onto the
|
210
|
-
# requested context for each of the attributes in the fields list
|
211
|
-
#
|
212
|
-
def add_validator_to_context(opts, fields, klazz)
|
213
|
-
fields.each do |field|
|
214
|
-
validator = klazz.new(field.to_sym, opts)
|
215
|
-
if opts[:context].is_a?(Symbol)
|
216
|
-
unless validators.context(opts[:context]).include?(validator)
|
217
|
-
validators.context(opts[:context]) << validator
|
218
|
-
create_context_instance_methods(opts[:context])
|
219
|
-
end
|
220
|
-
elsif opts[:context].is_a?(Array)
|
221
|
-
opts[:context].each do |c|
|
222
|
-
unless validators.context(c).include?(validator)
|
223
|
-
validators.context(c) << validator
|
224
|
-
create_context_instance_methods(c)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
end # module ClassMethods
|
232
|
-
end # module Validation
|
233
|
-
|
234
|
-
end # module CouchRest
|
@@ -1,168 +0,0 @@
|
|
1
|
-
module CouchRest
|
2
|
-
module Mixins
|
3
|
-
module Views
|
4
|
-
|
5
|
-
def self.included(base)
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
base.send(:class_inheritable_accessor, :design_doc)
|
8
|
-
base.send(:class_inheritable_accessor, :design_doc_slug_cache)
|
9
|
-
base.send(:class_inheritable_accessor, :design_doc_fresh)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
|
14
|
-
# Define a CouchDB view. The name of the view will be the concatenation
|
15
|
-
# of <tt>by</tt> and the keys joined by <tt>_and_</tt>
|
16
|
-
#
|
17
|
-
# ==== Example views:
|
18
|
-
#
|
19
|
-
# class Post
|
20
|
-
# # view with default options
|
21
|
-
# # query with Post.by_date
|
22
|
-
# view_by :date, :descending => true
|
23
|
-
#
|
24
|
-
# # view with compound sort-keys
|
25
|
-
# # query with Post.by_user_id_and_date
|
26
|
-
# view_by :user_id, :date
|
27
|
-
#
|
28
|
-
# # view with custom map/reduce functions
|
29
|
-
# # query with Post.by_tags :reduce => true
|
30
|
-
# view_by :tags,
|
31
|
-
# :map =>
|
32
|
-
# "function(doc) {
|
33
|
-
# if (doc['couchrest-type'] == 'Post' && doc.tags) {
|
34
|
-
# doc.tags.forEach(function(tag){
|
35
|
-
# emit(doc.tag, 1);
|
36
|
-
# });
|
37
|
-
# }
|
38
|
-
# }",
|
39
|
-
# :reduce =>
|
40
|
-
# "function(keys, values, rereduce) {
|
41
|
-
# return sum(values);
|
42
|
-
# }"
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# <tt>view_by :date</tt> will create a view defined by this Javascript
|
46
|
-
# function:
|
47
|
-
#
|
48
|
-
# function(doc) {
|
49
|
-
# if (doc['couchrest-type'] == 'Post' && doc.date) {
|
50
|
-
# emit(doc.date, null);
|
51
|
-
# }
|
52
|
-
# }
|
53
|
-
#
|
54
|
-
# It can be queried by calling <tt>Post.by_date</tt> which accepts all
|
55
|
-
# valid options for CouchRest::Database#view. In addition, calling with
|
56
|
-
# the <tt>:raw => true</tt> option will return the view rows
|
57
|
-
# themselves. By default <tt>Post.by_date</tt> will return the
|
58
|
-
# documents included in the generated view.
|
59
|
-
#
|
60
|
-
# CouchRest::Database#view options can be applied at view definition
|
61
|
-
# time as defaults, and they will be curried and used at view query
|
62
|
-
# time. Or they can be overridden at query time.
|
63
|
-
#
|
64
|
-
# Custom views can be queried with <tt>:reduce => true</tt> to return
|
65
|
-
# reduce results. The default for custom views is to query with
|
66
|
-
# <tt>:reduce => false</tt>.
|
67
|
-
#
|
68
|
-
# Views are generated (on a per-model basis) lazily on first-access.
|
69
|
-
# This means that if you are deploying changes to a view, the views for
|
70
|
-
# that model won't be available until generation is complete. This can
|
71
|
-
# take some time with large databases. Strategies are in the works.
|
72
|
-
#
|
73
|
-
# To understand the capabilities of this view system more compeletly,
|
74
|
-
# it is recommended that you read the RSpec file at
|
75
|
-
# <tt>spec/core/model_spec.rb</tt>.
|
76
|
-
|
77
|
-
def view_by(*keys)
|
78
|
-
self.design_doc ||= Design.new(default_design_doc)
|
79
|
-
opts = keys.pop if keys.last.is_a?(Hash)
|
80
|
-
opts ||= {}
|
81
|
-
ducktype = opts.delete(:ducktype)
|
82
|
-
unless ducktype || opts[:map]
|
83
|
-
opts[:guards] ||= []
|
84
|
-
opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')"
|
85
|
-
end
|
86
|
-
keys.push opts
|
87
|
-
self.design_doc.view_by(*keys)
|
88
|
-
self.design_doc_fresh = false
|
89
|
-
end
|
90
|
-
|
91
|
-
# returns stored defaults if the there is a view named this in the design doc
|
92
|
-
def has_view?(view)
|
93
|
-
view = view.to_s
|
94
|
-
design_doc && design_doc['views'] && design_doc['views'][view]
|
95
|
-
end
|
96
|
-
|
97
|
-
# Dispatches to any named view.
|
98
|
-
def view(name, query={}, &block)
|
99
|
-
unless design_doc_fresh
|
100
|
-
refresh_design_doc
|
101
|
-
end
|
102
|
-
query[:raw] = true if query[:reduce]
|
103
|
-
raw = query.delete(:raw)
|
104
|
-
fetch_view_with_docs(name, query, raw, &block)
|
105
|
-
end
|
106
|
-
|
107
|
-
def all_design_doc_versions
|
108
|
-
database.documents :startkey => "_design/#{self.to_s}-",
|
109
|
-
:endkey => "_design/#{self.to_s}-\u9999"
|
110
|
-
end
|
111
|
-
|
112
|
-
# Deletes any non-current design docs that were created by this class.
|
113
|
-
# Running this when you're deployed version of your application is steadily
|
114
|
-
# and consistently using the latest code, is the way to clear out old design
|
115
|
-
# docs. Running it to early could mean that live code has to regenerate
|
116
|
-
# potentially large indexes.
|
117
|
-
def cleanup_design_docs!
|
118
|
-
ddocs = all_design_doc_versions
|
119
|
-
ddocs["rows"].each do |row|
|
120
|
-
if (row['id'] != design_doc_id)
|
121
|
-
database.delete_doc({
|
122
|
-
"_id" => row['id'],
|
123
|
-
"_rev" => row['value']['rev']
|
124
|
-
})
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
|
-
def fetch_view_with_docs(name, opts, raw=false, &block)
|
132
|
-
if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false)
|
133
|
-
fetch_view(name, opts, &block)
|
134
|
-
else
|
135
|
-
begin
|
136
|
-
view = fetch_view name, opts.merge({:include_docs => true}), &block
|
137
|
-
view['rows'].collect{|r|new(r['doc'])} if view['rows']
|
138
|
-
rescue
|
139
|
-
# fallback for old versions of couchdb that don't
|
140
|
-
# have include_docs support
|
141
|
-
view = fetch_view(name, opts, &block)
|
142
|
-
view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def fetch_view view_name, opts, &block
|
148
|
-
retryable = true
|
149
|
-
begin
|
150
|
-
design_doc.view(view_name, opts, &block)
|
151
|
-
# the design doc could have been deleted by a rouge process
|
152
|
-
rescue RestClient::ResourceNotFound => e
|
153
|
-
if retryable
|
154
|
-
refresh_design_doc
|
155
|
-
retryable = false
|
156
|
-
retry
|
157
|
-
else
|
158
|
-
raise e
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
end # module ClassMethods
|
164
|
-
|
165
|
-
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
data/lib/couchrest/mixins.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '..', 'mixins', 'properties')
|
2
|
-
|
3
|
-
module CouchRest
|
4
|
-
module CastedModel
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.send(:include, CouchRest::Mixins::Properties)
|
8
|
-
base.send(:attr_accessor, :casted_by)
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(keys={})
|
12
|
-
super
|
13
|
-
keys.each do |k,v|
|
14
|
-
self[k.to_s] = v
|
15
|
-
end if keys
|
16
|
-
apply_defaults # defined in CouchRest::Mixins::Properties
|
17
|
-
# cast_keys # defined in CouchRest::Mixins::Properties
|
18
|
-
end
|
19
|
-
|
20
|
-
def []= key, value
|
21
|
-
super(key.to_s, value)
|
22
|
-
end
|
23
|
-
|
24
|
-
def [] key
|
25
|
-
super(key.to_s)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|