flagship 0.4.0 → 0.7.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 +5 -5
- data/.travis.yml +6 -4
- data/CHANGELOG.md +29 -0
- data/README.md +93 -0
- data/flagship.gemspec +1 -1
- data/lib/flagship/context.rb +26 -7
- data/lib/flagship/dsl.rb +17 -1
- data/lib/flagship/flagset.rb +3 -2
- data/lib/flagship/version.rb +1 -1
- data/lib/flagship.rb +57 -32
- metadata +8 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 60210addd43057e1808189582ab298a84f14bb70468c1685e676ceb5481a7a8a
|
4
|
+
data.tar.gz: 4f85d9c988e71f2d219938310990757869269cf31e84706e093640f0e07b1326
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43dd867d1d2a6de1433833709fdd9c251ee095fd088a0d4f024fc802d966067f24c80a4d0615202892663014c03f22ba295dacc4c55610982ed2d5f4316a3c9f
|
7
|
+
data.tar.gz: 45152c852249cfa8692da6c234f1d6f246ab30c1111c7458436ea8a99ab792077a0af4608570954bf9a4ea4ab2aff859a09e5067453d0867552acf68b7fc15e2
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,34 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [0.7.0] - 2020-05-01
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
|
7
|
+
- Make context thread safe [#32](https://github.com/yuya-takeyama/flagship/pull/32) ([@mtsmfm](https://github.com/mtsmfm))
|
8
|
+
|
9
|
+
## [0.6.1] - 2017-05-17
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Add separated methods to clear state [#30](https://github.com/yuya-takeyama/flagship/pull/30)
|
14
|
+
|
15
|
+
## [0.6.0] - 2017-05-15
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- Add `Flagship.with_context` method to apply temporal changes to context variables [#26](https://github.com/yuya-takeyama/flagship/pull/26)
|
20
|
+
- Now `Flagship.clear_state` clears context variables as well [#27](https://github.com/yuya-takeyama/flagship/pull/27)
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
- Allow `Flagship.set_context` to set multiple variables at once using `Hash` [#25](https://github.com/yuya-takeyama/flagship/pull/25)
|
25
|
+
|
26
|
+
## [0.5.0] - 2017-01-07
|
27
|
+
|
28
|
+
- Documented about helper methods [#21](https://github.com/yuya-takeyama/flagship/pull/21)
|
29
|
+
- Helper methods can be specified as a `Symbol` [#21](https://github.com/yuya-takeyama/flagship/pull/21)
|
30
|
+
- Helper methods are extended [#22](https://github.com/yuya-takeyama/flagship/pull/22)
|
31
|
+
|
3
32
|
## [0.4.0] - 2016-12-23
|
4
33
|
|
5
34
|
### Added
|
data/README.md
CHANGED
@@ -62,6 +62,24 @@ Or you can set a method too.
|
|
62
62
|
Flagship.set_context :current_user, method(:current_user)
|
63
63
|
```
|
64
64
|
|
65
|
+
### Apply temporal changes to context variables
|
66
|
+
|
67
|
+
Using `Flagship.with_context` method, you can override context variables temporarily.
|
68
|
+
|
69
|
+
```rb
|
70
|
+
class User
|
71
|
+
def enabled_features
|
72
|
+
Flagship.with_context current_user: self do
|
73
|
+
Flagship.features.enabled.map(&:key)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
It's useful when you implement a method using `Flagship` into some domain objects.
|
80
|
+
|
81
|
+
By using this, values specified in the argument is overridden and other values are inherited.
|
82
|
+
|
65
83
|
### Extend flagset
|
66
84
|
|
67
85
|
```rb
|
@@ -147,6 +165,81 @@ Flagship.define :blog do
|
|
147
165
|
end
|
148
166
|
```
|
149
167
|
|
168
|
+
## Helper methods
|
169
|
+
|
170
|
+
You can define helpers as normal methods with `def`. Methods can be used within blocks, procs, or as symbolic names for if statements to tidy up your code.
|
171
|
+
|
172
|
+
```rb
|
173
|
+
Flagship.define :blog do
|
174
|
+
def is_author(comment, user)
|
175
|
+
comment.author == user
|
176
|
+
end
|
177
|
+
|
178
|
+
def can_view_comment(context)
|
179
|
+
context.current_user.moderator?
|
180
|
+
end
|
181
|
+
|
182
|
+
enable :comment, if: :can_view_comment
|
183
|
+
enable :comment_deletion, if: ->(context) { is_author(context.comment, context.current_user) }
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
To share helpers, you can simply include them as modules.
|
188
|
+
|
189
|
+
```rb
|
190
|
+
module FlagHelpers
|
191
|
+
def is_author(context)
|
192
|
+
context.comment.author == context.current_user
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
Flagship.define :development do
|
197
|
+
include FlagHelpers
|
198
|
+
enable :delete, if: :is_author
|
199
|
+
end
|
200
|
+
|
201
|
+
Flagship.define :production do
|
202
|
+
include FlagHelpers
|
203
|
+
enable :delete, if: :is_author
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
And you can also extend helper methods from base flagset.
|
208
|
+
|
209
|
+
```rb
|
210
|
+
Flagship.define :base do
|
211
|
+
def is_author(context)
|
212
|
+
context.comment.author == context.current_user
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
Flagship.define :production do
|
217
|
+
enable :delete, if: :is_author
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
### Testing
|
222
|
+
|
223
|
+
#### RSpec
|
224
|
+
|
225
|
+
It's recommended to clear state of `Flagship` before the suite and after the each tests.
|
226
|
+
|
227
|
+
You can do it by configuring like below:
|
228
|
+
|
229
|
+
```rb
|
230
|
+
RSpec.configure do |config|
|
231
|
+
config.before(:suite) do
|
232
|
+
Flagship.clear_context
|
233
|
+
Flagship.clear_current_flagset
|
234
|
+
end
|
235
|
+
|
236
|
+
config.after(:each) do
|
237
|
+
Flagship.clear_context
|
238
|
+
Flagship.clear_current_flagset
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
150
243
|
## Development
|
151
244
|
|
152
245
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/flagship.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_development_dependency "bundler", "
|
24
|
+
spec.add_development_dependency "bundler", "> 1.13"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.5.0"
|
27
27
|
end
|
data/lib/flagship/context.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
class Flagship::Context
|
2
|
-
|
3
|
-
|
4
|
-
end
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :current_values, :clear
|
5
6
|
|
6
7
|
def __set(key, value)
|
7
|
-
|
8
|
+
current_values[key.to_sym] = value
|
8
9
|
end
|
9
10
|
|
10
11
|
def method_missing(name, args = [], &block)
|
11
|
-
if
|
12
|
-
value =
|
12
|
+
if current_values.key?(name)
|
13
|
+
value = current_values[name]
|
13
14
|
|
14
15
|
if value.respond_to?(:call)
|
15
16
|
value.call
|
@@ -22,6 +23,24 @@ class Flagship::Context
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def respond_to_missing?(name, include_private = false)
|
25
|
-
|
26
|
+
current_values.key?(name) or super
|
27
|
+
end
|
28
|
+
|
29
|
+
def with_values(new_values, &block)
|
30
|
+
original_values = current_values
|
31
|
+
self.current_values = current_values.dup.merge(new_values)
|
32
|
+
block.call
|
33
|
+
ensure
|
34
|
+
self.current_values = original_values
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def current_values
|
40
|
+
Thread.current[:__flagship_context_values] ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def current_values=(new_values)
|
44
|
+
Thread.current[:__flagship_context_values] = new_values
|
26
45
|
end
|
27
46
|
end
|
data/lib/flagship/dsl.rb
CHANGED
@@ -11,14 +11,26 @@ class Flagship::Dsl
|
|
11
11
|
@definition = block
|
12
12
|
@base_tags = {}
|
13
13
|
|
14
|
+
if @base
|
15
|
+
@base.helper_methods.each do |method|
|
16
|
+
define_singleton_method(method.name, &method)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
14
20
|
instance_eval(&@definition)
|
15
21
|
|
16
|
-
|
22
|
+
helper_methods = singleton_methods.map { |sym| method(sym) }
|
23
|
+
@flagset = ::Flagship::Flagset.new(@key, @features, @base, helper_methods)
|
17
24
|
end
|
18
25
|
|
19
26
|
def enable(key, opts = {})
|
20
27
|
tags = opts.dup
|
21
28
|
condition = tags.delete(:if)
|
29
|
+
# convert to proc
|
30
|
+
if condition.is_a?(Symbol)
|
31
|
+
sym = condition
|
32
|
+
condition = ->(context) { method(sym).call(context) }
|
33
|
+
end
|
22
34
|
|
23
35
|
if condition
|
24
36
|
@features[key] = ::Flagship::Feature.new(key, condition, @context, @base_tags.merge(tags))
|
@@ -49,4 +61,8 @@ class Flagship::Dsl
|
|
49
61
|
def disabled?(key)
|
50
62
|
@flagset.disabled?(key)
|
51
63
|
end
|
64
|
+
|
65
|
+
def include(mod)
|
66
|
+
extend mod
|
67
|
+
end
|
52
68
|
end
|
data/lib/flagship/flagset.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
class Flagship::Flagset
|
2
|
-
attr_reader :key
|
2
|
+
attr_reader :key, :helper_methods
|
3
3
|
|
4
4
|
class UndefinedFlagError < ::StandardError; end
|
5
5
|
|
6
|
-
def initialize(key, features_hash, base = nil)
|
6
|
+
def initialize(key, features_hash, base = nil, helper_methods = [])
|
7
7
|
@key = key
|
8
8
|
@features = base ?
|
9
9
|
extend_features(features_hash, base) :
|
10
10
|
features_hash
|
11
|
+
@helper_methods = helper_methods
|
11
12
|
end
|
12
13
|
|
13
14
|
def enabled?(key)
|
data/lib/flagship/version.rb
CHANGED
data/lib/flagship.rb
CHANGED
@@ -9,46 +9,71 @@ require "flagship/flagsets_container"
|
|
9
9
|
module Flagship
|
10
10
|
class NoFlagsetSelectedError < ::StandardError; end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
class << self
|
13
|
+
def define(key, options = {}, &block)
|
14
|
+
context = self.default_context
|
15
|
+
base = options[:extend] ? self.get_flagset(options[:extend]) : nil
|
16
|
+
default_flagsets_container.add ::Flagship::Dsl.new(key, context, base, &block).flagset
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def enabled?(key)
|
20
|
+
current_flagset.enabled?(key)
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
def set_context(key_or_hash, value=nil)
|
24
|
+
if key_or_hash.is_a?(Hash)
|
25
|
+
key_or_hash.each { |k, v| default_context.__set(k, v) }
|
26
|
+
else
|
27
|
+
default_context.__set(key_or_hash, value)
|
28
|
+
end
|
29
|
+
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
def with_context(values, &block)
|
32
|
+
default_context.with_values values do
|
33
|
+
block.call
|
34
|
+
end
|
35
|
+
end
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
def select_flagset(key)
|
38
|
+
@current_flagset = default_flagsets_container.get(key)
|
39
|
+
end
|
33
40
|
|
34
|
-
|
35
|
-
|
36
|
-
|
41
|
+
def features
|
42
|
+
current_flagset.features
|
43
|
+
end
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
45
|
+
def get_flagset(key)
|
46
|
+
default_flagsets_container.get(key)
|
47
|
+
end
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
49
|
+
def default_flagsets_container
|
50
|
+
@default_flagsts_container ||= ::Flagship::FlagsetsContainer.new
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
def current_flagset
|
54
|
+
@current_flagset or raise NoFlagsetSelectedError.new('No flagset is selected')
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_context
|
58
|
+
@default_context ||= ::Flagship::Context.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear_state
|
62
|
+
clear_flagsets_container
|
63
|
+
clear_current_flagset
|
64
|
+
clear_context
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear_flagsets_container
|
68
|
+
@default_flagsts_container = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear_current_flagset
|
72
|
+
@current_flagset = nil
|
73
|
+
end
|
49
74
|
|
50
|
-
|
51
|
-
|
52
|
-
|
75
|
+
def clear_context
|
76
|
+
@default_context && @default_context.clear
|
77
|
+
end
|
53
78
|
end
|
54
79
|
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flagship
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuya Takeyama
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.13'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.13'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -82,7 +82,7 @@ homepage: https://github.com/yuya-takeyama/flagship
|
|
82
82
|
licenses:
|
83
83
|
- MIT
|
84
84
|
metadata: {}
|
85
|
-
post_install_message:
|
85
|
+
post_install_message:
|
86
86
|
rdoc_options: []
|
87
87
|
require_paths:
|
88
88
|
- lib
|
@@ -97,9 +97,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
requirements: []
|
100
|
-
|
101
|
-
|
102
|
-
signing_key:
|
100
|
+
rubygems_version: 3.1.2
|
101
|
+
signing_key:
|
103
102
|
specification_version: 4
|
104
103
|
summary: Ship/unship features using flags defined with declarative DSL
|
105
104
|
test_files: []
|