phenomenal 0.11.11.24.4 → 0.99.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 +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
|