Tom H Tom H - 1 month ago 25
Ruby Question

Can the flipper gem return a count of enabled users?

We are exploring using the flipper gem (https://github.com/jnunemaker/flipper) to gate who sees new features. In one of our first tests, we want to show a specific feature to only the first X users that see a banner promoting it.

We looked at using a percentage, but the business is very specific on the number, and also wants to reach that number right away, then disable the feature for all other users, without disabling it for those that saw it first. Using a percentage, we weren't able to see a way to ensure the correct number would see it, and that everyone of the first x would see it.

Inside the gates/actor.rb, there is this:

enabled_actor_ids = value


which implies we could get the list of enabled ids, and perform a count on that, but we couldn't find whether or where that list may be exposed.

Since we are using the AR adapter as a trial, we instead created a scope on an actor object that joins to the flipper_gates table, but this feels extremely fragile and getting very much into the inner workings of the gem.

Any advice is greatly appreciated.

Answer

You should be able to accomplish this by programmatically turning the feature on for Individual Actors until an upper limit is reached.

IMPORTANT NOTE: according to the documentation:

The individual actor gate is typically not designed for hundreds or thousands of actors to be enabled. This is an explicit choice to make it easier to batch load data from the adapters instead of performing individual checks for actors over and over. If you need to enable something for more than 20 individual people, I would recommend using a group.

Now that we've agreed that we want to move forward with this anyways.. Let's talk about implementation.

Enabling the feature for an actor

The first thing you need to do is to ensure that the actor (probably a User) responds to flipper_id and that the flipper_id is unique for every actor. Once that is set up, you should be able to simply do enable the feature for a user when they see the banner like this:

flipper[:stats].enable_actor user

Counting actors enrolled in a feature

Now, in order to determine if we should enable the feature for a user, we need to determine how many users have been enrolled in the feature.

To do this we can query the Gate directly:

Flipper::Adapters::ActiveRecord::Gate.where(
  feature_key: "stats",
  key: "actors"
).count

This will return a count of the number of actors enrolled in a feature.

How do we know that works?

Well, let's take a look at the gem.

flipper[:stats].enable_actor actually calls Feature#enable_actor with the user we passed in earlier (that responds to flipper_id) being passed in as the actor.

Next, Feature#enable_actor passes the actor into Types::Actor.wrap which creates a new instance of Types::Actor which checks to make sure the actor isn't nil and that it has a flipper_id and then sets two instance variables, thing which is set to the actor, and value which is set to the flipper_id of the actor.

Now that we have an instance of Types::Actor, we pass it into Feature#enable which looks up the gate which in our case would be a Gates::Actor instance. Finally we call enable on the adaptor (which in your case is ActiveRecord).

In Adapters::ActiveRecord.enable we first look at gate.data_type which in our case, is :set. From there we do:

@gate_class.create! do |g|
  g.feature_key = feature.key
  g.key = gate.key
  g.value = thing.value.to_s
end

Where, as mentioned earlier, thing.value is the flipper_id. Bingo! @gate_class is the active record class responsible for the gates table and the default table name is "flipper_gates".

Now we know exactly what to query to get a count of the actors enrolled in the feature!

number_of_actors_enrolled_in_stats_feature = Flipper::Adapters::ActiveRecord::Gate.where(
  feature_key: "stats",
  key: "actors"
).count