phenomenal 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|