jchris-couchrest 0.17.0 → 0.22
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/lib/couchrest.rb +13 -5
- data/lib/couchrest/core/database.rb +0 -20
- data/lib/couchrest/core/document.rb +2 -2
- data/lib/couchrest/mixins/callbacks.rb +3 -3
- data/lib/couchrest/mixins/properties.rb +9 -5
- data/lib/couchrest/mixins/validation.rb +8 -3
- data/lib/couchrest/mixins/views.rb +3 -3
- data/lib/couchrest/monkeypatches.rb +1 -1
- data/lib/couchrest/more/extended_document.rb +5 -8
- data/lib/couchrest/support/class.rb +136 -151
- data/lib/couchrest/validation/validation_errors.rb +1 -1
- data/lib/couchrest/validation/validators/format_validator.rb +1 -1
- data/lib/couchrest/validation/validators/length_validator.rb +1 -1
- data/spec/couchrest/core/database_spec.rb +0 -44
- data/spec/couchrest/core/document_spec.rb +0 -45
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +54 -0
- metadata +22 -3
- data/spec/couchrest/support/class_spec.rb +0 -59
data/lib/couchrest.rb
CHANGED
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
|
|
28
28
|
|
29
29
|
# = CouchDB, close to the metal
|
30
30
|
module CouchRest
|
31
|
-
VERSION = '0.
|
31
|
+
VERSION = '0.22' unless self.const_defined?("VERSION")
|
32
32
|
|
33
33
|
autoload :Server, 'couchrest/core/server'
|
34
34
|
autoload :Database, 'couchrest/core/database'
|
@@ -69,6 +69,18 @@ module CouchRest
|
|
69
69
|
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
70
70
|
end
|
71
71
|
|
72
|
+
# extracted from Extlib
|
73
|
+
#
|
74
|
+
# Capitalizes the first word and turns underscores into spaces and strips _id.
|
75
|
+
# Like titleize, this is meant for creating pretty output.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# "employee_salary" #=> "Employee salary"
|
79
|
+
# "author_id" #=> "Author"
|
80
|
+
def humanize(lower_case_and_underscored_word)
|
81
|
+
lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
82
|
+
end
|
83
|
+
|
72
84
|
# todo, make this parse the url and instantiate a Server or Database instance
|
73
85
|
# depending on the specificity.
|
74
86
|
def new(*opts)
|
@@ -171,10 +183,6 @@ module CouchRest
|
|
171
183
|
def copy uri, destination
|
172
184
|
JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
|
173
185
|
end
|
174
|
-
|
175
|
-
def move uri, destination
|
176
|
-
JSON.parse(RestClient.move(uri, {'Destination' => destination}))
|
177
|
-
end
|
178
186
|
|
179
187
|
def paramify_url url, params = {}
|
180
188
|
if params && !params.empty?
|
@@ -224,26 +224,6 @@ module CouchRest
|
|
224
224
|
copy_doc(doc, dest)
|
225
225
|
end
|
226
226
|
|
227
|
-
# MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided.
|
228
|
-
# <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
|
229
|
-
# hash with a '_rev' key
|
230
|
-
def move_doc(doc, dest)
|
231
|
-
raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev']
|
232
|
-
slug = escape_docid(doc['_id'])
|
233
|
-
destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
|
234
|
-
"#{dest['_id']}?rev=#{dest['_rev']}"
|
235
|
-
else
|
236
|
-
dest
|
237
|
-
end
|
238
|
-
CouchRest.move "#{@uri}/#{slug}?rev=#{doc['_rev']}", destination
|
239
|
-
end
|
240
|
-
|
241
|
-
### DEPRECATION NOTICE
|
242
|
-
def move(doc, dest)
|
243
|
-
puts "CouchRest::Database's move method is being deprecated, please use move_doc instead"
|
244
|
-
move_doc(doc, dest)
|
245
|
-
end
|
246
|
-
|
247
227
|
# Compact the database, removing old document revisions and optimizing space use.
|
248
228
|
def compact!
|
249
229
|
CouchRest.post "#{@uri}/_compact"
|
@@ -5,10 +5,10 @@ module CouchRest
|
|
5
5
|
include CouchRest::Mixins::Attachments
|
6
6
|
|
7
7
|
# def self.inherited(subklass)
|
8
|
-
# subklass.send(:
|
8
|
+
# subklass.send(:extlib_inheritable_accessor, :database)
|
9
9
|
# end
|
10
10
|
|
11
|
-
|
11
|
+
extlib_inheritable_accessor :database
|
12
12
|
attr_accessor :database
|
13
13
|
|
14
14
|
# override the CouchRest::Model-wide default_database
|
@@ -426,10 +426,10 @@ module CouchRest
|
|
426
426
|
def define_callbacks(*symbols)
|
427
427
|
terminator = symbols.pop if symbols.last.is_a?(String)
|
428
428
|
symbols.each do |symbol|
|
429
|
-
self.
|
429
|
+
self.extlib_inheritable_accessor("_#{symbol}_terminator")
|
430
430
|
self.send("_#{symbol}_terminator=", terminator)
|
431
431
|
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
432
|
-
|
432
|
+
extlib_inheritable_accessor :_#{symbol}_callbacks
|
433
433
|
self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
|
434
434
|
|
435
435
|
def self.#{symbol}_callback(*filters, &blk)
|
@@ -480,4 +480,4 @@ module CouchRest
|
|
480
480
|
end
|
481
481
|
end
|
482
482
|
end
|
483
|
-
end
|
483
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'time'
|
1
2
|
require File.join(File.dirname(__FILE__), '..', 'more', 'property')
|
2
3
|
|
3
4
|
module CouchRest
|
@@ -7,10 +8,10 @@ module CouchRest
|
|
7
8
|
class IncludeError < StandardError; end
|
8
9
|
|
9
10
|
def self.included(base)
|
10
|
-
base.
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
base.class_eval <<-EOS, __FILE__, __LINE__
|
12
|
+
extlib_inheritable_accessor(:properties)
|
13
|
+
self.properties ||= []
|
14
|
+
EOS
|
14
15
|
base.extend(ClassMethods)
|
15
16
|
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
17
|
end
|
@@ -70,7 +71,10 @@ module CouchRest
|
|
70
71
|
module ClassMethods
|
71
72
|
|
72
73
|
def property(name, options={})
|
73
|
-
|
74
|
+
existing_property = self.properties.find{|p| p.name == name.to_s}
|
75
|
+
if existing_property.nil? || (existing_property.default != options[:default])
|
76
|
+
define_property(name, options)
|
77
|
+
end
|
74
78
|
end
|
75
79
|
|
76
80
|
protected
|
@@ -49,10 +49,10 @@ module CouchRest
|
|
49
49
|
module Validation
|
50
50
|
|
51
51
|
def self.included(base)
|
52
|
-
base.
|
52
|
+
base.extlib_inheritable_accessor(:auto_validation)
|
53
53
|
base.class_eval <<-EOS, __FILE__, __LINE__
|
54
54
|
# Turn off auto validation by default
|
55
|
-
|
55
|
+
self.auto_validation ||= false
|
56
56
|
|
57
57
|
# Force the auto validation for the class properties
|
58
58
|
# This feature is still not fully ported over,
|
@@ -60,6 +60,11 @@ module CouchRest
|
|
60
60
|
def self.auto_validate!
|
61
61
|
self.auto_validation = true
|
62
62
|
end
|
63
|
+
|
64
|
+
# share the validations with subclasses
|
65
|
+
def self.inherited(subklass)
|
66
|
+
subklass.instance_variable_set(:@validations, self.validators.dup)
|
67
|
+
end
|
63
68
|
EOS
|
64
69
|
|
65
70
|
base.extend(ClassMethods)
|
@@ -71,7 +76,7 @@ module CouchRest
|
|
71
76
|
base.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
72
77
|
def self.define_property(name, options={})
|
73
78
|
super
|
74
|
-
auto_generate_validations(properties.last)
|
79
|
+
auto_generate_validations(properties.last) if properties && properties.size > 0
|
75
80
|
autovalidation_check = true
|
76
81
|
end
|
77
82
|
RUBY_EVAL
|
@@ -4,9 +4,9 @@ module CouchRest
|
|
4
4
|
|
5
5
|
def self.included(base)
|
6
6
|
base.extend(ClassMethods)
|
7
|
-
base.send(:
|
8
|
-
base.send(:
|
9
|
-
base.send(:
|
7
|
+
base.send(:extlib_inheritable_accessor, :design_doc)
|
8
|
+
base.send(:extlib_inheritable_accessor, :design_doc_slug_cache)
|
9
|
+
base.send(:extlib_inheritable_accessor, :design_doc_fresh)
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
@@ -1,11 +1,3 @@
|
|
1
|
-
begin
|
2
|
-
# still required for Time parsing and pluralization in the validation
|
3
|
-
require 'extlib'
|
4
|
-
rescue
|
5
|
-
puts "CouchRest::ExtendedDocument still requires extlib (not for much longer). This is left out of the gemspec on purpose."
|
6
|
-
raise
|
7
|
-
end
|
8
|
-
|
9
1
|
require 'mime/types'
|
10
2
|
require File.join(File.dirname(__FILE__), "property")
|
11
3
|
require File.join(File.dirname(__FILE__), '..', 'mixins', 'extended_document_mixins')
|
@@ -22,6 +14,11 @@ module CouchRest
|
|
22
14
|
|
23
15
|
def self.inherited(subklass)
|
24
16
|
subklass.send(:include, CouchRest::Mixins::Properties)
|
17
|
+
subklass.class_eval <<-EOS, __FILE__, __LINE__
|
18
|
+
def self.inherited(subklass)
|
19
|
+
subklass.properties = self.properties.dup
|
20
|
+
end
|
21
|
+
EOS
|
25
22
|
end
|
26
23
|
|
27
24
|
# Accessors
|
@@ -25,167 +25,152 @@
|
|
25
25
|
# example, an array without those additions being shared with either their
|
26
26
|
# parent, siblings, or children, which is unlike the regular class-level
|
27
27
|
# attributes that are shared across the entire hierarchy.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# @return <Array[#to_s]> List of attributes that were made into cattr_readers
|
45
|
-
#
|
46
|
-
# @api public
|
47
|
-
#
|
48
|
-
# @todo Is this inconsistent in that it does not allow you to prevent
|
49
|
-
# an instance_reader via :instance_reader => false
|
50
|
-
def cattr_reader(*syms)
|
51
|
-
syms.flatten.each do |sym|
|
52
|
-
next if sym.is_a?(Hash)
|
53
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
54
|
-
unless defined? @@#{sym}
|
55
|
-
@@#{sym} = nil
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.#{sym}
|
59
|
-
@@#{sym}
|
60
|
-
end
|
61
|
-
|
62
|
-
def #{sym}
|
63
|
-
@@#{sym}
|
64
|
-
end
|
65
|
-
RUBY
|
28
|
+
class Class
|
29
|
+
# Defines class-level and instance-level attribute reader.
|
30
|
+
#
|
31
|
+
# @param *syms<Array> Array of attributes to define reader for.
|
32
|
+
# @return <Array[#to_s]> List of attributes that were made into cattr_readers
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
#
|
36
|
+
# @todo Is this inconsistent in that it does not allow you to prevent
|
37
|
+
# an instance_reader via :instance_reader => false
|
38
|
+
def cattr_reader(*syms)
|
39
|
+
syms.flatten.each do |sym|
|
40
|
+
next if sym.is_a?(Hash)
|
41
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
42
|
+
unless defined? @@#{sym}
|
43
|
+
@@#{sym} = nil
|
66
44
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def cattr_writer(*syms)
|
77
|
-
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
78
|
-
syms.flatten.each do |sym|
|
79
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
80
|
-
unless defined? @@#{sym}
|
81
|
-
@@#{sym} = nil
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.#{sym}=(obj)
|
85
|
-
@@#{sym} = obj
|
86
|
-
end
|
87
|
-
RUBY
|
88
|
-
|
89
|
-
unless options[:instance_writer] == false
|
90
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
91
|
-
def #{sym}=(obj)
|
92
|
-
@@#{sym} = obj
|
45
|
+
|
46
|
+
def self.#{sym}
|
47
|
+
@@#{sym}
|
48
|
+
end
|
49
|
+
|
50
|
+
def #{sym}
|
51
|
+
@@#{sym}
|
52
|
+
end
|
53
|
+
RUBY
|
93
54
|
end
|
94
|
-
|
95
|
-
|
55
|
+
end unless Class.respond_to?(:cattr_reader)
|
56
|
+
|
57
|
+
# Defines class-level (and optionally instance-level) attribute writer.
|
58
|
+
#
|
59
|
+
# @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
|
60
|
+
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
61
|
+
# @return <Array[#to_s]> List of attributes that were made into cattr_writers
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
def cattr_writer(*syms)
|
65
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
66
|
+
syms.flatten.each do |sym|
|
67
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
68
|
+
unless defined? @@#{sym}
|
69
|
+
@@#{sym} = nil
|
96
70
|
end
|
71
|
+
|
72
|
+
def self.#{sym}=(obj)
|
73
|
+
@@#{sym} = obj
|
74
|
+
end
|
75
|
+
RUBY
|
76
|
+
|
77
|
+
unless options[:instance_writer] == false
|
78
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
79
|
+
def #{sym}=(obj)
|
80
|
+
@@#{sym} = obj
|
81
|
+
end
|
82
|
+
RUBY
|
97
83
|
end
|
98
|
-
|
99
|
-
# Defines class-level (and optionally instance-level) attribute accessor.
|
100
|
-
#
|
101
|
-
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
|
102
|
-
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
103
|
-
# @return <Array[#to_s]> List of attributes that were made into accessors
|
104
|
-
#
|
105
|
-
# @api public
|
106
|
-
def cattr_accessor(*syms)
|
107
|
-
cattr_reader(*syms)
|
108
|
-
cattr_writer(*syms)
|
109
|
-
end
|
110
|
-
|
111
|
-
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
112
|
-
# each subclass has a copy of parent's attribute.
|
113
|
-
#
|
114
|
-
# @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
|
115
|
-
# @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
|
116
|
-
#
|
117
|
-
# @api public
|
118
|
-
#
|
119
|
-
# @todo Do we want to block instance_reader via :instance_reader => false
|
120
|
-
# @todo It would be preferable that we do something with a Hash passed in
|
121
|
-
# (error out or do the same as other methods above) instead of silently
|
122
|
-
# moving on). In particular, this makes the return value of this function
|
123
|
-
# less useful.
|
124
|
-
def class_inheritable_reader(*ivars)
|
125
|
-
instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
|
126
|
-
|
127
|
-
ivars.each do |ivar|
|
128
|
-
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
129
|
-
def self.#{ivar}
|
130
|
-
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
131
|
-
ivar = superclass.#{ivar}
|
132
|
-
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
133
|
-
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) && !ivar.is_a?(Symbol) ? ivar.dup : ivar
|
134
84
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
85
|
+
end unless Class.respond_to?(:cattr_writer)
|
86
|
+
|
87
|
+
# Defines class-level (and optionally instance-level) attribute accessor.
|
88
|
+
#
|
89
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
|
90
|
+
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
91
|
+
# @return <Array[#to_s]> List of attributes that were made into accessors
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
def cattr_accessor(*syms)
|
95
|
+
cattr_reader(*syms)
|
96
|
+
cattr_writer(*syms)
|
97
|
+
end unless Class.respond_to?(:cattr_accessor)
|
98
|
+
|
99
|
+
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
100
|
+
# each subclass has a copy of parent's attribute.
|
101
|
+
#
|
102
|
+
# @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
|
103
|
+
# @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
#
|
107
|
+
# @todo Do we want to block instance_reader via :instance_reader => false
|
108
|
+
# @todo It would be preferable that we do something with a Hash passed in
|
109
|
+
# (error out or do the same as other methods above) instead of silently
|
110
|
+
# moving on). In particular, this makes the return value of this function
|
111
|
+
# less useful.
|
112
|
+
def extlib_inheritable_reader(*ivars)
|
113
|
+
instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
|
114
|
+
|
115
|
+
ivars.each do |ivar|
|
116
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
117
|
+
def self.#{ivar}
|
118
|
+
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
119
|
+
ivar = superclass.#{ivar}
|
120
|
+
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
121
|
+
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
|
143
122
|
end
|
123
|
+
RUBY
|
124
|
+
unless instance_reader == false
|
125
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
126
|
+
def #{ivar}
|
127
|
+
self.class.#{ivar}
|
128
|
+
end
|
129
|
+
RUBY
|
144
130
|
end
|
145
|
-
|
146
|
-
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
147
|
-
# each subclass has a copy of parent's attribute.
|
148
|
-
#
|
149
|
-
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
150
|
-
# define inheritable writer for.
|
151
|
-
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
152
|
-
# @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
|
153
|
-
#
|
154
|
-
# @api public
|
155
|
-
#
|
156
|
-
# @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
|
157
|
-
# class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
|
158
|
-
def class_inheritable_writer(*ivars)
|
159
|
-
instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
|
160
|
-
ivars.each do |ivar|
|
161
|
-
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
162
|
-
def self.#{ivar}=(obj)
|
163
|
-
@#{ivar} = obj
|
164
131
|
end
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
132
|
+
end unless Class.respond_to?(:extlib_inheritable_reader)
|
133
|
+
|
134
|
+
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
135
|
+
# each subclass has a copy of parent's attribute.
|
136
|
+
#
|
137
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
138
|
+
# define inheritable writer for.
|
139
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
140
|
+
# @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
|
141
|
+
#
|
142
|
+
# @api public
|
143
|
+
#
|
144
|
+
# @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
|
145
|
+
# class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
|
146
|
+
def extlib_inheritable_writer(*ivars)
|
147
|
+
instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
|
148
|
+
ivars.each do |ivar|
|
149
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
150
|
+
def self.#{ivar}=(obj)
|
151
|
+
@#{ivar} = obj
|
171
152
|
end
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
178
|
-
# define inheritable accessor for.
|
179
|
-
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
180
|
-
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
181
|
-
#
|
182
|
-
# @api public
|
183
|
-
def class_inheritable_accessor(*syms)
|
184
|
-
class_inheritable_reader(*syms)
|
185
|
-
class_inheritable_writer(*syms)
|
153
|
+
RUBY
|
154
|
+
unless instance_writer == false
|
155
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
156
|
+
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
157
|
+
RUBY
|
186
158
|
end
|
187
159
|
end
|
188
|
-
end
|
160
|
+
end unless Class.respond_to?(:extlib_inheritable_writer)
|
161
|
+
|
162
|
+
# Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
|
163
|
+
# each subclass has a copy of parent's attribute.
|
164
|
+
#
|
165
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
166
|
+
# define inheritable accessor for.
|
167
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
168
|
+
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
169
|
+
#
|
170
|
+
# @api public
|
171
|
+
def extlib_inheritable_accessor(*syms)
|
172
|
+
extlib_inheritable_reader(*syms)
|
173
|
+
extlib_inheritable_writer(*syms)
|
174
|
+
end unless Class.respond_to?(:extlib_inheritable_accessor)
|
189
175
|
end
|
190
176
|
|
191
|
-
Class.send(:include, CouchRest::ClassExtension)
|
@@ -60,7 +60,7 @@ module CouchRest
|
|
60
60
|
cattr_writer :default_error_messages
|
61
61
|
|
62
62
|
def self.default_error_message(key, field, *values)
|
63
|
-
field =
|
63
|
+
field = CouchRest.humanize(field)
|
64
64
|
@@default_error_messages[key] % [field, *values].flatten
|
65
65
|
end
|
66
66
|
|
@@ -64,7 +64,7 @@ module CouchRest
|
|
64
64
|
|
65
65
|
error_message = @options[:message] || ValidationErrors.default_error_message(:invalid, field_name)
|
66
66
|
|
67
|
-
field =
|
67
|
+
field = CouchRest.humanize(field_name)
|
68
68
|
error_message = error_message.call(field, value) if error_message.respond_to?(:call)
|
69
69
|
|
70
70
|
add_error(target, error_message, field_name)
|
@@ -54,7 +54,7 @@ module CouchRest
|
|
54
54
|
|
55
55
|
# XXX: HACK seems hacky to do this on every validation, probably should
|
56
56
|
# do this elsewhere?
|
57
|
-
field =
|
57
|
+
field = CouchRest.humanize(field_name)
|
58
58
|
min = @range ? @range.min : @min
|
59
59
|
max = @range ? @range.max : @max
|
60
60
|
equal = @equal
|
@@ -554,50 +554,6 @@ describe CouchRest::Database do
|
|
554
554
|
end
|
555
555
|
end
|
556
556
|
|
557
|
-
describe "MOVE existing document" do
|
558
|
-
before :each do
|
559
|
-
@r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'})
|
560
|
-
@docid = 'tracks/zappa/muffin-man'
|
561
|
-
@doc = @db.get(@r['id'])
|
562
|
-
end
|
563
|
-
describe "to a new location" do
|
564
|
-
it "should work" do
|
565
|
-
@db.move_doc @doc, @docid
|
566
|
-
newdoc = @db.get(@docid)
|
567
|
-
newdoc['artist'].should == 'Zappa'
|
568
|
-
lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
|
569
|
-
end
|
570
|
-
it "should fail without an _id or _rev" do
|
571
|
-
lambda{@db.move({"not"=>"a real doc"})}.should raise_error(ArgumentError)
|
572
|
-
lambda{@db.move({"_id"=>"not a real doc"})}.should raise_error(ArgumentError)
|
573
|
-
end
|
574
|
-
end
|
575
|
-
describe "to an existing location" do
|
576
|
-
before :each do
|
577
|
-
@db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
|
578
|
-
end
|
579
|
-
it "should fail without a rev" do
|
580
|
-
@doc.delete("_rev")
|
581
|
-
lambda{@db.move_doc @doc, @docid}.should raise_error(ArgumentError)
|
582
|
-
lambda{@db.get(@r['id'])}.should_not raise_error
|
583
|
-
end
|
584
|
-
it "should succeed with a rev" do
|
585
|
-
@to_be_overwritten = @db.get(@docid)
|
586
|
-
@db.move_doc @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
|
587
|
-
newdoc = @db.get(@docid)
|
588
|
-
newdoc['artist'].should == 'Zappa'
|
589
|
-
lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
|
590
|
-
end
|
591
|
-
it "should succeed given the doc to overwrite" do
|
592
|
-
@to_be_overwritten = @db.get(@docid)
|
593
|
-
@db.move_doc @doc, @to_be_overwritten
|
594
|
-
newdoc = @db.get(@docid)
|
595
|
-
newdoc['artist'].should == 'Zappa'
|
596
|
-
lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
|
597
|
-
end
|
598
|
-
end
|
599
|
-
end
|
600
|
-
|
601
557
|
|
602
558
|
it "should list documents" do
|
603
559
|
5.times do
|
@@ -199,51 +199,6 @@ describe CouchRest::Document do
|
|
199
199
|
end
|
200
200
|
end
|
201
201
|
end
|
202
|
-
|
203
|
-
describe "MOVE existing document" do
|
204
|
-
before :each do
|
205
|
-
@db = reset_test_db!
|
206
|
-
@resp = @db.save_doc({'key' => 'value'})
|
207
|
-
@docid = 'new-location'
|
208
|
-
@doc = @db.get(@resp['id'])
|
209
|
-
end
|
210
|
-
describe "to a new location" do
|
211
|
-
it "should work" do
|
212
|
-
@doc.move @docid
|
213
|
-
newdoc = @db.get(@docid)
|
214
|
-
newdoc['key'].should == 'value'
|
215
|
-
lambda {@db.get(@resp['id'])}.should raise_error(RestClient::ResourceNotFound)
|
216
|
-
end
|
217
|
-
it "should fail without a database" do
|
218
|
-
lambda{CouchRest::Document.new({"not"=>"a real doc"}).move}.should raise_error(ArgumentError)
|
219
|
-
lambda{CouchRest::Document.new({"_id"=>"not a real doc"}).move}.should raise_error(ArgumentError)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
describe "to an existing location" do
|
223
|
-
before :each do
|
224
|
-
@db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
|
225
|
-
end
|
226
|
-
it "should fail without a rev" do
|
227
|
-
@doc.delete("_rev")
|
228
|
-
lambda{@doc.move @docid}.should raise_error(ArgumentError)
|
229
|
-
lambda{@db.get(@resp['id'])}.should_not raise_error
|
230
|
-
end
|
231
|
-
it "should succeed with a rev" do
|
232
|
-
@to_be_overwritten = @db.get(@docid)
|
233
|
-
@doc.move "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
|
234
|
-
newdoc = @db.get(@docid)
|
235
|
-
newdoc['key'].should == 'value'
|
236
|
-
lambda {@db.get(@resp['id'])}.should raise_error(RestClient::ResourceNotFound)
|
237
|
-
end
|
238
|
-
it "should succeed given the doc to overwrite" do
|
239
|
-
@to_be_overwritten = @db.get(@docid)
|
240
|
-
@doc.move @to_be_overwritten
|
241
|
-
newdoc = @db.get(@docid)
|
242
|
-
newdoc['key'].should == 'value'
|
243
|
-
lambda {@db.get(@resp['id'])}.should raise_error(RestClient::ResourceNotFound)
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
202
|
end
|
248
203
|
|
249
204
|
describe "dealing with attachments" do
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require File.join(FIXTURE_PATH, 'more', 'card')
|
3
|
+
|
4
|
+
# add a default value
|
5
|
+
Card.property :bg_color, :default => '#ccc'
|
6
|
+
|
7
|
+
class BusinessCard < Card
|
8
|
+
property :extension_code
|
9
|
+
property :job_title
|
10
|
+
end
|
11
|
+
|
12
|
+
class DesignBusinessCard < BusinessCard
|
13
|
+
property :bg_color, :default => '#eee'
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
describe "Subclassing an ExtendedDocument" do
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
@card = BusinessCard.new
|
21
|
+
end
|
22
|
+
|
23
|
+
it "shouldn't messup the parent's properties" do
|
24
|
+
Card.properties.should_not == BusinessCard.properties
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should share the same db default" do
|
28
|
+
@card.database.uri.should == Card.database.uri
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should share the same autovalidation details" do
|
32
|
+
@card.auto_validation.should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have kept the validation details" do
|
36
|
+
@card.should_not be_valid
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have added the new validation details" do
|
40
|
+
validated_fields = @card.class.validators.contexts[:default].map{|v| v.field_name}
|
41
|
+
validated_fields.should include(:extension_code)
|
42
|
+
validated_fields.should include(:job_title)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should inherit default property values" do
|
46
|
+
@card.bg_color.should == '#ccc'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to overwrite a default property" do
|
50
|
+
DesignBusinessCard.new.bg_color.should == '#eee'
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jchris-couchrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: "0.22"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Chris Anderson
|
@@ -141,15 +141,34 @@ files:
|
|
141
141
|
- spec/couchrest/more/casted_model_spec.rb
|
142
142
|
- spec/couchrest/more/extended_doc_attachment_spec.rb
|
143
143
|
- spec/couchrest/more/extended_doc_spec.rb
|
144
|
+
- spec/couchrest/more/extended_doc_subclass_spec.rb
|
144
145
|
- spec/couchrest/more/extended_doc_view_spec.rb
|
145
146
|
- spec/couchrest/more/property_spec.rb
|
146
|
-
- spec/couchrest/support
|
147
|
-
- spec/couchrest/support/class_spec.rb
|
148
147
|
- spec/fixtures
|
149
148
|
- spec/fixtures/attachments
|
150
149
|
- spec/fixtures/attachments/couchdb.png
|
151
150
|
- spec/fixtures/attachments/README
|
152
151
|
- spec/fixtures/attachments/test.html
|
152
|
+
- spec/fixtures/couchapp
|
153
|
+
- spec/fixtures/couchapp/_attachments
|
154
|
+
- spec/fixtures/couchapp/_attachments/index.html
|
155
|
+
- spec/fixtures/couchapp/doc.json
|
156
|
+
- spec/fixtures/couchapp/foo
|
157
|
+
- spec/fixtures/couchapp/foo/bar.txt
|
158
|
+
- spec/fixtures/couchapp/foo/test.json
|
159
|
+
- spec/fixtures/couchapp/test.json
|
160
|
+
- spec/fixtures/couchapp/views
|
161
|
+
- spec/fixtures/couchapp/views/example-map.js
|
162
|
+
- spec/fixtures/couchapp/views/example-reduce.js
|
163
|
+
- spec/fixtures/couchapp-test
|
164
|
+
- spec/fixtures/couchapp-test/my-app
|
165
|
+
- spec/fixtures/couchapp-test/my-app/_attachments
|
166
|
+
- spec/fixtures/couchapp-test/my-app/_attachments/index.html
|
167
|
+
- spec/fixtures/couchapp-test/my-app/foo
|
168
|
+
- spec/fixtures/couchapp-test/my-app/foo/bar.txt
|
169
|
+
- spec/fixtures/couchapp-test/my-app/views
|
170
|
+
- spec/fixtures/couchapp-test/my-app/views/example-map.js
|
171
|
+
- spec/fixtures/couchapp-test/my-app/views/example-reduce.js
|
153
172
|
- spec/fixtures/more
|
154
173
|
- spec/fixtures/more/article.rb
|
155
174
|
- spec/fixtures/more/card.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
|
2
|
-
require File.join(File.dirname(__FILE__), '..', '..', '..', 'lib', 'couchrest', 'support', 'class')
|
3
|
-
|
4
|
-
describe CouchRest::ClassExtension do
|
5
|
-
|
6
|
-
before :all do
|
7
|
-
class FullyDefinedClassExtensions
|
8
|
-
def self.respond_to?(method)
|
9
|
-
if CouchRest::ClassExtension::InstanceMethods.instance_methods.include?(method)
|
10
|
-
true
|
11
|
-
else
|
12
|
-
super
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class PartDefinedClassExtensions
|
18
|
-
def self.respond_to?(method)
|
19
|
-
methods = CouchRest::ClassExtension::InstanceMethods.instance_methods
|
20
|
-
methods.delete('cattr_reader')
|
21
|
-
|
22
|
-
if methods.include?(method)
|
23
|
-
false
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class NoClassExtensions
|
31
|
-
def self.respond_to?(method)
|
32
|
-
if CouchRest::ClassExtension::InstanceMethods.instance_methods.include?(method)
|
33
|
-
false
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should not include InstanceMethods if the class extensions are already defined" do
|
44
|
-
FullyDefinedClassExtensions.send(:include, CouchRest::ClassExtension)
|
45
|
-
FullyDefinedClassExtensions.ancestors.should_not include(CouchRest::ClassExtension::InstanceMethods)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should raise RuntimeError if the class extensions are only partially defined" do
|
49
|
-
lambda {
|
50
|
-
PartDefinedClassExtensions.send(:include, CouchRest::ClassExtension)
|
51
|
-
}.should raise_error(RuntimeError)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should include class extensions if they are not already defined" do
|
55
|
-
NoClassExtensions.send(:include, CouchRest::ClassExtension)
|
56
|
-
NoClassExtensions.ancestors.should include(CouchRest::ClassExtension::InstanceMethods)
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|