flag 0.0.2 → 1.0.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
2
  SHA1:
3
- metadata.gz: 6d92ca5beee75e55334375129bf62b0836adf5f7
4
- data.tar.gz: 868a270b5142903ef440f22fd6385a380fcc4bf3
3
+ metadata.gz: aaf08d31ce04248717ab95bcb04f05e040c9dad9
4
+ data.tar.gz: 7bcf051376520b23c40e8d2b80d2539cdad47567
5
5
  SHA512:
6
- metadata.gz: c8f3b6480ce579aa163eea68559385b6790bd02072fc08089a8bc26d6aca6e18b6e45a53920c7a6ad3cd45353b5a6cd5b353d1a0cb8a0508d730faa8b0a49020
7
- data.tar.gz: 7b064e5d80901a1b77d80b50f74cea3c91a681b089d570f596d9d958bc7016be866d6e7bba33d22587b1616d1746546d33aa2cd5adaad3208cc679ea9f993a41
6
+ metadata.gz: 1a67ef9024c6f14362ff7ac9b71b4931ccc7408c32aa92f81d70aa48ae6cd3fff5811a610842fe7566da770ac3e1e5d55df892159ca71e4c92601c380226ffea
7
+ data.tar.gz: 7f549faf4fcb3309e8529157ae9a073f6dfa09810ff1af36774bd3f4c5abc0e724af6658042d1c07b53499e32e21150a6f46e078a8ab1565a828a7757e7b6d54
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4.0
4
+ services:
5
+ - "redis-server"
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake"
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ flag (1.0.0)
5
+ redic (~> 1.5.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ clap (1.0.0)
11
+ cutest (1.2.3)
12
+ clap
13
+ hiredis (0.6.1)
14
+ rake (12.0.0)
15
+ redic (1.5.0)
16
+ hiredis
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ cutest (~> 1.2.3)
23
+ flag!
24
+ rake
25
+
26
+ BUNDLED WITH
27
+ 1.14.4
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Flag
2
+ [![Build Status](https://travis-ci.org/elcuervo/flag.svg?branch=master)](https://travis-ci.org/elcuervo/flag)
2
3
 
3
4
  Simple feature flags for any app
4
5
 
@@ -37,6 +38,18 @@ Flag(:something).on!("uuid")
37
38
  Flag(:something).on!(:group)
38
39
  ```
39
40
 
41
+ ## Quiet mode
42
+
43
+ Sometimes you don't want to have your server down when doing flag checks:
44
+
45
+ ```ruby
46
+ Flag.quiet!
47
+ # Now everything fails silently
48
+
49
+ Flag.store = Redic.new("redis://localhost:5433/123")
50
+ Flag(:quack).on!
51
+ ```
52
+
40
53
  ## Enable/Check feature flags
41
54
 
42
55
  ### Ids
data/Rakefile CHANGED
@@ -1,11 +1,7 @@
1
1
  require "rake/testtask"
2
2
 
3
- Rake::TestTask.new("spec") do |t|
4
- t.libs << "lib"
5
- t.libs << "test"
6
- t.pattern = "test/**/*_test.rb"
3
+ task :test do
4
+ exec "cutest test/*.rb"
7
5
  end
8
6
 
9
- task :default => [:test]
10
- task :all => [:test, :bench]
11
- task :test => [:spec]
7
+ task :default => :test
@@ -1,17 +1,15 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "flag"
3
- s.version = "0.0.2"
3
+ s.version = "1.0.0"
4
4
  s.summary = "Simple feature flags"
5
5
  s.description = "Feature flags for the humans and the coders"
6
6
  s.authors = ["elcuervo"]
7
7
  s.licenses = ["MIT", "HUGWARE"]
8
- s.email = ["yo@brunoaguirre.com"]
8
+ s.email = ["elcuervo@elcuervo.net"]
9
9
  s.homepage = "http://github.com/elcuervo/flag"
10
10
  s.files = `git ls-files`.split("\n")
11
11
  s.test_files = `git ls-files test`.split("\n")
12
12
 
13
- s.add_dependency("redic", "~> 1.4.1")
14
-
15
- s.add_development_dependency("minitest", "~> 5.3.0")
16
- s.add_development_dependency("minitest-given", "~> 3.0.0")
13
+ s.add_dependency("redic", "~> 1.5.0")
14
+ s.add_development_dependency("cutest", "~> 1.2.3")
17
15
  end
@@ -4,15 +4,19 @@ require "redic"
4
4
  module Flag
5
5
  FEATURES = "_flag:features".freeze
6
6
 
7
+ RedisGoneError = StandardError
8
+
7
9
  Members = Struct.new(:name) do
8
10
  USERS = "users".freeze
9
11
  GROUPS = "groups".freeze
10
12
 
11
13
  def <<(item)
12
- if item.to_s.end_with?("%")
13
- Flag.store.call("HSET", Flag::FEATURES, name, item[0...-1])
14
- else
15
- Flag.store.call("SADD", subkey(item), item)
14
+ Flag.execute do |store|
15
+ if item.to_s.end_with?("%")
16
+ store.call("HSET", Flag::FEATURES, name, item[0...-1])
17
+ else
18
+ store.call("SADD", subkey(item), item)
19
+ end
16
20
  end
17
21
  end
18
22
 
@@ -24,28 +28,39 @@ module Flag
24
28
  { percentage: percentage, users: users, groups: groups }
25
29
  end
26
30
 
27
- def groups; members_for(GROUPS).map(&:to_sym) end
28
- def users; members_for(USERS) end
31
+ def groups
32
+ members_for(GROUPS).map(&:to_sym)
33
+ end
34
+
35
+ def users
36
+ members_for(USERS)
37
+ end
29
38
 
30
39
  def include?(item)
31
40
  return percentage == item.to_i if item.to_s.end_with?("%")
32
41
  return true if Zlib.crc32(item.to_s) % 100 < percentage
33
42
 
34
- Flag.store.call("SISMEMBER", subkey(item), item).to_i == 1
43
+ Flag.execute do |store|
44
+ store.call("SISMEMBER", subkey(item), item).to_i == 1
45
+ end
35
46
  end
36
47
 
37
48
  def reset
38
- [USERS, GROUPS].each { |k| Flag.store.call("DEL", "#{key}:#{k}") }
49
+ [USERS, GROUPS].each do |k|
50
+ Flag.execute { |store| store.call("DEL", "#{key}:#{k}") }
51
+ end
39
52
  end
40
53
 
41
54
  def percentage
42
- Flag.store.call("HGET", Flag::FEATURES, name).to_i
55
+ Flag.execute { |store| store.call("HGET", Flag::FEATURES, name).to_i }
43
56
  end
44
57
 
45
58
  private
46
59
 
47
60
  def members_for(type)
48
- Flag.store.call("SMEMBERS", "#{key}:#{type}") || []
61
+ Flag.execute do |store|
62
+ store.call("SMEMBERS", "#{key}:#{type}") || []
63
+ end
49
64
  end
50
65
 
51
66
  def subkey(item)
@@ -55,7 +70,7 @@ module Flag
55
70
  def subgroup(item)
56
71
  case item
57
72
  when Integer, Fixnum, String then USERS
58
- when Symbol then GROUPS
73
+ when Symbol then GROUPS
59
74
  end
60
75
  end
61
76
  end
@@ -69,11 +84,21 @@ module Flag
69
84
  @members = Members.new(name)
70
85
  end
71
86
 
72
- def reset; @members.reset end
73
- def key; @members.key end
74
- def activated; @members.activated end
87
+ def reset
88
+ @members.reset
89
+ end
90
+
91
+ def key
92
+ @members.key
93
+ end
94
+
95
+ def activated
96
+ @members.activated
97
+ end
75
98
 
76
- def off?; !active? end
99
+ def off?
100
+ !active?
101
+ end
77
102
 
78
103
  def on?(what = false)
79
104
  return active? if !what
@@ -103,31 +128,58 @@ module Flag
103
128
  end
104
129
 
105
130
  class << self
106
- attr_accessor :store
131
+ attr_accessor :store, :quiet
107
132
 
108
133
  def flush
109
134
  @_group = nil
110
135
  features.each { |_, f| f.reset }
111
- self.store.call("DEL", FEATURES)
136
+ self.execute { |store| store.call("DEL", FEATURES) }
112
137
  end
113
138
 
114
139
  def enabled
115
140
  features.select { |k, v| v.on? }.keys
116
141
  end
117
142
 
118
- def store; @store ||= Redic.new end
119
- def group; @_group ||= Hash.new { |h, k| h[k] = lambda { |id| } } end
143
+ def quiet!
144
+ @quiet = true
145
+ end
146
+
147
+ def quiet?
148
+ @quiet == true
149
+ end
150
+
151
+ def execute
152
+ yield(store)
153
+ rescue Errno::ECONNREFUSED, Errno::EINVAL => e
154
+ raise RedisGoneError unless quiet?
155
+ end
120
156
 
121
- def groups; group.keys end
157
+ def store
158
+ @store ||= Redic.new
159
+ end
160
+
161
+ def group
162
+ @_group ||= Hash.new do |h, k|
163
+ h[k] = lambda { |id| }
164
+ end
165
+ end
166
+
167
+ def groups
168
+ group.keys
169
+ end
122
170
 
123
171
  def features
124
172
  @_features ||= Hash.new { |h, k| h[k] = Feature.new(k) }
125
173
 
126
- self.store.call("HKEYS", FEATURES).each { |k| @_features[k.to_sym] }
174
+ self.execute do |store|
175
+ store.call("HKEYS", FEATURES).each { |k| @_features[k.to_sym] }
176
+ end
127
177
 
128
178
  @_features
129
179
  end
130
180
  end
131
181
  end
132
182
 
133
- def Flag(feature); Flag.features[feature] end
183
+ def Flag(feature)
184
+ Flag.features[feature]
185
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "./spec_helper"
2
+
3
+ scope "fallback" do
4
+ setup do
5
+ Flag.store = Redic.new("redis://localhost:6380/23")
6
+ end
7
+
8
+ scope "normal mode" do
9
+ test "fail when Redis is gone" do
10
+ Flag.quiet = false
11
+
12
+ assert Flag.quiet? == false
13
+ assert_raise(Flag::RedisGoneError) do
14
+ Flag(:fallback).on!
15
+ end
16
+ end
17
+ end
18
+
19
+ scope "quiet mode" do
20
+ setup do
21
+ Flag.quiet!
22
+ end
23
+
24
+ test "do not fail when Redis is gone" do
25
+ assert Flag.quiet? == true
26
+
27
+ Flag(:fallback).on!
28
+
29
+ assert Flag(:fallback).on? == false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "./spec_helper"
2
+
3
+ prepare do
4
+ Flag.flush
5
+ end
6
+
7
+ scope "features" do
8
+ test "empty" do
9
+ assert_equal [], Flag.enabled
10
+ end
11
+
12
+ test "having one" do
13
+ Flag(:test).on!
14
+ assert_equal [:test], Flag.enabled
15
+ end
16
+
17
+ test "turning them off" do
18
+ Flag(:test).on!
19
+ Flag(:test).off!
20
+ Flag(:test2).on!
21
+
22
+ assert_equal [:test2], Flag.enabled
23
+ assert_equal [:test, :test2], Flag.features.keys
24
+ end
25
+
26
+ test "get feature info" do
27
+ Flag(:test).on!("50%")
28
+ Flag(:test).on!("25")
29
+ Flag(:test).on!("UUID")
30
+ Flag(:test).on!(:staff)
31
+
32
+ assert Flag(:test).activated.is_a?(Hash)
33
+ assert Flag(:test).activated[:percentage] == 50
34
+ assert Flag(:test).activated[:users].size == 2
35
+ assert Flag(:test).activated[:users].include?("25")
36
+ assert Flag(:test).activated[:users].include?("UUID")
37
+ assert Flag(:test).activated[:groups] == [:staff]
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require_relative "./spec_helper"
2
+
3
+ prepare do
4
+ Flag.flush
5
+ end
6
+
7
+ scope "groups" do
8
+ setup do
9
+ Flag.group[:staff] = lambda { |id| id > 1 }
10
+ end
11
+
12
+ test "adding groups" do
13
+ assert_equal [:staff], Flag.groups
14
+ end
15
+
16
+ test "testing if feature is activated for a group" do
17
+ Flag(:test).on!(:staff)
18
+
19
+ assert Flag(:test).on?(:staff)
20
+ end
21
+
22
+ test "trying to check for an empty group" do
23
+ Flag(:test).on!(:bogus)
24
+
25
+ assert_equal false, Flag(:test).on?(1)
26
+ end
27
+
28
+ test "testing if a user beloging to a group get stuff activated" do
29
+ Flag(:test).on!(:staff)
30
+
31
+ assert Flag(:test).on?(1) == false
32
+ assert Flag(:test).on?(2) == true
33
+ assert Flag(:test).on?(3) == true
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "./spec_helper"
2
+
3
+ prepare do
4
+ Flag.flush
5
+ end
6
+
7
+ scope "keys" do
8
+ test "uses the correct namespace" do
9
+ assert_equal "_flag:features:test_the_key", Flag(:test_the_key).key
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require_relative "./spec_helper"
2
+
3
+ prepare do
4
+ Flag.flush
5
+ end
6
+
7
+ scope "percentage" do
8
+ test "only half" do
9
+ Flag(:test).on!("50%")
10
+
11
+ assert Flag(:test).on?(1) == false
12
+ assert Flag(:test).on?(2) == true
13
+ assert Flag(:test).on?("49%") == false
14
+ assert Flag(:test).on?("50%") == true
15
+ end
16
+ end
@@ -1,7 +1,4 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../lib'
2
2
 
3
- require "minitest/autorun"
4
- require "minitest/spec"
5
- require "minitest/pride"
6
- require "minitest/given"
3
+ require "cutest"
7
4
  require "flag"
@@ -0,0 +1,20 @@
1
+ require_relative "./spec_helper"
2
+
3
+ prepare do
4
+ Flag.flush
5
+ end
6
+
7
+ scope "users" do
8
+ test "enabled only for a user" do
9
+ Flag(:test).on!(1)
10
+
11
+ assert Flag(:test).on?(1) == true
12
+ assert Flag(:test).on?(2) == false
13
+ end
14
+
15
+ test "using any kind of id" do
16
+ Flag(:test).on!("ImARandomUUID")
17
+
18
+ assert Flag(:test).on?("ImARandomUUID")
19
+ end
20
+ end
metadata CHANGED
@@ -1,71 +1,65 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - elcuervo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-11 00:00:00.000000000 Z
11
+ date: 2017-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redic
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.4.1
19
+ version: 1.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.4.1
26
+ version: 1.5.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: minitest
28
+ name: cutest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 5.3.0
33
+ version: 1.2.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 5.3.0
41
- - !ruby/object:Gem::Dependency
42
- name: minitest-given
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ~>
46
- - !ruby/object:Gem::Version
47
- version: 3.0.0
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ~>
53
- - !ruby/object:Gem::Version
54
- version: 3.0.0
40
+ version: 1.2.3
55
41
  description: Feature flags for the humans and the coders
56
42
  email:
57
- - yo@brunoaguirre.com
43
+ - elcuervo@elcuervo.net
58
44
  executables: []
59
45
  extensions: []
60
46
  extra_rdoc_files: []
61
47
  files:
48
+ - ".travis.yml"
49
+ - Gemfile
50
+ - Gemfile.lock
62
51
  - README.md
63
52
  - Rakefile
64
53
  - SPEC.md
65
54
  - flag.gemspec
66
55
  - lib/flag.rb
67
- - test/flag_test.rb
56
+ - test/fallback_test.rb
57
+ - test/features_test.rb
58
+ - test/groups_test.rb
59
+ - test/keys_test.rb
60
+ - test/percentage_test.rb
68
61
  - test/spec_helper.rb
62
+ - test/users_test.rb
69
63
  homepage: http://github.com/elcuervo/flag
70
64
  licenses:
71
65
  - MIT
@@ -77,20 +71,25 @@ require_paths:
77
71
  - lib
78
72
  required_ruby_version: !ruby/object:Gem::Requirement
79
73
  requirements:
80
- - - '>='
74
+ - - ">="
81
75
  - !ruby/object:Gem::Version
82
76
  version: '0'
83
77
  required_rubygems_version: !ruby/object:Gem::Requirement
84
78
  requirements:
85
- - - '>='
79
+ - - ">="
86
80
  - !ruby/object:Gem::Version
87
81
  version: '0'
88
82
  requirements: []
89
83
  rubyforge_project:
90
- rubygems_version: 2.0.14
84
+ rubygems_version: 2.5.1
91
85
  signing_key:
92
86
  specification_version: 4
93
87
  summary: Simple feature flags
94
88
  test_files:
95
- - test/flag_test.rb
89
+ - test/fallback_test.rb
90
+ - test/features_test.rb
91
+ - test/groups_test.rb
92
+ - test/keys_test.rb
93
+ - test/percentage_test.rb
96
94
  - test/spec_helper.rb
95
+ - test/users_test.rb
@@ -1,101 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Flag do
4
- after { Flag.flush }
5
-
6
- context "features" do
7
-
8
- context "empty" do
9
- Given(:features) { Flag.enabled }
10
- Then { features == [] }
11
- end
12
-
13
- context "having one" do
14
- Given { Flag(:test).on! }
15
- Then { Flag.enabled == [:test] }
16
- end
17
-
18
- context "turning them off" do
19
- Given { Flag(:test).on! }
20
- When { Flag(:test).off! }
21
- When { Flag(:test2).on! }
22
-
23
- Then { Flag.enabled == [:test2] }
24
- And { Flag.features.keys == [:test, :test2] }
25
- end
26
-
27
- end
28
-
29
- context "groups" do
30
- Given do
31
- Flag.group[:staff] = lambda { |id| id > 1 }
32
- end
33
-
34
- context "adding groups" do
35
- When(:groups) { Flag.groups }
36
- Then { groups == [:staff] }
37
- end
38
-
39
- context "testing if feature is activated for a group" do
40
- Given { Flag(:test).on!(:staff) }
41
- When(:feature) { Flag(:test).on?(:staff) }
42
- Then { feature == true }
43
- end
44
-
45
- context "trying to check for an empty group" do
46
- Given { Flag(:test).on!(:bogus) }
47
- When(:feature) { Flag(:test).on?(1) }
48
- Then { feature == false }
49
- end
50
-
51
- context "testing if a user beloging to a group get stuff activated" do
52
- Given { Flag(:test).on!(:staff) }
53
-
54
- Then { Flag(:test).on?(1) == false }
55
- And { Flag(:test).on?(2) == true }
56
- And { Flag(:test).on?(3) == true }
57
- end
58
- end
59
-
60
- context "users" do
61
- context "enabled only for a user" do
62
- When { Flag(:test).on!(1) }
63
-
64
- Then { Flag(:test).on?(1) == true }
65
- And { Flag(:test).on?(2) == false }
66
- end
67
-
68
- context "with non numeric users" do
69
- When { Flag(:test).on!("ImARandomUUID") }
70
- Then { Flag(:test).on?("ImARandomUUID") == true }
71
- end
72
- end
73
-
74
- context "keys" do
75
- Given(:feature) { Flag(:test_the_key) }
76
- Then { feature.key == "_flag:features:test_the_key" }
77
- end
78
-
79
- context "percentage" do
80
- Given { Flag(:test).on!("50%") }
81
-
82
- Then { Flag(:test).on?(1) == false }
83
- And { Flag(:test).on?(2) == true }
84
- And { Flag(:test).on?("49%") == false }
85
- And { Flag(:test).on?("50%") == true }
86
- end
87
-
88
- context "get feature info" do
89
- Given { Flag(:test).on!("50%") }
90
- Given { Flag(:test).on!("25") }
91
- Given { Flag(:test).on!("UUID") }
92
- Given { Flag(:test).on!(:staff) }
93
-
94
- Then { Flag(:test).activated.is_a?(Hash) }
95
- And { Flag(:test).activated[:percentage] == 50 }
96
- And { Flag(:test).activated[:users].size == 2 }
97
- And { Flag(:test).activated[:users].include?("25") }
98
- And { Flag(:test).activated[:users].include?("UUID") }
99
- And { Flag(:test).activated[:groups] == [:staff] }
100
- end
101
- end