Skip to content

Custom Rule Types & Rules

Andrew De Ponte edited this page Jun 30, 2016 · 7 revisions

Togls is specifically architected on top of a generic concept of a Togls::Rule. This empowers both Togls proper and the users to define any custom rule types, and use them to control their feature toggles. For example, you could use them to do A/B testing, define various groups (internal users, alpha testers, beta testers), manage percentage based rollouts, etc.

Defining Custom Rule Types

Custom rule types exist in code as classes that inherit from Togls::Rule that are then registered with Togls via the Togls.rule_types block method.

To implement a custom rule type, a new rule type class must be defined that abides by the following.

  • inherits from Togls::Rule or a decendent of it

  • must not define its own constructor

  • has an instance method named run that takes a required feature key and either a default value parameter of target or a required parameter of target. This method must determine if the toggle should be on(true)/off(false) based on the target, feature key, and initialization data @data and return the appropriate boolean value.

    def run(key, target = 'foo')
    ...
    end
    
    # or
    
    def run(key, target)
    ...
    end
  • has a class level method title which returns a string title of the rule type. This is required as of v3.0.0.

  • has a class level method description which returns a string description of the rule type. This is required as of v3.0.0.

  • has a class level method target_type which returns a symbol identifying the abstract target type that this rule type expects to be passed when being evaluated. Note: This is optional in the case that you have defined a rule type so generic you can't identify the abstract target type. A good example of this is the Togls::Rules::Group rule. It is worth noting that if you don't define the target type in the rule type class it will have to be set when constructing a rule instance.

Registering Custom Rule Types

As of v3.0.0, once you have defined a new rule type class you are required to register the rule type before you can use it. This is done as follows, generally in whatever file you define your feature toggles in.

Togls.rule_types do
  register(:my_custom_rule, MyCustomRule)
end

Registering your target type associates your rule type class with the symbol identifier you provide in the register(rule_type_id, rule_type_class) method. This allows Togls data to be represented in a platform agnostic manner.

Constructing a Custom Rule

Since v3.0.0 we provide a DSL to construct custom rules.

Once you have defined a custom rule type you can construct an instance of that rule type by calling the Togls.rule(rule_id, type_id, data, target_type: Togls::TargetTypes::NOT_SET) method. Having a rule instance allows you to associate that rule instance with a feature toggle when defining your toggles using Togls.feature(:foo, 'desc').on(my_rule_instance). The following is an example of how one would construct an instance of the :my_custom_rule type from above.

Togls.rule(:the_rule, :my_custom_rule, 'some_initialization_data', target_type: :special_target_type) # => instance of MyCustomRule

# or

Togls.rule(:the_rule, :my_custom_rule, 'some_initialization_data') # => instance of MyCustomRule

Prior to v3.0.0 you would have to construct them manually without the DSL. The following is an example.

MyCustomRule.new('some_initialization_data') # => instance of MyCustomRule

You will likely note that in the above example the rule_id, type_id, and target_type are missing. This is because those concepts were added in v3.0.0.

Example Custom Rule Type

Internally Togls uses these custom rule types to provide functionality and will evolve to contain more official rule types over time. If you have a generic custom rule type you think should be part of Togls please make a pull request.

A prime example of a custom rule type provided by Togls is the Togls::Rules::Group rule. You can see it below.

module Togls
  module Rules
    class Group < Togls::Rule
      def self.title
        "Group"
      end

      def self.description
        %Q{
The Group rule allows you to define an arbitrary collection of objects to be
used in evaluating against the target. Specify the initialization data as an
array of inclusive identifiers for the group. When the feature toggle is
evaluated if the passed in target is included in the group then it is evaluated
to on. If not, it evaluates to off. Examples:

# Group defined by user ids
alpha_testers = Togls::Rules::Group.new(:group, [23, 343, 222, 123], target_type: :user_id)

# Group defined by email addresses
beta_testers = Togls::Rules::Group.new(:group, ['bob@example.com', 'cindy@example.com'], target_type: :user_email_address)

Togls.release do
  feature(:foo, 'some foo desc', :user_email_address).on(beta_testers)
end

Togls.feature(:foo).on?('jack@example.com') # evalutes to false (a.k.a. off)
        }
      end

      def run(_key, target)
        @data.include?(target)
      end
    end
  end
end

Lets take a closer look at exactly what is going on here.

  • it is inheriting from the Togls::Rule class which meets one of the minimum requirements for defining a custom rule type.
  • it uses the base class (Togls::Rule) defined constructor which meets one of the minimum requirements for defining a custom rule type.
  • it defines a run method that returns a boolean value identifying if the feature should be on(true)/off(false) for the given key and target. It does so by identifying if the array passed in at construction time include? the given target. This meets one of the minimum requirement for a rule.
  • the run method signature requires a key and target. This meets a minimum requirement for a rule as well. It also makes sense in the case of group based rule as it has to have something to compare against the group.
  • the self.title method properly returns a string as the title as required
  • the self.description method properly returns a string as the description
  • the self.target_type method is not defined, so it falls back to the self.target_type method provided by Togls::Rule which returns Togls::TargetTypes::NOT_SET indicating that the rule type isn't able to specify its target type. This in turn, forces the target type to be assigned during instantiation of the rule.

Custom Rules Types & Rules are a simple yet extremely powerful concept that you shouldn't hesitate to use.