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.
- data.tar.gz.sig +0 -0
- data/History.txt +8 -0
- data/Manifest.txt +12 -17
- data/README.txt +0 -1
- data/examples/employer.rb +229 -0
- data/examples/node.rb +213 -0
- data/lib/contextr/class_methods.rb +14 -6
- data/lib/contextr/core_ext/module.rb +3 -2
- data/lib/contextr/event_machine.rb +12 -12
- data/lib/contextr/inner_class.rb +7 -3
- data/lib/contextr/layer.rb +17 -7
- data/lib/contextr/public_api.rb +44 -3
- data/lib/contextr/version.rb +3 -3
- data/lib/ext/active_support_subset.rb +0 -45
- data/spec/contextr_spec.rb +97 -7
- data/test/{test_class_side.mkd → class_side.mkd} +6 -13
- data/test/{test_dynamic_scope.mkd → dynamic_scope.mkd} +2 -2
- data/test/{test_dynamics.mkd → dynamics.mkd} +3 -3
- data/test/{test_hello_world.mkd → hello_world.mkd} +1 -1
- data/test/{test_introduction.mkd → introduction.mkd} +4 -4
- data/test/{test_layer_state.mkd → layer_state.mkd} +2 -2
- data/test/lib/example_test.rb +2 -2
- data/test/lib/literate_maruku_test.rb +11 -9
- data/test/{test_meta_api.mkd → meta_api.mkd} +1 -1
- data/test/method_missing.mkd +40 -0
- data/test/{test_ordering.mkd → ordering.mkd} +8 -8
- data/test/restrictions.mkd +177 -0
- data/test/test_contextr.rb +7 -0
- data/test/test_plain.rb +92 -0
- metadata +17 -29
- metadata.gz.sig +0 -0
- data/test/lib/literate_markaby_test.rb +0 -97
- data/test/test_class_side.rb +0 -4
- data/test/test_dynamic_scope.rb +0 -4
- data/test/test_dynamics.rb +0 -4
- data/test/test_hello_world.rb +0 -4
- data/test/test_introduction.rb +0 -4
- data/test/test_layer_state.rb +0 -3
- data/test/test_meta_api.rb +0 -4
- 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 |
|
13
|
-
|
14
|
-
|
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 |
|
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
|
53
|
-
|
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
|
-
|
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.
|
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
|
data/lib/contextr/inner_class.rb
CHANGED
@@ -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 !~ /^(__|
|
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(
|
44
|
-
|
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
|
data/lib/contextr/layer.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
module ContextR # :nodoc:
|
2
|
-
class Layer
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
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,
|
data/lib/contextr/public_api.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/contextr/version.rb
CHANGED
@@ -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,
|
data/spec/contextr_spec.rb
CHANGED
@@ -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 :
|
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 :
|
87
|
-
ContextR::with_layers :
|
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 :
|
92
|
-
ContextR::with_layers :
|
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 == [:
|
119
|
+
ContextR::active_layers.should == [:address, :education]
|
101
120
|
ContextR::with_layer :education do
|
102
|
-
ContextR::active_layers.should == [:
|
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
|
148
|
-
|
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
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
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,
|