class Flipper::Adapters::ActiveRecord
Attributes
Public: The name of the adapter.
Public Class Methods
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
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
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
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
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
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
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
# 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
# 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
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
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
# 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
# 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
# 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