lightningff 0.0.2

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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +91 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/lightning_manifest.js +1 -0
  6. data/app/assets/stylesheets/lightning/application.css +37 -0
  7. data/app/controllers/lightning/application_controller.rb +6 -0
  8. data/app/controllers/lightning/feature_opt_ins_controller.rb +32 -0
  9. data/app/controllers/lightning/features_controller.rb +63 -0
  10. data/app/helpers/lightning/application_helper.rb +4 -0
  11. data/app/helpers/lightning/flaggable.rb +7 -0
  12. data/app/jobs/lightning/application_job.rb +4 -0
  13. data/app/mailers/lightning/application_mailer.rb +6 -0
  14. data/app/models/lightning/application_record.rb +5 -0
  15. data/app/models/lightning/feature.rb +12 -0
  16. data/app/models/lightning/feature_opt_in.rb +16 -0
  17. data/app/views/layouts/lightning/application.html.erb +17 -0
  18. data/app/views/lightning/feature_opt_ins/_feature_opt_in.html.erb +3 -0
  19. data/app/views/lightning/feature_opt_ins/_form.html.erb +12 -0
  20. data/app/views/lightning/features/_form.html.erb +37 -0
  21. data/app/views/lightning/features/edit.html.erb +7 -0
  22. data/app/views/lightning/features/index.html.erb +31 -0
  23. data/app/views/lightning/features/new.html.erb +6 -0
  24. data/app/views/lightning/features/show.html.erb +39 -0
  25. data/config/routes.rb +7 -0
  26. data/db/migrate/20210513140212_create_feature_flag_framework.rb +21 -0
  27. data/lib/lightning.rb +17 -0
  28. data/lib/lightning/api.rb +56 -0
  29. data/lib/lightning/engine.rb +13 -0
  30. data/lib/lightning/errors.rb +9 -0
  31. data/lib/lightning/version.rb +3 -0
  32. data/lib/tasks/lightning_tasks.rake +51 -0
  33. metadata +99 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 88d8c73c6bac9eb684d13681f741c8c470388aaa6f704d3f7ea4acbd570d810f
4
+ data.tar.gz: 9519b22659caf09331f314d99d452040788ddfeb07aaa928e803d7cdb3098673
5
+ SHA512:
6
+ metadata.gz: fa2041ef7c649e5516f977e03a4a3cc1f1df35a0c735e6263985a99728c0635612ab6bbe0bb1975693ca621d4272b46a72795d351b75ce64311190c31391fb14
7
+ data.tar.gz: 66852cdcdeb487ced36b21ed29c93e352c99277d8df384a211f12f9afe6214a165a87dbd819ddc939589e01242931056c6a4d4741c9f11ca86e7ea02beadb774
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 Ramruthwick Pathireddy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # ⚡️ Lightning ![Tests](https://github.com/LightningFF/lightning/actions/workflows/run_test.yml/badge.svg) [![Gem Version](https://badge.fury.io/rb/lightningff.svg)](https://badge.fury.io/rb/lightningff)
2
+ An end-to-end feature flagging system that can be setup in <1 minute.
3
+
4
+ Lightning is a rails gem you can install into your Rails application to get both console and UI access to manage feature flags. Lightning saves you time to avoid building an in-house solution.
5
+
6
+ ## Install
7
+
8
+ Add the following link to Gemfile
9
+ ```ruby
10
+ gem 'lightningff', require: 'lightning'
11
+ ```
12
+ and run `bundle install`. _Note: You might need to run `bundle update` to resolve any incompatible issues with rails._
13
+
14
+ Set up feature flag migrations by running the following lines
15
+ ```bash
16
+ bin/rails lightning:install:migrations
17
+ bin/rails db:migrate SCOPE=lightning
18
+ ```
19
+
20
+ Create `config/initializers/lightning.rb` and set your flaggable entities
21
+ ```ruby
22
+ Lightning.flaggable_entities = ["User", "Workspace"]
23
+ ```
24
+ For each flaggable model, add `include Lightning::Flaggable`.
25
+ ```ruby
26
+ class User < ApplicationRecord
27
+ include Lightning::Flaggable
28
+ end
29
+ ```
30
+
31
+ To check feature availability for entity: `Lightning::Feature.enabled?(user, <feature_key>)`
32
+
33
+ ### UI setup
34
+
35
+ Mount engine in `routes.rb` file
36
+ ```ruby
37
+ Rails.application.routes.draw do
38
+ mount Lightning::Engine => "/lightning"
39
+ end
40
+ ```
41
+
42
+ ## Rails Console API Usage
43
+
44
+ ```ruby
45
+ ### Feature Management
46
+ # Create feature (state is disabled)
47
+ Lightning.create!('homepage_v2', 'New homepage with better logo')
48
+ # Get feature by key
49
+ Lightning.get('homepage_v2')
50
+ # List all features
51
+ Lightning.list
52
+ # Update feature state/description
53
+ Lightning.update('homepage_v2', {state: 'enabled_per_entity', description: 'Homepage with new nav'})
54
+ # Delete feature
55
+ Lightning.delete('homepage_v2')
56
+
57
+ ### Feature Permissions Management
58
+ u = User.create(name: 'Dummy user')
59
+ # Add entity to feature
60
+ Lightning.enable_entity('homepage_v2', u)
61
+ # List entities for feature
62
+ Lightning.entities
63
+ # Remove entity to feature
64
+ Lightning.remove_entity('homepage_v2', u)
65
+ # Check if feature is enabled for entity
66
+ Lightning.enabled?(u, 'homepage_v2')
67
+ ```
68
+
69
+ ## Advanced Configuration
70
+
71
+ Lightning makes is super easy to configure how data is represented through the UI.
72
+
73
+ ## Running Tests
74
+
75
+ To run the test suite, pull the repo locally and run `rspec spec/`. All tests live in the **spec/** folder.
76
+
77
+
78
+ ## Contributing
79
+
80
+ You need to have ruby version 2.7.3 installed locally and rails version 6.1.3.
81
+
82
+ ```ruby
83
+ cd test/dummy
84
+ bundle install
85
+ bin/rails db:create
86
+ bin/rails db:migrate
87
+ bin/rails server
88
+ ```
89
+
90
+ ## License
91
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/lightning .css
@@ -0,0 +1,37 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
16
+
17
+ html {
18
+ font-size: 14px;
19
+ }
20
+
21
+ .fw-monospace {
22
+ font-family: monospace;
23
+ }
24
+
25
+ .text-underline-on-hover {
26
+ text-decoration: none;
27
+ }
28
+
29
+ .text-underline-on-hover:hover {
30
+ text-decoration: underline;
31
+ }
32
+
33
+ .lightning-logo {
34
+ font-size: 18px;
35
+ font-weight: bolder;
36
+ letter-spacing: -0.5px;
37
+ }
@@ -0,0 +1,6 @@
1
+ require_dependency "lightning/application_controller"
2
+
3
+ module Lightning
4
+ class ApplicationController < ::ApplicationController
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ require_dependency "lightning/application_controller"
2
+
3
+ module Lightning
4
+ class FeatureOptInsController < ApplicationController
5
+
6
+ def create
7
+ @feature = Feature.find(params[:feature_id])
8
+ @feature_opt_in = @feature.feature_opt_ins.new
9
+ @feature_opt_in.entity_id = params[:feature_opt_in][:entity_id]
10
+ @feature_opt_in.entity_type = params[:feature_opt_in][:entity_type]
11
+ if @feature_opt_in.save
12
+ flash[:notice] = "Permissions has been created!"
13
+ else
14
+ flash[:notice] = "FAILED TO SAVE PERMISSIONS! INVALID ENTITY ID!"
15
+ end
16
+ redirect_to @feature
17
+ end
18
+
19
+ def destroy
20
+ @feature = Feature.find(params[:feature_id])
21
+ @feature_opt_in = @feature.feature_opt_ins.find(params[:id])
22
+ @feature_opt_in.destroy
23
+ redirect_to @feature, notice: 'Feature permission was successfully destroyed.'
24
+ end
25
+
26
+ private
27
+ def feature_opt_ins_params
28
+ params.require(:feature_opt_in).permit(:entity_id, :entity_type)
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ require_dependency "lightning/application_controller"
2
+
3
+ module Lightning
4
+ class FeaturesController < ApplicationController
5
+ before_action :set_feature, only: [:show, :edit, :update, :destroy]
6
+
7
+ # GET /features
8
+ def index
9
+ @features = Feature.all
10
+ end
11
+ #
12
+ # GET /features/1
13
+ def show
14
+ @flaggable_entities = Lightning.flaggable_entities
15
+ end
16
+
17
+ # GET /features/new
18
+ def new
19
+ @feature = Feature.new
20
+ end
21
+
22
+ # GET /feature/1/edit
23
+ def edit
24
+ end
25
+
26
+ # POST /features
27
+ def create
28
+ @feature = Feature.new(feature_params)
29
+
30
+ if @feature.save
31
+ redirect_to @feature, notice: 'Feature was successfully created.'
32
+ else
33
+ render :new
34
+ end
35
+ end
36
+
37
+ # PATCH/PUT /features/1
38
+ def update
39
+ if @feature.update(feature_params)
40
+ redirect_to @feature, notice: 'Feature was successfully updated.'
41
+ else
42
+ render :edit
43
+ end
44
+ end
45
+
46
+ # DELETE /features/1
47
+ def destroy
48
+ @feature.destroy
49
+ redirect_to features_url, notice: 'Feature was successfully destroyed.'
50
+ end
51
+
52
+ private
53
+ # Use callbacks to share common setup or constraints between actions.
54
+ def set_feature
55
+ @feature = Feature.find(params[:id])
56
+ end
57
+
58
+ # Only allow a list of trusted parameters through.
59
+ def feature_params
60
+ params.require(:feature).permit(:key, :description, :state)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,4 @@
1
+ module Lightning
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module Lightning
2
+ module Flaggable
3
+ def lightning_display
4
+ "#{self.id} - #{self.name} (#{self.class.to_s})"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Lightning
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Lightning
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Lightning
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module Lightning
2
+ class Feature < ApplicationRecord
3
+
4
+ enum state: { disabled: 0, enabled_globally: 1, enabled_per_entity: 2 }
5
+ has_many :feature_opt_ins
6
+
7
+ def enabled?
8
+ !disabled?
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Lightning
2
+ class FeatureOptIn < ApplicationRecord
3
+ belongs_to :feature
4
+
5
+ attr_accessor :entity_id
6
+
7
+ belongs_to :entity, polymorphic: true
8
+ before_validation :set_entity
9
+
10
+ private
11
+ def set_entity
12
+ self.entity = self.entity_type.constantize.find_by_id(self.entity_id)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Lightning — Feature Flags</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "lightning/application", media: "all" %>
9
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
10
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
11
+
12
+ </head>
13
+
14
+ <body>
15
+ <%= yield %>
16
+ </body>
17
+ </html>
@@ -0,0 +1,3 @@
1
+ <div>
2
+ <%= feature_opt_in.entity.lightning_display %>
3
+ <%= button_to 'Destroy', [feature_opt_in.feature, feature_opt_in], method: :delete, data: { confirm: 'Are you sure?' } %></div>
@@ -0,0 +1,12 @@
1
+ <h3>Permission Entities</h3>
2
+ <%= form_with model: [@feature, @feature.feature_opt_ins.build] do |form| %>
3
+ <p>
4
+ <%= form.label :entity_type, 'Select Entity Type' %><br>
5
+ <%= form.select :entity_type, options_for_select(@flaggable_entities.map {|u| [ u.to_s, u.to_s] }) %>
6
+ </p>
7
+ <p>
8
+ <%= form.label :entity_id, 'Entity Id' %><br>
9
+ <%= form.text_field :entity_id %>
10
+ </p>
11
+ <%= form.submit 'Add entity to feature' %>
12
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <%= form_with(model: feature) do |form| %>
2
+ <% if feature.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(feature.errors.count, "error") %> prohibited this feature from being saved:</h2>
5
+
6
+ <ul>
7
+ <% feature.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="field">
15
+ <%= form.label :key %>
16
+ <%= form.text_field :key %>
17
+ </div>
18
+
19
+ <div class="field">
20
+ <%= form.label :description %>
21
+ <%= form.text_area :description %>
22
+ </div>
23
+
24
+ <div class="field">
25
+ <%= form.label :state %>
26
+ <%= form.select :state, Lightning::Feature.states.keys %>
27
+ </div>
28
+
29
+ <!-- <div class="field">-->
30
+ <%#= form.label :globally_enabled %>
31
+ <%#= form.text_area :globally_enabled %>
32
+ <!-- </div>-->
33
+
34
+ <div class="actions">
35
+ <%= form.submit %>
36
+ </div>
37
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <h1>Editing Feature</h1>
2
+
3
+ <%= render 'form', feature: @feature %>
4
+ <br/>
5
+
6
+ <%= link_to 'Show', @feature %> |
7
+ <%= link_to 'Back', features_path %>
@@ -0,0 +1,31 @@
1
+ <div class="container">
2
+ <p id="notice"><%= notice %></p>
3
+
4
+ <div class="d-flex justify-content-between align-items-center">
5
+ <div class="lightning-logo">⚡️ Lightning</div>
6
+ <%= link_to '+ New Feature', new_feature_path, class: "btn btn-primary" %>
7
+ </div>
8
+
9
+ <table class="table mt-5">
10
+ <tbody>
11
+ <% @features.each do |feature| %>
12
+ <tr>
13
+ <td>
14
+ <% if feature.enabled_per_entity? %>
15
+ <span class="badge bg-primary"><%= pluralize feature.feature_opt_ins.count, "opt in" %></span>
16
+ <% elsif feature.enabled_globally? %>
17
+ <span class="badge bg-success">On</span>
18
+ <% else %>
19
+ <span class="badge bg-secondary">Off</span>
20
+ <% end %>
21
+ </td>
22
+ <td>
23
+ <%= link_to feature.key, feature, class: "fw-monospace fw-bold text-underline-on-hover text-reset" %>
24
+ </td>
25
+ <td><%= feature.description %></td>
26
+ <td><%= feature.created_at.to_formatted_s(:short) %></td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ </div>
@@ -0,0 +1,6 @@
1
+ <h1>New Feature</h1>
2
+
3
+ <%= render 'form', feature: @feature %>
4
+ <br/>
5
+
6
+ <%= link_to 'Back', features_path %>
@@ -0,0 +1,39 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <td><%= link_to 'Edit', edit_feature_path(@feature) %></td>
4
+ <td><%= button_to 'Destroy', feature_path(@feature), method: :delete, data: { confirm: 'Are you sure?' } %></td>
5
+
6
+ <p>
7
+ <strong>Key:</strong>
8
+ <%= @feature.key %>
9
+ </p>
10
+
11
+ <p>
12
+ <strong>Description:</strong>
13
+ <%= @feature.description %>
14
+ </p>
15
+
16
+ <p>
17
+ <strong>State:</strong>
18
+ <%= @feature.state %>
19
+ </p>
20
+
21
+
22
+ <p>
23
+ <strong>Created:</strong>
24
+ <%= @feature.created_at.to_formatted_s(:long) %>
25
+ </p>
26
+
27
+ <p>
28
+ <strong>Updated:</strong>
29
+ <%= @feature.updated_at.to_formatted_s(:long) %>
30
+ </p>
31
+
32
+
33
+ <h3>Opt In Entities</h3>
34
+ <%= render @feature.feature_opt_ins %>
35
+ <%= render "lightning/feature_opt_ins/form" %>
36
+ <br/>
37
+
38
+ <%= link_to 'Edit', edit_feature_path(@feature) %> |
39
+ <%= link_to 'Back', features_path %>
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Lightning::Engine.routes.draw do
2
+ root to: 'features#index'
3
+
4
+ resources :features do
5
+ resources :feature_opt_ins
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ class CreateFeatureFlagFramework < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :lightning_features do |t|
4
+ t.string :key, index: { unique: true }, null: false
5
+ t.text :description
6
+ t.integer :state, default: 0, null: false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ create_table :lightning_feature_opt_ins do |t|
12
+ t.integer :feature_id, null: false
13
+ t.integer :entity_id, null: false
14
+ t.string :entity_type, null: false
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :lightning_feature_opt_ins, [:entity_id, :entity_type]
20
+ end
21
+ end
data/lib/lightning.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'lightning/version'
2
+ require 'lightning/engine'
3
+ require 'lightning/api'
4
+ require 'lightning/errors'
5
+
6
+ module Lightning
7
+ # Your code goes here...
8
+ mattr_accessor :flaggable_entities
9
+
10
+ def self.flaggable_entities
11
+ @@flaggable_entities.map { |f| f.constantize }
12
+ end
13
+
14
+ class << self
15
+ delegate :create!, :get, :list, :update, :delete, :entities, :enable_entity, :remove_entity, :enabled?, to: Api
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lightning
4
+ module Api
5
+ def self.create!(key, description = '')
6
+ Feature.create!(key: key, description: description)
7
+ rescue StandardError
8
+ raise ::Lightning::Errors::FailedToCreate, 'Failed to create new feature'
9
+ end
10
+
11
+ def self.get(key)
12
+ Feature.find_by!(key: key)
13
+ rescue StandardError
14
+ raise ::Lightning::Errors::FeatureNotFound, "Feature with key: #{key} not found"
15
+ end
16
+
17
+ def self.list
18
+ Feature.all
19
+ end
20
+
21
+ def self.update(key, attributes)
22
+ get(key).update(attributes)
23
+ rescue ArgumentError
24
+ raise ::Lightning::Errors::InvalidFeatureState, "Failed to update state. State must be one of the following: #{Feature.states.keys} "
25
+ end
26
+
27
+ def self.delete(key)
28
+ get(key).destroy
29
+ end
30
+
31
+ def self.entities(key)
32
+ get(key).feature_opt_ins.all.map(&:entity)
33
+ end
34
+
35
+ def self.enable_entity(key, entity)
36
+ permissioned_entity = get(key).feature_opt_ins.new
37
+ permissioned_entity.entity_id = entity.id
38
+ permissioned_entity.entity_type = entity.class.to_s
39
+ permissioned_entity.save!
40
+ end
41
+
42
+ def self.remove_entity(key, entity)
43
+ get(key).feature_opt_ins.find_by(entity_id: entity.id, entity_type: entity.class.to_s)&.destroy
44
+ rescue ActiveRecord::RecordNotFound
45
+ raise ::Lightning::Errors::EntityNotFound, "Could not find entity with id #{entity.id} and type #{entity.class.to_s}"
46
+ end
47
+
48
+ def self.enabled?(entity, feature_key)
49
+ joined_table = Feature.left_outer_joins(:feature_opt_ins)
50
+ joined_table
51
+ .where(key: feature_key, state: :enabled_per_entity, feature_opt_ins: { entity_id: entity.id, entity_type: entity.class.to_s })
52
+ .or(joined_table.where(key: feature_key, state: :enabled_globally))
53
+ .exists?
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ module Lightning
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Lightning
4
+
5
+ initializer "lightning.assets.precompile" do |app|
6
+ app.config.assets.precompile += %w( lightning/application.css )
7
+ end
8
+
9
+ config.generators do |g|
10
+ g.test_framework :rspec
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Lightning::Errors
2
+ class FeatureNotFound < StandardError; end
3
+
4
+ class InvalidFeatureState < StandardError; end
5
+
6
+ class EntityNotFound < StandardError; end
7
+
8
+ class FailedToCreate < StandardError; end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Lightning
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,51 @@
1
+ desc "Setup script for the gem"
2
+ namespace :lightning do
3
+ task :install do
4
+ _, *args = ARGV
5
+
6
+
7
+ # Task goes here
8
+ puts 'Copying migrations from engines...'
9
+ `bin/rails lightning:install:migrations`
10
+
11
+ puts 'Running engine migrations...'
12
+ `bin/rails db:migrate SCOPE=lightning`
13
+
14
+ puts 'Creating initializers script'
15
+ `echo "Lightning.flaggable_entities = [#{args.map{ |a| '\"' + a + '\"'}.join(", ")}]" > config/initializers/lightning.rb`
16
+
17
+ args.each do |model|
18
+ puts 'Adding taggable to model: ' + model
19
+ Dir.glob("app/models/**/#{model.underscore}.rb").each do |f|
20
+ puts f
21
+
22
+ # Find the model name and add the line underneath it
23
+ file = File.open(f)
24
+ new_file_contents = StringIO.open
25
+ add_line_here = false
26
+ file.each do |line|
27
+ if add_line_here
28
+ new_file_contents << " include Lightning::Flaggable\n"
29
+ add_line_here = false
30
+ end
31
+
32
+ if line.include?('class') && line.include?(model) && line.include?(' < ')
33
+ add_line_here = true
34
+ end
35
+ new_file_contents << line
36
+ end
37
+ file.close
38
+
39
+ # Update file contents with new file contents
40
+ new_file_contents.seek 0
41
+ File.open(f, 'wb').write new_file_contents.read
42
+ end
43
+ end
44
+
45
+
46
+ puts 'Successfully ran setup script'
47
+
48
+ # Don't run the tasks in the arguments
49
+ exit
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lightningff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Ruthwick Pathireddy
8
+ - Pranav Singh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-05-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 6.1.3
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 6.1.3.2
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: 6.1.3
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 6.1.3.2
34
+ description: With Lightning, you can set up an end-to-end highly customizable feature
35
+ flagging system in <1 minute. It provides console and UI support for feature flag
36
+ management.
37
+ email:
38
+ - ruthwickp@gmail.com
39
+ - pranav@getcadet.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - MIT-LICENSE
45
+ - README.md
46
+ - Rakefile
47
+ - app/assets/config/lightning_manifest.js
48
+ - app/assets/stylesheets/lightning/application.css
49
+ - app/controllers/lightning/application_controller.rb
50
+ - app/controllers/lightning/feature_opt_ins_controller.rb
51
+ - app/controllers/lightning/features_controller.rb
52
+ - app/helpers/lightning/application_helper.rb
53
+ - app/helpers/lightning/flaggable.rb
54
+ - app/jobs/lightning/application_job.rb
55
+ - app/mailers/lightning/application_mailer.rb
56
+ - app/models/lightning/application_record.rb
57
+ - app/models/lightning/feature.rb
58
+ - app/models/lightning/feature_opt_in.rb
59
+ - app/views/layouts/lightning/application.html.erb
60
+ - app/views/lightning/feature_opt_ins/_feature_opt_in.html.erb
61
+ - app/views/lightning/feature_opt_ins/_form.html.erb
62
+ - app/views/lightning/features/_form.html.erb
63
+ - app/views/lightning/features/edit.html.erb
64
+ - app/views/lightning/features/index.html.erb
65
+ - app/views/lightning/features/new.html.erb
66
+ - app/views/lightning/features/show.html.erb
67
+ - config/routes.rb
68
+ - db/migrate/20210513140212_create_feature_flag_framework.rb
69
+ - lib/lightning.rb
70
+ - lib/lightning/api.rb
71
+ - lib/lightning/engine.rb
72
+ - lib/lightning/errors.rb
73
+ - lib/lightning/version.rb
74
+ - lib/tasks/lightning_tasks.rake
75
+ homepage: https://github.com/LightningFF/lightning
76
+ licenses:
77
+ - MIT
78
+ metadata:
79
+ homepage_uri: https://github.com/LightningFF/lightning
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubygems_version: 3.1.6
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Feature flagging for Rails
99
+ test_files: []