# frozen_string_literal: true class Rollout class Feature attr_accessor :groups, :users, :percentage, :data attr_reader :name, :options def initialize(name, string = nil, opts = {}) @options = opts @name = name if string raw_percentage, raw_users, raw_groups, raw_data = string.split('|', 4) @percentage = raw_percentage.to_f @users = users_from_string(raw_users) @groups = groups_from_string(raw_groups) @data = raw_data.nil? || raw_data.strip.empty? ? {} : JSON.parse(raw_data) else clear end end def serialize "#{@percentage}|#{@users.to_a.join(',')}|#{@groups.to_a.join(',')}|#{serialize_data}" end def add_user(user) id = user_id(user) @users << id unless @users.include?(id) end def remove_user(user) @users.delete(user_id(user)) end def add_group(group) @groups << group.to_sym unless @groups.include?(group.to_sym) end def remove_group(group) @groups.delete(group.to_sym) end def clear @groups = groups_from_string('') @users = users_from_string('') @percentage = 0 @data = {} end def active?(rollout, user) if user id = user_id(user) user_in_percentage?(id) || user_in_active_users?(id) || user_in_active_group?(user, rollout) else @percentage == 100 end end def user_in_active_users?(user) @users.include?(user_id(user)) end def to_hash { percentage: @percentage, groups: @groups, users: @users, data: @data, } end private def user_id(user) if user.is_a?(Integer) || user.is_a?(String) user.to_s else user.send(id_user_by).to_s end end def id_user_by @options[:id_user_by] || :id end def user_in_percentage?(user) Zlib.crc32(user_id_for_percentage(user)) < RAND_BASE * @percentage end def user_id_for_percentage(user) if @options[:randomize_percentage] user_id(user).to_s + @name.to_s else user_id(user) end end def user_in_active_group?(user, rollout) @groups.any? do |g| rollout.active_in_group?(g, user) end end def serialize_data return '' unless @data.is_a? Hash @data.to_json end def users_from_string(raw_users) users = (raw_users || '').split(',').map(&:to_s) if @options[:use_sets] users.to_set else users end end def groups_from_string(raw_groups) groups = (raw_groups || '').split(',').map(&:to_sym) if @options[:use_sets] groups.to_set else groups end end end end