langalex-couch_potato 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +35 -30
- data/VERSION.yml +1 -1
- data/lib/couch_potato.rb +10 -9
- data/lib/couch_potato/database.rb +18 -15
- data/lib/couch_potato/persistence.rb +1 -1
- data/lib/couch_potato/persistence/callbacks.rb +13 -50
- data/lib/couch_potato/persistence/properties.rb +3 -3
- data/lib/couch_potato/view/base_view_spec.rb +7 -3
- data/lib/couch_potato/view/custom_views.rb +15 -8
- data/lib/couch_potato/view/view_query.rb +6 -6
- data/spec/callbacks_spec.rb +1 -19
- data/spec/custom_view_spec.rb +25 -24
- data/spec/spec_helper.rb +3 -2
- data/spec/unit/database_spec.rb +20 -0
- metadata +1 -1
data/README.md
CHANGED
@@ -20,21 +20,26 @@ Lastly Couch Potato aims to provide a seamless integration with Ruby on Rails, e
|
|
20
20
|
|
21
21
|
### Installation
|
22
22
|
|
23
|
-
Couch Potato requires Ruby 1.9.
|
24
|
-
|
25
23
|
Couch Potato is hosted as a gem on github which you can install like this:
|
26
24
|
|
27
25
|
sudo gem source --add http://gems.github.com # if you haven't already
|
28
26
|
sudo gem install langalex-couch_potato
|
29
|
-
|
27
|
+
|
30
28
|
#### Using with your ruby application:
|
31
29
|
|
32
30
|
require 'rubygems'
|
33
31
|
gem 'langalex-couch_potato'
|
34
32
|
require 'couch_potato'
|
33
|
+
|
34
|
+
Alternatively you can download or clone the source repository and then require lib/couch_potato.rb.
|
35
|
+
|
36
|
+
You MUST specificy the name of the database:
|
37
|
+
|
35
38
|
CouchPotato::Config.database_name = 'name of the db'
|
36
|
-
|
37
|
-
|
39
|
+
|
40
|
+
The server URL will default to http://localhost:5984/ unless specified with:
|
41
|
+
|
42
|
+
CouchPotato::Config.database_server = "http://example.com:5984/"
|
38
43
|
|
39
44
|
#### Using with Rails
|
40
45
|
|
@@ -48,7 +53,7 @@ Then create a config/couchdb.yml:
|
|
48
53
|
test: test_db_name
|
49
54
|
production: http://db.server/production_db_name
|
50
55
|
|
51
|
-
Alternatively you can also install Couch Potato directly as a plugin.
|
56
|
+
Alternatively you can also install Couch Potato directly as a plugin.
|
52
57
|
|
53
58
|
### Introduction
|
54
59
|
|
@@ -71,15 +76,15 @@ If you want to store any properties you have to declare them:
|
|
71
76
|
|
72
77
|
class User
|
73
78
|
include CouchPotato::Persistence
|
74
|
-
|
79
|
+
|
75
80
|
property :name
|
76
81
|
end
|
77
|
-
|
82
|
+
|
78
83
|
Properties can be of any type:
|
79
84
|
|
80
85
|
class User
|
81
86
|
include CouchPotato::Persistence
|
82
|
-
|
87
|
+
|
83
88
|
property :address, :type => Address
|
84
89
|
end
|
85
90
|
|
@@ -87,7 +92,7 @@ Now you can save your objects. All database operations are encapsulated in the C
|
|
87
92
|
|
88
93
|
user = User.new :name => 'joe'
|
89
94
|
CouchPotato.database.save_document user # or save_document!
|
90
|
-
|
95
|
+
|
91
96
|
You can of course also retrieve your instance:
|
92
97
|
|
93
98
|
CouchPotato.database.load_document "id_of_the_user_document" # => <#User 0x3075>
|
@@ -104,13 +109,13 @@ You can access the properties you declared above through normal attribute access
|
|
104
109
|
user.created_at # => Fri Oct 24 19:05:54 +0200 2008
|
105
110
|
user.updated_at # => Fri Oct 24 19:05:54 +0200 2008
|
106
111
|
user.new? # => false
|
107
|
-
|
112
|
+
|
108
113
|
If you want to have properties that don't map to any JSON type, i.e. other than String, Number, Boolean, Hash or Array you have to define the type like this:
|
109
114
|
|
110
115
|
class User
|
111
116
|
property :date_of_birth, :type => Date
|
112
117
|
end
|
113
|
-
|
118
|
+
|
114
119
|
The date_of_birth property is now automatically serialized to JSON and back when storing/retrieving objects.
|
115
120
|
|
116
121
|
#### Dirty tracking
|
@@ -121,15 +126,15 @@ CouchPotato tracks the dirty state of attributes in the same way ActiveRecord do
|
|
121
126
|
user.name # => 'joe'
|
122
127
|
user.name_changed? # => false
|
123
128
|
user.name_was # => nil
|
124
|
-
|
129
|
+
|
125
130
|
You can also force a dirty state:
|
126
|
-
|
131
|
+
|
127
132
|
user.name = 'jane'
|
128
133
|
user.name_changed? # => true
|
129
134
|
user.name_not_changed
|
130
135
|
user.name_changed? # => false
|
131
136
|
CouchPotato.database.save_document user # does nothing as no attributes are dirty
|
132
|
-
|
137
|
+
|
133
138
|
|
134
139
|
#### Object validations
|
135
140
|
|
@@ -151,12 +156,12 @@ In order to find data in your CouchDB you have to create a view first. Couch Pot
|
|
151
156
|
class User
|
152
157
|
include CouchPotato::Persistence
|
153
158
|
property :name
|
154
|
-
|
159
|
+
|
155
160
|
view :all, :key => :created_at
|
156
161
|
end
|
157
|
-
|
162
|
+
|
158
163
|
This will create a view called "all" in the "user" design document with a map function that emits "created_at" for every user document.
|
159
|
-
|
164
|
+
|
160
165
|
CouchPotato.database.view User.all
|
161
166
|
|
162
167
|
This will load all user documents in your database sorted by created_at.
|
@@ -169,10 +174,10 @@ Composite keys are also possible:
|
|
169
174
|
|
170
175
|
class User
|
171
176
|
property :name
|
172
|
-
|
177
|
+
|
173
178
|
view :all, :key => [:created_at, :name]
|
174
179
|
end
|
175
|
-
|
180
|
+
|
176
181
|
The creation of views is based on view specification classes (see the CouchPotato::View module). The above code uses the ModelViewSpec class which is used to find models by their properties. For more sophisticated searches you can use other view specifications (either use the built-in or provide your own) by passing a type parameter:
|
177
182
|
|
178
183
|
If you have larger structures and you only want to load some attributes you can use the PropertiesViewSpec (the full class name is automatically derived):
|
@@ -180,25 +185,25 @@ If you have larger structures and you only want to load some attributes you can
|
|
180
185
|
class User
|
181
186
|
property :name
|
182
187
|
property :bio
|
183
|
-
|
188
|
+
|
184
189
|
view :all, :key => :created_at, :properties => [:name], :type => :properties
|
185
190
|
end
|
186
|
-
|
191
|
+
|
187
192
|
CouchPotato.database.view(User.everyone).first.name # => "joe"
|
188
193
|
CouchPotato.database.view(User.everyone).first.bio # => nil
|
189
|
-
|
194
|
+
|
190
195
|
You can also pass in custom map/reduce functions with the custom view spec:
|
191
196
|
|
192
197
|
class User
|
193
198
|
view :all, :map => "function(doc) { emit(doc.created_at, null)}", :include_docs => true, :type => :custom
|
194
199
|
end
|
195
|
-
|
200
|
+
|
196
201
|
If you don't want the results to be converted into models the raw view is your friend:
|
197
202
|
|
198
203
|
class User
|
199
204
|
view :all, :map => "function(doc) { emit(doc.created_at, doc.name)}", :type => :raw
|
200
205
|
end
|
201
|
-
|
206
|
+
|
202
207
|
When querying this view you will get the raw data returned by CouchDB which looks something like this: {'total_entries': 2, 'rows': [{'value': 'alex', 'key': '2009-01-03 00:02:34 +000', 'id': '75976rgi7546gi02a'}]}
|
203
208
|
|
204
209
|
To process this raw data you can also pass in a results filter:
|
@@ -221,7 +226,7 @@ Couch Potato supports the usual lifecycle callbacks known from ActiveRecord:
|
|
221
226
|
|
222
227
|
class User
|
223
228
|
include CouchPotato::Persistence
|
224
|
-
|
229
|
+
|
225
230
|
before_create :do_something_before_create
|
226
231
|
before_update {|user, db| user.do_something_on_update}
|
227
232
|
end
|
@@ -230,25 +235,25 @@ This will call the method do_something_before_create before creating an object a
|
|
230
235
|
|
231
236
|
Supported callbacks are: :before_validation_on_create, :before_validation_on_update, :before_validation_on_save, :before_create, :after_create, :before_update, :after_update, :before_save, :after_save, :before_destroy, :after_destroy.
|
232
237
|
|
233
|
-
#### Testing
|
238
|
+
#### Testing
|
234
239
|
|
235
240
|
To make testing easier and faster database logic has been put into its own class, which you can replace and stub out in whatever way you want:
|
236
241
|
|
237
242
|
class User
|
238
243
|
include CouchPotato::Persistence
|
239
244
|
end
|
240
|
-
|
245
|
+
|
241
246
|
# RSpec
|
242
247
|
describe 'save a user' do
|
243
248
|
it 'should save' do
|
244
|
-
couchrest_db = stub 'couchrest_db',
|
249
|
+
couchrest_db = stub 'couchrest_db',
|
245
250
|
database = CouchPotato::Database.new couchrest_db
|
246
251
|
user = User.new
|
247
252
|
couchrest_db.should_receive(:save_doc).with(...)
|
248
253
|
database.save_document user
|
249
254
|
end
|
250
255
|
end
|
251
|
-
|
256
|
+
|
252
257
|
By creating you own instances of CouchPotato::Database and passing them a fake CouchRest database instance you can completely disconnect your unit tests/spec from the database.
|
253
258
|
|
254
259
|
### Helping out
|
data/VERSION.yml
CHANGED
data/lib/couch_potato.rb
CHANGED
@@ -10,26 +10,26 @@ require 'validatable'
|
|
10
10
|
|
11
11
|
module CouchPotato
|
12
12
|
Config = OpenStruct.new
|
13
|
-
|
13
|
+
|
14
14
|
# Returns a database instance which you can then use to create objects and query views. You have to set the CouchPotato::Config.database_name before this works.
|
15
15
|
def self.database
|
16
16
|
@@__database ||= Database.new(self.couchrest_database)
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
# Returns the underlying CouchRest database object if you want low level access to your CouchDB. You have to set the CouchPotato::Config.database_name before this works.
|
20
20
|
def self.couchrest_database
|
21
21
|
@@__couchrest_database ||= CouchRest.database(full_url_to_database)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
private
|
25
|
-
|
25
|
+
|
26
26
|
def self.full_url_to_database
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
raise('No Database configured. Set CouchPotato::Config.database_name') unless CouchPotato::Config.database_name
|
28
|
+
if CouchPotato::Config.database_server
|
29
|
+
return "#{CouchPotato::Config.database_server}#{CouchPotato::Config.database_name}"
|
30
|
+
else
|
31
|
+
return "http://127.0.0.1:5984/#{CouchPotato::Config.database_name}"
|
31
32
|
end
|
32
|
-
url
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -37,5 +37,6 @@ require File.dirname(__FILE__) + '/core_ext/object'
|
|
37
37
|
require File.dirname(__FILE__) + '/core_ext/time'
|
38
38
|
require File.dirname(__FILE__) + '/core_ext/date'
|
39
39
|
require File.dirname(__FILE__) + '/core_ext/string'
|
40
|
+
require File.dirname(__FILE__) + '/core_ext/symbol'
|
40
41
|
require File.dirname(__FILE__) + '/couch_potato/persistence'
|
41
42
|
|
@@ -35,10 +35,10 @@ module CouchPotato
|
|
35
35
|
alias_method :save!, :save_document!
|
36
36
|
|
37
37
|
def destroy_document(document)
|
38
|
-
document.run_callbacks :before_destroy
|
38
|
+
document.run_callbacks :before_destroy
|
39
39
|
document._deleted = true
|
40
40
|
database.delete_doc document.to_hash
|
41
|
-
document.run_callbacks :after_destroy
|
41
|
+
document.run_callbacks :after_destroy
|
42
42
|
document._id = nil
|
43
43
|
document._rev = nil
|
44
44
|
end
|
@@ -48,7 +48,9 @@ module CouchPotato
|
|
48
48
|
raise "Can't load a document without an id (got nil)" if id.nil?
|
49
49
|
begin
|
50
50
|
json = database.get(id)
|
51
|
-
Class.const_get(json['ruby_class']).json_create json
|
51
|
+
instance = Class.const_get(json['ruby_class']).json_create json
|
52
|
+
instance.database = self
|
53
|
+
instance
|
52
54
|
rescue(RestClient::ResourceNotFound)
|
53
55
|
nil
|
54
56
|
end
|
@@ -62,29 +64,30 @@ module CouchPotato
|
|
62
64
|
private
|
63
65
|
|
64
66
|
def create_document(document)
|
65
|
-
document.
|
66
|
-
document.run_callbacks :
|
67
|
+
document.database = self
|
68
|
+
document.run_callbacks :before_validation_on_save
|
69
|
+
document.run_callbacks :before_validation_on_create
|
67
70
|
return unless document.valid?
|
68
|
-
document.run_callbacks :before_save
|
69
|
-
document.run_callbacks :before_create
|
71
|
+
document.run_callbacks :before_save
|
72
|
+
document.run_callbacks :before_create
|
70
73
|
res = database.save_doc document.to_hash
|
71
74
|
document._rev = res['rev']
|
72
75
|
document._id = res['id']
|
73
|
-
document.run_callbacks :after_save
|
74
|
-
document.run_callbacks :after_create
|
76
|
+
document.run_callbacks :after_save
|
77
|
+
document.run_callbacks :after_create
|
75
78
|
true
|
76
79
|
end
|
77
80
|
|
78
81
|
def update_document(document)
|
79
|
-
document.run_callbacks :before_validation_on_save
|
80
|
-
document.run_callbacks :before_validation_on_update
|
82
|
+
document.run_callbacks :before_validation_on_save
|
83
|
+
document.run_callbacks :before_validation_on_update
|
81
84
|
return unless document.valid?
|
82
|
-
document.run_callbacks :before_save
|
83
|
-
document.run_callbacks :before_update
|
85
|
+
document.run_callbacks :before_save
|
86
|
+
document.run_callbacks :before_update
|
84
87
|
res = database.save_doc document.to_hash
|
85
88
|
document._rev = res['rev']
|
86
|
-
document.run_callbacks :after_save
|
87
|
-
document.run_callbacks :after_update
|
89
|
+
document.run_callbacks :after_save
|
90
|
+
document.run_callbacks :after_update
|
88
91
|
true
|
89
92
|
end
|
90
93
|
|
@@ -17,7 +17,7 @@ module CouchPotato
|
|
17
17
|
base.send :include, DirtyAttributes
|
18
18
|
base.send :include, MagicTimestamps
|
19
19
|
base.class_eval do
|
20
|
-
attr_accessor :_id, :_rev, :_attachments, :_deleted
|
20
|
+
attr_accessor :_id, :_rev, :_attachments, :_deleted, :database
|
21
21
|
alias_method :id, :_id
|
22
22
|
end
|
23
23
|
end
|
@@ -1,75 +1,38 @@
|
|
1
1
|
module CouchPotato
|
2
2
|
module Persistence
|
3
3
|
module Callbacks
|
4
|
-
|
5
|
-
class Callback #:nodoc:
|
6
|
-
def initialize(model, name, database)
|
7
|
-
@model, @name, @database = model, name, database
|
8
|
-
end
|
9
|
-
|
10
|
-
def run
|
11
|
-
if @name.is_a?(Symbol)
|
12
|
-
run_method_callback @name
|
13
|
-
elsif @name.is_a?(Proc)
|
14
|
-
run_lambda_callback @name
|
15
|
-
else
|
16
|
-
raise "Don't know how to handle callback of type #{name.class.name}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def run_method_callback(name)
|
23
|
-
if callback_method(name).arity == 0
|
24
|
-
@model.send name
|
25
|
-
elsif callback_method(name).arity == 1
|
26
|
-
@model.send name, @database
|
27
|
-
else
|
28
|
-
raise "Don't know how to handle method callback with #{callback_method(name).arity} arguments"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def callback_method(name)
|
33
|
-
@model.method(name)
|
34
|
-
end
|
35
|
-
|
36
|
-
def run_lambda_callback(lambda)
|
37
|
-
if lambda.arity == 1
|
38
|
-
lambda.call @model
|
39
|
-
elsif lambda.arity == 2
|
40
|
-
lambda.call @model, @database
|
41
|
-
else raise "Don't know how to handle lambda callback with #{lambda.arity} arguments"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
4
|
def self.included(base)
|
48
5
|
base.extend ClassMethods
|
49
|
-
|
6
|
+
|
50
7
|
base.class_eval do
|
51
8
|
attr_accessor :skip_callbacks
|
52
9
|
def self.callbacks
|
53
10
|
@callbacks ||= {}
|
54
|
-
@callbacks[self.name] ||= {:before_validation_on_create => [],
|
55
|
-
:before_validation_on_update => [], :before_validation_on_save => [], :before_create => [],
|
11
|
+
@callbacks[self.name] ||= {:before_validation_on_create => [],
|
12
|
+
:before_validation_on_update => [], :before_validation_on_save => [], :before_create => [],
|
56
13
|
:after_create => [], :before_update => [], :after_update => [],
|
57
14
|
:before_save => [], :after_save => [],
|
58
15
|
:before_destroy => [], :after_destroy => []}
|
59
16
|
end
|
60
17
|
end
|
61
18
|
end
|
62
|
-
|
19
|
+
|
63
20
|
# Runs all callbacks on a model with the given name, i.g. :after_create.
|
64
21
|
#
|
65
22
|
# This method is called by the CouchPotato::Database object when saving/destroying an object
|
66
|
-
def run_callbacks(name
|
23
|
+
def run_callbacks(name)
|
67
24
|
return if skip_callbacks
|
68
25
|
self.class.callbacks[name].uniq.each do |callback|
|
69
|
-
|
26
|
+
if callback.is_a?(Symbol)
|
27
|
+
send callback
|
28
|
+
elsif callback.is_a?(Proc)
|
29
|
+
callback.call self
|
30
|
+
else
|
31
|
+
raise "Don't know how to handle callback of type #{name.class.name}"
|
32
|
+
end
|
70
33
|
end
|
71
34
|
end
|
72
|
-
|
35
|
+
|
73
36
|
module ClassMethods
|
74
37
|
[
|
75
38
|
:before_validation_on_create,
|
@@ -13,7 +13,7 @@ module CouchPotato
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
module ClassMethods
|
18
18
|
# returns all the property names of a model class that have been defined using the #property method
|
19
19
|
#
|
@@ -26,13 +26,13 @@ module CouchPotato
|
|
26
26
|
def property_names
|
27
27
|
properties.map(&:name)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def json_create(json) #:nodoc:
|
31
31
|
instance = super
|
32
32
|
instance.send(:assign_attribute_copies_for_dirty_tracking)
|
33
33
|
instance
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Declare a proprty on a model class. properties are not typed by default. You can use any of the basic types by JSON (String, Integer, Fixnum, Array, Hash). If you want a property to be of a custom class you have to define it using the :class option.
|
37
37
|
#
|
38
38
|
# example:
|
@@ -3,15 +3,19 @@ module CouchPotato
|
|
3
3
|
class BaseViewSpec
|
4
4
|
attr_reader :reduce_function, :design_document, :view_name, :view_parameters, :klass, :options
|
5
5
|
private :klass, :options
|
6
|
-
|
6
|
+
|
7
7
|
def initialize(klass, view_name, options, view_parameters)
|
8
8
|
@klass = klass
|
9
9
|
@design_document = klass.to_s.underscore
|
10
10
|
@view_name = view_name
|
11
11
|
@options = options
|
12
|
-
@view_parameters =
|
12
|
+
@view_parameters = {}
|
13
|
+
[:group, :include_docs, :descending, :group_level, :limit].each do |key|
|
14
|
+
@view_parameters[key] = options[key] if options.include?(key)
|
15
|
+
end
|
16
|
+
@view_parameters.merge!(view_parameters)
|
13
17
|
end
|
14
|
-
|
18
|
+
|
15
19
|
def process_results(results)
|
16
20
|
results
|
17
21
|
end
|
@@ -8,21 +8,28 @@ require File.dirname(__FILE__) + '/raw_view_spec'
|
|
8
8
|
module CouchPotato
|
9
9
|
module View
|
10
10
|
module CustomViews
|
11
|
-
|
11
|
+
|
12
12
|
def self.included(base)
|
13
13
|
base.extend ClassMethods
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
module ClassMethods
|
17
17
|
# Declare a CouchDB view, for examples on how to use see the *ViewSpec classes in CouchPotato::View
|
18
|
+
def views
|
19
|
+
@views ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute_view(view_name, view_parameters)
|
23
|
+
view_spec_class(views[view_name][:type]).new(self, view_name, views[view_name], view_parameters)
|
24
|
+
end
|
25
|
+
|
18
26
|
def view(view_name, options)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
27
|
+
view_name = view_name.to_s
|
28
|
+
views[view_name] = options
|
29
|
+
method_str = "def #{view_name}(view_parameters = {}); execute_view(\"#{view_name}\", view_parameters); end"
|
30
|
+
self.instance_eval(method_str)
|
24
31
|
end
|
25
|
-
|
32
|
+
|
26
33
|
def view_spec_class(type)
|
27
34
|
if type && type.is_a?(Class)
|
28
35
|
type
|
@@ -9,18 +9,18 @@ module CouchPotato
|
|
9
9
|
@map_function = map_function
|
10
10
|
@reduce_function = reduce_function
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def query_view!(parameters = {})
|
14
14
|
begin
|
15
15
|
query_view parameters
|
16
|
-
rescue RestClient::ResourceNotFound => e
|
16
|
+
rescue RestClient::ResourceNotFound# => e
|
17
17
|
create_view
|
18
18
|
retry
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
private
|
23
|
-
|
23
|
+
|
24
24
|
def create_view
|
25
25
|
design_doc = @database.get "_design/#{@design_document_name}" rescue nil
|
26
26
|
design_doc ||= {'views' => {}, "_id" => "_design/#{@design_document_name}"}
|
@@ -30,11 +30,11 @@ module CouchPotato
|
|
30
30
|
}
|
31
31
|
@database.save_doc(design_doc)
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def query_view(parameters)
|
35
35
|
@database.view view_url, parameters
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def view_url
|
39
39
|
"#{@design_document_name}/#{@view_name}"
|
40
40
|
end
|
data/spec/callbacks_spec.rb
CHANGED
@@ -24,8 +24,6 @@ class CallbackRecorder
|
|
24
24
|
|
25
25
|
attr_accessor :lambda_works
|
26
26
|
before_create lambda {|model| model.lambda_works = true }
|
27
|
-
after_create lambda {|model, db| db.view CallbackRecorder.all}
|
28
|
-
before_update :method_callback_with_argument
|
29
27
|
|
30
28
|
def callbacks
|
31
29
|
@callbacks ||= []
|
@@ -246,26 +244,10 @@ describe "destroy callbacks" do
|
|
246
244
|
end
|
247
245
|
end
|
248
246
|
|
249
|
-
describe "method callbacks" do
|
250
|
-
it "should pass the database to a method with arity 1" do
|
251
|
-
recorder = CallbackRecorder.new
|
252
|
-
db = stub 'db'
|
253
|
-
db.should_receive(:view)
|
254
|
-
recorder.run_callbacks :before_update, db
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
247
|
describe "lambda callbacks" do
|
259
248
|
it "should run the lambda" do
|
260
249
|
recorder = CallbackRecorder.new
|
261
|
-
recorder.run_callbacks :before_create
|
250
|
+
recorder.run_callbacks :before_create
|
262
251
|
recorder.lambda_works.should be_true
|
263
252
|
end
|
264
|
-
|
265
|
-
it "should pass the database to a lambda with arity 2" do
|
266
|
-
recorder = CallbackRecorder.new
|
267
|
-
db = stub 'db'
|
268
|
-
db.should_receive(:view)
|
269
|
-
recorder.run_callbacks :after_create, db
|
270
|
-
end
|
271
253
|
end
|
data/spec/custom_view_spec.rb
CHANGED
@@ -2,10 +2,10 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
2
2
|
|
3
3
|
class Build
|
4
4
|
include CouchPotato::Persistence
|
5
|
-
|
5
|
+
|
6
6
|
property :state
|
7
7
|
property :time
|
8
|
-
|
8
|
+
|
9
9
|
view :timeline, :key => :time
|
10
10
|
view :count, :key => :time, :reduce => true
|
11
11
|
view :minimal_timeline, :key => :time, :properties => [:state], :type => :properties
|
@@ -21,50 +21,51 @@ describe 'view' do
|
|
21
21
|
before(:each) do
|
22
22
|
recreate_db
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
it "should return instances of the class" do
|
26
26
|
CouchPotato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
|
27
|
-
CouchPotato.database.view(Build.timeline)
|
27
|
+
results = CouchPotato.database.view(Build.timeline)
|
28
|
+
results.map(&:class).should == [Build]
|
28
29
|
end
|
29
|
-
|
30
|
-
it "should pass the view options to the
|
30
|
+
|
31
|
+
it "should pass the view options to the view query" do
|
31
32
|
query = mock 'query'
|
32
33
|
CouchPotato::View::ViewQuery.stub!(:new).and_return(query)
|
33
34
|
query.should_receive(:query_view!).with(hash_including(:key => 1)).and_return('rows' => [])
|
34
35
|
CouchPotato.database.view Build.timeline(:key => 1)
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
it "should not return documents that don't have a matching ruby_class" do
|
38
39
|
CouchPotato.couchrest_database.save_doc({:time => 'x'})
|
39
40
|
CouchPotato.database.view(Build.timeline).should == []
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
it "should count documents" do
|
43
44
|
CouchPotato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
|
44
45
|
CouchPotato.database.view(Build.count(:reduce => true)).should == 1
|
45
46
|
end
|
46
|
-
|
47
|
+
|
47
48
|
it "should count zero documents" do
|
48
49
|
CouchPotato.database.view(Build.count(:reduce => true)).should == 0
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
describe "properties defined" do
|
52
53
|
it "should assign the configured properties" do
|
53
54
|
CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')
|
54
55
|
CouchPotato.database.view(Build.minimal_timeline).first.state.should == 'success'
|
55
56
|
end
|
56
|
-
|
57
|
+
|
57
58
|
it "should not assign the properties not configured" do
|
58
59
|
CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')
|
59
60
|
CouchPotato.database.view(Build.minimal_timeline).first.time.should be_nil
|
60
61
|
end
|
61
|
-
|
62
|
+
|
62
63
|
it "should assign the id even if it is not configured" do
|
63
64
|
id = CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')['id']
|
64
65
|
CouchPotato.database.view(Build.minimal_timeline).first._id.should == id
|
65
66
|
end
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
describe "no properties defined" do
|
69
70
|
it "should assign all properties to the objects by default" do
|
70
71
|
id = CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', :ruby_class => 'Build'})['id']
|
@@ -74,60 +75,60 @@ describe 'view' do
|
|
74
75
|
result._id.should == id
|
75
76
|
end
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
describe "map function given" do
|
79
80
|
it "should still return instances of the class" do
|
80
81
|
CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
|
81
82
|
CouchPotato.database.view(Build.custom_timeline).map(&:class).should == [Build]
|
82
83
|
end
|
83
|
-
|
84
|
+
|
84
85
|
it "should assign the properties from the value" do
|
85
86
|
CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
|
86
87
|
CouchPotato.database.view(Build.custom_timeline).map(&:state).should == ['custom_success']
|
87
88
|
end
|
88
|
-
|
89
|
+
|
89
90
|
it "should leave the other properties blank" do
|
90
91
|
CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
|
91
92
|
CouchPotato.database.view(Build.custom_timeline).map(&:time).should == [nil]
|
92
93
|
end
|
93
|
-
|
94
|
+
|
94
95
|
describe "that returns null documents" do
|
95
96
|
it "should return instances of the class" do
|
96
97
|
CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
|
97
98
|
CouchPotato.database.view(Build.custom_timeline_returns_docs).map(&:class).should == [Build]
|
98
99
|
end
|
99
|
-
|
100
|
+
|
100
101
|
it "should assign the properties from the value" do
|
101
102
|
CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
|
102
103
|
CouchPotato.database.view(Build.custom_timeline_returns_docs).map(&:state).should == ['success']
|
103
104
|
end
|
104
105
|
end
|
105
106
|
end
|
106
|
-
|
107
|
+
|
107
108
|
describe "with array as key" do
|
108
109
|
it "should create a map function with the composite key" do
|
109
110
|
CouchPotato::View::ViewQuery.should_receive(:new).with(anything, anything, anything, string_matching(/emit\(\[doc\['time'\], doc\['state'\]\]/), anything).and_return(stub('view query').as_null_object)
|
110
111
|
CouchPotato.database.view Build.key_array_timeline
|
111
112
|
end
|
112
113
|
end
|
113
|
-
|
114
|
+
|
114
115
|
describe "raw view" do
|
115
116
|
it "should return the raw data" do
|
116
117
|
CouchPotato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
|
117
118
|
CouchPotato.database.view(Build.raw)['rows'][0]['value'].should == 'success'
|
118
119
|
end
|
119
|
-
|
120
|
+
|
120
121
|
it "should return filtred raw data" do
|
121
122
|
CouchPotato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
|
122
123
|
CouchPotato.database.view(Build.filtered_raw).should == ['success']
|
123
124
|
end
|
124
|
-
|
125
|
+
|
125
126
|
it "should pass view options declared in the view declaration to the query" do
|
126
|
-
view_query = mock 'view_query'
|
127
|
+
view_query = mock 'view_query'
|
127
128
|
CouchPotato::View::ViewQuery.stub!(:new).and_return(view_query)
|
128
129
|
view_query.should_receive(:query_view!).with(hash_including(:group => true)).and_return({'rows' => []})
|
129
130
|
CouchPotato.database.view(Build.with_view_options)
|
130
131
|
end
|
131
132
|
end
|
132
|
-
|
133
|
+
|
133
134
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,13 +6,14 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
6
6
|
require 'couch_potato'
|
7
7
|
|
8
8
|
CouchPotato::Config.database_name = 'couch_potato_test'
|
9
|
+
CouchPotato::Config.database_server = 'http://127.0.0.1:5984/'
|
9
10
|
|
10
11
|
|
11
12
|
class Comment
|
12
13
|
include CouchPotato::Persistence
|
13
|
-
|
14
|
+
|
14
15
|
validates_presence_of :title
|
15
|
-
|
16
|
+
|
16
17
|
property :title
|
17
18
|
belongs_to :commenter
|
18
19
|
end
|
data/spec/unit/database_spec.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
|
3
|
+
class DbTestUser
|
4
|
+
end
|
5
|
+
|
3
6
|
describe CouchPotato::Database, 'new' do
|
4
7
|
it "should raise an exception if the database doesn't exist" do
|
5
8
|
lambda {
|
@@ -15,4 +18,21 @@ describe CouchPotato::Database, 'load' do
|
|
15
18
|
db.load nil
|
16
19
|
}.should raise_error("Can't load a document without an id (got nil)")
|
17
20
|
end
|
21
|
+
|
22
|
+
it "should set itself on the model" do
|
23
|
+
user = mock 'user'
|
24
|
+
DbTestUser.stub!(:new).and_return(user)
|
25
|
+
db = CouchPotato::Database.new(stub('couchrest db', :info => nil, :get => {'ruby_class' => 'DbTestUser'}))
|
26
|
+
user.should_receive(:database=).with(db)
|
27
|
+
db.load '1'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe CouchPotato::Database, 'save_document' do
|
32
|
+
it "should set itself on the model for a new object before doing anything else" do
|
33
|
+
db = CouchPotato::Database.new(stub('couchrest db', :info => nil))
|
34
|
+
user = stub('user', :new? => true, :valid? => false).as_null_object
|
35
|
+
user.should_receive(:database=).with(db)
|
36
|
+
db.save_document user
|
37
|
+
end
|
18
38
|
end
|