toggles 0.1.1 → 0.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 +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
|