mattetti-couchrest 0.21 → 0.22

Sign up to get free protection for your applications and to get access to all the features.
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.21' unless self.const_defined?("VERSION")
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'
@@ -40,6 +40,7 @@ module CouchRest
40
40
  autoload :Pager, 'couchrest/helper/pager'
41
41
  autoload :FileManager, 'couchrest/helper/file_manager'
42
42
  autoload :Streamer, 'couchrest/helper/streamer'
43
+ autoload :Upgrade, 'couchrest/helper/upgrade'
43
44
 
44
45
  autoload :ExtendedDocument, 'couchrest/more/extended_document'
45
46
  autoload :CastedModel, 'couchrest/more/casted_model'
@@ -68,6 +69,18 @@ module CouchRest
68
69
  Object.module_eval("::#{$1}", __FILE__, __LINE__)
69
70
  end
70
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
+
71
84
  # todo, make this parse the url and instantiate a Server or Database instance
72
85
  # depending on the specificity.
73
86
  def new(*opts)
@@ -170,10 +183,6 @@ module CouchRest
170
183
  def copy uri, destination
171
184
  JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
172
185
  end
173
-
174
- def move uri, destination
175
- JSON.parse(RestClient.move(uri, {'Destination' => destination}))
176
- end
177
186
 
178
187
  def paramify_url url, params = {}
179
188
  if params && !params.empty?
@@ -77,13 +77,15 @@ module CouchRest
77
77
  end
78
78
 
79
79
  # GET a document from CouchDB, by id. Returns a Ruby Hash.
80
- def get(id)
80
+ def get(id, params = {})
81
81
  slug = escape_docid(id)
82
- hash = CouchRest.get("#{@uri}/#{slug}")
83
- doc = if /^_design/ =~ hash["_id"]
84
- Design.new(hash)
82
+ url = CouchRest.paramify_url("#{@uri}/#{slug}", params)
83
+ result = CouchRest.get(url)
84
+ return result unless result.is_a?(Hash)
85
+ doc = if /^_design/ =~ result["_id"]
86
+ Design.new(result)
85
87
  else
86
- Document.new(hash)
88
+ Document.new(result)
87
89
  end
88
90
  doc.database = self
89
91
  doc
@@ -222,26 +224,6 @@ module CouchRest
222
224
  copy_doc(doc, dest)
223
225
  end
224
226
 
225
- # MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided.
226
- # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
227
- # hash with a '_rev' key
228
- def move_doc(doc, dest)
229
- raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev']
230
- slug = escape_docid(doc['_id'])
231
- destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
232
- "#{dest['_id']}?rev=#{dest['_rev']}"
233
- else
234
- dest
235
- end
236
- CouchRest.move "#{@uri}/#{slug}?rev=#{doc['_rev']}", destination
237
- end
238
-
239
- ### DEPRECATION NOTICE
240
- def move(doc, dest)
241
- puts "CouchRest::Database's move method is being deprecated, please use move_doc instead"
242
- move_doc(doc, dest)
243
- end
244
-
245
227
  # Compact the database, removing old document revisions and optimizing space use.
246
228
  def compact!
247
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(:class_inheritable_accessor, :database)
8
+ # subklass.send(:extlib_inheritable_accessor, :database)
9
9
  # end
10
10
 
11
- class_inheritable_accessor :database
11
+ extlib_inheritable_accessor :database
12
12
  attr_accessor :database
13
13
 
14
14
  # override the CouchRest::Model-wide default_database
@@ -0,0 +1,51 @@
1
+ module CouchRest
2
+ class Upgrade
3
+ attr_accessor :olddb, :newdb, :dbname
4
+ def initialize dbname, old_couch, new_couch
5
+ @dbname = dbname
6
+ @olddb = old_couch.database dbname
7
+ @newdb = new_couch.database!(dbname)
8
+ @bulk_docs = []
9
+ end
10
+ def clone!
11
+ puts "#{dbname} - #{olddb.info['doc_count']} docs"
12
+ streamer = CouchRest::Streamer.new(olddb)
13
+ streamer.view("_all_docs_by_seq") do |row|
14
+ load_row_docs(row) if row
15
+ maybe_flush_bulks
16
+ end
17
+ flush_bulks!
18
+ end
19
+
20
+ private
21
+
22
+ def maybe_flush_bulks
23
+ flush_bulks! if (@bulk_docs.length > 99)
24
+ end
25
+
26
+ def flush_bulks!
27
+ url = CouchRest.paramify_url "#{@newdb.uri}/_bulk_docs", {:all_or_nothing => true}
28
+ puts "posting #{@bulk_docs.length} bulk docs to #{url}"
29
+ begin
30
+ CouchRest.post url, {:docs => @bulk_docs}
31
+ @bulk_docs = []
32
+ rescue Exception => e
33
+ puts e.response
34
+ raise e
35
+ end
36
+ end
37
+
38
+ def load_row_docs(row)
39
+ results = @olddb.get(row["id"], {:open_revs => "all", :attachments => true})
40
+ results.select{|r|r["ok"]}.each do |r|
41
+ doc = r["ok"]
42
+ if /^_/.match(doc["_id"]) && !/^_design/.match(doc["_id"])
43
+ puts "invalid docid #{doc["_id"]} -- trimming"
44
+ doc["_id"] = doc["_id"].sub('_','')
45
+ end
46
+ doc.delete('_rev')
47
+ @bulk_docs << doc
48
+ end
49
+ end
50
+ end
51
+ end
@@ -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.class_inheritable_accessor("_#{symbol}_terminator")
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
- class_inheritable_accessor :_#{symbol}_callbacks
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.cattr_accessor(:properties)
11
- base.class_eval <<-EOS, __FILE__, __LINE__
12
- @@properties = []
13
- EOS
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
- define_property(name, options) unless self.properties.map{|p| p.name}.include?(name.to_s)
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.cattr_accessor(:auto_validation)
52
+ base.extlib_inheritable_accessor(:auto_validation)
53
53
  base.class_eval <<-EOS, __FILE__, __LINE__
54
54
  # Turn off auto validation by default
55
- @@auto_validation = false
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(:class_inheritable_accessor, :design_doc)
8
- base.send(:class_inheritable_accessor, :design_doc_slug_cache)
9
- base.send(:class_inheritable_accessor, :design_doc_fresh)
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
@@ -10,7 +10,7 @@ class Time
10
10
  # in your application.)
11
11
 
12
12
  def to_json(options = nil)
13
- u = self.utc
13
+ u = self.getutc
14
14
  %("#{u.strftime("%Y/%m/%d %H:%M:%S +0000")}")
15
15
  end
16
16
 
@@ -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
@@ -205,8 +202,8 @@ module CouchRest
205
202
  _run_destroy_callbacks do
206
203
  result = database.delete_doc(self, bulk)
207
204
  if result['ok']
208
- self['_rev'] = nil
209
- self['_id'] = nil
205
+ self.delete('_rev')
206
+ self.delete('_id')
210
207
  end
211
208
  result['ok']
212
209
  end
@@ -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
- module CouchRest
29
- module ClassExtension
30
- def self.included(base)
31
- if CouchRest::ClassExtension::InstanceMethods.instance_methods.all? {|m| base.respond_to?(m)}
32
- # do nothing
33
- elsif CouchRest::ClassExtension::InstanceMethods.instance_methods.any? {|m| base.respond_to?(m)}
34
- raise RuntimeError, "Conflicting extentions to Class, work it out"
35
- else
36
- base.send(:include, CouchRest::ClassExtension::InstanceMethods)
37
- end
38
- end
39
-
40
- module InstanceMethods
41
- # Defines class-level and instance-level attribute reader.
42
- #
43
- # @param *syms<Array> Array of attributes to define reader for.
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
- end
68
-
69
- # Defines class-level (and optionally instance-level) attribute writer.
70
- #
71
- # @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
72
- # @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
73
- # @return <Array[#to_s]> List of attributes that were made into cattr_writers
74
- #
75
- # @api public
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
- RUBY
95
- end
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
- RUBY
136
- unless instance_reader == false
137
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
138
- def #{ivar}
139
- self.class.#{ivar}
140
- end
141
- RUBY
142
- end
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
- RUBY
166
- unless instance_writer == false
167
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
168
- def #{ivar}=(obj) self.class.#{ivar} = obj end
169
- RUBY
170
- end
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
- end
173
-
174
- # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
175
- # each subclass has a copy of parent's attribute.
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 = Extlib::Inflection.humanize(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 = Extlib::Inflection.humanize(field_name)
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 = Extlib::Inflection.humanize(field_name)
57
+ field = CouchRest.humanize(field_name)
58
58
  min = @range ? @range.min : @min
59
59
  max = @range ? @range.max : @max
60
60
  equal = @equal
@@ -149,8 +149,8 @@ describe CouchRest::Database do
149
149
  {"mild" => "yet local"},
150
150
  {"another" => ["set","of","keys"]}
151
151
  ])
152
- rs['new_revs'].each do |r|
153
- @db.get(r['id'])
152
+ rs.each do |r|
153
+ @db.get(r['id']).rev.should == r["rev"]
154
154
  end
155
155
  end
156
156
 
@@ -170,26 +170,10 @@ describe CouchRest::Database do
170
170
  {"_id" => "twoB", "mild" => "yet local"},
171
171
  {"another" => ["set","of","keys"]}
172
172
  ])
173
- rs['new_revs'].each do |r|
174
- @db.get(r['id'])
173
+ rs.each do |r|
174
+ @db.get(r['id']).rev.should == r["rev"]
175
175
  end
176
176
  end
177
-
178
- it "in the case of an id conflict should not insert anything" do
179
- @r = @db.save_doc({'lemons' => 'from texas', 'and' => 'how', "_id" => "oneB"})
180
-
181
- lambda do
182
- rs = @db.bulk_save([
183
- {"_id" => "oneB", "wild" => "and random"},
184
- {"_id" => "twoB", "mild" => "yet local"},
185
- {"another" => ["set","of","keys"]}
186
- ])
187
- end.should raise_error(RestClient::RequestFailed)
188
-
189
- lambda do
190
- @db.get('twoB')
191
- end.should raise_error(RestClient::ResourceNotFound)
192
- end
193
177
 
194
178
  it "should empty the bulk save cache if no documents are given" do
195
179
  @db.save_doc({"_id" => "bulk_cache_1", "val" => "test"}, true)
@@ -570,49 +554,6 @@ describe CouchRest::Database do
570
554
  end
571
555
  end
572
556
 
573
- describe "MOVE existing document" do
574
- before :each do
575
- @r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'})
576
- @docid = 'tracks/zappa/muffin-man'
577
- @doc = @db.get(@r['id'])
578
- end
579
- describe "to a new location" do
580
- it "should work" do
581
- @db.move_doc @doc, @docid
582
- newdoc = @db.get(@docid)
583
- newdoc['artist'].should == 'Zappa'
584
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
585
- end
586
- it "should fail without an _id or _rev" do
587
- lambda{@db.move({"not"=>"a real doc"})}.should raise_error(ArgumentError)
588
- lambda{@db.move({"_id"=>"not a real doc"})}.should raise_error(ArgumentError)
589
- end
590
- end
591
- describe "to an existing location" do
592
- before :each do
593
- @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
594
- end
595
- it "should fail without a rev" do
596
- lambda{@db.move_doc @doc, @docid}.should raise_error(RestClient::RequestFailed)
597
- lambda{@db.get(@r['id'])}.should_not raise_error
598
- end
599
- it "should succeed with a rev" do
600
- @to_be_overwritten = @db.get(@docid)
601
- @db.move_doc @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
602
- newdoc = @db.get(@docid)
603
- newdoc['artist'].should == 'Zappa'
604
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
605
- end
606
- it "should succeed given the doc to overwrite" do
607
- @to_be_overwritten = @db.get(@docid)
608
- @db.move_doc @doc, @to_be_overwritten
609
- newdoc = @db.get(@docid)
610
- newdoc['artist'].should == 'Zappa'
611
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
612
- end
613
- end
614
- end
615
-
616
557
 
617
558
  it "should list documents" do
618
559
  5.times do
@@ -199,50 +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
- lambda{@doc.move @docid}.should raise_error(RestClient::RequestFailed)
228
- lambda{@db.get(@resp['id'])}.should_not raise_error
229
- end
230
- it "should succeed with a rev" do
231
- @to_be_overwritten = @db.get(@docid)
232
- @doc.move "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
233
- newdoc = @db.get(@docid)
234
- newdoc['key'].should == 'value'
235
- lambda {@db.get(@resp['id'])}.should raise_error(RestClient::ResourceNotFound)
236
- end
237
- it "should succeed given the doc to overwrite" do
238
- @to_be_overwritten = @db.get(@docid)
239
- @doc.move @to_be_overwritten
240
- newdoc = @db.get(@docid)
241
- newdoc['key'].should == 'value'
242
- lambda {@db.get(@resp['id'])}.should raise_error(RestClient::ResourceNotFound)
243
- end
244
- end
245
- end
246
202
  end
247
203
 
248
204
  describe "dealing with attachments" do
@@ -70,6 +70,7 @@ describe CouchRest::CastedModel do
70
70
 
71
71
  describe "saved document with casted models" do
72
72
  before(:each) do
73
+ reset_test_db!
73
74
  @obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
74
75
  @obj.save.should be_true
75
76
  @obj = DummyModel.get(@obj.id)
@@ -94,4 +95,4 @@ describe CouchRest::CastedModel do
94
95
 
95
96
  end
96
97
 
97
- end
98
+ end
@@ -4,6 +4,7 @@ describe "ExtendedDocument attachments" do
4
4
 
5
5
  describe "#has_attachment?" do
6
6
  before(:each) do
7
+ reset_test_db!
7
8
  @obj = Basic.new
8
9
  @obj.save.should == true
9
10
  @file = File.open(FIXTURE_PATH + '/attachments/test.html')
@@ -126,4 +127,4 @@ describe "ExtendedDocument attachments" do
126
127
  @obj.attachment_url(@attachment_name).should == "#{Basic.database}/#{@obj.id}/#{@attachment_name}"
127
128
  end
128
129
  end
129
- end
130
+ end
@@ -442,7 +442,7 @@ describe "ExtendedDocument" do
442
442
  @dobj.destroy
443
443
  @dobj.rev.should be_nil
444
444
  @dobj.id.should be_nil
445
- @dobj.save.should == true
445
+ @dobj.save.should == true
446
446
  end
447
447
  it "should make it go away" do
448
448
  @dobj.destroy
@@ -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
+
@@ -190,6 +190,7 @@ describe "ExtendedDocument views" do
190
190
 
191
191
  describe "with a lot of designs left around" do
192
192
  before(:each) do
193
+ reset_test_db!
193
194
  Article.by_date
194
195
  Article.view_by :field
195
196
  Article.by_field
@@ -203,4 +204,4 @@ describe "ExtendedDocument views" do
203
204
  ddocs["rows"].length.should == 1
204
205
  end
205
206
  end
206
- end
207
+ end
@@ -8,6 +8,7 @@ require File.join(FIXTURE_PATH, 'more', 'event')
8
8
  describe "ExtendedDocument properties" do
9
9
 
10
10
  before(:each) do
11
+ reset_test_db!
11
12
  @card = Card.new(:first_name => "matt")
12
13
  end
13
14
 
@@ -126,4 +127,4 @@ describe "ExtendedDocument properties" do
126
127
  end
127
128
  end
128
129
 
129
- end
130
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mattetti-couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.21"
4
+ version: "0.22"
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -89,6 +89,7 @@ files:
89
89
  - lib/couchrest/helper
90
90
  - lib/couchrest/helper/pager.rb
91
91
  - lib/couchrest/helper/streamer.rb
92
+ - lib/couchrest/helper/upgrade.rb
92
93
  - lib/couchrest/mixins
93
94
  - lib/couchrest/mixins/attachments.rb
94
95
  - lib/couchrest/mixins/callbacks.rb
@@ -140,10 +141,9 @@ files:
140
141
  - spec/couchrest/more/casted_model_spec.rb
141
142
  - spec/couchrest/more/extended_doc_attachment_spec.rb
142
143
  - spec/couchrest/more/extended_doc_spec.rb
144
+ - spec/couchrest/more/extended_doc_subclass_spec.rb
143
145
  - spec/couchrest/more/extended_doc_view_spec.rb
144
146
  - spec/couchrest/more/property_spec.rb
145
- - spec/couchrest/support
146
- - spec/couchrest/support/class_spec.rb
147
147
  - spec/fixtures
148
148
  - spec/fixtures/attachments
149
149
  - spec/fixtures/attachments/couchdb.png
@@ -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