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.
- 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
|