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 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: []