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.
Files changed (58) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +9 -1
  3. data/CHANGES.md +12 -0
  4. data/Gemfile.lock +29 -23
  5. data/MIT-LICENSE.txt +1 -1
  6. data/README.md +68 -38
  7. data/Rakefile +19 -60
  8. data/active_support_3_0.lock +4 -0
  9. data/active_support_3_1.lock +4 -0
  10. data/active_support_3_2 +4 -0
  11. data/active_support_3_2.lock +55 -0
  12. data/couch_potato.gemspec +1 -0
  13. data/lib/couch_potato/database.rb +55 -27
  14. data/lib/couch_potato/persistence/active_model_compliance.rb +6 -2
  15. data/lib/couch_potato/persistence/callbacks.rb +0 -1
  16. data/lib/couch_potato/persistence/dirty_attributes.rb +8 -17
  17. data/lib/couch_potato/persistence/json.rb +3 -2
  18. data/lib/couch_potato/persistence/properties.rb +19 -8
  19. data/lib/couch_potato/persistence/simple_property.rb +1 -3
  20. data/lib/couch_potato/persistence/type_caster.rb +7 -2
  21. data/lib/couch_potato/persistence.rb +27 -13
  22. data/lib/couch_potato/railtie.rb +1 -1
  23. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +12 -12
  24. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +8 -8
  25. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +10 -10
  26. data/lib/couch_potato/rspec/matchers.rb +8 -7
  27. data/lib/couch_potato/validation.rb +15 -11
  28. data/lib/couch_potato/version.rb +1 -1
  29. data/lib/couch_potato/view/base_view_spec.rb +14 -12
  30. data/lib/couch_potato/view/model_view_spec.rb +134 -39
  31. data/lib/couch_potato/view/properties_view_spec.rb +11 -7
  32. data/lib/couch_potato/view/view_query.rb +10 -9
  33. data/lib/couch_potato.rb +25 -10
  34. data/spec/callbacks_spec.rb +88 -0
  35. data/spec/create_spec.rb +22 -4
  36. data/spec/default_property_spec.rb +6 -0
  37. data/spec/property_spec.rb +81 -44
  38. data/spec/railtie_spec.rb +20 -18
  39. data/spec/spec_helper.rb +19 -1
  40. data/spec/unit/active_model_compliance_spec.rb +17 -11
  41. data/spec/unit/attributes_spec.rb +33 -10
  42. data/spec/unit/base_view_spec_spec.rb +19 -5
  43. data/spec/unit/couch_potato_spec.rb +1 -20
  44. data/spec/unit/create_spec.rb +2 -2
  45. data/spec/unit/database_spec.rb +113 -47
  46. data/spec/unit/dirty_attributes_spec.rb +19 -19
  47. data/spec/unit/model_view_spec_spec.rb +78 -1
  48. data/spec/unit/properties_view_spec_spec.rb +18 -5
  49. data/spec/unit/validation_spec.rb +1 -55
  50. data/spec/unit/view_query_spec.rb +48 -26
  51. data/spec/{custom_view_spec.rb → views_spec.rb} +59 -17
  52. metadata +88 -90
  53. data/.rvmrc +0 -1
  54. data/lib/core_ext/object.rb +0 -5
  55. data/lib/core_ext/string.rb +0 -12
  56. data/lib/core_ext/symbol.rb +0 -15
  57. data/lib/couch_potato/validation/with_active_model.rb +0 -27
  58. 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
- path = 'couch_potato_js_runner.js'
8
- File.open(path, 'w') {|f| f << js}
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
- case CouchPotato::Config.validation_framework
5
- when :validatable
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
@@ -1,3 +1,3 @@
1
1
  module CouchPotato
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0.pre.1"
3
3
  end
@@ -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, :view_parameters, :klass, :options
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
- "function(key, values) {
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 map_body(&block)
45
- "function(doc) {
46
- if(doc.#{JSON.create_id} && doc.#{JSON.create_id} == '#{@klass.name}'#{conditions_js}) {
47
- " + yield + "
48
- }
49
- }"
50
-
51
- end
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 key
62
- options[:key]
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(key)}, #{properties_for_map(properties)});"
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" => "javascript"}
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, :validation_framework, :split_design_documents_per_view).new
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 CouchPotato::Config.database_name
36
- if CouchPotato::Config.database_name.match(%r{https?://})
37
- CouchPotato::Config.database_name
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/#{CouchPotato::Config.database_name}"
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)
@@ -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