contextr 0.1.0 → 0.1.1

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.
@@ -0,0 +1,178 @@
1
+ require File.dirname(__FILE__) + "/test_helper.rb"
2
+
3
+ test_class(:TestLayerState)
4
+
5
+ # One of the most frequent examples for the use of aspect oriented programming
6
+ # is caching. You have a method, that takes some time to compute a result,
7
+ # but the result is always the same, if the parameters are the same. Such
8
+ # methods are often called functions. The have no side effects and do not depend
9
+ # on inner state.
10
+ #
11
+ # Perhaps you would like to attach caching to such functions. But only under
12
+ # certain circumstances. If your application lacks time, it is a good idea to
13
+ # activate result caching. If it lacks memory, it is a bad one.
14
+ #
15
+ # This perfectly sounds like a problem, that can be solved using
16
+ # context-oriented programming.
17
+ #
18
+ # But for caching, we need a place to store the computed results. One could
19
+ # store them in the instance that computes them, but this would be strange,
20
+ # because, the code residing in it does not make direct use of this state.
21
+ # Perhaps we are using variables, that other methods use for their own
22
+ # purpose and this would result in strange results. We could use cryptic
23
+ # instance variable names, but all this is just a workaround.
24
+ #
25
+ # What we want is to attach the state to the code, that uses it. This is the
26
+ # main idea of object-oriented design anyway. So ContextR gives you the
27
+ # opportunity to save state inside your method modules. Code and state stay
28
+ # side by side. This state will reside there even after deactivating the
29
+ # layer, so you can reuse it. Perfect for our caching approach.
30
+
31
+
32
+ # Fibonacci numbers
33
+ # =================
34
+
35
+ # We will use the good old Fibonacci numbers to demonstrate our approach. First
36
+ # the simple recursive computation, that becomes really slow for larger numbers.
37
+ # I know that there is a non-recursive algorithm, working faster, but this
38
+ # would not make such a good example. So just assume, that the following
39
+ # code is the fastest, you could possibly get.
40
+
41
+ module Fibonacci
42
+ module ClassMethods
43
+ def compute(fixnum)
44
+ if fixnum == 1 or fixnum == 0
45
+ fixnum
46
+ elsif fixnum < 0
47
+ raise ArgumentError, "Fibonacci not defined for negative numbers"
48
+ else
49
+ compute(fixnum - 1) + compute(fixnum - 2)
50
+ end
51
+ end
52
+ end
53
+ self.extend(ClassMethods)
54
+ end
55
+
56
+ example do
57
+ result_of(Fibonacci.compute(1)) == 1
58
+ result_of(Fibonacci.compute(2)) == 1
59
+ result_of(Fibonacci.compute(3)) == 2
60
+ end
61
+
62
+ # Just to make sure, that it is slow, I will try to compute Fib(100)
63
+
64
+ require 'timeout'
65
+ example do
66
+ timeout_raised = false
67
+ begin
68
+ Timeout::timeout(0.05) do
69
+ Fibonacci.compute(100)
70
+ end
71
+
72
+ rescue Timeout::Error
73
+ timeout_raised = true
74
+ end
75
+
76
+ result_of(timeout_raised) == true
77
+ end
78
+
79
+ # Okay, the 0.01 seconds are really impatient, but I know, that caching will
80
+ # come to rescue and makes it happen.
81
+ #
82
+ # Let's define a simple caching method. If I already know the result, return
83
+ # it, if not, let the base implementation compute it and save the it into
84
+ # our variable.
85
+
86
+ module Fibonacci
87
+ module ClassMethods
88
+ module CacheMethods
89
+ def cache
90
+ @cache ||= {}
91
+ end
92
+
93
+ def compute(fixnum)
94
+ cache[fixnum] ||= yield(:next, fixnum)
95
+ end
96
+ end
97
+ end
98
+
99
+ extend ClassMethods::CacheMethods => :cache
100
+ end
101
+
102
+ # If you are not familiar with the above syntax, to define context dependent
103
+ # behaviour, have a look at test_class_side.rb.
104
+ #
105
+ # Now let's compute Fib(100) again. Of course with caching enabled
106
+
107
+ example do
108
+ timeout_raised = false
109
+ begin
110
+ Timeout::timeout(0.05) do
111
+ ContextR::with_layer :cache do
112
+ result_of(Fibonacci.compute(100)) == 354_224_848_179_261_915_075
113
+ end
114
+ end
115
+
116
+ rescue Timeout::Error
117
+ timeout_raised = true
118
+ end
119
+
120
+ # This time the time out was not triggered
121
+ result_of(timeout_raised) == false
122
+ end
123
+
124
+ # It is that simple to add state to your method modules. And just to make sure,
125
+ # that I did not cheat, I will add a simple case, were instance variables and
126
+ # layer specific variables _would_ conflict, but in fact don't.
127
+
128
+ class LayerStateExample
129
+ attr_accessor :state
130
+ module StateMethods
131
+ attr_accessor :state
132
+ end
133
+
134
+ include StateMethods => :test_layer_state
135
+ end
136
+
137
+ # When StateMethods would be included normally, its attr_accessor would simply
138
+ # be the same as in the class. But this does not happen, when using layers.
139
+ #
140
+ # Let's do a little warm up and make sure, everything works as expected.
141
+
142
+ $layer_state_example = LayerStateExample.new
143
+
144
+ example do
145
+ $layer_state_example.state = true
146
+ result_of($layer_state_example.state) == true
147
+ $layer_state_example.state = false
148
+ result_of($layer_state_example.state) == false
149
+
150
+ ContextR::with_layer :test_layer_state do
151
+ $layer_state_example.state = true
152
+ result_of($layer_state_example.state) == true
153
+ $layer_state_example.state = false
154
+ result_of($layer_state_example.state) == false
155
+ end
156
+ end
157
+
158
+ # Until now, I did not prove anything. Let's try it.
159
+
160
+ example do
161
+ # Set the state
162
+ $layer_state_example.state = true
163
+ ContextR::with_layer :test_layer_state do
164
+ $layer_state_example.state = false
165
+ end
166
+
167
+ # And make sure, that they differ
168
+ result_of($layer_state_example.state) == true
169
+
170
+ ContextR::with_layer :test_layer_state do
171
+ result_of($layer_state_example.state) == false
172
+ end
173
+ end
174
+
175
+ # The last example was very theoretical and looks strange when seen isolated.
176
+ # Its main purpose is to show, that layer specific methods and base methods
177
+ # do not share their state, and that the layer specific state remains, also
178
+ # after layer deactivation. Don't take too serious.
@@ -0,0 +1,146 @@
1
+ require File.dirname(__FILE__) + "/test_helper.rb"
2
+
3
+ test_class(:TestOrdering)
4
+
5
+ # This document tries to demonstrate the invocation order within
6
+ # context-specific method calls. There are two cases where this is relevant:
7
+ # 1. There is more than one layer active, that extends the method.
8
+ # 2. There is more than one module in a single layer that extends the method.
9
+ #
10
+ # Unfortunately I could not find any relevant example that clearly demonstrates
11
+ # this behaviour. Therefore I will use foo bar code. But I think it is still
12
+ # easy to get the message.
13
+
14
+ # 1. Multiple active layers
15
+ # =========================
16
+
17
+ class OrderingTest
18
+ def test_method
19
+ "base_method"
20
+ end
21
+
22
+ module FooMethods
23
+ def test_method
24
+ "foo_before #{yield(:next)} foo_after"
25
+ end
26
+ end
27
+ module BarMethods
28
+ def test_method
29
+ "bar_before #{yield(:next)} bar_after"
30
+ end
31
+ end
32
+
33
+ include FooMethods => :foo
34
+ include BarMethods => :bar
35
+ end
36
+
37
+ # When multiple layers extend a single method, the order of activation
38
+ # determines the order of execution.
39
+
40
+ example do
41
+ instance = OrderingTest.new
42
+ result_of(instance.test_method) == "base_method"
43
+
44
+ ContextR::with_layer :foo do
45
+ result_of(instance.test_method) == "foo_before base_method foo_after"
46
+ end
47
+ ContextR::with_layer :bar do
48
+ result_of(instance.test_method) == "bar_before base_method bar_after"
49
+ end
50
+
51
+ ContextR::with_layer :bar, :foo do
52
+ result_of(instance.test_method) ==
53
+ "bar_before foo_before base_method foo_after bar_after"
54
+ end
55
+
56
+ ContextR::with_layer :bar do
57
+ ContextR::with_layer :foo do
58
+ result_of(instance.test_method) ==
59
+ "bar_before foo_before base_method foo_after bar_after"
60
+ end
61
+ end
62
+
63
+ ContextR::with_layer :foo, :bar do
64
+ result_of(instance.test_method) ==
65
+ "foo_before bar_before base_method bar_after foo_after"
66
+ end
67
+
68
+ ContextR::with_layer :foo do
69
+ ContextR::with_layer :bar do
70
+ result_of(instance.test_method) ==
71
+ "foo_before bar_before base_method bar_after foo_after"
72
+ end
73
+ end
74
+ end
75
+
76
+ # As you can see, the innermost layer activation provides the innermost method
77
+ # definition. It is not important, whether the layers were activated at once
78
+ # or one after the other.
79
+ #
80
+ # Activating and already active layer may update the execution order. The outer
81
+ # activation is hidden, but is restored again after leaving the block.
82
+
83
+ example do
84
+ instance = OrderingTest.new
85
+
86
+ ContextR::with_layer :bar, :foo do
87
+ result_of(instance.test_method) ==
88
+ "bar_before foo_before base_method foo_after bar_after"
89
+
90
+ ContextR::with_layer :bar do
91
+ result_of(instance.test_method) ==
92
+ "foo_before bar_before base_method bar_after foo_after"
93
+ end
94
+
95
+ result_of(instance.test_method) ==
96
+ "bar_before foo_before base_method foo_after bar_after"
97
+ end
98
+ end
99
+
100
+
101
+ # Multiple modules per layer and class
102
+ # ====================================
103
+
104
+ # It is also possible to have more than one module define the context-dependent
105
+ # behaviour of a class. In this case it may also happen, that multiple modules
106
+ # extend the same method definition.
107
+ #
108
+ # In this case we can reuse or already defined class and modules. This time
109
+ # we include them into the same layer and see what happens.
110
+
111
+ class OrderingTest
112
+ include FooMethods => :foo_bar
113
+ include BarMethods => :foo_bar
114
+ end
115
+
116
+ example do
117
+ instance = OrderingTest.new
118
+ result_of(instance.test_method) == "base_method"
119
+
120
+ ContextR::with_layer :foo_bar do
121
+ result_of(instance.test_method) ==
122
+ "bar_before foo_before base_method foo_after bar_after"
123
+ end
124
+ end
125
+
126
+ # This time the last inclusion defines the outermost method. This can be again
127
+ # changed. After repeating an inclusion the ordering is updated.
128
+
129
+ example do
130
+ class OrderingTest
131
+ include FooMethods => :foo_bar
132
+ end
133
+
134
+ instance = OrderingTest.new
135
+ result_of(instance.test_method) == "base_method"
136
+
137
+ ContextR::with_layer :foo_bar do
138
+ result_of(instance.test_method) ==
139
+ "foo_before bar_before base_method bar_after foo_after"
140
+ end
141
+ end
142
+
143
+ # These should be all case where ordering matters. If you are aware of the
144
+ # two basic rules, everything should work as expected. In an ideal world the
145
+ # the execution order would not be important. But hey, this is not the ideal
146
+ # world, so you better know.
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: contextr
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2007-09-07 00:00:00 +02:00
6
+ version: 0.1.1
7
+ date: 2007-09-16 00:00:00 +02:00
8
8
  summary: The goal is to equip Ruby with an API to allow context-oriented programming.
9
9
  require_paths:
10
10
  - lib
@@ -25,17 +25,39 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
+ - |
29
+ -----BEGIN CERTIFICATE-----
30
+ MIIDODCCAiCgAwIBAgIBADANBgkqhkiG9w0BAQUFADBCMQ0wCwYDVQQDDARydWJ5
31
+ MR0wGwYKCZImiZPyLGQBGRYNc2NobWlkdHdpc3NlcjESMBAGCgmSJomT8ixkARkW
32
+ AmRlMB4XDTA3MDkxNjEwMzkyN1oXDTA4MDkxNTEwMzkyN1owQjENMAsGA1UEAwwE
33
+ cnVieTEdMBsGCgmSJomT8ixkARkWDXNjaG1pZHR3aXNzZXIxEjAQBgoJkiaJk/Is
34
+ ZAEZFgJkZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOzbl83o33wh
35
+ veLMQ2JyEstgOulisHRBFpfbF9LbdavuS/EdoOUeEPkziWL/ZI0jvUo0MGmQ/8Of
36
+ F9DJbvNbhDdg0bK7BMe4R/I3Wpu49mX+7pOsdlC44nzJkVG1DQ67qWp6jx1zvDRc
37
+ iCoXaQKnROtsx6bCavVvm4P7XLrAQvs7l+1Ke5KLkXRtJ9xJWtAyBLRFoM4e6DeT
38
+ Py0DsixF9Zb5Nrb7UvK0CN8m6dulsKXNRDVQLHkFa5Zg/BEb0RI93LPmeBt8KGIE
39
+ eYVjk+6z+py03D18xd9KsUhOB/0lC0a5vWSZIKfZnxf1uYY9TTZVqTGGUMFi1sD4
40
+ k2TYBqQVsS8CAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
41
+ BBYEFAeq4388e2PUlF/YeemzaY8neefWMA0GCSqGSIb3DQEBBQUAA4IBAQAArQQo
42
+ 3teQbCcYmZFTrdOzujMca6F4JVzTp+yTnOsp1/5hiBEUMc3GreCVAPh2tU9+IpuB
43
+ Lif+s5nLfYdI+JrpCHDzm+ecJEJ7u7gxidzUwEBPYpVuU32ALge7fuWWhQfi29JY
44
+ QwNZgIkGe34z3a2+r2GLBns/GY7t0Lomv6U2SvwLreLc8g7thr2hZfgEJidvcrJR
45
+ Q6amsFqY06NalH+I175Bp4y9rR7IArgNDS3I5Cly5/heVKK2SRm5Z+IACmKMxIXh
46
+ hzBK8YDsrjUvPNIJn9yl0LeEsZ5VhupI2OXr6Cqa/tVMJq0oiTsLfel3Tsb9Ph83
47
+ y3O9DT3o4BiyPe77
48
+ -----END CERTIFICATE-----
49
+
28
50
  post_install_message:
29
51
  authors:
30
52
  - Gregor Schmidt
31
53
  files:
54
+ - COPYING.txt
32
55
  - History.txt
33
- - License.txt
56
+ - LICENSE.txt
34
57
  - Manifest.txt
35
58
  - README.txt
36
59
  - Rakefile
37
- - examples/general.rb
38
- - examples/ordering.rb
60
+ - examples/README
39
61
  - lib/contextr.rb
40
62
  - lib/contextr/class_methods.rb
41
63
  - lib/contextr/core_ext.rb
@@ -54,25 +76,30 @@ files:
54
76
  - spec/contextr_spec.rb
55
77
  - spec/spec.opts
56
78
  - spec/spec_helper.rb
79
+ - test/test_class_side.rb
57
80
  - test/test_contextr.rb
81
+ - test/test_dynamics.rb
58
82
  - test/test_helper.rb
59
- - website/index.html
60
- - website/index.txt
61
- - website/javascripts/rounded_corners_lite.inc.js
62
- - website/stylesheets/screen.css
63
- - website/template.rhtml
83
+ - test/test_introduction.rb
84
+ - test/test_layer_state.rb
85
+ - test/test_ordering.rb
64
86
  test_files:
87
+ - test/test_class_side.rb
65
88
  - test/test_contextr.rb
89
+ - test/test_dynamics.rb
66
90
  - test/test_helper.rb
91
+ - test/test_introduction.rb
92
+ - test/test_layer_state.rb
93
+ - test/test_ordering.rb
67
94
  rdoc_options:
68
95
  - --main
69
96
  - README.txt
70
97
  extra_rdoc_files:
98
+ - COPYING.txt
71
99
  - History.txt
72
- - License.txt
100
+ - LICENSE.txt
73
101
  - Manifest.txt
74
102
  - README.txt
75
- - website/index.txt
76
103
  executables: []
77
104
 
78
105
  extensions: []
metadata.gz.sig ADDED
Binary file
data/License.txt DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2007 FIXME full name
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/examples/general.rb DELETED
@@ -1,152 +0,0 @@
1
- require File.dirname(__FILE__) + "/../lib/contextr.rb"
2
-
3
- module Common
4
- module AddressMethods
5
- def to_s
6
- [yield(:next), yield(:receiver).address].join("; ")
7
- end
8
- end
9
- end
10
-
11
- class Student < Struct.new(:first_name, :last_name, :address, :university)
12
- def to_s
13
- [first_name, last_name].join(" ")
14
- end
15
-
16
- module EducationMethods
17
- def to_s
18
- [yield(:next), yield(:receiver).university].join("; ")
19
- end
20
- end
21
-
22
- include Common
23
- include EducationMethods => :education
24
- include AddressMethods => :address
25
- end
26
-
27
- class University < Struct.new(:name, :address)
28
- def to_s
29
- name
30
- end
31
-
32
- include Common
33
- include AddressMethods => :address
34
- end
35
-
36
- module Fibonacci
37
- module ClassMethods
38
- def compute(fixnum)
39
- if fixnum == 1 or fixnum == 0
40
- fixnum
41
- elsif fixnum < 0
42
- raise ArgumentError, "Fibonacci not defined for negative numbers"
43
- else
44
- compute(fixnum - 1) + compute(fixnum - 2)
45
- end
46
- end
47
-
48
- module LoggingMethods
49
- def compute(fixnum)
50
- print "."
51
- yield(:next, fixnum)
52
- end
53
- end
54
-
55
- module CacheMethods
56
- def cache
57
- @cache ||={}
58
- end
59
-
60
- def compute(fixnum)
61
- cache[fixnum] ||= yield(:next, fixnum)
62
- end
63
- end
64
- end
65
-
66
- self.extend(ClassMethods)
67
- include :class_side => true,
68
- ClassMethods::CacheMethods => :cache,
69
- ClassMethods::LoggingMethods => :logging
70
- end
71
-
72
- puts
73
- puts "Example on Instance Side"
74
- module Example
75
- module ClassMethods
76
- def show
77
- me = Student.new("Gregor", "Schmidt", "Berlin")
78
- hpi = University.new("HPI", "Potsdam")
79
- me.university = hpi
80
- print(me)
81
- end
82
-
83
- def print(me)
84
- puts me
85
- with_addresses do
86
- puts me
87
- end
88
- with_education do
89
- puts me
90
- end
91
- with_education do
92
- with_addresses do
93
- puts me
94
- end
95
- end
96
- end
97
-
98
- def with_addresses
99
- ContextR::with_layers :address do
100
- yield
101
- end
102
- end
103
-
104
- def with_education
105
- ContextR::with_layers :education do
106
- yield
107
- end
108
- end
109
- end
110
- self.extend(ClassMethods)
111
- end
112
- Example.show
113
-
114
- puts
115
- puts "Example with changed instance methods after behaviour registration"
116
- class Student
117
- def to_s
118
- [first_name, last_name].reverse.join(", ")
119
- end
120
- end
121
- Example.show
122
-
123
- puts
124
- puts "Example with changed behaviour methods after their registration"
125
- module Student::EducationMethods
126
- def to_s
127
- [yield(:next), yield(:receiver).university].join("\n - ")
128
- end
129
- end
130
- Example.show
131
-
132
- puts
133
- puts "Example on Class Side"
134
- class ExampleClassSide
135
- module ClassMethods
136
- def show(int = 3)
137
- puts Fibonacci.compute(int)
138
- ContextR::with_layers :cache do
139
- puts Fibonacci.compute(int)
140
- end
141
- ContextR::with_layers :logging do
142
- puts Fibonacci.compute(2 * int)
143
-
144
- ContextR::with_layers :cache do
145
- puts Fibonacci.compute(2 * int)
146
- end
147
- end
148
- end
149
- end
150
- self.extend(ClassMethods)
151
- end
152
- ExampleClassSide.show