binflip 0.0.1

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/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@binflip --create --verbose
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'mocha'
7
+ gem 'pry'
8
+ gem 'redis', :require => false
9
+ gem 'rollout', :require => false
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ binflip (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ coderay (1.0.5)
10
+ metaclass (0.0.1)
11
+ method_source (0.7.1)
12
+ mocha (0.11.0)
13
+ metaclass (~> 0.0.1)
14
+ pry (0.9.8.2)
15
+ coderay (~> 1.0.5)
16
+ method_source (~> 0.7)
17
+ slop (>= 2.4.4, < 3)
18
+ rake (0.9.2.2)
19
+ redis (2.2.2)
20
+ rollout (1.1.0)
21
+ slop (2.4.4)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ binflip!
28
+ mocha
29
+ pry
30
+ rake
31
+ redis
32
+ rollout
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ Binflip
2
+ =======
3
+
4
+ Simple environmental feature toggling (that works well with Rollout)
5
+
6
+
7
+ Description
8
+ ===========
9
+
10
+ Keeping true to twelve-factor application principles <http://12factor.net>, this simple library uses environment variables, with a straightforward naming convention, to specify feature toggles.
11
+
12
+ Rollout Compatibility
13
+ ======================
14
+
15
+ If Rollout <https://github.com/jamesgolick/rollout> is present, it will delegate all methods, adding to `active?` a preliminary test to see if the environment toggle is on before checking rollout.
16
+
17
+ Rationale
18
+ =========
19
+
20
+ When using a continuous delivery process, it is important to try to get all code integrated into the mainline as soon as possible. Feature toggles are favored over feature branches (<http://www.infoq.com/interviews/jez-humble-martin-fowler-cd>, <http://martinfowler.com/bliki/FeatureToggle.html>, <http://fournines.wordpress.com/2011/11/20/feature-branches-vs-feature-toggles/>, and <http://blog.jayfields.com/2010/10/experience-report-feature-toggle-over.html>) during the dev process (to guard against incomplete features being deployed to production).
21
+
22
+ Twelve-factor tells us to keep app configuration setting in environment variables. Hence this libaray adds a tiny bit of convention on how feature toggle env vars are spelled.
23
+
24
+ As the ultimate acceptance of a feature is the market response, this library has been designed to work with rollout.
25
+
26
+ Installation
27
+ ============
28
+
29
+ Add this line to your application's Gemfile:
30
+
31
+ gem 'binflip'
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install binflip
40
+
41
+
42
+
43
+ Usage
44
+ =====
45
+
46
+ Set an environment variable following a FEATURE_[name] pattern:
47
+
48
+ Procfile
49
+
50
+ FEATURE_CHAT = 1
51
+ FEATURE_UPLOAD_VIDEO = 1
52
+
53
+ (Or set the ENV vars manually)
54
+
55
+ Then in application setup / initialization:
56
+
57
+ $toggle = Binflip.new
58
+
59
+
60
+ Check for toggles, using just the [name]:
61
+
62
+ $toggle.active?(:chat)
63
+ $toggle.active?(:upload_video)
64
+
65
+ NOTE: Absence of feature toggle env variable means the feature is not active.
66
+
67
+ Usage with Rollout
68
+ ==================
69
+
70
+ Procfile
71
+
72
+ FEATURE_CHAT = 1
73
+ FEATURE_UPLOAD_VIDEO = 1
74
+
75
+ (Or set the ENV vars manually)
76
+
77
+ Then in application setup / initialization:
78
+
79
+ $redis = Redis.new
80
+ $toggle = Binflip.new($redis)
81
+
82
+
83
+ Check for toggles:
84
+
85
+ $toggle.active?(:chat, @user)
86
+ $toggle.active?(:upload_video, @user)
87
+
88
+ If Rollout is present, Binflip delegates all methods to it (such as `activate_user`).
89
+
90
+ License
91
+ =======
92
+
93
+ Copyright (c) 2012 Brian Kaney
94
+
95
+ MIT License
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs.push "lib"
5
+ t.test_files = FileList['test/*_test.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
data/binflip.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "binflip"
5
+ s.version = '0.0.1'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Brian Kaney"]
8
+ s.email = ["brian@vermonster.com"]
9
+ s.homepage = ""
10
+ s.summary = %q{Kanban Flipper}
11
+ s.description = %q{Environment-based feature flipper. Compatiable with rollout.}
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib", "tools"]
17
+ end
@@ -0,0 +1,56 @@
1
+ require 'binflip'
2
+
3
+ module Binflip
4
+ module Cucumber
5
+
6
+ def self.toggle_bin!(current_bin, scenario)
7
+ # Toggle bin
8
+ Binflip.module_eval <<-RUBY
9
+ def self.current_bin
10
+ return '#{current_bin}'
11
+ end
12
+ RUBY
13
+
14
+ # Toggle RAILS_ENV
15
+ ENV["RAILS_ENV"] = Rails.env = "cucumber_#{current_bin}"
16
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
17
+ ActiveRecord::Base.establish_connection
18
+
19
+
20
+ # Reload routes
21
+ reload_routes_if_new_bin(scenario)
22
+ end
23
+
24
+ def self.current_tags(scenario)
25
+ [ scenario.instance_variable_get(:@tags).tag_names +
26
+ scenario.instance_variable_get(:@feature).instance_variable_get(:@tags).tag_names
27
+ ].flatten
28
+ end
29
+
30
+ def self.rails_app
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def self.reload_routes_if_new_bin(scenario)
35
+ if current_tags(scenario) != $tags
36
+ rails_app.reload_routes!
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ Before do |scenario|
44
+ tags = Binflip::Cucumber.current_tags(scenario)
45
+
46
+ if tags.size <= 1
47
+ bin = tags.first.try(:gsub, '@', '')
48
+ bin = bin.nil? ? Binflip::DEPLOYED_BIN : bin
49
+ Binflip::Cucumber.toggle_bin!(bin, scenario)
50
+ end
51
+
52
+ end
53
+
54
+ After do |scenario|
55
+ $tags = Binflip::Cucumber.current_tags(scenario)
56
+ end
@@ -0,0 +1,53 @@
1
+ module ActiveRecord
2
+ # This patch all adds a #skip? method on the migration classes. It is
3
+ # mainly intended to be used with feature toggles.
4
+ #
5
+ # class FauxMigration < ActiveRecord::Migration
6
+ #
7
+ # def skip?
8
+ # Feature.active?('178_feature_x')
9
+ # end
10
+ #
11
+ # def up
12
+ # add_column :patients, :foob, :string
13
+ # end
14
+ #
15
+ # def down
16
+ # remove_column :patients, :foob
17
+ # end
18
+ # end
19
+ #
20
+ class Migration
21
+
22
+ def skip?
23
+ false
24
+ end
25
+
26
+ def migrate_with_skip(direction)
27
+ if skip?
28
+ announce "Skipping Migration, skip? returned true."
29
+ else
30
+ migrate_without_skip(direction)
31
+ end
32
+ end
33
+ alias_method :migrate_without_skip, :migrate
34
+ alias_method :migrate, :migrate_with_skip
35
+ end
36
+
37
+ class MigrationProxy
38
+ delegate :skip?, :to => :migration
39
+ end
40
+
41
+ class Migrator
42
+ def record_version_state_after_migrating_with_skip(version)
43
+ current = migrations.detect { |m| m.version == version } # This is actually the proxy for the migration
44
+ unless current.skip?
45
+ record_version_state_after_migrating_without_skip(version)
46
+ end
47
+ end
48
+
49
+ alias_method :record_version_state_after_migrating_without_skip, :record_version_state_after_migrating
50
+ alias_method :record_version_state_after_migrating, :record_version_state_after_migrating_with_skip
51
+ end
52
+
53
+ end
@@ -0,0 +1,29 @@
1
+ require 'binflip'
2
+
3
+ namespace :db do
4
+ namespace :test do
5
+ task :prepare_with_kanban do
6
+ Binflip::BINS.each do |bin|
7
+ # Toggle bin
8
+
9
+ ENV['RAILS_ENV'] = Rails.env = "cucumber_#{bin}"
10
+ puts "=== prepare for #{Rails.env}"
11
+
12
+ Binflip.module_eval <<-RUBY
13
+ def self.current_bin
14
+ return '#{bin}'
15
+ end
16
+ RUBY
17
+
18
+ Rake::Task['db:drop'].execute
19
+ Rake::Task['db:create'].execute
20
+ Rake::Task['db:migrate'].execute
21
+ Rake::Task['db:schema:dump'].execute
22
+ Rake::Task['db:test:prepare_without_kanban'].execute
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ alias_task('db:test:prepare_without_kanban', 'db:test:prepare')
29
+ alias_task('db:test:prepare', 'db:test:prepare_with_kanban')
@@ -0,0 +1 @@
1
+ require 'binflip/rails/active_support' #if defined?(ActiveSupport::Base)
data/lib/binflip.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'delegate'
2
+
3
+ class Binflip
4
+
5
+ def self.rollout?
6
+ defined?(Rollout) == 'constant'
7
+ end
8
+
9
+ def initialize(redis=nil)
10
+ if Binflip.rollout?
11
+ @source = SimpleDelegator.new(Rollout.new(redis))
12
+ end
13
+ end
14
+
15
+ def active?(feature, user=nil)
16
+ if environment_active?(feature) && @source
17
+ @source.active?(feature, user)
18
+ else
19
+ environment_active?(feature)
20
+ end
21
+ end
22
+
23
+ def method_missing(meth, *args, &block)
24
+ if @source
25
+ @source.send(meth, *args, &block)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def environment_key(feature)
34
+ ("FEATURE_%s" % feature).upcase
35
+ end
36
+
37
+ def environment_active?(feature)
38
+ ENV[environment_key(feature)] == '1'
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ # This is an alias method, e.g.:
2
+ #
3
+ # alias_task(:alias_task, :original_task)
4
+ #
5
+ def alias_task(name, old_name)
6
+ t = Rake::Task[old_name]
7
+ desc t.full_comment if t.full_comment
8
+ task name, *t.arg_names do |_, args|
9
+ # values_at is broken on Rake::TaskArguments
10
+ args = t.arg_names.map { |a| args[a] }
11
+ t.invoke(args)
12
+ end
13
+ end
14
+
@@ -0,0 +1,64 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/spec'
3
+
4
+ require 'bundler/setup'
5
+ require 'mocha'
6
+ require 'pry'
7
+ require 'rollout'
8
+ require 'redis'
9
+ require 'binflip'
10
+
11
+ describe Binflip do
12
+
13
+ describe "without rollout" do
14
+
15
+ before do
16
+ Binflip.stubs(:rollout?).returns(false)
17
+
18
+ @binflip = Binflip.new
19
+ ENV['FEATURE_X'] = "1"
20
+ ENV['FEATURE_Y'] = "0"
21
+ end
22
+
23
+ it "should be active" do
24
+ @binflip.active?(:x).must_equal true
25
+ end
26
+
27
+ it "should not be active" do
28
+ @binflip.active?(:y).must_equal false
29
+ end
30
+
31
+ it "should not be active for unspecified" do
32
+ @binflip.active?(:z).must_equal false
33
+ end
34
+
35
+ describe "with Rollout" do
36
+ before do
37
+ Binflip.expects(:rollout?).returns(true)
38
+ Redis.new.flushdb
39
+
40
+ @redis = Redis.new
41
+ @binflip = Binflip.new(@redis)
42
+ ENV['FEATURE_X'] = "1"
43
+ ENV['FEATURE_Y'] = "0"
44
+ end
45
+
46
+ it "must be true if the environment is true and rollout is true" do
47
+ @binflip.activate_user(:x, stub(:id => 51))
48
+ @binflip.active?(:x, stub(:id => 51)).must_equal true
49
+ end
50
+
51
+ it "must be true if the environment is true and rollout is false" do
52
+ @binflip.active?(:x, stub(:id => 51)).must_equal false
53
+ end
54
+
55
+ it "must be false if the environment is false" do
56
+ @binflip.activate_user(:x, stub(:id => 51))
57
+ @binflip.active?(:y, stub(:id => 51)).must_equal false
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binflip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Kaney
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-20 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Environment-based feature flipper. Compatiable with rollout.
15
+ email:
16
+ - brian@vermonster.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .rvmrc
22
+ - .travis.yml
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - README.md
26
+ - Rakefile
27
+ - binflip.gemspec
28
+ - lib/binflip.rb
29
+ - lib/binflip/cucumber.rb
30
+ - lib/binflip/rails.rb
31
+ - lib/binflip/rails/active_support.rb
32
+ - lib/binflip/rails/tasks/db.rake
33
+ - lib/core_ext/rake.rb
34
+ - test/binflip_test.rb
35
+ homepage: ''
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ - tools
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.15
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Kanban Flipper
60
+ test_files:
61
+ - test/binflip_test.rb