couchrest 0.38 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/README.md +8 -8
  2. data/Rakefile +3 -4
  3. data/couchrest.gemspec +25 -105
  4. data/history.txt +5 -4
  5. data/lib/couchrest.rb +31 -52
  6. data/lib/couchrest/{core/database.rb → database.rb} +6 -11
  7. data/lib/couchrest/{core/design.rb → design.rb} +2 -2
  8. data/lib/couchrest/{core/document.rb → document.rb} +1 -1
  9. data/lib/couchrest/helper/attachments.rb +29 -0
  10. data/lib/couchrest/middlewares/logger.rb +3 -3
  11. data/lib/couchrest/monkeypatches.rb +1 -71
  12. data/lib/couchrest/{core/response.rb → response.rb} +0 -0
  13. data/lib/couchrest/{core/rest_api.rb → rest_api.rb} +8 -12
  14. data/lib/couchrest/{core/server.rb → server.rb} +0 -2
  15. data/spec/couchrest/{core/couchrest_spec.rb → couchrest_spec.rb} +15 -9
  16. data/spec/couchrest/{core/database_spec.rb → database_spec.rb} +4 -4
  17. data/spec/couchrest/{core/design_spec.rb → design_spec.rb} +2 -2
  18. data/spec/couchrest/{core/document_spec.rb → document_spec.rb} +1 -1
  19. data/spec/couchrest/{core/server_spec.rb → server_spec.rb} +2 -2
  20. data/spec/spec.opts +0 -1
  21. data/spec/spec_helper.rb +0 -4
  22. metadata +32 -133
  23. data/examples/model/example.rb +0 -144
  24. data/lib/couchrest/core/adapters/restclient.rb +0 -35
  25. data/lib/couchrest/core/http_abstraction.rb +0 -48
  26. data/lib/couchrest/core/view.rb +0 -4
  27. data/lib/couchrest/mixins.rb +0 -4
  28. data/lib/couchrest/mixins/attachments.rb +0 -31
  29. data/lib/couchrest/mixins/attribute_protection.rb +0 -74
  30. data/lib/couchrest/mixins/callbacks.rb +0 -532
  31. data/lib/couchrest/mixins/class_proxy.rb +0 -124
  32. data/lib/couchrest/mixins/collection.rb +0 -260
  33. data/lib/couchrest/mixins/design_doc.rb +0 -103
  34. data/lib/couchrest/mixins/document_queries.rb +0 -80
  35. data/lib/couchrest/mixins/extended_attachments.rb +0 -70
  36. data/lib/couchrest/mixins/extended_document_mixins.rb +0 -9
  37. data/lib/couchrest/mixins/properties.rb +0 -158
  38. data/lib/couchrest/mixins/validation.rb +0 -246
  39. data/lib/couchrest/mixins/views.rb +0 -173
  40. data/lib/couchrest/more/casted_model.rb +0 -58
  41. data/lib/couchrest/more/extended_document.rb +0 -310
  42. data/lib/couchrest/more/property.rb +0 -58
  43. data/lib/couchrest/more/typecast.rb +0 -180
  44. data/lib/couchrest/support/blank.rb +0 -42
  45. data/lib/couchrest/support/rails.rb +0 -42
  46. data/lib/couchrest/validation/auto_validate.rb +0 -157
  47. data/lib/couchrest/validation/contextual_validators.rb +0 -78
  48. data/lib/couchrest/validation/validation_errors.rb +0 -125
  49. data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
  50. data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -107
  51. data/lib/couchrest/validation/validators/format_validator.rb +0 -122
  52. data/lib/couchrest/validation/validators/formats/email.rb +0 -66
  53. data/lib/couchrest/validation/validators/formats/url.rb +0 -43
  54. data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
  55. data/lib/couchrest/validation/validators/length_validator.rb +0 -139
  56. data/lib/couchrest/validation/validators/method_validator.rb +0 -89
  57. data/lib/couchrest/validation/validators/numeric_validator.rb +0 -109
  58. data/lib/couchrest/validation/validators/required_field_validator.rb +0 -114
  59. data/spec/couchrest/more/attribute_protection_spec.rb +0 -150
  60. data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -73
  61. data/spec/couchrest/more/casted_model_spec.rb +0 -406
  62. data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -135
  63. data/spec/couchrest/more/extended_doc_inherited_spec.rb +0 -40
  64. data/spec/couchrest/more/extended_doc_spec.rb +0 -807
  65. data/spec/couchrest/more/extended_doc_subclass_spec.rb +0 -98
  66. data/spec/couchrest/more/extended_doc_view_spec.rb +0 -456
  67. data/spec/couchrest/more/property_spec.rb +0 -628
  68. data/spec/fixtures/more/article.rb +0 -35
  69. data/spec/fixtures/more/card.rb +0 -22
  70. data/spec/fixtures/more/cat.rb +0 -20
  71. data/spec/fixtures/more/course.rb +0 -22
  72. data/spec/fixtures/more/event.rb +0 -8
  73. data/spec/fixtures/more/invoice.rb +0 -17
  74. data/spec/fixtures/more/person.rb +0 -9
  75. data/spec/fixtures/more/question.rb +0 -6
  76. data/spec/fixtures/more/service.rb +0 -12
  77. data/spec/fixtures/more/user.rb +0 -22
@@ -1,173 +0,0 @@
1
- module CouchRest
2
- module Mixins
3
- module Views
4
-
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
9
- module ClassMethods
10
- # Define a CouchDB view. The name of the view will be the concatenation
11
- # of <tt>by</tt> and the keys joined by <tt>_and_</tt>
12
- #
13
- # ==== Example views:
14
- #
15
- # class Post
16
- # # view with default options
17
- # # query with Post.by_date
18
- # view_by :date, :descending => true
19
- #
20
- # # view with compound sort-keys
21
- # # query with Post.by_user_id_and_date
22
- # view_by :user_id, :date
23
- #
24
- # # view with custom map/reduce functions
25
- # # query with Post.by_tags :reduce => true
26
- # view_by :tags,
27
- # :map =>
28
- # "function(doc) {
29
- # if (doc['couchrest-type'] == 'Post' && doc.tags) {
30
- # doc.tags.forEach(function(tag){
31
- # emit(doc.tag, 1);
32
- # });
33
- # }
34
- # }",
35
- # :reduce =>
36
- # "function(keys, values, rereduce) {
37
- # return sum(values);
38
- # }"
39
- # end
40
- #
41
- # <tt>view_by :date</tt> will create a view defined by this Javascript
42
- # function:
43
- #
44
- # function(doc) {
45
- # if (doc['couchrest-type'] == 'Post' && doc.date) {
46
- # emit(doc.date, null);
47
- # }
48
- # }
49
- #
50
- # It can be queried by calling <tt>Post.by_date</tt> which accepts all
51
- # valid options for CouchRest::Database#view. In addition, calling with
52
- # the <tt>:raw => true</tt> option will return the view rows
53
- # themselves. By default <tt>Post.by_date</tt> will return the
54
- # documents included in the generated view.
55
- #
56
- # Calling with :database => [instance of CouchRest::Database] will
57
- # send the query to a specific database, otherwise it will go to
58
- # the model's default database (use_database)
59
- #
60
- # CouchRest::Database#view options can be applied at view definition
61
- # time as defaults, and they will be curried and used at view query
62
- # time. Or they can be overridden at query time.
63
- #
64
- # Custom views can be queried with <tt>:reduce => true</tt> to return
65
- # reduce results. The default for custom views is to query with
66
- # <tt>:reduce => false</tt>.
67
- #
68
- # Views are generated (on a per-model basis) lazily on first-access.
69
- # This means that if you are deploying changes to a view, the views for
70
- # that model won't be available until generation is complete. This can
71
- # take some time with large databases. Strategies are in the works.
72
- #
73
- # To understand the capabilities of this view system more completely,
74
- # it is recommended that you read the RSpec file at
75
- # <tt>spec/couchrest/more/extended_doc_spec.rb</tt>.
76
-
77
- def view_by(*keys)
78
- opts = keys.pop if keys.last.is_a?(Hash)
79
- opts ||= {}
80
- ducktype = opts.delete(:ducktype)
81
- unless ducktype || opts[:map]
82
- opts[:guards] ||= []
83
- opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')"
84
- end
85
- keys.push opts
86
- self.design_doc.view_by(*keys)
87
- self.design_doc_fresh = false
88
- end
89
-
90
- # returns stored defaults if the there is a view named this in the design doc
91
- def has_view?(view)
92
- view = view.to_s
93
- design_doc && design_doc['views'] && design_doc['views'][view]
94
- end
95
-
96
- # Dispatches to any named view.
97
- def view(name, query={}, &block)
98
- db = query.delete(:database) || database
99
- unless design_doc_fresh
100
- refresh_design_doc_on(db)
101
- end
102
- query[:raw] = true if query[:reduce]
103
- raw = query.delete(:raw)
104
- fetch_view_with_docs(db, name, query, raw, &block)
105
- end
106
-
107
- # DEPRECATED
108
- # user model_design_doc to retrieve the current design doc
109
- def all_design_doc_versions(db = database)
110
- db.documents :startkey => "_design/#{self.to_s}",
111
- :endkey => "_design/#{self.to_s}-\u9999"
112
- end
113
-
114
- def model_design_doc(db = database)
115
- begin
116
- @model_design_doc = db.get("_design/#{self.to_s}")
117
- rescue
118
- nil
119
- end
120
- end
121
-
122
- # Deletes the current design doc for the current class.
123
- # Running it to early could mean that live code has to regenerate
124
- # potentially large indexes.
125
- def cleanup_design_docs!(db = database)
126
- save_design_doc_on(db)
127
- end
128
-
129
- private
130
-
131
- def fetch_view_with_docs(db, name, opts, raw=false, &block)
132
- if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false)
133
- fetch_view(db, name, opts, &block)
134
- else
135
- begin
136
- if block.nil?
137
- collection_proxy_for(design_doc, name, opts.merge({:include_docs => true}))
138
- else
139
- view = fetch_view db, name, opts.merge({:include_docs => true}), &block
140
- view['rows'].collect{|r|create_from_database(r['doc'])} if view['rows']
141
- end
142
- rescue
143
- # fallback for old versions of couchdb that don't
144
- # have include_docs support
145
- view = fetch_view(db, name, opts, &block)
146
- view['rows'].collect{|r|create_from_database(db.get(r['id']))} if view['rows']
147
- end
148
- end
149
- end
150
-
151
- def fetch_view(db, view_name, opts, &block)
152
- raise "A view needs a database to operate on (specify :database option, or use_database in the #{self.class} class)" unless db
153
- retryable = true
154
- begin
155
- design_doc.view_on(db, view_name, opts, &block)
156
- # the design doc may not have been saved yet on this database
157
- rescue HttpAbstraction::ResourceNotFound => e
158
- if retryable
159
- save_design_doc_on(db)
160
- retryable = false
161
- retry
162
- else
163
- raise e
164
- end
165
- end
166
- end
167
-
168
- end # module ClassMethods
169
-
170
-
171
- end
172
- end
173
- end
@@ -1,58 +0,0 @@
1
- require File.expand_path('../../mixins/properties', __FILE__)
2
-
3
-
4
- module CouchRest
5
- module CastedModel
6
-
7
- def self.included(base)
8
- base.send(:include, ::CouchRest::Callbacks)
9
- base.send(:include, ::CouchRest::Mixins::Properties)
10
- base.send(:attr_accessor, :casted_by)
11
- base.send(:attr_accessor, :document_saved)
12
- end
13
-
14
- def initialize(keys={})
15
- raise StandardError unless self.is_a? Hash
16
- apply_defaults # defined in CouchRest::Mixins::Properties
17
- super()
18
- keys.each do |k,v|
19
- self[k.to_s] = v
20
- end if keys
21
- cast_keys # defined in CouchRest::Mixins::Properties
22
- end
23
-
24
- def []= key, value
25
- super(key.to_s, value)
26
- end
27
-
28
- def [] key
29
- super(key.to_s)
30
- end
31
-
32
- # Gets a reference to the top level extended
33
- # document that a model is saved inside of
34
- def base_doc
35
- return nil unless @casted_by
36
- @casted_by.base_doc
37
- end
38
-
39
- # False if the casted model has already
40
- # been saved in the containing document
41
- def new?
42
- !@document_saved
43
- end
44
- alias :new_record? :new?
45
-
46
- # Sets the attributes from a hash
47
- def update_attributes_without_saving(hash)
48
- hash.each do |k, v|
49
- raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=")
50
- end
51
- hash.each do |k, v|
52
- self.send("#{k}=",v)
53
- end
54
- end
55
- alias :attributes= :update_attributes_without_saving
56
-
57
- end
58
- end
@@ -1,310 +0,0 @@
1
- require 'mime/types'
2
- require File.join(File.dirname(__FILE__), "property")
3
- require File.join(File.dirname(__FILE__), '..', 'mixins', 'extended_document_mixins')
4
- require "enumerator"
5
-
6
- module CouchRest
7
-
8
- # Same as CouchRest::Document but with properties and validations
9
- class ExtendedDocument < Document
10
- include CouchRest::Callbacks
11
- include CouchRest::Mixins::DocumentQueries
12
- include CouchRest::Mixins::Views
13
- include CouchRest::Mixins::DesignDoc
14
- include CouchRest::Mixins::ExtendedAttachments
15
- include CouchRest::Mixins::ClassProxy
16
- include CouchRest::Mixins::Collection
17
- include CouchRest::Mixins::AttributeProtection
18
-
19
- def self.subclasses
20
- @subclasses ||= []
21
- end
22
-
23
- def self.inherited(subklass)
24
- super
25
- subklass.send(:include, CouchRest::Mixins::Properties)
26
- subklass.class_eval <<-EOS, __FILE__, __LINE__ + 1
27
- def self.inherited(subklass)
28
- super
29
- subklass.properties = self.properties.dup
30
- end
31
- EOS
32
- subclasses << subklass
33
- end
34
-
35
- # Accessors
36
- attr_accessor :casted_by
37
-
38
- # Callbacks
39
- define_callbacks :create, "result == :halt"
40
- define_callbacks :save, "result == :halt"
41
- define_callbacks :update, "result == :halt"
42
- define_callbacks :destroy, "result == :halt"
43
-
44
- # Creates a new instance, bypassing attribute protection
45
- #
46
- # ==== Returns
47
- # a document instance
48
- def self.create_from_database(passed_keys={})
49
- new(passed_keys, :directly_set_attributes => true)
50
- end
51
-
52
- def initialize(passed_keys={}, options={})
53
- apply_defaults # defined in CouchRest::Mixins::Properties
54
- remove_protected_attributes(passed_keys) unless options[:directly_set_attributes]
55
- directly_set_attributes(passed_keys) unless passed_keys.nil?
56
- super(passed_keys)
57
- cast_keys # defined in CouchRest::Mixins::Properties
58
- unless self['_id'] && self['_rev']
59
- self['couchrest-type'] = self.class.to_s
60
- end
61
- after_initialize if respond_to?(:after_initialize)
62
- end
63
-
64
- # Defines an instance and save it directly to the database
65
- #
66
- # ==== Returns
67
- # returns the reloaded document
68
- def self.create(options)
69
- instance = new(options)
70
- instance.create
71
- instance
72
- end
73
-
74
- # Defines an instance and save it directly to the database
75
- #
76
- # ==== Returns
77
- # returns the reloaded document or raises an exception
78
- def self.create!(options)
79
- instance = new(options)
80
- instance.create!
81
- instance
82
- end
83
-
84
- # Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
85
- # on the document whenever saving occurs. CouchRest uses a pretty
86
- # decent time format by default. See Time#to_json
87
- def self.timestamps!
88
- class_eval <<-EOS, __FILE__, __LINE__
89
- property(:updated_at, :read_only => true, :type => 'Time', :auto_validation => false)
90
- property(:created_at, :read_only => true, :type => 'Time', :auto_validation => false)
91
-
92
- set_callback :save, :before do |object|
93
- object['updated_at'] = Time.now
94
- object['created_at'] = object['updated_at'] if object.new?
95
- end
96
- EOS
97
- end
98
-
99
- # Name a method that will be called before the document is first saved,
100
- # which returns a string to be used for the document's <tt>_id</tt>.
101
- # Because CouchDB enforces a constraint that each id must be unique,
102
- # this can be used to enforce eg: uniq usernames. Note that this id
103
- # must be globally unique across all document types which share a
104
- # database, so if you'd like to scope uniqueness to this class, you
105
- # should use the class name as part of the unique id.
106
- def self.unique_id method = nil, &block
107
- if method
108
- define_method :set_unique_id do
109
- self['_id'] ||= self.send(method)
110
- end
111
- elsif block
112
- define_method :set_unique_id do
113
- uniqid = block.call(self)
114
- raise ArgumentError, "unique_id block must not return nil" if uniqid.nil?
115
- self['_id'] ||= uniqid
116
- end
117
- end
118
- end
119
-
120
- # Temp solution to make the view_by methods available
121
- def self.method_missing(m, *args, &block)
122
- if has_view?(m)
123
- query = args.shift || {}
124
- view(m, query, *args, &block)
125
- else
126
- super
127
- end
128
- end
129
-
130
- ### instance methods
131
-
132
- # Returns the Class properties
133
- #
134
- # ==== Returns
135
- # Array:: the list of properties for the instance
136
- def properties
137
- self.class.properties
138
- end
139
-
140
- # Gets a reference to the actual document in the DB
141
- # Calls up to the next document if there is one,
142
- # Otherwise we're at the top and we return self
143
- def base_doc
144
- return self if base_doc?
145
- @casted_by.base_doc
146
- end
147
-
148
- # Checks if we're the top document
149
- def base_doc?
150
- !@casted_by
151
- end
152
-
153
- # Takes a hash as argument, and applies the values by using writer methods
154
- # for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are
155
- # missing. In case of error, no attributes are changed.
156
- def update_attributes_without_saving(hash)
157
- # remove attributes that cannot be updated, silently ignoring them
158
- # which matches Rails behavior when, for instance, setting created_at.
159
- # make a copy, we don't want to change arguments
160
- attrs = hash.dup
161
- %w[_id _rev created_at updated_at].each {|attr| attrs.delete(attr)}
162
- check_properties_exist(attrs)
163
- set_attributes(attrs)
164
- end
165
- alias :attributes= :update_attributes_without_saving
166
-
167
- # Takes a hash as argument, and applies the values by using writer methods
168
- # for each key. Raises a NoMethodError if the corresponding methods are
169
- # missing. In case of error, no attributes are changed.
170
- def update_attributes(hash)
171
- update_attributes_without_saving hash
172
- save
173
- end
174
-
175
- # for compatibility with old-school frameworks
176
- alias :new_record? :new?
177
- alias :new_document? :new?
178
-
179
- # Trigger the callbacks (before, after, around)
180
- # and create the document
181
- # It's important to have a create callback since you can't check if a document
182
- # was new after you saved it
183
- #
184
- # When creating a document, both the create and the save callbacks will be triggered.
185
- def create(bulk = false)
186
- caught = catch(:halt) do
187
- _run_create_callbacks do
188
- _run_save_callbacks do
189
- create_without_callbacks(bulk)
190
- end
191
- end
192
- end
193
- end
194
-
195
- # unlike save, create returns the newly created document
196
- def create_without_callbacks(bulk =false)
197
- raise ArgumentError, "a document requires a database to be created to (The document or the #{self.class} default database were not set)" unless database
198
- set_unique_id if new? && self.respond_to?(:set_unique_id)
199
- result = database.save_doc(self, bulk)
200
- (result["ok"] == true) ? self : false
201
- end
202
-
203
- # Creates the document in the db. Raises an exception
204
- # if the document is not created properly.
205
- def create!
206
- raise "#{self.inspect} failed to save" unless self.create
207
- end
208
-
209
- # Trigger the callbacks (before, after, around)
210
- # only if the document isn't new
211
- def update(bulk = false)
212
- caught = catch(:halt) do
213
- if self.new?
214
- save(bulk)
215
- else
216
- _run_update_callbacks do
217
- _run_save_callbacks do
218
- save_without_callbacks(bulk)
219
- end
220
- end
221
- end
222
- end
223
- end
224
-
225
- # Trigger the callbacks (before, after, around)
226
- # and save the document
227
- def save(bulk = false)
228
- caught = catch(:halt) do
229
- if self.new?
230
- _run_save_callbacks do
231
- save_without_callbacks(bulk)
232
- end
233
- else
234
- update(bulk)
235
- end
236
- end
237
- end
238
-
239
- # Overridden to set the unique ID.
240
- # Returns a boolean value
241
- def save_without_callbacks(bulk = false)
242
- raise ArgumentError, "a document requires a database to be saved to (The document or the #{self.class} default database were not set)" unless database
243
- set_unique_id if new? && self.respond_to?(:set_unique_id)
244
- result = database.save_doc(self, bulk)
245
- mark_as_saved
246
- result["ok"] == true
247
- end
248
-
249
- # Saves the document to the db using save. Raises an exception
250
- # if the document is not saved properly.
251
- def save!
252
- raise "#{self.inspect} failed to save" unless self.save
253
- true
254
- end
255
-
256
- # Deletes the document from the database. Runs the :destroy callbacks.
257
- # Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
258
- # document to be saved to a new <tt>_id</tt>.
259
- def destroy(bulk=false)
260
- caught = catch(:halt) do
261
- _run_destroy_callbacks do
262
- result = database.delete_doc(self, bulk)
263
- if result['ok']
264
- self.delete('_rev')
265
- self.delete('_id')
266
- end
267
- result['ok']
268
- end
269
- end
270
- end
271
-
272
- protected
273
-
274
- # Set document_saved flag on all casted models to true
275
- def mark_as_saved
276
- self.each do |key, prop|
277
- if prop.is_a?(Array)
278
- prop.each do |item|
279
- if item.respond_to?(:document_saved)
280
- item.send(:document_saved=, true)
281
- end
282
- end
283
- elsif prop.respond_to?(:document_saved)
284
- prop.send(:document_saved=, true)
285
- end
286
- end
287
- end
288
-
289
- private
290
-
291
- def check_properties_exist(attrs)
292
- attrs.each do |attribute_name, attribute_value|
293
- raise NoMethodError, "#{attribute_name}= method not available, use property :#{attribute_name}" unless self.respond_to?("#{attribute_name}=")
294
- end
295
- end
296
-
297
- def directly_set_attributes(hash)
298
- hash.each do |attribute_name, attribute_value|
299
- if self.respond_to?("#{attribute_name}=")
300
- self.send("#{attribute_name}=", hash.delete(attribute_name))
301
- end
302
- end
303
- end
304
-
305
- def set_attributes(hash)
306
- attrs = remove_protected_attributes(hash)
307
- directly_set_attributes(attrs)
308
- end
309
- end
310
- end