flip_fab 1.0.1 → 1.0.2
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 +4 -4
- data/.rubocop.yml +29 -0
- data/example/rails_app/Gemfile +2 -19
- data/example/rails_app/app/assets/javascripts/application.js +0 -1
- data/example/rails_app/app/controllers/beavers_controller.rb +11 -12
- data/example/rails_app/bin/rails +1 -1
- data/example/rails_app/config.ru +1 -1
- data/example/rails_app/config/environments/development.rb +1 -1
- data/example/rails_app/config/environments/test.rb +2 -2
- data/example/rails_app/config/initializers/cookies_serializer.rb +1 -1
- data/example/rails_app/db/schema.rb +5 -7
- data/example/rails_app/spec/rails_helper.rb +2 -2
- data/example/rails_app/spec/spec_helper.rb +0 -1
- data/example/rails_app/test/controllers/beavers_controller_test.rb +12 -12
- data/flip_fab.gemspec +9 -8
- data/lib/flip_fab.rb +3 -3
- data/lib/flip_fab/contextual_feature.rb +11 -17
- data/lib/flip_fab/cookie_persistence.rb +11 -16
- data/lib/flip_fab/feature.rb +2 -3
- data/lib/flip_fab/features_by_name.rb +4 -4
- data/lib/flip_fab/helper.rb +0 -1
- data/lib/flip_fab/persistence.rb +2 -3
- data/lib/flip_fab/version.rb +1 -1
- data/script/cibuild +10 -0
- data/spec/lib/flip_fab/contextual_feature_spec.rb +47 -56
- data/spec/lib/flip_fab/cookie_persistence_spec.rb +40 -43
- data/spec/lib/flip_fab/feature_spec.rb +6 -9
- data/spec/lib/flip_fab/features_by_name_spec.rb +3 -6
- data/spec/lib/flip_fab/helper_spec.rb +35 -38
- data/spec/lib/flip_fab/persistence_spec.rb +2 -5
- data/spec/lib/flip_fab_spec.rb +11 -15
- data/spec/spec_helper.rb +47 -49
- data/spec/support/test_app.rb +2 -2
- data/spec/support/test_context.rb +1 -1
- data/spec/support/test_multiple_persistence.rb +2 -3
- data/spec/support/test_persistence.rb +2 -3
- data/spec/support/test_rack_context.rb +3 -3
- metadata +38 -25
- data/example/rails_app/README.rdoc +0 -28
- data/example/rails_app/config/rabbit_feed.yml +0 -8
- data/example/rails_app/config/unicorn.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41ce116c1d345217c1e9b1eb22afb50ae36cf0b5
|
4
|
+
data.tar.gz: 30446aedb0dab73151be9b4bb1744b9dbad8d15e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f39f8b192ab5a888ef29d325ee94de4eb88f86a78b09b76ef872a61e3895ff1018ce1223185cb8cd53df92edfb34823ed05106ca2b2792f23b15ea334a21a26f
|
7
|
+
data.tar.gz: 8758cb9740f64f0abe4035ef0048c000e9e671fc88ea52cf36f81b37ff03f09948c21e77f80f71e23dc1952c03e0c9d759e9ca8ebf3846f01843ec0734f67e19
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
DisplayStyleGuide: true
|
4
|
+
|
5
|
+
Lint/EndAlignment:
|
6
|
+
Enabled: true
|
7
|
+
|
8
|
+
Lint/UnusedMethodArgument:
|
9
|
+
Exclude:
|
10
|
+
- 'lib/flip_fab/persistence.rb'
|
11
|
+
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Exclude:
|
14
|
+
- 'spec/support/test_app.rb'
|
15
|
+
|
16
|
+
Metrics/LineLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/ModuleLength:
|
20
|
+
Exclude:
|
21
|
+
- 'spec/lib/flip_fab/contextual_feature_spec.rb'
|
22
|
+
|
23
|
+
Style/ClassAndModuleChildren:
|
24
|
+
Exclude:
|
25
|
+
- 'example/rails_app/test/test_helper.rb'
|
26
|
+
|
27
|
+
Style/Documentation:
|
28
|
+
Enabled: false
|
29
|
+
|
data/example/rails_app/Gemfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
4
|
-
gem 'rails', '~>
|
4
|
+
gem 'rails', '~> 5.0'
|
5
5
|
# Use sqlite3 as the database for Active Record
|
6
6
|
gem 'sqlite3'
|
7
7
|
# Use SCSS for stylesheets
|
@@ -10,27 +10,10 @@ gem 'sass-rails'
|
|
10
10
|
gem 'uglifier', '>= 1.3.0'
|
11
11
|
# Use CoffeeScript for .js.coffee assets and views
|
12
12
|
gem 'coffee-rails'
|
13
|
-
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
14
|
-
# gem 'therubyracer', platforms: :ruby
|
15
|
-
|
16
13
|
# Use jquery as the JavaScript library
|
17
14
|
gem 'jquery-rails'
|
18
|
-
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
19
|
-
gem 'turbolinks'
|
20
|
-
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
21
|
-
gem 'jbuilder', '~> 2.0'
|
22
|
-
# bundle exec rake doc:rails generates the API under doc/api.
|
23
|
-
gem 'sdoc', '~> 0.4.0', group: :doc
|
24
|
-
|
25
|
-
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
26
|
-
gem 'spring', group: :development
|
27
|
-
|
28
|
-
# Use unicorn as the app server
|
29
|
-
gem 'unicorn'
|
30
|
-
|
31
|
-
# Use Capistrano for deployment
|
32
|
-
# gem 'capistrano-rails', group: :development
|
33
15
|
|
34
16
|
gem 'flip_fab', path: '../../'
|
35
17
|
|
18
|
+
gem 'rails-controller-testing', group: :test
|
36
19
|
gem 'rspec-rails', group: :test
|
@@ -9,8 +9,7 @@ class BeaversController < ApplicationController
|
|
9
9
|
|
10
10
|
# GET /beavers/1
|
11
11
|
# GET /beavers/1.json
|
12
|
-
def show
|
13
|
-
end
|
12
|
+
def show; end
|
14
13
|
|
15
14
|
# GET /beavers/new
|
16
15
|
def new
|
@@ -18,8 +17,7 @@ class BeaversController < ApplicationController
|
|
18
17
|
end
|
19
18
|
|
20
19
|
# GET /beavers/1/edit
|
21
|
-
def edit
|
22
|
-
end
|
20
|
+
def edit; end
|
23
21
|
|
24
22
|
# POST /beavers
|
25
23
|
# POST /beavers.json
|
@@ -62,13 +60,14 @@ class BeaversController < ApplicationController
|
|
62
60
|
end
|
63
61
|
|
64
62
|
private
|
65
|
-
# Use callbacks to share common setup or constraints between actions.
|
66
|
-
def set_beaver
|
67
|
-
@beaver = Beaver.find(params[:id])
|
68
|
-
end
|
69
63
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
64
|
+
# Use callbacks to share common setup or constraints between actions.
|
65
|
+
def set_beaver
|
66
|
+
@beaver = Beaver.find(params[:id])
|
67
|
+
end
|
68
|
+
|
69
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
70
|
+
def beaver_params
|
71
|
+
params.require(:beaver).permit(:name)
|
72
|
+
end
|
74
73
|
end
|
data/example/rails_app/bin/rails
CHANGED
data/example/rails_app/config.ru
CHANGED
@@ -20,7 +20,7 @@ Rails.application.configure do
|
|
20
20
|
# config.action_dispatch.rack_cache = true
|
21
21
|
|
22
22
|
# Disable Rails's static asset server (Apache or nginx will already do this).
|
23
|
-
config.
|
23
|
+
config.public_file_server.enabled = false
|
24
24
|
|
25
25
|
# Compress JavaScripts and CSS.
|
26
26
|
config.assets.js_compressor = :uglifier
|
@@ -13,8 +13,8 @@ Rails.application.configure do
|
|
13
13
|
config.eager_load = false
|
14
14
|
|
15
15
|
# Configure static asset server for tests with Cache-Control for performance.
|
16
|
-
config.
|
17
|
-
config.
|
16
|
+
config.public_file_server.enabled = true
|
17
|
+
config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
|
18
18
|
|
19
19
|
# Show full error reports and disable caching.
|
20
20
|
config.consider_all_requests_local = true
|
@@ -11,12 +11,10 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
15
|
-
|
16
|
-
|
17
|
-
t.
|
18
|
-
t.datetime
|
19
|
-
t.datetime "updated_at"
|
14
|
+
ActiveRecord::Schema.define(version: 20_140_424_102_400) do
|
15
|
+
create_table 'beavers', force: true do |t|
|
16
|
+
t.string 'name'
|
17
|
+
t.datetime 'created_at'
|
18
|
+
t.datetime 'updated_at'
|
20
19
|
end
|
21
|
-
|
22
20
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
-
ENV[
|
2
|
+
ENV['RAILS_ENV'] ||= 'test'
|
3
3
|
require 'spec_helper'
|
4
|
-
require File.expand_path(
|
4
|
+
require File.expand_path('../../config/environment', __FILE__)
|
5
5
|
require 'rspec/rails'
|
6
6
|
# Add additional requires below this line. Rails is not loaded until this point!
|
7
7
|
|
@@ -5,43 +5,43 @@ class BeaversControllerTest < ActionController::TestCase
|
|
5
5
|
@beaver = beavers(:one)
|
6
6
|
end
|
7
7
|
|
8
|
-
test
|
8
|
+
test 'should get index' do
|
9
9
|
get :index
|
10
10
|
assert_response :success
|
11
11
|
assert_not_nil assigns(:beavers)
|
12
12
|
end
|
13
13
|
|
14
|
-
test
|
14
|
+
test 'should get new' do
|
15
15
|
get :new
|
16
16
|
assert_response :success
|
17
17
|
end
|
18
18
|
|
19
|
-
test
|
19
|
+
test 'should create beaver' do
|
20
20
|
assert_difference('Beaver.count') do
|
21
|
-
post :create, beaver: { name: @beaver.name }
|
21
|
+
post :create, params: { beaver: { name: @beaver.name } }
|
22
22
|
end
|
23
23
|
|
24
24
|
assert_redirected_to beaver_path(assigns(:beaver))
|
25
25
|
end
|
26
26
|
|
27
|
-
test
|
28
|
-
get :show, id: @beaver
|
27
|
+
test 'should show beaver' do
|
28
|
+
get :show, params: { id: @beaver }
|
29
29
|
assert_response :success
|
30
30
|
end
|
31
31
|
|
32
|
-
test
|
33
|
-
get :edit, id: @beaver
|
32
|
+
test 'should get edit' do
|
33
|
+
get :edit, params: { id: @beaver }
|
34
34
|
assert_response :success
|
35
35
|
end
|
36
36
|
|
37
|
-
test
|
38
|
-
patch :update, id: @beaver, beaver: { name: @beaver.name }
|
37
|
+
test 'should update beaver' do
|
38
|
+
patch :update, params: { id: @beaver, beaver: { name: @beaver.name } }
|
39
39
|
assert_redirected_to beaver_path(assigns(:beaver))
|
40
40
|
end
|
41
41
|
|
42
|
-
test
|
42
|
+
test 'should destroy beaver' do
|
43
43
|
assert_difference('Beaver.count', -1) do
|
44
|
-
delete :destroy, id: @beaver
|
44
|
+
delete :destroy, params: { id: @beaver }
|
45
45
|
end
|
46
46
|
|
47
47
|
assert_redirected_to beavers_path
|
data/flip_fab.gemspec
CHANGED
@@ -8,19 +8,20 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = FlipFab::VERSION
|
9
9
|
spec.authors = ['Simply Business']
|
10
10
|
spec.email = ['tech@simplybusiness.co.uk']
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
11
|
+
spec.description = 'A gem providing persistent, per-user feature flipping to Rack applications.'
|
12
|
+
spec.summary = 'A gem providing persistent, per-user feature flipping to Rack applications.'
|
13
13
|
spec.homepage = 'https://github.com/simplybusiness/flip_fab'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split(
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency 'rack'
|
22
|
-
spec.add_development_dependency 'rack-test'
|
23
|
-
spec.add_development_dependency 'rspec'
|
24
|
-
spec.add_development_dependency 'rutabaga'
|
25
|
-
spec.add_development_dependency 'timecop'
|
21
|
+
spec.add_development_dependency 'rack', '~> 2.0'
|
22
|
+
spec.add_development_dependency 'rack-test', '~> 0.6'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
24
|
+
spec.add_development_dependency 'rutabaga', '~> 2.1'
|
25
|
+
spec.add_development_dependency 'timecop', '~> 0.8'
|
26
|
+
spec.add_development_dependency 'rubocop', '~> 0.46'
|
26
27
|
end
|
data/lib/flip_fab.rb
CHANGED
@@ -6,16 +6,16 @@ require 'flip_fab/persistence'
|
|
6
6
|
require 'flip_fab/cookie_persistence'
|
7
7
|
|
8
8
|
module FlipFab
|
9
|
-
extend self
|
10
|
-
|
11
9
|
attr_reader :features
|
12
10
|
|
13
|
-
def define_feature
|
11
|
+
def define_feature(name, options = {})
|
14
12
|
@features ||= {}
|
15
13
|
@features[name] = Feature.new name, options
|
16
14
|
end
|
17
15
|
|
18
16
|
@features ||= FeaturesByName.new
|
17
|
+
|
18
|
+
module_function :features, :define_feature
|
19
19
|
end
|
20
20
|
|
21
21
|
if defined?(ActionController)
|
@@ -2,13 +2,12 @@ module FlipFab
|
|
2
2
|
class ContextualFeature
|
3
3
|
attr_reader :feature, :context
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize(feature, context)
|
6
6
|
@feature = feature
|
7
7
|
@context = context
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
8
|
+
return unless overridden?
|
9
|
+
@state = override
|
10
|
+
persist
|
12
11
|
end
|
13
12
|
|
14
13
|
def enabled?
|
@@ -27,16 +26,15 @@ module FlipFab
|
|
27
26
|
self.state = :disabled
|
28
27
|
end
|
29
28
|
|
30
|
-
def state=
|
29
|
+
def state=(value)
|
31
30
|
raise "Invalid state provided: `#{value}`, possible states are :enabled, :disabled" unless %i(enabled disabled).include? value
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
31
|
+
return if overridden?
|
32
|
+
@state = value
|
33
|
+
persist
|
36
34
|
end
|
37
35
|
|
38
36
|
def persist
|
39
|
-
persistence_adapters.each{ |adapter| adapter.write state }
|
37
|
+
persistence_adapters.each { |adapter| adapter.write state }
|
40
38
|
end
|
41
39
|
|
42
40
|
private
|
@@ -46,11 +44,7 @@ module FlipFab
|
|
46
44
|
end
|
47
45
|
|
48
46
|
def state
|
49
|
-
@state ||=
|
50
|
-
state_from_context
|
51
|
-
else
|
52
|
-
default_state
|
53
|
-
end
|
47
|
+
@state ||= state_in_context? ? state_from_context : default_state
|
54
48
|
end
|
55
49
|
|
56
50
|
def state_in_context?
|
@@ -62,7 +56,7 @@ module FlipFab
|
|
62
56
|
end
|
63
57
|
|
64
58
|
def first_adapter_with_state
|
65
|
-
persistence_adapters.detect{|adapter| !adapter.read.nil?}
|
59
|
+
persistence_adapters.detect { |adapter| !adapter.read.nil? }
|
66
60
|
end
|
67
61
|
|
68
62
|
def default_state
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module FlipFab
|
2
2
|
class CookiePersistence < FlipFab::Persistence
|
3
|
+
COOKIE_PATH = '/'.freeze
|
4
|
+
COOKIE_DURATION_MONTHS = 12
|
5
|
+
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L214
|
6
|
+
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
3
7
|
|
4
|
-
|
5
|
-
COOKIE_DURATION_MONTHS = 12
|
6
|
-
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L214
|
7
|
-
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
8
|
-
|
9
|
-
def initialize feature_name, context
|
8
|
+
def initialize(feature_name, context)
|
10
9
|
super
|
11
10
|
end
|
12
11
|
|
@@ -14,14 +13,12 @@ module FlipFab
|
|
14
13
|
value.to_sym unless value.nil?
|
15
14
|
end
|
16
15
|
|
17
|
-
def write
|
16
|
+
def write(state)
|
18
17
|
cookie_domain = ".#{top_level_domain}" unless top_level_domain.nil?
|
19
|
-
context.response.set_cookie key,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
path: COOKIE_PATH,
|
24
|
-
}
|
18
|
+
context.response.set_cookie key, value: state,
|
19
|
+
expires: cookie_expiration,
|
20
|
+
domain: cookie_domain,
|
21
|
+
path: COOKIE_PATH
|
25
22
|
end
|
26
23
|
|
27
24
|
private
|
@@ -36,9 +33,7 @@ module FlipFab
|
|
36
33
|
|
37
34
|
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L286-L294
|
38
35
|
def top_level_domain
|
39
|
-
if (host !~ /^[\d.]+$/) && (host =~ DOMAIN_REGEXP)
|
40
|
-
$&
|
41
|
-
end
|
36
|
+
$& if (host !~ /^[\d.]+$/) && (host =~ DOMAIN_REGEXP)
|
42
37
|
end
|
43
38
|
|
44
39
|
def cookie_expiration
|
data/lib/flip_fab/feature.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
module FlipFab
|
2
2
|
class Feature
|
3
|
-
|
4
3
|
attr_reader :name, :default, :persistence_adapters
|
5
4
|
|
6
|
-
def initialize
|
5
|
+
def initialize(name, options = {})
|
7
6
|
@name = name
|
8
7
|
@default = options[:default] || :disabled
|
9
8
|
@persistence_adapters = options[:persistence_adapters] || [CookiePersistence]
|
@@ -17,7 +16,7 @@ module FlipFab
|
|
17
16
|
!enabled?
|
18
17
|
end
|
19
18
|
|
20
|
-
def with_context
|
19
|
+
def with_context(context)
|
21
20
|
ContextualFeature.new self, context
|
22
21
|
end
|
23
22
|
end
|
@@ -4,17 +4,17 @@ module FlipFab
|
|
4
4
|
class FeaturesByName
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
def initialize
|
7
|
+
def initialize(features_by_name = {})
|
8
8
|
@features_by_name = features_by_name
|
9
9
|
end
|
10
10
|
|
11
|
-
def []
|
11
|
+
def [](name)
|
12
12
|
raise "no feature has been defined with the name: #{name}" if @features_by_name[name].nil?
|
13
13
|
@features_by_name[name]
|
14
14
|
end
|
15
15
|
|
16
|
-
def with_context
|
17
|
-
FeaturesByName.new Hash[@features_by_name.map{|name, feature| [name, (feature.with_context context)]}]
|
16
|
+
def with_context(context)
|
17
|
+
FeaturesByName.new Hash[@features_by_name.map { |name, feature| [name, (feature.with_context context)] }]
|
18
18
|
end
|
19
19
|
|
20
20
|
def_delegators :@features_by_name, :[]=, :clear, :count, :each
|