flagship 0.4.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b5e0f2c9053b747cfc441a1e3ba8f31a5453d00d
4
- data.tar.gz: ba52d5627d54d495773f47649bb8333a37793dbe
2
+ SHA256:
3
+ metadata.gz: 60210addd43057e1808189582ab298a84f14bb70468c1685e676ceb5481a7a8a
4
+ data.tar.gz: 4f85d9c988e71f2d219938310990757869269cf31e84706e093640f0e07b1326
5
5
  SHA512:
6
- metadata.gz: cb5353d961b9a00391af1e0e42028b9bc432772073743d0f4ebb291a91342a3f7aa99bd555b234fee50128bcd85ae9bd0daa44e8fb7ad86e3b502390f21c8c46
7
- data.tar.gz: 8db9f8a90a544de056952bccc6ab0b88dd2189029b40d62c1320a40a0eb99f499a54e4a488a95879bada243f8e7532ea774a9920d85938b33c56c4895b284a56
6
+ metadata.gz: 43dd867d1d2a6de1433833709fdd9c251ee095fd088a0d4f024fc802d966067f24c80a4d0615202892663014c03f22ba295dacc4c55610982ed2d5f4316a3c9f
7
+ data.tar.gz: 45152c852249cfa8692da6c234f1d6f246ab30c1111c7458436ea8a99ab792077a0af4608570954bf9a4ea4ab2aff859a09e5067453d0867552acf68b7fc15e2
data/.travis.yml CHANGED
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.2.5
5
- - 2.3.3
6
-
7
- before_install: gem install bundler --no-document
4
+ - 2.2.10
5
+ - 2.3.8
6
+ - 2.4.10
7
+ - 2.5.8
8
+ - 2.6.6
9
+ - 2.7.1
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", "~> 1.13"
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
@@ -1,15 +1,16 @@
1
+ require 'forwardable'
2
+
1
3
  class Flagship::Context
2
- def initialize
3
- @values = {}
4
- end
4
+ extend Forwardable
5
+ def_delegators :current_values, :clear
5
6
 
6
7
  def __set(key, value)
7
- @values[key.to_sym] = value
8
+ current_values[key.to_sym] = value
8
9
  end
9
10
 
10
11
  def method_missing(name, args = [], &block)
11
- if @values.key?(name)
12
- value = @values[name]
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
- @values.key?(name) or super
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
- @flagset = ::Flagship::Flagset.new(@key, @features, @base)
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
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Flagship
2
- VERSION = "0.4.0"
2
+ VERSION = "0.7.0"
3
3
  end
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
- def self.define(key, options = {}, &block)
13
- context = self.default_context
14
- base = options[:extend] ? self.get_flagset(options[:extend]) : nil
15
- self.default_flagsets_container.add ::Flagship::Dsl.new(key, context, base, &block).flagset
16
- end
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
- def self.enabled?(key)
19
- self.current_flagset.enabled?(key)
20
- end
19
+ def enabled?(key)
20
+ current_flagset.enabled?(key)
21
+ end
21
22
 
22
- def self.set_context(key, value)
23
- self.default_context.__set(key, value)
24
- end
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
- def self.select_flagset(key)
27
- @@current_flagset = self.default_flagsets_container.get(key)
28
- end
31
+ def with_context(values, &block)
32
+ default_context.with_values values do
33
+ block.call
34
+ end
35
+ end
29
36
 
30
- def self.features
31
- self.current_flagset.features
32
- end
37
+ def select_flagset(key)
38
+ @current_flagset = default_flagsets_container.get(key)
39
+ end
33
40
 
34
- def self.get_flagset(key)
35
- self.default_flagsets_container.get(key)
36
- end
41
+ def features
42
+ current_flagset.features
43
+ end
37
44
 
38
- def self.default_flagsets_container
39
- @@default_flagsts_container ||= ::Flagship::FlagsetsContainer.new
40
- end
45
+ def get_flagset(key)
46
+ default_flagsets_container.get(key)
47
+ end
41
48
 
42
- def self.current_flagset
43
- @@current_flagset or raise NoFlagsetSelectedError.new('No flagset is selected')
44
- end
49
+ def default_flagsets_container
50
+ @default_flagsts_container ||= ::Flagship::FlagsetsContainer.new
51
+ end
45
52
 
46
- def self.default_context
47
- @@default_context ||= ::Flagship::Context.new
48
- end
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
- def self.clear_state
51
- @@default_flagsts_container = nil
52
- @@current_flagset = nil
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.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: 2016-12-22 00:00:00.000000000 Z
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
- rubyforge_project:
101
- rubygems_version: 2.5.2
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: []