flipflop 2.3.1 → 2.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 79ce3ddc4c05be4f6b655519b73fc592b749177a
4
- data.tar.gz: 9de114a7fa334e5689d9bb49e6b2c0e0189b59b4
2
+ SHA256:
3
+ metadata.gz: 2d991f9cd5e93e4a837c0e6835da5ae2339498bf74a26fc6e21cee4503793ca6
4
+ data.tar.gz: 943ba8e6ea38bf2afbc45454890f8111a3b9029383018e5ddf3a76f8faac0732
5
5
  SHA512:
6
- metadata.gz: d89175f3cdf7c36f31fa22ec6b3174bf3d69399ec2ad58d1c1c68bbb5e239456b85c3386144414e5e40ce87d4c504f49cdbc54293aa5b9659dedcba237b94e9c
7
- data.tar.gz: d5a233ec2f53cdfb4d1aacd6afda3b385618887cfb524a6f7fc6de5eb0ad2ad05b949080a34e8e48eab5f4c18fd6dae571affee63ca576533ebf6277f153360c
6
+ metadata.gz: a81cbf3b37f9d91ec310bc971eec5c06b2d4b0581bd42191366503782bb8d95ef6b51fef33588bd28ec571a57ebc03f51cd1c5f9514ac8151f1a855e4072e085
7
+ data.tar.gz: 30a184287b36b7a346cc72fb0bf4f2ed4cb59083461f3859d70d1ec9908c37b3c1b65e54dc3536ed472fed85f94465ea191c6d4eeb7b049679b4056cb3585b05
data/.travis.yml CHANGED
@@ -1,38 +1,49 @@
1
1
  language: ruby
2
+ cache: bundler
2
3
  rvm:
3
- - 2.4.0
4
- - 2.3.0
5
- - jruby-9.1.2.0
4
+ - 2.5.0
5
+ - 2.4.2
6
+ - 2.3.5
7
+ - jruby-9.1.13.0
6
8
  - ruby-head
7
9
  - jruby-head
8
10
  env:
9
11
  - RAILS_VERSION=master
10
12
  - RAILS_VERSION=master RAILS_API_ONLY=1
13
+ - RAILS_VERSION=5.2
14
+ - RAILS_VERSION=5.2 RAILS_API_ONLY=1
15
+ - RAILS_VERSION=5.1
16
+ - RAILS_VERSION=5.1 RAILS_API_ONLY=1
11
17
  - RAILS_VERSION=5.0
12
18
  - RAILS_VERSION=5.0 RAILS_API_ONLY=1
13
19
  matrix:
14
20
  include:
15
- - rvm: 2.3.0
21
+ - rvm: 2.3.5
16
22
  env: RAILS_VERSION=4.2
17
- - rvm: 2.2.6
23
+ - rvm: 2.2.8
18
24
  env: RAILS_VERSION=4.2
19
- - rvm: 2.1.9
25
+ - rvm: 2.1.10
20
26
  env: RAILS_VERSION=4.2
21
27
  - rvm: 2.0.0
22
28
  env: RAILS_VERSION=4.2
23
- - rvm: 2.3.0
29
+ - rvm: 2.3.5
24
30
  env: RAILS_VERSION=4.1
25
- - rvm: 2.2.6
31
+ - rvm: 2.2.8
26
32
  env: RAILS_VERSION=4.1
27
- - rvm: 2.1.9
33
+ - rvm: 2.1.10
28
34
  env: RAILS_VERSION=4.1
29
35
  - rvm: 2.0.0
30
36
  env: RAILS_VERSION=4.1
37
+ exclude:
38
+ - rvm: 2.3.5
39
+ env: RAILS_VERSION=master
40
+ - rvm: 2.3.5
41
+ env: RAILS_VERSION=master RAILS_API_ONLY=1
31
42
  allow_failures:
32
- - rvm: jruby-9.1.2.0
43
+ - rvm: jruby-9.1.13.0
33
44
  - rvm: ruby-head
34
45
  - rvm: jruby-head
35
- before_deploy: rake assets:compile
46
+ before_deploy: bundle exec rake assets:compile
36
47
  deploy:
37
48
  provider: rubygems
38
49
  api_key:
@@ -41,4 +52,4 @@ deploy:
41
52
  on:
42
53
  tags: true
43
54
  repo: voormedia/flipflop
44
- ruby: 2.3.0
55
+ ruby: 2.3.5
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 2.4.0
2
+
3
+ * Add location of feature definition.
4
+ * Raise error for undefined feature or strategy keys. This change can potentially break test cases that use dummy keys.
5
+
1
6
  ## 2.3.1
2
7
 
3
8
  * Fixed backwards compatibility of strategies controller. The incompatibility was introduced in 2.3.0. (@jcoyne)
data/Gemfile CHANGED
@@ -15,7 +15,7 @@ group :test do
15
15
  gem "fakeredis", require: false
16
16
  gem "sqlite3", ">= 1.3", platform: :ruby
17
17
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby,
18
- github: "jruby/activerecord-jdbc-adapter", branch: "rails-5"
18
+ github: "jruby/activerecord-jdbc-adapter"
19
19
 
20
20
  gem "minitest", ">= 4.2"
21
21
  gem "capybara", ">= 2.6"
data/README.md CHANGED
@@ -78,6 +78,11 @@ end
78
78
  This file is automatically reloaded in development mode. No need to restart
79
79
  your server after making changes.
80
80
 
81
+ Feature definitions support these options:
82
+ * `:default` – The feature's default value. This is the value of the feature if no strategy configures an explicit value. Defaults to `false`.
83
+ * `:description` – An optional description of the feature. Displayed on the dashboard if present.
84
+ * `:title` – An optional title of the feature. This defaults to a humanized version of the feature name. Displayed on the dashboard.
85
+
81
86
  ## Strategies
82
87
 
83
88
  The following strategies are provided:
@@ -3,12 +3,12 @@ module Flipflop
3
3
  include ActionController::RequestForgeryProtection
4
4
 
5
5
  def update
6
- strategy.switch!(feature_key, enable?)
6
+ FeatureSet.current.switch!(feature_key, strategy_key, enable?)
7
7
  redirect_to(features_url)
8
8
  end
9
9
 
10
10
  def destroy
11
- strategy.clear!(feature_key)
11
+ FeatureSet.current.clear!(feature_key, strategy_key)
12
12
  redirect_to(features_url)
13
13
  end
14
14
 
@@ -27,8 +27,8 @@ module Flipflop
27
27
  params[:feature_id].to_sym
28
28
  end
29
29
 
30
- def strategy
31
- FeatureSet.current.strategy(params[:id])
30
+ def strategy_key
31
+ params[:id]
32
32
  end
33
33
  end
34
34
  end
@@ -1,14 +1,19 @@
1
1
  module Flipflop
2
2
  class FeatureDefinition
3
- attr_reader :key, :name, :title, :description, :default, :group
3
+ attr_reader :key, :name, :title, :description, :default, :group, :location
4
4
 
5
5
  def initialize(key, **options)
6
6
  @key = key
7
7
  @name = @key.to_s.freeze
8
- @title = @name.humanize.freeze
8
+ @title = options.delete(:title).freeze || @name.humanize.freeze
9
9
  @description = options.delete(:description).freeze
10
10
  @default = !!options.delete(:default) || false
11
11
  @group = options.delete(:group).freeze
12
+ @location = caller_locations(3, 1).first.freeze
13
+
14
+ if options.any?
15
+ raise FeatureError.new(name, "has unknown option #{options.keys.map(&:inspect) * ', '}")
16
+ end
12
17
  end
13
18
  end
14
19
  end
@@ -1,13 +1,19 @@
1
1
  module Flipflop
2
2
  class FeatureError < StandardError
3
- def initialize(feature, error)
4
- super("Feature '#{feature}' #{error}.")
3
+ def initialize(key, error)
4
+ super("Feature '#{key}' #{error}.")
5
5
  end
6
6
  end
7
7
 
8
8
  class StrategyError < StandardError
9
- def initialize(strategy, error)
10
- super("Strategy '#{strategy}' #{error}.")
9
+ def initialize(key, error)
10
+ super("Strategy '#{key}' #{error}.")
11
+ end
12
+ end
13
+
14
+ class Callback < StandardError
15
+ def initialize(key, error)
16
+ super("Callback '#{key}' #{error}.")
11
17
  end
12
18
  end
13
19
 
@@ -70,19 +76,22 @@ module Flipflop
70
76
  end
71
77
  end
72
78
 
73
- def enabled?(feature)
74
- FeatureCache.current.fetch(feature) do
79
+ def enabled?(feature_key)
80
+ FeatureCache.current.fetch(feature_key) do
81
+ feature = feature(feature_key)
82
+
75
83
  result = @strategies.each_value.inject(nil) do |status, strategy|
76
84
  break status unless status.nil?
77
- strategy.enabled?(feature)
85
+ strategy.enabled?(feature_key)
78
86
  end
79
- result.nil? ? feature(feature).default : result
87
+
88
+ result.nil? ? feature.default : result
80
89
  end
81
90
  end
82
91
 
83
- def feature(feature)
84
- @features.fetch(feature) do
85
- raise FeatureError.new(feature, "unknown")
92
+ def feature(feature_key)
93
+ @features.fetch(feature_key) do
94
+ raise FeatureError.new(feature_key, "unknown")
86
95
  end
87
96
  end
88
97
 
@@ -90,14 +99,28 @@ module Flipflop
90
99
  @features.values
91
100
  end
92
101
 
93
- def strategy(strategy)
94
- @strategies.fetch(strategy) do
95
- raise StrategyError.new(strategy, "unknown")
102
+ def strategy(strategy_key)
103
+ @strategies.fetch(strategy_key) do
104
+ raise StrategyError.new(strategy_key, "unknown")
96
105
  end
97
106
  end
98
107
 
99
108
  def strategies
100
109
  @strategies.values
101
110
  end
111
+
112
+ def switch!(feature_key, strategy_key, value)
113
+ strategy = strategy(strategy_key)
114
+ feature = feature(feature_key)
115
+
116
+ strategy.switch!(feature.key, value)
117
+ end
118
+
119
+ def clear!(feature_key, strategy_key)
120
+ strategy = strategy(strategy_key)
121
+ feature = feature(feature_key)
122
+
123
+ strategy.clear!(feature.key)
124
+ end
102
125
  end
103
126
  end
@@ -1,3 +1,3 @@
1
1
  module Flipflop
2
- VERSION = "2.3.1"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -10,26 +10,27 @@ class Flipflop::InstallGenerator < Rails::Generators::Base
10
10
  end
11
11
 
12
12
  def configure_dashboard
13
- comment = <<-RUBY
14
- # Replace with a lambda or method name defined in ApplicationController
15
- # to implement access control for the Flipflop dashboard.
16
- RUBY
17
-
18
- forbidden = <<-RUBY
19
- config.flipflop.dashboard_access_filter = -> { head :forbidden }
20
- RUBY
21
-
22
- allowed = <<-RUBY
23
- config.flipflop.dashboard_access_filter = nil
24
- RUBY
13
+ app = tmpl("-> { head :forbidden }")
14
+ env_dev_test = tmpl("nil")
25
15
 
26
- environment(indent(comment + forbidden + "\n", 4).lstrip)
27
- environment(indent(comment + allowed + "\n", 2).lstrip, env: [:development, :test])
16
+ environment(indent(app + "\n", 4).lstrip)
17
+ environment(indent(env_dev_test + "\n", 2).lstrip, env: [:development, :test])
28
18
  end
29
19
 
30
20
  private
31
21
 
22
+ def tmpl(access_filter)
23
+ return <<-RUBY
24
+ # Before filter for Flipflop dashboard. Replace with a lambda or method name
25
+ # defined in ApplicationController to implement access control.
26
+ config.flipflop.dashboard_access_filter = #{access_filter}
27
+ RUBY
28
+ end
29
+
32
30
  def indent(content, multiplier = 2)
31
+ # Don't fix indentation if Rails already does this (5.2+).
32
+ return content if respond_to?(:optimize_indentation, true)
33
+
33
34
  spaces = " " * multiplier
34
35
  content.each_line.map {|line| line.blank? ? line : "#{spaces}#{line}" }.join
35
36
  end
@@ -14,6 +14,23 @@ describe Flipflop do
14
14
  @app
15
15
  end
16
16
 
17
+ describe "configuration" do
18
+ it "should be added to dev" do
19
+ assert_match %r{^ config\.flipflop\.dashboard_access_filter = nil$},
20
+ File.read("config/environments/development.rb")
21
+ end
22
+
23
+ it "should be added to test" do
24
+ assert_match %r{^ config\.flipflop\.dashboard_access_filter = nil$},
25
+ File.read("config/environments/test.rb")
26
+ end
27
+
28
+ it "should be added to app" do
29
+ assert_match %r{^ config\.flipflop\.dashboard_access_filter = -> \{ head :forbidden \}$},
30
+ File.read("config/application.rb")
31
+ end
32
+ end
33
+
17
34
  describe "middleware" do
18
35
  it "should include cache middleware" do
19
36
  middlewares = Rails.application.middleware.map(&:klass)
data/test/test_helper.rb CHANGED
@@ -110,6 +110,10 @@ class TestApp
110
110
  skip_turbolinks: true,
111
111
  ).invoke_all
112
112
 
113
+ # Remove bootsnap if present, this interferes with reloading apps.
114
+ boot_path = File.expand_path("../../" + path + "/config/boot.rb", __FILE__)
115
+ File.write(boot_path, File.read(boot_path).gsub("require 'bootsnap/setup'", ""))
116
+
113
117
  Flipflop::InstallGenerator.new([],
114
118
  quiet: true,
115
119
  ).invoke_all
@@ -141,7 +145,13 @@ class TestApp
141
145
 
142
146
  silence_stdout { ActiveRecord::Tasks::DatabaseTasks.create_current }
143
147
  ActiveRecord::Migration.verbose = false
144
- ActiveRecord::Migrator.migrate(Rails.application.paths["db/migrate"].to_a)
148
+
149
+ if defined?(ActiveRecord::Migrator.migrate)
150
+ ActiveRecord::Migrator.migrate(Rails.application.paths["db/migrate"].to_a)
151
+ else
152
+ # Rails 5.2+
153
+ ActiveRecord::MigrationContext.new(Rails.application.paths["db/migrate"]).migrate
154
+ end
145
155
  end
146
156
 
147
157
  def unload!
@@ -29,6 +29,11 @@ describe Flipflop::FeatureDefinition do
29
29
  it "should have no group" do
30
30
  assert_nil subject.group
31
31
  end
32
+
33
+ it "should have location" do
34
+ # Because we have no indirection via FeatureSet, the location is minitest.
35
+ assert_equal "instance_eval", subject.location.label
36
+ end
32
37
  end
33
38
 
34
39
  describe "with options" do
@@ -63,5 +68,26 @@ describe Flipflop::FeatureDefinition do
63
68
  it "should have specified group" do
64
69
  assert_equal :my_group, subject.group.key
65
70
  end
71
+
72
+ it "should have location" do
73
+ # Because we have no indirection via FeatureSet, the location is minitest.
74
+ assert_equal "instance_eval", subject.location.label
75
+ end
76
+ end
77
+
78
+ describe "with unknown options" do
79
+ subject do
80
+ Flipflop::FeatureDefinition.new(:my_key,
81
+ unknown: "one",
82
+ other: "two",
83
+ )
84
+ end
85
+
86
+ it "should raise error with message" do
87
+ error = assert_raises Flipflop::FeatureError do
88
+ subject
89
+ end
90
+ assert_equal "Feature 'my_key' has unknown option :unknown, :other.", error.message
91
+ end
66
92
  end
67
93
  end
@@ -51,7 +51,13 @@ describe Flipflop::Strategies::AbstractStrategy do
51
51
  it "should raise if request is missing in thread" do
52
52
  Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = 3
53
53
  assert_raises Flipflop::StrategyError do
54
- Thread.new { subject.send(:request) }.value
54
+ Thread.new {
55
+ if Thread.current.respond_to?(:report_on_exception)
56
+ Thread.current.report_on_exception = false
57
+ end
58
+
59
+ subject.send(:request)
60
+ }.value
55
61
  end
56
62
  end
57
63
  end
@@ -1,6 +1,14 @@
1
1
  require File.expand_path("../../test_helper", __FILE__)
2
2
 
3
3
  describe "Flipflop::StrategiesController" do
4
+ before do
5
+ @app = TestApp.new
6
+ end
7
+
8
+ after do
9
+ @app.unload!
10
+ end
11
+
4
12
  subject do
5
13
  Flipflop::StrategiesController.new
6
14
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipflop
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Annesley
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-03-24 00:00:00.000000000 Z
13
+ date: 2018-04-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -82,8 +82,6 @@ files:
82
82
  - lib/generators/flipflop/migration/templates/create_features.rb
83
83
  - lib/generators/flipflop/routes/USAGE
84
84
  - lib/generators/flipflop/routes/routes_generator.rb
85
- - lib/test_engine/config/features.rb
86
- - lib/test_engine/test_engine.rb
87
85
  - src/stylesheets/_flipflop.scss
88
86
  - test/integration/app_test.rb
89
87
  - test/integration/dashboard_test.rb
@@ -130,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
128
  version: '0'
131
129
  requirements: []
132
130
  rubyforge_project:
133
- rubygems_version: 2.4.5
131
+ rubygems_version: 2.7.6
134
132
  signing_key:
135
133
  specification_version: 4
136
134
  summary: A feature flipflopper for Rails web applications.
@@ -1,3 +0,0 @@
1
- Flipflop.configure do
2
- feature :engine_feature
3
- end
@@ -1,7 +0,0 @@
1
- class TestEngine < Rails::Engine
2
- config.root = "lib/test_engine"
3
-
4
- initializer "features" do
5
- Flipflop::FeatureLoader.current.append(self)
6
- end
7
- end