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.
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