bastet 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,31 +1,30 @@
1
- # Bastet - The unstable beast
2
-
3
- Goddess associated with war, protection of Lower Egypt and the pharaoh, the sun, perfumes, ointments and embalming
1
+ # Bastet - The group based feature rollout beast
4
2
 
5
3
  ## Gemfile
6
4
 
7
- gem "bastet", "~> 0.0.1"
5
+ gem "bastet"
8
6
 
9
7
  ## Configuration
10
8
 
11
9
  redis = Redis.new
12
- $bastet = Bastet.setup(redis)
10
+ bastet = Bastet.setup(redis) #=> Bastet::Base.instance ...
13
11
 
14
12
  ## Usage
15
13
 
16
- $bastet.activate(:crazy_feature, current_user)
17
- $bastet.active?(:crazy_feature, current_user) #=> true
14
+ group = Bastet::Group.new('admin_users') { |user| user.admin? }
15
+ bastet.activate("admin_only_feature", group)
16
+
17
+ user = User.new(admin: true)
18
+ bastet.active?("admin_only_feature", user) #=> true
18
19
 
19
- $bastet.active?(:scary_feature, :admins) #=> false
20
- $bastet.activate(:scary_feature, :admins)
21
- $bastet.active?(:scary_feature, :admins) #=> true
20
+ bastet.deactivate("admin_only_feature", group)
21
+ bastet.inactive?("admin_only_feature", user) #=> true
22
22
 
23
23
  ## To do
24
24
 
25
- 1. Support activating/deactivating all features for groups/users
26
- 2. Support activating/deactivating for multiple users/groups at once
27
- 3. Percentage support
28
- 4. Logging
25
+ 1. Support activating/deactivating for multiple users/groups at once
26
+ 2. Percentage support
27
+ 3. Logging
29
28
 
30
29
  ## Contibuting
31
30
 
data/lib/bastet/base.rb CHANGED
@@ -1,49 +1,23 @@
1
- class Bastet::Base
2
- attr_accessor :redis
1
+ require 'singleton'
3
2
 
4
- def initialize redis
5
- @redis = redis
6
- end
3
+ class Bastet::Base
4
+ include Singleton
7
5
 
8
- def activate feature, target
9
- act :sadd, feature, target
6
+ def activate feature, group
7
+ Bastet.redis.sadd("feature_#{feature}", group.name)
10
8
  end
11
9
 
12
- def deactivate feature, target
13
- act :srem, feature, target
10
+ def deactivate feature, group
11
+ Bastet.redis.srem("feature_#{feature}", group.name)
14
12
  end
15
13
 
16
- def active? feature, target
17
- act :sismember, feature, target
14
+ def active? feature, entity
15
+ group_names = Bastet.redis.smembers("feature_#{feature}")
16
+ groups = Bastet.groups.select { |group| group_names.include?(group.name) }
17
+ groups.any? { |group| group.criteria.call(entity) }
18
18
  end
19
19
 
20
- def inactive? feature, target
21
- !active? feature, target
20
+ def inactive? feature, entity
21
+ !active? feature, entity
22
22
  end
23
-
24
- private
25
-
26
- def act method, feature, target
27
- @redis.send(method, key(feature, target), val(target))
28
- end
29
-
30
- def key feature, target
31
- [].tap do |key|
32
- key << "feature"
33
- key << feature.to_s
34
- key << namespace(target)
35
- end.join(":")
36
- end
37
-
38
- def val target
39
- instance?(target) ? target.id : target.to_s
40
- end
41
-
42
- def namespace target
43
- instance?(target) ? "users" : "groups"
44
- end
45
-
46
- def instance? target
47
- target.respond_to?(:id)
48
- end
49
23
  end
@@ -0,0 +1,31 @@
1
+ class Bastet::Group
2
+ attr_accessor :name, :criteria
3
+
4
+ def initialize name, &block
5
+ raise ArgumentError.new("Need to pass a criteria") unless block_given?
6
+ validate_name!(name.to_s)
7
+
8
+ @name = name.to_s
9
+ @criteria = block
10
+
11
+ persist!
12
+ end
13
+
14
+ def contains? entity
15
+ criteria.call(entity)
16
+ end
17
+
18
+ private
19
+
20
+ def validate_name! name
21
+ if Bastet.redis.sismember('bastet_groups', name)
22
+ raise ArgumentError.new("#{name} is already initialized as a group.")
23
+ else
24
+ Bastet.redis.sadd('bastet_groups', name)
25
+ end
26
+ end
27
+
28
+ def persist!
29
+ Bastet.groups << self
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module Bastet
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/bastet.rb CHANGED
@@ -1,9 +1,24 @@
1
- require "bastet/version"
1
+ require 'bastet/version'
2
+ require 'bastet/base'
3
+ require 'bastet/group'
2
4
 
3
5
  module Bastet
4
- autoload "Base", "bastet/base"
6
+ class << self
7
+ def redis
8
+ @@redis
9
+ end
5
10
 
6
- def self.setup redis
7
- Bastet::Base.new(redis)
11
+ def redis=(redis)
12
+ @@redis = redis
13
+ end
14
+
15
+ def groups
16
+ @@groups ||= []
17
+ end
18
+
19
+ def setup(redis)
20
+ self.redis = redis
21
+ Bastet::Base.instance
22
+ end
8
23
  end
9
24
  end
@@ -2,49 +2,29 @@ require 'spec_helper'
2
2
 
3
3
  describe Bastet::Base do
4
4
  before do
5
- @redis = Redis.new
6
- @bastet = Bastet::Base.new(@redis)
7
- end
8
-
9
- describe "initialization" do
10
- it "should configure the Redis instance" do
11
- Bastet::Base.new(@redis).redis.should == @redis
12
- end
5
+ @bastet = Bastet.setup(@redis)
13
6
  end
14
7
 
15
8
  describe "activate" do
16
- it "should activate the :banana for the user" do
17
- user = mock('user')
18
- user.expects(:id).at_least(2).returns(100)
9
+ it "should activate the :banana for the group" do
10
+ group = Bastet::Group.new("admins") { |entity| entity.admin? }
11
+ user = mock('user', admin?: true)
19
12
 
20
- @bastet.activate(:banana, user)
13
+ @bastet.activate(:banana, group)
21
14
  @bastet.active?(:banana, user).should be_true
22
15
  end
23
-
24
- it "should activate the :banana for the group" do
25
- @bastet.activate(:banana, :admins)
26
- @bastet.active?(:banana, :admins).should be_true
27
- end
28
16
  end
29
17
 
30
18
  describe "deactivate" do
31
- it "should deactivate the :banana for the user" do
32
- user = mock('user')
33
- user.expects(:id).at_least(2).returns(100)
19
+ it "should deactive :banana for the group" do
20
+ group = Bastet::Group.new("admins") { |entity| entity.admin? }
21
+ user = mock('user', admin?: true)
34
22
 
35
- @bastet.activate(:banana, user)
23
+ @bastet.activate(:banana, group)
36
24
  @bastet.active?(:banana, user).should be_true
37
25
 
38
- @bastet.deactivate(:banana, user)
26
+ @bastet.deactivate(:banana, group)
39
27
  @bastet.inactive?(:banana, user).should be_true
40
28
  end
41
-
42
- it "should deactivate the :banana for the group" do
43
- @bastet.activate(:banana, :admins)
44
- @bastet.active?(:banana, :admins).should be_true
45
-
46
- @bastet.deactivate(:banana, :admins)
47
- @bastet.inactive?(:banana, :admins).should be_true
48
- end
49
29
  end
50
30
  end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bastet::Group do
4
+ before do
5
+ @bastet = Bastet.setup(@redis)
6
+ end
7
+
8
+ describe "initializer" do
9
+ it "should take a group name" do
10
+ Bastet::Group.new('cool_feature') { }.name.should == "cool_feature"
11
+ end
12
+
13
+ it "should always return a string" do
14
+ Bastet::Group.new('cooler_feature') { }.name.should be_a(String)
15
+ end
16
+
17
+ it "should take a required criteria block" do
18
+ Bastet::Group.new('bananas') { }.criteria.should be_a(Proc)
19
+ end
20
+
21
+ it "should raise an argument error if there is no block" do
22
+ lambda {
23
+ Bastet::Group.new('cooler_feature')
24
+ }.should raise_exception(ArgumentError)
25
+ end
26
+
27
+ it "should make sure the name is unique" do
28
+ Bastet::Group.new('cooler_feature') { }
29
+ ['cooler_feature', :cooler_feature].each do |name|
30
+ lambda {
31
+ Bastet::Group.new(name) { }
32
+ }.should raise_exception(ArgumentError)
33
+ end
34
+ end
35
+
36
+ it "should add the group to the groups" do
37
+ lambda {
38
+ Bastet::Group.new('banana') { }
39
+ }.should change(Bastet.groups, :count).by(1)
40
+ end
41
+ end
42
+
43
+ describe "contains?" do
44
+ it "should be true if the criteria matches" do
45
+ group = Bastet::Group.new('admins_only') { |entity| entity.admin? }
46
+ group.contains?(mock(admin?: true)).should be_true
47
+ end
48
+
49
+ it "should be false if the criteria doesn't match" do
50
+ group = Bastet::Group.new('people_named_bill') { |entity| entity.name == "Bill" }
51
+ group.contains?(mock(name: 'Joe')).should be_false
52
+ end
53
+
54
+ it "should be true for all" do
55
+ group = Bastet::Group.new('all') { |entity| true }
56
+ group.contains?(mock).should be_true
57
+ end
58
+
59
+ it "should be false for all" do
60
+ group = Bastet::Group.new('none') { |entity| false }
61
+ group.contains?(mock).should be_false
62
+ end
63
+ end
64
+ end
data/spec/bastet_spec.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Bastet do
4
- before do
5
- @redis = Redis.new
6
- end
7
-
8
4
  describe "::setup" do
9
5
  it "should return a new Bastet::Base object" do
10
- Bastet.setup(@redis).should be_a(Bastet::Base)
6
+ Bastet.setup(@redis).should == Bastet::Base.instance
7
+ end
8
+ end
9
+
10
+ describe "::groups" do
11
+ it "should have no groups" do
12
+ Bastet.groups.should be_empty
11
13
  end
12
14
  end
13
15
  end
data/spec/spec_helper.rb CHANGED
@@ -9,4 +9,12 @@ Bundler.setup
9
9
 
10
10
  RSpec.configure do |config|
11
11
  config.mock_with :mocha
12
+
13
+ config.before(:each) do
14
+ @redis = Redis.new
15
+ end
16
+
17
+ config.after(:each) do
18
+ @redis.flushdb
19
+ end
12
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bastet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-24 00:00:00.000000000 Z
12
+ date: 2011-12-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70170044063740 !ruby/object:Gem::Requirement
16
+ requirement: &70334749774580 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.9.2.2
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70170044063740
24
+ version_requirements: *70334749774580
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70170044062960 !ruby/object:Gem::Requirement
27
+ requirement: &70334749774080 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 2.7.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70170044062960
35
+ version_requirements: *70334749774080
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mocha
38
- requirement: &70170044061900 !ruby/object:Gem::Requirement
38
+ requirement: &70334749773620 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.10.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70170044061900
46
+ version_requirements: *70334749773620
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: redis
49
- requirement: &70170044061420 !ruby/object:Gem::Requirement
49
+ requirement: &70334749773160 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.2.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70170044061420
57
+ version_requirements: *70334749773160
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: simplecov
60
- requirement: &70170044060960 !ruby/object:Gem::Requirement
60
+ requirement: &70334749772660 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70170044060960
68
+ version_requirements: *70334749772660
69
69
  description: Simple feature rollout for users, and groups
70
70
  email:
71
71
  - itsmeduncan@gmail.com
@@ -84,8 +84,10 @@ files:
84
84
  - bastet.gemspec
85
85
  - lib/bastet.rb
86
86
  - lib/bastet/base.rb
87
+ - lib/bastet/group.rb
87
88
  - lib/bastet/version.rb
88
89
  - spec/bastet/base_spec.rb
90
+ - spec/bastet/group_spec.rb
89
91
  - spec/bastet_spec.rb
90
92
  - spec/spec_helper.rb
91
93
  homepage: https://github.com/itsmeduncan/bastet
@@ -114,5 +116,6 @@ specification_version: 3
114
116
  summary: Simple feature rollout for users, and groups
115
117
  test_files:
116
118
  - spec/bastet/base_spec.rb
119
+ - spec/bastet/group_spec.rb
117
120
  - spec/bastet_spec.rb
118
121
  - spec/spec_helper.rb