couch_potato 1.7.1 → 1.9.0

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 (56) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +38 -0
  3. data/.gitignore +3 -0
  4. data/CHANGES.md +169 -131
  5. data/Gemfile +4 -0
  6. data/README.md +55 -75
  7. data/Rakefile +11 -10
  8. data/couch_potato-rspec.gemspec +2 -1
  9. data/couch_potato.gemspec +8 -6
  10. data/gemfiles/active_support_5_0 +1 -5
  11. data/gemfiles/active_support_5_1 +7 -0
  12. data/gemfiles/active_support_5_2 +7 -0
  13. data/gemfiles/active_support_6_0 +7 -0
  14. data/gemfiles/active_support_6_1 +7 -0
  15. data/lib/couch_potato/database.rb +165 -70
  16. data/lib/couch_potato/persistence/dirty_attributes.rb +3 -21
  17. data/lib/couch_potato/persistence/magic_timestamps.rb +3 -3
  18. data/lib/couch_potato/persistence/properties.rb +15 -10
  19. data/lib/couch_potato/persistence/simple_property.rb +0 -4
  20. data/lib/couch_potato/persistence/type_caster.rb +9 -6
  21. data/lib/couch_potato/persistence.rb +0 -1
  22. data/lib/couch_potato/railtie.rb +6 -11
  23. data/lib/couch_potato/validation.rb +8 -0
  24. data/lib/couch_potato/version.rb +1 -1
  25. data/lib/couch_potato/view/base_view_spec.rb +8 -32
  26. data/lib/couch_potato/view/custom_views.rb +4 -3
  27. data/lib/couch_potato/view/flex_view_spec.rb +121 -0
  28. data/lib/couch_potato/view/view_parameters.rb +34 -0
  29. data/lib/couch_potato.rb +32 -9
  30. data/spec/callbacks_spec.rb +45 -19
  31. data/spec/conflict_handling_spec.rb +0 -1
  32. data/spec/property_spec.rb +2 -2
  33. data/spec/railtie_spec.rb +10 -0
  34. data/spec/spec_helper.rb +4 -3
  35. data/spec/unit/active_model_compliance_spec.rb +7 -3
  36. data/spec/unit/attributes_spec.rb +1 -1
  37. data/spec/unit/caching_spec.rb +105 -0
  38. data/spec/unit/couch_potato_spec.rb +70 -5
  39. data/spec/unit/create_spec.rb +5 -4
  40. data/spec/unit/database_spec.rb +235 -135
  41. data/spec/unit/dirty_attributes_spec.rb +5 -26
  42. data/spec/unit/flex_view_spec_spec.rb +17 -0
  43. data/spec/unit/model_view_spec_spec.rb +1 -1
  44. data/spec/unit/rspec_stub_db_spec.rb +31 -0
  45. data/spec/unit/validation_spec.rb +42 -2
  46. data/spec/views_spec.rb +214 -103
  47. data/vendor/pouchdb-collate/LICENSE +202 -0
  48. data/vendor/pouchdb-collate/pouchdb-collate.js +430 -0
  49. metadata +46 -42
  50. data/.ruby-version +0 -1
  51. data/.travis.yml +0 -21
  52. data/gemfiles/active_support_4_0 +0 -11
  53. data/gemfiles/active_support_4_1 +0 -11
  54. data/gemfiles/active_support_4_2 +0 -11
  55. data/lib/couch_potato/persistence/deep_dirty_attributes.rb +0 -180
  56. data/spec/unit/deep_dirty_attributes_spec.rb +0 -434
@@ -1,9 +1,10 @@
1
1
  require 'couch_potato/view/base_view_spec'
2
+ require 'couch_potato/view/flex_view_spec'
2
3
  require 'couch_potato/view/model_view_spec'
3
4
  require 'couch_potato/view/properties_view_spec'
4
5
  require 'couch_potato/view/custom_view_spec'
5
6
  require 'couch_potato/view/raw_view_spec'
6
-
7
+ require 'couch_potato/view/view_parameters'
7
8
 
8
9
  module CouchPotato
9
10
  module View
@@ -25,7 +26,7 @@ module CouchPotato
25
26
  def execute_view(view_name, view_parameters) #:nodoc:
26
27
  view_spec_class(views(view_name)[:type]).new(self, view_name, views(view_name), view_parameters)
27
28
  end
28
-
29
+
29
30
  # Declare a CouchDB view, for examples on how to use see the *ViewSpec classes in CouchPotato::View
30
31
  def view(view_name, options)
31
32
  view_name = view_name.to_s
@@ -42,7 +43,7 @@ module CouchPotato
42
43
  CouchPotato::View.const_get("#{name}ViewSpec")
43
44
  end
44
45
  end
45
-
46
+
46
47
  def _find_view(view) #:nodoc:
47
48
  (@views && @views[view]) || (superclass._find_view(view) if superclass.respond_to?(:_find_view))
48
49
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CouchPotato
4
+ module View
5
+ # A flexible view spec.
6
+ # It allows to either just define a key and option conditions like
7
+ # the model view spec or custom map/reduce functions.
8
+ # In addition, it returns a result object that allows convenient access
9
+ # to either the raw result, the keys, values, ids or docs. The result object can
10
+ # be extended with custom module, too.
11
+ # Examples:
12
+ # class Thing
13
+ # module ResultsExt
14
+ # def average_time
15
+ # keys.sum / keys.size # can access other result methods
16
+ # end
17
+ # end
18
+ # property :time
19
+ # view :by_time, type: :flex, key: :time, extend_results: ResultsExt
20
+ # view :by_custom_time, type: :flex,
21
+ # reduce: '_sum'
22
+ # map: <<~JS
23
+ # function(doc) {
24
+ # emit(doc.time, 1);
25
+ # }
26
+ # JS
27
+ # end
28
+ #
29
+ # usage:
30
+ # irb> result = db.view Thing.by_time
31
+ # irb> result.raw # raw CouchDB results
32
+ # irb> result.ids # ids of rows
33
+ # irb> result.keys # keys emitted in map function
34
+ # irb> result.values # values emitted in map function
35
+ # irb> result.average_time # custom method from ResultsExt module
36
+ # irb> db.view(Thing.by_time(include_docs: true)).docs # documents
37
+ # irb> db.view(Thing.by_time(reduce: true)).reduce_value # value of first row, i.e. result of the reduce function (without grouping)
38
+ class FlexViewSpec
39
+ attr_reader :klass
40
+
41
+ class Results
42
+ attr_accessor :database # set by database
43
+
44
+ def initialize(raw_results)
45
+ @raw_results = raw_results
46
+ end
47
+
48
+ def raw
49
+ @raw_results
50
+ end
51
+
52
+ def ids
53
+ rows.map { |row| row['id'] }
54
+ end
55
+
56
+ def keys
57
+ rows.map { |row| row['key'] }
58
+ end
59
+
60
+ def values
61
+ rows.map { |row| row['value'] }
62
+ end
63
+
64
+ def reduce_value
65
+ rows.dig(0, 'value')
66
+ end
67
+
68
+ # returns a count from a CouchDB reduce. returns 0 when the result
69
+ # set is empty (which would result in `nil` when calling #reduce_value).
70
+ # you still have to pass reduce=true to the view call.
71
+ def reduce_count
72
+ reduce_value || 0
73
+ end
74
+
75
+ def docs
76
+ rows.map do |row|
77
+ doc = row['doc']
78
+ doc.database = database if doc.respond_to?(:database=)
79
+ doc
80
+ end
81
+ end
82
+
83
+ def rows
84
+ @raw_results['rows']
85
+ end
86
+ end
87
+
88
+ def initialize(klass, view_name, options, view_parameters)
89
+ @extend_results_module = options[:extend_results]
90
+ @klass = klass
91
+ @view_name = view_name
92
+ @options = options.except(:extend_results)
93
+ @view_parameters = view_parameters
94
+ end
95
+
96
+ delegate :view_name, :view_parameters, :design_document, :map_function,
97
+ :reduce_function, :list_name, :lib, :language, to: :view_spec_delegate
98
+
99
+ def process_results(results)
100
+ results = Results.new(results)
101
+ results.extend @extend_results_module if @extend_results_module
102
+ results
103
+ end
104
+
105
+ private
106
+
107
+ def view_spec_delegate
108
+ unless @view_spec_delegate
109
+ view_spec_class = @options[:map] ? RawViewSpec : ModelViewSpec
110
+ @view_spec_delegate = view_spec_class.new(
111
+ @klass, @view_name, @options,
112
+ ViewParameters
113
+ .normalize_view_parameters(@view_parameters)
114
+ .reverse_merge(reduce: false, include_docs: false)
115
+ )
116
+ end
117
+ @view_spec_delegate
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,34 @@
1
+ module CouchPotato
2
+ module View
3
+ module ViewParameters
4
+ module_function
5
+
6
+ def normalize_view_parameters(params)
7
+ hash = wrap_in_hash params
8
+ remove_nil_stale(replace_range_key(hash))
9
+ end
10
+
11
+ def remove_nil_stale(params)
12
+ params.reject{|name, value| name.to_s == 'stale' && value.nil?}
13
+ end
14
+
15
+ def wrap_in_hash(params)
16
+ if params.is_a?(Hash)
17
+ params
18
+ else
19
+ {:key => params}
20
+ end
21
+ end
22
+
23
+ def replace_range_key(params)
24
+ if((key = params[:key]).is_a?(Range))
25
+ params.delete :key
26
+ params[:startkey] = key.first
27
+ params[:endkey] = key.last
28
+ end
29
+ params
30
+ end
31
+
32
+ end
33
+ end
34
+ end
data/lib/couch_potato.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'couchrest'
2
4
  require 'json'
3
5
 
@@ -8,15 +10,30 @@ CouchRest.decode_json_objects = true
8
10
 
9
11
  module CouchPotato
10
12
  Config = Struct.new(:database_host, :database_name, :digest_view_names,
11
- :split_design_documents_per_view, :default_language).new
13
+ :split_design_documents_per_view, :default_language, :additional_databases).new
12
14
  Config.split_design_documents_per_view = false
13
15
  Config.digest_view_names = false
14
16
  Config.default_language = :javascript
15
17
  Config.database_host = 'http://127.0.0.1:5984'
18
+ Config.additional_databases = {}
16
19
 
17
20
  class NotFound < StandardError; end
18
21
  class Conflict < StandardError; end
19
22
 
23
+ def self.configure(config)
24
+ if config.is_a?(String)
25
+ Config.database_name = config
26
+ else
27
+ config = config.stringify_keys
28
+ Config.database_name = config['database']
29
+ Config.database_host = config['database_host'] if config['database_host']
30
+ Config.additional_databases = config['additional_databases'].stringify_keys if config['additional_databases']
31
+ Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view']
32
+ Config.digest_view_names = config['digest_view_names'] if config['digest_view_names']
33
+ Config.default_language = config['default_language'] if config['default_language']
34
+ end
35
+ end
36
+
20
37
  # returns all the classes that include the CouchPotato::Persistence module
21
38
  def self.models
22
39
  @models ||= []
@@ -35,12 +52,17 @@ module CouchPotato
35
52
 
36
53
  # Returns a specific database instance
37
54
  def self.use(database_name)
55
+ resolved_database_name = resolve_database_name(database_name)
38
56
  Thread.current[:__couch_potato_databases] ||= {}
39
- Thread.current[:__couch_potato_databases][database_name] = Database.new(couchrest_database_for_name!(database_name)) unless Thread.current[:__couch_potato_databases][database_name]
40
- Thread.current[:__couch_potato_databases][database_name]
57
+ Thread.current[:__couch_potato_databases][resolved_database_name] ||= Database.new(couchrest_database_for_name!(resolved_database_name), name: database_name)
41
58
  end
42
59
 
43
- # Executes a block of code and yields a datbase with the given name.
60
+ # resolves a name to a database name/full url configured under additional databases
61
+ def self.resolve_database_name(database_name)
62
+ Config.additional_databases[database_name] || database_name
63
+ end
64
+
65
+ # Executes a block of code and yields a database with the given name.
44
66
  #
45
67
  # example:
46
68
  # CouchPotato.with_database('couch_customer') do |couch|
@@ -48,23 +70,24 @@ module CouchPotato
48
70
  # end
49
71
  #
50
72
  def self.with_database(database_name)
51
- Thread.current[:__couch_potato_databases] ||= {}
52
- Thread.current[:__couch_potato_databases][database_name] = Database.new(couchrest_database_for_name(database_name)) unless Thread.current[:__couch_potato_databases][database_name]
53
- yield Thread.current[:__couch_potato_databases][database_name]
73
+ yield use(database_name)
54
74
  end
55
75
 
56
76
  # Returns a CouchRest-Database for directly accessing that functionality.
57
77
  def self.couchrest_database_for_name(database_name)
58
- CouchRest.database(full_url_to_database(database_name, CouchPotato::Config.database_host))
78
+ Thread.current[:__couchrest_databases] ||= {}
79
+ Thread.current[:__couchrest_databases][database_name] ||= CouchRest.database(full_url_to_database(database_name, CouchPotato::Config.database_host))
59
80
  end
60
81
 
61
82
  # Creates a CouchRest-Database for directly accessing that functionality.
62
83
  def self.couchrest_database_for_name!(database_name)
63
- CouchRest.database!(full_url_to_database(database_name))
84
+ Thread.current[:__couchrest_databases] ||= {}
85
+ Thread.current[:__couchrest_databases][database_name] ||= CouchRest.database!(full_url_to_database(database_name))
64
86
  end
65
87
 
66
88
  def self.full_url_to_database(database_name = CouchPotato::Config.database_name, database_host = CouchPotato::Config.database_host)
67
89
  raise('No Database configured. Set CouchPotato::Config.database_name') unless database_name
90
+
68
91
  if database_name.match(%r{https?://})
69
92
  database_name
70
93
  else
@@ -382,13 +382,13 @@ describe "validation callbacks and filter halt" do
382
382
 
383
383
  property :name
384
384
  before_validation :check_name
385
- before_validation_on_update :return_false_from_callback
385
+ before_validation_on_update :abort_callback
386
386
 
387
387
  def check_name
388
388
  errors.add(:name, 'should be Paul') unless name == "Paul"
389
389
  end
390
390
 
391
- def return_false_from_callback
391
+ def abort_callback
392
392
  false
393
393
  end
394
394
  end
@@ -398,14 +398,14 @@ describe "validation callbacks and filter halt" do
398
398
 
399
399
  property :name
400
400
  before_validation :check_name
401
- before_validation_on_save :return_false_from_callback
402
- before_validation_on_create :return_false_from_callback
401
+ before_validation_on_save :abort_callback
402
+ before_validation_on_create :abort_callback
403
403
 
404
404
  def check_name
405
405
  errors.add(:name, 'should be Paul') unless name == "Paul"
406
406
  end
407
407
 
408
- def return_false_from_callback
408
+ def abort_callback
409
409
  false
410
410
  end
411
411
  end
@@ -414,9 +414,9 @@ describe "validation callbacks and filter halt" do
414
414
  include CouchPotato::Persistence
415
415
 
416
416
  property :name
417
- before_update :return_false_from_callback
417
+ before_update :abort_callback
418
418
 
419
- def return_false_from_callback
419
+ def abort_callback
420
420
  false
421
421
  end
422
422
  end
@@ -425,10 +425,10 @@ describe "validation callbacks and filter halt" do
425
425
  include CouchPotato::Persistence
426
426
 
427
427
  property :name
428
- before_save :return_false_from_callback
429
- before_create :return_false_from_callback
428
+ before_save :abort_callback
429
+ before_create :abort_callback
430
430
 
431
- def return_false_from_callback
431
+ def abort_callback
432
432
  false
433
433
  end
434
434
  end
@@ -450,16 +450,42 @@ describe "validation callbacks and filter halt" do
450
450
  expect(@db.save_document(@user)).to eq(false)
451
451
  end
452
452
 
453
- it "should return false on saving a document when a before update filter returned false" do
454
- @user = FilterSaveUpdateUser.new(:name => "Paul")
455
- expect(@db.save_document(@user)).to eq(true)
456
- @user.name = 'Bert'
457
- expect(@db.save_document(@user)).to eq(false)
458
- end
453
+ if ActiveModel.version.segments.first < 5
454
+ it "should return false on saving a document when a before update filter returned false" do
455
+ @user = FilterSaveUpdateUser.new(:name => "Paul")
456
+ expect(@db.save_document(@user)).to eq(true)
457
+ @user.name = 'Bert'
458
+ expect(@db.save_document(@user)).to eq(false)
459
+ end
459
460
 
460
- it "should return false on saving a document when a before save or before create filter returned false" do
461
- @user = FilterSaveCreateUser.new(:name => "Bert")
462
- expect(@db.save_document(@user)).to eq(false)
461
+ it "should return false on saving a document when a before save or before create filter returned false" do
462
+ @user = FilterSaveCreateUser.new(:name => "Bert")
463
+ expect(@db.save_document(@user)).to eq(false)
464
+ end
465
+ else
466
+ class FilterSaveCreateUser5 < FilterSaveCreateUser
467
+ def abort_callback
468
+ throw :abort
469
+ end
470
+ end
471
+
472
+ class FilterSaveUpdateUser5 < FilterSaveUpdateUser
473
+ def abort_callback
474
+ throw :abort
475
+ end
476
+ end
477
+
478
+ it "returns false on saving a document when a before update filter throws :abort" do
479
+ @user = FilterSaveUpdateUser5.new(:name => "Paul")
480
+ expect(@db.save_document(@user)).to eq(true)
481
+ @user.name = 'Bert'
482
+ expect(@db.save_document(@user)).to eq(false)
483
+ end
484
+
485
+ it "returns false on saving a document when a before save or before create filter throws :abort" do
486
+ @user = FilterSaveCreateUser5.new(:name => "Bert")
487
+ expect(@db.save_document(@user)).to eq(false)
488
+ end
463
489
  end
464
490
 
465
491
  end
@@ -14,7 +14,6 @@ describe 'conflict handling' do
14
14
 
15
15
  db.couchrest_database.save_doc measurement.reload._document.merge('value' => 2)
16
16
 
17
- measurement.is_dirty
18
17
  db.save measurement do |m|
19
18
  m.value += 1
20
19
  end
@@ -93,10 +93,10 @@ describe 'properties' do
93
93
 
94
94
  it "should persist a big decimal" do
95
95
  require 'bigdecimal'
96
- c = BigDecimalContainer.new :number => BigDecimal.new( '42.42' )
96
+ c = BigDecimalContainer.new :number => BigDecimal( '42.42' )
97
97
  CouchPotato.database.save_document! c
98
98
  c = CouchPotato.database.load_document c.id
99
- expect(c.number).to eq(BigDecimal.new( '42.42' ))
99
+ expect(c.number).to eq(BigDecimal( '42.42' ))
100
100
  end
101
101
 
102
102
  it "should persist a hash" do
data/spec/railtie_spec.rb CHANGED
@@ -83,6 +83,16 @@ describe "railtie" do
83
83
  end
84
84
  end
85
85
 
86
+ context 'yaml file contains additional_databases' do
87
+ it 'assigns additional_databases to config' do
88
+ allow(File).to receive_messages(:read => "test:\n database: test\n additional_databases:\n db2: test2")
89
+
90
+ expect(CouchPotato::Config).to receive(:additional_databases=).with('db2' => 'test2')
91
+
92
+ CouchPotato.rails_init
93
+ end
94
+ end
95
+
86
96
  it "should process the yml file with erb" do
87
97
  allow(File).to receive_messages(:read => "test: \n database: <%= 'db' %>")
88
98
 
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'rspec'
3
5
  require 'time'
4
6
  require 'active_support'
5
7
  require 'timecop'
6
8
 
7
- $:.unshift(File.dirname(__FILE__) + '/../lib')
9
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
8
10
 
9
11
  require 'couch_potato'
10
12
 
@@ -13,7 +15,7 @@ CouchPotato::Config.database_name = ENV['DATABASE'] || 'couch_potato_test'
13
15
  # silence deprecation warnings from ActiveModel as the Spec uses Errors#on
14
16
  begin
15
17
  ActiveSupport::Deprecation.silenced = true
16
- rescue
18
+ rescue StandardError
17
19
  # ignore errors, ActiveSupport is probably not installed
18
20
  end
19
21
 
@@ -64,5 +66,4 @@ RSpec::Matchers.define :eql_ignoring_indentation do |expected|
64
66
  def strip_indentation(string)
65
67
  string.gsub(/^\s+/m, '')
66
68
  end
67
-
68
69
  end
@@ -12,8 +12,12 @@ begin
12
12
  end
13
13
  end
14
14
 
15
- def assert_equal(one, other)
16
- expect(one).to equal(other)
15
+ def assert_equal(one, other, _message = nil)
16
+ expect(one).to eq(other)
17
+ end
18
+
19
+ def assert_respond_to(receiver, method)
20
+ expect(receiver).to respond_to(method)
17
21
  end
18
22
 
19
23
  class ActiveComment
@@ -62,7 +66,7 @@ begin
62
66
  describe "#errors" do
63
67
  it "should return a single error as array" do
64
68
  @model.valid?
65
- expect(@model.errors[:name]).to be_kind_of(Array)
69
+ expect(@model.errors[:name]).to eq(["can't be blank"])
66
70
  end
67
71
 
68
72
  it "should return multiple errors as array" do
@@ -11,7 +11,7 @@ class Plant
11
11
  property :leaf_count
12
12
  property :typed_leaf_count, type: Fixnum
13
13
  property :integer_something, type: Integer
14
- property :typed_leaf_size, type: Float
14
+ property :typed_leaf_size, type: Float
15
15
  property :branch, type: Branch
16
16
  end
17
17
 
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'database caching' do
6
+ let(:couchrest_db) do
7
+ double(:couchrest_db, info: double(:info),
8
+ root: '', get: double.as_null_object)
9
+ end
10
+
11
+ let(:db) do
12
+ CouchPotato::Database.new(couchrest_db).tap do |db|
13
+ db.cache = cache
14
+ end
15
+ end
16
+
17
+ let(:cache) do
18
+ {}
19
+ end
20
+
21
+ it 'gets an object from the cache the 2nd time via #load_documemt' do
22
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
23
+
24
+ db.load_document '1'
25
+ db.load_document '1'
26
+ end
27
+
28
+ it 'gets an object from the cache the 2nd time via #load' do
29
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
30
+
31
+ db.load '1'
32
+ db.load '1'
33
+ end
34
+
35
+ it 'gets an object from the cache the 2nd time via #load!' do
36
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
37
+
38
+ db.load! '1'
39
+ db.load! '1'
40
+ end
41
+
42
+ it 'returns the correct object' do
43
+ doc = double(:doc, 'database=': nil)
44
+ allow(couchrest_db).to receive_messages(get: doc)
45
+
46
+ db.load_document '1'
47
+ expect(db.load_document('1')).to eql(doc)
48
+ end
49
+
50
+ it 'does not cache bulk loads' do
51
+ allow(couchrest_db).to receive_messages(bulk_load: {'rows' => []})
52
+ expect(couchrest_db).to receive(:bulk_load).with(['1']).exactly(2).times
53
+
54
+ db.load_document ['1']
55
+ db.load_document ['1']
56
+ end
57
+
58
+ it 'clears the cache when destroying a document via #destroy_document' do
59
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
60
+
61
+ db.load_document '1'
62
+ db.destroy_document double.as_null_object
63
+ db.load_document '1'
64
+ end
65
+
66
+ it 'clears the cache when destroying a document via #destroy' do
67
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
68
+
69
+ db.load_document '1'
70
+ db.destroy double.as_null_object
71
+ db.load_document '1'
72
+ end
73
+
74
+ it 'clears the cache when updating a document via #save_document' do
75
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
76
+
77
+ db.load_document '1'
78
+ db.save_document double.as_null_object
79
+ db.load_document '1'
80
+ end
81
+
82
+ it 'clears the cache when updating a document via #save_document!' do
83
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
84
+
85
+ db.load_document '1'
86
+ db.save_document! double.as_null_object
87
+ db.load_document '1'
88
+ end
89
+
90
+ it 'clears the cache when updating a document via #save' do
91
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
92
+
93
+ db.load_document '1'
94
+ db.save double.as_null_object
95
+ db.load_document '1'
96
+ end
97
+
98
+ it 'clears the cache when updating a document via #save!' do
99
+ expect(couchrest_db).to receive(:get).with('1').exactly(2).times
100
+
101
+ db.load_document '1'
102
+ db.save! double.as_null_object
103
+ db.load_document '1'
104
+ end
105
+ end