Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ If a resource needs to be conveyed as a feature flag option, utilize the `option
resource :user, type: User, option: true
```

To call a feature without passing a resource, use the `required: false` option (e.g., for managing the global state of the feature).

```ruby
resource :user, type: User, option: true, required: false
```

To transfer a resource to a nested group, utilize the `nested` option:

```ruby
Expand Down
1 change: 1 addition & 0 deletions examples/usual/example1/d_feature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class DFeature < Usual::Example1::Base
# prefix :usual_example_1_d

resource :record, type: Usual::Example1::MainFeature::Record
resource :comment, type: String, required: false

condition ->(resources:) { resources.record.id == "123" }

Expand Down
1 change: 1 addition & 0 deletions examples/usual/example1/e_feature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class EFeature < Usual::Example1::Base
prefix :usual_example_1_e

resource :record, type: Usual::Example1::MainFeature::Record
resource :comment, type: String, required: false

condition ->(resources:) { resources.record.id == "123" }

Expand Down
3 changes: 3 additions & 0 deletions examples/usual/example1/main_feature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ module Example1
class MainFeature < Usual::Example1::Base
Record = Struct.new(:id, keyword_init: true)
User = Struct.new(:id, keyword_init: true)
Thing = Struct.new(:id, keyword_init: true)

prefix :usual_example_1

resource :record, type: Record, nested: true
resource :user, type: User, option: true
resource :thing, type: Thing, option: true, required: false
resource :comment, type: String, nested: true, required: false

condition ->(resources:) { resources.record.id == "123" }

Expand Down
12 changes: 8 additions & 4 deletions lib/featury/actions/service/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ def create
@model_class.const_set(Builder::SERVICE_CLASS_NAME, class_sample)
end

def create_service_class # rubocop:disable Metrics/MethodLength, Metrics/AbcSize,Metrics/CyclomaticComplexity
def create_service_class # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
collection_of_resources = @collection_of_resources

Class.new(Featury::Service::Builder) do
collection_of_resources.each do |resource|
input resource.name, **resource.options
input resource.name, required: resource.required?, **resource.options
end

input :action, type: Featury::Actions::Action
Expand Down Expand Up @@ -75,19 +75,23 @@ def conditions_are_true
end
end

def features_are_true
def features_are_true # rubocop:disable Metrics/AbcSize
options = inputs.collection_of_resources.only_option.to_h do |resource|
[resource.name, inputs.public_send(resource.name)]
end

options = options.reject { |_, v| v.blank? }

inputs.action.block.call(features: inputs.collection_of_features.names, **options)
end

def groups_are_true
def groups_are_true # rubocop:disable Metrics/AbcSize
arguments = inputs.collection_of_resources.only_nested.to_h do |resource|
[resource.name, inputs.public_send(resource.name)]
end

arguments = arguments.reject { |_, v| v.blank? }

inputs.collection_of_groups.all? do |group|
group.group_class.public_send(inputs.action.name, **arguments)
end
Expand Down
9 changes: 7 additions & 2 deletions lib/featury/resources/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ class Resource
def initialize(name, **options)
@name = name

@nested = options.delete(:nested) || false
@option = options.delete(:option) || false
@nested = options.delete(:nested) { false }
@option = options.delete(:option) { false }
@required = options.delete(:required) { true }

@options = options
end
Expand All @@ -21,6 +22,10 @@ def nested?
def option?
@option
end

def required?
@required
end
end
end
end
104 changes: 77 additions & 27 deletions spec/examples/usual/example1/main_feature_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@
let(:arguments) do
{
record:,
user:
user:,
thing:,
comment:
}
end

let(:record) { Usual::Example1::MainFeature::Record.new(id: "123") }
let(:user) { Usual::Example1::MainFeature::User.new(id: "456") }
let(:thing) { Usual::Example1::MainFeature::Thing.new(id: "789") }
let(:comment) { "A comment" }

let(:expected_resources_for_options) { [user, thing] }

shared_examples "expected successful behavior" do
describe "#enabled?" do
subject(:perform) { feature_class.enabled? }

before do
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_a, user).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_b, user).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_c, user).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_a,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_b,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_c,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_d_i).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_d_ii).and_call_original
allow(FeatureLib).to receive(:enabled?).with(:usual_example_1_d_iii).and_call_original
Expand Down Expand Up @@ -82,9 +91,9 @@
it :aggregate_failures do
perform

expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_a, user).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_b, user).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_c, user).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_a, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_b, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_c, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_d_i).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_d_ii).once
expect(FeatureLib).to have_received(:enabled?).with(:usual_example_1_d_iii).once
Expand Down Expand Up @@ -176,9 +185,12 @@
subject(:perform) { feature_class.disabled? }

before do
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_a, user).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_b, user).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_c, user).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_a,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_b,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_c,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_d_i).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_d_ii).and_call_original
allow(FeatureLib).to receive(:disabled?).with(:usual_example_1_d_iii).and_call_original
Expand Down Expand Up @@ -242,9 +254,9 @@
it :aggregate_failures do
perform

expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_a, user).once
expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_b, user).once
expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_c, user).once
expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_a, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_b, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:disabled?).with(:usual_example_1_c, *expected_resources_for_options).once
expect(FeatureLib).not_to have_received(:disabled?).with(:usual_example_1_d_i)
expect(FeatureLib).not_to have_received(:disabled?).with(:usual_example_1_d_ii)
expect(FeatureLib).not_to have_received(:disabled?).with(:usual_example_1_d_iii)
Expand Down Expand Up @@ -332,9 +344,12 @@
subject(:perform) { feature_class.enable }

before do
allow(FeatureLib).to receive(:enable).with(:usual_example_1_a, user).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_b, user).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_c, user).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_a,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_b,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_c,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_d_i).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_d_ii).and_call_original
allow(FeatureLib).to receive(:enable).with(:usual_example_1_d_iii).and_call_original
Expand Down Expand Up @@ -398,9 +413,9 @@
it :aggregate_failures do
perform

expect(FeatureLib).to have_received(:enable).with(:usual_example_1_a, user).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_b, user).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_c, user).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_a, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_b, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_c, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_d_i).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_d_ii).once
expect(FeatureLib).to have_received(:enable).with(:usual_example_1_d_iii).once
Expand Down Expand Up @@ -489,9 +504,12 @@
subject(:perform) { feature_class.disable }

before do
allow(FeatureLib).to receive(:disable).with(:usual_example_1_a, user).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_b, user).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_c, user).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_a,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_b,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_c,
*expected_resources_for_options).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_d_i).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_d_ii).and_call_original
allow(FeatureLib).to receive(:disable).with(:usual_example_1_d_iii).and_call_original
Expand Down Expand Up @@ -555,9 +573,9 @@
it :aggregate_failures do
perform

expect(FeatureLib).to have_received(:disable).with(:usual_example_1_a, user).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_b, user).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_c, user).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_a, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_b, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_c, *expected_resources_for_options).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_d_i).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_d_ii).once
expect(FeatureLib).to have_received(:disable).with(:usual_example_1_d_iii).once
Expand Down Expand Up @@ -692,7 +710,37 @@
context "when `with` method is used" do
let(:feature_class) { described_class.with(**arguments) }

it_behaves_like "expected successful behavior"
context "when optional resources are passed" do
it_behaves_like "expected successful behavior"
end

context "when optional resources are not passed" do
let(:arguments) do
{
record:,
user:
}
end

let(:expected_resources_for_options) { [user] }

it_behaves_like "expected successful behavior"
end

context "when optional resources passed with blank values" do
let(:arguments) do
{
record:,
user:,
thing: nil,
comment: ""
}
end

let(:expected_resources_for_options) { [user] }

it_behaves_like "expected successful behavior"
end
end

context "when `with` method is not used" do
Expand Down Expand Up @@ -739,7 +787,9 @@
it do
expect(perform.resources).to contain_exactly(
:record,
:user
:user,
:thing,
:comment
)
end

Expand Down