bastet 0.0.2 → 0.1.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.
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