flipside 0.1.1 → 0.2.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 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