rails_flags 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fd89ec7b4bef50b03eeda197bffb23d1f88da83dfd91e7e68d18f65643b46c46
4
+ data.tar.gz: b3fe66c98c53b2f07e10460a08d8853f4e2f62d79ca37f20eb2b66c2d453a6ea
5
+ SHA512:
6
+ metadata.gz: 39f075f0e4bd50afffcb40f53a6130b535151c0e8cfcf1f60dc3bc265ef82e84b2393ca2457cfe54dc7b1a32728501593cc792267c3d0269e121cd3032ecc7c5
7
+ data.tar.gz: 3da159ab062c096037bda7c92a38696fd987c6fde0209dc5206885e3f71bc22f13cbf8a6f9d5c5f00649b28d3d6a976c1c52ecf692037b5400c8fb957b9c1c2a
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Dhurba Baral
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,113 @@
1
+ # RailsFlags
2
+
3
+ A simple and powerful feature flagging system for Ruby on Rails applications. RailsFlags provides an easy way to manage feature flags with support for multiple storage backends.
4
+
5
+ ## Features
6
+
7
+ - Multiple storage adapters (Redis, Memory)
8
+ - Simple and intuitive API
9
+ - Thread-safe operations
10
+ - Configurable default states
11
+ - Support for flag metadata (descriptions, creation timestamps)
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem "rails_flags"
19
+ ```
20
+
21
+ And then execute:
22
+ ```bash
23
+ bundle install
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ RailsFlags can be configured with different adapters based on your needs:
29
+
30
+ ### Memory Adapter (Default)
31
+ ```ruby
32
+ # config/initializers/rails_flags.rb
33
+ RailsFlags.configure do |config|
34
+ config.adapter = RailsFlags::Adapters::MemoryAdapter.new
35
+ end
36
+ ```
37
+
38
+ ### Redis Adapter
39
+ ```ruby
40
+ # config/initializers/rails_flags.rb
41
+ RailsFlags.configure do |config|
42
+ config.adapter = RailsFlags::Adapters::RedisAdapter.new(
43
+ host: "localhost",
44
+ port: 6379
45
+ )
46
+ end
47
+ ```
48
+
49
+ ### Registering Flags
50
+ ```ruby
51
+ # Register a new feature flag (disabled by default)
52
+ RailsFlags.register(
53
+ :new_feature,
54
+ enabled: false,
55
+ description: "Awesome new feature"
56
+ )
57
+
58
+ # Register an enabled feature flag
59
+ RailsFlags.register(
60
+ :another_feature,
61
+ enabled: true,
62
+ description: "Another awesome feature"
63
+ )
64
+ ```
65
+
66
+ ### Checking Flags
67
+ ```ruby
68
+ # Check if a flag is enabled
69
+ if RailsFlags.enabled?(:new_feature)
70
+ # Feature code here
71
+ end
72
+
73
+ # Check if a flag is registered
74
+ RailsFlags.registered?(:new_feature)
75
+ ```
76
+
77
+ ### Managing Flags
78
+ ```ruby
79
+ # Enable a flag
80
+ RailsFlags.enable(:new_feature)
81
+
82
+ # Disable a flag
83
+ RailsFlags.disable(:new_feature)
84
+
85
+ # Get all flags
86
+ all_flags = RailsFlags.all_flags
87
+ ```
88
+
89
+ ## Storage Adapters
90
+
91
+ ### Memory Adapter
92
+ - Simple in-memory storage
93
+ - Suitable for development and testing
94
+ - Data is lost when the application restarts
95
+
96
+ ### Redis Adapter
97
+ - Persistent storage using Redis
98
+ - Suitable for production use
99
+ - Provides atomic operations
100
+ - Requires Redis server
101
+
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it
106
+ 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin feature/my-new-feature`)
109
+ 5. Create new Pull Request
110
+
111
+ ## License
112
+
113
+ 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,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
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
+ */
@@ -0,0 +1,4 @@
1
+ module RailsFlags
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RailsFlags
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RailsFlags
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module RailsFlags
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module RailsFlags
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Rails flags</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "rails_flags/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ RailsFlags::Engine.routes.draw do
2
+ end
@@ -0,0 +1,25 @@
1
+ module RailsFlags
2
+ module Adapters
3
+ class Base
4
+ def read(flag)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def write(flag, value)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def delete(flag)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def all
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def clear
21
+ raise NotImplementedError
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require_relative 'base'
2
+
3
+ module RailsFlags
4
+ module Adapters
5
+ class MemoryAdapter < Base
6
+ def initialize
7
+ @features = {}
8
+ end
9
+
10
+ def read(flag)
11
+ @features[flag.to_sym]
12
+ end
13
+
14
+ def write(flag, value)
15
+ @features[flag.to_sym] = value
16
+ end
17
+
18
+ def delete(flag)
19
+ @features.delete(flag.to_sym)
20
+ end
21
+
22
+ def all
23
+ @features.dup
24
+ end
25
+
26
+ def clear
27
+ @features.clear
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'redis'
2
+ require 'json'
3
+ require_relative 'base'
4
+
5
+ module RailsFlags
6
+ module Adapters
7
+ class RedisAdapter < Base
8
+ REDIS_KEY_PREFIX = 'rails_flags:'.freeze
9
+
10
+ def initialize(redis: nil, **redis_config)
11
+ @redis = redis || Redis.new(redis_config)
12
+ end
13
+
14
+ def read(flag)
15
+ data = @redis.get(key_for(flag))
16
+ return nil unless data
17
+
18
+ JSON.parse(data, symbolize_names: true)
19
+ end
20
+
21
+ def write(flag, value)
22
+ @redis.set(key_for(flag), value.to_json)
23
+ end
24
+
25
+ def delete(flag)
26
+ @redis.del(key_for(flag))
27
+ end
28
+
29
+ def all
30
+ keys = @redis.keys("#{REDIS_KEY_PREFIX}*")
31
+ return {} if keys.empty?
32
+
33
+ values = @redis.mget(keys)
34
+ keys.zip(values).each_with_object({}) do |(key, value), hash|
35
+ flag_name = key.sub(REDIS_KEY_PREFIX, '').to_sym
36
+ hash[flag_name] = JSON.parse(value, symbolize_names: true) if value
37
+ end
38
+ end
39
+
40
+ def clear
41
+ keys = @redis.keys("#{REDIS_KEY_PREFIX}*")
42
+ @redis.del(keys) unless keys.empty?
43
+ end
44
+
45
+ private
46
+
47
+ def key_for(flag)
48
+ "#{REDIS_KEY_PREFIX}#{flag}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,64 @@
1
+ require_relative "adapters/base"
2
+ require_relative "adapters/redis_adapter"
3
+ require_relative "adapters/memory_adapter"
4
+
5
+ module RailsFlags
6
+ class Configuration
7
+ attr_accessor :adapter
8
+
9
+ def initialize(adapter: nil)
10
+ @adapter = adapter || Adapters::MemoryAdapter.new
11
+ end
12
+
13
+ def register(name, enabled: false, description: nil)
14
+ validate_flag_name!(name)
15
+ data = {
16
+ enabled: enabled,
17
+ description: description,
18
+ created_at: Time.now
19
+ }
20
+
21
+ @adapter.write(name.to_sym, data)
22
+ data
23
+ end
24
+
25
+ def enabled?(flag)
26
+ validate_flag_exists!(flag)
27
+ data = @adapter.read(flag.to_sym)
28
+ data ? data[:enabled] == true : false
29
+ end
30
+
31
+ def enable(flag)
32
+ validate_flag_exists!(flag)
33
+ data = @adapter.read(flag.to_sym).dup
34
+ data[:enabled] = true
35
+ @adapter.write(flag.to_sym, data)
36
+ end
37
+
38
+ def disable(flag)
39
+ validate_flag_exists!(flag)
40
+ data = @adapter.read(flag.to_sym).dup
41
+ data[:enabled] = false
42
+ @adapter.write(flag.to_sym, data)
43
+ end
44
+
45
+ def registered?(flag)
46
+ @adapter.read(flag.to_sym) ? true : false
47
+ end
48
+
49
+ def all_flags
50
+ @adapter.all
51
+ end
52
+
53
+ private
54
+
55
+ def validate_flag_name!(name)
56
+ raise ArgumentError, "Flag name must be a String or Symbol" unless name.is_a?(String) || name.is_a?(Symbol)
57
+ raise ArgumentError, "Flag name cannot be empty" if name.to_s.strip.empty?
58
+ end
59
+
60
+ def validate_flag_exists!(flag)
61
+ raise KeyError, "Flag '#{flag}' is not registered" unless registered?(flag)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ module RailsFlags
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsFlags
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module RailsFlags
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,52 @@
1
+ require "rails_flags/version"
2
+ require "rails_flags/engine"
3
+ require "rails_flags/configuration"
4
+
5
+ module RailsFlags
6
+ class Error < StandardError; end
7
+ class FlagNotRegisteredError < Error; end
8
+
9
+ class << self
10
+ def configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def register(flag_name, **options)
19
+ configuration.register(flag_name, **options)
20
+ end
21
+
22
+ def enabled?(flag)
23
+ configuration.enabled?(flag)
24
+ rescue KeyError => e
25
+ raise FlagNotRegisteredError, e.message
26
+ end
27
+
28
+ def disabled?(flag)
29
+ !enabled?(flag)
30
+ end
31
+
32
+ def enable(flag)
33
+ configuration.enable(flag)
34
+ rescue KeyError => e
35
+ raise FlagNotRegisteredError, e.message
36
+ end
37
+
38
+ def disable(flag)
39
+ configuration.disable(flag)
40
+ rescue KeyError => e
41
+ raise FlagNotRegisteredError, e.message
42
+ end
43
+
44
+ def registered?(flag)
45
+ configuration.registered?(flag)
46
+ end
47
+
48
+ def all_flags
49
+ configuration.all_flags
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_flags do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_flags
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dhurba Baral
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 8.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 8.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A simple and powerful feature flagging system for Ruby on Rails applications.
56
+ RailsFlags provides an easy way to manage feature flags with support for multiple
57
+ storage backends.
58
+ email:
59
+ - dhurba87@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - MIT-LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - app/assets/stylesheets/rails_flags/application.css
68
+ - app/controllers/rails_flags/application_controller.rb
69
+ - app/helpers/rails_flags/application_helper.rb
70
+ - app/jobs/rails_flags/application_job.rb
71
+ - app/mailers/rails_flags/application_mailer.rb
72
+ - app/models/rails_flags/application_record.rb
73
+ - app/views/layouts/rails_flags/application.html.erb
74
+ - config/routes.rb
75
+ - lib/rails_flags.rb
76
+ - lib/rails_flags/adapters/base.rb
77
+ - lib/rails_flags/adapters/memory_adapter.rb
78
+ - lib/rails_flags/adapters/redis_adapter.rb
79
+ - lib/rails_flags/configuration.rb
80
+ - lib/rails_flags/engine.rb
81
+ - lib/rails_flags/version.rb
82
+ - lib/tasks/rails_flags_tasks.rake
83
+ homepage: https://github.com/dhurba87/rails_flags
84
+ licenses:
85
+ - MIT
86
+ metadata:
87
+ homepage_uri: https://github.com/dhurba87/rails_flags
88
+ source_code_uri: https://github.com/dhurba87/rails_flags
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 3.5.3
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Feature flags for Rails.
108
+ test_files: []