flipper-active_record 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/flipper/adapters/active_record.rb +31 -6
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/version.rb +1 -1
- data/lib/generators/flipper/templates/migration.erb +1 -1
- data/spec/flipper/adapters/active_record_spec.rb +16 -4
- data/spec/flipper/model/active_record_spec.rb +61 -0
- data/test/adapters/active_record_test.rb +2 -2
- data/test_rails/generators/flipper/active_record_generator_test.rb +0 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c4aa6610a01147dbc81d8e256344ecb508ee59fa87b74d6ad746de90d4306c5
|
4
|
+
data.tar.gz: a5c419782f0d82df4a417a5d5c89200fcc8322a8251f1cbba8c5628c40322aa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fb31e98eb30ac6323ed1f9662ef49df1ba44c71e6006aade9b7c55c2d236be1e2b608e2e2c3a12c8ab5ba9e748fc5d56fd63d0f663a009820fee37184a26ff5
|
7
|
+
data.tar.gz: bc3fa0ec74c8af61b1e5b537d1fc16c973d555de8f3d75238c6e620a33527af65e143d8ceee9999666950d6bcc5b2110c481c4ad4036c7dc9d01d3b097fab420
|
@@ -19,6 +19,8 @@ module Flipper
|
|
19
19
|
"flipper_features",
|
20
20
|
Model.table_name_suffix,
|
21
21
|
].join
|
22
|
+
|
23
|
+
has_many :gates, foreign_key: "feature_key", primary_key: "key"
|
22
24
|
end
|
23
25
|
|
24
26
|
# Private: Do not use outside of this adapter.
|
@@ -30,8 +32,10 @@ module Flipper
|
|
30
32
|
].join
|
31
33
|
end
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
+
VALUE_TO_TEXT_WARNING = <<-EOS
|
36
|
+
Your database needs migrated to use the latest Flipper features.
|
37
|
+
See https://github.com/flippercloud/flipper/issues/557
|
38
|
+
EOS
|
35
39
|
|
36
40
|
# Public: Initialize a new ActiveRecord adapter instance.
|
37
41
|
#
|
@@ -49,6 +53,8 @@ module Flipper
|
|
49
53
|
@name = options.fetch(:name, :active_record)
|
50
54
|
@feature_class = options.fetch(:feature_class) { Feature }
|
51
55
|
@gate_class = options.fetch(:gate_class) { Gate }
|
56
|
+
|
57
|
+
warn VALUE_TO_TEXT_WARNING if value_not_text?
|
52
58
|
end
|
53
59
|
|
54
60
|
# Public: The set of known features.
|
@@ -156,6 +162,8 @@ module Flipper
|
|
156
162
|
set(feature, gate, thing, clear: true)
|
157
163
|
when :integer
|
158
164
|
set(feature, gate, thing)
|
165
|
+
when :json
|
166
|
+
set(feature, gate, thing, json: true)
|
159
167
|
when :set
|
160
168
|
enable_multi(feature, gate, thing)
|
161
169
|
else
|
@@ -178,6 +186,8 @@ module Flipper
|
|
178
186
|
clear(feature)
|
179
187
|
when :integer
|
180
188
|
set(feature, gate, thing)
|
189
|
+
when :json
|
190
|
+
delete(feature, gate)
|
181
191
|
when :set
|
182
192
|
with_connection(@gate_class) do
|
183
193
|
@gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).destroy_all
|
@@ -198,15 +208,20 @@ module Flipper
|
|
198
208
|
|
199
209
|
def set(feature, gate, thing, options = {})
|
200
210
|
clear_feature = options.fetch(:clear, false)
|
211
|
+
json_feature = options.fetch(:json, false)
|
212
|
+
|
213
|
+
raise VALUE_TO_TEXT_WARNING if json_feature && value_not_text?
|
214
|
+
|
201
215
|
with_connection(@gate_class) do
|
202
216
|
@gate_class.transaction do
|
203
217
|
clear(feature) if clear_feature
|
218
|
+
delete(feature, gate)
|
204
219
|
@gate_class.where(feature_key: feature.key, key: gate.key).destroy_all
|
205
220
|
begin
|
206
221
|
@gate_class.create! do |g|
|
207
222
|
g.feature_key = feature.key
|
208
223
|
g.key = gate.key
|
209
|
-
g.value = thing.value.to_s
|
224
|
+
g.value = json_feature ? Typecast.to_json(thing.value) : thing.value.to_s
|
210
225
|
end
|
211
226
|
rescue ::ActiveRecord::RecordNotUnique
|
212
227
|
# assume this happened concurrently with the same thing and its fine
|
@@ -218,6 +233,10 @@ module Flipper
|
|
218
233
|
nil
|
219
234
|
end
|
220
235
|
|
236
|
+
def delete(feature, gate)
|
237
|
+
@gate_class.where(feature_key: feature.key, key: gate.key).destroy_all
|
238
|
+
end
|
239
|
+
|
221
240
|
def enable_multi(feature, gate, thing)
|
222
241
|
with_connection(@gate_class) do
|
223
242
|
@gate_class.create! do |g|
|
@@ -238,13 +257,13 @@ module Flipper
|
|
238
257
|
feature.gates.each do |gate|
|
239
258
|
result[gate.key] =
|
240
259
|
case gate.data_type
|
241
|
-
when :boolean
|
260
|
+
when :boolean, :integer
|
242
261
|
if row = gates.detect { |key, value| !key.nil? && key.to_sym == gate.key }
|
243
262
|
row.last
|
244
263
|
end
|
245
|
-
when :
|
264
|
+
when :json
|
246
265
|
if row = gates.detect { |key, value| !key.nil? && key.to_sym == gate.key }
|
247
|
-
row.last
|
266
|
+
Typecast.from_json(row.last)
|
248
267
|
end
|
249
268
|
when :set
|
250
269
|
gates.select { |key, value| !key.nil? && key.to_sym == gate.key }.map(&:last).to_set
|
@@ -255,6 +274,12 @@ module Flipper
|
|
255
274
|
result
|
256
275
|
end
|
257
276
|
|
277
|
+
# Check if value column is text instead of string
|
278
|
+
# See https://github.com/flippercloud/flipper/pull/692
|
279
|
+
def value_not_text?
|
280
|
+
@gate_class.column_for_attribute(:value).type != :text
|
281
|
+
end
|
282
|
+
|
258
283
|
def with_connection(model = @feature_class, &block)
|
259
284
|
model.connection_pool.with_connection(&block)
|
260
285
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Flipper
|
2
|
+
module Model
|
3
|
+
module ActiveRecord
|
4
|
+
# The id of the record when used as an actor.
|
5
|
+
#
|
6
|
+
# class User < ActiveRecord::Base
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# user = User.first
|
10
|
+
# Flipper.enable :some_feature, user
|
11
|
+
# Flipper.enabled? :some_feature, user #=> true
|
12
|
+
#
|
13
|
+
def flipper_id
|
14
|
+
"#{self.class.base_class.name};#{id}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Properties used to evaluate expressions
|
18
|
+
def flipper_properties
|
19
|
+
{"type" => self.class.name}.merge(attributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/flipper/version.rb
CHANGED
@@ -9,7 +9,7 @@ class CreateFlipperTables < ActiveRecord::Migration<%= migration_version %>
|
|
9
9
|
create_table :flipper_gates do |t|
|
10
10
|
t.string :feature_key, null: false
|
11
11
|
t.string :key, null: false
|
12
|
-
t.
|
12
|
+
t.text :value
|
13
13
|
t.timestamps null: false
|
14
14
|
end
|
15
15
|
add_index :flipper_gates, [:feature_key, :key, :value], unique: true
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'flipper/adapters/active_record'
|
2
|
+
require 'active_support/core_ext/kernel'
|
2
3
|
|
3
4
|
# Turn off migration logging for specs
|
4
5
|
ActiveRecord::Migration.verbose = false
|
@@ -15,7 +16,7 @@ RSpec.describe Flipper::Adapters::ActiveRecord do
|
|
15
16
|
ActiveRecord::Base.connection.execute <<-SQL
|
16
17
|
CREATE TABLE flipper_features (
|
17
18
|
id integer PRIMARY KEY,
|
18
|
-
key
|
19
|
+
key string NOT NULL UNIQUE,
|
19
20
|
created_at datetime NOT NULL,
|
20
21
|
updated_at datetime NOT NULL
|
21
22
|
)
|
@@ -25,7 +26,7 @@ RSpec.describe Flipper::Adapters::ActiveRecord do
|
|
25
26
|
CREATE TABLE flipper_gates (
|
26
27
|
id integer PRIMARY KEY,
|
27
28
|
feature_key text NOT NULL,
|
28
|
-
key
|
29
|
+
key string NOT NULL,
|
29
30
|
value text DEFAULT NULL,
|
30
31
|
created_at datetime NOT NULL,
|
31
32
|
updated_at datetime NOT NULL
|
@@ -44,13 +45,24 @@ RSpec.describe Flipper::Adapters::ActiveRecord do
|
|
44
45
|
|
45
46
|
it_should_behave_like 'a flipper adapter'
|
46
47
|
|
48
|
+
it "should load actor ids fine" do
|
49
|
+
flipper.enable_percentage_of_time(:foo, 1)
|
50
|
+
|
51
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
52
|
+
INSERT INTO flipper_gates (feature_key, key, value, created_at, updated_at)
|
53
|
+
VALUES ("foo", "actors", "Organization;4", time(), time())
|
54
|
+
SQL
|
55
|
+
|
56
|
+
flipper = Flipper.new(subject)
|
57
|
+
flipper.preload([:foo])
|
58
|
+
end
|
59
|
+
|
47
60
|
context 'requiring "flipper-active_record"' do
|
48
61
|
before do
|
49
62
|
Flipper.configuration = nil
|
50
63
|
Flipper.instance = nil
|
51
64
|
|
52
|
-
load 'flipper/adapters/active_record.rb'
|
53
|
-
ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
|
65
|
+
silence_warnings { load 'flipper/adapters/active_record.rb' }
|
54
66
|
end
|
55
67
|
|
56
68
|
it 'configures itself' do
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'flipper/model/active_record'
|
3
|
+
|
4
|
+
# Turn off migration logging for specs
|
5
|
+
ActiveRecord::Migration.verbose = false
|
6
|
+
|
7
|
+
RSpec.describe Flipper::Model::ActiveRecord do
|
8
|
+
before(:all) do
|
9
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
14
|
+
CREATE TABLE users (
|
15
|
+
id integer PRIMARY KEY,
|
16
|
+
name string NOT NULL,
|
17
|
+
age integer,
|
18
|
+
is_confirmed boolean,
|
19
|
+
created_at datetime NOT NULL,
|
20
|
+
updated_at datetime NOT NULL
|
21
|
+
)
|
22
|
+
SQL
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
ActiveRecord::Base.connection.execute("DROP table IF EXISTS `users`")
|
27
|
+
end
|
28
|
+
|
29
|
+
class User < ActiveRecord::Base
|
30
|
+
include Flipper::Model::ActiveRecord
|
31
|
+
end
|
32
|
+
|
33
|
+
class Admin < User
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "flipper_id" do
|
37
|
+
it "returns class name and id" do
|
38
|
+
expect(User.new(id: 1).flipper_id).to eq("User;1")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "uses base class name" do
|
42
|
+
expect(Admin.new(id: 2).flipper_id).to eq("User;2")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "flipper_properties" do
|
47
|
+
subject { User.create!(name: "Test", age: 22, is_confirmed: true) }
|
48
|
+
|
49
|
+
it "includes all attributes" do
|
50
|
+
expect(subject.flipper_properties).to eq({
|
51
|
+
"type" => "User",
|
52
|
+
"id" => subject.id,
|
53
|
+
"name" => "Test",
|
54
|
+
"age" => 22,
|
55
|
+
"is_confirmed" => true,
|
56
|
+
"created_at" => subject.created_at,
|
57
|
+
"updated_at" => subject.updated_at
|
58
|
+
})
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -11,8 +11,6 @@ class ActiveRecordTest < MiniTest::Test
|
|
11
11
|
database: ':memory:')
|
12
12
|
|
13
13
|
def setup
|
14
|
-
@adapter = Flipper::Adapters::ActiveRecord.new
|
15
|
-
|
16
14
|
ActiveRecord::Base.connection.execute <<-SQL
|
17
15
|
CREATE TABLE flipper_features (
|
18
16
|
id integer PRIMARY KEY,
|
@@ -36,6 +34,8 @@ class ActiveRecordTest < MiniTest::Test
|
|
36
34
|
ActiveRecord::Base.connection.execute <<-SQL
|
37
35
|
CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value)
|
38
36
|
SQL
|
37
|
+
|
38
|
+
@adapter = Flipper::Adapters::ActiveRecord.new
|
39
39
|
end
|
40
40
|
|
41
41
|
def teardown
|
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: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08
|
11
|
+
date: 2023-12-08 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: 1.
|
19
|
+
version: 1.1.0
|
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: 1.
|
26
|
+
version: 1.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,10 +60,12 @@ files:
|
|
60
60
|
- flipper-active_record.gemspec
|
61
61
|
- lib/flipper-active_record.rb
|
62
62
|
- lib/flipper/adapters/active_record.rb
|
63
|
+
- lib/flipper/model/active_record.rb
|
63
64
|
- lib/flipper/version.rb
|
64
65
|
- lib/generators/flipper/active_record_generator.rb
|
65
66
|
- lib/generators/flipper/templates/migration.erb
|
66
67
|
- spec/flipper/adapters/active_record_spec.rb
|
68
|
+
- spec/flipper/model/active_record_spec.rb
|
67
69
|
- test/adapters/active_record_test.rb
|
68
70
|
- test_rails/generators/flipper/active_record_generator_test.rb
|
69
71
|
homepage: https://www.flippercloud.io/docs/adapters/active-record
|
@@ -96,4 +98,5 @@ specification_version: 4
|
|
96
98
|
summary: ActiveRecord adapter for Flipper
|
97
99
|
test_files:
|
98
100
|
- spec/flipper/adapters/active_record_spec.rb
|
101
|
+
- spec/flipper/model/active_record_spec.rb
|
99
102
|
- test/adapters/active_record_test.rb
|