flipper-mongo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ log
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'rake'
5
+
6
+ group(:guard) do
7
+ gem 'guard'
8
+ gem 'guard-rspec'
9
+ gem 'guard-bundler'
10
+ gem 'growl'
11
+ end
12
+
13
+ group(:test) do
14
+ gem 'rspec'
15
+ gem 'timecop'
16
+ gem 'bson_ext'
17
+ gem 'mongo'
18
+ end
19
+
data/Guardfile ADDED
@@ -0,0 +1,18 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ watch(/^.+\.gemspec/)
7
+ end
8
+
9
+ rspec_options = {
10
+ :all_after_pass => false,
11
+ :all_on_start => false,
12
+ }
13
+
14
+ guard 'rspec', rspec_options do
15
+ watch(%r{^spec/.+_spec\.rb$}) { "spec" }
16
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
17
+ watch('spec/helper.rb') { "spec" }
18
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 John Nunemaker
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Flipper Mongo
2
+
3
+ A mongo adapter for [Flipper](https://github.com/jnunemaker/flipper), the feature flipping gems.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'flipper-mongo'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself with:
16
+
17
+ $ gem install flipper-mongo
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'flipper/adapters/mongo'
23
+ collection = Mongo::Connection.new.db('testing')['flipper']
24
+ adapter = Flipper::Adapters::Mongo.new(collection, 'features')
25
+ flipper = Flipper.new(adapter)
26
+ # profit...
27
+ ```
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/flipper/adapters/mongo/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["John Nunemaker"]
6
+ gem.email = ["nunemaker@gmail.com"]
7
+ gem.description = %q{Mongo adapter for Flipper}
8
+ gem.summary = %q{Mongo adapter for Flipper}
9
+ 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
+ gem.require_paths = ["lib"]
16
+ gem.version = Flipper::Adapters::Mongo::VERSION
17
+ gem.add_dependency 'flipper', '~> 0.1.0'
18
+ end
@@ -0,0 +1 @@
1
+ require 'flipper/adapters/mongo'
@@ -0,0 +1,65 @@
1
+ require 'set'
2
+ require 'mongo'
3
+
4
+ module Flipper
5
+ module Adapters
6
+ class Mongo
7
+ DefaultId = 'flipper'
8
+
9
+ def initialize(collection, options = {})
10
+ @collection = collection
11
+ @options = options
12
+ @mongo_criteria = {:_id => id}
13
+ @mongo_options = {:upsert => true, :safe => true}
14
+ end
15
+
16
+ def read(key)
17
+ read_key(key)
18
+ end
19
+
20
+ def write(key, value)
21
+ update '$set' => {key => value}
22
+ end
23
+
24
+ def delete(key)
25
+ update '$unset' => {key => 1}
26
+ end
27
+
28
+ def set_add(key, value)
29
+ update '$addToSet' => {key => value}
30
+ end
31
+
32
+ def set_delete(key, value)
33
+ update '$pull' => {key => value}
34
+ end
35
+
36
+ def set_members(key)
37
+ read_key(key) { Set.new }.to_set
38
+ end
39
+
40
+ private
41
+
42
+ def id
43
+ @id ||= @options.fetch(:id) { DefaultId }
44
+ end
45
+
46
+ def update(updates)
47
+ @collection.update(@mongo_criteria, updates, @mongo_options)
48
+ end
49
+
50
+ def read_key(key, &block)
51
+ load
52
+
53
+ if block_given?
54
+ @document.fetch(key, &block)
55
+ else
56
+ @document[key]
57
+ end
58
+ end
59
+
60
+ def load
61
+ @document = @collection.find_one(@mongo_criteria) || {}
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ module Flipper
2
+ module Adapters
3
+ class Mongo
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ require 'set'
2
+ require 'mongo'
3
+ require 'flipper/adapters/mongo'
4
+
5
+ module Flipper
6
+ module Adapters
7
+ class MongoWithTTL < Mongo
8
+ private
9
+
10
+ # Override Mongo adapter's load
11
+ def load
12
+ if expired?
13
+ @document = fresh_load
14
+ end
15
+ end
16
+
17
+ def fresh_load
18
+ @last_load_at = Time.now.to_i
19
+ @collection.find_one(@mongo_criteria) || {}
20
+ end
21
+
22
+ def ttl
23
+ @options.fetch(:ttl) { 0 }
24
+ end
25
+
26
+ def expired?
27
+ return true if never_loaded?
28
+ Time.now.to_i >= (@last_load_at + ttl)
29
+ end
30
+
31
+ def never_loaded?
32
+ @last_load_at.nil?
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
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(:criteria) { {:_id => id} }
8
+ let(:id) { described_class::DefaultId }
9
+
10
+ subject { Flipper::Adapters::Mongo.new(collection) }
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]
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
+ if value.is_a?(::Set)
30
+ value = value.to_a
31
+ end
32
+
33
+ options = {:upsert => true}
34
+ updates = {'$set' => {key => value}}
35
+ collection.update criteria, updates, options
36
+ end
37
+
38
+ it_should_behave_like 'a flipper adapter'
39
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+ require 'flipper/adapters/mongo_with_ttl'
3
+ require 'flipper/spec/shared_adapter_specs'
4
+
5
+ describe Flipper::Adapters::MongoWithTTL do
6
+ let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
7
+ let(:criteria) { {:_id => id} }
8
+ let(:id) { described_class::DefaultId }
9
+
10
+ subject { Flipper::Adapters::MongoWithTTL.new(collection) }
11
+
12
+ before do
13
+ collection.remove(criteria)
14
+ end
15
+
16
+ it_should_behave_like 'a flipper adapter'
17
+
18
+ it "can cache document in process for a number of seconds" do
19
+ options = {:ttl => 10}
20
+ adapter = Flipper::Adapters::MongoWithTTL.new(collection, options)
21
+ adapter.write('foo', 'bar')
22
+ now = Time.now
23
+ Timecop.freeze(now)
24
+
25
+ collection.should_receive(:find_one).with(:_id => id)
26
+ adapter.read('foo')
27
+
28
+ adapter.read('foo')
29
+ adapter.read('bar')
30
+
31
+ Timecop.travel(3)
32
+ adapter.read('foo')
33
+
34
+ Timecop.travel(6)
35
+ adapter.read('foo')
36
+
37
+ collection.should_receive(:find_one).with(:_id => id)
38
+ Timecop.travel(1)
39
+ adapter.read('foo')
40
+
41
+ Timecop.travel(4)
42
+ adapter.read('foo')
43
+ end
44
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,29 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
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
+ require 'bundler'
13
+
14
+ Bundler.require(:default, :test)
15
+
16
+ require 'flipper-mongo'
17
+
18
+ Logger.new(log_path.join('test.log'))
19
+
20
+ require 'support/accessor_helpers'
21
+
22
+ RSpec.configure do |config|
23
+ config.filter_run :focused => true
24
+ config.alias_example_to :fit, :focused => true
25
+ config.alias_example_to :xit, :pending => true
26
+ config.run_all_when_everything_filtered = true
27
+
28
+ config.include AccessorHelpers
29
+ end
@@ -0,0 +1,23 @@
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
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flipper-mongo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Nunemaker
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: flipper
16
+ requirement: &70236308172920 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70236308172920
25
+ description: Mongo adapter for Flipper
26
+ email:
27
+ - nunemaker@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - Guardfile
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - flipper-mongo.gemspec
39
+ - lib/flipper-mongo.rb
40
+ - lib/flipper/adapters/mongo.rb
41
+ - lib/flipper/adapters/mongo/version.rb
42
+ - lib/flipper/adapters/mongo_with_ttl.rb
43
+ - spec/flipper/adapters/mongo_spec.rb
44
+ - spec/flipper/adapters/mongo_with_ttl_spec.rb
45
+ - spec/helper.rb
46
+ - spec/support/accessor_helpers.rb
47
+ homepage: http://jnunemaker.github.com/flipper-mongo
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ segments:
60
+ - 0
61
+ hash: 3423381534094134437
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ segments:
69
+ - 0
70
+ hash: 3423381534094134437
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.10
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Mongo adapter for Flipper
77
+ test_files:
78
+ - spec/flipper/adapters/mongo_spec.rb
79
+ - spec/flipper/adapters/mongo_with_ttl_spec.rb
80
+ - spec/helper.rb
81
+ - spec/support/accessor_helpers.rb