toggles 0.1.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +24 -0
  3. data/CHANGELOG.md +83 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +13 -0
  6. data/README.md +76 -0
  7. data/features/abbreviations_cn22.yml +3 -0
  8. data/features/file_s3.yml +3 -0
  9. data/features/nested_foo/bar_baz.yml +3 -0
  10. data/features/s3_file.yml +3 -0
  11. data/lib/toggles/constant_lookup.rb +50 -0
  12. data/lib/toggles/feature/attribute.rb +15 -0
  13. data/lib/toggles/feature/operation/and.rb +0 -15
  14. data/lib/toggles/feature/operation/in.rb +1 -1
  15. data/lib/toggles/feature/operation/range.rb +0 -2
  16. data/lib/toggles/feature/permissions.rb +34 -29
  17. data/lib/toggles/feature.rb +80 -12
  18. data/lib/toggles/version.rb +5 -0
  19. data/lib/toggles.rb +39 -38
  20. data/spec/spec_helper.rb +8 -3
  21. data/spec/toggles/feature/acceptance/collection_spec.rb +1 -1
  22. data/spec/toggles/feature/acceptance/complex_and_spec.rb +1 -1
  23. data/spec/toggles/feature/acceptance/multiple_subjects_spec.rb +1 -1
  24. data/spec/toggles/feature/acceptance/nested_attributes_spec.rb +1 -1
  25. data/spec/toggles/feature/acceptance/or_attributes_spec.rb +1 -1
  26. data/spec/toggles/feature/acceptance/type_spec.rb +20 -4
  27. data/spec/toggles/feature/attribute_spec.rb +9 -0
  28. data/spec/toggles/feature/operation_spec.rb +69 -0
  29. data/spec/toggles/feature/permissions_spec.rb +1 -1
  30. data/spec/toggles/feature_spec.rb +37 -0
  31. data/spec/toggles/init_spec.rb +89 -0
  32. data/toggles.gemspec +25 -15
  33. metadata +39 -67
  34. data/lib/toggles/feature/base.rb +0 -27
  35. data/lib/toggles/feature/operation/attribute.rb +0 -19
  36. data/lib/toggles/feature/operation/lt.rb +0 -9
  37. data/lib/toggles/feature/operation/not.rb +0 -15
  38. data/lib/toggles/feature/operation/or.rb +0 -15
  39. data/lib/toggles/feature/operation.rb +0 -8
  40. data/spec/toggles/feature/base_spec.rb +0 -10
  41. data/spec/toggles/feature/operation/and_spec.rb +0 -9
  42. data/spec/toggles/feature/operation/attribute_spec.rb +0 -9
  43. data/spec/toggles/feature/operation/gt_spec.rb +0 -6
  44. data/spec/toggles/feature/operation/in_spec.rb +0 -7
  45. data/spec/toggles/feature/operation/lt_spec.rb +0 -6
  46. data/spec/toggles/feature/operation/not_spec.rb +0 -6
  47. data/spec/toggles/feature/operation/or_spec.rb +0 -6
  48. data/spec/toggles/feature/operation/range_spec.rb +0 -5
@@ -1,4 +1,4 @@
1
- describe Feature::OrAttributes do
1
+ describe "Feature::OrAttributes" do
2
2
  specify do
3
3
  expect(Feature::OrAttributes.enabled_for?(
4
4
  user: double(foo: 20, bar: 10))).to eq true
@@ -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
- expect(Feature::Type.enabled_for?(user_id: 1)).to eq true
4
- expect(Feature::Type.enabled_for?(user_id: 25)).to eq false
5
- expect(Feature::Type.enabled_for?(user_id: nil)).to eq false
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.new(path) }
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 = "toggles"
3
- s.version = "0.1.1"
4
- s.authors = ["Andrew Tribone"]
5
- s.summary = "YAML backed feature toggles"
6
- s.email = "tribone@easypost.com"
7
- s.homepage = "https://github.com/att14/toggles"
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(/^(spec)\//)
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 "bundler"
13
- s.add_development_dependency "pry"
14
- s.add_development_dependency "pry-nav"
15
- s.add_development_dependency "pry-remote"
16
- s.add_development_dependency "rake"
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.1.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Tribone
8
- autorequire:
8
+ - James Brown
9
+ - Josh Lane
10
+ autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2015-08-25 00:00:00.000000000 Z
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: pry
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: rake
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-its
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
- email: tribone@easypost.com
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/base.rb
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/base_spec.rb
151
- - spec/toggles/feature/operation/and_spec.rb
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/att14/toggles
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
- rubyforge_project:
182
- rubygems_version: 2.2.2
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/base_spec.rb
196
- - spec/toggles/feature/operation/and_spec.rb
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
@@ -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,9 +0,0 @@
1
- module Feature
2
- module Operation
3
- class LessThan
4
- def self.call(entity, attr_name, expected)
5
- entity.send(attr_name) < expected
6
- end
7
- end
8
- end
9
- 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::And do
2
- specify do
3
- [[60, true], [40, false], [80, false]].each do |(id, expected)|
4
- expect(
5
- described_class.call(double(id: id), :id, {"gt" => 50, "lt" => 70})
6
- ).to eq expected
7
- end
8
- end
9
- 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,6 +0,0 @@
1
- describe Feature::Operation::GreaterThan do
2
- specify do
3
- expect(described_class.call(double(id: 50), :id, 40)).to eq true
4
- expect(described_class.call(double(id: 50), :id, 60)).to eq false
5
- end
6
- 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
@@ -1,6 +0,0 @@
1
- describe Feature::Operation::LessThan do
2
- specify do
3
- expect(described_class.call(double(id: 50), :id, 60)).to eq true
4
- expect(described_class.call(double(id: 50), :id, 40)).to eq false
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- describe Feature::Operation::Not do
2
- specify do
3
- expect(described_class.call(double(id: 50), :id, {"in" => (10..20)})).to eq true
4
- expect(described_class.call(double(id: 50), :id, 50)).to eq false
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- describe Feature::Operation::Or do
2
- specify do
3
- expect(described_class.call(double(id: 16), :id, {"in" => (10..20), "not" => 15})).to eq true
4
- expect(described_class.call(double(id: 15), :id, {"in" => (10..20), "not" => 15})).to eq true
5
- end
6
- end