flipside 0.1.1 → 0.2.1

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: 40777dd3225cce9bdb82e6d66301c8612a7284a51011610203cf717e6b68d2bc
4
- data.tar.gz: 26f0f2f2570389e80c76ebc3cdb347ff328d504b6291a4ba50d3df88d1c61a91
3
+ metadata.gz: e49af6fc8d31128f0e88089ad6d46ce1745a223a68c4f5e4f2e5353f99091073
4
+ data.tar.gz: cb1547b1c3892b363830aa5f75375998092b90f150e6a275b3224051b6eb3b19
5
5
  SHA512:
6
- metadata.gz: 88e26979066424e8474657fe29818254e85ac182113513bfb9fae0849fd73c9c54541c7e1cbca2f33a98e41b01b1c8b54c5c9adcb6613000fa99c181086128bd
7
- data.tar.gz: 899954def9a08cbdd895683e2975e32e59ab2ab1147d423460be32db25eeb764342124dfe7b89aacbf95abcca6613181cfb9470b1c377cfd25c83ddbfbb71757
6
+ metadata.gz: cc95be97c95f5c6285d4eb57e6b378b47277d61e43f6606eb44bf9c9c6f914a2b6019c4a143c8b9a5058c64eac6ea0f4cffbecf7138ad3836ca0dcb196515c22
7
+ data.tar.gz: fb5262d598dea36dd87e7c987ae1d263500b2bc7e39882d7da5a119a6c11aab009821d7981732a442176985054900355e6bcf2b8d2b0fa68eab99e03a6469337
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- ## [Unreleased]
1
+ ## [0.2.1] - 2025-01-30
2
+
3
+ - Option to create missing features
4
+ - Option to set link back to main application
5
+
6
+ ## [0.2.0] - 2025-01-29
7
+
8
+ - Support checking multiple objects at once
9
+ - Show hover text for feature statuses
10
+ - Support feature names with spaces
2
11
 
3
12
  ## [0.1.1] - 2024-11-22
4
13
 
data/README.md CHANGED
@@ -35,12 +35,51 @@ This will create a migration file. Run the migration, to add the flipside tables
35
35
 
36
36
  1. Defining Features
37
37
 
38
- Feature are created by running this (in a console or from code):
38
+ Features are created by running this (in a console or from code):
39
39
  ```ruby
40
- Flipside::Feature.create(name: "MyFeature", description: "Some optional description about what this feature do")
40
+ Flipside::Feature.create(
41
+ name: "MyFeature",
42
+ description: "Some optional description about what this feature do"
43
+ )
41
44
  ```
42
45
 
43
46
  By default features are turned off. If you would like it turned on from the beginning you could pass in `enabled: true`.
47
+ ```ruby
48
+ Flipside::Feature.create(name: "MyFeature", enabled: true)
49
+ ```
50
+
51
+ Features can be active during a given period. Set `activated_at` and/or `deactivated_at` to define this period.
52
+ Note: A feature is always disabled outside of the active period.
53
+ ```ruby
54
+ Flipside::Feature.create(
55
+ name: "MyFeature",
56
+ activated_at: 1.week.from_now,
57
+ deactivated_at: 2.weeks.from_now
58
+ )
59
+ ```
60
+
61
+ Features can be enabled for a certain record, typically a certain user or organization. This records are called entities. To enable a feature for a given record use `.add_entity`:
62
+ ```ruby
63
+ user = User.first
64
+ Flipside.enabled? user # => false
65
+ Flipside.add_entity(name: "MyFeature", user)
66
+ Flipside.enabled? user # => true
67
+ ```
68
+
69
+ Features can be enabled for records responding true to a certain method. This is called a "role". Given that User records have an admin? method. A feature can then be enabled
70
+ for all users how are admins, using the `.add_role` method:
71
+ ```ruby
72
+ user1 = User.new(admin: false)
73
+ user2 = User.new(admin: true)
74
+ Flipside.add_role(
75
+ name: "MyFeature",
76
+ class_name: "User",
77
+ method_name: :admin?
78
+ )
79
+ Flipside.enabled? user1 # => false
80
+ Flipside.enabled? user2 # => true
81
+ ```
82
+
44
83
 
45
84
  2. Checking Feature Status
46
85
 
@@ -54,13 +93,41 @@ Flipside.enabled? "MyFeature"
54
93
 
55
94
  #### For a Specific Record
56
95
 
57
- Check if a feature is enabled for a specific record (e.g. a user, an organization or a user responding `true` to `user.admin?`):
96
+ Check if a feature is enabled for a specific record (e.g. a user):
58
97
 
59
98
  ```ruby
60
99
  Flipside.enabled? "MyFeature", user
61
100
  ```
62
101
 
63
- ## Configuration
102
+ ## UI
103
+ Flipside comes with a sinatra web ui to mange feature flags. To mount this sinatra app in Rails add the following to your routes.rb file.
104
+ ```ruby
105
+ mount Flipside::Web, at: '/flipside'
106
+ ```
107
+ Note: you probably want to wrap this inside a constraints block to provide some authentication.
108
+
109
+ ![UI](/features.png)
110
+
111
+
112
+ ### Configuration
113
+
114
+ Entities can be added to a feature by searching for records and adding them.
115
+ ![Start screen](/add_entity.png)
116
+
117
+ To make this work, some configuration is required. Use the class method `Flipside.register_entity` for this.
118
+ ```ruby
119
+ Flipside.register_entity(
120
+ class_name: "User",
121
+ search_by: :name,
122
+ display_as: :name,
123
+ identified_by: :id
124
+ )
125
+ ```
126
+ Typically this should be configured in an initializer file.
127
+
128
+ The `.register_entity` method should be called once for each class that may be used as feature enablers.
129
+ The `search_by` keyword argument, which may be a symbol or a proc, dictates how records are found from searching in the ui. When a symbol is given, then searches with an exact match on the corresponding attribute are returned. E.g. `User.where(name: query)`.
130
+
64
131
  TODO
65
132
 
66
133
  ## Development
@@ -0,0 +1,7 @@
1
+ module Flipside
2
+ module Config
3
+ module Settings
4
+ attr_accessor :ui_back_path, :create_missing_features
5
+ end
6
+ end
7
+ end
@@ -12,7 +12,7 @@ class FeaturePresenter
12
12
  end
13
13
 
14
14
  def href
15
- File.join(base_path, "feature", name)
15
+ File.join(base_path, "feature", ERB::Util.url_encode(name))
16
16
  end
17
17
 
18
18
  def toggle_path
@@ -87,6 +87,14 @@ class FeaturePresenter
87
87
  feature.deactivated_at <= Time.now + period
88
88
  end
89
89
 
90
+ def status_title
91
+ if activates_soon?
92
+ "Activates at: #{activated_at}"
93
+ elsif deactivates_soon?
94
+ "deactivates at: #{deactivated_at}"
95
+ end
96
+ end
97
+
90
98
  def entity_count_str
91
99
  count = feature.entities.count
92
100
  if count == 1
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Flipside
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -1,8 +1,12 @@
1
1
  <div class="p-4 grid grid-cols-12 gap-4 cursor-pointer text-slate-300">
2
2
  <div class="col-span-3 text-left text-xl"><%= feature.name %></div>
3
- <div class="col-span-4 text-left text-lg"><%= feature.description&.capitalize %></div>
4
- <div class="col-span-4 text-right text-lg font-normal">
5
- <span class="inline-block py-1 px-2 min-w-20 text-center rounded-lg <%= feature.status_color %>"><%= feature.status %></span>
3
+ <div class="col-span-7 text-center text-lg"><%= feature.description&.capitalize %></div>
4
+ <div class="col-span-1 text-right text-lg font-normal">
5
+ <span
6
+ class="inline-block py-1 px-2 min-w-20 text-center rounded-lg <%= feature.status_color %>"
7
+ title="<%= feature.status_title %>">
8
+ <%= feature.status %>
9
+ </span>
6
10
  </div>
7
11
  <div class="text-right">
8
12
  <%= erb :_toggle_button, locals: {feature:} %>
@@ -1,15 +1,20 @@
1
- <div class="mt-12 w-3/4 m-auto bg-gray-800 text-slate-400 p-8 pb-24 rounded-lg shadow-lg">
2
- <h1 class="text-3xl text-center font-bold mb-6">Flipside feature flags</h1>
1
+ <div class="m-12">
2
+ <% if Flipside.ui_back_path %>
3
+ <a href="<%= Flipside.ui_back_path %>" class="p-2 rounded-lg text-xl text-slate-300 bg-blue-900 border border-blue-950 hover:bg-blue-800">Back</a>
4
+ <% end %>
5
+ <div class="mt-12 w-3/4 m-auto bg-gray-800 text-slate-400 p-8 pb-24 rounded-lg shadow-lg">
6
+ <h1 class="text-3xl text-center font-bold mb-6">Flipside feature flags</h1>
3
7
 
4
- <div class="mt-12 w-3/4 m-auto">
5
- <ul>
6
- <% features.each do |feature| %>
7
- <li class="border-b border-slate-500 hover:font-semibold">
8
- <a href=<%= feature.href %>>
9
- <%= erb :_feature_item, locals: {feature:} %>
10
- </a>
11
- </li>
12
- <% end %>
13
- </ul>
8
+ <div class="mt-12 m-auto">
9
+ <ul>
10
+ <% features.each do |feature| %>
11
+ <li class="border-b border-slate-500 hover:font-semibold">
12
+ <a href=<%= feature.href %>>
13
+ <%= erb :_feature_item, locals: {feature:} %>
14
+ </a>
15
+ </li>
16
+ <% end %>
17
+ </ul>
18
+ </div>
14
19
  </div>
15
20
  </div>
data/lib/flipside.rb CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  require "flipside/version"
4
4
  require "flipside/web"
5
+ require "flipside/config/settings"
5
6
  require "flipside/config/entities"
6
7
  require "flipside/config/roles"
7
8
  require "models/flipside/feature"
8
9
 
9
10
  module Flipside
11
+ extend Config::Settings
10
12
  extend Config::Entities
11
13
  extend Config::Roles
12
14
 
@@ -19,11 +21,12 @@ module Flipside
19
21
  end
20
22
 
21
23
  class << self
22
- def enabled?(name, object = nil)
24
+ def enabled?(name, *objects)
23
25
  feature = find_by(name:)
24
26
  return false unless feature
25
27
 
26
- feature.enabled? object
28
+ objects << nil if objects.empty?
29
+ objects.any? { |object| feature.enabled? object }
27
30
  end
28
31
 
29
32
  def enable!(name)
@@ -51,12 +54,21 @@ module Flipside
51
54
  feature.roles.find_by(id: role_id)&.destroy
52
55
  end
53
56
 
54
- def find_by(name:)
55
- Feature.find_by(name:)
57
+ def find_by(name:, create_on_missing: create_missing_features)
58
+ feature = Feature.find_by(name:)
59
+ feature ||= create_missing(name) if create_on_missing
60
+ feature
56
61
  end
57
62
 
58
63
  def find_by!(name:)
59
64
  find_by(name:) || raise(NoSuchFeauture.new(name))
60
65
  end
66
+
67
+ def create_missing(name)
68
+ trace = caller.find { |trace| !trace.start_with? __FILE__ }
69
+ source, line, _ = trace.split(":")
70
+ source = [source, line].join(":") if line.match?(/\d+/)
71
+ Feature.create(name:, description: "Created from #{source}")
72
+ end
61
73
  end
62
74
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipside
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sammy Henningsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-25 00:00:00.000000000 Z
11
+ date: 2025-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -103,6 +103,7 @@ files:
103
103
  - lib/flipside/config/registered_entity.rb
104
104
  - lib/flipside/config/registered_role.rb
105
105
  - lib/flipside/config/roles.rb
106
+ - lib/flipside/config/settings.rb
106
107
  - lib/flipside/feature_presenter.rb
107
108
  - lib/flipside/public/index.js
108
109
  - lib/flipside/public/modal_controller.js