contextr 0.1.9 → 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 (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,