phenomenal 0.99.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -7
- data/lib/phenomenal.rb +10 -1
- data/lib/phenomenal/adaptation.rb +6 -8
- data/lib/phenomenal/conflict_policies.rb +2 -1
- data/lib/phenomenal/context.rb +36 -31
- data/lib/phenomenal/dsl.rb +14 -9
- data/lib/phenomenal/feature.rb +1 -0
- data/lib/phenomenal/manager.rb +10 -12
- data/lib/phenomenal/proc.rb +2 -2
- data/lib/phenomenal/relationships/context_relationships.rb +2 -0
- data/lib/phenomenal/relationships/dsl.rb +1 -0
- data/lib/phenomenal/relationships/feature_relationships.rb +2 -2
- data/lib/phenomenal/relationships/implication.rb +10 -4
- data/lib/phenomenal/relationships/relationship.rb +13 -0
- data/lib/phenomenal/relationships/relationships_manager.rb +1 -1
- data/lib/phenomenal/relationships/relationships_store.rb +13 -7
- data/lib/phenomenal/relationships/requirement.rb +5 -0
- data/lib/phenomenal/relationships/suggestion.rb +14 -6
- data/lib/phenomenal/version.rb +1 -1
- data/lib/phenomenal/viewer/dsl.rb +15 -0
- data/lib/phenomenal/viewer/graphical.rb +136 -0
- data/lib/phenomenal/viewer/textual.rb +36 -0
- data/spec/context_spec.rb +93 -27
- data/spec/dsl_spec.rb +0 -39
- data/spec/integration/adaptation_spec.rb +127 -0
- data/spec/integration/combined_contexts_spec.rb +48 -0
- data/spec/integration/composition_spec.rb +62 -0
- data/spec/integration/conflict_policy_spec.rb +143 -0
- data/spec/integration/open_context.rb +48 -0
- data/spec/{behavior → integration}/relationships_spec.rb +0 -6
- data/spec/manager_spec.rb +30 -19
- data/spec/relationships/context_relationships_spec.rb +23 -3
- data/spec/relationships/dsl_spec.rb +9 -3
- data/spec/relationships/feature_relationships_spec.rb +22 -3
- data/spec/relationships/relationship_spec.rb +13 -1
- data/spec/relationships/relationships_manager_spec.rb +0 -11
- data/spec/relationships/relationships_store_spec.rb +31 -7
- data/spec/spec_helper.rb +1 -0
- data/spec/test_classes.rb +3 -0
- metadata +16 -13
- data/spec/behavior/adaptation_spec.rb +0 -5
- data/spec/behavior/combined_contexts_spec.rb +0 -5
- data/spec/behavior/composition_spec.rb +0 -5
- data/spec/behavior/conflict_policy_spec.rb +0 -5
- data/spec/behavior/open_context.rb +0 -5
data/Rakefile
CHANGED
data/lib/phenomenal.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Load the gem files in the system
|
2
|
-
module Phenomenal
|
2
|
+
module Phenomenal
|
3
|
+
module Viewer end
|
4
|
+
end
|
3
5
|
#Relationships
|
4
6
|
require_relative "./phenomenal/relationships/context_relationships.rb"
|
5
7
|
require_relative "./phenomenal/relationships/feature_relationships.rb"
|
@@ -19,6 +21,13 @@ require_relative "./phenomenal/logger.rb"
|
|
19
21
|
require_relative "./phenomenal/manager.rb"
|
20
22
|
require_relative "./phenomenal/proc.rb"
|
21
23
|
|
24
|
+
# Viewer
|
25
|
+
require_relative "./phenomenal/viewer/graphical.rb"
|
26
|
+
require_relative "./phenomenal/viewer/textual.rb"
|
27
|
+
|
22
28
|
# DSL
|
23
29
|
require_relative "./phenomenal/relationships/dsl.rb"
|
30
|
+
require_relative "./phenomenal/viewer/dsl.rb"
|
24
31
|
require_relative "./phenomenal/dsl.rb"
|
32
|
+
|
33
|
+
|
@@ -2,6 +2,7 @@
|
|
2
2
|
class Phenomenal::Adaptation
|
3
3
|
attr_accessor :context, :klass, :method_name, :implementation, :src_file,
|
4
4
|
:src_line,:instance_adaptation
|
5
|
+
alias_method :instance_adaptation?, :instance_adaptation
|
5
6
|
|
6
7
|
def initialize(context,klass, method_name,instance_adapatation,implementation)
|
7
8
|
@context = context
|
@@ -9,11 +10,10 @@ class Phenomenal::Adaptation
|
|
9
10
|
@method_name = method_name
|
10
11
|
@implementation = implementation
|
11
12
|
@instance_adaptation=instance_adapatation
|
12
|
-
|
13
13
|
check_validity
|
14
14
|
# Save the source location if any, this is used to find again the adaptation
|
15
|
-
# in a
|
16
|
-
# implemented in C -> Not a problem because these one never use
|
15
|
+
# in a proceed call. It always exists except for method directly
|
16
|
+
# implemented in C -> Not a problem because these one never use proceed
|
17
17
|
source_location = implementation.source_location
|
18
18
|
if source_location
|
19
19
|
@src_file = implementation.source_location[0]
|
@@ -33,9 +33,9 @@ class Phenomenal::Adaptation
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
# IMPROVE
|
36
|
+
# IMPROVE try to find a better implementation
|
37
37
|
# Bind the implementation corresponding to this adaptation to 'instance' when
|
38
|
-
#
|
38
|
+
# instance_adaptation or to implementation class when class method
|
39
39
|
def bind(instance,*args,&block)
|
40
40
|
if instance_adaptation?
|
41
41
|
if implementation.class==Proc
|
@@ -58,9 +58,7 @@ class Phenomenal::Adaptation
|
|
58
58
|
self.method_name==method_name &&
|
59
59
|
self.instance_adaptation==instance_adaptation
|
60
60
|
end
|
61
|
-
|
62
|
-
alias_method :instance_adaptation?, :instance_adaptation
|
63
|
-
|
61
|
+
|
64
62
|
# String representation
|
65
63
|
def to_s
|
66
64
|
":#{context.name} => #{klass.name}.#{method_name} :: #{src_file}:#{src_line}"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Context conflict resolution policies
|
2
2
|
module Phenomenal::ConflictPolicies
|
3
|
+
|
3
4
|
# Prefer not default adaptation, error if two not default ones
|
4
5
|
def no_resolution_conflict_policy(adaptation1,adaptation2)
|
5
6
|
if adaptation1.context==default_context()
|
@@ -8,7 +9,7 @@ module Phenomenal::ConflictPolicies
|
|
8
9
|
-1
|
9
10
|
else #Fail if two non default adaptations
|
10
11
|
Phenomenal::Logger.instance.error(
|
11
|
-
"
|
12
|
+
"Illegal duplicate adapation of #{adaptation1.to_s}"
|
12
13
|
)
|
13
14
|
end
|
14
15
|
end
|
data/lib/phenomenal/context.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# Represents a first class context
|
2
2
|
class Phenomenal::Context
|
3
3
|
include Phenomenal::ContextRelationships
|
4
4
|
@@total_activations = 0
|
5
|
+
|
6
|
+
attr_accessor :activation_age, :activation_frequency, :adaptations,
|
7
|
+
:activation_count, :parent,:forgotten
|
8
|
+
attr_reader :manager,:name
|
9
|
+
|
5
10
|
def self.create(context,*contexts,nested,closest_feature,&block)
|
6
11
|
manager = Phenomenal::Manager.instance
|
7
12
|
contexts.insert(0,context)
|
@@ -12,7 +17,7 @@ class Phenomenal::Context
|
|
12
17
|
context = manager.find_context(context)
|
13
18
|
if !context.instance_of?(self)
|
14
19
|
Phenomenal::Logger.instance.error(
|
15
|
-
"Only #{self.name} can be used with this keyword"
|
20
|
+
"Only #{self.name} can be used with this keyword."
|
16
21
|
)
|
17
22
|
end
|
18
23
|
end
|
@@ -29,7 +34,7 @@ class Phenomenal::Context
|
|
29
34
|
c = manager.find_context(c)
|
30
35
|
if !nested && c!=first && !c.instance_of?(self)
|
31
36
|
Phenomenal::Logger.instance.error(
|
32
|
-
"Only #{self.name} can be used with this keyword"
|
37
|
+
"Only #{self.name} can be used with this keyword."
|
33
38
|
)
|
34
39
|
end
|
35
40
|
else
|
@@ -42,16 +47,17 @@ class Phenomenal::Context
|
|
42
47
|
manager.combined_contexts[context] = instances
|
43
48
|
else
|
44
49
|
context = manager.find_context(*contexts)
|
50
|
+
if !context.instance_of?(self)
|
51
|
+
Phenomenal::Logger.instance.error(
|
52
|
+
"Only #{self.name} can be used with this keyword."
|
53
|
+
)
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
context.add_adaptations(&block)
|
48
58
|
context
|
49
59
|
end
|
50
60
|
|
51
|
-
attr_accessor :activation_age, :activation_frequency, :adaptations,
|
52
|
-
:activation_count, :parent,:forgotten
|
53
|
-
attr_reader :manager,:name
|
54
|
-
|
55
61
|
def initialize(name=nil, manager=nil)
|
56
62
|
@manager = manager || Phenomenal::Manager.instance
|
57
63
|
@name = name
|
@@ -64,7 +70,7 @@ class Phenomenal::Context
|
|
64
70
|
end
|
65
71
|
|
66
72
|
# Unregister the context from the context manager,
|
67
|
-
# This context
|
73
|
+
# This context shouldn't be used after.
|
68
74
|
# The context has to be inactive before being forgetted
|
69
75
|
# TODO handle relationships references
|
70
76
|
def forget
|
@@ -74,7 +80,7 @@ class Phenomenal::Context
|
|
74
80
|
)
|
75
81
|
else
|
76
82
|
manager.unregister_context(self)
|
77
|
-
forgotten=true
|
83
|
+
self.forgotten=true
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
@@ -87,8 +93,8 @@ class Phenomenal::Context
|
|
87
93
|
end
|
88
94
|
if adaptations.find{ |i| i.concern?(klass,method_name,instance) }
|
89
95
|
Phenomenal::Logger.instance.error(
|
90
|
-
"
|
91
|
-
"#{klass.name}:#{method_name}"
|
96
|
+
"Illegal duplicated adaptation in context: #{self} for " +
|
97
|
+
"#{klass.name}:#{method_name}."
|
92
98
|
)
|
93
99
|
else
|
94
100
|
if klass.instance_methods.include?(method_name) && instance
|
@@ -97,20 +103,19 @@ class Phenomenal::Context
|
|
97
103
|
method = klass.method(method_name)
|
98
104
|
else
|
99
105
|
Phenomenal::Logger.instance.error(
|
100
|
-
"
|
106
|
+
"Illegal adaptation for context #{self},a method with "+
|
101
107
|
"name: #{method_name} should exist in class #{klass.name} to "+
|
102
|
-
"be adapted"
|
108
|
+
"be adapted."
|
103
109
|
)
|
104
110
|
end
|
105
111
|
if method.arity != implementation.arity
|
106
112
|
Phenomenal::Logger.instance.error(
|
107
|
-
"
|
113
|
+
"Illegal adaptation for context #{self},the adaptation "+
|
108
114
|
"have to keep the original method arity for method: " +
|
109
115
|
"#{klass.name}.#{method_name}: (#{method.arity} instead of " +
|
110
|
-
"#{implementation.arity})"
|
116
|
+
"#{implementation.arity})."
|
111
117
|
)
|
112
118
|
end
|
113
|
-
|
114
119
|
adaptation = Phenomenal::Adaptation.new(
|
115
120
|
self, klass, method_name,instance, implementation
|
116
121
|
)
|
@@ -149,8 +154,8 @@ class Phenomenal::Context
|
|
149
154
|
add_adaptation(@current_adapted_class,method,true,&block)
|
150
155
|
end
|
151
156
|
|
152
|
-
# Adapt a method for @current_adapted_class
|
153
|
-
def
|
157
|
+
# Adapt a class method for @current_adapted_class
|
158
|
+
def adapt_class(method,&block)
|
154
159
|
add_adaptation(@current_adapted_class,method,false,&block)
|
155
160
|
end
|
156
161
|
|
@@ -160,8 +165,8 @@ class Phenomenal::Context
|
|
160
165
|
adaptations.find_index{ |i| i.concern?(klass, method_name,instance) }
|
161
166
|
if !adaptation_index
|
162
167
|
Phenomenal::Logger.instance.error(
|
163
|
-
"
|
164
|
-
"#{self} for #{klass.name}.#{method_name})"
|
168
|
+
"Illegal deleting of an inexistent adaptation in context: " +
|
169
|
+
"#{self} for #{klass.name}.#{method_name})."
|
165
170
|
)
|
166
171
|
end
|
167
172
|
|
@@ -209,18 +214,18 @@ class Phenomenal::Context
|
|
209
214
|
end
|
210
215
|
|
211
216
|
# Return the activation age of the context:
|
212
|
-
#
|
213
|
-
#
|
217
|
+
# (The age counter minus the age counter when the context was activated
|
218
|
+
# for the last time)
|
214
219
|
def age
|
215
220
|
@@total_activations-activation_age
|
216
221
|
end
|
217
222
|
|
218
223
|
# Return context informations:
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
+
# - Name
|
225
|
+
# - List of the adaptations
|
226
|
+
# - Active state
|
227
|
+
# - Activation age
|
228
|
+
# - Activation count
|
224
229
|
def information
|
225
230
|
{
|
226
231
|
:name=>name,
|
@@ -250,11 +255,11 @@ class Phenomenal::Context
|
|
250
255
|
if name
|
251
256
|
name.to_s
|
252
257
|
elsif self==manager.default_context
|
253
|
-
"
|
258
|
+
"Default context"
|
254
259
|
elsif manager.combined_contexts[self]
|
255
|
-
"
|
260
|
+
"#{manager.combined_contexts[self].flatten}"
|
256
261
|
else
|
257
|
-
"
|
262
|
+
"Anonymous context"
|
258
263
|
end
|
259
264
|
end
|
260
265
|
|
@@ -262,7 +267,7 @@ class Phenomenal::Context
|
|
262
267
|
def check_validity
|
263
268
|
if forgotten
|
264
269
|
Phenomenal::Logger.instance.error(
|
265
|
-
"Action not allowed anymore when context has been forgotten"
|
270
|
+
"Action not allowed anymore when context has been forgotten."
|
266
271
|
)
|
267
272
|
end
|
268
273
|
end
|
data/lib/phenomenal/dsl.rb
CHANGED
@@ -3,10 +3,6 @@ module Phenomenal::DSL
|
|
3
3
|
#Override included hook method
|
4
4
|
def self.included(klass)
|
5
5
|
klass.class_eval do
|
6
|
-
#Define Context
|
7
|
-
def phen_define_context(name=nil,priority=nil)
|
8
|
-
Phenomenal::Context.new(name,priority)
|
9
|
-
end
|
10
6
|
# Define context with adaptations
|
11
7
|
def phen_context(context,*contexts,&block)
|
12
8
|
Phenomenal::Context.create(context,*contexts,false,nil,&block)
|
@@ -19,7 +15,6 @@ module Phenomenal::DSL
|
|
19
15
|
end
|
20
16
|
Phenomenal::DSL.phen_alias(:feature,klass)
|
21
17
|
|
22
|
-
|
23
18
|
# Forget Context
|
24
19
|
def phen_forget_context(context)
|
25
20
|
Phenomenal::Manager.instance.find_context(context).forget
|
@@ -52,14 +47,22 @@ module Phenomenal::DSL
|
|
52
47
|
end
|
53
48
|
|
54
49
|
# Activate Context
|
55
|
-
def phen_activate_context(context)
|
56
|
-
|
50
|
+
def phen_activate_context(context,*contexts)
|
51
|
+
contexts=[] if contexts.nil?
|
52
|
+
contexts.push(context)
|
53
|
+
contexts.each do |c|
|
54
|
+
Phenomenal::Manager.instance.find_context(c).activate
|
55
|
+
end
|
57
56
|
end
|
58
57
|
Phenomenal::DSL.phen_alias(:activate_context,klass)
|
59
58
|
|
60
59
|
# Deactivate Context
|
61
|
-
def phen_deactivate_context(context)
|
62
|
-
|
60
|
+
def phen_deactivate_context(context,*contexts)
|
61
|
+
contexts=[] if contexts.nil?
|
62
|
+
contexts.push(context)
|
63
|
+
contexts.each do |c|
|
64
|
+
Phenomenal::Manager.instance.find_context(c).deactivate
|
65
|
+
end
|
63
66
|
end
|
64
67
|
Phenomenal::DSL.phen_alias(:deactivate_context,klass)
|
65
68
|
|
@@ -96,6 +99,8 @@ module Phenomenal::DSL
|
|
96
99
|
end
|
97
100
|
# Add relationships specific DSL
|
98
101
|
define_relationships(klass)
|
102
|
+
# Add Viewers specific DSL
|
103
|
+
define_viewers(klass)
|
99
104
|
end
|
100
105
|
|
101
106
|
private
|
data/lib/phenomenal/feature.rb
CHANGED
data/lib/phenomenal/manager.rb
CHANGED
@@ -5,7 +5,7 @@ class Phenomenal::Manager
|
|
5
5
|
include Phenomenal::ConflictPolicies
|
6
6
|
|
7
7
|
attr_accessor :active_adaptations, :deployed_adaptations, :contexts,
|
8
|
-
:default_context, :combined_contexts, :shared_contexts, :rmanager
|
8
|
+
:default_context, :combined_contexts, :shared_contexts, :rmanager
|
9
9
|
|
10
10
|
# Register a new context
|
11
11
|
def register_context(context)
|
@@ -20,18 +20,20 @@ class Phenomenal::Manager
|
|
20
20
|
" If you want to have named context it has to be a globally unique name"
|
21
21
|
)
|
22
22
|
end
|
23
|
+
# Update the relationships that concern this context
|
24
|
+
rmanager.update_relationships_references(context)
|
25
|
+
# Store the context at its ID
|
23
26
|
contexts[context]=context
|
24
27
|
end
|
25
28
|
|
26
29
|
# Unregister a context (forget)
|
27
30
|
def unregister_context(context)
|
28
|
-
if context==default_context &&
|
31
|
+
if context==default_context && contexts.size>1
|
29
32
|
Phenomenal::Logger.instance.error(
|
30
33
|
"Default context can only be forgotten when alone"
|
31
34
|
)
|
32
35
|
else
|
33
36
|
contexts.delete(context)
|
34
|
-
|
35
37
|
# Forgot combined contexts
|
36
38
|
combined_contexts.delete(context)
|
37
39
|
if shared_contexts[context]
|
@@ -39,7 +41,6 @@ class Phenomenal::Manager
|
|
39
41
|
c.forget
|
40
42
|
end
|
41
43
|
end
|
42
|
-
|
43
44
|
# Restore default context
|
44
45
|
init_default() if context==default_context
|
45
46
|
end
|
@@ -65,8 +66,7 @@ class Phenomenal::Manager
|
|
65
66
|
def activate_context(context)
|
66
67
|
begin
|
67
68
|
# Relationships managment
|
68
|
-
rmanager.activate_relationships(context) if context.just_activated?
|
69
|
-
|
69
|
+
rmanager.activate_relationships(context) if context.just_activated?
|
70
70
|
# Activation of adaptations
|
71
71
|
context.adaptations.each{ |i| activate_adaptation(i) }
|
72
72
|
#puts "activation of #{context}"
|
@@ -114,9 +114,7 @@ class Phenomenal::Manager
|
|
114
114
|
adaptations_stack = sorted_adaptations_for(calling_adaptation.klass,
|
115
115
|
calling_adaptation.method_name,calling_adaptation.instance_adaptation?)
|
116
116
|
calling_adaptation_index = adaptations_stack.find_index(calling_adaptation)
|
117
|
-
|
118
117
|
next_adaptation = adaptations_stack[calling_adaptation_index+1]
|
119
|
-
|
120
118
|
next_adaptation.bind(instance,*args, &block)
|
121
119
|
end
|
122
120
|
|
@@ -127,8 +125,8 @@ class Phenomenal::Manager
|
|
127
125
|
self.class.class_eval{define_method(:conflict_policy,&block)}
|
128
126
|
end
|
129
127
|
|
130
|
-
# Return the corresponding context or raise an error
|
131
|
-
# currently registered.
|
128
|
+
# Return the corresponding context (or combined context) or raise an error
|
129
|
+
# if the context isn't currently registered.
|
132
130
|
# The 'context' parameter can be either a reference to a context instance or
|
133
131
|
# a Symbol with the name of a named (not anonymous) context.
|
134
132
|
def find_context(context, *contexts)
|
@@ -140,7 +138,7 @@ class Phenomenal::Manager
|
|
140
138
|
end
|
141
139
|
end
|
142
140
|
|
143
|
-
# Check wether context 'context' exist in the context manager
|
141
|
+
# Check wether context 'context' (or combined context) exist in the context manager
|
144
142
|
# Context can be either the context name or the context instance itself
|
145
143
|
# Return the context if found, or nil otherwise
|
146
144
|
def context_defined?(context, *contexts)
|
@@ -152,7 +150,7 @@ class Phenomenal::Manager
|
|
152
150
|
end
|
153
151
|
return c
|
154
152
|
end
|
155
|
-
|
153
|
+
|
156
154
|
private
|
157
155
|
def find_simple_context(context)
|
158
156
|
find=nil
|
data/lib/phenomenal/proc.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
class Proc
|
3
3
|
# Define a bind method on Proc object,
|
4
4
|
# This allow to execute a Proc as it was an instance method of reciever
|
5
|
-
#
|
5
|
+
# Used for composition of instance methods adaptations
|
6
6
|
# Src: http://www.ruby-forum.com/topic/173699
|
7
7
|
def phenomenal_bind(receiver)
|
8
8
|
block, time = self, Time.now
|
@@ -17,7 +17,7 @@ class Proc
|
|
17
17
|
|
18
18
|
# Define a bind_class method on Proc object,
|
19
19
|
# This allow to execute a Proc as it was an class method of klass
|
20
|
-
#
|
20
|
+
# Used for composition of class methods adaptations
|
21
21
|
def phenomenal_class_bind(klass)
|
22
22
|
block, time = self, Time.now
|
23
23
|
method_name = "__bind_#{time.to_i}_#{time.usec}-#{rand(100000)}"
|