phenomenal 0.99.0 → 1.0.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 (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