phenomenal 0.9.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/LICENSE +27 -0
- data/README +3 -0
- data/Rakefile +11 -0
- data/lib/phenomenal.rb +24 -0
- data/lib/phenomenal/adaptation.rb +79 -0
- data/lib/phenomenal/conflict_policies.rb +20 -0
- data/lib/phenomenal/context.rb +269 -0
- data/lib/phenomenal/dsl.rb +119 -0
- data/lib/phenomenal/feature.rb +8 -0
- data/lib/phenomenal/logger.rb +34 -0
- data/lib/phenomenal/manager.rb +317 -0
- data/lib/phenomenal/proc.rb +34 -0
- data/lib/phenomenal/relationships/context_relationships.rb +22 -0
- data/lib/phenomenal/relationships/dsl.rb +18 -0
- data/lib/phenomenal/relationships/feature_relationships.rb +42 -0
- data/lib/phenomenal/relationships/implication.rb +35 -0
- data/lib/phenomenal/relationships/relationship.rb +42 -0
- data/lib/phenomenal/relationships/relationships_manager.rb +63 -0
- data/lib/phenomenal/relationships/relationships_store.rb +73 -0
- data/lib/phenomenal/relationships/requirement.rb +26 -0
- data/lib/phenomenal/relationships/suggestion.rb +41 -0
- data/lib/phenomenal/version.rb +3 -0
- data/spec/adaptation_spec.rb +64 -0
- data/spec/behavior/adaptation_spec.rb +5 -0
- data/spec/behavior/combined_contexts_spec.rb +5 -0
- data/spec/behavior/composition_spec.rb +5 -0
- data/spec/behavior/conflict_policy_spec.rb +5 -0
- data/spec/behavior/open_context.rb +5 -0
- data/spec/behavior/relationships_spec.rb +249 -0
- data/spec/context_spec.rb +268 -0
- data/spec/dsl_spec.rb +181 -0
- data/spec/feature_spec.rb +5 -0
- data/spec/manager_spec.rb +84 -0
- data/spec/proc_spec.rb +20 -0
- data/spec/relationships/context_relationships_spec.rb +13 -0
- data/spec/relationships/dsl_spec.rb +13 -0
- data/spec/relationships/feature_relationships_spec.rb +13 -0
- data/spec/relationships/relationship_spec.rb +31 -0
- data/spec/relationships/relationships_manager_spec.rb +15 -0
- data/spec/relationships/relationships_store_spec.rb +19 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/test_classes.rb +111 -0
- metadata +124 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
class Phenomenal::Implication < Phenomenal::Relationship
|
2
|
+
attr_accessor :activation_counter
|
3
|
+
def initialize(source,target,feature)
|
4
|
+
super(source,target,feature)
|
5
|
+
@activation_counter=0
|
6
|
+
end
|
7
|
+
|
8
|
+
def activate_feature
|
9
|
+
if source.active?
|
10
|
+
target.activate
|
11
|
+
activation_counter+=1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def deactivate_feature
|
16
|
+
if activation_counter>0
|
17
|
+
target.deactivate
|
18
|
+
activation_coutner-=1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def activate_context(context)
|
23
|
+
if source==context
|
24
|
+
target.activate
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def deactivate_context(context)
|
29
|
+
if source==context
|
30
|
+
target.deactivate
|
31
|
+
else
|
32
|
+
source.deactivate
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Phenomenal::Relationship
|
2
|
+
attr_accessor :source,:target,:manager,:feature
|
3
|
+
|
4
|
+
def initialize(source,target,feature)
|
5
|
+
@source=source
|
6
|
+
@target=target
|
7
|
+
@manager=Phenomenal::Manager.instance
|
8
|
+
@feature=feature
|
9
|
+
refresh
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
self.class==other.class &&
|
14
|
+
self.source==other.source &&
|
15
|
+
self.target==other.target &&
|
16
|
+
self.feature==other.feature
|
17
|
+
end
|
18
|
+
|
19
|
+
def refresh
|
20
|
+
s = manager.context_defined?(source)
|
21
|
+
self.source=s if !s.nil?
|
22
|
+
|
23
|
+
t = manager.context_defined?(target)
|
24
|
+
self.target=t if !t.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def activate_context(context)
|
28
|
+
end
|
29
|
+
|
30
|
+
def deactivate_context(context)
|
31
|
+
end
|
32
|
+
|
33
|
+
def activate_feature
|
34
|
+
end
|
35
|
+
|
36
|
+
def deactivate_feature
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{self.class.name} between #{source.class.name}:#{source} and #{target.class.name}:#{target}"
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class Phenomenal::RelationshipsManager
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_accessor :relationships
|
7
|
+
|
8
|
+
def activate_relationships(context)
|
9
|
+
# Step 1: Import the new relationships for a feature
|
10
|
+
if context.is_a?(Phenomenal::Feature)
|
11
|
+
import_relationships(context)
|
12
|
+
end
|
13
|
+
# Step 2: Apply relationships
|
14
|
+
relationships.get_for(context).each do |relationship|
|
15
|
+
relationship.activate_context(context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def deactivate_relationships(context)
|
20
|
+
# Step 1: Unapply relationships
|
21
|
+
relationships.get_for(context).each do |relationship|
|
22
|
+
relationship.deactivate_context(context)
|
23
|
+
end
|
24
|
+
# Step 2: Remove relationships
|
25
|
+
if context.is_a?(Phenomenal::Feature)
|
26
|
+
remove_relationships(context)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Called when a context is defined in the manager
|
31
|
+
def update_relationships_references(context)
|
32
|
+
relationships.update_references(context)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def import_relationships(feature)
|
37
|
+
begin
|
38
|
+
feature.relationships.each do |relationship|
|
39
|
+
relationship.refresh # Update references
|
40
|
+
relationship.activate_feature # Activate relationship
|
41
|
+
relationships.add(relationship)
|
42
|
+
end
|
43
|
+
rescue Phenomenal::Error => m
|
44
|
+
feature.deactivate
|
45
|
+
Phenomenal::Logger.instance.debug(
|
46
|
+
"Unable to activate the feature #{feature} \n #{m}"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_relationships(feature)
|
52
|
+
feature.relationships.each do |relationship|
|
53
|
+
if relationships.include?(relationship)
|
54
|
+
relationship.deactivate_feature
|
55
|
+
relationships.remove(relationship)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize
|
61
|
+
@relationships = Phenomenal::RelationshipsStore.new
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class Phenomenal::RelationshipsStore
|
2
|
+
attr_accessor :sources, :targets
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@sources = {}
|
6
|
+
@targets = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(relationship)
|
10
|
+
if @sources[relationship.source].nil?
|
11
|
+
@sources[relationship.source] = Array.new
|
12
|
+
end
|
13
|
+
@sources[relationship.source].push(relationship)
|
14
|
+
|
15
|
+
if @targets[relationship.target].nil?
|
16
|
+
@targets[relationship.target] = Array.new
|
17
|
+
end
|
18
|
+
@targets[relationship.target].push(relationship)
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(relationship)
|
22
|
+
@sources[relationship.source].delete(relationship) if @sources[relationship.source] # In case of rollback
|
23
|
+
@targets[relationship.target].delete(relationship) if @targets[relationship.target]
|
24
|
+
end
|
25
|
+
|
26
|
+
def include?(relationship)
|
27
|
+
if @sources[relationship.source]
|
28
|
+
@sources[relationship.source].include?(relationship)
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_references(context)
|
35
|
+
# Do nothing when anonymous, references are already valid
|
36
|
+
return if context.anonymous?
|
37
|
+
# Update sources
|
38
|
+
@sources[context.name].each do |relationship|
|
39
|
+
relationship.source=context
|
40
|
+
end
|
41
|
+
@sources[context]=@source.delete(context.name)
|
42
|
+
# Update targets
|
43
|
+
@targets[context.name].each do |relationship|
|
44
|
+
relationship.target=context
|
45
|
+
end
|
46
|
+
@targets[context]=@targets.delete(context.name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_for(context)
|
50
|
+
get_for_source(context).concat(get_for_target(context))
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
# Return an array of relationships
|
55
|
+
def get_for_source(source)
|
56
|
+
rel = @sources[source]
|
57
|
+
if rel.nil?
|
58
|
+
Array.new
|
59
|
+
else
|
60
|
+
rel
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return an array of relationships
|
65
|
+
def get_for_target(target)
|
66
|
+
rel = @targets[target]
|
67
|
+
if rel.nil?
|
68
|
+
Array.new
|
69
|
+
else
|
70
|
+
rel
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Phenomenal::Requirement < Phenomenal::Relationship
|
2
|
+
def activate_feature
|
3
|
+
check_requirement
|
4
|
+
end
|
5
|
+
|
6
|
+
def activate_context(context)
|
7
|
+
if(source==context)
|
8
|
+
check_requirement
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def deactivate_context(context)
|
13
|
+
if(target==context)
|
14
|
+
source.deactivate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def check_requirement
|
20
|
+
if source.active? && !target.active?
|
21
|
+
Phenomenal::Logger.instance.error(
|
22
|
+
"Requirement of #{target} for #{source} is not satisfied"
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Phenomenal::Suggestion < Phenomenal::Relationship
|
2
|
+
attr_accessor :activation_counter
|
3
|
+
|
4
|
+
def initialize(source,target,feature)
|
5
|
+
super(source,target,feature)
|
6
|
+
@activation_counter=0
|
7
|
+
end
|
8
|
+
|
9
|
+
def activate_feature
|
10
|
+
begin
|
11
|
+
if source.active?
|
12
|
+
target.activate
|
13
|
+
self.activation_counter+=1
|
14
|
+
end
|
15
|
+
rescue
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def deactivate_feature
|
20
|
+
begin
|
21
|
+
if activation_counter>0
|
22
|
+
target.deactivate
|
23
|
+
self.activation_coutner-=1
|
24
|
+
end
|
25
|
+
rescue
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def activate_context(context)
|
31
|
+
if source==context
|
32
|
+
target.activate
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def deactivate_context(context)
|
37
|
+
if source==context
|
38
|
+
target.deactivate
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
describe Phenomenal::Adaptation do
|
3
|
+
before :each do
|
4
|
+
define_test_classes
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "#deploy" do
|
8
|
+
it "should be able to deploy itself in instances to override default implementation" do
|
9
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:length,true,Proc.new{-1})
|
10
|
+
t = TestString.new("1234")
|
11
|
+
t.length.should == 4
|
12
|
+
expect {adaptation.deploy}.to_not raise_error
|
13
|
+
t.length.should == -1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to deploy itself in classes to override default implementation" do
|
17
|
+
TestString.name.should == "TestString"
|
18
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:name,false,Proc.new{"TEST"})
|
19
|
+
expect {adaptation.deploy}.to_not raise_error
|
20
|
+
TestString.name.should == "TEST"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#bind" do
|
25
|
+
it "should be possible to temporary bind adapation as instance methods" do
|
26
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:length,true,Proc.new{-10})
|
27
|
+
t = TestString.new("1234")
|
28
|
+
t.length.should == -1
|
29
|
+
adaptation.bind(t).should == -10
|
30
|
+
t.length.should == -1
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should be possible to temporary bind adapatation as class methods" do
|
34
|
+
TestString.name.should == "TEST"
|
35
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:name,false,Proc.new{"TEST2"})
|
36
|
+
adaptation.bind(TestString).should == "TEST2"
|
37
|
+
TestString.name.should == "TEST"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#instance_adaptation?" do
|
42
|
+
it "should return true if the method is a instance method" do
|
43
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:length,true,Proc.new{-10})
|
44
|
+
adaptation.instance_adaptation?.should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return false if the method is a class method" do
|
48
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:name,false,Proc.new{"TEST2"})
|
49
|
+
adaptation.instance_adaptation?.should be_false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#concern" do
|
54
|
+
it "should return true if the adaptation concern the class n_klass and method n_method and is instance method if instance=true" do
|
55
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:length,true,Proc.new{-10})
|
56
|
+
adaptation.concern?(TestString,:length,true).should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should return false if the adaptation doesn't concern the class n_klass and method n_method and is instance method if instance=true" do
|
60
|
+
adaptation = Phenomenal::Adaptation.new(nil,TestString,:size,true,Proc.new{-10})
|
61
|
+
adaptation.concern?(TestString,:length,true).should be_false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Relationships" do
|
4
|
+
before :each do
|
5
|
+
@manager = Phenomenal::Manager.instance
|
6
|
+
@feature = Phenomenal::Feature.new(:feature)
|
7
|
+
@context_names = [:a,:b,:c,:d,:e,:f,:g]
|
8
|
+
@context_names.each do |name|
|
9
|
+
@feature.context(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
after :each do
|
15
|
+
force_forget_context(@feature)
|
16
|
+
@context_names.each do |name|
|
17
|
+
force_forget_context(name)
|
18
|
+
end
|
19
|
+
@manager.default_context.deactivate
|
20
|
+
@manager.default_context.forget
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Phenomenal::Feature do
|
24
|
+
it "should be able to add requirements for contexts" do
|
25
|
+
@feature.should respond_to :requirements_for
|
26
|
+
@feature.method(:requirements_for).arity.should be(2),
|
27
|
+
"Bad arity, should be 2"
|
28
|
+
end
|
29
|
+
it "should be able to add implications for contexts" do
|
30
|
+
@feature.should respond_to :implications_for
|
31
|
+
@feature.method(:implications_for).arity.should be(2),
|
32
|
+
"Bad arity, should be 2"
|
33
|
+
end
|
34
|
+
it "should be able to add suggestions for contexts" do
|
35
|
+
@feature.should respond_to :suggestions_for
|
36
|
+
@feature.method(:suggestions_for).arity.should be(2),
|
37
|
+
"Bad arity, should be 2"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "Requirements" do
|
41
|
+
it "should store requirements" do
|
42
|
+
@feature.requirements_for :a, :on=>[:b,:c,:d]
|
43
|
+
@feature.requirements_for :a, :on=>:e
|
44
|
+
@feature.relationships.should have(4).items
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should avoid activation with missing requirements" do
|
48
|
+
@feature.requirements_for :a, :on=>[:b,:c]
|
49
|
+
phen_activate_context(:feature)
|
50
|
+
expect {phen_activate_context(:a)}.to raise_error Phenomenal::Error
|
51
|
+
phen_context_active?(:a).should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow activation with all requirements" do
|
55
|
+
phen_activate_context(:feature)
|
56
|
+
phen_activate_context(:b)
|
57
|
+
phen_activate_context(:c)
|
58
|
+
expect {phen_activate_context(:a)}.to_not raise_error
|
59
|
+
phen_context_active?(:a).should be_true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should deactivate source when target requirement is deactivated" do
|
63
|
+
@feature.requirements_for :a, :on=>[:b,:c]
|
64
|
+
phen_activate_context(:feature)
|
65
|
+
phen_activate_context(:b)
|
66
|
+
phen_activate_context(:c)
|
67
|
+
phen_activate_context(:a)
|
68
|
+
expect {phen_deactivate_context(:b)}.to_not raise_error
|
69
|
+
phen_context_active?(:a).should be_false
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should avoid feature activation when adding a not satisfied requirement" do
|
73
|
+
@feature.requirements_for :a, :on=>[:b,:c]
|
74
|
+
phen_activate_context(:a)
|
75
|
+
expect{phen_activate_context(:feature)}.to_not raise_error
|
76
|
+
phen_context_active?(:feature).should be_false
|
77
|
+
end
|
78
|
+
|
79
|
+
after do
|
80
|
+
@manager.default_context.deactivate
|
81
|
+
@manager.default_context.forget
|
82
|
+
end
|
83
|
+
it "should be possible to add requirements to the default context" do
|
84
|
+
requirements_for :a, :on=>[:b,:c,:d]
|
85
|
+
requirements_for :a, :on=>:e
|
86
|
+
@manager.default_context.relationships.should have(4).items
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should be possible to put requirements in the nested contexts" do
|
90
|
+
feature :feature do
|
91
|
+
context :a do
|
92
|
+
requires :b
|
93
|
+
end
|
94
|
+
end
|
95
|
+
@feature.relationships.should have(1).items
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should be possible to put requirements in combined nested contexts" do
|
99
|
+
feature :feature do
|
100
|
+
context :a,:b do
|
101
|
+
requires :c,:d,:e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
@feature.relationships.should have(3).items
|
105
|
+
end
|
106
|
+
end
|
107
|
+
describe "Implications" do
|
108
|
+
it "should store implications" do
|
109
|
+
@feature.implications_for :a, :on=>[:b,:c,:d]
|
110
|
+
@feature.implications_for :a, :on=>:e
|
111
|
+
@feature.relationships.should have(4).items
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should activate target when source is activated" do
|
115
|
+
@feature.implications_for :a, :on=>:b
|
116
|
+
@feature.activate
|
117
|
+
expect {phen_activate_context(:a)}.to_not raise_error
|
118
|
+
phen_context_active?(:a).should be_true
|
119
|
+
phen_context_active?(:b).should be_true
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should deactivate target when source is deactivated" do
|
123
|
+
@feature.implications_for :a, :on=>:b
|
124
|
+
@feature.activate
|
125
|
+
phen_activate_context(:a)
|
126
|
+
phen_deactivate_context(:a)
|
127
|
+
phen_context_active?(:b).should be_false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should deactivate source when target is deactivated" do
|
131
|
+
@feature.implications_for :a, :on=>:b
|
132
|
+
@feature.activate
|
133
|
+
phen_activate_context(:a)
|
134
|
+
phen_deactivate_context(:b)
|
135
|
+
phen_context_active?(:a).should be_false
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should be possible to put implications in the nested contexts" do
|
139
|
+
feature :feature do
|
140
|
+
context :a do
|
141
|
+
implies :b
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@feature.relationships.should have(1).items
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should be possible to put implications in combined nested contexts" do
|
148
|
+
feature :feature do
|
149
|
+
context :a,:b do
|
150
|
+
implies :c,:d,:e
|
151
|
+
end
|
152
|
+
end
|
153
|
+
@feature.relationships.should have(3).items
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "Suggestions" do
|
158
|
+
it "should be working on the default feature" do
|
159
|
+
suggestions_for :a,:on=>:b
|
160
|
+
@manager.default_context.relationships.should have(1).items
|
161
|
+
context(:a).active?.should be_false
|
162
|
+
context(:b).active?.should be_false
|
163
|
+
expect {activate_context :a}.to_not raise_error
|
164
|
+
context(:a).active?.should be_true
|
165
|
+
context(:b).active?.should be_true
|
166
|
+
expect {deactivate_context :a}.to_not raise_error
|
167
|
+
context(:a).active?.should be_false
|
168
|
+
context(:b).active?.should be_false
|
169
|
+
#TODO forget default
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should be possible to add relationships on active features" do
|
173
|
+
context(:a).active?.should be_false
|
174
|
+
context(:b).active?.should be_false
|
175
|
+
expect {activate_context :a}.to_not raise_error
|
176
|
+
suggestions_for :a,:on=>:b
|
177
|
+
@manager.default_context.relationships.should have(1).items
|
178
|
+
context(:a).active?.should be_true
|
179
|
+
context(:b).active?.should be_true
|
180
|
+
expect {deactivate_context :a}.to_not raise_error
|
181
|
+
context(:a).active?.should be_false
|
182
|
+
context(:b).active?.should be_false
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should apply the relationship on the activation of the feature that contain it" do
|
186
|
+
context(:a).active?.should be_false
|
187
|
+
context(:b).active?.should be_false
|
188
|
+
expect {activate_context :a}.to_not raise_error
|
189
|
+
@feature.suggestions_for :a,:on=>:b
|
190
|
+
context(:a).active?.should be_true
|
191
|
+
context(:b).active?.should be_false
|
192
|
+
expect {activate_context @feature}.to_not raise_error
|
193
|
+
context(:a).active?.should be_true
|
194
|
+
context(:b).active?.should be_true
|
195
|
+
expect {deactivate_context @feature}.to_not raise_error
|
196
|
+
context(:a).active?.should be_true
|
197
|
+
context(:b).active?.should be_false
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should store suggestions" do
|
201
|
+
@feature.suggestions_for :a, :on=>[:b,:c,:d]
|
202
|
+
@feature.suggestions_for :a, :on=>:e
|
203
|
+
@feature.relationships.should have(4).items
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should activate target when source is activated" do
|
207
|
+
@feature.suggestions_for :a, :on=>:b
|
208
|
+
@feature.activate
|
209
|
+
expect {phen_activate_context(:a)}.to_not raise_error
|
210
|
+
phen_context_active?(:a).should be_true
|
211
|
+
phen_context_active?(:b).should be_true
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should deactivate target when source is deactivated" do
|
215
|
+
@feature.suggestions_for :a, :on=>:b
|
216
|
+
@feature.activate
|
217
|
+
phen_activate_context(:a)
|
218
|
+
phen_deactivate_context(:a)
|
219
|
+
phen_context_active?(:b).should be_false
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should not deactivate source when target is deactivated" do
|
223
|
+
@feature.suggestions_for :a, :on=>:b
|
224
|
+
@feature.activate
|
225
|
+
phen_activate_context(:a)
|
226
|
+
phen_deactivate_context(:b)
|
227
|
+
phen_context_active?(:a).should be_true
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be possible to put suggestions in the nested contexts" do
|
231
|
+
feature :feature do
|
232
|
+
context :a do
|
233
|
+
suggests :b
|
234
|
+
end
|
235
|
+
end
|
236
|
+
@feature.relationships.should have(1).items
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should be possible to put suggestions in combined nested contexts" do
|
240
|
+
feature :feature do
|
241
|
+
context :a,:b do
|
242
|
+
suggests :c,:d,:e
|
243
|
+
end
|
244
|
+
end
|
245
|
+
@feature.relationships.should have(3).items
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|