diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e49532..ee0b884 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rubyversion: ['2.4', '2.5', '2.6', '2.7', '3.0'] + rubyversion: ['2.7', '3.0', '3.1'] steps: - uses: actions/checkout@v2 - name: set up ruby diff --git a/lib/toggles.rb b/lib/toggles.rb index f8d99ad..1dc32cc 100644 --- a/lib/toggles.rb +++ b/lib/toggles.rb @@ -34,9 +34,7 @@ def init path = Pathname.new(abspath).relative_path_from(top_level_p).to_s features = path.split('/')[0..-2].inject(Feature.features) { |a, e| a[e.to_sym] ||= {} } feature_key = File.basename(path, File.extname(path)).to_sym - features[feature_key] = Class.new(Feature::Base) do |c| - c.const_set(:PERMISSIONS, Feature::Permissions.new(abspath)) - end + features[feature_key] = Feature::Permissions.from_yaml(abspath) end stbuf = File.stat(top_level) diff --git a/lib/toggles/feature.rb b/lib/toggles/feature.rb index c26a945..01fc80c 100644 --- a/lib/toggles/feature.rb +++ b/lib/toggles/feature.rb @@ -31,7 +31,6 @@ def self.disabled?(*sym, **criteria) end require 'toggles/constant_lookup' -require "toggles/feature/base" require "toggles/feature/attribute" require "toggles/feature/permissions" require "toggles/feature/subject" diff --git a/lib/toggles/feature/base.rb b/lib/toggles/feature/base.rb deleted file mode 100644 index 4011677..0000000 --- a/lib/toggles/feature/base.rb +++ /dev/null @@ -1,27 +0,0 @@ -require "yaml" - -module Feature - class Base - attr_reader :subjects - - def self.enabled_for?(subjects = {}) - new(subjects).enabled? - end - - def self.disabled_for?(subjects = {}) - !enabled_for? subjects - end - - def initialize(subjects) - @subjects = subjects - end - - def permissions - @permissions ||= self.class::PERMISSIONS - end - - def enabled? - permissions.valid_for? subjects - end - end -end diff --git a/lib/toggles/feature/permissions.rb b/lib/toggles/feature/permissions.rb index cae8731..a4e1949 100644 --- a/lib/toggles/feature/permissions.rb +++ b/lib/toggles/feature/permissions.rb @@ -1,15 +1,16 @@ require 'ostruct' require 'forwardable' +require 'yaml' -class Feature::Permissions +Feature::Permissions = Struct.new(:rules) do extend Forwardable - attr_reader :rules - def_delegators :rules, :all?, :keys - def initialize(path) - @rules = YAML.load(File.read(path)) + def self.from_yaml(path) + new( + YAML.safe_load(File.read(path), permitted_classes: [Symbol]) + ) end def subjects @@ -17,7 +18,9 @@ def subjects end def valid_for?(entities) - raise Feature::Subject::Invalid, Feature::Subject.difference(subjects, entities.keys) unless subjects == entities.keys + unless subjects == entities.keys + raise Feature::Subject::Invalid, Feature::Subject.difference(subjects, entities.keys) + end rules.all? do |name, rule| entity = entities[name.to_sym] @@ -34,4 +37,12 @@ def valid_for?(entities) end end end + + def enabled_for?(subjects = {}) + valid_for?(subjects) + end + + def disabled_for?(subjects = {}) + !valid_for?(subjects) + end end diff --git a/spec/toggles/feature/base_spec.rb b/spec/toggles/feature/base_spec.rb deleted file mode 100644 index 07f5a74..0000000 --- a/spec/toggles/feature/base_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -describe Feature::Base do - let(:user) { double(id: 1, logged_in?: true) } - let(:widget) { double(id: 2) } - - context 'multiple subjects' do - subject { Feature::MultipleSubjects.new(user: user, widget: widget) } - - its(:enabled?) do is_expected.to eq true end - its(:subjects) do is_expected.to eq user: user, widget: widget end - its('permissions.subjects') { is_expected.to eq %i[user widget] } - end - - context 'abbreviation with numbers' do - subject { Feature::AbbreviationsCN22.new(user: user) } - - its(:enabled?) do is_expected.to eq true end - its(:subjects) do is_expected.to eq user: user end - its('permissions.subjects') { is_expected.to eq [:user] } - end - - context 'irregular capitalization' do - subject { Feature::S3File.new(user: user) } - - its(:enabled?) do is_expected.to eq true end - its(:subjects) do is_expected.to eq user: user end - its('permissions.subjects') { is_expected.to eq [:user] } - end - - context 'irregular capitalization' do - subject { Feature::FileS3.new(user: user) } - - its(:enabled?) do is_expected.to eq true end - its(:subjects) do is_expected.to eq user: user end - its('permissions.subjects') { is_expected.to eq [:user] } - end -end diff --git a/spec/toggles/feature/permissions_spec.rb b/spec/toggles/feature/permissions_spec.rb index e6320f3..68df562 100644 --- a/spec/toggles/feature/permissions_spec.rb +++ b/spec/toggles/feature/permissions_spec.rb @@ -1,7 +1,7 @@ describe Feature::Permissions do let(:path) { "features/multiple_subjects.yml" } - subject { Feature::Permissions.new(path) } + subject { Feature::Permissions.from_yaml(path) } its(:rules) { is_expected.to eq({"user"=>{"id"=>1, "logged_in?"=>true}, "widget"=>{"id"=>2}}) } diff --git a/spec/toggles/feature_spec.rb b/spec/toggles/feature_spec.rb new file mode 100644 index 0000000..c81b593 --- /dev/null +++ b/spec/toggles/feature_spec.rb @@ -0,0 +1,37 @@ +describe 'features' do + let(:user) { double(id: 1, logged_in?: true) } + let(:widget) { double(id: 2) } + + context 'multiple subjects' do + specify { expect(Feature).to be_enabled(:multiple_subjects, user: user, widget: widget) } + specify { expect(Feature::MultipleSubjects).to be_enabled_for(user: user, widget: widget) } + specify { expect(Feature).not_to be_disabled(:multiple_subjects, user: user, widget: widget) } + specify { expect(Feature::MultipleSubjects).not_to be_disabled_for(user: user, widget: widget) } + end + + context 'abbreviation with numbers' do + subject { Feature::AbbreviationsCN22.new(user: user) } + + specify { expect(Feature).to be_enabled(:abbreviations_cn22, user: user) } + specify { expect(Feature::AbbreviationsCN22).to be_enabled_for(user: user) } + specify { expect(Feature).not_to be_disabled(:abbreviations_cn22, user: user) } + specify { expect(Feature::AbbreviationsCN22).not_to be_disabled_for(user: user) } + end + + context 'irregular capitalization' do + specify { expect(Feature).to be_enabled(:s3_file, user: user) } + specify { expect(Feature::S3File).to be_enabled_for(user: user) } + specify { expect(Feature).not_to be_disabled(:s3_file, user: user) } + specify { expect(Feature).to be_disabled(:s3_file, user: widget) } + specify { expect(Feature::S3File).not_to be_disabled_for(user: user) } + specify { expect(Feature::S3File).to be_disabled_for(user: widget) } + end + + context 'irregular capitalization' do + specify { expect(Feature).to be_enabled(:file_s3, user: user) } + specify { expect(Feature::FileS3).to be_enabled_for(user: user) } + specify { expect(Feature).not_to be_disabled(:file_s3, user: user) } + specify { expect(Feature::FileS3).not_to be_disabled_for(user: user) } + specify { expect(Feature).to be_disabled(:file_s3, user: widget) } + end +end