couch_potato 0.6.0 → 0.7.0.pre.1
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/.gitignore +0 -1
- data/.travis.yml +9 -1
- data/CHANGES.md +12 -0
- data/Gemfile.lock +29 -23
- data/MIT-LICENSE.txt +1 -1
- data/README.md +68 -38
- data/Rakefile +19 -60
- data/active_support_3_0.lock +4 -0
- data/active_support_3_1.lock +4 -0
- data/active_support_3_2 +4 -0
- data/active_support_3_2.lock +55 -0
- data/couch_potato.gemspec +1 -0
- data/lib/couch_potato/database.rb +55 -27
- data/lib/couch_potato/persistence/active_model_compliance.rb +6 -2
- data/lib/couch_potato/persistence/callbacks.rb +0 -1
- data/lib/couch_potato/persistence/dirty_attributes.rb +8 -17
- data/lib/couch_potato/persistence/json.rb +3 -2
- data/lib/couch_potato/persistence/properties.rb +19 -8
- data/lib/couch_potato/persistence/simple_property.rb +1 -3
- data/lib/couch_potato/persistence/type_caster.rb +7 -2
- data/lib/couch_potato/persistence.rb +27 -13
- data/lib/couch_potato/railtie.rb +1 -1
- data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +12 -12
- data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +8 -8
- data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +10 -10
- data/lib/couch_potato/rspec/matchers.rb +8 -7
- data/lib/couch_potato/validation.rb +15 -11
- data/lib/couch_potato/version.rb +1 -1
- data/lib/couch_potato/view/base_view_spec.rb +14 -12
- data/lib/couch_potato/view/model_view_spec.rb +134 -39
- data/lib/couch_potato/view/properties_view_spec.rb +11 -7
- data/lib/couch_potato/view/view_query.rb +10 -9
- data/lib/couch_potato.rb +25 -10
- data/spec/callbacks_spec.rb +88 -0
- data/spec/create_spec.rb +22 -4
- data/spec/default_property_spec.rb +6 -0
- data/spec/property_spec.rb +81 -44
- data/spec/railtie_spec.rb +20 -18
- data/spec/spec_helper.rb +19 -1
- data/spec/unit/active_model_compliance_spec.rb +17 -11
- data/spec/unit/attributes_spec.rb +33 -10
- data/spec/unit/base_view_spec_spec.rb +19 -5
- data/spec/unit/couch_potato_spec.rb +1 -20
- data/spec/unit/create_spec.rb +2 -2
- data/spec/unit/database_spec.rb +113 -47
- data/spec/unit/dirty_attributes_spec.rb +19 -19
- data/spec/unit/model_view_spec_spec.rb +78 -1
- data/spec/unit/properties_view_spec_spec.rb +18 -5
- data/spec/unit/validation_spec.rb +1 -55
- data/spec/unit/view_query_spec.rb +48 -26
- data/spec/{custom_view_spec.rb → views_spec.rb} +59 -17
- metadata +88 -90
- data/.rvmrc +0 -1
- data/lib/core_ext/object.rb +0 -5
- data/lib/core_ext/string.rb +0 -12
- data/lib/core_ext/symbol.rb +0 -15
- data/lib/couch_potato/validation/with_active_model.rb +0 -27
- data/lib/couch_potato/validation/with_validatable.rb +0 -41
@@ -1,12 +1,13 @@
|
|
1
|
+
require 'v8'
|
2
|
+
|
1
3
|
module CouchPotato
|
2
4
|
module RSpec
|
3
5
|
module RunJS
|
4
6
|
private
|
5
|
-
|
7
|
+
|
6
8
|
def run_js(js)
|
7
|
-
|
8
|
-
|
9
|
-
`js #{path}`
|
9
|
+
cxt = V8::Context.new
|
10
|
+
cxt.eval(js)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
@@ -22,15 +23,15 @@ module RSpec
|
|
22
23
|
def map(document)
|
23
24
|
CouchPotato::RSpec::MapToProxy.new(document)
|
24
25
|
end
|
25
|
-
|
26
|
+
|
26
27
|
def reduce(docs, keys)
|
27
28
|
CouchPotato::RSpec::ReduceToProxy.new(docs, keys)
|
28
29
|
end
|
29
|
-
|
30
|
+
|
30
31
|
def rereduce(docs, keys)
|
31
32
|
CouchPotato::RSpec::ReduceToProxy.new(docs, keys, true)
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def list(results)
|
35
36
|
CouchPotato::RSpec::ListAsProxy.new(results)
|
36
37
|
end
|
@@ -1,16 +1,20 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_model/translation'
|
3
|
+
|
1
4
|
module CouchPotato
|
2
5
|
module Validation
|
3
6
|
def self.included(base) #:nodoc:
|
4
|
-
|
5
|
-
|
6
|
-
require 'couch_potato/validation/with_validatable'
|
7
|
-
base.send :include, CouchPotato::Validation::WithValidatable
|
8
|
-
when :active_model
|
9
|
-
require 'couch_potato/validation/with_active_model'
|
10
|
-
base.send :include, CouchPotato::Validation::WithActiveModel
|
11
|
-
else
|
12
|
-
raise "Unknown CouchPotato::Config.validation_framework #{CouchPotato::Config.validation_framework.inspect}, options are :validatable or :active_model"
|
13
|
-
end
|
7
|
+
base.send :include, ::ActiveModel::Validations
|
8
|
+
base.send :include, ::ActiveModel::Validations::Callbacks
|
14
9
|
end
|
15
10
|
end
|
16
|
-
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# provide same interface to errors object as in Validatable
|
14
|
+
module ::ActiveModel
|
15
|
+
class Errors
|
16
|
+
def errors
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/couch_potato/version.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
module CouchPotato
|
2
2
|
module View
|
3
3
|
class BaseViewSpec
|
4
|
-
attr_reader :reduce_function, :list_name, :list_function, :design_document, :view_name,
|
4
|
+
attr_reader :reduce_function, :list_name, :list_function, :design_document, :view_name,
|
5
|
+
:view_parameters, :klass, :options, :language
|
5
6
|
private :klass, :options
|
6
7
|
|
7
8
|
def initialize(klass, view_name, options, view_parameters)
|
8
9
|
normalized_view_parameters = normalize_view_parameters view_parameters
|
9
|
-
|
10
|
+
|
10
11
|
@list_name = normalized_view_parameters.delete(:list) || options[:list]
|
11
|
-
|
12
|
+
@language = options[:language] || Config.default_language
|
13
|
+
|
12
14
|
assert_valid_view_parameters normalized_view_parameters
|
13
15
|
@klass = klass
|
14
16
|
@design_document = translate_to_design_doc_name(klass.to_s, view_name, @list_name)
|
15
17
|
@view_name = view_name
|
16
18
|
@options = options
|
17
|
-
|
19
|
+
|
18
20
|
@list_function = klass.lists(@list_name) if @list_name
|
19
21
|
@view_parameters = {}
|
20
22
|
[:group, :include_docs, :descending, :group_level, :limit].each do |key|
|
@@ -26,19 +28,19 @@ module CouchPotato
|
|
26
28
|
def process_results(results)
|
27
29
|
results
|
28
30
|
end
|
29
|
-
|
31
|
+
|
30
32
|
private
|
31
|
-
|
33
|
+
|
32
34
|
def normalize_view_parameters(params)
|
33
35
|
normalized_params = params.dup
|
34
36
|
hash = wrap_in_hash params
|
35
37
|
remove_nil_stale(replace_range_key(hash))
|
36
38
|
end
|
37
|
-
|
39
|
+
|
38
40
|
def remove_nil_stale(params)
|
39
41
|
params.reject{|name, value| name.to_s == 'stale' && value.nil?}
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
def wrap_in_hash(params)
|
43
45
|
if params.is_a?(Hash)
|
44
46
|
params
|
@@ -46,7 +48,7 @@ module CouchPotato
|
|
46
48
|
{:key => params}
|
47
49
|
end
|
48
50
|
end
|
49
|
-
|
51
|
+
|
50
52
|
def replace_range_key(params)
|
51
53
|
if((key = params[:key]).is_a?(Range))
|
52
54
|
params.delete :key
|
@@ -55,17 +57,17 @@ module CouchPotato
|
|
55
57
|
end
|
56
58
|
params
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
def assert_valid_view_parameters(params)
|
60
62
|
params.keys.each do |key|
|
61
63
|
raise ArgumentError.new("invalid view parameter: #{key}") unless valid_view_parameters.include?(key.to_s)
|
62
64
|
end
|
63
65
|
end
|
64
|
-
|
66
|
+
|
65
67
|
def valid_view_parameters
|
66
68
|
%w(key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end)
|
67
69
|
end
|
68
|
-
|
70
|
+
|
69
71
|
def translate_to_design_doc_name(klass_name, view_name, list_name)
|
70
72
|
klass_name = klass_name.dup
|
71
73
|
klass_name.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
@@ -5,11 +5,124 @@ module CouchPotato
|
|
5
5
|
#
|
6
6
|
# example:
|
7
7
|
# view :my_view, :key => :name
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# in addition you can pass in conditions as a javascript string
|
10
10
|
# view :my_view_only_completed, :key => :name, :conditions => 'doc.completed = true'
|
11
11
|
class ModelViewSpec < BaseViewSpec
|
12
|
-
|
12
|
+
class ErlangGenerator
|
13
|
+
def initialize(options, klass)
|
14
|
+
@options = options
|
15
|
+
@klass = klass
|
16
|
+
end
|
17
|
+
|
18
|
+
def map_function
|
19
|
+
raise NotImplementedError.new("conditions in Erlang not implemented") if @options[:conditions]
|
20
|
+
raise NotImplementedError.new("emit_value in Erlang not implemented") if @options[:emit_value]
|
21
|
+
<<-ERL
|
22
|
+
fun({Doc}) ->
|
23
|
+
case proplists:get_value(<<"#{JSON.create_id}">>, Doc) of
|
24
|
+
<<"#{@klass.name}">> ->
|
25
|
+
#{formatted_key},
|
26
|
+
Emit(#{composite_key_brackets emit_key}, 1);
|
27
|
+
_ ->
|
28
|
+
ok
|
29
|
+
end
|
30
|
+
end.
|
31
|
+
ERL
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def composite_key_brackets(key)
|
37
|
+
if key.include?(',')
|
38
|
+
"[#{key}]"
|
39
|
+
else
|
40
|
+
key
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def formatted_key(_key = nil)
|
45
|
+
_key ||= key
|
46
|
+
if _key.is_a? Array
|
47
|
+
parts = []
|
48
|
+
_key.each_with_index{|k, i|
|
49
|
+
parts << "Key_#{i} = proplists:get_value(<<\"#{k}\">>, Doc, null)"
|
50
|
+
}
|
51
|
+
parts.join(",\n")
|
52
|
+
else
|
53
|
+
"Key = proplists:get_value(<<\"#{_key}\">>, Doc, null)"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def emit_key
|
58
|
+
if key.is_a?(Array)
|
59
|
+
parts = []
|
60
|
+
key.each_with_index{|k, i|
|
61
|
+
parts << "Key_#{i}"
|
62
|
+
}
|
63
|
+
parts.join(', ')
|
64
|
+
else
|
65
|
+
'Key'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def key
|
70
|
+
@options[:key]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class JavascriptGenerator
|
75
|
+
def initialize(options, klass)
|
76
|
+
@options = options
|
77
|
+
@klass = klass
|
78
|
+
end
|
79
|
+
|
80
|
+
def map_body(&block)
|
81
|
+
<<-JS
|
82
|
+
function(doc) {
|
83
|
+
if(doc.#{JSON.create_id} && doc.#{JSON.create_id} == '#{@klass.name}'#{conditions}) {
|
84
|
+
#{yield}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
JS
|
88
|
+
end
|
89
|
+
|
90
|
+
def map_function
|
91
|
+
map_body do
|
92
|
+
"emit(#{formatted_key}, #{emit_value});"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def formatted_key(_key = nil)
|
97
|
+
_key ||= @options[:key]
|
98
|
+
if _key.is_a? Array
|
99
|
+
'[' + _key.map{|key_part| formatted_key(key_part)}.join(', ') + ']'
|
100
|
+
else
|
101
|
+
"doc['#{_key}']"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Allow custom emit values. Raise when the specified argument is not recognized
|
108
|
+
def emit_value
|
109
|
+
case @options[:emit_value]
|
110
|
+
when Symbol then "doc['#{@options[:emit_value]}']"
|
111
|
+
when String then @options[:emit_value]
|
112
|
+
when Numeric then @options[:emit_value]
|
113
|
+
when NilClass then 1
|
114
|
+
else
|
115
|
+
raise "The emit value specified is not recognized"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def conditions
|
120
|
+
" && (#{@options[:conditions]})" if @options[:conditions]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
delegate :map_function, :map_body, :formatted_key, :to => :generator
|
125
|
+
|
13
126
|
def view_parameters
|
14
127
|
_super = super
|
15
128
|
if _super[:reduce]
|
@@ -18,19 +131,11 @@ module CouchPotato
|
|
18
131
|
{:include_docs => true, :reduce => false}.merge(_super)
|
19
132
|
end
|
20
133
|
end
|
21
|
-
|
22
|
-
def map_function
|
23
|
-
map_body do
|
24
|
-
"emit(#{formatted_key(key)}, 1);"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
134
|
+
|
28
135
|
def reduce_function
|
29
|
-
"
|
30
|
-
return sum(values);
|
31
|
-
}"
|
136
|
+
"_sum"
|
32
137
|
end
|
33
|
-
|
138
|
+
|
34
139
|
def process_results(results)
|
35
140
|
if count?
|
36
141
|
results['rows'].first.try(:[], 'value') || 0
|
@@ -38,38 +143,28 @@ module CouchPotato
|
|
38
143
|
results['rows'].map { |row| row['doc'] || row['id'] }
|
39
144
|
end
|
40
145
|
end
|
41
|
-
|
146
|
+
|
42
147
|
private
|
43
|
-
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def conditions_js
|
54
|
-
" && (#{options[:conditions]})" if options[:conditions]
|
148
|
+
|
149
|
+
def generator
|
150
|
+
case language
|
151
|
+
when :javascript
|
152
|
+
@generator ||= JavascriptGenerator.new(@options, @klass)
|
153
|
+
when :erlang
|
154
|
+
@generator ||= ErlangGenerator.new(@options, @klass)
|
155
|
+
else
|
156
|
+
invalid_language
|
157
|
+
end
|
55
158
|
end
|
56
|
-
|
159
|
+
|
57
160
|
def count?
|
58
161
|
view_parameters[:reduce]
|
59
162
|
end
|
60
|
-
|
61
|
-
def
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
def formatted_key(key)
|
66
|
-
if key.is_a? Array
|
67
|
-
'[' + key.map{|attribute| formatted_key(attribute)}.join(', ') + ']'
|
68
|
-
else
|
69
|
-
"doc['#{key}']"
|
70
|
-
end
|
163
|
+
|
164
|
+
def invalid_language
|
165
|
+
raise "unsupported language #{language}"
|
71
166
|
end
|
72
|
-
|
167
|
+
|
73
168
|
end
|
74
169
|
end
|
75
170
|
end
|
@@ -7,10 +7,10 @@ module CouchPotato
|
|
7
7
|
class PropertiesViewSpec < ModelViewSpec
|
8
8
|
def map_function
|
9
9
|
map_body do
|
10
|
-
"emit(#{formatted_key
|
10
|
+
"emit(#{formatted_key}, #{properties_for_map(properties)});"
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def reduce_function
|
15
15
|
<<-JS
|
16
16
|
function(key, values, rereduce) {
|
@@ -22,23 +22,27 @@ module CouchPotato
|
|
22
22
|
}
|
23
23
|
JS
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def process_results(results)
|
27
27
|
results['rows'].map do |row|
|
28
28
|
klass.json_create row['value'].merge(:_id => row['id'])
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def view_parameters
|
33
33
|
{:include_docs => false}.merge(super)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
|
+
def language
|
37
|
+
:javascript
|
38
|
+
end
|
39
|
+
|
36
40
|
private
|
37
|
-
|
41
|
+
|
38
42
|
def properties
|
39
43
|
options[:properties]
|
40
44
|
end
|
41
|
-
|
45
|
+
|
42
46
|
def properties_for_map(properties)
|
43
47
|
'{' + properties.map{|p| "#{p}: doc.#{p}"}.join(', ') + '}'
|
44
48
|
end
|
@@ -2,12 +2,13 @@ module CouchPotato
|
|
2
2
|
module View
|
3
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
4
|
class ViewQuery
|
5
|
-
def initialize(couchrest_database, design_document_name, view, list = nil)
|
5
|
+
def initialize(couchrest_database, design_document_name, view, list = nil, language = :javascript)
|
6
6
|
@database = couchrest_database
|
7
7
|
@design_document_name = design_document_name
|
8
8
|
@view_name = view.keys[0]
|
9
9
|
@map_function = view.values[0][:map]
|
10
10
|
@reduce_function = view.values[0][:reduce]
|
11
|
+
@language = language
|
11
12
|
if list
|
12
13
|
@list_function = list.values[0]
|
13
14
|
@list_name = list.keys[0]
|
@@ -35,11 +36,11 @@ module CouchPotato
|
|
35
36
|
design_doc['views'][@view_name.to_s] = view_functions
|
36
37
|
if @list_function
|
37
38
|
design_doc['lists'] ||= {}
|
38
|
-
design_doc['lists'][@list_name.to_s] = @list_function
|
39
|
+
design_doc['lists'][@list_name.to_s] = @list_function
|
39
40
|
end
|
40
41
|
@database.save_doc(design_doc) if original_views != design_doc['views'] || original_lists != design_doc['lists']
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
def view_functions
|
44
45
|
if @reduce_function
|
45
46
|
{'map' => @map_function, 'reduce' => @reduce_function}
|
@@ -47,19 +48,19 @@ module CouchPotato
|
|
47
48
|
{'map' => @map_function}
|
48
49
|
end
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
def empty_design_document
|
52
|
-
{'views' => {}, 'lists' => {}, "_id" => "_design/#{@design_document_name}", "language" =>
|
53
|
+
{'views' => {}, 'lists' => {}, "_id" => "_design/#{@design_document_name}", "language" => @language.to_s}
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def view_has_been_updated?
|
56
57
|
updated_views[[@design_document_name, @view_name]]
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
def view_updated
|
60
61
|
updated_views[[@design_document_name, @view_name]] = true
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def updated_views
|
64
65
|
@@updated_views ||= {}
|
65
66
|
@@updated_views
|
@@ -79,4 +80,4 @@ module CouchPotato
|
|
79
80
|
|
80
81
|
end
|
81
82
|
end
|
82
|
-
end
|
83
|
+
end
|
data/lib/couch_potato.rb
CHANGED
@@ -7,9 +7,9 @@ require 'ostruct'
|
|
7
7
|
JSON.create_id = 'ruby_class'
|
8
8
|
|
9
9
|
module CouchPotato
|
10
|
-
Config = Struct.new(:database_name, :
|
11
|
-
Config.validation_framework = :active_model
|
10
|
+
Config = Struct.new(:database_name, :split_design_documents_per_view, :default_language).new
|
12
11
|
Config.split_design_documents_per_view = false
|
12
|
+
Config.default_language = :javascript
|
13
13
|
|
14
14
|
class NotFound < StandardError; end
|
15
15
|
|
@@ -29,25 +29,40 @@ module CouchPotato
|
|
29
29
|
@@__couchrest_database ||= CouchRest.database(full_url_to_database)
|
30
30
|
end
|
31
31
|
|
32
|
+
# Executes a block of code and yields a datbase with the given name.
|
33
|
+
#
|
34
|
+
# example:
|
35
|
+
# CouchPotato.with_database('couch_customer') do |couch|
|
36
|
+
# couch.save @customer
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
def self.with_database(database_name)
|
40
|
+
@@__databases ||= {}
|
41
|
+
@@__databases["#{database_name}"] = Database.new(couchrest_database_for_name(database_name)) unless @@__databases["#{database_name}"]
|
42
|
+
yield(@@__databases["#{database_name}"])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Creates a CouchRest-Database for directly accessing that functionality.
|
46
|
+
def self.couchrest_database_for_name(database_name)
|
47
|
+
CouchRest.database(full_url_to_database(database_name))
|
48
|
+
end
|
49
|
+
|
32
50
|
private
|
33
51
|
|
34
|
-
def self.full_url_to_database
|
35
|
-
raise('No Database configured. Set CouchPotato::Config.database_name') unless
|
36
|
-
if
|
37
|
-
|
52
|
+
def self.full_url_to_database(database_name=CouchPotato::Config.database_name)
|
53
|
+
raise('No Database configured. Set CouchPotato::Config.database_name') unless database_name
|
54
|
+
if database_name.match(%r{https?://})
|
55
|
+
database_name
|
38
56
|
else
|
39
|
-
"http://127.0.0.1:5984/#{
|
57
|
+
"http://127.0.0.1:5984/#{database_name}"
|
40
58
|
end
|
41
59
|
end
|
42
60
|
end
|
43
61
|
|
44
62
|
$LOAD_PATH << File.dirname(__FILE__)
|
45
63
|
|
46
|
-
require 'core_ext/object'
|
47
64
|
require 'core_ext/time'
|
48
65
|
require 'core_ext/date'
|
49
|
-
require 'core_ext/string'
|
50
|
-
require 'core_ext/symbol'
|
51
66
|
require 'couch_potato/validation'
|
52
67
|
require 'couch_potato/persistence'
|
53
68
|
require 'couch_potato/railtie' if defined?(Rails)
|
data/spec/callbacks_spec.rb
CHANGED
@@ -295,3 +295,91 @@ describe "validation callbacks" do
|
|
295
295
|
user.errors[:name].should == []
|
296
296
|
end
|
297
297
|
end
|
298
|
+
|
299
|
+
describe "validation callbacks and filter halt" do
|
300
|
+
class FilterValidationUpdateUser
|
301
|
+
include CouchPotato::Persistence
|
302
|
+
|
303
|
+
property :name
|
304
|
+
before_validation :check_name
|
305
|
+
before_validation_on_update :return_false_from_callback
|
306
|
+
|
307
|
+
def check_name
|
308
|
+
errors.add(:name, 'should be Paul') unless name == "Paul"
|
309
|
+
end
|
310
|
+
|
311
|
+
def return_false_from_callback
|
312
|
+
false
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
class FilterValidationCreateUser
|
317
|
+
include CouchPotato::Persistence
|
318
|
+
|
319
|
+
property :name
|
320
|
+
before_validation :check_name
|
321
|
+
before_validation_on_save :return_false_from_callback
|
322
|
+
before_validation_on_create :return_false_from_callback
|
323
|
+
|
324
|
+
def check_name
|
325
|
+
errors.add(:name, 'should be Paul') unless name == "Paul"
|
326
|
+
end
|
327
|
+
|
328
|
+
def return_false_from_callback
|
329
|
+
false
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
class FilterSaveUpdateUser
|
334
|
+
include CouchPotato::Persistence
|
335
|
+
|
336
|
+
property :name
|
337
|
+
before_update :return_false_from_callback
|
338
|
+
|
339
|
+
def return_false_from_callback
|
340
|
+
false
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
class FilterSaveCreateUser
|
345
|
+
include CouchPotato::Persistence
|
346
|
+
|
347
|
+
property :name
|
348
|
+
before_save :return_false_from_callback
|
349
|
+
before_create :return_false_from_callback
|
350
|
+
|
351
|
+
def return_false_from_callback
|
352
|
+
false
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
before(:each) do
|
357
|
+
recreate_db
|
358
|
+
@db = CouchPotato.database
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should keep error messages set in custom before_validation if an update filter returns false" do
|
362
|
+
@user = FilterValidationUpdateUser.new(:name => "Paul")
|
363
|
+
@db.save_document(@user).should == true
|
364
|
+
@user.name = 'Bert'
|
365
|
+
@db.save_document(@user).should == false
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should keep error messages set in custom before_validation if a create filter returns false" do
|
369
|
+
@user = FilterValidationCreateUser.new(:name => "Bert")
|
370
|
+
@db.save_document(@user).should == false
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should return false on saving a document when a before update filter returned false" do
|
374
|
+
@user = FilterSaveUpdateUser.new(:name => "Paul")
|
375
|
+
@db.save_document(@user).should == true
|
376
|
+
@user.name = 'Bert'
|
377
|
+
@db.save_document(@user).should == false
|
378
|
+
end
|
379
|
+
|
380
|
+
it "should return false on saving a document when a before save or before create filter returned false" do
|
381
|
+
@user = FilterSaveCreateUser.new(:name => "Bert")
|
382
|
+
@db.save_document(@user).should == false
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
data/spec/create_spec.rb
CHANGED
@@ -4,26 +4,27 @@ describe "create" do
|
|
4
4
|
before(:each) do
|
5
5
|
recreate_db
|
6
6
|
end
|
7
|
+
|
7
8
|
describe "succeeds" do
|
8
9
|
it "should store the class" do
|
9
10
|
@comment = Comment.new :title => 'my_title'
|
10
11
|
CouchPotato.database.save_document! @comment
|
11
12
|
CouchPotato.couchrest_database.get(@comment.id).send(JSON.create_id).should == 'Comment'
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
it "should persist a given created_at" do
|
15
16
|
@comment = Comment.new :created_at => Time.parse('2010-01-02 12:34:48 +0000'), :title => '-'
|
16
17
|
CouchPotato.database.save_document! @comment
|
17
18
|
CouchPotato.couchrest_database.get(@comment.id).created_at.should == Time.parse('2010-01-02 12:34:48 +0000')
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
it "should persist a given updated_at" do
|
21
22
|
@comment = Comment.new :updated_at => Time.parse('2010-01-02 12:34:48 +0000'), :title => '-'
|
22
23
|
CouchPotato.database.save_document! @comment
|
23
24
|
CouchPotato.couchrest_database.get(@comment.id).updated_at.should == Time.parse('2010-01-02 12:34:48 +0000')
|
24
25
|
end
|
25
26
|
end
|
26
|
-
|
27
|
+
|
27
28
|
describe "fails" do
|
28
29
|
it "should not store anything" do
|
29
30
|
@comment = Comment.new
|
@@ -31,5 +32,22 @@ describe "create" do
|
|
31
32
|
CouchPotato.couchrest_database.documents['rows'].should be_empty
|
32
33
|
end
|
33
34
|
end
|
34
|
-
end
|
35
35
|
|
36
|
+
describe "multi-db" do
|
37
|
+
TEST_DBS = ['comment_a', 'comment_b', 'comment_c']
|
38
|
+
|
39
|
+
before(:each) do
|
40
|
+
TEST_DBS.each { |db_name| CouchPotato.couchrest_database_for_name(db_name).recreate! }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should create documents in multiple dbs" do
|
44
|
+
TEST_DBS.each do |db_name|
|
45
|
+
@comment = Comment.new(:title => 'my_title')
|
46
|
+
CouchPotato.with_database(db_name) do |couch|
|
47
|
+
couch.save_document! @comment
|
48
|
+
end
|
49
|
+
CouchPotato.couchrest_database_for_name(db_name).get(@comment.id).send(JSON.create_id).should == 'Comment'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -5,6 +5,7 @@ class Test
|
|
5
5
|
|
6
6
|
property :test, :default => 'Test value'
|
7
7
|
property :complex, :default => [1, 2, 3]
|
8
|
+
property :false_value, :default => false
|
8
9
|
end
|
9
10
|
|
10
11
|
describe 'default properties' do
|
@@ -35,4 +36,9 @@ describe 'default properties' do
|
|
35
36
|
it "should not return the default value when the actual value is empty" do
|
36
37
|
t = Test.new(:complex => []).complex.should == []
|
37
38
|
end
|
39
|
+
|
40
|
+
it "uses the default value also if the default is false" do
|
41
|
+
t = Test.new
|
42
|
+
t.false_value.should == false
|
43
|
+
end
|
38
44
|
end
|