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/README.txt CHANGED
@@ -1,3 +1,16 @@
1
- README for contextr
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
- layers.each do |layer|
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
- class Module
2
- alias_method :include_without_layers, :include
3
- def include_with_layers(associations)
4
- if associations.delete(:class_side)
5
- klass = class << self; self; end
6
- else
7
- klass = self
8
- end
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(klass, modul)
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 behavioural_class
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
@@ -1,5 +1,5 @@
1
1
  module ContextR
2
- class EventMachine
2
+ class EventMachine # :nodoc: all
3
3
  module ClassMethods
4
4
  include UniqueId
5
5
 
@@ -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] |= [methods_module]
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)
@@ -1,5 +1,5 @@
1
1
  require "thread"
2
- module MutexCode
2
+ module MutexCode #:nodoc:
3
3
  def semaphore
4
4
  @semaphore ||= Mutex.new
5
5
  end
@@ -1,4 +1,4 @@
1
- module UniqueId
1
+ module UniqueId #:nodoc:
2
2
  def new_unique_id
3
3
  $id_semaphore ||= Mutex.new
4
4
  $id_semaphore.synchronize do
@@ -1,14 +1,12 @@
1
1
  module ContextR
2
- module ClassMethods
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::current_layers # => [:default, :foo, :bar]
6
+ # ContextR::active_layers # => [:default, :foo, :bar]
9
7
  #
10
8
  # ContextR::with_layers(:baz) do
11
- # ContextR::current_layers # => [:default, :foo, :bar, :baz]
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 | layer_symbol |
18
+ layers = layer_symbols.collect do |layer_symbol|
21
19
  layer_by_symbol(layer_symbol)
22
20
  end
23
- Dynamic.let({ :layers => Dynamic[:layers] | layers }, &block)
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::current_layers # => [:default, :foo, :bar]
28
+ # ContextR::active_layers # => [:default, :foo, :bar]
31
29
  #
32
30
  # ContextR::without_layers(:foo) do
33
- # ContextR::current_layers # => [:default, :bar]
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 | layer_symbol |
40
+ layers = layer_symbols.collect do |layer_symbol|
43
41
  layer_by_symbol(layer_symbol)
44
42
  end
45
- Dynamic.let({ :layers => Dynamic[:layers] - layers }, &block)
43
+ layered_do(active_layers_as_classes - layers, block)
46
44
  end
47
45
  alias without_layer without_layers
48
46
 
49
- def layers
50
- Dynamic[:layers]
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
- def layer_symbols
54
- layers.collect { | layer | symbol_by_layer(layer) }
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
 
@@ -2,7 +2,7 @@ module ContextR #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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
- module Inflector
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