contextr 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +2 -0
- data/COPYING.txt +340 -0
- data/History.txt +11 -0
- data/LICENSE.txt +58 -0
- data/Manifest.txt +8 -8
- data/README.txt +15 -2
- data/examples/README +9 -0
- data/lib/contextr/class_methods.rb +23 -4
- data/lib/contextr/core_ext/module.rb +48 -9
- data/lib/contextr/core_ext/object.rb +68 -1
- data/lib/contextr/event_machine.rb +1 -1
- data/lib/contextr/layer.rb +5 -3
- data/lib/contextr/modules/mutex_code.rb +1 -1
- data/lib/contextr/modules/unique_id.rb +1 -1
- data/lib/contextr/public_api.rb +17 -15
- data/lib/contextr/version.rb +1 -1
- data/lib/ext/active_support_subset.rb +86 -4
- data/lib/ext/dynamic.rb +2 -1
- data/spec/contextr_spec.rb +211 -6
- data/spec/spec_helper.rb +1 -0
- data/test/test_class_side.rb +225 -0
- data/test/test_contextr.rb +19 -11
- data/test/test_dynamics.rb +207 -0
- data/test/test_helper.rb +55 -0
- data/test/test_introduction.rb +311 -0
- data/test/test_layer_state.rb +178 -0
- data/test/test_ordering.rb +146 -0
- metadata +39 -12
- metadata.gz.sig +0 -0
- data/License.txt +0 -20
- data/examples/general.rb +0 -152
- data/examples/ordering.rb +0 -29
- data/website/index.html +0 -116
- data/website/index.txt +0 -61
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.rhtml +0 -48
data/README.txt
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
-
|
2
|
-
===================
|
1
|
+
= ContextR
|
3
2
|
|
3
|
+
A context-oriented programming library for Ruby.
|
4
|
+
|
5
|
+
Inspired by ContextL (Pascal Costanza) and ContextS (Robert Hirschfeld) with
|
6
|
+
thanks to Christian Neukirchen for giving the name and lots of ideas.
|
7
|
+
|
8
|
+
For more information see
|
9
|
+
- http://contextr.rubyforge.org/
|
10
|
+
- http://www.contextr.org/ or
|
11
|
+
- http://www.swa.hpi.uni-potsdam.de/cop/
|
12
|
+
|
13
|
+
This code is published under the same license as Ruby. See LICENSE.txt for more
|
14
|
+
information.
|
15
|
+
|
16
|
+
(c) 2007 - Gregor Schmidt - Berlin, Germany
|
data/examples/README
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
All documentation may be found in /test and /spec. The first contains written
|
2
|
+
english and some embedded usage examples, the latter a specification written in
|
3
|
+
RSpec.
|
4
|
+
|
5
|
+
This allows the documentation to be executable and testable. Therefore it is
|
6
|
+
more likely that is in sync with the current development of the libary. This
|
7
|
+
is all for your best.
|
8
|
+
|
9
|
+
So just have a look at the files in test and you will see what I mean.
|
@@ -1,5 +1,7 @@
|
|
1
|
-
module ContextR
|
2
|
-
module ClassMethods
|
1
|
+
module ContextR # :nodoc:
|
2
|
+
module ClassMethods # :nodoc:
|
3
|
+
include MutexCode
|
4
|
+
|
3
5
|
def const_missing(const_name)
|
4
6
|
if const_name.to_s =~ /.*Layer$/
|
5
7
|
self.const_set(const_name, Class.new(ContextR::Layer))
|
@@ -14,8 +16,22 @@ module ContextR
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
def active_layers_as_classes
|
20
|
+
Dynamic[:layers]
|
21
|
+
end
|
22
|
+
|
23
|
+
def layered_do(layers, block)
|
24
|
+
Dynamic.let({:layers => layers}, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def layers_as_classes
|
28
|
+
constants.select { |l| l =~ /.+Layer$/ }.collect { |l|
|
29
|
+
l.scan(/(.+)Layer/).first.first.underscore.to_sym
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
17
33
|
def symbol_by_layer(lay)
|
18
|
-
lay.to_s.gsub( /^ContextR::(.*)Layer$/, '\1' ).underscore
|
34
|
+
lay.to_s.gsub( /^ContextR::(.*)Layer$/, '\1' ).underscore.to_sym
|
19
35
|
end
|
20
36
|
|
21
37
|
def layer_by_symbol(sym)
|
@@ -45,7 +61,7 @@ module ContextR
|
|
45
61
|
def on_core_method_called(receiver, contextified_class,
|
46
62
|
method_name, arguments, block)
|
47
63
|
proxies = []
|
48
|
-
|
64
|
+
active_layers_as_classes.each do |layer|
|
49
65
|
proxies += layer.context_proxies(contextified_class, method_name)
|
50
66
|
end.compact
|
51
67
|
|
@@ -62,6 +78,9 @@ module ContextR
|
|
62
78
|
def observe_core_method(klass, method_name, version)
|
63
79
|
only_once do
|
64
80
|
klass.class_eval(%Q{
|
81
|
+
if self.instance_methods.include?("#{method_name}")
|
82
|
+
undef_method("#{method_name}")
|
83
|
+
end
|
65
84
|
def #{method_name}(*arguments, &block)
|
66
85
|
ContextR::on_core_method_called(
|
67
86
|
self,
|
@@ -1,18 +1,57 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
#--
|
2
|
+
# The aliasing of these methods is done in a class_eval block to avoid code
|
3
|
+
# documentation by RDoc.
|
4
|
+
#++
|
5
|
+
Module.class_eval do
|
6
|
+
alias_method :include_without_layers, :include
|
7
|
+
end
|
8
|
+
|
9
|
+
class Module
|
10
|
+
protected
|
11
|
+
def include_with_layers(associations) # :nodoc:
|
9
12
|
associations.each do | modul, layer |
|
10
|
-
ContextR::layer_by_symbol(layer).add_method_collection(
|
13
|
+
ContextR::layer_by_symbol(layer).add_method_collection(self, modul)
|
11
14
|
end
|
15
|
+
self
|
12
16
|
end
|
13
17
|
|
18
|
+
# call-seq:
|
19
|
+
# include(module, ...) => self
|
20
|
+
# include(module => layer_qualifier, ...) => self
|
21
|
+
#
|
22
|
+
# Invokes <code>Module.append_features</code> on each parameter in turn.
|
23
|
+
#
|
24
|
+
# If called with a hash, adds the module to the given layer. The behaviour
|
25
|
+
# is associated with the class side of the object.
|
26
|
+
#
|
27
|
+
# module Mod
|
28
|
+
# def name
|
29
|
+
# "Hello from #{yield(:next)}.\n"
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# class Klass
|
34
|
+
# def name
|
35
|
+
# "Klass"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# include Mod => :hello
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# k = Klass.new
|
42
|
+
# k.name #=> "Klass.\n"
|
43
|
+
# ContextR::with_layer :hello do
|
44
|
+
# k.name #=> "Hello from Klass.\n"
|
45
|
+
# end
|
46
|
+
# k.name #=> "Klass.\n"
|
47
|
+
#
|
14
48
|
def include(*args)
|
15
49
|
args.first.is_a?(Module) ? include_without_layers(*args) :
|
16
50
|
include_with_layers(*args)
|
17
51
|
end
|
18
52
|
end
|
53
|
+
|
54
|
+
Module.class_eval do
|
55
|
+
private :include
|
56
|
+
private :include_with_layers
|
57
|
+
end
|
@@ -1,5 +1,72 @@
|
|
1
|
+
#--
|
2
|
+
# The aliasing of these methods is done in a class_eval block to avoid code
|
3
|
+
# documentation by RDoc.
|
4
|
+
#++
|
5
|
+
Object.class_eval do
|
6
|
+
alias_method :extend_without_layers, :extend
|
7
|
+
end
|
8
|
+
|
1
9
|
class Object
|
2
|
-
def
|
10
|
+
def extend_with_layers(associations) # :nodoc:
|
11
|
+
klass = class << self; self; end
|
12
|
+
associations.each do | modul, layer |
|
13
|
+
ContextR::layer_by_symbol(layer).add_method_collection(klass, modul)
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# call-seq:
|
19
|
+
# obj.extend(module, ...) => obj
|
20
|
+
# obj.extend(module => layer_qualifier, ...) => obj
|
21
|
+
#
|
22
|
+
# Adds to _obj_ the instance methods from each module given as a
|
23
|
+
# parameter.
|
24
|
+
#
|
25
|
+
# module Mod
|
26
|
+
# def hello
|
27
|
+
# "Hello from Mod.\n"
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# class Klass
|
32
|
+
# def hello
|
33
|
+
# "Hello from Klass.\n"
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# k = Klass.new
|
38
|
+
# k.hello #=> "Hello from Klass.\n"
|
39
|
+
# k.extend(Mod) #=> #<Klass:0x401b3bc8>
|
40
|
+
# k.hello #=> "Hello from Mod.\n"
|
41
|
+
#
|
42
|
+
# If called with a hash, adds the module to the given layer. The behaviour
|
43
|
+
# is associated with the class side of the object.
|
44
|
+
#
|
45
|
+
# module Mod
|
46
|
+
# def name
|
47
|
+
# "Hello from #{yield(:next)}.\n"
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# class Klass
|
52
|
+
# def name
|
53
|
+
# "Klass"
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# k = Klass.new
|
58
|
+
# k.extend(Mod => :hello) #=> #<Klass:0x401b3bc8>
|
59
|
+
# k.name #=> "Klass.\n"
|
60
|
+
# ContextR::with_layer :hello do
|
61
|
+
# k.name #=> "Hello from Klass.\n"
|
62
|
+
# end
|
63
|
+
# k.name #=> "Klass.\n"
|
64
|
+
def extend(*args)
|
65
|
+
args.first.is_a?(Module) ? extend_without_layers(*args) :
|
66
|
+
extend_with_layers(*args)
|
67
|
+
end
|
68
|
+
|
69
|
+
def behavioural_class #:nodoc:
|
3
70
|
if self.kind_of?(Module)
|
4
71
|
class << self; self; end
|
5
72
|
else
|
data/lib/contextr/layer.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
module ContextR
|
2
|
-
class Layer
|
1
|
+
module ContextR # :nodoc:
|
2
|
+
class Layer # :nodoc: all
|
3
3
|
module ClassMethods
|
4
4
|
def definitions
|
5
5
|
@definitions ||= {}
|
@@ -10,7 +10,9 @@ module ContextR
|
|
10
10
|
|
11
11
|
def add_method_collection(contextified_class, methods_module)
|
12
12
|
definitions[contextified_class] ||= []
|
13
|
-
definitions[contextified_class]
|
13
|
+
definitions[contextified_class].delete(methods_module)
|
14
|
+
definitions[contextified_class].push(methods_module)
|
15
|
+
|
14
16
|
(methods_module.instance_methods &
|
15
17
|
contextified_class.instance_methods).each do | method_name |
|
16
18
|
replace_core_method(contextified_class, method_name, 0)
|
data/lib/contextr/public_api.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
module ContextR
|
2
|
-
module
|
3
|
-
include MutexCode
|
4
|
-
|
2
|
+
module PublicApi
|
5
3
|
# allows the explicit activation of layers within a block context
|
6
4
|
#
|
7
5
|
# ContextR::with_layers(:foo, :bar) do
|
8
|
-
# ContextR::
|
6
|
+
# ContextR::active_layers # => [:default, :foo, :bar]
|
9
7
|
#
|
10
8
|
# ContextR::with_layers(:baz) do
|
11
|
-
# ContextR::
|
9
|
+
# ContextR::active_layers # => [:default, :foo, :bar, :baz]
|
12
10
|
# end
|
13
11
|
#
|
14
12
|
# end
|
@@ -17,20 +15,20 @@ module ContextR
|
|
17
15
|
# with_layers(layer_name, ...) { ... }
|
18
16
|
#
|
19
17
|
def with_layers(*layer_symbols, &block)
|
20
|
-
layers = layer_symbols.collect do |
|
18
|
+
layers = layer_symbols.collect do |layer_symbol|
|
21
19
|
layer_by_symbol(layer_symbol)
|
22
20
|
end
|
23
|
-
|
21
|
+
layered_do(active_layers_as_classes - layers + layers, block)
|
24
22
|
end
|
25
23
|
alias with_layer with_layers
|
26
24
|
|
27
25
|
# allows the explicit deactivation of layers within a block context
|
28
26
|
#
|
29
27
|
# ContextR::with_layers(:foo, :bar) do
|
30
|
-
# ContextR::
|
28
|
+
# ContextR::active_layers # => [:default, :foo, :bar]
|
31
29
|
#
|
32
30
|
# ContextR::without_layers(:foo) do
|
33
|
-
# ContextR::
|
31
|
+
# ContextR::active_layers # => [:default, :bar]
|
34
32
|
# end
|
35
33
|
#
|
36
34
|
# end
|
@@ -39,20 +37,24 @@ module ContextR
|
|
39
37
|
# without_layers(layer_name, ...) { ... }
|
40
38
|
#
|
41
39
|
def without_layers(*layer_symbols, &block)
|
42
|
-
layers = layer_symbols.collect do |
|
40
|
+
layers = layer_symbols.collect do |layer_symbol|
|
43
41
|
layer_by_symbol(layer_symbol)
|
44
42
|
end
|
45
|
-
|
43
|
+
layered_do(active_layers_as_classes - layers, block)
|
46
44
|
end
|
47
45
|
alias without_layer without_layers
|
48
46
|
|
49
|
-
|
50
|
-
|
47
|
+
# returns all currently active layers in their activation order
|
48
|
+
def active_layers
|
49
|
+
active_layers_as_classes.collect { |layer| symbol_by_layer(layer) }
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
|
52
|
+
# returns all layers that where defined, but are not neccessarily active
|
53
|
+
def layers
|
54
|
+
layers_as_classes.collect { |layer| symbol_by_layer(layer) }
|
55
55
|
end
|
56
|
+
|
56
57
|
end
|
58
|
+
self.extend(PublicApi)
|
57
59
|
end
|
58
60
|
|
data/lib/contextr/version.rb
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
unless Object.const_defined? "ActiveSupport"
|
2
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.
|
3
24
|
def alias_method_chain(target, feature)
|
4
25
|
# Strip out punctuation on predicates or bang methods since
|
5
26
|
# e.g. target?_without_feature is not a valid method name.
|
@@ -23,9 +44,24 @@ unless Object.const_defined? "ActiveSupport"
|
|
23
44
|
end
|
24
45
|
end
|
25
46
|
|
26
|
-
|
47
|
+
# The Inflector transforms words from singular to plural, class names to
|
48
|
+
# table names, modularized class names to ones without, and class names to
|
49
|
+
# foreign keys. The default inflections for pluralization, singularization,
|
50
|
+
# and uncountable words are kept in inflections.rb.
|
51
|
+
module Inflector #:nodoc:
|
27
52
|
extend self
|
28
53
|
|
54
|
+
# By default, camelize converts strings to UpperCamelCase. If the argument
|
55
|
+
# to camelize is set to ":lower" then camelize produces lowerCamelCase.
|
56
|
+
#
|
57
|
+
# camelize will also convert '/' to '::' which is useful for converting
|
58
|
+
# paths to namespaces
|
59
|
+
#
|
60
|
+
# Examples
|
61
|
+
# "active_record".camelize #=> "ActiveRecord"
|
62
|
+
# "active_record".camelize(:lower) #=> "activeRecord"
|
63
|
+
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
64
|
+
# "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
|
29
65
|
def camelize(lower_case_and_underscored_word,
|
30
66
|
first_letter_in_uppercase = true)
|
31
67
|
if first_letter_in_uppercase
|
@@ -37,6 +73,14 @@ unless Object.const_defined? "ActiveSupport"
|
|
37
73
|
end
|
38
74
|
end
|
39
75
|
|
76
|
+
# The reverse of +camelize+. Makes an underscored form from the expression
|
77
|
+
# in the string.
|
78
|
+
#
|
79
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
80
|
+
#
|
81
|
+
# Examples
|
82
|
+
# "ActiveRecord".underscore #=> "active_record"
|
83
|
+
# "ActiveRecord::Errors".underscore #=> active_record/errors
|
40
84
|
def underscore(camel_cased_word)
|
41
85
|
camel_cased_word.to_s.gsub(/::/, '/').
|
42
86
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
@@ -45,6 +89,13 @@ unless Object.const_defined? "ActiveSupport"
|
|
45
89
|
downcase
|
46
90
|
end
|
47
91
|
|
92
|
+
# Constantize tries to find a declared constant with the name specified
|
93
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
94
|
+
# or is not initialized.
|
95
|
+
#
|
96
|
+
# Examples
|
97
|
+
# "Module".constantize #=> Module
|
98
|
+
# "Class".constantize #=> Class
|
48
99
|
def constantize(camel_cased_word)
|
49
100
|
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
50
101
|
raise NameError,
|
@@ -55,10 +106,26 @@ unless Object.const_defined? "ActiveSupport"
|
|
55
106
|
end
|
56
107
|
end
|
57
108
|
|
58
|
-
module ActiveSupport
|
59
|
-
module CoreExtensions
|
60
|
-
module String
|
109
|
+
module ActiveSupport #:nodoc:
|
110
|
+
module CoreExtensions #:nodoc:
|
111
|
+
module String #:nodoc:
|
112
|
+
# String inflections define new methods on the String class to
|
113
|
+
# transform names for different purposes. For instance, you can figure
|
114
|
+
# out the name of a database from the name of a class.
|
115
|
+
# "ScaleScore".tableize => "scale_scores"
|
61
116
|
module Inflections
|
117
|
+
# By default, camelize converts strings to UpperCamelCase. If the
|
118
|
+
# argument to camelize is set to ":lower" then camelize produces
|
119
|
+
# lowerCamelCase.
|
120
|
+
#
|
121
|
+
# camelize will also convert '/' to '::' which is useful for
|
122
|
+
# converting paths to namespaces
|
123
|
+
#
|
124
|
+
# Examples
|
125
|
+
# "active_record".camelize #=> "ActiveRecord"
|
126
|
+
# "active_record".camelize(:lower) #=> "activeRecord"
|
127
|
+
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
128
|
+
# "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
|
62
129
|
def camelize(first_letter = :upper)
|
63
130
|
case first_letter
|
64
131
|
when :upper then Inflector.camelize(self, true)
|
@@ -67,10 +134,25 @@ unless Object.const_defined? "ActiveSupport"
|
|
67
134
|
end
|
68
135
|
alias_method :camelcase, :camelize
|
69
136
|
|
137
|
+
# The reverse of +camelize+. Makes an underscored form from the
|
138
|
+
# expression in the string.
|
139
|
+
#
|
140
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
141
|
+
#
|
142
|
+
# Examples
|
143
|
+
# "ActiveRecord".underscore #=> "active_record"
|
144
|
+
# "ActiveRecord::Errors".underscore #=> active_record/errors
|
70
145
|
def underscore
|
71
146
|
Inflector.underscore(self)
|
72
147
|
end
|
73
148
|
|
149
|
+
# Create a class name from a table name like Rails does for table
|
150
|
+
# names to models. Note that this returns a string and not a Class.
|
151
|
+
# (To convert to an actual class follow classify with constantize.)
|
152
|
+
#
|
153
|
+
# Examples
|
154
|
+
# "egg_and_hams".classify #=> "EggAndHam"
|
155
|
+
# "post".classify #=> "Post"
|
74
156
|
def constantize
|
75
157
|
Inflector.constantize(self)
|
76
158
|
end
|