class Flipper::Adapters::ActiveRecord

Attributes

name[R]

Public: The name of the adapter.

Public Class Methods

new(options = {}) click to toggle source

Public: Initialize a new ActiveRecord adapter instance.

name - The Symbol name for this adapter. Optional (default :active_record) feature_class - The AR class responsible for the features table. gate_class - The AR class responsible for the gates table.

Allowing the overriding of name is so you can differentiate multiple instances of this adapter from each other, if, for some reason, that is a thing you do.

Allowing the overriding of the default feature/gate classes means you can roll your own tables and what not, if you so desire.

# File lib/flipper/adapters/active_record.rb, line 35
def initialize(options = {})
  @name = options.fetch(:name, :active_record)
  @feature_class = options.fetch(:feature_class) { Feature }
  @gate_class = options.fetch(:gate_class) { Gate }
end

Public Instance Methods

add(feature) click to toggle source

Public: Adds a feature to the set of known features.

# File lib/flipper/adapters/active_record.rb, line 47
def add(feature)
  # race condition, but add is only used by enable/disable which happen
  # super rarely, so it shouldn't matter in practice; additionally
  # to_a.first is used instead of first because of a Ruby 2.4/Rails 3.2.21
  # CI failure (https://travis-ci.org/jnunemaker/flipper/jobs/297274000).
  unless @feature_class.where(key: feature.key).to_a.first
    @feature_class.create! { |f| f.key = feature.key }
  end
  true
end
clear(feature) click to toggle source

Public: Clears the gate values for a feature.

# File lib/flipper/adapters/active_record.rb, line 68
def clear(feature)
  @gate_class.where(feature_key: feature.key).delete_all
  true
end
disable(feature, gate, thing) click to toggle source

Public: Disables a gate for a given thing.

feature - The Flipper::Feature for the gate. gate - The Flipper::Gate to disable. thing - The Flipper::Type being disabled for the gate.

Returns true.

# File lib/flipper/adapters/active_record.rb, line 134
def disable(feature, gate, thing)
  case gate.data_type
  when :boolean
    clear(feature)
  when :integer
    @gate_class.transaction do
      @gate_class.where(
        feature_key: feature.key,
        key: gate.key
      ).delete_all

      @gate_class.create! do |g|
        g.feature_key = feature.key
        g.key = gate.key
        g.value = thing.value.to_s
      end
    end
  when :set
    @gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).delete_all
  else
    unsupported_data_type gate.data_type
  end

  true
end
enable(feature, gate, thing) click to toggle source

Public: Enables a gate for a given thing.

feature - The Flipper::Feature for the gate. gate - The Flipper::Gate to disable. thing - The Flipper::Type being enabled for the gate.

Returns true.

# File lib/flipper/adapters/active_record.rb, line 114
def enable(feature, gate, thing)
  case gate.data_type
  when :boolean, :integer
    enable_single(feature, gate, thing)
  when :set
    enable_multi(feature, gate, thing)
  else
    unsupported_data_type gate.data_type
  end

  true
end
features() click to toggle source

Public: The set of known features.

# File lib/flipper/adapters/active_record.rb, line 42
def features
  @feature_class.all.map(&:key).to_set
end
get(feature) click to toggle source

Public: Gets the values for all gates for a given feature.

Returns a Hash of Flipper::Gate#key => value.

# File lib/flipper/adapters/active_record.rb, line 76
def get(feature)
  db_gates = @gate_class.where(feature_key: feature.key)
  result_for_feature(feature, db_gates)
end
get_all() click to toggle source
# File lib/flipper/adapters/active_record.rb, line 91
def get_all
  rows = ::ActiveRecord::Base.connection.select_all <<-SQL
    SELECT ff.key AS feature_key, fg.key, fg.value
    FROM #{@feature_class.table_name} ff
    LEFT JOIN #{@gate_class.table_name} fg ON ff.key = fg.feature_key
  SQL
  db_gates = rows.map { |row| Gate.new(row) }
  grouped_db_gates = db_gates.group_by(&:feature_key)
  result = Hash.new { |hash, key| hash[key] = default_config }
  features = grouped_db_gates.keys.map { |key| Flipper::Feature.new(key, self) }
  features.each do |feature|
    result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
  end
  result
end
get_multi(features) click to toggle source
# File lib/flipper/adapters/active_record.rb, line 81
def get_multi(features)
  db_gates = @gate_class.where(feature_key: features.map(&:key))
  grouped_db_gates = db_gates.group_by(&:feature_key)
  result = {}
  features.each do |feature|
    result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
  end
  result
end
remove(feature) click to toggle source

Public: Removes a feature from the set of known features.

# File lib/flipper/adapters/active_record.rb, line 59
def remove(feature)
  @feature_class.transaction do
    @feature_class.where(key: feature.key).delete_all
    clear(feature)
  end
  true
end
unsupported_data_type(data_type) click to toggle source

Private

# File lib/flipper/adapters/active_record.rb, line 161
def unsupported_data_type(data_type)
  raise "#{data_type} is not supported by this adapter"
end

Private Instance Methods

enable_multi(feature, gate, thing) click to toggle source
# File lib/flipper/adapters/active_record.rb, line 178
def enable_multi(feature, gate, thing)
  @gate_class.create! do |g|
    g.feature_key = feature.key
    g.key = gate.key
    g.value = thing.value.to_s
  end
rescue ::ActiveRecord::RecordNotUnique
rescue ::ActiveRecord::StatementInvalid => error
  raise unless error.message =~ /unique/i
end
enable_single(feature, gate, thing) click to toggle source
# File lib/flipper/adapters/active_record.rb, line 167
def enable_single(feature, gate, thing)
  @gate_class.transaction do
    @gate_class.where(feature_key: feature.key, key: gate.key).delete_all
    @gate_class.create! do |g|
      g.feature_key = feature.key
      g.key = gate.key
      g.value = thing.value.to_s
    end
  end
end
result_for_feature(feature, db_gates) click to toggle source
# File lib/flipper/adapters/active_record.rb, line 189
def result_for_feature(feature, db_gates)
  db_gates ||= []
  result = {}
  feature.gates.each do |gate|
    result[gate.key] =
      case gate.data_type
      when :boolean
        if db_gate = db_gates.detect { |db_gate| db_gate.key == gate.key.to_s }
          db_gate.value
        end
      when :integer
        if db_gate = db_gates.detect { |db_gate| db_gate.key == gate.key.to_s }
          db_gate.value
        end
      when :set
        db_gates.select { |db_gate| db_gate.key == gate.key.to_s }.map(&:value).to_set
      else
        unsupported_data_type gate.data_type
      end
  end
  result
end