phenomenal 0.99.0 → 1.0.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/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)}"
|