mattetti-couchrest 0.21 → 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 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