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.
Files changed (51) hide show
  1. data/LICENSE +1 -1
  2. data/README +3 -4
  3. data/Rakefile +3 -0
  4. data/lib/phenomenal.rb +15 -2
  5. data/lib/phenomenal/adaptation.rb +22 -12
  6. data/lib/phenomenal/context.rb +127 -134
  7. data/lib/phenomenal/dsl.rb +41 -14
  8. data/lib/phenomenal/feature.rb +8 -0
  9. data/lib/phenomenal/logger.rb +0 -1
  10. data/lib/phenomenal/manager.rb +117 -35
  11. data/lib/phenomenal/relationships/context_relationships.rb +22 -0
  12. data/lib/phenomenal/relationships/dsl.rb +18 -0
  13. data/lib/phenomenal/relationships/feature_relationships.rb +42 -0
  14. data/lib/phenomenal/relationships/implication.rb +35 -0
  15. data/lib/phenomenal/relationships/relationship.rb +42 -0
  16. data/lib/phenomenal/relationships/relationships_manager.rb +63 -0
  17. data/lib/phenomenal/relationships/relationships_store.rb +73 -0
  18. data/lib/phenomenal/relationships/requirement.rb +26 -0
  19. data/lib/phenomenal/relationships/suggestion.rb +41 -0
  20. data/lib/phenomenal/version.rb +3 -0
  21. data/spec/adaptation_spec.rb +64 -0
  22. data/spec/behavior/adaptation_spec.rb +5 -0
  23. data/spec/behavior/combined_contexts_spec.rb +5 -0
  24. data/spec/behavior/composition_spec.rb +5 -0
  25. data/spec/behavior/conflict_policy_spec.rb +5 -0
  26. data/spec/behavior/open_context.rb +5 -0
  27. data/spec/behavior/relationships_spec.rb +249 -0
  28. data/spec/context_spec.rb +268 -0
  29. data/spec/dsl_spec.rb +181 -0
  30. data/spec/feature_spec.rb +5 -0
  31. data/spec/manager_spec.rb +84 -0
  32. data/spec/proc_spec.rb +20 -0
  33. data/spec/relationships/context_relationships_spec.rb +13 -0
  34. data/spec/relationships/dsl_spec.rb +13 -0
  35. data/spec/relationships/feature_relationships_spec.rb +13 -0
  36. data/spec/relationships/relationship_spec.rb +31 -0
  37. data/spec/relationships/relationships_manager_spec.rb +15 -0
  38. data/spec/relationships/relationships_store_spec.rb +19 -0
  39. data/spec/spec_helper.rb +18 -0
  40. data/{test → spec}/test_classes.rb +3 -0
  41. metadata +69 -24
  42. data/demo.rb +0 -24
  43. data/demo_age.rb +0 -89
  44. data/demo_dsl.rb +0 -28
  45. data/phenomenal-0.11.11.24.3.gem +0 -0
  46. data/phenomenal.gemspec +0 -15
  47. data/test/test_cop_adaptation.rb +0 -168
  48. data/test/test_cop_composition.rb +0 -84
  49. data/test/test_cop_conflictpolicy.rb +0 -177
  50. data/test/test_cop_infrastructure.rb +0 -129
  51. data/test_declaration.rb +0 -18
data/LICENSE CHANGED
@@ -23,5 +23,5 @@ Year : 2011-2012
23
23
  Students names,sections :
24
24
  - Poncelet Thibault (SINF22MS)
25
25
  - Vigneron Loic (SINF22MS)
26
- Date : May 1, 2011
26
+ Start date : May 1, 2011
27
27
 
data/README CHANGED
@@ -1,4 +1,3 @@
1
- Build gem command:
2
- gem build phenomenal.gemspec
3
- Test gem command:
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
@@ -6,3 +6,6 @@ end
6
6
 
7
7
  desc "Run tests"
8
8
  task :default => :test
9
+
10
+ require 'rspec/core/rake_task'
11
+ RSpec::Core::RakeTask.new('spec')
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, implementation)
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
- #TODO check for better implem
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 && self.method_name==method_name
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
@@ -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
- def self.create(*args,&block)
6
- if args.length==1
7
- if !Phenomenal::Manager.instance.context_defined?(args[0])
8
- context = Phenomenal::Context.new(args[0])
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
- #TODO
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
- def self.create_feature(*args,&block)
17
- context = self.create(*args,&block)
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, priority=nil,persistent=false,manager=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
- @implied = Array.new
35
- @is_implied = {}
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 Find a way to avoid the use of forgeted context (use forgeted flag?)
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
- # Requires
113
- def requires(*args)
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
- #TODO
157
- # check_required
158
- # activate_implicated
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
- if activation_age == 0
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
- def informations
223
+ # - Activation count
224
+ def information
260
225
  {
261
226
  :name=>name,
262
227
  :adaptations=>adaptations,
263
228
  :active=>active?,
264
- :activation_age=>age
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