toggles 0.1.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +24 -0
- data/CHANGELOG.md +83 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +13 -0
- data/README.md +76 -0
- data/features/abbreviations_cn22.yml +3 -0
- data/features/file_s3.yml +3 -0
- data/features/nested_foo/bar_baz.yml +3 -0
- data/features/s3_file.yml +3 -0
- data/lib/toggles/constant_lookup.rb +50 -0
- data/lib/toggles/feature/attribute.rb +15 -0
- data/lib/toggles/feature/operation/and.rb +0 -15
- data/lib/toggles/feature/operation/in.rb +1 -1
- data/lib/toggles/feature/operation/range.rb +0 -2
- data/lib/toggles/feature/permissions.rb +34 -29
- data/lib/toggles/feature.rb +80 -12
- data/lib/toggles/version.rb +5 -0
- data/lib/toggles.rb +39 -38
- data/spec/spec_helper.rb +8 -3
- data/spec/toggles/feature/acceptance/collection_spec.rb +1 -1
- data/spec/toggles/feature/acceptance/complex_and_spec.rb +1 -1
- data/spec/toggles/feature/acceptance/multiple_subjects_spec.rb +1 -1
- data/spec/toggles/feature/acceptance/nested_attributes_spec.rb +1 -1
- data/spec/toggles/feature/acceptance/or_attributes_spec.rb +1 -1
- data/spec/toggles/feature/acceptance/type_spec.rb +20 -4
- data/spec/toggles/feature/attribute_spec.rb +9 -0
- data/spec/toggles/feature/operation_spec.rb +69 -0
- data/spec/toggles/feature/permissions_spec.rb +1 -1
- data/spec/toggles/feature_spec.rb +37 -0
- data/spec/toggles/init_spec.rb +89 -0
- data/toggles.gemspec +25 -15
- metadata +39 -67
- data/lib/toggles/feature/base.rb +0 -27
- data/lib/toggles/feature/operation/attribute.rb +0 -19
- data/lib/toggles/feature/operation/lt.rb +0 -9
- data/lib/toggles/feature/operation/not.rb +0 -15
- data/lib/toggles/feature/operation/or.rb +0 -15
- data/lib/toggles/feature/operation.rb +0 -8
- data/spec/toggles/feature/base_spec.rb +0 -10
- data/spec/toggles/feature/operation/and_spec.rb +0 -9
- data/spec/toggles/feature/operation/attribute_spec.rb +0 -9
- data/spec/toggles/feature/operation/gt_spec.rb +0 -6
- data/spec/toggles/feature/operation/in_spec.rb +0 -7
- data/spec/toggles/feature/operation/lt_spec.rb +0 -6
- data/spec/toggles/feature/operation/not_spec.rb +0 -6
- data/spec/toggles/feature/operation/or_spec.rb +0 -6
- data/spec/toggles/feature/operation/range_spec.rb +0 -5
@@ -1,7 +1,23 @@
|
|
1
|
-
describe Feature::Type do
|
1
|
+
describe "Feature::Type" do
|
2
|
+
specify 'deprecated' do
|
3
|
+
aggregate_failures do
|
4
|
+
expect(Feature::Type.enabled_for?(user_id: 1)).to eq true
|
5
|
+
expect(Feature::Type.enabled_for?(user_id: 25)).to eq false
|
6
|
+
expect(Feature::Type.enabled_for?(user_id: nil)).to eq false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
2
10
|
specify do
|
3
|
-
|
4
|
-
|
5
|
-
|
11
|
+
aggregate_failures do
|
12
|
+
expect(Feature.enabled?(:type, user_id: 1)).to eq(true)
|
13
|
+
expect(Feature.disabled?(:type, user_id: 1)).to eq(false)
|
14
|
+
expect(Feature.enabled?(:type, user_id: 25)).to eq(false)
|
15
|
+
expect(Feature.enabled?(:nested_foo, :bar_baz, id: 25)).to eq(false)
|
16
|
+
expect(Feature.enabled?(:nested_foo, :bar_baz, id: 1)).to eq(true)
|
17
|
+
expect(Feature.disabled?(:type, user_id: 25)).to eq(true)
|
18
|
+
expect(Feature.disabled?(:nested_foo, :bar_baz, id: 25)).to eq(true)
|
19
|
+
expect(Feature.disabled?(:nested_foo, :bar_baz, id: 1)).to eq(false)
|
20
|
+
expect { Feature.disabled?(:nested_foo, :bar_boz, id: 1) }.to raise_error(Feature::Unknown)
|
21
|
+
end
|
6
22
|
end
|
7
23
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
RSpec.describe Feature::Attribute do
|
2
|
+
specify do
|
3
|
+
expect(described_class.call(double(id: 50), :id, 50)).to eq true
|
4
|
+
expect(described_class.call(double(id: 50), :id, 51)).to eq false
|
5
|
+
|
6
|
+
expect(described_class.call(double(id: 50), :id, { 'in' => (0..50), 'not' => 49 })).to eq true
|
7
|
+
expect(described_class.call(double(id: 49), :id, { 'in' => (0..50), 'not' => 49 })).to eq false
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe 'operations' do
|
4
|
+
context 'and' do
|
5
|
+
subject { Feature.operations[:and] }
|
6
|
+
|
7
|
+
specify do
|
8
|
+
[[60, true], [40, false], [80, false]].each do |(id, expected)|
|
9
|
+
expect(
|
10
|
+
subject.call(double(id: id), :id, {"gt" => 50, "lt" => 70})
|
11
|
+
).to eq expected
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'gt' do
|
17
|
+
subject { Feature.operations[:gt] }
|
18
|
+
|
19
|
+
specify do
|
20
|
+
expect(subject.call(double(id: 50), :id, 40)).to eq true
|
21
|
+
expect(subject.call(double(id: 50), :id, 60)).to eq false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'in' do
|
26
|
+
subject { Feature.operations[:in] }
|
27
|
+
|
28
|
+
specify do
|
29
|
+
expect(subject.call(double(id: 50), :id, {"range" => [40, 60]})).to eq true
|
30
|
+
expect(subject.call(double(id: 50), :id, [1])).to eq false
|
31
|
+
expect(subject.call(double(id: nil), :id, [1])).to eq false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'lt' do
|
36
|
+
subject { Feature.operations[:lt] }
|
37
|
+
|
38
|
+
specify do
|
39
|
+
expect(subject.call(double(id: 50), :id, 60)).to eq true
|
40
|
+
expect(subject.call(double(id: 50), :id, 40)).to eq false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'not' do
|
45
|
+
subject { Feature.operations[:not] }
|
46
|
+
|
47
|
+
specify do
|
48
|
+
expect(subject.call(double(id: 50), :id, {"in" => (10..20)})).to eq true
|
49
|
+
expect(subject.call(double(id: 50), :id, 50)).to eq false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'or' do
|
54
|
+
subject { Feature.operations[:or] }
|
55
|
+
|
56
|
+
specify do
|
57
|
+
expect(subject.call(double(id: 16), :id, {"in" => (10..20), "not" => 15})).to eq true
|
58
|
+
expect(subject.call(double(id: 15), :id, {"in" => (10..20), "not" => 15})).to eq true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'range' do
|
63
|
+
subject { Feature.operations[:range] }
|
64
|
+
|
65
|
+
specify do
|
66
|
+
expect(subject.call([10, 20])).to eq((10..20))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
describe Feature::Permissions do
|
2
2
|
let(:path) { "features/multiple_subjects.yml" }
|
3
3
|
|
4
|
-
subject { Feature::Permissions.
|
4
|
+
subject { Feature::Permissions.from_yaml(path) }
|
5
5
|
|
6
6
|
its(:rules) { is_expected.to eq({"user"=>{"id"=>1, "logged_in?"=>true},
|
7
7
|
"widget"=>{"id"=>2}}) }
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe 'features' do
|
2
|
+
let(:user) { double(id: 1, logged_in?: true) }
|
3
|
+
let(:widget) { double(id: 2) }
|
4
|
+
|
5
|
+
context 'multiple subjects' do
|
6
|
+
specify { expect(Feature).to be_enabled(:multiple_subjects, user: user, widget: widget) }
|
7
|
+
specify { expect(Feature::MultipleSubjects).to be_enabled_for(user: user, widget: widget) }
|
8
|
+
specify { expect(Feature).not_to be_disabled(:multiple_subjects, user: user, widget: widget) }
|
9
|
+
specify { expect(Feature::MultipleSubjects).not_to be_disabled_for(user: user, widget: widget) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'abbreviation with numbers' do
|
13
|
+
subject { Feature::AbbreviationsCN22.new(user: user) }
|
14
|
+
|
15
|
+
specify { expect(Feature).to be_enabled(:abbreviations_cn22, user: user) }
|
16
|
+
specify { expect(Feature::AbbreviationsCN22).to be_enabled_for(user: user) }
|
17
|
+
specify { expect(Feature).not_to be_disabled(:abbreviations_cn22, user: user) }
|
18
|
+
specify { expect(Feature::AbbreviationsCN22).not_to be_disabled_for(user: user) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'irregular capitalization' do
|
22
|
+
specify { expect(Feature).to be_enabled(:s3_file, user: user) }
|
23
|
+
specify { expect(Feature::S3File).to be_enabled_for(user: user) }
|
24
|
+
specify { expect(Feature).not_to be_disabled(:s3_file, user: user) }
|
25
|
+
specify { expect(Feature).to be_disabled(:s3_file, user: widget) }
|
26
|
+
specify { expect(Feature::S3File).not_to be_disabled_for(user: user) }
|
27
|
+
specify { expect(Feature::S3File).to be_disabled_for(user: widget) }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'irregular capitalization' do
|
31
|
+
specify { expect(Feature).to be_enabled(:file_s3, user: user) }
|
32
|
+
specify { expect(Feature::FileS3).to be_enabled_for(user: user) }
|
33
|
+
specify { expect(Feature).not_to be_disabled(:file_s3, user: user) }
|
34
|
+
specify { expect(Feature::FileS3).not_to be_disabled_for(user: user) }
|
35
|
+
specify { expect(Feature).to be_disabled(:file_s3, user: widget) }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
describe Toggles do
|
2
|
+
describe "#init" do
|
3
|
+
include_context "uses temp dir"
|
4
|
+
|
5
|
+
it "correctly loads configuration" do
|
6
|
+
Dir.mkdir("#{temp_dir}/foo")
|
7
|
+
Dir.mkdir("#{temp_dir}/bar")
|
8
|
+
|
9
|
+
File.open("#{temp_dir}/foo/users.yml", "w") do |f|
|
10
|
+
f.write("{\"id\": {\"in\": [1, 2]}}")
|
11
|
+
end
|
12
|
+
|
13
|
+
File.open("#{temp_dir}/bar/users.yml", "w") do |f|
|
14
|
+
f.write("{\"id\": {\"in\": [3, 4]}}")
|
15
|
+
end
|
16
|
+
|
17
|
+
Toggles.configure do |c|
|
18
|
+
c.features_dir = temp_dir
|
19
|
+
end
|
20
|
+
|
21
|
+
expect(Feature::Foo::Users.enabled_for?(id: 1)).to eq(true)
|
22
|
+
expect(Feature::Bar::Users.enabled_for?(id: 3)).to eq(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "reloads configuration when #init is called" do
|
26
|
+
Dir.mkdir("#{temp_dir}/foo")
|
27
|
+
|
28
|
+
File.open("#{temp_dir}/foo/users.yml", "w") do |f|
|
29
|
+
f.write("{\"id\": {\"in\": [1, 2]}}")
|
30
|
+
end
|
31
|
+
File.open("#{temp_dir}/foo/children.yml", "w") do |f|
|
32
|
+
f.write("{\"id\": {\"in\": [1, 2]}}")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
Toggles.configure do |c|
|
37
|
+
c.features_dir = temp_dir
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(Feature::Foo::Users.enabled_for?(id: 1)).to eq(true)
|
41
|
+
expect(Feature::Foo::Children.enabled_for?(id: 1)).to eq(true)
|
42
|
+
|
43
|
+
File.open("#{temp_dir}/foo/users.yml", "w") do |f|
|
44
|
+
f.write("{\"id\": {\"in\": [2]}}")
|
45
|
+
end
|
46
|
+
|
47
|
+
File.unlink("#{temp_dir}/foo/children.yml")
|
48
|
+
|
49
|
+
Toggles.init
|
50
|
+
|
51
|
+
expect(Feature::Foo::Users.enabled_for?(id: 1)).to eq(false)
|
52
|
+
expect { Feature::Bar::Children.enabled_for?(id: 1) }
|
53
|
+
.to raise_error(Feature::ConstantLookup::Error, 'Feature::Bar')
|
54
|
+
expect { Feature::Foo::Children.enabled_for?(id: 1) }
|
55
|
+
.to raise_error(Feature::ConstantLookup::Error, 'Feature::Foo::Children')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#reinit_if_changed" do
|
60
|
+
include_context "uses temp dir"
|
61
|
+
|
62
|
+
it "reloads when the contents have changed" do
|
63
|
+
Dir.mkdir("#{temp_dir}/features")
|
64
|
+
|
65
|
+
Toggles.configure do |c|
|
66
|
+
c.features_dir = "#{temp_dir}/features"
|
67
|
+
end
|
68
|
+
|
69
|
+
# the OS might reuse the inode if we do this in the obvious order
|
70
|
+
Dir.mkdir("#{temp_dir}/features2")
|
71
|
+
Dir.delete("#{temp_dir}/features")
|
72
|
+
File.rename("#{temp_dir}/features2", "#{temp_dir}/features")
|
73
|
+
|
74
|
+
expect(Toggles).to receive("init")
|
75
|
+
Toggles.reinit_if_changed
|
76
|
+
end
|
77
|
+
|
78
|
+
it "does not reload when the contents have not changed" do
|
79
|
+
Dir.mkdir("#{temp_dir}/features")
|
80
|
+
|
81
|
+
Toggles.configure do |c|
|
82
|
+
c.features_dir = "#{temp_dir}/features"
|
83
|
+
end
|
84
|
+
|
85
|
+
expect(Toggles).not_to receive("init")
|
86
|
+
Toggles.reinit_if_changed
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/toggles.gemspec
CHANGED
@@ -1,19 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'toggles/version'
|
6
|
+
|
1
7
|
Gem::Specification.new do |s|
|
2
|
-
s.name =
|
3
|
-
s.version =
|
4
|
-
s.authors = [
|
5
|
-
s.summary =
|
6
|
-
s.email =
|
7
|
-
s.homepage =
|
8
|
-
s.license =
|
8
|
+
s.name = 'toggles'
|
9
|
+
s.version = Toggles::VERSION
|
10
|
+
s.authors = ['Andrew Tribone', 'James Brown', 'Josh Lane']
|
11
|
+
s.summary = 'YAML backed feature toggles'
|
12
|
+
s.email = 'oss@easypost.com'
|
13
|
+
s.homepage = 'https://github.com/EasyPost/toggles'
|
14
|
+
s.license = 'ISC'
|
9
15
|
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
10
|
-
s.test_files = s.files.grep(
|
16
|
+
s.test_files = s.files.grep(%r{^(spec)/})
|
17
|
+
s.description = <<-EOF
|
18
|
+
YAML-backed implementation of the feature flags pattern. Build a
|
19
|
+
hierarchy of features in YAML files in the filesystem, apply various
|
20
|
+
conditions using boolean logic and a selection of filters, and easily
|
21
|
+
check whether a given feature should be applied.
|
22
|
+
EOF
|
11
23
|
|
12
|
-
s.add_development_dependency
|
13
|
-
s.add_development_dependency
|
14
|
-
s.add_development_dependency
|
15
|
-
s.add_development_dependency
|
16
|
-
s.add_development_dependency
|
17
|
-
s.add_development_dependency "rspec"
|
18
|
-
s.add_development_dependency "rspec-its"
|
24
|
+
s.add_development_dependency 'bundler'
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'rspec'
|
27
|
+
s.add_development_dependency 'rspec-its'
|
28
|
+
s.add_development_dependency 'rspec-temp_dir'
|
19
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toggles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Tribone
|
8
|
-
|
8
|
+
- James Brown
|
9
|
+
- Josh Lane
|
10
|
+
autorequire:
|
9
11
|
bindir: bin
|
10
12
|
cert_chain: []
|
11
|
-
date:
|
13
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
14
|
dependencies:
|
13
15
|
- !ruby/object:Gem::Dependency
|
14
16
|
name: bundler
|
@@ -25,35 +27,7 @@ dependencies:
|
|
25
27
|
- !ruby/object:Gem::Version
|
26
28
|
version: '0'
|
27
29
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: pry-nav
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry-remote
|
30
|
+
name: rake
|
57
31
|
requirement: !ruby/object:Gem::Requirement
|
58
32
|
requirements:
|
59
33
|
- - ">="
|
@@ -67,7 +41,7 @@ dependencies:
|
|
67
41
|
- !ruby/object:Gem::Version
|
68
42
|
version: '0'
|
69
43
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
44
|
+
name: rspec
|
71
45
|
requirement: !ruby/object:Gem::Requirement
|
72
46
|
requirements:
|
73
47
|
- - ">="
|
@@ -81,7 +55,7 @@ dependencies:
|
|
81
55
|
- !ruby/object:Gem::Version
|
82
56
|
version: '0'
|
83
57
|
- !ruby/object:Gem::Dependency
|
84
|
-
name: rspec
|
58
|
+
name: rspec-its
|
85
59
|
requirement: !ruby/object:Gem::Requirement
|
86
60
|
requirements:
|
87
61
|
- - ">="
|
@@ -95,7 +69,7 @@ dependencies:
|
|
95
69
|
- !ruby/object:Gem::Version
|
96
70
|
version: '0'
|
97
71
|
- !ruby/object:Gem::Dependency
|
98
|
-
name: rspec-
|
72
|
+
name: rspec-temp_dir
|
99
73
|
requirement: !ruby/object:Gem::Requirement
|
100
74
|
requirements:
|
101
75
|
- - ">="
|
@@ -108,37 +82,46 @@ dependencies:
|
|
108
82
|
- - ">="
|
109
83
|
- !ruby/object:Gem::Version
|
110
84
|
version: '0'
|
111
|
-
description:
|
112
|
-
|
85
|
+
description: |2
|
86
|
+
YAML-backed implementation of the feature flags pattern. Build a
|
87
|
+
hierarchy of features in YAML files in the filesystem, apply various
|
88
|
+
conditions using boolean logic and a selection of filters, and easily
|
89
|
+
check whether a given feature should be applied.
|
90
|
+
email: oss@easypost.com
|
113
91
|
executables: []
|
114
92
|
extensions: []
|
115
93
|
extra_rdoc_files: []
|
116
94
|
files:
|
95
|
+
- ".github/workflows/ci.yml"
|
117
96
|
- ".gitignore"
|
118
97
|
- ".rspec"
|
98
|
+
- CHANGELOG.md
|
119
99
|
- Gemfile
|
100
|
+
- LICENSE.txt
|
101
|
+
- README.md
|
120
102
|
- Rakefile
|
103
|
+
- features/abbreviations_cn22.yml
|
121
104
|
- features/collection.yml
|
122
105
|
- features/complex_and.yml
|
106
|
+
- features/file_s3.yml
|
123
107
|
- features/multiple_subjects.yml
|
124
108
|
- features/nested_attributes.yml
|
109
|
+
- features/nested_foo/bar_baz.yml
|
125
110
|
- features/or_attributes.yml
|
111
|
+
- features/s3_file.yml
|
126
112
|
- features/type.yml
|
127
113
|
- lib/toggles.rb
|
128
114
|
- lib/toggles/configuration.rb
|
115
|
+
- lib/toggles/constant_lookup.rb
|
129
116
|
- lib/toggles/feature.rb
|
130
|
-
- lib/toggles/feature/
|
131
|
-
- lib/toggles/feature/operation.rb
|
117
|
+
- lib/toggles/feature/attribute.rb
|
132
118
|
- lib/toggles/feature/operation/and.rb
|
133
|
-
- lib/toggles/feature/operation/attribute.rb
|
134
119
|
- lib/toggles/feature/operation/gt.rb
|
135
120
|
- lib/toggles/feature/operation/in.rb
|
136
|
-
- lib/toggles/feature/operation/lt.rb
|
137
|
-
- lib/toggles/feature/operation/not.rb
|
138
|
-
- lib/toggles/feature/operation/or.rb
|
139
121
|
- lib/toggles/feature/operation/range.rb
|
140
122
|
- lib/toggles/feature/permissions.rb
|
141
123
|
- lib/toggles/feature/subject.rb
|
124
|
+
- lib/toggles/version.rb
|
142
125
|
- spec/spec_helper.rb
|
143
126
|
- spec/toggles/configuration_spec.rb
|
144
127
|
- spec/toggles/feature/acceptance/collection_spec.rb
|
@@ -147,23 +130,18 @@ files:
|
|
147
130
|
- spec/toggles/feature/acceptance/nested_attributes_spec.rb
|
148
131
|
- spec/toggles/feature/acceptance/or_attributes_spec.rb
|
149
132
|
- spec/toggles/feature/acceptance/type_spec.rb
|
150
|
-
- spec/toggles/feature/
|
151
|
-
- spec/toggles/feature/
|
152
|
-
- spec/toggles/feature/operation/attribute_spec.rb
|
153
|
-
- spec/toggles/feature/operation/gt_spec.rb
|
154
|
-
- spec/toggles/feature/operation/in_spec.rb
|
155
|
-
- spec/toggles/feature/operation/lt_spec.rb
|
156
|
-
- spec/toggles/feature/operation/not_spec.rb
|
157
|
-
- spec/toggles/feature/operation/or_spec.rb
|
158
|
-
- spec/toggles/feature/operation/range_spec.rb
|
133
|
+
- spec/toggles/feature/attribute_spec.rb
|
134
|
+
- spec/toggles/feature/operation_spec.rb
|
159
135
|
- spec/toggles/feature/permissions_spec.rb
|
160
136
|
- spec/toggles/feature/subject_spec.rb
|
137
|
+
- spec/toggles/feature_spec.rb
|
138
|
+
- spec/toggles/init_spec.rb
|
161
139
|
- toggles.gemspec
|
162
|
-
homepage: https://github.com/
|
140
|
+
homepage: https://github.com/EasyPost/toggles
|
163
141
|
licenses:
|
164
|
-
-
|
142
|
+
- ISC
|
165
143
|
metadata: {}
|
166
|
-
post_install_message:
|
144
|
+
post_install_message:
|
167
145
|
rdoc_options: []
|
168
146
|
require_paths:
|
169
147
|
- lib
|
@@ -178,9 +156,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
156
|
- !ruby/object:Gem::Version
|
179
157
|
version: '0'
|
180
158
|
requirements: []
|
181
|
-
|
182
|
-
|
183
|
-
signing_key:
|
159
|
+
rubygems_version: 3.3.26
|
160
|
+
signing_key:
|
184
161
|
specification_version: 4
|
185
162
|
summary: YAML backed feature toggles
|
186
163
|
test_files:
|
@@ -192,14 +169,9 @@ test_files:
|
|
192
169
|
- spec/toggles/feature/acceptance/nested_attributes_spec.rb
|
193
170
|
- spec/toggles/feature/acceptance/or_attributes_spec.rb
|
194
171
|
- spec/toggles/feature/acceptance/type_spec.rb
|
195
|
-
- spec/toggles/feature/
|
196
|
-
- spec/toggles/feature/
|
197
|
-
- spec/toggles/feature/operation/attribute_spec.rb
|
198
|
-
- spec/toggles/feature/operation/gt_spec.rb
|
199
|
-
- spec/toggles/feature/operation/in_spec.rb
|
200
|
-
- spec/toggles/feature/operation/lt_spec.rb
|
201
|
-
- spec/toggles/feature/operation/not_spec.rb
|
202
|
-
- spec/toggles/feature/operation/or_spec.rb
|
203
|
-
- spec/toggles/feature/operation/range_spec.rb
|
172
|
+
- spec/toggles/feature/attribute_spec.rb
|
173
|
+
- spec/toggles/feature/operation_spec.rb
|
204
174
|
- spec/toggles/feature/permissions_spec.rb
|
205
175
|
- spec/toggles/feature/subject_spec.rb
|
176
|
+
- spec/toggles/feature_spec.rb
|
177
|
+
- spec/toggles/init_spec.rb
|
data/lib/toggles/feature/base.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
|
3
|
-
module Feature
|
4
|
-
class Base
|
5
|
-
attr_reader :subjects
|
6
|
-
|
7
|
-
def self.enabled_for?(subjects = {})
|
8
|
-
new(subjects).enabled?
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.disabled_for?(subjects = {})
|
12
|
-
!enabled_for? subjects
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(subjects)
|
16
|
-
@subjects = subjects
|
17
|
-
end
|
18
|
-
|
19
|
-
def permissions
|
20
|
-
@permissions ||= self.class::PERMISSIONS
|
21
|
-
end
|
22
|
-
|
23
|
-
def enabled?
|
24
|
-
permissions.valid_for? subjects
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Feature
|
2
|
-
module Operation
|
3
|
-
class Attribute
|
4
|
-
def self.call(entity, attr_name, expected)
|
5
|
-
if expected.kind_of? Hash
|
6
|
-
expected.all? do |operation, rules|
|
7
|
-
if OPERATIONS.include? operation.to_sym
|
8
|
-
OPERATIONS[operation.to_sym].call(entity, attr_name, rules)
|
9
|
-
else
|
10
|
-
Operation::Attribute.call(entity.send(attr_name), operation, rules)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
else
|
14
|
-
entity.send(attr_name) == expected
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Feature
|
2
|
-
module Operation
|
3
|
-
class Not
|
4
|
-
def self.call(entity, attr_name, expected)
|
5
|
-
if expected.kind_of? Hash
|
6
|
-
expected.none? do |operation, value|
|
7
|
-
OPERATIONS[operation.to_sym].call(entity, attr_name, value)
|
8
|
-
end
|
9
|
-
else
|
10
|
-
entity.send(attr_name) != expected
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Feature
|
2
|
-
module Operation
|
3
|
-
class Or
|
4
|
-
def self.call(entity, attr_name, expected)
|
5
|
-
expected.any? do |operation, value|
|
6
|
-
if OPERATIONS.include? operation.to_sym
|
7
|
-
OPERATIONS[operation.to_sym].call(entity, attr_name, value)
|
8
|
-
else
|
9
|
-
Operation::Attribute.call(entity, operation, value)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,8 +0,0 @@
|
|
1
|
-
require "toggles/feature/operation/and"
|
2
|
-
require "toggles/feature/operation/attribute"
|
3
|
-
require "toggles/feature/operation/gt"
|
4
|
-
require "toggles/feature/operation/in"
|
5
|
-
require "toggles/feature/operation/lt"
|
6
|
-
require "toggles/feature/operation/not"
|
7
|
-
require "toggles/feature/operation/or"
|
8
|
-
require "toggles/feature/operation/range"
|
@@ -1,10 +0,0 @@
|
|
1
|
-
describe Feature::Base do
|
2
|
-
let(:user) { double(id: 1, logged_in?: true) }
|
3
|
-
let(:widget) { double(id: 2) }
|
4
|
-
|
5
|
-
subject { Feature::MultipleSubjects.new(user: user, widget: widget) }
|
6
|
-
|
7
|
-
its(:enabled?) { is_expected.to eq true }
|
8
|
-
its(:subjects) { is_expected.to eq user: user, widget: widget }
|
9
|
-
its("permissions.subjects") { is_expected.to eq [:user, :widget] }
|
10
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
describe Feature::Operation::Attribute do
|
2
|
-
specify do
|
3
|
-
expect(described_class.call(double(id: 50), :id, 50)).to eq true
|
4
|
-
expect(described_class.call(double(id: 50), :id, 51)).to eq false
|
5
|
-
|
6
|
-
expect(described_class.call(double(id: 50), :id, {"in" => (0..50), "not" => 49})).to eq true
|
7
|
-
expect(described_class.call(double(id: 49), :id, {"in" => (0..50), "not" => 49})).to eq false
|
8
|
-
end
|
9
|
-
end
|
@@ -1,7 +0,0 @@
|
|
1
|
-
describe Feature::Operation::In do
|
2
|
-
specify do
|
3
|
-
expect(described_class.call(double(id: 50), :id, {"range" => [40, 60]})).to eq true
|
4
|
-
expect(described_class.call(double(id: 50), :id, [1])).to eq false
|
5
|
-
expect(described_class.call(double(id: nil), :id, [1])).to eq false
|
6
|
-
end
|
7
|
-
end
|