couch_potato 0.6.0 → 0.7.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|