sapling 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/lib/sapling.rb +1 -0
- data/lib/sapling/active_record.rb +32 -10
- data/lib/sapling/generators/css_generator.rb +31 -0
- data/lib/sapling/memory.rb +14 -7
- data/lib/sapling/rails.rb +17 -0
- data/lib/sapling/rails/action_controller.rb +23 -0
- data/lib/sapling/rails/controllers/sapling_controller.rb +7 -0
- data/lib/sapling/rails/railtie.rb +7 -0
- data/lib/sapling/rails/view_helpers.rb +22 -0
- data/lib/sapling/util.rb +9 -11
- data/lib/sapling/version.rb +1 -1
- data/rails/init.rb +2 -0
- data/sapling.gemspec +4 -3
- data/spec/active_record_spec.rb +0 -26
- data/spec/css_generator_spec.rb +47 -0
- data/spec/memory_feature_spec.rb +2 -2
- data/spec/memory_spec.rb +0 -26
- data/spec/rails_app/README +243 -0
- data/spec/rails_app/Rakefile +10 -0
- data/spec/rails_app/app/controllers/application_controller.rb +20 -0
- data/spec/rails_app/app/controllers/spaceman_spiffs_controller.rb +90 -0
- data/spec/rails_app/app/controllers/user_sessions_controller.rb +14 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/helpers/spaceman_spiffs_helper.rb +2 -0
- data/spec/rails_app/app/models/spaceman_spiff.rb +2 -0
- data/spec/rails_app/app/models/user.rb +3 -0
- data/spec/rails_app/app/views/layouts/spaceman_spiffs.html.erb +18 -0
- data/spec/rails_app/app/views/spaceman_spiffs/edit.html.erb +16 -0
- data/spec/rails_app/app/views/spaceman_spiffs/index.html.erb +21 -0
- data/spec/rails_app/app/views/spaceman_spiffs/multiple_features.html.erb +7 -0
- data/spec/rails_app/app/views/spaceman_spiffs/new.html.erb +15 -0
- data/spec/rails_app/app/views/spaceman_spiffs/show.html.erb +8 -0
- data/spec/rails_app/config/boot.rb +114 -0
- data/spec/rails_app/config/database.yml +17 -0
- data/spec/rails_app/config/environment.rb +48 -0
- data/spec/rails_app/config/environments/development.rb +17 -0
- data/spec/rails_app/config/environments/production.rb +28 -0
- data/spec/rails_app/config/environments/test.rb +28 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/cookie_verification_secret.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/rails_app/config/initializers/session_store.rb +15 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +48 -0
- data/spec/rails_app/db/migrate/20111207234232_create_spaceman_spiffs.rb +13 -0
- data/spec/rails_app/db/migrate/20111208003900_create_users.rb +13 -0
- data/spec/rails_app/db/migrate/20111208004048_add_sapling_settings.rb +13 -0
- data/spec/rails_app/db/schema.rb +32 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/doc/README_FOR_APP +2 -0
- data/spec/rails_app/public/404.html +30 -0
- data/spec/rails_app/public/422.html +30 -0
- data/spec/rails_app/public/500.html +30 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +973 -0
- data/spec/rails_app/public/javascripts/effects.js +1128 -0
- data/spec/rails_app/public/javascripts/prototype.js +4320 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/public/stylesheets/scaffold.css +54 -0
- data/spec/rails_app/script/about +4 -0
- data/spec/rails_app/script/console +3 -0
- data/spec/rails_app/script/dbconsole +3 -0
- data/spec/rails_app/script/destroy +3 -0
- data/spec/rails_app/script/generate +3 -0
- data/spec/rails_app/script/performance/benchmarker +3 -0
- data/spec/rails_app/script/performance/profiler +3 -0
- data/spec/rails_app/script/plugin +3 -0
- data/spec/rails_app/script/runner +3 -0
- data/spec/rails_app/script/server +3 -0
- data/spec/rails_app/test/fixtures/sapling_settings.yml +9 -0
- data/spec/rails_app/test/fixtures/spaceman_spiffs.yml +7 -0
- data/spec/rails_app/test/fixtures/users.yml +7 -0
- data/spec/rails_app/test/integration/feature_test.rb +30 -0
- data/spec/rails_app/test/performance/browsing_test.rb +9 -0
- data/spec/rails_app/test/test_helper.rb +38 -0
- data/spec/rails_app/test/unit/helpers/spaceman_spiffs_helper_test.rb +4 -0
- data/spec/rails_app/test/unit/spaceman_spiff_test.rb +8 -0
- data/spec/rails_app/test/unit/user_test.rb +8 -0
- data/spec/sapling_examples.rb +88 -6
- metadata +181 -25
data/.gitignore
CHANGED
data/lib/sapling.rb
CHANGED
@@ -5,18 +5,40 @@ module Sapling
|
|
5
5
|
Model.table_name
|
6
6
|
end
|
7
7
|
|
8
|
+
private
|
9
|
+
def query_conditions(feature,normalized_options={})
|
10
|
+
ret=[
|
11
|
+
"#{'feature = ? AND ' if feature} ((user_id IS NOT NULL AND user_id = ?) OR (percentage IS NOT NULL AND ? < percentage)) "
|
12
|
+
]
|
13
|
+
ret<<feature.to_s if feature
|
14
|
+
ret+=[
|
15
|
+
normalized_options[:user_id],
|
16
|
+
Util::modded_context_id(normalized_options)
|
17
|
+
]
|
18
|
+
ret
|
19
|
+
end
|
20
|
+
public
|
21
|
+
|
8
22
|
module ClientAPI
|
9
23
|
# see Sapling::API::Client
|
10
24
|
def active?(feature, options={})
|
11
25
|
options = Util.normalized_options options
|
12
|
-
v =Model.count(:conditions =>
|
13
|
-
"feature = ? AND ((user_id IS NOT NULL AND user_id = ?) OR (percentage IS NOT NULL AND ? < percentage)) ",
|
14
|
-
feature,
|
15
|
-
(u=options[:user]) && u.id,
|
16
|
-
((c=options[:context_id]) && c%100) || 100
|
17
|
-
])
|
26
|
+
v = Model.count(:conditions => query_conditions(feature,options))
|
18
27
|
v > 0
|
19
28
|
end
|
29
|
+
|
30
|
+
def features
|
31
|
+
features = Model.find :all, :select => "feature", :group => "feature", :order => "feature"
|
32
|
+
features.map {|record| record.feature.to_sym}
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns a list of features enabled for a user
|
36
|
+
# see Sapling::API::Client
|
37
|
+
def active_features(options={})
|
38
|
+
options = Util.normalized_options options
|
39
|
+
features = Model.find :all, :conditions => query_conditions(nil,options)
|
40
|
+
features.map {|record| record.feature.to_sym}.uniq
|
41
|
+
end
|
20
42
|
end
|
21
43
|
include ClientAPI
|
22
44
|
|
@@ -25,25 +47,25 @@ module Sapling
|
|
25
47
|
def activate_user(feature, user)
|
26
48
|
Model.transaction do
|
27
49
|
deactivate_user(feature, user)
|
28
|
-
Model.create(:feature => feature, :user_id => user.id)
|
50
|
+
Model.create(:feature => feature.to_s, :user_id => user.id)
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
32
54
|
def deactivate_user(feature, user)
|
33
|
-
Model.delete_all ["feature = ? AND percentage IS NULL and user_id = ?",feature,user.id]
|
55
|
+
Model.delete_all ["feature = ? AND percentage IS NULL and user_id = ?",feature.to_s,user.id]
|
34
56
|
end
|
35
57
|
|
36
58
|
def activate_percentage(feature, percentage)
|
37
59
|
raise "invalid percentage #{percentage.inspect}" unless percentage.kind_of?(Integer) && percentage>=0 && percentage<=100
|
38
60
|
Model.transaction do
|
39
61
|
deactivate_percentage(feature)
|
40
|
-
Model.create(:feature => feature, :percentage => percentage)
|
62
|
+
Model.create(:feature => feature.to_s, :percentage => percentage)
|
41
63
|
end
|
42
64
|
end
|
43
65
|
|
44
66
|
def deactivate_percentage(feature)
|
45
67
|
Model.delete_all ["feature = ? AND percentage IS NOT NULL AND user_id IS NULL",
|
46
|
-
feature
|
68
|
+
feature.to_s
|
47
69
|
]
|
48
70
|
end
|
49
71
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module Sapling
|
4
|
+
class CssGenerator
|
5
|
+
attr_accessor :sapling
|
6
|
+
|
7
|
+
def initialize(sapling)
|
8
|
+
@sapling=sapling
|
9
|
+
end
|
10
|
+
|
11
|
+
def prefix
|
12
|
+
"sapling_css"
|
13
|
+
end
|
14
|
+
|
15
|
+
def css_class(feature, on)
|
16
|
+
"#{prefix}_#{feature.to_s}_#{on ? 'on' : 'off'}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# see Sapling::API::Client for options
|
20
|
+
def to_s(options={})
|
21
|
+
features = Set.new @sapling.features
|
22
|
+
active_features = Set.new @sapling.active_features options
|
23
|
+
inactive_features = features - active_features
|
24
|
+
|
25
|
+
[
|
26
|
+
active_features.collect {|f|".#{css_class(f, false)} { display:none !important; }"},
|
27
|
+
inactive_features.collect {|f|".#{css_class(f, true)} { display:none !important; }"},
|
28
|
+
].flatten.join("\n")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/sapling/memory.rb
CHANGED
@@ -11,12 +11,12 @@ module Sapling
|
|
11
11
|
|
12
12
|
# see Sapling::API::Client
|
13
13
|
def active?(options={})
|
14
|
-
options = Util
|
14
|
+
options = Util.normalized_options(options)
|
15
15
|
individually_active?(options[:user]) || percentage_active?(options)
|
16
16
|
end
|
17
17
|
|
18
18
|
def percentage_active?(options={})
|
19
|
-
(Util.
|
19
|
+
(Util.modded_context_id(options)) < percentage
|
20
20
|
end
|
21
21
|
|
22
22
|
def individually_active?(user)
|
@@ -40,8 +40,6 @@ module Sapling
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
attr_accessor :features
|
44
|
-
|
45
43
|
def initialize
|
46
44
|
@features={}
|
47
45
|
end
|
@@ -49,14 +47,23 @@ module Sapling
|
|
49
47
|
module ClientAPI
|
50
48
|
# see Sapling::API::Client
|
51
49
|
def active?(feature, options={})
|
52
|
-
options = Util
|
50
|
+
options = Util.normalized_options(options)
|
53
51
|
(f = @features[feature]) && f.active?(options)
|
54
52
|
end
|
53
|
+
|
54
|
+
def features
|
55
|
+
@features.keys.sort_by {|k| k.to_s}
|
56
|
+
end
|
57
|
+
|
58
|
+
# see Sapling::API::Client
|
59
|
+
def active_features(options={})
|
60
|
+
features.select {|feature| active?(feature,options)}
|
61
|
+
end
|
55
62
|
end
|
56
63
|
|
57
64
|
module AdminAPI
|
58
65
|
def activate_feature(feature)
|
59
|
-
features[feature]||=Feature.new
|
66
|
+
@features[feature]||=Feature.new
|
60
67
|
end
|
61
68
|
|
62
69
|
def activate_user(feature, user)
|
@@ -64,7 +71,7 @@ module Sapling
|
|
64
71
|
end
|
65
72
|
|
66
73
|
def deactivate_user(feature, user)
|
67
|
-
(f
|
74
|
+
(f=@features[feature]) && f.deactivate_user(user)
|
68
75
|
end
|
69
76
|
|
70
77
|
def activate_percentage(feature, percentage)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'sapling/rails/action_controller'
|
2
|
+
require 'sapling/rails/view_helpers'
|
3
|
+
|
4
|
+
ActiveSupport::Dependencies.autoload_paths << File.expand_path(File.join(File.dirname(__FILE__), "rails", "controllers"))
|
5
|
+
|
6
|
+
ActionController::Routing::Routes.draw do |map|
|
7
|
+
map.sapling_stylesheet 'sapling/stylesheet.css', :controller => 'sapling', :action => 'stylesheet'
|
8
|
+
end
|
9
|
+
|
10
|
+
if defined?(Rails)
|
11
|
+
if Rails.version =~ /^3\./
|
12
|
+
# Haven't tested this
|
13
|
+
require 'sapling/rails/railtie'
|
14
|
+
else
|
15
|
+
ActionView::Base.send :include, Sapling::ViewHelpers
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
|
3
|
+
module Sapling::ActionControllerExt
|
4
|
+
def feature_active?(feature, options={})
|
5
|
+
options[:user] ||= current_user
|
6
|
+
sapling.active?(feature, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def sapling
|
11
|
+
@@sapling ||= Sapling::ActiveRecord.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def sapling_css_generator
|
15
|
+
@@sapling_css_generator ||= Sapling::CssGenerator.new(sapling)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class ActionController::Base
|
21
|
+
include Sapling::ActionControllerExt
|
22
|
+
helper_method :feature_active?, :sapling, :sapling_css_generator
|
23
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class SaplingController < ApplicationController
|
2
|
+
def stylesheet
|
3
|
+
styles = sapling_css_generator.to_s(:user => current_user, :context_id => (request.session_options[:id] if request.session))
|
4
|
+
# styles = "boo"
|
5
|
+
render :text => styles, :content_type => 'text/css'
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sapling
|
2
|
+
module ViewHelpers
|
3
|
+
|
4
|
+
# include ActionView::Helpers::CaptureHelper
|
5
|
+
def feature_on(feature, &block)
|
6
|
+
feature_block(sapling_css_generator.css_class(feature, "style=\"display:none;\"", true), &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def feature_off(feature, &block)
|
10
|
+
feature_block(sapling_css_generator.css_class(feature, nil, false), &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def feature_block(css_class, params, &block)
|
15
|
+
concat "<span class=\"#{css_class}\" #{params}>"
|
16
|
+
block.call
|
17
|
+
concat "</span>"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
data/lib/sapling/util.rb
CHANGED
@@ -1,21 +1,19 @@
|
|
1
1
|
module Sapling
|
2
2
|
class Util
|
3
|
+
CONTEXT_ID_ONLY_ENABLED_IF_100_PERCENT_ENABLED = 99
|
4
|
+
|
3
5
|
class << self
|
4
6
|
def context_id(options)
|
5
|
-
options[:context_id] || options[:user].id
|
7
|
+
options[:context_id] || ((u=options[:user]) && u.id)
|
8
|
+
end
|
9
|
+
def modded_context_id(options)
|
10
|
+
((cid=context_id(options)) && (cid%100)) || CONTEXT_ID_ONLY_ENABLED_IF_100_PERCENT_ENABLED
|
6
11
|
end
|
7
12
|
|
8
13
|
def normalized_options(options)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
options
|
13
|
-
when Integer then
|
14
|
-
{:context_id => options}
|
15
|
-
else
|
16
|
-
user=options
|
17
|
-
{:user => user, :context_id => user.id}
|
18
|
-
end
|
14
|
+
options[:user_id] ||= options[:user].id if options[:user]
|
15
|
+
options[:context_id] ||= options[:user_id]
|
16
|
+
options
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
data/lib/sapling/version.rb
CHANGED
data/rails/init.rb
ADDED
data/sapling.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Shane Brinkman-Davis", "Jason Strutz"]
|
9
9
|
s.email = ["shanebdavis@imikimi.com", "jason@cumuluscode.com"]
|
10
10
|
s.homepage = ""
|
11
|
-
s.summary = %q{
|
12
|
-
s.description = %q{}
|
11
|
+
s.summary = %q{Incrementally roll out your features. Uses ActiveRecord to store configuration and supports client-side roll-out of cached pages.}
|
12
|
+
s.description = %q{Sapling lets you seed your new features to just a few users at a time. You can change which and how many users are seeded for a feature dynamically by updating the database via the Sapling API. Core features are the ability to seed a feature for specific users and/or a percentage of users.}
|
13
13
|
|
14
14
|
s.rubyforge_project = "sapling"
|
15
15
|
|
@@ -19,8 +19,9 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
+
s.add_runtime_dependency "activerecord", ">2.3"
|
22
23
|
s.add_development_dependency "rspec", "~>2.7.0"
|
24
|
+
s.add_development_dependency "rails", "2.3.12"
|
23
25
|
s.add_development_dependency "mocha"
|
24
|
-
s.add_development_dependency "activerecord", "~>3.1.1"
|
25
26
|
s.add_development_dependency "sqlite3", "~>1.3.4"
|
26
27
|
end
|
data/spec/active_record_spec.rb
CHANGED
@@ -12,31 +12,5 @@ describe "Sapling::ActiveRecord" do
|
|
12
12
|
ActiveRecord::Base.connection.execute sql
|
13
13
|
@sapling = Sapling::ActiveRecord.new
|
14
14
|
end
|
15
|
-
|
16
15
|
end
|
17
|
-
#
|
18
|
-
# it "should init" do
|
19
|
-
# Sapling::Memory.new
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# it "should support activating users" do
|
23
|
-
# mem = Sapling::Memory.new
|
24
|
-
# user = UserMock.new
|
25
|
-
#
|
26
|
-
# mem.active?(:my_feature, user).should be_false
|
27
|
-
#
|
28
|
-
# mem.activate_user(:my_feature, user)
|
29
|
-
# mem.active?(:my_feature, user).should be_true
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# it "should support deactivating users" do
|
33
|
-
# mem = Sapling::Memory.new
|
34
|
-
# user = UserMock.new
|
35
|
-
#
|
36
|
-
# mem.activate_user(:my_feature, user)
|
37
|
-
# mem.active?(:my_feature, user).should be_true
|
38
|
-
#
|
39
|
-
# mem.deactivate_user(:my_feature, user)
|
40
|
-
# mem.active?(:my_feature, user).should be_false
|
41
|
-
# end
|
42
16
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Sapling::CssGenerator" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
ActiveRecord::Base.establish_connection(
|
7
|
+
:adapter => 'sqlite3',
|
8
|
+
:database => ':memory:'
|
9
|
+
)
|
10
|
+
sql = File.read(File.expand_path(File.dirname(__FILE__) + '/../db/create.sql'))
|
11
|
+
ActiveRecord::Base.connection.execute sql
|
12
|
+
@sapling = Sapling::ActiveRecord.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "creating basic css" do
|
16
|
+
before do
|
17
|
+
@sapling.activate_user(:chat, stub(:id => 1))
|
18
|
+
end
|
19
|
+
|
20
|
+
it "outputs css" do
|
21
|
+
Sapling::CssGenerator.new(@sapling).to_s(:user => stub(:id => 1)).should == ".sapling_css_chat_off { display:none !important; }"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "creating more complex css" do
|
26
|
+
before do
|
27
|
+
@sapling.activate_percentage(:bicycle, 10)
|
28
|
+
@sapling.activate_user(:chat, stub(:id => 115))
|
29
|
+
@sapling.activate_user(:pwn, stub(:id => 102))
|
30
|
+
@sapling.activate_user(:juggle, stub(:id => 115))
|
31
|
+
end
|
32
|
+
|
33
|
+
it "test user bicycle & pwn user" do
|
34
|
+
output = Sapling::CssGenerator.new(@sapling).to_s(:user => stub(:id => 102))
|
35
|
+
%w{ bicycle_off chat_on juggle_on pwn_off }.each do |key|
|
36
|
+
output.should =~ /#{key}[^}]+?display:none/
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "test chat & juggle user" do
|
41
|
+
output = Sapling::CssGenerator.new(@sapling).to_s(:user => stub(:id => 115))
|
42
|
+
%w{ bicycle_on chat_off juggle_off pwn_on }.each do |key|
|
43
|
+
output.should =~ /#{key}[^}]+?display:none/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/memory_feature_spec.rb
CHANGED
@@ -5,9 +5,9 @@ describe "Sapling::Memory::Feature" do
|
|
5
5
|
f=Sapling::Memory::Feature.new
|
6
6
|
u=UserMock.new
|
7
7
|
|
8
|
-
f.active?(u).should be_false
|
8
|
+
f.active?(:user=>u).should be_false
|
9
9
|
f.activate_user(u)
|
10
10
|
|
11
|
-
f.active?(u).should be_true
|
11
|
+
f.active?(:user=>u).should be_true
|
12
12
|
end
|
13
13
|
end
|
data/spec/memory_spec.rb
CHANGED
@@ -5,31 +5,5 @@ describe "Sapling::Memory" do
|
|
5
5
|
before do
|
6
6
|
@sapling = Sapling::Memory.new
|
7
7
|
end
|
8
|
-
|
9
8
|
end
|
10
|
-
#
|
11
|
-
# it "should init" do
|
12
|
-
# Sapling::Memory.new
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# it "should support activating users" do
|
16
|
-
# mem = Sapling::Memory.new
|
17
|
-
# user = UserMock.new
|
18
|
-
#
|
19
|
-
# mem.active?(:my_feature, user).should be_false
|
20
|
-
#
|
21
|
-
# mem.activate_user(:my_feature, user)
|
22
|
-
# mem.active?(:my_feature, user).should be_true
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# it "should support deactivating users" do
|
26
|
-
# mem = Sapling::Memory.new
|
27
|
-
# user = UserMock.new
|
28
|
-
#
|
29
|
-
# mem.activate_user(:my_feature, user)
|
30
|
-
# mem.active?(:my_feature, user).should be_true
|
31
|
-
#
|
32
|
-
# mem.deactivate_user(:my_feature, user)
|
33
|
-
# mem.active?(:my_feature, user).should be_false
|
34
|
-
# end
|
35
9
|
end
|