flipper-mongo 0.3.0 → 0.5.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.
- data/Gemfile +0 -2
- data/README.md +53 -3
- data/examples/basic.rb +27 -0
- data/examples/internals.rb +52 -0
- data/flipper-mongo.gemspec +9 -7
- data/lib/flipper-mongo.rb +0 -1
- data/lib/flipper/adapters/mongo.rb +88 -24
- data/lib/flipper/adapters/mongo/version.rb +1 -1
- data/spec/helper.rb +5 -17
- data/spec/mongo_spec.rb +15 -0
- metadata +26 -19
- data/lib/flipper/adapters/mongo/document.rb +0 -80
- data/lib/flipper/adapters/mongo_single_document.rb +0 -55
- data/lib/flipper/middleware/mongo_single_document_query_cache.rb +0 -36
- data/spec/flipper/adapters/mongo/document_spec.rb +0 -279
- data/spec/flipper/adapters/mongo_single_document_spec.rb +0 -95
- data/spec/flipper/adapters/mongo_spec.rb +0 -41
- data/spec/flipper/middleware/mongo_single_document_query_cache_spec.rb +0 -121
- data/spec/support/accessor_helpers.rb +0 -23
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Flipper Mongo
|
2
2
|
|
3
|
-
A mongo adapter for [Flipper](https://github.com/jnunemaker/flipper)
|
3
|
+
A [MongoDB](https://github.com/mongodb/mongo-ruby-driver) adapter for [Flipper](https://github.com/jnunemaker/flipper).
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -20,12 +20,62 @@ Or install it yourself with:
|
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
require 'flipper/adapters/mongo'
|
23
|
-
collection = Mongo::
|
24
|
-
adapter = Flipper::Adapters::Mongo.new(collection
|
23
|
+
collection = Mongo::MongoClient.new.db('testing')['flipper']
|
24
|
+
adapter = Flipper::Adapters::Mongo.new(collection)
|
25
25
|
flipper = Flipper.new(adapter)
|
26
26
|
# profit...
|
27
27
|
```
|
28
28
|
|
29
|
+
## Internals
|
30
|
+
|
31
|
+
Each feature is stored in a document, which means getting a feature is single query.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'flipper/adapters/mongo'
|
35
|
+
collection = Mongo::MongoClient.new.db('testing')['flipper']
|
36
|
+
adapter = Flipper::Adapters::Mongo.new(collection)
|
37
|
+
flipper = Flipper.new(adapter)
|
38
|
+
|
39
|
+
# Register a few groups.
|
40
|
+
Flipper.register(:admins) { |thing| thing.admin? }
|
41
|
+
Flipper.register(:early_access) { |thing| thing.early_access? }
|
42
|
+
|
43
|
+
# Create a user class that has flipper_id instance method.
|
44
|
+
User = Struct.new(:flipper_id)
|
45
|
+
|
46
|
+
flipper[:stats].enable
|
47
|
+
flipper[:stats].enable flipper.group(:admins)
|
48
|
+
flipper[:stats].enable flipper.group(:early_access)
|
49
|
+
flipper[:stats].enable User.new('25')
|
50
|
+
flipper[:stats].enable User.new('90')
|
51
|
+
flipper[:stats].enable User.new('180')
|
52
|
+
flipper[:stats].enable flipper.random(15)
|
53
|
+
flipper[:stats].enable flipper.actors(45)
|
54
|
+
|
55
|
+
flipper[:search].enable
|
56
|
+
|
57
|
+
puts 'all docs in collection'
|
58
|
+
pp collection.find.to_a
|
59
|
+
# all docs in collection
|
60
|
+
# [{"_id"=>"stats",
|
61
|
+
# "actors"=>["25", "90", "180"],
|
62
|
+
# "boolean"=>"true",
|
63
|
+
# "groups"=>["admins", "early_access"],
|
64
|
+
# "percentage_of_actors"=>"45",
|
65
|
+
# "percentage_of_random"=>"15"},
|
66
|
+
# {"_id"=>"flipper_features", "features"=>["stats", "search"]},
|
67
|
+
# {"_id"=>"search", "boolean"=>"true"}]
|
68
|
+
|
69
|
+
puts 'flipper get of feature'
|
70
|
+
pp adapter.get(flipper[:stats])
|
71
|
+
# flipper get of feature
|
72
|
+
# {:boolean=>"true",
|
73
|
+
# :groups=>#<Set: {"admins", "early_access"}>,
|
74
|
+
# :actors=>#<Set: {"25", "90", "180"}>,
|
75
|
+
# :percentage_of_actors=>"45",
|
76
|
+
# :percentage_of_random=>"15"}
|
77
|
+
```
|
78
|
+
|
29
79
|
## Contributing
|
30
80
|
|
31
81
|
1. Fork it
|
data/examples/basic.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
5
|
+
lib_path = root_path.join('lib')
|
6
|
+
$:.unshift(lib_path)
|
7
|
+
|
8
|
+
require 'flipper/adapters/mongo'
|
9
|
+
collection = Mongo::MongoClient.new.db('testing')['flipper']
|
10
|
+
adapter = Flipper::Adapters::Mongo.new(collection)
|
11
|
+
flipper = Flipper.new(adapter)
|
12
|
+
|
13
|
+
flipper[:stats].enable
|
14
|
+
|
15
|
+
if flipper[:stats].enabled?
|
16
|
+
puts "Enabled!"
|
17
|
+
else
|
18
|
+
puts "Disabled!"
|
19
|
+
end
|
20
|
+
|
21
|
+
flipper[:stats].disable
|
22
|
+
|
23
|
+
if flipper[:stats].enabled?
|
24
|
+
puts "Enabled!"
|
25
|
+
else
|
26
|
+
puts "Disabled!"
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'pathname'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
6
|
+
lib_path = root_path.join('lib')
|
7
|
+
$:.unshift(lib_path)
|
8
|
+
|
9
|
+
require 'flipper/adapters/mongo'
|
10
|
+
collection = Mongo::MongoClient.new.db('testing')['flipper']
|
11
|
+
adapter = Flipper::Adapters::Mongo.new(collection)
|
12
|
+
flipper = Flipper.new(adapter)
|
13
|
+
|
14
|
+
# Register a few groups.
|
15
|
+
Flipper.register(:admins) { |thing| thing.admin? }
|
16
|
+
Flipper.register(:early_access) { |thing| thing.early_access? }
|
17
|
+
|
18
|
+
# Create a user class that has flipper_id instance method.
|
19
|
+
User = Struct.new(:flipper_id)
|
20
|
+
|
21
|
+
flipper[:stats].enable
|
22
|
+
flipper[:stats].enable flipper.group(:admins)
|
23
|
+
flipper[:stats].enable flipper.group(:early_access)
|
24
|
+
flipper[:stats].enable User.new('25')
|
25
|
+
flipper[:stats].enable User.new('90')
|
26
|
+
flipper[:stats].enable User.new('180')
|
27
|
+
flipper[:stats].enable flipper.random(15)
|
28
|
+
flipper[:stats].enable flipper.actors(45)
|
29
|
+
|
30
|
+
flipper[:search].enable
|
31
|
+
|
32
|
+
puts 'all docs in collection'
|
33
|
+
pp collection.find.to_a
|
34
|
+
# all docs in collection
|
35
|
+
# [{"_id"=>"stats",
|
36
|
+
# "actors"=>["25", "90", "180"],
|
37
|
+
# "boolean"=>"true",
|
38
|
+
# "groups"=>["admins", "early_access"],
|
39
|
+
# "percentage_of_actors"=>"45",
|
40
|
+
# "percentage_of_random"=>"15"},
|
41
|
+
# {"_id"=>"flipper_features", "features"=>["stats", "search"]},
|
42
|
+
# {"_id"=>"search", "boolean"=>"true"}]
|
43
|
+
puts
|
44
|
+
|
45
|
+
puts 'flipper get of feature'
|
46
|
+
pp adapter.get(flipper[:stats])
|
47
|
+
# flipper get of feature
|
48
|
+
# {:boolean=>"true",
|
49
|
+
# :groups=>#<Set: {"admins", "early_access"}>,
|
50
|
+
# :actors=>#<Set: {"25", "90", "180"}>,
|
51
|
+
# :percentage_of_actors=>"45",
|
52
|
+
# :percentage_of_random=>"15"}
|
data/flipper-mongo.gemspec
CHANGED
@@ -2,17 +2,19 @@
|
|
2
2
|
require File.expand_path('../lib/flipper/adapters/mongo/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "flipper-mongo"
|
6
|
+
gem.version = Flipper::Adapters::Mongo::VERSION
|
5
7
|
gem.authors = ["John Nunemaker"]
|
6
8
|
gem.email = ["nunemaker@gmail.com"]
|
7
9
|
gem.description = %q{Mongo adapter for Flipper}
|
8
10
|
gem.summary = %q{Mongo adapter for Flipper}
|
9
11
|
gem.homepage = "http://jnunemaker.github.com/flipper-mongo"
|
10
|
-
|
11
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
-
gem.files = `git ls-files`.split("\n")
|
13
|
-
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
-
gem.name = "flipper-mongo"
|
15
12
|
gem.require_paths = ["lib"]
|
16
|
-
|
17
|
-
gem.
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
|
18
|
+
gem.add_dependency 'flipper', '~> 0.5.0'
|
19
|
+
gem.add_dependency 'mongo', '~> 1.8'
|
18
20
|
end
|
data/lib/flipper-mongo.rb
CHANGED
@@ -1,59 +1,123 @@
|
|
1
1
|
require 'set'
|
2
|
-
require '
|
2
|
+
require 'flipper'
|
3
3
|
require 'mongo'
|
4
4
|
|
5
5
|
module Flipper
|
6
6
|
module Adapters
|
7
7
|
class Mongo
|
8
|
-
|
8
|
+
include Flipper::Adapter
|
9
|
+
|
10
|
+
# Private: The key that stores the set of known features.
|
11
|
+
FeaturesKey = :flipper_features
|
12
|
+
|
13
|
+
# Public: The name of the adapter.
|
14
|
+
attr_reader :name
|
9
15
|
|
10
16
|
def initialize(collection)
|
11
17
|
@collection = collection
|
12
|
-
@
|
18
|
+
@name = :mongo
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
# Public: Gets the values for all gates for a given feature.
|
22
|
+
#
|
23
|
+
# Returns a Hash of Flipper::Gate#key => value.
|
24
|
+
def get(feature)
|
25
|
+
result = {}
|
26
|
+
doc = find(feature.key)
|
18
27
|
|
19
|
-
|
20
|
-
|
21
|
-
|
28
|
+
feature.gates.each do |gate|
|
29
|
+
result[gate.key] = case gate.data_type
|
30
|
+
when :boolean, :integer
|
31
|
+
doc[gate.key.to_s]
|
32
|
+
when :set
|
33
|
+
doc.fetch(gate.key.to_s) { Set.new }.to_set
|
34
|
+
else
|
35
|
+
unsupported_data_type gate.data_type
|
36
|
+
end
|
37
|
+
end
|
22
38
|
|
23
|
-
|
24
|
-
remove key
|
39
|
+
result
|
25
40
|
end
|
26
41
|
|
27
|
-
|
28
|
-
|
42
|
+
# Public: Enables a gate for a given thing.
|
43
|
+
#
|
44
|
+
# feature - The Flipper::Feature for the gate.
|
45
|
+
# gate - The Flipper::Gate to disable.
|
46
|
+
# thing - The Flipper::Type being disabled for the gate.
|
47
|
+
#
|
48
|
+
# Returns true.
|
49
|
+
def enable(feature, gate, thing)
|
50
|
+
case gate.data_type
|
51
|
+
when :boolean, :integer
|
52
|
+
update feature.key, '$set' => {
|
53
|
+
gate.key.to_s => thing.value.to_s,
|
54
|
+
}
|
55
|
+
when :set
|
56
|
+
update feature.key, '$addToSet' => {
|
57
|
+
gate.key.to_s => thing.value.to_s,
|
58
|
+
}
|
59
|
+
else
|
60
|
+
unsupported_data_type gate.data_type
|
61
|
+
end
|
62
|
+
|
63
|
+
true
|
29
64
|
end
|
30
65
|
|
31
|
-
|
32
|
-
|
66
|
+
# Public: Disables a gate for a given thing.
|
67
|
+
#
|
68
|
+
# feature - The Flipper::Feature for the gate.
|
69
|
+
# gate - The Flipper::Gate to disable.
|
70
|
+
# thing - The Flipper::Type being disabled for the gate.
|
71
|
+
#
|
72
|
+
# Returns true.
|
73
|
+
def disable(feature, gate, thing)
|
74
|
+
case gate.data_type
|
75
|
+
when :boolean
|
76
|
+
remove feature.key
|
77
|
+
when :integer
|
78
|
+
update feature.key, '$set' => {gate.key.to_s => thing.value.to_s}
|
79
|
+
when :set
|
80
|
+
update feature.key, '$pull' => {gate.key.to_s => thing.value.to_s}
|
81
|
+
else
|
82
|
+
unsupported_data_type gate.data_type
|
83
|
+
end
|
84
|
+
|
85
|
+
true
|
33
86
|
end
|
34
87
|
|
35
|
-
|
36
|
-
|
88
|
+
# Public: Adds a feature to the set of known features.
|
89
|
+
def add(feature)
|
90
|
+
update FeaturesKey, '$addToSet' => {'features' => feature.name.to_s}
|
91
|
+
true
|
37
92
|
end
|
38
93
|
|
39
|
-
|
94
|
+
# Public: The set of known features.
|
95
|
+
def features
|
96
|
+
find(FeaturesKey).fetch('features') { Set.new }.to_set
|
97
|
+
end
|
40
98
|
|
41
|
-
|
42
|
-
|
99
|
+
# Private
|
100
|
+
def unsupported_data_type(data_type)
|
101
|
+
raise "#{data_type} is not supported by this adapter"
|
102
|
+
end
|
43
103
|
|
44
|
-
|
45
|
-
|
46
|
-
|
104
|
+
# Private
|
105
|
+
def find(key)
|
106
|
+
@collection.find_one(criteria(key)) || {}
|
47
107
|
end
|
48
108
|
|
109
|
+
# Private
|
49
110
|
def update(key, updates)
|
50
|
-
|
111
|
+
options = {:upsert => true}
|
112
|
+
@collection.update criteria(key), updates, options
|
51
113
|
end
|
52
114
|
|
115
|
+
# Private
|
53
116
|
def remove(key)
|
54
117
|
@collection.remove criteria(key)
|
55
118
|
end
|
56
119
|
|
120
|
+
# Private
|
57
121
|
def criteria(key)
|
58
122
|
{:_id => key.to_s}
|
59
123
|
end
|
data/spec/helper.rb
CHANGED
@@ -1,29 +1,17 @@
|
|
1
1
|
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
2
|
|
3
|
-
require 'pathname'
|
4
|
-
require 'logger'
|
5
|
-
|
6
|
-
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
7
|
-
lib_path = root_path.join('lib')
|
8
|
-
log_path = root_path.join('log')
|
9
|
-
log_path.mkpath
|
10
|
-
|
11
|
-
require 'rubygems'
|
12
3
|
require 'bundler'
|
13
|
-
|
14
|
-
Bundler.require(:default, :test)
|
15
|
-
|
4
|
+
Bundler.setup :default
|
16
5
|
require 'flipper-mongo'
|
17
6
|
|
18
|
-
Logger.new(log_path.join('test.log'))
|
19
|
-
|
20
|
-
require 'support/accessor_helpers'
|
21
|
-
|
22
7
|
RSpec.configure do |config|
|
23
8
|
config.filter_run :focused => true
|
24
9
|
config.alias_example_to :fit, :focused => true
|
25
10
|
config.alias_example_to :xit, :pending => true
|
26
11
|
config.run_all_when_everything_filtered = true
|
12
|
+
config.fail_fast = true
|
27
13
|
|
28
|
-
config.
|
14
|
+
config.backtrace_clean_patterns = [
|
15
|
+
/rspec-(core|expectations)/,
|
16
|
+
]
|
29
17
|
end
|
data/spec/mongo_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/mongo'
|
3
|
+
require 'flipper/spec/shared_adapter_specs'
|
4
|
+
|
5
|
+
describe Flipper::Adapters::Mongo do
|
6
|
+
let(:collection) { Mongo::MongoClient.new.db('testing')['testing'] }
|
7
|
+
|
8
|
+
subject { described_class.new(collection) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
collection.remove
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like 'a flipper adapter'
|
15
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper-mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: flipper
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 0.5.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,23 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 0.5.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mongo
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.8'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.8'
|
30
46
|
description: Mongo adapter for Flipper
|
31
47
|
email:
|
32
48
|
- nunemaker@gmail.com
|
@@ -41,19 +57,14 @@ files:
|
|
41
57
|
- LICENSE
|
42
58
|
- README.md
|
43
59
|
- Rakefile
|
60
|
+
- examples/basic.rb
|
61
|
+
- examples/internals.rb
|
44
62
|
- flipper-mongo.gemspec
|
45
63
|
- lib/flipper-mongo.rb
|
46
64
|
- lib/flipper/adapters/mongo.rb
|
47
|
-
- lib/flipper/adapters/mongo/document.rb
|
48
65
|
- lib/flipper/adapters/mongo/version.rb
|
49
|
-
- lib/flipper/adapters/mongo_single_document.rb
|
50
|
-
- lib/flipper/middleware/mongo_single_document_query_cache.rb
|
51
|
-
- spec/flipper/adapters/mongo/document_spec.rb
|
52
|
-
- spec/flipper/adapters/mongo_single_document_spec.rb
|
53
|
-
- spec/flipper/adapters/mongo_spec.rb
|
54
|
-
- spec/flipper/middleware/mongo_single_document_query_cache_spec.rb
|
55
66
|
- spec/helper.rb
|
56
|
-
- spec/
|
67
|
+
- spec/mongo_spec.rb
|
57
68
|
homepage: http://jnunemaker.github.com/flipper-mongo
|
58
69
|
licenses: []
|
59
70
|
post_install_message:
|
@@ -68,7 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
79
|
version: '0'
|
69
80
|
segments:
|
70
81
|
- 0
|
71
|
-
hash:
|
82
|
+
hash: 1536259830562571499
|
72
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
84
|
none: false
|
74
85
|
requirements:
|
@@ -77,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
88
|
version: '0'
|
78
89
|
segments:
|
79
90
|
- 0
|
80
|
-
hash:
|
91
|
+
hash: 1536259830562571499
|
81
92
|
requirements: []
|
82
93
|
rubyforge_project:
|
83
94
|
rubygems_version: 1.8.23
|
@@ -85,9 +96,5 @@ signing_key:
|
|
85
96
|
specification_version: 3
|
86
97
|
summary: Mongo adapter for Flipper
|
87
98
|
test_files:
|
88
|
-
- spec/flipper/adapters/mongo/document_spec.rb
|
89
|
-
- spec/flipper/adapters/mongo_single_document_spec.rb
|
90
|
-
- spec/flipper/adapters/mongo_spec.rb
|
91
|
-
- spec/flipper/middleware/mongo_single_document_query_cache_spec.rb
|
92
99
|
- spec/helper.rb
|
93
|
-
- spec/
|
100
|
+
- spec/mongo_spec.rb
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'mongo'
|
3
|
-
|
4
|
-
module Flipper
|
5
|
-
module Adapters
|
6
|
-
class MongoSingleDocument
|
7
|
-
class Document
|
8
|
-
DefaultId = 'flipper'
|
9
|
-
|
10
|
-
def initialize(collection, options = {})
|
11
|
-
@collection = collection
|
12
|
-
@options = options
|
13
|
-
@id = @options[:id] || DefaultId
|
14
|
-
@source = @options.fetch(:source) { {} }
|
15
|
-
@criteria = {:_id => @id}
|
16
|
-
@mongo_options = {:safe => true, :upsert => true}
|
17
|
-
end
|
18
|
-
|
19
|
-
def read(key)
|
20
|
-
source[key.to_s]
|
21
|
-
end
|
22
|
-
|
23
|
-
def write(key, value)
|
24
|
-
value = value.to_s
|
25
|
-
@collection.update @criteria, {'$set' => {key.to_s => value}}, @mongo_options
|
26
|
-
@source[key.to_s] = value
|
27
|
-
end
|
28
|
-
|
29
|
-
def delete(key)
|
30
|
-
@collection.update @criteria, {'$unset' => {key.to_s => 1}}, @mongo_options
|
31
|
-
@source.delete key.to_s
|
32
|
-
end
|
33
|
-
|
34
|
-
def set_members(key)
|
35
|
-
members = source.fetch(key.to_s) { @source[key.to_s] = Set.new }
|
36
|
-
|
37
|
-
if members.is_a?(Array)
|
38
|
-
@source[key.to_s] = members.to_set
|
39
|
-
else
|
40
|
-
members
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def set_add(key, value)
|
45
|
-
value = value.to_s
|
46
|
-
@collection.update @criteria, {'$addToSet' => {key.to_s => value}}, @mongo_options
|
47
|
-
set_members(key.to_s).add(value)
|
48
|
-
end
|
49
|
-
|
50
|
-
def set_delete(key, value)
|
51
|
-
value = value.to_s
|
52
|
-
@collection.update @criteria, {'$pull' => {key.to_s => value}}, @mongo_options
|
53
|
-
set_members(key.to_s).delete(value)
|
54
|
-
end
|
55
|
-
|
56
|
-
def clear
|
57
|
-
@loaded = nil
|
58
|
-
@source.clear
|
59
|
-
end
|
60
|
-
|
61
|
-
def loaded?
|
62
|
-
@loaded == true
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def source
|
68
|
-
load unless loaded?
|
69
|
-
@source
|
70
|
-
end
|
71
|
-
|
72
|
-
def load
|
73
|
-
@loaded = true
|
74
|
-
@source.clear
|
75
|
-
@source.update @collection.find_one(@criteria) || {}
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'forwardable'
|
3
|
-
require 'mongo'
|
4
|
-
require 'flipper/adapters/mongo/document'
|
5
|
-
|
6
|
-
module Flipper
|
7
|
-
module Adapters
|
8
|
-
class MongoSingleDocument
|
9
|
-
extend Forwardable
|
10
|
-
|
11
|
-
def initialize(collection, options = {})
|
12
|
-
@collection = collection
|
13
|
-
@options = options
|
14
|
-
@document_cache = false
|
15
|
-
end
|
16
|
-
|
17
|
-
def_delegators :document, :read, :write, :delete, :set_members, :set_add, :set_delete
|
18
|
-
|
19
|
-
def using_document_cache?
|
20
|
-
@document_cache == true
|
21
|
-
end
|
22
|
-
|
23
|
-
def document_cache=(value)
|
24
|
-
reset_document_cache
|
25
|
-
@document_cache = value
|
26
|
-
end
|
27
|
-
|
28
|
-
def use_document_cache(&block)
|
29
|
-
original = @document_cache
|
30
|
-
@document_cache = true
|
31
|
-
yield
|
32
|
-
ensure
|
33
|
-
@document_cache = original
|
34
|
-
end
|
35
|
-
|
36
|
-
def reset_document_cache
|
37
|
-
@document = nil
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def document
|
43
|
-
if @document_cache == true
|
44
|
-
@document ||= fresh_document
|
45
|
-
else
|
46
|
-
fresh_document
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def fresh_document
|
51
|
-
Document.new(@collection, :id => @options[:id])
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Flipper
|
2
|
-
module Middleware
|
3
|
-
class MongoSingleDocumentQueryCache
|
4
|
-
class Body
|
5
|
-
def initialize(target, adapter, original)
|
6
|
-
@target = target
|
7
|
-
@adapter = adapter
|
8
|
-
@original = original
|
9
|
-
end
|
10
|
-
|
11
|
-
def each(&block)
|
12
|
-
@target.each(&block)
|
13
|
-
end
|
14
|
-
|
15
|
-
def close
|
16
|
-
@target.close if @target.respond_to?(:close)
|
17
|
-
ensure
|
18
|
-
@adapter.document_cache = @original
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize(app, adapter)
|
23
|
-
@app = app
|
24
|
-
@adapter = adapter
|
25
|
-
end
|
26
|
-
|
27
|
-
def call(env)
|
28
|
-
original = @adapter.using_document_cache?
|
29
|
-
@adapter.document_cache = true
|
30
|
-
|
31
|
-
status, headers, body = @app.call(env)
|
32
|
-
[status, headers, Body.new(body, @adapter, original)]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,279 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'flipper/adapters/mongo/document'
|
3
|
-
|
4
|
-
describe Flipper::Adapters::MongoSingleDocument::Document do
|
5
|
-
subject { described_class.new(collection, :id => id, :source => source) }
|
6
|
-
let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
|
7
|
-
let(:id) { described_class::DefaultId }
|
8
|
-
let(:source) { {} }
|
9
|
-
let(:criteria) { {:_id => id} }
|
10
|
-
let(:options) { {:safe => true, :upsert => true} }
|
11
|
-
|
12
|
-
def document
|
13
|
-
collection.find_one(criteria)
|
14
|
-
end
|
15
|
-
|
16
|
-
before do
|
17
|
-
collection.remove(criteria)
|
18
|
-
end
|
19
|
-
|
20
|
-
it "defaults id to flipper" do
|
21
|
-
described_class.new(collection).instance_variable_get("@id").should eq('flipper')
|
22
|
-
end
|
23
|
-
|
24
|
-
it "defaults id to flipper even if nil passed in for id" do
|
25
|
-
described_class.new(collection, :id => nil).instance_variable_get("@id").should eq('flipper')
|
26
|
-
end
|
27
|
-
|
28
|
-
describe "loading document" do
|
29
|
-
before do
|
30
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar', 'people' => ['1', '2', '3']}}, options)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "only happens once" do
|
34
|
-
collection.should_receive(:find_one).with(criteria).once.and_return({})
|
35
|
-
subject.read('foo')
|
36
|
-
subject.set_members('people')
|
37
|
-
end
|
38
|
-
|
39
|
-
it "happens again if document is cleared" do
|
40
|
-
collection.should_receive(:find_one).with(criteria)
|
41
|
-
subject.read('foo')
|
42
|
-
subject.set_members('people')
|
43
|
-
|
44
|
-
subject.clear
|
45
|
-
|
46
|
-
collection.should_receive(:find_one).with(criteria)
|
47
|
-
subject.read('foo')
|
48
|
-
subject.set_members('people')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe "#read" do
|
53
|
-
context "existing key" do
|
54
|
-
before do
|
55
|
-
source['baz'] = 'wick'
|
56
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar'}}, options)
|
57
|
-
@result = subject.read('foo')
|
58
|
-
end
|
59
|
-
|
60
|
-
it "returns value" do
|
61
|
-
@result.should eq('bar')
|
62
|
-
end
|
63
|
-
|
64
|
-
it "clears and loads source hash" do
|
65
|
-
source.should eq({
|
66
|
-
'_id' => id,
|
67
|
-
'foo' => 'bar',
|
68
|
-
})
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context "missing key" do
|
73
|
-
before do
|
74
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar'}}, options)
|
75
|
-
@result = subject.read('apple')
|
76
|
-
end
|
77
|
-
|
78
|
-
it "returns nil" do
|
79
|
-
@result.should be_nil
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
context "missing document" do
|
84
|
-
before do
|
85
|
-
@result = subject.read('foo')
|
86
|
-
end
|
87
|
-
|
88
|
-
it "returns nil" do
|
89
|
-
@result.should be_nil
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe "#write" do
|
95
|
-
context "existing key" do
|
96
|
-
before do
|
97
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar'}}, options)
|
98
|
-
subject.write('foo', 'new value')
|
99
|
-
end
|
100
|
-
|
101
|
-
it "sets key" do
|
102
|
-
document.fetch('foo').should eq('new value')
|
103
|
-
source.fetch('foo').should eq('new value')
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
context "missing key" do
|
108
|
-
before do
|
109
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar'}}, options)
|
110
|
-
subject.write('apple', 'orange')
|
111
|
-
end
|
112
|
-
|
113
|
-
it "sets key" do
|
114
|
-
document.fetch('apple').should eq('orange')
|
115
|
-
source.fetch('apple').should eq('orange')
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context "missing document" do
|
120
|
-
before do
|
121
|
-
subject.write('foo', 'bar')
|
122
|
-
end
|
123
|
-
|
124
|
-
it "creates document" do
|
125
|
-
document.should_not be_nil
|
126
|
-
end
|
127
|
-
|
128
|
-
it "sets key" do
|
129
|
-
document.fetch('foo').should eq('bar')
|
130
|
-
source.fetch('foo').should eq('bar')
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
describe "#delete" do
|
136
|
-
before do
|
137
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar', 'apple' => 'orange'}}, options)
|
138
|
-
@result = subject.delete('foo')
|
139
|
-
end
|
140
|
-
|
141
|
-
it "removes the key" do
|
142
|
-
document.key?('foo').should be_false
|
143
|
-
source.key?('foo').should be_false
|
144
|
-
end
|
145
|
-
|
146
|
-
it "does not remove other keys" do
|
147
|
-
document.fetch('apple').should eq('orange')
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
describe "#set_members" do
|
152
|
-
context "existing key" do
|
153
|
-
before do
|
154
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3'], 'foo' => 'bar'}}, options)
|
155
|
-
@result = subject.set_members('people')
|
156
|
-
end
|
157
|
-
|
158
|
-
it "returns set" do
|
159
|
-
@result.should eq(Set['1', '2', '3'])
|
160
|
-
end
|
161
|
-
|
162
|
-
it "loads source hash" do
|
163
|
-
source.should eq({
|
164
|
-
'_id' => id,
|
165
|
-
'people' => Set['1', '2', '3'],
|
166
|
-
'foo' => 'bar',
|
167
|
-
})
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
context "missing key" do
|
172
|
-
before do
|
173
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3']}}, options)
|
174
|
-
@result = subject.set_members('users')
|
175
|
-
end
|
176
|
-
|
177
|
-
it "returns empty set" do
|
178
|
-
@result.should eq(Set.new)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context "missing document" do
|
183
|
-
it "returns empty set" do
|
184
|
-
subject.set_members('people').should eq(Set.new)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
describe "#set_add" do
|
190
|
-
context "existing key" do
|
191
|
-
before do
|
192
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3']}}, options)
|
193
|
-
subject.set_add('people', 4)
|
194
|
-
end
|
195
|
-
|
196
|
-
it "adds value to set" do
|
197
|
-
document.fetch('people').should eq(['1', '2', '3', '4'])
|
198
|
-
source.fetch('people').should eq(Set['1', '2', '3', '4'])
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
context "missing key" do
|
203
|
-
before do
|
204
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3']}}, options)
|
205
|
-
subject.set_add('users', '1')
|
206
|
-
end
|
207
|
-
|
208
|
-
it "adds value to set" do
|
209
|
-
document.fetch('users').should eq(['1'])
|
210
|
-
source.fetch('users').should eq(Set['1'])
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
context "missing document" do
|
215
|
-
before do
|
216
|
-
subject.set_add('people', '1')
|
217
|
-
end
|
218
|
-
|
219
|
-
it "creates document" do
|
220
|
-
document.should_not be_nil
|
221
|
-
end
|
222
|
-
|
223
|
-
it "adds value to set" do
|
224
|
-
document.fetch('people').should eq(['1'])
|
225
|
-
source.fetch('people').should eq(Set['1'])
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
describe "#set_delete" do
|
231
|
-
context "existing key" do
|
232
|
-
before do
|
233
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3']}}, options)
|
234
|
-
subject.set_delete 'people', '3'
|
235
|
-
end
|
236
|
-
|
237
|
-
it "removes value to key" do
|
238
|
-
document.fetch('people').should eq(['1', '2'])
|
239
|
-
source.fetch('people').should eq(Set['1', '2'])
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
context "missing key" do
|
244
|
-
before do
|
245
|
-
collection.update(criteria, {'$set' => {'people' => ['1', '2', '3']}}, options)
|
246
|
-
end
|
247
|
-
|
248
|
-
it "does not error" do
|
249
|
-
expect { subject.set_delete 'foo', '1' }.to_not raise_error
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
context "missing document" do
|
254
|
-
it "does not error" do
|
255
|
-
expect { subject.set_delete 'foo', 1 }.to_not raise_error
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
describe "#clear" do
|
261
|
-
before do
|
262
|
-
collection.update(criteria, {'$set' => {'foo' => 'bar'}}, options)
|
263
|
-
subject.read('foo') # load the source hash
|
264
|
-
subject.clear
|
265
|
-
end
|
266
|
-
|
267
|
-
it "clears the source hash" do
|
268
|
-
source.should be_empty
|
269
|
-
end
|
270
|
-
|
271
|
-
it "does not remove the document" do
|
272
|
-
document.should_not be_empty
|
273
|
-
end
|
274
|
-
|
275
|
-
it "marks the document as not loaded" do
|
276
|
-
subject.loaded?.should be_false
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'flipper/adapters/mongo_single_document'
|
3
|
-
require 'flipper/spec/shared_adapter_specs'
|
4
|
-
|
5
|
-
describe Flipper::Adapters::MongoSingleDocument do
|
6
|
-
let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
|
7
|
-
let(:criteria) { {:_id => id} }
|
8
|
-
let(:id) { 'flipper' }
|
9
|
-
|
10
|
-
subject { described_class.new(collection, :id => id) }
|
11
|
-
|
12
|
-
before do
|
13
|
-
collection.remove(criteria)
|
14
|
-
end
|
15
|
-
|
16
|
-
def read_key(key)
|
17
|
-
if (doc = collection.find_one(criteria))
|
18
|
-
value = doc[key.to_s]
|
19
|
-
|
20
|
-
if value.is_a?(::Array)
|
21
|
-
value = value.to_set
|
22
|
-
end
|
23
|
-
|
24
|
-
value
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def write_key(key, value)
|
29
|
-
value = if value.is_a?(::Set)
|
30
|
-
value.to_a.map(&:to_s)
|
31
|
-
else
|
32
|
-
value.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
options = {:upsert => true}
|
36
|
-
updates = {'$set' => {key.to_s => value}}
|
37
|
-
collection.update criteria, updates, options
|
38
|
-
end
|
39
|
-
|
40
|
-
context "with cache" do
|
41
|
-
before do
|
42
|
-
subject.document_cache = true
|
43
|
-
end
|
44
|
-
|
45
|
-
it_should_behave_like 'a flipper adapter'
|
46
|
-
|
47
|
-
it "should only query mongo once until reloaded" do
|
48
|
-
collection.should_receive(:find_one).with(criteria).once.and_return({})
|
49
|
-
subject.read('foo')
|
50
|
-
subject.read('foo')
|
51
|
-
subject.read('foo')
|
52
|
-
subject.set_members('users')
|
53
|
-
|
54
|
-
subject.reset_document_cache
|
55
|
-
|
56
|
-
collection.should_receive(:find_one).with(criteria).once.and_return({})
|
57
|
-
subject.read('foo')
|
58
|
-
subject.read('foo')
|
59
|
-
subject.set_members('users')
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context "without cache" do
|
64
|
-
before do
|
65
|
-
subject.document_cache = false
|
66
|
-
end
|
67
|
-
|
68
|
-
it_should_behave_like 'a flipper adapter'
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "#use_document_cache" do
|
72
|
-
it "turns cache on for block and restores to original after block" do
|
73
|
-
subject.using_document_cache?.should be_false
|
74
|
-
subject.use_document_cache do
|
75
|
-
subject.using_document_cache?.should be_true
|
76
|
-
end
|
77
|
-
subject.using_document_cache?.should be_false
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe "#document_cache=" do
|
82
|
-
it "sets document cache" do
|
83
|
-
subject.document_cache = true
|
84
|
-
subject.using_document_cache?.should be_true
|
85
|
-
|
86
|
-
subject.document_cache = false
|
87
|
-
subject.using_document_cache?.should be_false
|
88
|
-
end
|
89
|
-
|
90
|
-
it "resets cached document" do
|
91
|
-
subject.should_receive(:reset_document_cache)
|
92
|
-
subject.document_cache = true
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'flipper/adapters/mongo'
|
3
|
-
require 'flipper/spec/shared_adapter_specs'
|
4
|
-
|
5
|
-
describe Flipper::Adapters::Mongo do
|
6
|
-
let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
|
7
|
-
let(:id) { 'flipper' }
|
8
|
-
|
9
|
-
subject { described_class.new(collection) }
|
10
|
-
|
11
|
-
before do
|
12
|
-
collection.remove
|
13
|
-
end
|
14
|
-
|
15
|
-
def read_key(key)
|
16
|
-
if (doc = collection.find_one(:_id => key.to_s))
|
17
|
-
value = doc['v']
|
18
|
-
|
19
|
-
if value.is_a?(::Array)
|
20
|
-
value = value.to_set
|
21
|
-
end
|
22
|
-
|
23
|
-
value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def write_key(key, value)
|
28
|
-
value = if value.is_a?(::Set)
|
29
|
-
value.to_a.map(&:to_s)
|
30
|
-
else
|
31
|
-
value.to_s
|
32
|
-
end
|
33
|
-
|
34
|
-
criteria = {:_id => key.to_s}
|
35
|
-
updates = {'$set' => {'v' => value}}
|
36
|
-
options = {:upsert => true}
|
37
|
-
collection.update criteria, updates, options
|
38
|
-
end
|
39
|
-
|
40
|
-
it_should_behave_like 'a flipper adapter'
|
41
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'rack/test'
|
3
|
-
require 'flipper/middleware/mongo_single_document_query_cache'
|
4
|
-
|
5
|
-
describe Flipper::Middleware::MongoSingleDocumentQueryCache do
|
6
|
-
include Rack::Test::Methods
|
7
|
-
|
8
|
-
let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
|
9
|
-
let(:adapter) { Flipper::Adapters::MongoSingleDocument.new(collection) }
|
10
|
-
let(:flipper) { Flipper.new(adapter) }
|
11
|
-
|
12
|
-
class Enum < Struct.new(:iter)
|
13
|
-
def each(&b)
|
14
|
-
iter.call(&b)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
let(:app) {
|
19
|
-
# ensure scoped for builder block, annoying...
|
20
|
-
instance = adapter
|
21
|
-
middleware = described_class
|
22
|
-
|
23
|
-
Rack::Builder.new do
|
24
|
-
use middleware, instance
|
25
|
-
|
26
|
-
map "/" do
|
27
|
-
run lambda {|env| [200, {}, []] }
|
28
|
-
end
|
29
|
-
|
30
|
-
map "/fail" do
|
31
|
-
run lambda {|env| raise "FAIL!" }
|
32
|
-
end
|
33
|
-
end.to_app
|
34
|
-
}
|
35
|
-
|
36
|
-
it "delegates" do
|
37
|
-
called = false
|
38
|
-
app = lambda { |env|
|
39
|
-
called = true
|
40
|
-
[200, {}, nil]
|
41
|
-
}
|
42
|
-
middleware = described_class.new app, adapter
|
43
|
-
middleware.call({})
|
44
|
-
called.should be_true
|
45
|
-
end
|
46
|
-
|
47
|
-
it "enables document cache during delegation" do
|
48
|
-
app = lambda { |env|
|
49
|
-
adapter.using_document_cache?.should be_true
|
50
|
-
[200, {}, nil]
|
51
|
-
}
|
52
|
-
middleware = described_class.new app, adapter
|
53
|
-
middleware.call({})
|
54
|
-
end
|
55
|
-
|
56
|
-
it "enables document cache for body each" do
|
57
|
-
app = lambda { |env|
|
58
|
-
[200, {}, Enum.new(lambda { |&b|
|
59
|
-
adapter.using_document_cache?.should be_true
|
60
|
-
b.call "hello"
|
61
|
-
})]
|
62
|
-
}
|
63
|
-
middleware = described_class.new app, adapter
|
64
|
-
body = middleware.call({}).last
|
65
|
-
body.each { |x| x.should eql('hello') }
|
66
|
-
end
|
67
|
-
|
68
|
-
it "disables document cache after body close" do
|
69
|
-
app = lambda { |env| [200, {}, []] }
|
70
|
-
middleware = described_class.new app, adapter
|
71
|
-
body = middleware.call({}).last
|
72
|
-
|
73
|
-
adapter.using_document_cache?.should be_true
|
74
|
-
body.close
|
75
|
-
adapter.using_document_cache?.should be_false
|
76
|
-
end
|
77
|
-
|
78
|
-
it "clears document cache after body close" do
|
79
|
-
app = lambda { |env| [200, {}, []] }
|
80
|
-
middleware = described_class.new app, adapter
|
81
|
-
body = middleware.call({}).last
|
82
|
-
adapter.write('hello', 'world')
|
83
|
-
|
84
|
-
adapter.instance_variable_get("@document").should_not be_nil
|
85
|
-
body.close
|
86
|
-
adapter.instance_variable_get("@document").should be_nil
|
87
|
-
end
|
88
|
-
|
89
|
-
it "really does cache" do
|
90
|
-
flipper[:stats].enable
|
91
|
-
|
92
|
-
collection.should_receive(:find_one).once.and_return({})
|
93
|
-
|
94
|
-
app = lambda { |env|
|
95
|
-
flipper[:stats].enabled?
|
96
|
-
flipper[:stats].enabled?
|
97
|
-
flipper[:stats].enabled?
|
98
|
-
flipper[:stats].enabled?
|
99
|
-
flipper[:stats].enabled?
|
100
|
-
flipper[:stats].enabled?
|
101
|
-
|
102
|
-
[200, {}, []]
|
103
|
-
}
|
104
|
-
middleware = described_class.new app, adapter
|
105
|
-
middleware.call({})
|
106
|
-
end
|
107
|
-
|
108
|
-
context "with a successful request" do
|
109
|
-
it "clears the document cache" do
|
110
|
-
adapter.should_receive(:reset_document_cache).twice
|
111
|
-
get '/'
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context "when the request raises an error" do
|
116
|
-
it "clears the document cache" do
|
117
|
-
adapter.should_receive(:reset_document_cache).once
|
118
|
-
get '/fail' rescue nil
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module AccessorHelpers
|
2
|
-
def read_key(key)
|
3
|
-
if (doc = collection.find_one(criteria))
|
4
|
-
value = doc[key]
|
5
|
-
|
6
|
-
if value.is_a?(::Array)
|
7
|
-
value = value.to_set
|
8
|
-
end
|
9
|
-
|
10
|
-
value
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def write_key(key, value)
|
15
|
-
if value.is_a?(::Set)
|
16
|
-
value = value.to_a
|
17
|
-
end
|
18
|
-
|
19
|
-
options = {:upsert => true}
|
20
|
-
updates = {'$set' => {key => value}}
|
21
|
-
collection.update criteria, updates, options
|
22
|
-
end
|
23
|
-
end
|