contextr 0.1.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +8 -0
  3. data/Manifest.txt +12 -17
  4. data/README.txt +0 -1
  5. data/examples/employer.rb +229 -0
  6. data/examples/node.rb +213 -0
  7. data/lib/contextr/class_methods.rb +14 -6
  8. data/lib/contextr/core_ext/module.rb +3 -2
  9. data/lib/contextr/event_machine.rb +12 -12
  10. data/lib/contextr/inner_class.rb +7 -3
  11. data/lib/contextr/layer.rb +17 -7
  12. data/lib/contextr/public_api.rb +44 -3
  13. data/lib/contextr/version.rb +3 -3
  14. data/lib/ext/active_support_subset.rb +0 -45
  15. data/spec/contextr_spec.rb +97 -7
  16. data/test/{test_class_side.mkd → class_side.mkd} +6 -13
  17. data/test/{test_dynamic_scope.mkd → dynamic_scope.mkd} +2 -2
  18. data/test/{test_dynamics.mkd → dynamics.mkd} +3 -3
  19. data/test/{test_hello_world.mkd → hello_world.mkd} +1 -1
  20. data/test/{test_introduction.mkd → introduction.mkd} +4 -4
  21. data/test/{test_layer_state.mkd → layer_state.mkd} +2 -2
  22. data/test/lib/example_test.rb +2 -2
  23. data/test/lib/literate_maruku_test.rb +11 -9
  24. data/test/{test_meta_api.mkd → meta_api.mkd} +1 -1
  25. data/test/method_missing.mkd +40 -0
  26. data/test/{test_ordering.mkd → ordering.mkd} +8 -8
  27. data/test/restrictions.mkd +177 -0
  28. data/test/test_contextr.rb +7 -0
  29. data/test/test_plain.rb +92 -0
  30. metadata +17 -29
  31. metadata.gz.sig +0 -0
  32. data/test/lib/literate_markaby_test.rb +0 -97
  33. data/test/test_class_side.rb +0 -4
  34. data/test/test_dynamic_scope.rb +0 -4
  35. data/test/test_dynamics.rb +0 -4
  36. data/test/test_hello_world.rb +0 -4
  37. data/test/test_introduction.rb +0 -4
  38. data/test/test_layer_state.rb +0 -3
  39. data/test/test_meta_api.rb +0 -4
  40. data/test/test_ordering.rb +0 -3
@@ -9,9 +9,9 @@ module ContextR # :nodoc:
9
9
  end
10
10
 
11
11
  def stored_module_definitions
12
- @stored_module_definitions ||= Hash.new do |hash, key|
13
- hash[key] = Hash.new do |hash, key|
14
- hash[key] = Module.new
12
+ @stored_module_definitions ||= Hash.new do |layer_hash, layer|
13
+ layer_hash[layer] = Hash.new do |extended_modules_hash, x_module|
14
+ extended_modules_hash[x_module] = Module.new
15
15
  end
16
16
  end
17
17
  end
@@ -40,17 +40,25 @@ module ContextR # :nodoc:
40
40
  if stack.size == 1
41
41
  stack.pop.call(*arguments, &block)
42
42
  else
43
- stack.pop.__send__(method_name, *arguments) do | action, *rest_args |
43
+ stack.pop.__send__(method_name, *arguments) do |action, *rest_args|
44
44
  case action
45
45
  when :receiver
46
46
  receiver
47
47
  when :block
48
+ block
49
+ when :block!
48
50
  block.call(*rest_args)
51
+ when :block=
52
+ block = rest_args.first
53
+ when :block_given?
54
+ !block.nil?
49
55
  when :next
56
+ rest_args.shift unless method_name == :method_missing
50
57
  call_methods_stack(stack, receiver, method_name, rest_args, block)
51
58
  else
52
- raise ArgumentError.new("Use only :receiver, :block or :next " +
53
- "as first argument.")
59
+ raise ArgumentError, "Use only :receiver, :block, :block_given?, " +
60
+ ":block!, :block=, or :next " +
61
+ "as first argument."
54
62
  end
55
63
  end
56
64
  end
@@ -27,10 +27,11 @@ class Module
27
27
  def in_layer(layer_symbol, &block)
28
28
  extension = ContextR::stored_module_definitions[layer_symbol][self]
29
29
 
30
- extension.module_eval(&block) if block_given?
30
+ return_value = extension.module_eval(&block) if block_given?
31
31
 
32
32
  ContextR::layer_by_symbol(layer_symbol).add_method_collection(self,
33
33
  extension)
34
- extension
34
+
35
+ block_given? ? return_value : extension
35
36
  end
36
37
  end
@@ -52,20 +52,20 @@ module ContextR
52
52
  end
53
53
 
54
54
  def observe_method_added(modul)
55
- modul.class_eval(%Q{
56
- def self.method_added_with_contextr_listener(name)
57
- ContextR::EventMachine::on_method_added(self, name)
58
- method_added_without_contextr_listener(name)
59
- end
60
- unless self.methods.include? "method_added"
61
- def self.method_added(name); end
62
- end
63
- class << self
64
- alias_method_chain(:method_added, :contextr_listener)
65
- end
66
- }, __FILE__, __LINE__)
55
+ modul.extend(ContextRListener)
67
56
  end
68
57
  end
69
58
  self.extend(ClassMethods)
59
+
60
+ module ContextRListener
61
+ def method_added(name)
62
+ ContextR::EventMachine::on_method_added(self, name)
63
+ super
64
+ end
65
+ def include(modul)
66
+ modul.instance_method.each { |m| method_added(m) }
67
+ super
68
+ end
69
+ end
70
70
  end
71
71
  end
@@ -13,7 +13,7 @@ module ContextR
13
13
  # hide +instance_eval+ or any method beginning with "__".
14
14
  def hide(name)
15
15
  if instance_methods.include?(name.to_s) and
16
- name !~ /^(__|instance_eval)/
16
+ name !~ /^(__|extend)/
17
17
  @hidden_methods ||= {}
18
18
  @hidden_methods[name.to_sym] = instance_method(name)
19
19
  undef_method name
@@ -40,8 +40,12 @@ module ContextR
40
40
 
41
41
  instance_methods.each { |m| hide(m) }
42
42
 
43
- def method_missing(method_name, *rest_args)
44
- yield(:next, *rest_args)
43
+ def method_missing(*rest_args)
44
+ if block_given?
45
+ yield(:next, *rest_args)
46
+ else
47
+ super
48
+ end
45
49
  end
46
50
  end
47
51
  end
@@ -1,5 +1,17 @@
1
1
  module ContextR # :nodoc:
2
- class Layer # :nodoc: all
2
+ class Layer
3
+ def activated
4
+ nil
5
+ end
6
+ def deactivated
7
+ nil
8
+ end
9
+ def inspect
10
+ "ContextR::layer(:#{ContextR::symbol_by_layer(self)})"
11
+ end
12
+ alias_method :to_s, :inspect
13
+
14
+ # :nodoc: all
3
15
  def definitions
4
16
  @definitions ||= {}
5
17
  end
@@ -38,11 +50,9 @@ module ContextR # :nodoc:
38
50
 
39
51
  def context_proxy_for_module(receiver, methods_module)
40
52
  proxies[methods_module] ||= begin
41
- c = Class.new(ContextR::InnerClass)
42
- c.class_eval(%Q{
43
- include ObjectSpace._id2ref(#{methods_module.object_id})
44
- }, __FILE__, __LINE__)
45
- c.new
53
+ p = ContextR::InnerClass.new.extend(methods_module)
54
+ class << p; hide(:extend); end
55
+ p
46
56
  end
47
57
  end
48
58
 
@@ -70,7 +80,7 @@ module ContextR # :nodoc:
70
80
  version)
71
81
  end
72
82
 
73
- def register_callbacks(cclass, mmodule)
83
+ def register_callbacks(cclass, mmodule)
74
84
  {:on_wrapper_method_added => mmodule,
75
85
  :on_class_method_added => cclass }.each do | callback, klass |
76
86
  ContextR::EventMachine.register(self, callback,
@@ -17,8 +17,14 @@ module ContextR
17
17
  def with_layers(*layer_symbols, &block)
18
18
  layers = layer_symbols.collect do |layer_symbol|
19
19
  layer_by_symbol(layer_symbol)
20
- end
21
- layered_do(active_layers_as_classes - layers + layers, block)
20
+ end.reverse
21
+ layers_being_activated = layers - active_layers_as_classes
22
+ layers_being_activated.each { |l| l.activated }
23
+
24
+ return_value = layered_do(layers | active_layers_as_classes, block)
25
+
26
+ layers_being_activated.each { |l| l.deactivated }
27
+ return_value
22
28
  end
23
29
  alias with_layer with_layers
24
30
 
@@ -40,7 +46,13 @@ module ContextR
40
46
  layers = layer_symbols.collect do |layer_symbol|
41
47
  layer_by_symbol(layer_symbol)
42
48
  end
43
- layered_do(active_layers_as_classes - layers, block)
49
+ layers_being_deactivated = layers & active_layers_as_classes
50
+ layers_being_deactivated.each { |l| l.deactivated }
51
+
52
+ return_value = layered_do(active_layers_as_classes - layers, block)
53
+
54
+ layers_being_deactivated.each { |l| l.activated }
55
+ return_value
44
56
  end
45
57
  alias without_layer without_layers
46
58
 
@@ -54,6 +66,35 @@ module ContextR
54
66
  layers_as_classes.collect { |layer| symbol_by_layer(layer) }
55
67
  end
56
68
 
69
+ # returns the layer, defined by the given name. If passed a block, it will
70
+ # instance_eval it on the layer and return its value instead. The latter
71
+ # may be used to define the activated and deactived methods for a layer.
72
+ #
73
+ # ContextR::layer(:log) do
74
+ # def logger
75
+ # @logger ||= Logger.new
76
+ # end
77
+ # def activated
78
+ # logger.log("Logging active")
79
+ # end
80
+ # def deactivated
81
+ # logger.log("Logging inactive")
82
+ # end
83
+ # end
84
+ #
85
+ # # will call activated before executing the block
86
+ # # and deactivated afterwards
87
+ # ContextR::with_layer(:log) do
88
+ # 1 + 1
89
+ # end
90
+ #
91
+ def layer(name, &block)
92
+ if block_given?
93
+ layer_by_symbol(name).instance_eval(&block)
94
+ else
95
+ layer_by_symbol(name)
96
+ end
97
+ end
57
98
  end
58
99
  self.extend(PublicApi)
59
100
  end
@@ -1,8 +1,8 @@
1
1
  module ContextR #:nodoc:
2
2
  module VERSION #:nodoc:
3
- MAJOR = 0
4
- MINOR = 1
5
- TINY = 9
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,49 +1,4 @@
1
1
  unless Object.const_defined? "ActiveSupport"
2
- class Module
3
- # Encapsulates the common pattern of:
4
- #
5
- # alias_method :foo_without_feature, :foo
6
- # alias_method :foo, :foo_with_feature
7
- #
8
- # With this, you simply do:
9
- #
10
- # alias_method_chain :foo, :feature
11
- #
12
- # And both aliases are set up for you.
13
- #
14
- # Query and bang methods (foo?, foo!) keep the same punctuation:
15
- #
16
- # alias_method_chain :foo?, :feature
17
- #
18
- # is equivalent to
19
- #
20
- # alias_method :foo_without_feature?, :foo?
21
- # alias_method :foo?, :foo_with_feature?
22
- #
23
- # so you can safely chain foo, foo?, and foo! with the same feature.
24
- def alias_method_chain(target, feature)
25
- # Strip out punctuation on predicates or bang methods since
26
- # e.g. target?_without_feature is not a valid method name.
27
- aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
28
- yield(aliased_target, punctuation) if block_given?
29
-
30
- with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
31
- without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
32
-
33
- alias_method without_method, target
34
- alias_method target, with_method
35
-
36
- case
37
- when public_method_defined?(without_method)
38
- public target
39
- when protected_method_defined?(without_method)
40
- protected target
41
- when private_method_defined?(without_method)
42
- private target
43
- end
44
- end
45
- end
46
-
47
2
  # The Inflector transforms words from singular to plural, class names to
48
3
  # table names, modularized class names to ones without, and class names to
49
4
  # foreign keys. The default inflections for pluralization, singularization,
@@ -52,6 +52,25 @@ class Ordering
52
52
  end
53
53
  end
54
54
 
55
+ class ExceptionExample
56
+ def secure
57
+ insecure
58
+ rescue RuntimeError
59
+ "caught in secure method"
60
+ end
61
+
62
+ def insecure
63
+ raise "insecure action failed"
64
+ end
65
+
66
+ in_layer :security do
67
+ def insecure
68
+ super
69
+ rescue RuntimeError
70
+ "caught in security layer"
71
+ end
72
+ end
73
+ end
55
74
 
56
75
  describe "A contextified object" do
57
76
  before do
@@ -77,19 +96,19 @@ describe "A contextified object" do
77
96
  end
78
97
 
79
98
  it "should show specific behaviour down the whole stack for all layers" do
80
- ContextR::with_layers :education, :address do
99
+ ContextR::with_layers :address, :education do
81
100
  @student.to_s.should == "Gregor Schmidt (Berlin), HPI (Potsdam)"
82
101
  end
83
102
  end
84
103
 
85
104
  it "should take care of layer activation odering" do
86
- ContextR::with_layers :education do
87
- ContextR::with_layers :address do
105
+ ContextR::with_layers :address do
106
+ ContextR::with_layers :education do
88
107
  @student.to_s.should == "Gregor Schmidt (Berlin), HPI (Potsdam)"
89
108
  end
90
109
  end
91
- ContextR::with_layers :address do
92
- ContextR::with_layers :education do
110
+ ContextR::with_layers :education do
111
+ ContextR::with_layers :address do
93
112
  @student.to_s.should == "Gregor Schmidt, HPI (Potsdam) (Berlin)"
94
113
  end
95
114
  end
@@ -97,9 +116,9 @@ describe "A contextified object" do
97
116
 
98
117
  it "should avoid double activation, but update ordering" do
99
118
  ContextR::with_layers :education, :address do
100
- ContextR::active_layers.should == [:education, :address]
119
+ ContextR::active_layers.should == [:address, :education]
101
120
  ContextR::with_layer :education do
102
- ContextR::active_layers.should == [:address, :education]
121
+ ContextR::active_layers.should == [:education, :address]
103
122
  end
104
123
  end
105
124
  end
@@ -219,4 +238,75 @@ describe "ContextR" do
219
238
  ContextR::layers.sort_by{ |s| s.to_s }.should include(layer)
220
239
  end
221
240
  end
241
+
242
+ it "should return a layer on #layer(:name)" do
243
+ ContextR::layer(:log).should == ContextR::layer_by_symbol(:log)
244
+ end
245
+ it "should execute the block given to #layer(:name)" do
246
+ lambda do
247
+ ContextR::layer(:log) do
248
+ def moo
249
+ "moo"
250
+ end
251
+ end.should == nil
252
+ end.should_not raise_error
253
+
254
+ ContextR::layer(:log).moo == "moo"
255
+ end
256
+
257
+ it "should execute activated for all layers on activation" do
258
+ ContextR::layer(:log) do
259
+ def activated
260
+ raise "activated"
261
+ end
262
+ end
263
+
264
+ lambda do
265
+ ContextR::with_layer(:log) { 1 + 1 }
266
+ end.should raise_error(RuntimeError, "activated")
267
+
268
+ ContextR::layer(:log) do
269
+ def activated
270
+ nil
271
+ end
272
+ end
273
+ end
274
+
275
+ it "should execute deactivated for all layers on deactivation" do
276
+ ContextR::layer(:log) do
277
+ def deactivated
278
+ raise "deactivated"
279
+ end
280
+ end
281
+
282
+ lambda do
283
+ ContextR::with_layer(:log) { 1 + 1 }
284
+ end.should raise_error(RuntimeError, "deactivated")
285
+
286
+ ContextR::layer(:log) do
287
+ def deactivated
288
+ nil
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ describe "ContextR" do
295
+ it "should propagate exceptions into outer layers first" do
296
+
297
+ instance = ExceptionExample.new
298
+
299
+ instance.secure.should == "caught in secure method"
300
+
301
+ ContextR::with_layer :security do
302
+ instance.secure.should == "caught in security layer"
303
+ end
304
+ end
305
+ end
306
+
307
+ describe ContextR::Layer do
308
+ it "should give a nice, self-referencing output on inspect and to_s" do
309
+ eval(ContextR::layer(:log).inspect).should == ContextR::layer(:log)
310
+ eval(ContextR::layer(:log).to_s).should == ContextR::layer(:log)
311
+ end
222
312
  end
@@ -1,5 +1,3 @@
1
- **TODO: Update to use new in\_layer API**
2
-
3
1
  In Ruby there are multiple ways of defining behaviour on the class side. That
4
2
  are messages that are send to the class, not to the instance. Class side
5
3
  behaviour is often useful for functional methods, i.e. methods that do not
@@ -128,8 +126,6 @@ different methods of defining class side behaviour.
128
126
  Using `def self.method_name`
129
127
  ----------------------------
130
128
 
131
- **TODO: Allow def self.method\_name to add class side behaviour**
132
-
133
129
  Okay, we won't get rid of the modules, used to encapsulate the
134
130
  context-dependent behaviour, so the extension is a bit noisier, than the
135
131
  basic notation.
@@ -144,9 +140,8 @@ basic notation.
144
140
  end
145
141
  end
146
142
 
147
- But we can use the same principles, like we did for the instance side. Simply
148
- use a hash to tell extend, that the module should only be used in a certain
149
- layer.
143
+ But we can use the same principles, like we did for the instance side - just in
144
+ side the singleton class.
150
145
 
151
146
  example do
152
147
  result_of(SimpleMath.pi) == 3.14159265
@@ -170,9 +165,7 @@ manage the extension.
170
165
  "Euler's constant"
171
166
  end
172
167
  end
173
- end
174
168
 
175
- class << self
176
169
  in_layer :german do
177
170
  def e
178
171
  "Eulersche Zahl"
@@ -196,12 +189,12 @@ manage the extension.
196
189
  Using a module
197
190
  --------------
198
191
 
199
- Hey, this is what we did all the time, so it is only natural to have the same
200
- syntax to extend a class using in module for context-dependent behaviour. But
201
- for the sake of completeness, I will attach another example.
192
+ It is only natural to have the same syntax to extend a class using in module
193
+ for context-dependent behaviour. But for the sake of completeness, I will
194
+ attach another example.
202
195
 
203
196
  class SimpleMath
204
- class << self
197
+ module ClassMethods
205
198
  in_layer :exact_computation do
206
199
  def golden_ratio
207
200
  sleep(0.01) # In real life this would take a bit longer,