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.
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