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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f11ba9f2e7fa94ab06ddddfc753ccb1b398ffc19023ab6b2f5a980ccb55bd974
4
- data.tar.gz: 2566b337548372d8d080f732dbb2824b771f5c58e429ac350ceb0afb5455fea8
3
+ metadata.gz: 4c4aa6610a01147dbc81d8e256344ecb508ee59fa87b74d6ad746de90d4306c5
4
+ data.tar.gz: a5c419782f0d82df4a417a5d5c89200fcc8322a8251f1cbba8c5628c40322aa2
5
5
  SHA512:
6
- metadata.gz: 40e29c142239ff3a5f474f2f0dd6019d3bc60fcc4aaa76021715cf1dcb241c50c2d2dbf1e669cd5b2efcfa5f6740b40bea477988e25d6b5445ff4fcddd111173
7
- data.tar.gz: 6d9c313e0402d1bd197a1abc17a1aa930296f8f854c132773bebc08c6dbf5fab49a2978a4df196267ca0e29af8b5277868458ba4a7d7a35feb4efdcdb44ed1e0
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
- # Public: The name of the adapter.
34
- attr_reader :name
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 :integer
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
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -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.string :value
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 text NOT NULL UNIQUE,
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 text NOT NULL,
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
@@ -1,4 +1,3 @@
1
- require 'helper'
2
1
  require 'active_record'
3
2
  require 'rails/generators/test_case'
4
3
  require 'generators/flipper/active_record_generator'
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.0.0
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-23 00:00:00.000000000 Z
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.0.0
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.0.0
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