couch_potato 1.7.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
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