couch_potato 0.2.12
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/CHANGES.md +15 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.md +295 -0
- data/VERSION.yml +4 -0
- data/init.rb +3 -0
- data/lib/core_ext/date.rb +10 -0
- data/lib/core_ext/object.rb +5 -0
- data/lib/core_ext/string.rb +19 -0
- data/lib/core_ext/symbol.rb +15 -0
- data/lib/core_ext/time.rb +11 -0
- data/lib/couch_potato.rb +40 -0
- data/lib/couch_potato/database.rb +106 -0
- data/lib/couch_potato/persistence.rb +98 -0
- data/lib/couch_potato/persistence/attachments.rb +31 -0
- data/lib/couch_potato/persistence/callbacks.rb +60 -0
- data/lib/couch_potato/persistence/dirty_attributes.rb +49 -0
- data/lib/couch_potato/persistence/ghost_attributes.rb +22 -0
- data/lib/couch_potato/persistence/json.rb +46 -0
- data/lib/couch_potato/persistence/magic_timestamps.rb +13 -0
- data/lib/couch_potato/persistence/properties.rb +52 -0
- data/lib/couch_potato/persistence/simple_property.rb +83 -0
- data/lib/couch_potato/persistence/validation.rb +18 -0
- data/lib/couch_potato/view/base_view_spec.rb +24 -0
- data/lib/couch_potato/view/custom_view_spec.rb +27 -0
- data/lib/couch_potato/view/custom_views.rb +44 -0
- data/lib/couch_potato/view/model_view_spec.rb +63 -0
- data/lib/couch_potato/view/properties_view_spec.rb +39 -0
- data/lib/couch_potato/view/raw_view_spec.rb +25 -0
- data/lib/couch_potato/view/view_query.rb +44 -0
- data/rails/init.rb +7 -0
- data/spec/attachments_spec.rb +23 -0
- data/spec/callbacks_spec.rb +271 -0
- data/spec/create_spec.rb +22 -0
- data/spec/custom_view_spec.rb +149 -0
- data/spec/default_property_spec.rb +34 -0
- data/spec/destroy_spec.rb +29 -0
- data/spec/fixtures/address.rb +9 -0
- data/spec/fixtures/person.rb +6 -0
- data/spec/property_spec.rb +101 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/unit/attributes_spec.rb +48 -0
- data/spec/unit/callbacks_spec.rb +33 -0
- data/spec/unit/couch_potato_spec.rb +20 -0
- data/spec/unit/create_spec.rb +58 -0
- data/spec/unit/customs_views_spec.rb +15 -0
- data/spec/unit/database_spec.rb +50 -0
- data/spec/unit/dirty_attributes_spec.rb +131 -0
- data/spec/unit/string_spec.rb +13 -0
- data/spec/unit/view_query_spec.rb +9 -0
- data/spec/update_spec.rb +40 -0
- metadata +153 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module Persistence
|
3
|
+
class SimpleProperty #:nodoc:
|
4
|
+
attr_accessor :name, :type
|
5
|
+
|
6
|
+
def initialize(owner_clazz, name, options = {})
|
7
|
+
self.name = name
|
8
|
+
self.type = options[:type]
|
9
|
+
|
10
|
+
define_accessors accessors_module_for(owner_clazz), name, options
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(object, json)
|
14
|
+
value = json[name.to_s] || json[name.to_sym]
|
15
|
+
typecast_value = if type
|
16
|
+
type.json_create value
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
object.send "#{name}=", typecast_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def dirty?(object)
|
24
|
+
object.send("#{name}_changed?")
|
25
|
+
end
|
26
|
+
|
27
|
+
def save(object)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroy(object)
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize(json, object)
|
36
|
+
json[name] = object.send name
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def accessors_module_for(clazz)
|
42
|
+
unless clazz.const_defined?('AccessorMethods')
|
43
|
+
accessors_module = clazz.const_set('AccessorMethods', Module.new)
|
44
|
+
clazz.send(:include, accessors_module)
|
45
|
+
end
|
46
|
+
clazz.const_get('AccessorMethods')
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_accessors(base, name, options)
|
50
|
+
base.class_eval do
|
51
|
+
attr_reader "#{name}_was"
|
52
|
+
|
53
|
+
define_method "#{name}" do
|
54
|
+
value = self.instance_variable_get("@#{name}")
|
55
|
+
if value.blank? && options[:default]
|
56
|
+
default = clone_attribute(options[:default])
|
57
|
+
self.instance_variable_set("@#{name}", default)
|
58
|
+
default
|
59
|
+
else
|
60
|
+
value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
define_method "#{name}=" do |value|
|
65
|
+
self.instance_variable_set("@#{name}", value)
|
66
|
+
end
|
67
|
+
|
68
|
+
define_method "#{name}?" do
|
69
|
+
!self.send(name).nil? && !self.send(name).try(:blank?)
|
70
|
+
end
|
71
|
+
|
72
|
+
define_method "#{name}_changed?" do
|
73
|
+
!self.instance_variable_get("@#{name}_not_changed") && self.send(name) != self.send("#{name}_was")
|
74
|
+
end
|
75
|
+
|
76
|
+
define_method "#{name}_not_changed" do
|
77
|
+
self.instance_variable_set("@#{name}_not_changed", true)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'validatable'
|
2
|
+
|
3
|
+
module CouchPotato
|
4
|
+
module Persistence
|
5
|
+
module Validation
|
6
|
+
def self.included(base)
|
7
|
+
base.send :include, Validatable
|
8
|
+
base.class_eval do
|
9
|
+
# Override the validate method to first run before_validation callback
|
10
|
+
def valid?
|
11
|
+
self.run_callbacks :before_validation
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
class BaseViewSpec
|
4
|
+
attr_reader :reduce_function, :design_document, :view_name, :view_parameters, :klass, :options
|
5
|
+
private :klass, :options
|
6
|
+
|
7
|
+
def initialize(klass, view_name, options, view_parameters)
|
8
|
+
@klass = klass
|
9
|
+
@design_document = klass.to_s.underscore
|
10
|
+
@view_name = view_name
|
11
|
+
@options = options
|
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)
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_results(results)
|
20
|
+
results
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
# a view for custom map/reduce functions that still returns model instances
|
4
|
+
#
|
5
|
+
# example:
|
6
|
+
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom, :reduce => nil
|
7
|
+
class CustomViewSpec < BaseViewSpec
|
8
|
+
def map_function
|
9
|
+
options[:map]
|
10
|
+
end
|
11
|
+
|
12
|
+
def reduce_function
|
13
|
+
options[:reduce]
|
14
|
+
end
|
15
|
+
|
16
|
+
def view_parameters
|
17
|
+
{:include_docs => options[:include_docs] || false}.merge(super)
|
18
|
+
end
|
19
|
+
|
20
|
+
def process_results(results)
|
21
|
+
results['rows'].map do |row|
|
22
|
+
klass.json_create row['doc'] || row['value'].merge(:_id => row['id'] || row['key'])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base_view_spec'
|
2
|
+
require File.dirname(__FILE__) + '/model_view_spec'
|
3
|
+
require File.dirname(__FILE__) + '/properties_view_spec'
|
4
|
+
require File.dirname(__FILE__) + '/custom_view_spec'
|
5
|
+
require File.dirname(__FILE__) + '/raw_view_spec'
|
6
|
+
|
7
|
+
|
8
|
+
module CouchPotato
|
9
|
+
module View
|
10
|
+
module CustomViews
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
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
|
+
|
26
|
+
def view(view_name, options)
|
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)
|
31
|
+
end
|
32
|
+
|
33
|
+
def view_spec_class(type)
|
34
|
+
if type && type.is_a?(Class)
|
35
|
+
type
|
36
|
+
else
|
37
|
+
name = type.nil? ? 'Model' : type.to_s.camelize
|
38
|
+
CouchPotato::View.const_get("#{name}ViewSpec")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
# A view to return model instances by searching its properties.
|
4
|
+
# If you pass reduce => true will count instead
|
5
|
+
#
|
6
|
+
# example:
|
7
|
+
# view :my_view, :key => :name
|
8
|
+
class ModelViewSpec < BaseViewSpec
|
9
|
+
|
10
|
+
def view_parameters
|
11
|
+
_super = super
|
12
|
+
if _super[:reduce]
|
13
|
+
_super
|
14
|
+
else
|
15
|
+
{:include_docs => true, :reduce => false}.merge(_super)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def map_function
|
20
|
+
"function(doc) {
|
21
|
+
if(doc.ruby_class && doc.ruby_class == '#{@klass.name}') {
|
22
|
+
emit(#{formatted_key(key)}, null);
|
23
|
+
}
|
24
|
+
}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def reduce_function
|
28
|
+
"function(key, values) {
|
29
|
+
return values.length;
|
30
|
+
}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def process_results(results)
|
34
|
+
if count?
|
35
|
+
results['rows'].first.try(:[], 'value') || 0
|
36
|
+
else
|
37
|
+
results['rows'].map do |row|
|
38
|
+
klass.json_create row['doc']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def count?
|
46
|
+
view_parameters[:reduce]
|
47
|
+
end
|
48
|
+
|
49
|
+
def key
|
50
|
+
options[:key]
|
51
|
+
end
|
52
|
+
|
53
|
+
def formatted_key(key)
|
54
|
+
if key.is_a? Array
|
55
|
+
'[' + key.map{|attribute| formatted_key(attribute)}.join(', ') + ']'
|
56
|
+
else
|
57
|
+
"doc['#{key}']"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
# A view to return model instances with only some properties poulated by searching its properties, e.g. for very large documents where you are only interested in some of their data
|
4
|
+
#
|
5
|
+
# example:
|
6
|
+
# view :my_view, :key => :name, :properties => [:name, :author], :type => :properties
|
7
|
+
class PropertiesViewSpec < ModelViewSpec
|
8
|
+
def map_function
|
9
|
+
"function(doc) {
|
10
|
+
if(doc.ruby_class && doc.ruby_class == '#{@klass.name}') {
|
11
|
+
emit(#{formatted_key(key)}, #{properties_for_map(properties)});
|
12
|
+
}
|
13
|
+
}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_results(results)
|
17
|
+
results['rows'].map do |row|
|
18
|
+
klass.json_create row['value'].merge(:_id => row['id'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def view_parameters
|
23
|
+
{:include_docs => false}.merge(super)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def properties
|
29
|
+
options[:properties]
|
30
|
+
end
|
31
|
+
|
32
|
+
def properties_for_map(properties)
|
33
|
+
'{' + properties.map{|p| "#{p}: doc.#{p}"}.join(', ') + '}'
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
# A view for custom map/reduce functions that returns the raw data fromcouchdb
|
4
|
+
#
|
5
|
+
# example:
|
6
|
+
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :reduce => nil
|
7
|
+
# optionally you can pass in a results filter which you can use to process the raw couchdb results before returning them
|
8
|
+
#
|
9
|
+
# example:
|
10
|
+
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :results_filter => lambda{|results| results['rows].map{|row| row['value']}}
|
11
|
+
class RawViewSpec < BaseViewSpec
|
12
|
+
def map_function
|
13
|
+
options[:map]
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_results(results)
|
17
|
+
options[:results_filter] ? options[:results_filter].call(results) : results
|
18
|
+
end
|
19
|
+
|
20
|
+
def reduce_function
|
21
|
+
options[:reduce]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module CouchPotato
|
2
|
+
module View
|
3
|
+
# Used to query views (and create them if they don't exist). Usually you won't have to use this class directly. Instead it is used internally by the CouchPotato::Database.view method.
|
4
|
+
class ViewQuery
|
5
|
+
def initialize(couchrest_database, design_document_name, view_name, map_function, reduce_function = nil)
|
6
|
+
@database = couchrest_database
|
7
|
+
@design_document_name = design_document_name
|
8
|
+
@view_name = view_name
|
9
|
+
@map_function = map_function
|
10
|
+
@reduce_function = reduce_function
|
11
|
+
end
|
12
|
+
|
13
|
+
def query_view!(parameters = {})
|
14
|
+
begin
|
15
|
+
query_view parameters
|
16
|
+
rescue RestClient::ResourceNotFound# => e
|
17
|
+
create_view
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def create_view
|
25
|
+
design_doc = @database.get "_design/#{@design_document_name}" rescue nil
|
26
|
+
design_doc ||= {'views' => {}, "_id" => "_design/#{@design_document_name}"}
|
27
|
+
design_doc['views'][@view_name.to_s] = {
|
28
|
+
'map' => @map_function,
|
29
|
+
'reduce' => @reduce_function
|
30
|
+
}
|
31
|
+
@database.save_doc(design_doc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def query_view(parameters)
|
35
|
+
@database.view view_url, parameters
|
36
|
+
end
|
37
|
+
|
38
|
+
def view_url
|
39
|
+
"#{@design_document_name}/#{@view_name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# this is for rails only
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/couch_potato'
|
4
|
+
|
5
|
+
CouchPotato::Config.database_name = YAML::load(File.read(Rails.root.to_s + '/config/couchdb.yml'))[RAILS_ENV]
|
6
|
+
|
7
|
+
RAILS_DEFAULT_LOGGER.info "** couch_potato: initialized from #{__FILE__}"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe CouchPotato, 'attachments' do
|
4
|
+
it "should persist an attachment" do
|
5
|
+
comment = Comment.new :title => 'nil'
|
6
|
+
comment._attachments = {'body' => {'data' => 'a useful comment', 'content_type' => 'text/plain'}}
|
7
|
+
CouchPotato.database.save! comment
|
8
|
+
CouchPotato.couchrest_database.fetch_attachment(comment.to_hash, 'body').to_s.should == 'a useful comment'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should give me information about the attachments of a document" do
|
12
|
+
comment = Comment.new :title => 'nil'
|
13
|
+
comment._attachments = {'body' => {'data' => 'a useful comment', 'content_type' => 'text/plain'}}
|
14
|
+
CouchPotato.database.save! comment
|
15
|
+
comment_reloaded = CouchPotato.database.load comment.id
|
16
|
+
comment_reloaded._attachments.should == {"body" => {"content_type" => "text/plain", "stub" => true, "length" => 16}}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have an empty array for a new object" do
|
20
|
+
Comment.new._attachments.should == {}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class CallbackRecorder
|
4
|
+
include CouchPotato::Persistence
|
5
|
+
|
6
|
+
property :required_property
|
7
|
+
|
8
|
+
validates_presence_of :required_property
|
9
|
+
|
10
|
+
[:before_validation, :before_validation_on_create,
|
11
|
+
:before_validation_on_save, :before_validation_on_update,
|
12
|
+
:before_save, :before_create, :before_create,
|
13
|
+
:after_save, :after_create, :after_create,
|
14
|
+
:before_update, :after_update,
|
15
|
+
:before_destroy, :after_destroy
|
16
|
+
].each do |callback|
|
17
|
+
define_method callback do
|
18
|
+
callbacks << callback
|
19
|
+
end
|
20
|
+
self.send callback, callback
|
21
|
+
end
|
22
|
+
|
23
|
+
view :all, :key => :required_property
|
24
|
+
|
25
|
+
attr_accessor :lambda_works
|
26
|
+
before_create lambda {|model| model.lambda_works = true }
|
27
|
+
|
28
|
+
def callbacks
|
29
|
+
@callbacks ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def method_callback_with_argument(db)
|
35
|
+
db.view CallbackRecorder.all
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "multiple callbacks at once" do
|
41
|
+
|
42
|
+
class Monkey
|
43
|
+
include CouchPotato::Persistence
|
44
|
+
attr_accessor :eaten_banana, :eaten_apple
|
45
|
+
|
46
|
+
before_create :eat_apple, :eat_banana
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def eat_banana
|
51
|
+
self.eaten_banana = true
|
52
|
+
end
|
53
|
+
|
54
|
+
def eat_apple
|
55
|
+
self.eaten_apple = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
it "should run all callback methods given to the callback method call" do
|
59
|
+
monkey = Monkey.new
|
60
|
+
CouchPotato.database.save_document! monkey
|
61
|
+
monkey.eaten_banana.should be_true
|
62
|
+
monkey.eaten_apple.should be_true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'create callbacks' do
|
67
|
+
|
68
|
+
before(:each) do
|
69
|
+
@recorder = CallbackRecorder.new
|
70
|
+
couchrest_database = stub 'couchrest_database', :save_doc => {'id' => '1', 'rev' => '2'}, :view => {'rows' => []}, :info => nil
|
71
|
+
@db = CouchPotato::Database.new(couchrest_database)
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "successful create" do
|
75
|
+
before(:each) do
|
76
|
+
@recorder.required_property = 1
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should call before_validation" do
|
80
|
+
@recorder.valid?
|
81
|
+
@recorder.callbacks.should include(:before_validation)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should call before_validation_on_create" do
|
85
|
+
@db.save_document! @recorder
|
86
|
+
@recorder.callbacks.should include(:before_validation_on_create)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should call before_validation_on_save" do
|
90
|
+
@db.save_document! @recorder
|
91
|
+
@recorder.callbacks.should include(:before_validation_on_save)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should call before_save" do
|
95
|
+
@db.save_document! @recorder
|
96
|
+
@recorder.callbacks.should include(:before_save)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should call after_save" do
|
100
|
+
@db.save_document! @recorder
|
101
|
+
@recorder.callbacks.should include(:after_save)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should call before_create" do
|
105
|
+
@db.save_document! @recorder
|
106
|
+
@recorder.callbacks.should include(:before_create)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should call after_create" do
|
110
|
+
@db.save_document! @recorder
|
111
|
+
@recorder.callbacks.should include(:after_create)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "failed create" do
|
117
|
+
|
118
|
+
it "should call before_validation" do
|
119
|
+
@recorder.valid?
|
120
|
+
@recorder.callbacks.should include(:before_validation)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should call before_validation_on_create" do
|
124
|
+
@db.save_document @recorder
|
125
|
+
@recorder.callbacks.should include(:before_validation_on_create)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should call before_validation_on_save" do
|
129
|
+
@db.save_document @recorder
|
130
|
+
@recorder.callbacks.should include(:before_validation_on_save)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not call before_save" do
|
134
|
+
@db.save_document @recorder
|
135
|
+
@recorder.callbacks.should_not include(:before_save)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should not call after_save" do
|
139
|
+
@db.save_document @recorder
|
140
|
+
@recorder.callbacks.should_not include(:after_save)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should not call before_create" do
|
144
|
+
@db.save_document @recorder
|
145
|
+
@recorder.callbacks.should_not include(:before_create)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should not call after_create" do
|
149
|
+
@db.save_document @recorder
|
150
|
+
@recorder.callbacks.should_not include(:after_create)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "update callbacks" do
|
156
|
+
|
157
|
+
before(:each) do
|
158
|
+
@recorder = CallbackRecorder.new :required_property => 1
|
159
|
+
|
160
|
+
couchrest_database = stub 'couchrest_database', :save_doc => {'id' => '1', 'rev' => '2'}, :view => {'rows' => []}, :info => nil
|
161
|
+
@db = CouchPotato::Database.new(couchrest_database)
|
162
|
+
@db.save_document! @recorder
|
163
|
+
|
164
|
+
@recorder.required_property = 2
|
165
|
+
@recorder.callbacks.clear
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "successful update" do
|
169
|
+
|
170
|
+
before(:each) do
|
171
|
+
@db.save_document! @recorder
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should call before_validation" do
|
175
|
+
@recorder.callbacks.should include(:before_validation)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should call before_validation_on_update" do
|
179
|
+
@recorder.callbacks.should include(:before_validation_on_update)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should call before_validation_on_save" do
|
183
|
+
@recorder.callbacks.should include(:before_validation_on_save)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should call before_save" do
|
187
|
+
@recorder.callbacks.should include(:before_save)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should call after_save" do
|
191
|
+
@recorder.callbacks.should include(:after_save)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should call before_update" do
|
195
|
+
@recorder.callbacks.should include(:before_update)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should call after_update" do
|
199
|
+
@recorder.callbacks.should include(:after_update)
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "failed update" do
|
205
|
+
|
206
|
+
before(:each) do
|
207
|
+
@recorder.required_property = nil
|
208
|
+
@db.save_document @recorder
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should call before_validation" do
|
212
|
+
@recorder.callbacks.should include(:before_validation)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should call before_validation_on_update" do
|
216
|
+
@recorder.callbacks.should include(:before_validation_on_update)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should call before_validation_on_save" do
|
220
|
+
@recorder.callbacks.should include(:before_validation_on_save)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should not call before_save" do
|
224
|
+
@recorder.callbacks.should_not include(:before_save)
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should not call after_save" do
|
228
|
+
@recorder.callbacks.should_not include(:after_save)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should not call before_update" do
|
232
|
+
@recorder.callbacks.should_not include(:before_update)
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should not call after_update" do
|
236
|
+
@recorder.callbacks.should_not include(:after_update)
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "destroy callbacks" do
|
244
|
+
|
245
|
+
before(:each) do
|
246
|
+
@recorder = CallbackRecorder.new :required_property => 1
|
247
|
+
couchrest_database = stub 'couchrest_database', :save_doc => {'id' => '1', 'rev' => '2'}, :delete_doc => nil, :view => {'rows' => []}, :info => nil
|
248
|
+
@db = CouchPotato::Database.new(couchrest_database)
|
249
|
+
@db.save_document! @recorder
|
250
|
+
|
251
|
+
@recorder.callbacks.clear
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should call before_destroy" do
|
255
|
+
@db.destroy_document @recorder
|
256
|
+
@recorder.callbacks.should include(:before_destroy)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should call after_destroy" do
|
260
|
+
@db.destroy_document @recorder
|
261
|
+
@recorder.callbacks.should include(:after_destroy)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "lambda callbacks" do
|
266
|
+
it "should run the lambda" do
|
267
|
+
recorder = CallbackRecorder.new
|
268
|
+
recorder.run_callbacks :before_create
|
269
|
+
recorder.lambda_works.should be_true
|
270
|
+
end
|
271
|
+
end
|