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