phenomenal 0.11.11.24.4 → 0.99.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README +3 -4
- data/Rakefile +3 -0
- data/lib/phenomenal.rb +15 -2
- data/lib/phenomenal/adaptation.rb +22 -12
- data/lib/phenomenal/context.rb +127 -134
- data/lib/phenomenal/dsl.rb +41 -14
- data/lib/phenomenal/feature.rb +8 -0
- data/lib/phenomenal/logger.rb +0 -1
- data/lib/phenomenal/manager.rb +117 -35
- 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/{test → spec}/test_classes.rb +3 -0
- metadata +69 -24
- data/demo.rb +0 -24
- data/demo_age.rb +0 -89
- data/demo_dsl.rb +0 -28
- data/phenomenal-0.11.11.24.3.gem +0 -0
- data/phenomenal.gemspec +0 -15
- data/test/test_cop_adaptation.rb +0 -168
- data/test/test_cop_composition.rb +0 -84
- data/test/test_cop_conflictpolicy.rb +0 -177
- data/test/test_cop_infrastructure.rb +0 -129
- data/test_declaration.rb +0 -18
data/LICENSE
CHANGED
data/README
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
rake test
|
1
|
+
Phenomenal Gem is a context-oriented framework implemented in Ruby that allows context-oriented programming in Ruby and Ruby on Rails applications.
|
2
|
+
|
3
|
+
See www.phenomenal-gem.com for more details
|
data/Rakefile
CHANGED
data/lib/phenomenal.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
# Load the gem files in the system
|
2
2
|
module Phenomenal end
|
3
|
+
#Relationships
|
4
|
+
require_relative "./phenomenal/relationships/context_relationships.rb"
|
5
|
+
require_relative "./phenomenal/relationships/feature_relationships.rb"
|
6
|
+
require_relative "./phenomenal/relationships/relationships_store.rb"
|
7
|
+
require_relative "./phenomenal/relationships/relationships_manager.rb"
|
8
|
+
require_relative "./phenomenal/relationships/relationship.rb"
|
9
|
+
require_relative "./phenomenal/relationships/requirement.rb"
|
10
|
+
require_relative "./phenomenal/relationships/implication.rb"
|
11
|
+
require_relative "./phenomenal/relationships/suggestion.rb"
|
12
|
+
|
13
|
+
# Core
|
3
14
|
require_relative "./phenomenal/adaptation.rb"
|
4
15
|
require_relative "./phenomenal/conflict_policies.rb"
|
5
16
|
require_relative "./phenomenal/context.rb"
|
17
|
+
require_relative "./phenomenal/feature.rb"
|
6
18
|
require_relative "./phenomenal/logger.rb"
|
7
19
|
require_relative "./phenomenal/manager.rb"
|
8
20
|
require_relative "./phenomenal/proc.rb"
|
9
|
-
require_relative "./phenomenal/dsl.rb"
|
10
|
-
|
11
21
|
|
22
|
+
# DSL
|
23
|
+
require_relative "./phenomenal/relationships/dsl.rb"
|
24
|
+
require_relative "./phenomenal/dsl.rb"
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# Represent a method adaptation for a particular context
|
2
2
|
class Phenomenal::Adaptation
|
3
3
|
attr_accessor :context, :klass, :method_name, :implementation, :src_file,
|
4
|
-
:src_line
|
4
|
+
:src_line,:instance_adaptation
|
5
5
|
|
6
|
-
def initialize(context,klass, method_name,
|
6
|
+
def initialize(context,klass, method_name,instance_adapatation,implementation)
|
7
7
|
@context = context
|
8
8
|
@klass = klass
|
9
9
|
@method_name = method_name
|
10
10
|
@implementation = implementation
|
11
|
+
@instance_adaptation=instance_adapatation
|
11
12
|
|
13
|
+
check_validity
|
12
14
|
# Save the source location if any, this is used to find again the adaptation
|
13
15
|
# in a ctxt_proceed call. It always exists except for method directly
|
14
16
|
# implemented in C -> Not a problem because these one never use ctxt_proceed
|
@@ -31,9 +33,7 @@ class Phenomenal::Adaptation
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
#
|
35
|
-
#TODO we are forced to keep unBoundMethod bind code,
|
36
|
-
# so allow user to use unbound meth?
|
36
|
+
# IMPROVE check for better implementation
|
37
37
|
# Bind the implementation corresponding to this adaptation to 'instance' when
|
38
38
|
# instance_method or to implementation klass when class method
|
39
39
|
def bind(instance,*args,&block)
|
@@ -52,18 +52,28 @@ class Phenomenal::Adaptation
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
# True if the adapted method is an instance method
|
56
|
-
def instance_adaptation?
|
57
|
-
klass.instance_methods.include?(method_name)
|
58
|
-
end
|
59
|
-
|
60
55
|
#True if the adaptation concern the class n_klass and method n_method
|
61
|
-
def concern?(klass,method_name)
|
62
|
-
self.klass==klass &&
|
56
|
+
def concern?(klass,method_name,instance_adaptation)
|
57
|
+
self.klass==klass &&
|
58
|
+
self.method_name==method_name &&
|
59
|
+
self.instance_adaptation==instance_adaptation
|
63
60
|
end
|
64
61
|
|
62
|
+
alias_method :instance_adaptation?, :instance_adaptation
|
63
|
+
|
65
64
|
# String representation
|
66
65
|
def to_s
|
67
66
|
":#{context.name} => #{klass.name}.#{method_name} :: #{src_file}:#{src_line}"
|
68
67
|
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def check_validity
|
71
|
+
if klass.instance_methods.include?(method_name) && !instance_adaptation? ||
|
72
|
+
!klass.instance_methods.include?(method_name) && instance_adaptation?
|
73
|
+
Phenomenal::Logger.instance.error(
|
74
|
+
"Illegal adaptation for context: #{context}" +
|
75
|
+
" for #{klass.name}.#{method_name}, type mismatch"
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
69
79
|
end
|
data/lib/phenomenal/context.rb
CHANGED
@@ -1,47 +1,72 @@
|
|
1
1
|
# Represent a first class context
|
2
2
|
class Phenomenal::Context
|
3
|
+
include Phenomenal::ContextRelationships
|
3
4
|
@@total_activations = 0
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def self.create(context,*contexts,nested,closest_feature,&block)
|
6
|
+
manager = Phenomenal::Manager.instance
|
7
|
+
contexts.insert(0,context)
|
8
|
+
if contexts.length==1
|
9
|
+
if !manager.context_defined?(context)
|
10
|
+
context = self.new(context)
|
11
|
+
else
|
12
|
+
context = manager.find_context(context)
|
13
|
+
if !context.instance_of?(self)
|
14
|
+
Phenomenal::Logger.instance.error(
|
15
|
+
"Only #{self.name} can be used with this keyword"
|
16
|
+
)
|
17
|
+
end
|
9
18
|
end
|
10
|
-
context.add_adaptations(&block)
|
11
19
|
else #Combined contexts
|
12
|
-
#
|
20
|
+
if !manager.context_defined?(*contexts) # New combined context
|
21
|
+
context = self.new
|
22
|
+
context.parent=closest_feature # Set parent
|
23
|
+
instances = Array.new
|
24
|
+
first = contexts.first
|
25
|
+
contexts.each do |c|
|
26
|
+
# Use the object instance if already available
|
27
|
+
# otherwise create it
|
28
|
+
if manager.context_defined?(c)
|
29
|
+
c = manager.find_context(c)
|
30
|
+
if !nested && c!=first && !c.instance_of?(self)
|
31
|
+
Phenomenal::Logger.instance.error(
|
32
|
+
"Only #{self.name} can be used with this keyword"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
c = self.new(c)
|
37
|
+
end
|
38
|
+
instances.push(c)
|
39
|
+
manager.shared_contexts[c]= Array.new if !manager.shared_contexts[c]
|
40
|
+
manager.shared_contexts[c].push(context)
|
41
|
+
end
|
42
|
+
manager.combined_contexts[context] = instances
|
43
|
+
else
|
44
|
+
context = manager.find_context(*contexts)
|
45
|
+
end
|
13
46
|
end
|
47
|
+
context.add_adaptations(&block)
|
48
|
+
context
|
14
49
|
end
|
15
50
|
|
16
|
-
|
17
|
-
|
18
|
-
context.persistent=true
|
19
|
-
end
|
20
|
-
|
21
|
-
attr_accessor :activation_age, :activation_frequency, :priority, :adaptations,
|
22
|
-
:activation_count, :persistent, :required, :implied, :guessed, :is_implied, :is_required
|
51
|
+
attr_accessor :activation_age, :activation_frequency, :adaptations,
|
52
|
+
:activation_count, :parent,:forgotten
|
23
53
|
attr_reader :manager,:name
|
24
54
|
|
25
|
-
def initialize(name=nil,
|
26
|
-
@persistent = persistent
|
55
|
+
def initialize(name=nil, manager=nil)
|
27
56
|
@manager = manager || Phenomenal::Manager.instance
|
28
57
|
@name = name
|
29
|
-
@priority = priority
|
30
58
|
@activation_age = 0
|
31
59
|
@activation_count = 0
|
32
60
|
@adaptations = Array.new
|
33
61
|
@manager.register_context(self)
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@required = Array.new
|
37
|
-
@is_required = {}
|
38
|
-
@guessed = Array.new
|
62
|
+
@parent=nil
|
63
|
+
@forgotten=false
|
39
64
|
end
|
40
65
|
|
41
66
|
# Unregister the context from the context manager,
|
42
67
|
# This context shoudn't be used after.
|
43
68
|
# The context has to be inactive before being forgetted
|
44
|
-
# TODO
|
69
|
+
# TODO handle relationships references
|
45
70
|
def forget
|
46
71
|
if active?
|
47
72
|
Phenomenal::Logger.instance.error(
|
@@ -49,24 +74,26 @@ class Phenomenal::Context
|
|
49
74
|
)
|
50
75
|
else
|
51
76
|
manager.unregister_context(self)
|
77
|
+
forgotten=true
|
52
78
|
end
|
53
79
|
end
|
54
80
|
|
55
81
|
# Add a new method adaptation to the context
|
56
82
|
# Return the adaptation just created
|
57
|
-
def add_adaptation(klass, method_name,umeth=nil, &implementation)
|
83
|
+
def add_adaptation(klass, method_name,instance,umeth=nil, &implementation)
|
58
84
|
if umeth
|
59
85
|
implementation = umeth
|
86
|
+
instance = klass.instance_methods.include?(method_name)
|
60
87
|
end
|
61
|
-
if adaptations.find{ |i| i.concern?(klass,method_name) }
|
88
|
+
if adaptations.find{ |i| i.concern?(klass,method_name,instance) }
|
62
89
|
Phenomenal::Logger.instance.error(
|
63
90
|
"Error: Illegal duplicated adaptation in context: #{self} for " +
|
64
91
|
"#{klass.name}:#{method_name}"
|
65
92
|
)
|
66
93
|
else
|
67
|
-
if klass.instance_methods.include?(method_name)
|
94
|
+
if klass.instance_methods.include?(method_name) && instance
|
68
95
|
method = klass.instance_method(method_name)
|
69
|
-
elsif klass.methods.include?(method_name)
|
96
|
+
elsif klass.methods.include?(method_name) && !instance
|
70
97
|
method = klass.method(method_name)
|
71
98
|
else
|
72
99
|
Phenomenal::Logger.instance.error(
|
@@ -85,17 +112,31 @@ class Phenomenal::Context
|
|
85
112
|
end
|
86
113
|
|
87
114
|
adaptation = Phenomenal::Adaptation.new(
|
88
|
-
self, klass, method_name, implementation
|
115
|
+
self, klass, method_name,instance, implementation
|
89
116
|
)
|
90
117
|
adaptations.push(adaptation)
|
91
118
|
manager.register_adaptation(adaptation)
|
92
119
|
adaptation
|
93
120
|
end
|
94
121
|
end
|
122
|
+
|
123
|
+
# Catch nested context calls and transform them in nested contexts creation
|
124
|
+
def context(context,*contexts,&block)
|
125
|
+
check_validity
|
126
|
+
Phenomenal::Context.create(self,context,*contexts,true,self,&block)
|
127
|
+
end
|
128
|
+
alias_method :phen_context,:context
|
129
|
+
|
130
|
+
# Catch nested feature calls and transform them in nested contexts creation
|
131
|
+
def feature(feature,*features, &block)
|
132
|
+
check_validity
|
133
|
+
Phenomenal::Feature.create(self,feature,*features,true,self,&block)
|
134
|
+
end
|
135
|
+
alias_method :phen_feature,:feature
|
95
136
|
|
96
137
|
# Add multiple adaptations at definition time
|
97
138
|
def add_adaptations(&block)
|
98
|
-
instance_eval(&block)
|
139
|
+
instance_eval(&block) if block
|
99
140
|
end
|
100
141
|
|
101
142
|
# Set the current adapted class for the next adapt calls
|
@@ -105,41 +146,18 @@ class Phenomenal::Context
|
|
105
146
|
|
106
147
|
# Adapt a method for @current_adapted_class
|
107
148
|
def adapt(method,&block)
|
108
|
-
add_adaptation(@current_adapted_class,method,&block)
|
149
|
+
add_adaptation(@current_adapted_class,method,true,&block)
|
109
150
|
end
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
args.each do |context|
|
115
|
-
if !required.include?(context)
|
116
|
-
required.push(context)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Implies
|
122
|
-
def implies(*args)
|
123
|
-
args.each do |context|
|
124
|
-
if !implies.include?(context)
|
125
|
-
implied.push(context)
|
126
|
-
end
|
127
|
-
end
|
151
|
+
|
152
|
+
# Adapt a method for @current_adapted_class
|
153
|
+
def adapt_klass(method,&block)
|
154
|
+
add_adaptation(@current_adapted_class,method,false,&block)
|
128
155
|
end
|
129
156
|
|
130
|
-
# Guess
|
131
|
-
def guess(*args)
|
132
|
-
args.each do |context|
|
133
|
-
if !guessed.include?(context)
|
134
|
-
guessed.push(context)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
157
|
# Remove a method adaptation from the context
|
140
|
-
def remove_adaptation(klass,method_name)
|
158
|
+
def remove_adaptation(klass,method_name,instance)
|
141
159
|
adaptation_index =
|
142
|
-
adaptations.find_index{ |i| i.concern?(klass, method_name) }
|
160
|
+
adaptations.find_index{ |i| i.concern?(klass, method_name,instance) }
|
143
161
|
if !adaptation_index
|
144
162
|
Phenomenal::Logger.instance.error(
|
145
163
|
"Error: Illegal deleting of an inexistent adaptation in context: " +
|
@@ -153,75 +171,20 @@ class Phenomenal::Context
|
|
153
171
|
|
154
172
|
# Activate the context
|
155
173
|
def activate
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
# activate_guessed
|
160
|
-
@@total_activations = @@total_activations+1
|
161
|
-
self.activation_age = @@total_activations
|
174
|
+
check_validity
|
175
|
+
@@total_activations +=1
|
176
|
+
self.activation_age =@@total_activations
|
162
177
|
self.activation_count = self.activation_count+1
|
163
178
|
manager.activate_context(self)
|
164
179
|
self
|
165
|
-
end
|
166
|
-
|
167
|
-
def check_required
|
168
|
-
required.each do |context_name|
|
169
|
-
context = manager.find_context(context_name)
|
170
|
-
if !context.active?
|
171
|
-
context.is_required[self.__id__] = self
|
172
|
-
Phenomenal::Logger.instance.error(
|
173
|
-
"Error: Required context #{context_name} not active."
|
174
|
-
)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def activate_implicated
|
180
|
-
activated = Array.new
|
181
|
-
begin
|
182
|
-
implicated.each do |context_name|
|
183
|
-
context = manager.find_context(context_name)
|
184
|
-
if !context.active?
|
185
|
-
context.activate
|
186
|
-
context.is_implied[self.__id__] = self
|
187
|
-
activated.push
|
188
|
-
end
|
189
|
-
end
|
190
|
-
rescue PhenomenalError => error
|
191
|
-
activated.reverse.each do |context|
|
192
|
-
context.deactivate
|
193
|
-
end
|
194
|
-
Phenomenal::Logger.instance.error(
|
195
|
-
"Error: Implication not satisfied for context #{name} : \n"+error
|
196
|
-
)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def activate_guessed
|
201
|
-
begin
|
202
|
-
implicated.each do |context_name|
|
203
|
-
context = manager.find_context(context_name)
|
204
|
-
if !context.active?
|
205
|
-
context.activate
|
206
|
-
end
|
207
|
-
end
|
208
|
-
rescue PhenomenalError # Don't care of error in case of guess
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
def can_activate
|
215
|
-
#TODO for exclude relations
|
216
|
-
true
|
217
|
-
end
|
218
|
-
|
219
|
-
|
180
|
+
end
|
220
181
|
|
221
182
|
# Deactivate the context
|
222
|
-
def deactivate
|
183
|
+
def deactivate(caller_context=nil)
|
184
|
+
check_validity
|
223
185
|
was_active = active?
|
224
186
|
if self.activation_count>0
|
187
|
+
#Deactivation
|
225
188
|
self.activation_count = self.activation_count-1
|
226
189
|
end
|
227
190
|
if was_active && !active?
|
@@ -230,25 +193,26 @@ class Phenomenal::Context
|
|
230
193
|
self
|
231
194
|
end
|
232
195
|
|
233
|
-
def can_deactivate
|
234
|
-
#TODO for other relations
|
235
|
-
true
|
236
|
-
end
|
237
|
-
|
238
196
|
# True if the context is active
|
239
197
|
def active?
|
240
198
|
activation_count>0
|
241
199
|
end
|
242
200
|
|
201
|
+
# True if the context has just became active
|
202
|
+
def just_activated?
|
203
|
+
activation_count==1
|
204
|
+
end
|
205
|
+
|
206
|
+
# True if the context is anonymous
|
207
|
+
def anonymous?
|
208
|
+
name.nil?
|
209
|
+
end
|
210
|
+
|
243
211
|
# Return the activation age of the context:
|
244
212
|
# The age counter minus the age counter when the context was activated
|
245
213
|
# for the last time
|
246
214
|
def age
|
247
|
-
|
248
|
-
@@total_activations
|
249
|
-
else
|
250
|
-
@@total_activations-activation_age
|
251
|
-
end
|
215
|
+
@@total_activations-activation_age
|
252
216
|
end
|
253
217
|
|
254
218
|
# Return context informations:
|
@@ -256,21 +220,50 @@ class Phenomenal::Context
|
|
256
220
|
# - List of the adaptations
|
257
221
|
# - Active state
|
258
222
|
# - Activation age
|
259
|
-
|
223
|
+
# - Activation count
|
224
|
+
def information
|
260
225
|
{
|
261
226
|
:name=>name,
|
262
227
|
:adaptations=>adaptations,
|
263
228
|
:active=>active?,
|
264
|
-
:
|
229
|
+
:age=>age,
|
230
|
+
:activation_count=>activation_count,
|
231
|
+
:type=>self.class.name
|
265
232
|
}
|
266
233
|
end
|
267
234
|
|
235
|
+
# Return the closest parent feature of the context
|
236
|
+
def parent_feature
|
237
|
+
p = parent
|
238
|
+
while p!=nil && !p.is_a?(Phenomenal::Feature) do
|
239
|
+
p=p.parent
|
240
|
+
end
|
241
|
+
if p.nil?
|
242
|
+
manager.default_context
|
243
|
+
else
|
244
|
+
p
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
268
248
|
# String representation of the context
|
269
249
|
def to_s
|
270
250
|
if name
|
271
251
|
name.to_s
|
252
|
+
elsif self==manager.default_context
|
253
|
+
"'Default context'"
|
254
|
+
elsif manager.combined_contexts[self]
|
255
|
+
"'Combined context : #{manager.combined_contexts[self].flatten}'"
|
272
256
|
else
|
273
|
-
"Anonymous context"
|
257
|
+
"'Anonymous context'"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
def check_validity
|
263
|
+
if forgotten
|
264
|
+
Phenomenal::Logger.instance.error(
|
265
|
+
"Action not allowed anymore when context has been forgotten"
|
266
|
+
)
|
274
267
|
end
|
275
268
|
end
|
276
269
|
end
|