phenomenal 0.99.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Rakefile +1 -7
  2. data/lib/phenomenal.rb +10 -1
  3. data/lib/phenomenal/adaptation.rb +6 -8
  4. data/lib/phenomenal/conflict_policies.rb +2 -1
  5. data/lib/phenomenal/context.rb +36 -31
  6. data/lib/phenomenal/dsl.rb +14 -9
  7. data/lib/phenomenal/feature.rb +1 -0
  8. data/lib/phenomenal/manager.rb +10 -12
  9. data/lib/phenomenal/proc.rb +2 -2
  10. data/lib/phenomenal/relationships/context_relationships.rb +2 -0
  11. data/lib/phenomenal/relationships/dsl.rb +1 -0
  12. data/lib/phenomenal/relationships/feature_relationships.rb +2 -2
  13. data/lib/phenomenal/relationships/implication.rb +10 -4
  14. data/lib/phenomenal/relationships/relationship.rb +13 -0
  15. data/lib/phenomenal/relationships/relationships_manager.rb +1 -1
  16. data/lib/phenomenal/relationships/relationships_store.rb +13 -7
  17. data/lib/phenomenal/relationships/requirement.rb +5 -0
  18. data/lib/phenomenal/relationships/suggestion.rb +14 -6
  19. data/lib/phenomenal/version.rb +1 -1
  20. data/lib/phenomenal/viewer/dsl.rb +15 -0
  21. data/lib/phenomenal/viewer/graphical.rb +136 -0
  22. data/lib/phenomenal/viewer/textual.rb +36 -0
  23. data/spec/context_spec.rb +93 -27
  24. data/spec/dsl_spec.rb +0 -39
  25. data/spec/integration/adaptation_spec.rb +127 -0
  26. data/spec/integration/combined_contexts_spec.rb +48 -0
  27. data/spec/integration/composition_spec.rb +62 -0
  28. data/spec/integration/conflict_policy_spec.rb +143 -0
  29. data/spec/integration/open_context.rb +48 -0
  30. data/spec/{behavior → integration}/relationships_spec.rb +0 -6
  31. data/spec/manager_spec.rb +30 -19
  32. data/spec/relationships/context_relationships_spec.rb +23 -3
  33. data/spec/relationships/dsl_spec.rb +9 -3
  34. data/spec/relationships/feature_relationships_spec.rb +22 -3
  35. data/spec/relationships/relationship_spec.rb +13 -1
  36. data/spec/relationships/relationships_manager_spec.rb +0 -11
  37. data/spec/relationships/relationships_store_spec.rb +31 -7
  38. data/spec/spec_helper.rb +1 -0
  39. data/spec/test_classes.rb +3 -0
  40. metadata +16 -13
  41. data/spec/behavior/adaptation_spec.rb +0 -5
  42. data/spec/behavior/combined_contexts_spec.rb +0 -5
  43. data/spec/behavior/composition_spec.rb +0 -5
  44. data/spec/behavior/conflict_policy_spec.rb +0 -5
  45. data/spec/behavior/open_context.rb +0 -5
data/Rakefile CHANGED
@@ -1,11 +1,5 @@
1
- require 'rake/testtask'
2
-
3
- Rake::TestTask.new do |t|
4
- t.libs << 'test'
5
- end
6
-
7
1
  desc "Run tests"
8
- task :default => :test
2
+ task :default => :spec
9
3
 
10
4
  require 'rspec/core/rake_task'
11
5
  RSpec::Core::RakeTask.new('spec')
data/lib/phenomenal.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # Load the gem files in the system
2
- module Phenomenal end
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 ctxt_proceed call. It always exists except for method directly
16
- # implemented in C -> Not a problem because these one never use ctxt_proceed
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 check for better implementation
36
+ # IMPROVE try to find a better implementation
37
37
  # Bind the implementation corresponding to this adaptation to 'instance' when
38
- # instance_method or to implementation klass when class method
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
- "Error: Illegal duplicate adapation of #{adaptation1.to_s}"
12
+ "Illegal duplicate adapation of #{adaptation1.to_s}"
12
13
  )
13
14
  end
14
15
  end
@@ -1,7 +1,12 @@
1
- # Represent a first class context
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 shoudn't be used after.
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
- "Error: Illegal duplicated adaptation in context: #{self} for " +
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
- "Error: Illegal adaptation for context #{self},a method with "+
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
- "Error: Illegal adaptation for context #{self},the adaptation "+
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 adapt_klass(method,&block)
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
- "Error: Illegal deleting of an inexistent adaptation in context: " +
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
- # The age counter minus the age counter when the context was activated
213
- # for the last time
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
- # - Name
220
- # - List of the adaptations
221
- # - Active state
222
- # - Activation age
223
- # - Activation count
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
- "'Default context'"
258
+ "Default context"
254
259
  elsif manager.combined_contexts[self]
255
- "'Combined context : #{manager.combined_contexts[self].flatten}'"
260
+ "#{manager.combined_contexts[self].flatten}"
256
261
  else
257
- "'Anonymous context'"
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
@@ -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
- Phenomenal::Manager.instance.find_context(context).activate
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
- Phenomenal::Manager.instance.find_context(context).deactivate
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
@@ -1,3 +1,4 @@
1
+ # Represents a first class feature from context
1
2
  class Phenomenal::Feature < Phenomenal::Context
2
3
  include Phenomenal::FeatureRelationships
3
4
 
@@ -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 && !contexts.size==1
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 if the context isn't
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
- # ==== Private methods ==== #
153
+
156
154
  private
157
155
  def find_simple_context(context)
158
156
  find=nil
@@ -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
- # --> used for composition of instance methods adaptations
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
- # --> used for composition of class methods adaptations
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)}"
@@ -1,3 +1,5 @@
1
+ # Define the methods that can be called by a context to
2
+ # define relationships
1
3
  module Phenomenal::ContextRelationships
2
4
  def requires(context,*contexts)
3
5
  contexts = contexts.push(context)
@@ -1,3 +1,4 @@
1
+ # Define the DSL methods for relationships
1
2
  module Phenomenal::DSL
2
3
  def self.define_relationships(klass)
3
4
  klass.class_eval do