flipper-active_record 0.26.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/flipper/adapters/active_record.rb +66 -48
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/active_record_spec.rb +122 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5244fa15983cbfcba71bcf8d555d71d3e9142314d035dc7dad084ea413d0abfd
|
4
|
+
data.tar.gz: 810a08e5c66d66765f8a09e5b4554804792200b0fa8084fd38bb12a2db44cb08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20d45ea8dc75cbab5be320ea9ca22487f7a15483088c2370d48bb0e1b01e1fbf6cb657f9cd3f17ffd02c4fbeef1e20a80d3f328eb5a29d03961ddbfc87bd1439
|
7
|
+
data.tar.gz: 5d3871e886483ee2c9cefa90ef1fb918b4fd795c46121251b62c4c7543868578ba519f6f9547a58a749c87f4840e09496ef9c403f1765b91851be8bfd44c5d6b
|
@@ -53,18 +53,20 @@ module Flipper
|
|
53
53
|
|
54
54
|
# Public: The set of known features.
|
55
55
|
def features
|
56
|
-
@feature_class.all.map(&:key).to_set
|
56
|
+
with_connection(@feature_class) { @feature_class.all.map(&:key).to_set }
|
57
57
|
end
|
58
58
|
|
59
59
|
# Public: Adds a feature to the set of known features.
|
60
60
|
def add(feature)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
with_connection(@feature_class) do
|
62
|
+
# race condition, but add is only used by enable/disable which happen
|
63
|
+
# super rarely, so it shouldn't matter in practice
|
64
|
+
@feature_class.transaction do
|
65
|
+
unless @feature_class.where(key: feature.key).first
|
66
|
+
begin
|
67
|
+
@feature_class.create! { |f| f.key = feature.key }
|
68
|
+
rescue ::ActiveRecord::RecordNotUnique
|
69
|
+
end
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -74,16 +76,18 @@ module Flipper
|
|
74
76
|
|
75
77
|
# Public: Removes a feature from the set of known features.
|
76
78
|
def remove(feature)
|
77
|
-
@feature_class
|
78
|
-
@feature_class.
|
79
|
-
|
79
|
+
with_connection(@feature_class) do
|
80
|
+
@feature_class.transaction do
|
81
|
+
@feature_class.where(key: feature.key).destroy_all
|
82
|
+
clear(feature)
|
83
|
+
end
|
80
84
|
end
|
81
85
|
true
|
82
86
|
end
|
83
87
|
|
84
88
|
# Public: Clears the gate values for a feature.
|
85
89
|
def clear(feature)
|
86
|
-
@gate_class.where(feature_key: feature.key).destroy_all
|
90
|
+
with_connection(@gate_class) { @gate_class.where(feature_key: feature.key).destroy_all }
|
87
91
|
true
|
88
92
|
end
|
89
93
|
|
@@ -91,35 +95,39 @@ module Flipper
|
|
91
95
|
#
|
92
96
|
# Returns a Hash of Flipper::Gate#key => value.
|
93
97
|
def get(feature)
|
94
|
-
db_gates = @gate_class.where(feature_key: feature.key)
|
98
|
+
db_gates = with_connection(@gate_class) { @gate_class.where(feature_key: feature.key) }
|
95
99
|
result_for_feature(feature, db_gates)
|
96
100
|
end
|
97
101
|
|
98
102
|
def get_multi(features)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
with_connection(@gate_class) do
|
104
|
+
db_gates = @gate_class.where(feature_key: features.map(&:key))
|
105
|
+
grouped_db_gates = db_gates.group_by(&:feature_key)
|
106
|
+
result = {}
|
107
|
+
features.each do |feature|
|
108
|
+
result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
|
109
|
+
end
|
110
|
+
result
|
104
111
|
end
|
105
|
-
result
|
106
112
|
end
|
107
113
|
|
108
114
|
def get_all
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
115
|
+
with_connection(@feature_class) do
|
116
|
+
features = ::Arel::Table.new(@feature_class.table_name.to_sym)
|
117
|
+
gates = ::Arel::Table.new(@gate_class.table_name.to_sym)
|
118
|
+
rows_query = features.join(gates, Arel::Nodes::OuterJoin)
|
119
|
+
.on(features[:key].eq(gates[:feature_key]))
|
120
|
+
.project(features[:key].as('feature_key'), gates[:key], gates[:value])
|
121
|
+
rows = @feature_class.connection.select_all rows_query
|
122
|
+
db_gates = rows.map { |row| @gate_class.new(row) }
|
123
|
+
grouped_db_gates = db_gates.group_by(&:feature_key)
|
124
|
+
result = Hash.new { |hash, key| hash[key] = default_config }
|
125
|
+
features = grouped_db_gates.keys.map { |key| Flipper::Feature.new(key, self) }
|
126
|
+
features.each do |feature|
|
127
|
+
result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
|
128
|
+
end
|
129
|
+
result
|
121
130
|
end
|
122
|
-
result
|
123
131
|
end
|
124
132
|
|
125
133
|
# Public: Enables a gate for a given thing.
|
@@ -158,7 +166,9 @@ module Flipper
|
|
158
166
|
when :integer
|
159
167
|
set(feature, gate, thing)
|
160
168
|
when :set
|
161
|
-
@gate_class
|
169
|
+
with_connection(@gate_class) do
|
170
|
+
@gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).destroy_all
|
171
|
+
end
|
162
172
|
else
|
163
173
|
unsupported_data_type gate.data_type
|
164
174
|
end
|
@@ -175,18 +185,20 @@ module Flipper
|
|
175
185
|
|
176
186
|
def set(feature, gate, thing, options = {})
|
177
187
|
clear_feature = options.fetch(:clear, false)
|
178
|
-
@gate_class
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
188
|
+
with_connection(@gate_class) do
|
189
|
+
@gate_class.transaction do
|
190
|
+
clear(feature) if clear_feature
|
191
|
+
@gate_class.where(feature_key: feature.key, key: gate.key).destroy_all
|
192
|
+
begin
|
193
|
+
@gate_class.create! do |g|
|
194
|
+
g.feature_key = feature.key
|
195
|
+
g.key = gate.key
|
196
|
+
g.value = thing.value.to_s
|
197
|
+
end
|
198
|
+
rescue ::ActiveRecord::RecordNotUnique
|
199
|
+
# assume this happened concurrently with the same thing and its fine
|
200
|
+
# see https://github.com/jnunemaker/flipper/issues/544
|
186
201
|
end
|
187
|
-
rescue ::ActiveRecord::RecordNotUnique
|
188
|
-
# assume this happened concurrently with the same thing and its fine
|
189
|
-
# see https://github.com/jnunemaker/flipper/issues/544
|
190
202
|
end
|
191
203
|
end
|
192
204
|
|
@@ -194,10 +206,12 @@ module Flipper
|
|
194
206
|
end
|
195
207
|
|
196
208
|
def enable_multi(feature, gate, thing)
|
197
|
-
@gate_class
|
198
|
-
|
199
|
-
|
200
|
-
|
209
|
+
with_connection(@gate_class) do
|
210
|
+
@gate_class.create! do |g|
|
211
|
+
g.feature_key = feature.key
|
212
|
+
g.key = gate.key
|
213
|
+
g.value = thing.value.to_s
|
214
|
+
end
|
201
215
|
end
|
202
216
|
|
203
217
|
nil
|
@@ -227,6 +241,10 @@ module Flipper
|
|
227
241
|
end
|
228
242
|
result
|
229
243
|
end
|
244
|
+
|
245
|
+
def with_connection(model = @feature_class, &block)
|
246
|
+
model.connection_pool.with_connection(&block)
|
247
|
+
end
|
230
248
|
end
|
231
249
|
end
|
232
250
|
end
|
data/lib/flipper/version.rb
CHANGED
@@ -57,4 +57,126 @@ RSpec.describe Flipper::Adapters::ActiveRecord do
|
|
57
57
|
expect(Flipper.adapter.adapter).to be_a(Flipper::Adapters::ActiveRecord)
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
61
|
+
context "ActiveRecord connection_pool" do
|
62
|
+
before do
|
63
|
+
ActiveRecord::Base.clear_active_connections!
|
64
|
+
end
|
65
|
+
|
66
|
+
context "#features" do
|
67
|
+
it "does not hold onto connections" do
|
68
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
69
|
+
subject.features
|
70
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does not release previously held connection" do
|
74
|
+
ActiveRecord::Base.connection # establish a new connection
|
75
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
76
|
+
subject.features
|
77
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "#get_all" do
|
82
|
+
it "does not hold onto connections" do
|
83
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
84
|
+
subject.get_all
|
85
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "does not release previously held connection" do
|
89
|
+
ActiveRecord::Base.connection # establish a new connection
|
90
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
91
|
+
subject.get_all
|
92
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "#add / #remove / #clear" do
|
97
|
+
let(:feature) { Flipper::Feature.new(:search, subject) }
|
98
|
+
|
99
|
+
it "does not hold onto connections" do
|
100
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
101
|
+
subject.add(feature)
|
102
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
103
|
+
subject.remove(feature)
|
104
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
105
|
+
subject.clear(feature)
|
106
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "does not release previously held connection" do
|
110
|
+
ActiveRecord::Base.connection # establish a new connection
|
111
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
112
|
+
subject.add(feature)
|
113
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
114
|
+
subject.remove(feature)
|
115
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
116
|
+
subject.clear(feature)
|
117
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "#get_multi" do
|
122
|
+
let(:feature) { Flipper::Feature.new(:search, subject) }
|
123
|
+
|
124
|
+
it "does not hold onto connections" do
|
125
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
126
|
+
subject.get_multi([feature])
|
127
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "does not release previously held connection" do
|
131
|
+
ActiveRecord::Base.connection # establish a new connection
|
132
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
133
|
+
subject.get_multi([feature])
|
134
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "#enable/#disable boolean" do
|
139
|
+
let(:feature) { Flipper::Feature.new(:search, subject) }
|
140
|
+
let(:gate) { feature.gate(:boolean)}
|
141
|
+
|
142
|
+
it "does not hold onto connections" do
|
143
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
144
|
+
subject.enable(feature, gate, gate.wrap(true))
|
145
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
146
|
+
subject.disable(feature, gate, gate.wrap(false))
|
147
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "does not release previously held connection" do
|
151
|
+
ActiveRecord::Base.connection # establish a new connection
|
152
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
153
|
+
subject.enable(feature, gate, gate.wrap(true))
|
154
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
155
|
+
subject.disable(feature, gate, gate.wrap(false))
|
156
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "#enable/#disable set" do
|
161
|
+
let(:feature) { Flipper::Feature.new(:search, subject) }
|
162
|
+
let(:gate) { feature.gate(:group) }
|
163
|
+
|
164
|
+
it "does not hold onto connections" do
|
165
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
166
|
+
subject.enable(feature, gate, gate.wrap(:admin))
|
167
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
168
|
+
subject.disable(feature, gate, gate.wrap(:admin))
|
169
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "does not release previously held connection" do
|
173
|
+
ActiveRecord::Base.connection # establish a new connection
|
174
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
175
|
+
subject.enable(feature, gate, gate.wrap(:admin))
|
176
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
177
|
+
subject.disable(feature, gate, gate.wrap(:admin))
|
178
|
+
expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
60
182
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper-active_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.26.
|
4
|
+
version: 0.26.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flipper
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.26.
|
19
|
+
version: 0.26.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.26.
|
26
|
+
version: 0.26.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|