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.
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