flip-flop 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 527196b717129538ce2edf0ab15e95afd641ef35
4
+ data.tar.gz: e264c2af251f924114c26dc433f4a62c7093a2d0
5
+ SHA512:
6
+ metadata.gz: 3c3c0b6f202b99d9f16e9643f4aff91e6c2a20cb5e25e82a2a09f6ff8e4beeeb845cd79dc092c907e4fc14c2e1868a176a6e41b2cbec139ca2c49b79eea90c51
7
+ data.tar.gz: 0634f5b6c9079721004902381cd1510ff80e056a9205a767976f32b94d5e5f513a11315a112bf917d01db9d6fdc6a745f11603cb2a13a50ec0a7ce3bc9610cc1
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :name => 'flip-flop'
3
+
4
+ gem 'rake', '~> 10.4.2'
5
+ gem 'rspec', '~> 3.0'
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ flip-flop (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.4.2)
11
+ rspec (3.4.0)
12
+ rspec-core (~> 3.4.0)
13
+ rspec-expectations (~> 3.4.0)
14
+ rspec-mocks (~> 3.4.0)
15
+ rspec-core (3.4.4)
16
+ rspec-support (~> 3.4.0)
17
+ rspec-expectations (3.4.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.4.0)
20
+ rspec-mocks (3.4.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.4.0)
23
+ rspec-support (3.4.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ flip-flop!
30
+ rake (~> 10.4.2)
31
+ rspec (~> 3.0)
32
+
33
+ BUNDLED WITH
34
+ 1.11.2
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ FlipFlop
2
+ ========
3
+
4
+ Provide an easy mechanism for turning features on or off, either with dates or just
5
+ a boolean value.
6
+
7
+ Install
8
+ -------
9
+
10
+ Add the following to your Gemfile
11
+
12
+ gem 'flip-flop'
13
+
14
+ and run bundler to install
15
+
16
+ $ bundle
17
+
18
+ or
19
+
20
+ $ gem install flip-flop
21
+
22
+
23
+ Configure
24
+ ---------
25
+
26
+ in `config/initializers/flip-flop.rb`
27
+
28
+ ```ruby
29
+ require 'flip-flop/adapters/yaml'
30
+ FlipFlop.configure do |config|
31
+ config[:adapter] = FlipFlop::Adapters::YAML
32
+ config[:yaml_path] = 'config/flip_flop.yml'
33
+ end
34
+ ```
35
+
36
+ and add a YAML file: `config/flip-flop.yml`
37
+
38
+ for example:
39
+
40
+ ```yaml
41
+ ---
42
+ :after_date_example:
43
+ :type: :after_date
44
+ :value: 2016-01-01
45
+ :until_date_example:
46
+ :type: :until_date
47
+ :value: 2020-01-01
48
+ :percentage_of_time_example:
49
+ :type: :percentage_of_time
50
+ :value: 50
51
+ :date_range_example:
52
+ :type: :date_range
53
+ :value: !ruby/range
54
+ begin: 2016-01-01
55
+ end: 2016-02-01
56
+ excl: false
57
+ ```
58
+
59
+ Example Usage
60
+ -------------
61
+
62
+ In a view:
63
+
64
+ ```ruby
65
+ if feature_enabled? :my_cool_feature
66
+ puts 'Cool'
67
+ else
68
+ puts ''
69
+ end
70
+
71
+ ```
72
+
73
+ In a controller:
74
+
75
+ ```ruby
76
+ class SomeController < ApplicationController
77
+ include FlipFlop::ViewHelpers
78
+
79
+ def my_action
80
+ load_cool_stuff if feature_enabled? :cool_stuff
81
+ end
82
+ end
83
+ ```
84
+
85
+ Without Rails:
86
+
87
+ ```ruby
88
+ if FlipFlop::get_instance.feature_enabled? :some_feature
89
+ puts 'something'
90
+ else
91
+ puts 'something else'
92
+ end
93
+ ```
94
+
95
+ Gates
96
+ -----
97
+
98
+ * `boolean` &mdash; enable or disable a feature
99
+ * `after_date` &mdash; enable a feature after the date has passed
100
+ * `until_date` &mdash; enable a feature until the date has passed
101
+ * `date_range` &mdash; enable a feature for the duration of the date range
102
+ * `percentage_of_time` &mdash; enable a feature for a given percentage of checks
103
+
104
+ Adapters
105
+ --------
106
+
107
+ ### Memory
108
+
109
+ This adapter is primarily used for testing, all configuration is stored in a
110
+ hash in memory.
111
+
112
+ ### YAML
113
+
114
+ The `FlipFlop::Adapters::YAML` adapter is uses a YAML config file, to store info
115
+ about enabled and disabled features. The YAML file is parsed once when the application is
116
+ loaded. It's quick and easy to use, but it requres a deployment if a feature needs
117
+ to be quickly flipped.
118
+
119
+
120
+ Extending FlipFlop
121
+ ------------------
122
+
123
+ ### Adding Custom Gates
124
+
125
+ You can easily add custom gates by adding methods to the `FlipFlop::Gates` module,
126
+ see the below example.
127
+
128
+ ```ruby
129
+ module FlipFlop::Gates
130
+ def always_on(value)
131
+ true
132
+ end
133
+ end
134
+ ```
135
+
136
+ ### Adding Custom Adapters
137
+
138
+ Custom adapters can be added by creating a new adapter class, and then setting the
139
+ adapture when configuring the FlipFlop. See the "Configure" for an example.
140
+
141
+ Contributing to FlipFlop
142
+ ------------------------
143
+
144
+ * fork
145
+ * branch
146
+ * commit
147
+ * push
148
+ * pull request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rake
2
+
3
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
4
+ require "flip-flop/version"
5
+
6
+ # gem install pkg/*.gem
7
+ # gem uninstall flip-flop
8
+ desc 'Build gem into the pkg directory'
9
+ task :build do
10
+ FileUtils.rm_rf('pkg')
11
+ Dir['*.gemspec'].each do |gemspec|
12
+ system "gem build #{gemspec}"
13
+ end
14
+ FileUtils.mkdir_p('pkg')
15
+ FileUtils.mv(Dir['*.gem'], 'pkg')
16
+ end
17
+
18
+ require "rspec/core/rake_task"
19
+ RSpec::Core::RakeTask.new(:spec) do |t|
20
+ t.rspec_opts = %w(--color)
21
+ end
22
+
23
+ task :default => :spec
data/flip-flop.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/flip-flop/version', __FILE__)
3
+
4
+ plugin_files = []
5
+ plugin_test_files = []
6
+
7
+ ignored_files = plugin_files
8
+ ignored_files << ".gitignore"
9
+ ignored_files.uniq!
10
+
11
+ Gem::Specification.new do |gem|
12
+ gem.authors = ["Brian Kulyk"]
13
+ gem.email = ["brian@kulyk.ca"]
14
+ gem.summary = %q{Feature flipper}
15
+ gem.description = %q{Enable or disable features easily}
16
+ gem.homepage = "https://github.com/bkulyk/flip-flop"
17
+ gem.license = "MIT"
18
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ gem.files = `git ls-files`.split("\n") - ignored_files + ["lib/flip-flop/version.rb"]
20
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ gem.name = "flip-flop"
22
+ gem.require_paths = ["lib"]
23
+ gem.version = FlipFlop::VERSION
24
+ end
@@ -0,0 +1,33 @@
1
+ require 'date'
2
+
3
+ module FlipFlop
4
+ module Adapters
5
+ module Memory
6
+ attr_accessor :features
7
+
8
+ def after_initialize
9
+ @features = {}
10
+ end
11
+
12
+ def get_feature(name)
13
+ @features[name] || nil
14
+ end
15
+
16
+ def set_feature(name, type, value)
17
+ @features[name] = {type: type, value: value}
18
+ end
19
+
20
+ def disable_feature(name)
21
+ set_feature(name, :boolean, false)
22
+ end
23
+
24
+ def feature_type(name)
25
+ @features[name][:type]
26
+ end
27
+
28
+ def feature_value(name)
29
+ @features[name][:value]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require 'date'
2
+
3
+ module FlipFlop
4
+ module Adapters
5
+ module YAML
6
+ include Memory
7
+
8
+ DEFAULT_YAML_PATH = 'config/flip_flip.yml'
9
+
10
+ def after_initialize
11
+ @features = load_yaml
12
+ end
13
+
14
+ def yaml_path
15
+ @config[:yaml_path] || DEFAULT_YAML_PATH
16
+ end
17
+
18
+ def load_yaml
19
+ ::YAML.load_file(yaml_path) || {}
20
+ end
21
+
22
+ def write_yaml
23
+ File.open(yaml_path, 'w') { |f| f.write @features.to_yaml }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require 'date'
2
+
3
+ module FlipFlop
4
+ module Gates
5
+ def boolean(value)
6
+ value
7
+ end
8
+
9
+ def date_range(value)
10
+ value.include? Date.today
11
+ end
12
+
13
+ def until_date(value)
14
+ Date.today < value
15
+ end
16
+
17
+ def after_date(value)
18
+ Date.today > value
19
+ end
20
+
21
+ def percentage_of_time(value)
22
+ rand < (value / 100.0)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ require 'flip-flop/view_helpers'
2
+
3
+ module FlipFlop
4
+ class Railtie < Rails::Railtie
5
+ initializer "flip-flop.view_helpers" do
6
+ ActionView::Base.send :include, ViewHelpers
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module FlipFlop
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,18 @@
1
+ module FlipFlop
2
+ module ViewHelpers
3
+
4
+ # Check the if a feature should be enabled or not from a view
5
+ #
6
+ # Example:
7
+ # >> FlipFlop::get_instance.feature_enabled? :some_feature
8
+ #
9
+ # Arguments:
10
+ # configuration block: (:symbol)
11
+ #
12
+ # Returns:
13
+ # boolean
14
+ def feature_enabled?(feature_name)
15
+ ::FlipFlop::get_instance.feature_enabled? feature_name.to_sym
16
+ end
17
+ end
18
+ end
data/lib/flip-flop.rb ADDED
@@ -0,0 +1,74 @@
1
+ require 'flip-flop/gates'
2
+ require 'flip-flop/adapters/memory'
3
+ require 'flip-flop/railtie' if defined? Rails
4
+
5
+ module FlipFlop
6
+
7
+ # Get the instnace of FlipFlop or initialize a new one.
8
+ #
9
+ # Example:
10
+ # >> FlipFlop::get_intance
11
+ # => <FlipFlop::FlipFlop>
12
+ def self.get_instance
13
+ @flip_flop ||= FlipFlop.new
14
+ end
15
+
16
+ # Initialize and configure FlipFlop for use
17
+ #
18
+ # Example:
19
+ # >> FlipFlop::configure { |config| config[:adapter] = FlipFlop::Adapters::Memory }
20
+ #
21
+ # Arguments:
22
+ # configuration block: (block)
23
+ def self.configure(&block)
24
+ get_instance.configure(&block)
25
+ end
26
+
27
+ class FlipFlop
28
+ attr_reader :config
29
+
30
+ # Initialize and configure FlipFlop for use
31
+ #
32
+ # Example:
33
+ # >> FlipFlop::get_instance.configure { |config| config[:adapter] = FlipFlop::Adapters::Memory }
34
+ #
35
+ # Arguments:
36
+ # configuration block: (block)
37
+ def configure
38
+ @config ||= {}
39
+ yield @config
40
+
41
+ initialize_adapter
42
+ end
43
+
44
+ # Check the if a feature should be enabled or not
45
+ #
46
+ # Example:
47
+ # >> FlipFlop::get_instance.feature_enabled? :some_feature
48
+ #
49
+ # Arguments:
50
+ # configuration block: (:symbol)
51
+ #
52
+ # Returns:
53
+ # boolean
54
+ def feature_enabled?(name)
55
+ public_send feature_type(name), feature_value(name)
56
+ rescue
57
+ false
58
+ end
59
+
60
+ private
61
+
62
+ # If no adapter has been configured, use memory, then run the `after_initialize`
63
+ # method defined in the adapter if it's there
64
+ def initialize_adapter
65
+ # if no adapter has been initialized, use memory adapter
66
+ config[:adapter] ||= Adapters::Memory
67
+
68
+ extend config[:adapter]
69
+ after_initialize if respond_to? :after_initialize
70
+ end
71
+
72
+ include Gates
73
+ end
74
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'flip-flop/view_helpers'
3
+
4
+ class DummyController
5
+ include FlipFlop::ViewHelpers
6
+ end
7
+
8
+ describe DummyController do
9
+
10
+ describe '#feature_enabled?' do
11
+ it 'should return false if nothing has been set' do
12
+ expect(subject.feature_enabled? :asdf).to be_falsey
13
+ end
14
+
15
+ context 'boolean gate' do
16
+ before :each do
17
+ ::FlipFlop::get_instance.set_feature(:asdf, :boolean, :true)
18
+ end
19
+
20
+ it 'should return true if the feature has been enabled' do
21
+ expect(subject.feature_enabled? :asdf).to be_truthy
22
+ end
23
+
24
+ it 'should return false if the feature has been disabled' do
25
+ ::FlipFlop::get_instance.disable_feature(:asdf)
26
+ expect(subject.feature_enabled? :asdf).to be_falsey
27
+ end
28
+ end
29
+
30
+ context 'until_date gate' do
31
+ it 'should return true if the feature has been set to expire in the future' do
32
+ ::FlipFlop::get_instance.set_feature(:something, :until_date, Date.today + 3)
33
+ expect(subject.feature_enabled? :something).to be_truthy
34
+ end
35
+
36
+ it 'should return false if the feature has been set to expire in the past' do
37
+ FlipFlop::get_instance.set_feature(:something, :until_date, Date.today - 3)
38
+ expect(subject.feature_enabled? :something).to be_falsey
39
+ end
40
+ end
41
+
42
+ context 'after_date gate' do
43
+ it 'should return true if the feature has been set to expire after a date in the past' do
44
+ FlipFlop::get_instance.set_feature(:blah, :after_date, Date.today - 3)
45
+ expect(subject.feature_enabled? :blah).to be_truthy
46
+ end
47
+
48
+ it 'should return false if the feature has been set to enable after a date in the future' do
49
+ FlipFlop::get_instance.set_feature(:blah, :after_date, Date.today + 3)
50
+ expect(subject.feature_enabled? :blah).to be_falsey
51
+ end
52
+ end
53
+
54
+ context 'date range gate' do
55
+ it 'feature should be enabled if current date is in the date range' do
56
+ FlipFlop::get_instance.set_feature(:ranged, :date_range, (Date.today - 1)..(Date.today + 1))
57
+ expect(subject.feature_enabled? :ranged).to be_truthy
58
+ end
59
+
60
+ it 'feature should be disabled if range not started yet' do
61
+ ::FlipFlop::get_instance.set_feature(:ranged, :date_range, (Date.today + 1)..(Date.today + 2))
62
+ expect(subject.feature_enabled? :ranged).to be_falsey
63
+ end
64
+
65
+ it 'feature should be disabled if range is past' do
66
+ ::FlipFlop::get_instance.set_feature(:ranged, :date_range, (Date.today - 4)..(Date.today - 2))
67
+ expect(subject.feature_enabled? :ranged).to be_falsey
68
+ end
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.expand_path('../lib', __FILE__))
2
+
3
+ require 'pathname'
4
+ FlopFlopRoot = Pathname(__FILE__).dirname.join('..').expand_path
5
+
6
+ require 'rubygems'
7
+ require 'bundler'
8
+
9
+ Bundler.setup(:default)
10
+
11
+ require 'flip-flop'
12
+
13
+ RSpec.configure do |config|
14
+ FlipFlop.configure do
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flip-flop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brian Kulyk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Enable or disable features easily
14
+ email:
15
+ - brian@kulyk.ca
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - Gemfile.lock
22
+ - README.md
23
+ - Rakefile
24
+ - flip-flop.gemspec
25
+ - lib/flip-flop.rb
26
+ - lib/flip-flop/adapters/memory.rb
27
+ - lib/flip-flop/adapters/yaml.rb
28
+ - lib/flip-flop/gates.rb
29
+ - lib/flip-flop/railtie.rb
30
+ - lib/flip-flop/version.rb
31
+ - lib/flip-flop/view_helpers.rb
32
+ - spec/lib/view_helpers_spec.rb
33
+ - spec/spec_helper.rb
34
+ homepage: https://github.com/bkulyk/flip-flop
35
+ licenses:
36
+ - MIT
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.4.8
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Feature flipper
58
+ test_files:
59
+ - spec/lib/view_helpers_spec.rb
60
+ - spec/spec_helper.rb