simple_feature_flags 0.1.0
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 +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +78 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/.vscode/settings.json +11 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +89 -0
- data/LICENSE.txt +21 -0
- data/README.md +189 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/simple_feature_flags +9 -0
- data/lib/example_files/config/initializers/simple_feature_flags.rb +13 -0
- data/lib/example_files/config/simple_feature_flags.yml +19 -0
- data/lib/example_files/config/simple_feature_flags.yml.example +18 -0
- data/lib/simple_feature_flags.rb +11 -0
- data/lib/simple_feature_flags/cli.rb +7 -0
- data/lib/simple_feature_flags/cli/command.rb +9 -0
- data/lib/simple_feature_flags/cli/command/generate.rb +77 -0
- data/lib/simple_feature_flags/cli/options.rb +41 -0
- data/lib/simple_feature_flags/cli/runner.rb +23 -0
- data/lib/simple_feature_flags/ram_storage.rb +123 -0
- data/lib/simple_feature_flags/redis_storage.rb +131 -0
- data/lib/simple_feature_flags/test_ram_storage.rb +11 -0
- data/lib/simple_feature_flags/version.rb +5 -0
- data/simple_feature_flags.gemspec +38 -0
- metadata +202 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e5ef1f22981517d9de71ff3dd332c76b355ea7711b23954f49a1ffe080bbbc3f
|
|
4
|
+
data.tar.gz: cd1df44c32ae4a3d320af005f34e08bee655e60912c9463069c112e926de2568
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 653d6ad9e44a2700b20735feec5ae8e4a7a29ce1882c010ea8de203777579c0849dd32c5aa9660a66e9584d8ef63b806d1e3b883c09304bf51cf5674099887eb
|
|
7
|
+
data.tar.gz: 0c3d7c8cbd3a3e2175a80f01cdb4d6c202d108c172451fefcefc06d7d9e0db8a58a87a46b0f8c624ee78f1bab930037493dee7b1feb3c83ec482849e507758f6
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Style/StringLiterals:
|
|
2
|
+
Enabled: false
|
|
3
|
+
|
|
4
|
+
Style/SingleArgumentDig:
|
|
5
|
+
Enabled: false
|
|
6
|
+
|
|
7
|
+
Layout/LineLength:
|
|
8
|
+
Enabled: false
|
|
9
|
+
|
|
10
|
+
Style/Documentation:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
Style/ClassAndModuleChildren:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
|
+
Style/MethodCallWithArgsParentheses:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Style/ModuleFunction:
|
|
20
|
+
EnforcedStyle: 'extend_self'
|
|
21
|
+
|
|
22
|
+
Style/MissingElse:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Lint/NumberConversion:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Lint/ConstantResolution:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Style/RescueStandardError:
|
|
32
|
+
Enabled: false
|
|
33
|
+
|
|
34
|
+
Style/FormatStringToken:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Style/FormatString:
|
|
38
|
+
Enabled: false
|
|
39
|
+
|
|
40
|
+
Style/DocumentationMethod:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Style/Copyright:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/StringHashKeys:
|
|
47
|
+
Enabled: false
|
|
48
|
+
|
|
49
|
+
Style/InlineComment:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Layout/FirstHashElementLineBreak:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
Layout/FirstMethodArgumentLineBreak:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
Style/ConstantVisibility:
|
|
59
|
+
Enabled: false
|
|
60
|
+
|
|
61
|
+
Layout/FirstArrayElementLineBreak:
|
|
62
|
+
Enabled: false
|
|
63
|
+
|
|
64
|
+
Layout/MultilineMethodArgumentLineBreaks:
|
|
65
|
+
Enabled: false
|
|
66
|
+
|
|
67
|
+
Layout/MultilineAssignmentLayout:
|
|
68
|
+
Enabled: false
|
|
69
|
+
|
|
70
|
+
Bundler/GemComment:
|
|
71
|
+
Enabled: false
|
|
72
|
+
|
|
73
|
+
AllCops:
|
|
74
|
+
EnabledByDefault: true
|
|
75
|
+
Exclude:
|
|
76
|
+
- 'bin/*'
|
|
77
|
+
- 'spec/**/*'
|
|
78
|
+
- 'test/**/*'
|
data/.ruby-gemset
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
simple_feature_flags
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.4
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
simple_feature_flags (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
ast (2.4.2)
|
|
10
|
+
backport (1.2.0)
|
|
11
|
+
benchmark (0.1.1)
|
|
12
|
+
bundler-audit (0.8.0)
|
|
13
|
+
bundler (>= 1.2.0, < 3)
|
|
14
|
+
thor (~> 1.0)
|
|
15
|
+
byebug (11.1.3)
|
|
16
|
+
diff-lcs (1.4.4)
|
|
17
|
+
e2mmap (0.1.0)
|
|
18
|
+
jaro_winkler (1.5.4)
|
|
19
|
+
kramdown (2.3.1)
|
|
20
|
+
rexml
|
|
21
|
+
kramdown-parser-gfm (1.1.0)
|
|
22
|
+
kramdown (~> 2.0)
|
|
23
|
+
mini_portile2 (2.6.1)
|
|
24
|
+
minitest (5.14.4)
|
|
25
|
+
nokogiri (1.12.3)
|
|
26
|
+
mini_portile2 (~> 2.6.1)
|
|
27
|
+
racc (~> 1.4)
|
|
28
|
+
parallel (1.20.1)
|
|
29
|
+
parser (3.0.2.0)
|
|
30
|
+
ast (~> 2.4.1)
|
|
31
|
+
racc (1.5.2)
|
|
32
|
+
rainbow (3.0.0)
|
|
33
|
+
rake (12.3.3)
|
|
34
|
+
redis (4.4.0)
|
|
35
|
+
redis-namespace (1.8.1)
|
|
36
|
+
redis (>= 3.0.4)
|
|
37
|
+
regexp_parser (2.1.1)
|
|
38
|
+
reverse_markdown (2.0.0)
|
|
39
|
+
nokogiri
|
|
40
|
+
rexml (3.2.5)
|
|
41
|
+
rubocop (1.19.0)
|
|
42
|
+
parallel (~> 1.10)
|
|
43
|
+
parser (>= 3.0.0.0)
|
|
44
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
45
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
46
|
+
rexml
|
|
47
|
+
rubocop-ast (>= 1.9.1, < 2.0)
|
|
48
|
+
ruby-progressbar (~> 1.7)
|
|
49
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
50
|
+
rubocop-ast (1.9.1)
|
|
51
|
+
parser (>= 3.0.1.1)
|
|
52
|
+
ruby-progressbar (1.11.0)
|
|
53
|
+
solargraph (0.43.0)
|
|
54
|
+
backport (~> 1.2)
|
|
55
|
+
benchmark
|
|
56
|
+
bundler (>= 1.17.2)
|
|
57
|
+
diff-lcs (~> 1.4)
|
|
58
|
+
e2mmap
|
|
59
|
+
jaro_winkler (~> 1.5)
|
|
60
|
+
kramdown (~> 2.3)
|
|
61
|
+
kramdown-parser-gfm (~> 1.1)
|
|
62
|
+
parser (~> 3.0)
|
|
63
|
+
reverse_markdown (>= 1.0.5, < 3)
|
|
64
|
+
rubocop (>= 0.52)
|
|
65
|
+
thor (~> 1.0)
|
|
66
|
+
tilt (~> 2.0)
|
|
67
|
+
yard (~> 0.9, >= 0.9.24)
|
|
68
|
+
thor (1.1.0)
|
|
69
|
+
tilt (2.0.10)
|
|
70
|
+
unicode-display_width (2.0.0)
|
|
71
|
+
yard (0.9.26)
|
|
72
|
+
|
|
73
|
+
PLATFORMS
|
|
74
|
+
ruby
|
|
75
|
+
|
|
76
|
+
DEPENDENCIES
|
|
77
|
+
bundler
|
|
78
|
+
bundler-audit
|
|
79
|
+
byebug
|
|
80
|
+
minitest (~> 5.0)
|
|
81
|
+
rake (~> 12.0)
|
|
82
|
+
redis
|
|
83
|
+
redis-namespace
|
|
84
|
+
rubocop
|
|
85
|
+
simple_feature_flags!
|
|
86
|
+
solargraph
|
|
87
|
+
|
|
88
|
+
BUNDLED WITH
|
|
89
|
+
2.1.4
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Espago
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Simple Feature Flags
|
|
2
|
+
|
|
3
|
+
Fast, simple and reliable feature flags using Redis or local memory in your Rails app.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
[[_TOC_]]
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### Gem installation
|
|
11
|
+
|
|
12
|
+
Add this line to your application's Gemfile:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
gem 'simple_feature_flags'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
And then execute:
|
|
19
|
+
|
|
20
|
+
$ bundle install
|
|
21
|
+
|
|
22
|
+
Or install it yourself as:
|
|
23
|
+
|
|
24
|
+
$ gem install simple_feature_flags
|
|
25
|
+
|
|
26
|
+
### Generate config files
|
|
27
|
+
|
|
28
|
+
This gem uses it's custom generator to make configuration easier for you.
|
|
29
|
+
|
|
30
|
+
#### Rails apps
|
|
31
|
+
|
|
32
|
+
1. Navigate to the root directory of your Rails project
|
|
33
|
+
1. Generate config files
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
$ simple_feature_flags -g
|
|
37
|
+
# or
|
|
38
|
+
$ simple_feature_flags --generate
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This should generate an initializer `config/initializers/simple_feature_flags.rb`
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# config/initializers/simple_feature_flags.rb
|
|
45
|
+
|
|
46
|
+
# frozen_string_literal: true
|
|
47
|
+
# Redis has 16 DBs (0 to 15)
|
|
48
|
+
|
|
49
|
+
FEATURE_FLAGS = if ::Rails.env.test?
|
|
50
|
+
# Use TestRamStorage in tests to make them faster
|
|
51
|
+
::SimpleFeatureFlags::TestRamStorage.new("#{::Rails.root.to_s}/config/simple_feature_flags.yml")
|
|
52
|
+
else
|
|
53
|
+
redis = ::Redis.new(host: '127.0.0.1', port: 6379, db: 0)
|
|
54
|
+
# We recommend using the `redis-namespace` gem to avoid key conflicts with Sidekiq or Resque
|
|
55
|
+
# redis = ::Redis::Namespace.new(:simple_feature_flags, redis: redis)
|
|
56
|
+
|
|
57
|
+
::SimpleFeatureFlags::RedisStorage.new(redis, "#{::Rails.root.to_s}/config/simple_feature_flags.yml")
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This initializer in turn makes use of the generated config file `config/simple_feature_flags.yml`
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
---
|
|
65
|
+
# Feature Flags that will be created if they don't exist already
|
|
66
|
+
:mandatory:
|
|
67
|
+
# example flag - it will be created with these properties if there is no such flag in Redis/RAM
|
|
68
|
+
# - name: example
|
|
69
|
+
# active: 'true' # 'false' is the default value
|
|
70
|
+
# description: example
|
|
71
|
+
|
|
72
|
+
- name: example_flag
|
|
73
|
+
description: This is an example flag which will be automatically added when you start your app (it will be disabled)
|
|
74
|
+
|
|
75
|
+
- name: example_active_flag
|
|
76
|
+
active: 'true'
|
|
77
|
+
description: This is an example flag which will be automatically added when you start your app (it will be enabled)
|
|
78
|
+
|
|
79
|
+
# nothing will happen if flag that is to be removed does not exist in Redis/RAM
|
|
80
|
+
# An array of Feature Flag names that will be removed on app startup
|
|
81
|
+
:remove:
|
|
82
|
+
- flag_to_be_removed
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Non-Rails apps
|
|
87
|
+
|
|
88
|
+
1. Navigate to the root directory of your project
|
|
89
|
+
1. Generate config files
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
$ simple_feature_flags -g --no-rails
|
|
93
|
+
# or
|
|
94
|
+
$ simple_feature_flags --generate --no-rails
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
This gem provides an easy way of dealing with feature flags. At this point in time it only supports global feature flags stored either in RAM or Redis.
|
|
100
|
+
|
|
101
|
+
### Storage types/Storage adapters
|
|
102
|
+
|
|
103
|
+
All storage adapters have the same API for dealing with feature flags. The only difference is in how they store data.
|
|
104
|
+
|
|
105
|
+
#### SimpleFeatureFlags::RedisStorage
|
|
106
|
+
|
|
107
|
+
This class makes use of Redis to store feature flag data. You can make use of it like so:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
require 'redis'
|
|
111
|
+
|
|
112
|
+
redis = ::Redis.new
|
|
113
|
+
config_file = "#{::Rails.root.to_s}/config/simple_feature_flags.yml"
|
|
114
|
+
|
|
115
|
+
FEATURE_FLAGS = ::SimpleFeatureFlags::RedisStorage.new(redis, config_file)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### SimpleFeatureFlags::RamStorage
|
|
119
|
+
|
|
120
|
+
This class stores all feature flag data in a simple Ruby `::Hash`. You can make use of it like so:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
config_file = "#{::Rails.root.to_s}/config/simple_feature_flags.yml"
|
|
124
|
+
|
|
125
|
+
FEATURE_FLAGS = ::SimpleFeatureFlags::RamStorage.new(config_file)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Functionality
|
|
129
|
+
|
|
130
|
+
#### Adding feature flags
|
|
131
|
+
|
|
132
|
+
You can add new feature flags programmatically, though we highly encourage you to use the generated `config/simple_feature_flags.yml` file instead. It will make it easier to add and/or remove feature flags automatically on app startup without having to add them manually after merging a branch with new feature flags.
|
|
133
|
+
|
|
134
|
+
In case you'd like to add flags programmatically
|
|
135
|
+
```ruby
|
|
136
|
+
FEATURE_FLAGS.add(:feature_name, 'Description')
|
|
137
|
+
FEATURE_FLAGS.active?(:feature_name) #=> false
|
|
138
|
+
|
|
139
|
+
# add a new active flag
|
|
140
|
+
FEATURE_FLAGS.add(:active_feature_name, 'Description', true)
|
|
141
|
+
FEATURE_FLAGS.active?(:active_feature_name) #=> true
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Removing feature flags
|
|
145
|
+
|
|
146
|
+
You can remove feature flags programmatically, though we highly encourage you to use the generated `config/simple_feature_flags.yml` file instead. It will make it easier to add and/or remove feature flags automatically on app startup without having to add them manually after merging a branch with new feature flags.
|
|
147
|
+
|
|
148
|
+
In case you'd like to remove flags programmatically
|
|
149
|
+
```ruby
|
|
150
|
+
FEATURE_FLAGS.remove(:feature_name)
|
|
151
|
+
FEATURE_FLAGS.active?(:feature_name) #=> false
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Run a block of code only when the flag is active
|
|
155
|
+
|
|
156
|
+
There are two ways of running code only when the feature flag is active
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
number = 1
|
|
160
|
+
if FEATURE_FLAGS.active?(:feature_name)
|
|
161
|
+
number += 1
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# or using a block
|
|
165
|
+
|
|
166
|
+
# this code will run only when the :feature_name flag is active
|
|
167
|
+
FEATURE_FLAGS.with_feature(:feature_name) do
|
|
168
|
+
number += 1
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# feature flags that don't exist will return false
|
|
172
|
+
FEATURE_FLAGS.active?(:non_existant) #=> false
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
179
|
+
|
|
180
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
181
|
+
|
|
182
|
+
## Contributing
|
|
183
|
+
|
|
184
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/simple_feature_flags.
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "simple_feature_flags"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Redis has 16 DBs (0 to 15)
|
|
3
|
+
|
|
4
|
+
FEATURE_FLAGS = if ::Rails.env.test?
|
|
5
|
+
# Use TestRamStorage in tests to make them faster
|
|
6
|
+
::SimpleFeatureFlags::TestRamStorage.new("#{::Rails.root.to_s}/config/simple_feature_flags.yml")
|
|
7
|
+
else
|
|
8
|
+
redis = ::Redis.new(host: '127.0.0.1', port: 6379, db: 0)
|
|
9
|
+
# We recommend using the `redis-namespace` gem to avoid key conflicts with Sidekiq or Resque
|
|
10
|
+
# redis = ::Redis::Namespace.new(:simple_feature_flags, redis: redis)
|
|
11
|
+
|
|
12
|
+
::SimpleFeatureFlags::RedisStorage.new(redis, "#{::Rails.root.to_s}/config/simple_feature_flags.yml")
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Feature Flags that will be created if they don't exist already
|
|
3
|
+
:mandatory:
|
|
4
|
+
# example flag - it will be created with these properties if there is no such flag in Redis/RAM
|
|
5
|
+
# - name: example
|
|
6
|
+
# active: 'true' # 'false' is the default value
|
|
7
|
+
# description: example
|
|
8
|
+
|
|
9
|
+
- name: example_flag
|
|
10
|
+
description: This is an example flag which will be automatically added when you start your app (it will be disabled)
|
|
11
|
+
|
|
12
|
+
- name: example_active_flag
|
|
13
|
+
active: 'true'
|
|
14
|
+
description: This is an example flag which will be automatically added when you start your app (it will be enabled)
|
|
15
|
+
|
|
16
|
+
# nothing will happen if flag that is to be removed does not exist in Redis/RAM
|
|
17
|
+
# An array of Feature Flag names that will be removed on app startup
|
|
18
|
+
:remove:
|
|
19
|
+
- flag_to_be_removed
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Feature Flags that will be created if they don't exist already
|
|
3
|
+
:mandatory:
|
|
4
|
+
# example flag - it will be created with these properties if there is no such flag in Redis/RAM
|
|
5
|
+
# - name: example
|
|
6
|
+
# active: 'true' # 'false' is the default value
|
|
7
|
+
# description: example
|
|
8
|
+
|
|
9
|
+
- name: example_flag
|
|
10
|
+
description: This is an example flag which will be automatically added when you start your app (it will be disabled)
|
|
11
|
+
|
|
12
|
+
- name: example_active_flag
|
|
13
|
+
description: This is an example flag which will be automatically added when you start your app (it will be enabled)
|
|
14
|
+
|
|
15
|
+
# nothing will happen if flag that is to be removed does not exist in Redis/RAM
|
|
16
|
+
# An array of Feature Flag names that will be removed on app startup
|
|
17
|
+
:remove:
|
|
18
|
+
- flag_to_be_removed
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleFeatureFlags
|
|
4
|
+
class NoSuchCommandError < StandardError; end
|
|
5
|
+
|
|
6
|
+
class IncorrectWorkingDirectoryError < StandardError; end
|
|
7
|
+
|
|
8
|
+
class FlagNotDefinedError < StandardError; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Dir[File.expand_path('simple_feature_flags/*.rb', __dir__)].sort.each { |file| require file }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'byebug'
|
|
5
|
+
|
|
6
|
+
module SimpleFeatureFlags
|
|
7
|
+
module Cli
|
|
8
|
+
module Command
|
|
9
|
+
class Generate
|
|
10
|
+
CONFIG_FILE = 'simple_feature_flags.yml'
|
|
11
|
+
|
|
12
|
+
attr_reader :options
|
|
13
|
+
|
|
14
|
+
def initialize(options)
|
|
15
|
+
@options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
if options.rails
|
|
20
|
+
generate_for_rails
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
::FileUtils.cp example_config_file, destination_file
|
|
25
|
+
|
|
26
|
+
puts 'Generated:'
|
|
27
|
+
puts '----------'
|
|
28
|
+
puts "* #{destination_file}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def generate_for_rails
|
|
34
|
+
::FileUtils.cp_r example_config_dir, destination_dir
|
|
35
|
+
|
|
36
|
+
puts "Generated:"
|
|
37
|
+
puts '----------'
|
|
38
|
+
puts "- #{::File.join(destination_dir, 'config')}"
|
|
39
|
+
print_dir_tree(example_config_dir, 1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def print_dir_tree(dir, embed_level = 0)
|
|
43
|
+
padding = ' ' * (embed_level * 2)
|
|
44
|
+
|
|
45
|
+
children = ::Dir.new(dir).entries.reject { |el| /^\.{1,2}$/ =~ el }
|
|
46
|
+
|
|
47
|
+
children.each do |child|
|
|
48
|
+
child_dir = ::File.join(dir, child)
|
|
49
|
+
::Dir.new(::File.join(dir, child))
|
|
50
|
+
puts "#{padding}- #{child}"
|
|
51
|
+
print_dir_tree(child_dir, embed_level + 1)
|
|
52
|
+
rescue Errno::ENOTDIR
|
|
53
|
+
puts "#{padding}* #{child}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def example_config_dir
|
|
58
|
+
::File.join(::File.expand_path(__dir__), '..', '..', '..', 'example_files', 'config')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def example_config_file
|
|
62
|
+
::File.join(example_config_dir, CONFIG_FILE)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def destination_dir
|
|
66
|
+
raise IncorrectWorkingDirectoryError, "You should enter the main directory of your Rails project!" if options.rails && !::Dir.new(::Dir.pwd).entries.include?('config')
|
|
67
|
+
|
|
68
|
+
::Dir.pwd
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def destination_file
|
|
72
|
+
@destination_file ||= ::File.join(destination_dir, CONFIG_FILE)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module SimpleFeatureFlags
|
|
6
|
+
module Cli
|
|
7
|
+
class Options
|
|
8
|
+
attr_reader :opt_parser, :generate, :help, :rails
|
|
9
|
+
|
|
10
|
+
def initialize(args)
|
|
11
|
+
@rails = true
|
|
12
|
+
|
|
13
|
+
@opt_parser = ::OptionParser.new do |opts|
|
|
14
|
+
opts.banner = 'Usage: simple_feature_flags [options]'
|
|
15
|
+
|
|
16
|
+
opts.separator ''
|
|
17
|
+
opts.separator 'Commands:'
|
|
18
|
+
|
|
19
|
+
opts.on('-g', '--generate', 'Generate necessary config files') { @generate = true }
|
|
20
|
+
|
|
21
|
+
opts.on('-h', '--help', 'Display the manual') do
|
|
22
|
+
puts opts
|
|
23
|
+
exit
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
opts.on('-v', '--version', 'Show gem version') do
|
|
27
|
+
puts VERSION
|
|
28
|
+
exit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
opts.separator ''
|
|
32
|
+
opts.separator 'Modifiers:'
|
|
33
|
+
|
|
34
|
+
opts.on('--[no-]rails', 'Use generators suited for Rails apps') { |r| @rails = r }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opt_parser.parse!(args)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleFeatureFlags
|
|
4
|
+
module Cli
|
|
5
|
+
class Runner
|
|
6
|
+
attr_reader :options
|
|
7
|
+
|
|
8
|
+
def initialize(args = ARGV)
|
|
9
|
+
@options = Options.new(args)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
command_class = if @options.generate
|
|
14
|
+
::SimpleFeatureFlags::Cli::Command::Generate
|
|
15
|
+
else
|
|
16
|
+
raise NoSuchCommandError, 'No such command!'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
command_class.new(options).run
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleFeatureFlags
|
|
4
|
+
class RamStorage
|
|
5
|
+
attr_reader :file, :mandatory_flags, :flags
|
|
6
|
+
|
|
7
|
+
def initialize(file)
|
|
8
|
+
@file = file
|
|
9
|
+
@redis = redis
|
|
10
|
+
@mandatory_flags = []
|
|
11
|
+
@flags = {}
|
|
12
|
+
|
|
13
|
+
import_flags_from_file
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def active?(feature, _ignore_file = false)
|
|
17
|
+
__active__(feature)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def exists?(feature)
|
|
21
|
+
return false if [nil, ''].include? flags[feature.to_sym]
|
|
22
|
+
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def description(feature)
|
|
27
|
+
flags.dig(feature.to_sym, 'description')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def with_feature(feature, ignore_file = false, &block)
|
|
31
|
+
return unless active?(feature, ignore_file)
|
|
32
|
+
|
|
33
|
+
block.call
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def activate(feature)
|
|
37
|
+
return false unless exists?(feature)
|
|
38
|
+
|
|
39
|
+
flags[feature.to_sym]['active'] = 'true'
|
|
40
|
+
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def deactivate(feature)
|
|
45
|
+
return false unless exists?(feature)
|
|
46
|
+
|
|
47
|
+
flags[feature.to_sym]['active'] = 'false'
|
|
48
|
+
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get(feature)
|
|
53
|
+
return unless exists?(feature)
|
|
54
|
+
|
|
55
|
+
hash = flags[feature.to_sym]
|
|
56
|
+
hash['mandatory'] = mandatory_flags.include?(feature.to_s)
|
|
57
|
+
|
|
58
|
+
hash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add(feature, description, active = 'false')
|
|
62
|
+
return false if exists?(feature)
|
|
63
|
+
|
|
64
|
+
active = case active
|
|
65
|
+
when true, 'true'
|
|
66
|
+
'true'
|
|
67
|
+
else
|
|
68
|
+
'false'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
hash = {
|
|
72
|
+
'name' => feature.to_s,
|
|
73
|
+
'active' => active,
|
|
74
|
+
'description' => description
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
flags[feature.to_sym] = hash
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def remove(feature)
|
|
81
|
+
return false unless exists?(feature)
|
|
82
|
+
|
|
83
|
+
removed = get(feature)
|
|
84
|
+
flags.delete(feature.to_sym)
|
|
85
|
+
|
|
86
|
+
removed
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def all
|
|
90
|
+
hashes = []
|
|
91
|
+
|
|
92
|
+
flags.each do |key, _val|
|
|
93
|
+
hashes << get(key)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
hashes
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def redis; end
|
|
100
|
+
|
|
101
|
+
def namespaced_redis; end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def __active__(feature)
|
|
106
|
+
flags.dig(feature.to_sym, 'active') == 'true'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def import_flags_from_file
|
|
110
|
+
changes = YAML.load_file(file)
|
|
111
|
+
changes = { mandatory: [], remove: [] } unless changes.is_a? Hash
|
|
112
|
+
|
|
113
|
+
changes[:mandatory].each do |el|
|
|
114
|
+
mandatory_flags << el['name']
|
|
115
|
+
add(el['name'], el['description'], el['active'])
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
changes[:remove].each do |el|
|
|
119
|
+
remove(el)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleFeatureFlags
|
|
4
|
+
class RedisStorage
|
|
5
|
+
attr_reader :file, :redis, :mandatory_flags
|
|
6
|
+
|
|
7
|
+
def initialize(redis, file)
|
|
8
|
+
@file = file
|
|
9
|
+
@redis = redis
|
|
10
|
+
@mandatory_flags = []
|
|
11
|
+
|
|
12
|
+
import_flags_from_file
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def active?(feature, _ignore_file = false)
|
|
16
|
+
__active__(feature)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def exists?(feature)
|
|
20
|
+
return false if [nil, ''].include? redis.hget(feature.to_s, 'name')
|
|
21
|
+
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def description(feature)
|
|
26
|
+
redis.hget(feature.to_s, 'description')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def with_feature(feature, _ignore_file = false, &block)
|
|
30
|
+
return unless active?(feature)
|
|
31
|
+
|
|
32
|
+
block.call
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def activate(feature)
|
|
36
|
+
return false unless exists?(feature)
|
|
37
|
+
|
|
38
|
+
redis.hset(feature.to_s, 'active', 'true')
|
|
39
|
+
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def deactivate(feature)
|
|
44
|
+
return false unless exists?(feature)
|
|
45
|
+
|
|
46
|
+
redis.hset(feature.to_s, 'active', 'false')
|
|
47
|
+
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def get(feature)
|
|
52
|
+
return unless exists?(feature)
|
|
53
|
+
|
|
54
|
+
hash = redis.hgetall(feature.to_s)
|
|
55
|
+
hash['mandatory'] = mandatory_flags.include?(feature.to_s)
|
|
56
|
+
|
|
57
|
+
hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def add(feature, description, active = 'false')
|
|
61
|
+
return false if exists?(feature)
|
|
62
|
+
|
|
63
|
+
active = case active
|
|
64
|
+
when true, 'true'
|
|
65
|
+
'true'
|
|
66
|
+
else
|
|
67
|
+
'false'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
hash = {
|
|
71
|
+
'name' => feature.to_s,
|
|
72
|
+
'active' => active,
|
|
73
|
+
'description' => description
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
redis.hset(feature.to_s, hash)
|
|
77
|
+
hash
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def remove(feature)
|
|
81
|
+
return false unless exists?(feature)
|
|
82
|
+
|
|
83
|
+
removed = get(feature)
|
|
84
|
+
redis.del(feature.to_s)
|
|
85
|
+
|
|
86
|
+
removed
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def all
|
|
90
|
+
keys = []
|
|
91
|
+
hashes = []
|
|
92
|
+
redis.scan_each(match: "*") do |key|
|
|
93
|
+
next if keys.include?(key)
|
|
94
|
+
|
|
95
|
+
keys << key
|
|
96
|
+
hashes << get(key)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
hashes
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def namespaced_redis
|
|
103
|
+
redis
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def __active__(feature)
|
|
109
|
+
case redis.hget(feature.to_s, 'active')
|
|
110
|
+
when 'true'
|
|
111
|
+
true
|
|
112
|
+
when 'false'
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def import_flags_from_file
|
|
118
|
+
changes = YAML.load_file(file)
|
|
119
|
+
changes = { mandatory: [], remove: [] } unless changes.is_a? Hash
|
|
120
|
+
|
|
121
|
+
changes[:mandatory].each do |el|
|
|
122
|
+
mandatory_flags << el['name']
|
|
123
|
+
add(el['name'], el['description'], el['active'])
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
changes[:remove].each do |el|
|
|
127
|
+
remove(el)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleFeatureFlags
|
|
4
|
+
class TestRamStorage < RamStorage
|
|
5
|
+
def active?(feature, ignore_file = false)
|
|
6
|
+
raise(FlagNotDefinedError, "Feature Flag `#{feature}` is not defined as mandatory in #{file}") if !ignore_file && !mandatory_flags.include?(feature.to_s)
|
|
7
|
+
|
|
8
|
+
__active__(feature)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/simple_feature_flags/version'
|
|
4
|
+
|
|
5
|
+
::Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "simple_feature_flags"
|
|
7
|
+
spec.version = ::SimpleFeatureFlags::VERSION
|
|
8
|
+
spec.authors = ["Espago", "Mateusz Drewniak"]
|
|
9
|
+
spec.email = ["m.drewniak@espago.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Simple feature flag functionality for your Ruby/Rails/Sinatra app!"
|
|
12
|
+
spec.description = "A simple Ruby gem which lets you dynamically enable/disable parts of your code using Redis or your server's RAM!"
|
|
13
|
+
spec.homepage = "https://github.com/espago/simple_feature_flags"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ::Gem::Requirement.new(">= 2.5.0")
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/espago/simple_feature_flags"
|
|
19
|
+
|
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
+
spec.files = ::Dir.chdir(::File.expand_path(__dir__)) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
24
|
+
end
|
|
25
|
+
spec.bindir = "exe"
|
|
26
|
+
spec.executables = ['simple_feature_flags']
|
|
27
|
+
spec.require_paths = ["lib"]
|
|
28
|
+
|
|
29
|
+
spec.add_development_dependency 'bundler'
|
|
30
|
+
spec.add_development_dependency 'bundler-audit'
|
|
31
|
+
spec.add_development_dependency 'byebug'
|
|
32
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
|
33
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
|
34
|
+
spec.add_development_dependency 'redis'
|
|
35
|
+
spec.add_development_dependency 'redis-namespace'
|
|
36
|
+
spec.add_development_dependency 'rubocop'
|
|
37
|
+
spec.add_development_dependency 'solargraph'
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: simple_feature_flags
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Espago
|
|
8
|
+
- Mateusz Drewniak
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2021-08-13 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: bundler
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ">="
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0'
|
|
21
|
+
type: :development
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - ">="
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: bundler-audit
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '0'
|
|
35
|
+
type: :development
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: byebug
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '0'
|
|
49
|
+
type: :development
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: minitest
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '5.0'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '5.0'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: rake
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - "~>"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '12.0'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - "~>"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '12.0'
|
|
84
|
+
- !ruby/object:Gem::Dependency
|
|
85
|
+
name: redis
|
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
type: :development
|
|
92
|
+
prerelease: false
|
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
- !ruby/object:Gem::Dependency
|
|
99
|
+
name: redis-namespace
|
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
type: :development
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: rubocop
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '0'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: solargraph
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
140
|
+
description: A simple Ruby gem which lets you dynamically enable/disable parts of
|
|
141
|
+
your code using Redis or your server's RAM!
|
|
142
|
+
email:
|
|
143
|
+
- m.drewniak@espago.com
|
|
144
|
+
executables:
|
|
145
|
+
- simple_feature_flags
|
|
146
|
+
extensions: []
|
|
147
|
+
extra_rdoc_files: []
|
|
148
|
+
files:
|
|
149
|
+
- ".gitignore"
|
|
150
|
+
- ".rubocop.yml"
|
|
151
|
+
- ".ruby-gemset"
|
|
152
|
+
- ".ruby-version"
|
|
153
|
+
- ".travis.yml"
|
|
154
|
+
- ".vscode/settings.json"
|
|
155
|
+
- Gemfile
|
|
156
|
+
- Gemfile.lock
|
|
157
|
+
- LICENSE.txt
|
|
158
|
+
- README.md
|
|
159
|
+
- Rakefile
|
|
160
|
+
- bin/console
|
|
161
|
+
- bin/setup
|
|
162
|
+
- exe/simple_feature_flags
|
|
163
|
+
- lib/example_files/config/initializers/simple_feature_flags.rb
|
|
164
|
+
- lib/example_files/config/simple_feature_flags.yml
|
|
165
|
+
- lib/example_files/config/simple_feature_flags.yml.example
|
|
166
|
+
- lib/simple_feature_flags.rb
|
|
167
|
+
- lib/simple_feature_flags/cli.rb
|
|
168
|
+
- lib/simple_feature_flags/cli/command.rb
|
|
169
|
+
- lib/simple_feature_flags/cli/command/generate.rb
|
|
170
|
+
- lib/simple_feature_flags/cli/options.rb
|
|
171
|
+
- lib/simple_feature_flags/cli/runner.rb
|
|
172
|
+
- lib/simple_feature_flags/ram_storage.rb
|
|
173
|
+
- lib/simple_feature_flags/redis_storage.rb
|
|
174
|
+
- lib/simple_feature_flags/test_ram_storage.rb
|
|
175
|
+
- lib/simple_feature_flags/version.rb
|
|
176
|
+
- simple_feature_flags.gemspec
|
|
177
|
+
homepage: https://github.com/espago/simple_feature_flags
|
|
178
|
+
licenses:
|
|
179
|
+
- MIT
|
|
180
|
+
metadata:
|
|
181
|
+
homepage_uri: https://github.com/espago/simple_feature_flags
|
|
182
|
+
source_code_uri: https://github.com/espago/simple_feature_flags
|
|
183
|
+
post_install_message:
|
|
184
|
+
rdoc_options: []
|
|
185
|
+
require_paths:
|
|
186
|
+
- lib
|
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
188
|
+
requirements:
|
|
189
|
+
- - ">="
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: 2.5.0
|
|
192
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
|
+
requirements:
|
|
194
|
+
- - ">="
|
|
195
|
+
- !ruby/object:Gem::Version
|
|
196
|
+
version: '0'
|
|
197
|
+
requirements: []
|
|
198
|
+
rubygems_version: 3.1.6
|
|
199
|
+
signing_key:
|
|
200
|
+
specification_version: 4
|
|
201
|
+
summary: Simple feature flag functionality for your Ruby/Rails/Sinatra app!
|
|
202
|
+
test_files: []
|